Repository: jesseduffield/lazygit (language: go)

Issue:
Add date in stash panel like in local branches panel
Some projects can be messy, and stashes may accumulate. Sometimes old stashes stay in the stash list. If they are too old I may want to remove them. But I don't get the date information on lazygit.

Local branches panel has a "2d" / "3m" short information to show the last modifications. This could be nice to have the same presentation in the stash panel.

We also can show the date in the right detail view as a header, but it is less visible than into the stash panel.

FYI to get the stash date, we can add an option to the command like that : `git stash list --date=local`.

Not a Go developer and already checked lazygit code by curiosity, I won't be able to make a patch infortunately.

Thanks !


Discussion / hints:
I had a time to looks through this and here is what I found

In `stash_loader.go` if we change the format how stashes are listed to something like this to include the age (at line 36 and 69 respectively)
```go
rawString, _ := self.cmd.New("git stash list -z --pretty='%gs date@{%ct}'").DontLog().RunWithOutput()` 
```
```go
rawString, err := self.cmd.New("git stash list --name-only --pretty='%gd: %gs date@{%ct}'").DontLog().RunWithOutput()
```
we can get the age information and its just matter of displaying it by getting the age from the line

```go
func (c *StashLoader) stashEntryFromLine(line string, index int) *models.StashEntry {
	regular := regexp.MustCompile(`(?P<line>.*?) date@\{(?P<age>.*?)\}`)
	match := regular.FindStringSubmatch(line)
	if match != nil {
		date, _ := strconv.Atoi(match[2])
		recency := utils.UnixToTimeAgo(int64(date))
		line =  recency + " " + match[1]
	}
	return &models.StashEntry{
		Name:  line,
		Index: index,
	}
}
```

Output would look something like this 
![lg](https://user-images.githubusercontent.com/95934259/219439592-e9408133-e6e0-4d6a-b92a-763d815638ad.png)
![filtered_lg](https://user-images.githubusercontent.com/95934259/219441105-c0cec640-e03a-4c96-8011-78658b1256f9.png)

let me know if I am in the right direction, I will be more than happy to create a PR!
@glendsoza This seems to be a good way to show it, did you try to make a PR on this ? 

Fix patch:
diff --git a/pkg/commands/git_commands/stash_loader.go b/pkg/commands/git_commands/stash_loader.go
index 1ab0e0ad0e9..f97cc718a44 100644
--- a/pkg/commands/git_commands/stash_loader.go
+++ b/pkg/commands/git_commands/stash_loader.go
@@ -32,14 +32,14 @@ func (self *StashLoader) GetStashEntries(filterPath string) []*models.StashEntry
 		return self.getUnfilteredStashEntries()
 	}
 
-	cmdArgs := NewGitCmd("stash").Arg("list", "--name-only").ToArgv()
+	cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--name-only", "--pretty=%ct|%gs").ToArgv()
 	rawString, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
 	if err != nil {
 		return self.getUnfilteredStashEntries()
 	}
 	stashEntries := []*models.StashEntry{}
 	var currentStashEntry *models.StashEntry
-	lines := utils.SplitLines(rawString)
+	lines := utils.SplitNul(rawString)
 	isAStash := func(line string) bool { return strings.HasPrefix(line, "stash@{") }
 	re := regexp.MustCompile(`stash@\{(\d+)\}`)
 
@@ -66,7 +66,7 @@ outer:
 }
 
 func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
-	cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--pretty=%gs").ToArgv()
+	cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--pretty=%ct|%gs").ToArgv()
 
 	rawString, _ := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
 	return lo.Map(utils.SplitNul(rawString), func(line string, index int) *models.StashEntry {
@@ -75,8 +75,23 @@ func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
 }
 
 func (c *StashLoader) stashEntryFromLine(line string, index int) *models.StashEntry {
-	return &models.StashEntry{
+	model := &models.StashEntry{
 		Name:  line,
 		Index: index,
 	}
+
+	tstr, msg, ok := strings.Cut(line, "|")
+	if !ok {
+		return model
+	}
+
+	t, err := strconv.ParseInt(tstr, 10, 64)
+	if err != nil {
+		return model
+	}
+
+	model.Name = msg
+	model.Recency = utils.UnixToTimeAgo(t)
+
+	return model
 }
diff --git a/pkg/commands/models/stash_entry.go b/pkg/commands/models/stash_entry.go
index e70dfbf0982..2a1cc8435cb 100644
--- a/pkg/commands/models/stash_entry.go
+++ b/pkg/commands/models/stash_entry.go
@@ -4,8 +4,9 @@ import "fmt"
 
 // StashEntry : A git stash entry
 type StashEntry struct {
-	Index int
-	Name  string
+	Index   int
+	Recency string
+	Name    string
 }
 
 func (s *StashEntry) FullRefName() string {
diff --git a/pkg/gui/presentation/stash_entries.go b/pkg/gui/presentation/stash_entries.go
index c45e919825b..c4a1a4de167 100644
--- a/pkg/gui/presentation/stash_entries.go
+++ b/pkg/gui/presentation/stash_entries.go
@@ -3,6 +3,7 @@ package presentation
 import (
 	"github.com/jesseduffield/lazygit/pkg/commands/models"
 	"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
+	"github.com/jesseduffield/lazygit/pkg/gui/style"
 	"github.com/jesseduffield/lazygit/pkg/theme"
 	"github.com/samber/lo"
 )
@@ -21,10 +22,13 @@ func getStashEntryDisplayStrings(s *models.StashEntry, diffed bool) []string {
 		textStyle = theme.DiffTerminalColor
 	}
 
-	res := make([]string, 0, 2)
+	res := make([]string, 0, 3)
+	res = append(res, style.FgCyan.Sprint(s.Recency))
+
 	if icons.IsIconEnabled() {
 		res = append(res, textStyle.Sprint(icons.IconForStash(s)))
 	}
+
 	res = append(res, textStyle.Sprint(s.Name))
 	return res
 }


Test patch:
diff --git a/pkg/commands/git_commands/stash_loader_test.go b/pkg/commands/git_commands/stash_loader_test.go
index d48b83be749..393b4d4b93b 100644
--- a/pkg/commands/git_commands/stash_loader_test.go
+++ b/pkg/commands/git_commands/stash_loader_test.go
@@ -22,14 +22,14 @@ func TestGetStashEntries(t *testing.T) {
 			"No stash entries found",
 			"",
 			oscommands.NewFakeRunner(t).
-				ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"}, "", nil),
+				ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%ct|%gs"}, "", nil),
 			[]*models.StashEntry{},
 		},
 		{
 			"Several stash entries found",
 			"",
 			oscommands.NewFakeRunner(t).
-				ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"},
+				ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%ct|%gs"},
 					"WIP on add-pkg-commands-test: 55c6af2 increase parallel build\x00WIP on master: bb86a3f update github template\x00",
 					nil,
 				),
diff --git a/pkg/integration/tests/stash/rename.go b/pkg/integration/tests/stash/rename.go
index eb57fa6541e..4122b3aa893 100644
--- a/pkg/integration/tests/stash/rename.go
+++ b/pkg/integration/tests/stash/rename.go
@@ -22,14 +22,14 @@ var Rename = NewIntegrationTest(NewIntegrationTestArgs{
 		t.Views().Stash().
 			Focus().
 			Lines(
-				Equals("On master: bar"),
-				Equals("On master: foo"),
+				Contains("On master: bar"),
+				Contains("On master: foo"),
 			).
 			SelectNextItem().
 			Press(keys.Stash.RenameStash).
 			Tap(func() {
 				t.ExpectPopup().Prompt().Title(Equals("Rename stash: stash@{1}")).Type(" baz").Confirm()
 			}).
-			SelectedLine(Equals("On master: foo baz"))
+			SelectedLine(Contains("On master: foo baz"))
 	},
 })
diff --git a/pkg/integration/tests/stash/stash.go b/pkg/integration/tests/stash/stash.go
index d5fc3e92e81..9f829215649 100644
--- a/pkg/integration/tests/stash/stash.go
+++ b/pkg/integration/tests/stash/stash.go
@@ -29,7 +29,7 @@ var Stash = NewIntegrationTest(NewIntegrationTestArgs{
 
 		t.Views().Stash().
 			Lines(
-				Contains("my stashed file"),
+				MatchesRegexp(`\ds .* my stashed file`),
 			)
 
 		t.Views().Files().