Skip to content

Commit a52d953

Browse files
authored
Fix performance regression on startup in repos with many tags (#4777)
- **PR Description** In #4663 we added information in the tags panel about the selected tag (whether it's annotated etc). Unfortunately this introduced a performance regression in repositories with many tags, so revert this and implement the feature in a slightly different way to avoid the performance hit. Fixes #4770.
2 parents 8483239 + 1c533dc commit a52d953

File tree

7 files changed

+60
-31
lines changed

7 files changed

+60
-31
lines changed

pkg/commands/git_commands/tag.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package git_commands
22

33
import (
4+
"strings"
5+
46
"github.com/jesseduffield/gocui"
57
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
68
)
@@ -74,3 +76,13 @@ func (self *TagCommands) ShowAnnotationInfo(tagName string) (string, error) {
7476

7577
return self.cmd.New(cmdArgs).RunWithOutput()
7678
}
79+
80+
func (self *TagCommands) IsTagAnnotated(tagName string) (bool, error) {
81+
cmdArgs := NewGitCmd("cat-file").
82+
Arg("-t").
83+
Arg("refs/tags/" + tagName).
84+
ToArgv()
85+
86+
output, err := self.cmd.New(cmdArgs).RunWithOutput()
87+
return strings.TrimSpace(output) == "tag", err
88+
}

pkg/commands/git_commands/tag_loader.go

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package git_commands
22

33
import (
4-
"strings"
4+
"regexp"
55

66
"github.com/jesseduffield/lazygit/pkg/commands/models"
77
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
@@ -26,34 +26,30 @@ func NewTagLoader(
2626
}
2727

2828
func (self *TagLoader) GetTags() ([]*models.Tag, error) {
29-
// get tags, sorted by creation date (descending)
29+
// get remote branches, sorted by creation date (descending)
3030
// see: https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---sortltkeygt
31-
cmdArgs := NewGitCmd("for-each-ref").
32-
Arg("--sort=-creatordate").
33-
Arg("--format=%(refname)%00%(objecttype)%00%(contents:subject)").
34-
Arg("refs/tags").
35-
ToArgv()
31+
cmdArgs := NewGitCmd("tag").Arg("--list", "-n", "--sort=-creatordate").ToArgv()
3632
tagsOutput, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
3733
if err != nil {
3834
return nil, err
3935
}
4036

4137
split := utils.SplitLines(tagsOutput)
4238

43-
tags := lo.FilterMap(split, func(line string, _ int) (*models.Tag, bool) {
44-
fields := strings.SplitN(line, "\x00", 3)
45-
if len(fields) != 3 {
46-
return nil, false
39+
lineRegex := regexp.MustCompile(`^([^\s]+)(\s+)?(.*)$`)
40+
41+
tags := lo.Map(split, func(line string, _ int) *models.Tag {
42+
matches := lineRegex.FindStringSubmatch(line)
43+
tagName := matches[1]
44+
message := ""
45+
if len(matches) > 3 {
46+
message = matches[3]
4747
}
48-
tagName := fields[0]
49-
objectType := fields[1]
50-
message := fields[2]
5148

5249
return &models.Tag{
53-
Name: strings.TrimPrefix(tagName, "refs/tags/"),
54-
Message: message,
55-
IsAnnotated: objectType == "tag",
56-
}, true
50+
Name: tagName,
51+
Message: message,
52+
}
5753
})
5854

5955
return tags, nil

pkg/commands/git_commands/tag_loader_test.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import (
99
"github.com/stretchr/testify/assert"
1010
)
1111

12-
const tagsOutput = "refs/tags/tag1\x00tag\x00this is my message\n" +
13-
"refs/tags/tag2\x00commit\x00\n" +
14-
"refs/tags/tag3\x00tag\x00this is my other message\n"
12+
const tagsOutput = `tag1 this is my message
13+
tag2
14+
tag3 this is my other message
15+
`
1516

1617
func TestGetTags(t *testing.T) {
1718
type scenario struct {
@@ -25,18 +26,18 @@ func TestGetTags(t *testing.T) {
2526
{
2627
testName: "should return no tags if there are none",
2728
runner: oscommands.NewFakeRunner(t).
28-
ExpectGitArgs([]string{"for-each-ref", "--sort=-creatordate", "--format=%(refname)%00%(objecttype)%00%(contents:subject)", "refs/tags"}, "", nil),
29+
ExpectGitArgs([]string{"tag", "--list", "-n", "--sort=-creatordate"}, "", nil),
2930
expectedTags: []*models.Tag{},
3031
expectedError: nil,
3132
},
3233
{
3334
testName: "should return tags if present",
3435
runner: oscommands.NewFakeRunner(t).
35-
ExpectGitArgs([]string{"for-each-ref", "--sort=-creatordate", "--format=%(refname)%00%(objecttype)%00%(contents:subject)", "refs/tags"}, tagsOutput, nil),
36+
ExpectGitArgs([]string{"tag", "--list", "-n", "--sort=-creatordate"}, tagsOutput, nil),
3637
expectedTags: []*models.Tag{
37-
{Name: "tag1", Message: "this is my message", IsAnnotated: true},
38-
{Name: "tag2", Message: "", IsAnnotated: false},
39-
{Name: "tag3", Message: "this is my other message", IsAnnotated: true},
38+
{Name: "tag1", Message: "this is my message"},
39+
{Name: "tag2", Message: ""},
40+
{Name: "tag3", Message: "this is my other message"},
4041
},
4142
expectedError: nil,
4243
},

pkg/commands/models/tag.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,9 @@ package models
33
// Tag : A git tag
44
type Tag struct {
55
Name string
6-
76
// this is either the first line of the message of an annotated tag, or the
87
// first line of a commit message for a lightweight tag
98
Message string
10-
11-
// true if this is an annotated tag, false if it's a lightweight tag
12-
IsAnnotated bool
139
}
1410

1511
func (t *Tag) FullRefName() string {

pkg/gui/controllers/tags_controller.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,12 @@ func (self *TagsController) GetOnRenderToMain() func() {
117117
}
118118

119119
func (self *TagsController) getTagInfo(tag *models.Tag) string {
120-
if tag.IsAnnotated {
120+
tagIsAnnotated, err := self.c.Git().Tag.IsTagAnnotated(tag.Name)
121+
if err != nil {
122+
self.c.Log.Warnf("Error checking if tag is annotated: %v", err)
123+
}
124+
125+
if tagIsAnnotated {
121126
info := fmt.Sprintf("%s: %s", self.c.Tr.AnnotatedTag, style.AttrBold.Sprint(style.FgYellow.Sprint(tag.Name)))
122127
output, err := self.c.Git().Tag.ShowAnnotationInfo(tag.Name)
123128
if err == nil {

pkg/integration/tests/tag/crud_annotated.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ var CrudAnnotated = NewIntegrationTest(NewIntegrationTestArgs{
3232
Lines(
3333
MatchesRegexp(`new-tag.*message`).IsSelected(),
3434
).
35+
Tap(func() {
36+
t.Views().Main().ContainsLines(
37+
Equals("Annotated tag: new-tag"),
38+
Equals(""),
39+
Contains("Tagger:"),
40+
Contains("TaggerDate:"),
41+
Equals(""),
42+
Equals("message"),
43+
Equals(""),
44+
Equals("---"),
45+
)
46+
}).
3547
Press(keys.Universal.Push).
3648
Tap(func() {
3749
t.ExpectPopup().Prompt().

pkg/integration/tests/tag/crud_lightweight.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ var CrudLightweight = NewIntegrationTest(NewIntegrationTestArgs{
2828
Lines(
2929
MatchesRegexp(`new-tag.*initial commit`).IsSelected(),
3030
).
31+
Tap(func() {
32+
t.Views().Main().ContainsLines(
33+
Equals("Lightweight tag: new-tag"),
34+
Equals(""),
35+
Equals("---"),
36+
)
37+
}).
3138
PressEnter().
3239
Tap(func() {
3340
// view the commits of the tag

0 commit comments

Comments
 (0)