Skip to content

Commit 889a8c2

Browse files
zeripath6543
andauthored
Use full output of git show-ref --tags to get tags for PushUpdateAddTag (#19235)
Strangely #19038 appears to relate to an issue whereby a tag appears to be listed in `git show-ref --tags` but then does not appear when `git show-ref --tags -- short_name` is called. As a solution though I propose to stop the second call as it is unnecessary and only likely to cause problems. I've also noticed that the tags calls are wildly inefficient and aren't using the common cat-files - so these have been added. I've also noticed that the git commit-graph is not being written on mirroring - so I've also added writing this to the migration which should improve mirror rendering somewhat. Fix #19038 Signed-off-by: Andrew Thornton <[email protected]> Co-authored-by: 6543 <[email protected]>
1 parent 1eebbf2 commit 889a8c2

File tree

9 files changed

+323
-102
lines changed

9 files changed

+323
-102
lines changed

modules/git/repo_branch_gogit.go

+40-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"strings"
1414

1515
"github.com/go-git/go-git/v5/plumbing"
16+
"github.com/go-git/go-git/v5/plumbing/storer"
1617
)
1718

1819
// IsObjectExist returns true if given reference exists in the repository.
@@ -82,7 +83,8 @@ func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
8283
}
8384

8485
// WalkReferences walks all the references from the repository
85-
func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) error) (int, error) {
86+
// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
87+
func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refname string) error) (int, error) {
8688
repo := RepositoryFromContext(ctx, repoPath)
8789
if repo == nil {
8890
var err error
@@ -101,9 +103,45 @@ func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) er
101103
defer iter.Close()
102104

103105
err = iter.ForEach(func(ref *plumbing.Reference) error {
104-
err := walkfn(string(ref.Name()))
106+
err := walkfn(ref.Hash().String(), string(ref.Name()))
105107
i++
106108
return err
107109
})
108110
return i, err
109111
}
112+
113+
// WalkReferences walks all the references from the repository
114+
func (repo *Repository) WalkReferences(arg ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
115+
i := 0
116+
var iter storer.ReferenceIter
117+
var err error
118+
switch arg {
119+
case ObjectTag:
120+
iter, err = repo.gogitRepo.Tags()
121+
case ObjectBranch:
122+
iter, err = repo.gogitRepo.Branches()
123+
default:
124+
iter, err = repo.gogitRepo.References()
125+
}
126+
if err != nil {
127+
return i, err
128+
}
129+
defer iter.Close()
130+
131+
err = iter.ForEach(func(ref *plumbing.Reference) error {
132+
if i < skip {
133+
i++
134+
return nil
135+
}
136+
err := walkfn(ref.Hash().String(), string(ref.Name()))
137+
i++
138+
if err != nil {
139+
return err
140+
}
141+
if limit != 0 && i >= skip+limit {
142+
return storer.ErrStop
143+
}
144+
return nil
145+
})
146+
return i, err
147+
}

modules/git/repo_branch_nogogit.go

+26-9
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,29 @@ func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
6868
}
6969

7070
// WalkReferences walks all the references from the repository
71-
func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) error) (int, error) {
71+
func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refname string) error) (int, error) {
7272
return walkShowRef(ctx, repoPath, "", 0, 0, walkfn)
7373
}
7474

75+
// WalkReferences walks all the references from the repository
76+
// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
77+
func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
78+
var arg string
79+
switch refType {
80+
case ObjectTag:
81+
arg = "--tags"
82+
case ObjectBranch:
83+
arg = "--heads"
84+
default:
85+
arg = ""
86+
}
87+
88+
return walkShowRef(repo.Ctx, repo.Path, arg, skip, limit, walkfn)
89+
}
90+
7591
// callShowRef return refs, if limit = 0 it will not limit
7692
func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) {
77-
countAll, err = walkShowRef(ctx, repoPath, arg, skip, limit, func(branchName string) error {
93+
countAll, err = walkShowRef(ctx, repoPath, arg, skip, limit, func(_, branchName string) error {
7894
branchName = strings.TrimPrefix(branchName, prefix)
7995
branchNames = append(branchNames, branchName)
8096

@@ -83,7 +99,7 @@ func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit
8399
return
84100
}
85101

86-
func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, walkfn func(string) error) (countAll int, err error) {
102+
func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
87103
stdoutReader, stdoutWriter := io.Pipe()
88104
defer func() {
89105
_ = stdoutReader.Close()
@@ -130,11 +146,7 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal
130146
for limit == 0 || i < skip+limit {
131147
// The output of show-ref is simply a list:
132148
// <sha> SP <ref> LF
133-
_, err := bufReader.ReadSlice(' ')
134-
for err == bufio.ErrBufferFull {
135-
// This shouldn't happen but we'll tolerate it for the sake of peace
136-
_, err = bufReader.ReadSlice(' ')
137-
}
149+
sha, err := bufReader.ReadString(' ')
138150
if err == io.EOF {
139151
return i, nil
140152
}
@@ -154,7 +166,12 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal
154166
if len(branchName) > 0 {
155167
branchName = branchName[:len(branchName)-1]
156168
}
157-
err = walkfn(branchName)
169+
170+
if len(sha) > 0 {
171+
sha = sha[:len(sha)-1]
172+
}
173+
174+
err = walkfn(sha, branchName)
158175
if err != nil {
159176
return i, err
160177
}

modules/git/repo_commitgraph.go

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2022 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package git
6+
7+
import (
8+
"context"
9+
"fmt"
10+
)
11+
12+
// WriteCommitGraph write commit graph to speed up repo access
13+
// this requires git v2.18 to be installed
14+
func WriteCommitGraph(ctx context.Context, repoPath string) error {
15+
if CheckGitVersionAtLeast("2.18") == nil {
16+
if _, err := NewCommand(ctx, "commit-graph", "write").RunInDir(repoPath); err != nil {
17+
return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err)
18+
}
19+
}
20+
return nil
21+
}

modules/git/repo_tag.go

+14-77
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"fmt"
1111
"strings"
1212

13-
"code.gitea.io/gitea/modules/log"
1413
"code.gitea.io/gitea/modules/util"
1514
)
1615

@@ -34,69 +33,6 @@ func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error
3433
return err
3534
}
3635

37-
func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
38-
t, ok := repo.tagCache.Get(tagID.String())
39-
if ok {
40-
log.Debug("Hit cache: %s", tagID)
41-
tagClone := *t.(*Tag)
42-
tagClone.Name = name // This is necessary because lightweight tags may have same id
43-
return &tagClone, nil
44-
}
45-
46-
tp, err := repo.GetTagType(tagID)
47-
if err != nil {
48-
return nil, err
49-
}
50-
51-
// Get the commit ID and tag ID (may be different for annotated tag) for the returned tag object
52-
commitIDStr, err := repo.GetTagCommitID(name)
53-
if err != nil {
54-
// every tag should have a commit ID so return all errors
55-
return nil, err
56-
}
57-
commitID, err := NewIDFromString(commitIDStr)
58-
if err != nil {
59-
return nil, err
60-
}
61-
62-
// If type is "commit, the tag is a lightweight tag
63-
if ObjectType(tp) == ObjectCommit {
64-
commit, err := repo.GetCommit(commitIDStr)
65-
if err != nil {
66-
return nil, err
67-
}
68-
tag := &Tag{
69-
Name: name,
70-
ID: tagID,
71-
Object: commitID,
72-
Type: tp,
73-
Tagger: commit.Committer,
74-
Message: commit.Message(),
75-
}
76-
77-
repo.tagCache.Set(tagID.String(), tag)
78-
return tag, nil
79-
}
80-
81-
// The tag is an annotated tag with a message.
82-
data, err := NewCommand(repo.Ctx, "cat-file", "-p", tagID.String()).RunInDirBytes(repo.Path)
83-
if err != nil {
84-
return nil, err
85-
}
86-
87-
tag, err := parseTagData(data)
88-
if err != nil {
89-
return nil, err
90-
}
91-
92-
tag.Name = name
93-
tag.ID = tagID
94-
tag.Type = tp
95-
96-
repo.tagCache.Set(tagID.String(), tag)
97-
return tag, nil
98-
}
99-
10036
// GetTagNameBySHA returns the name of a tag from its tag object SHA or commit SHA
10137
func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
10238
if len(sha) < 5 {
@@ -159,6 +95,20 @@ func (repo *Repository) GetTag(name string) (*Tag, error) {
15995
return tag, nil
16096
}
16197

98+
// GetTagWithID returns a Git tag by given name and ID
99+
func (repo *Repository) GetTagWithID(idStr, name string) (*Tag, error) {
100+
id, err := NewIDFromString(idStr)
101+
if err != nil {
102+
return nil, err
103+
}
104+
105+
tag, err := repo.getTag(id, name)
106+
if err != nil {
107+
return nil, err
108+
}
109+
return tag, nil
110+
}
111+
162112
// GetTagInfos returns all tag infos of the repository.
163113
func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
164114
// TODO this a slow implementation, makes one git command per tag
@@ -192,19 +142,6 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
192142
return tags, tagsTotal, nil
193143
}
194144

195-
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
196-
func (repo *Repository) GetTagType(id SHA1) (string, error) {
197-
// Get tag type
198-
stdout, err := NewCommand(repo.Ctx, "cat-file", "-t", id.String()).RunInDir(repo.Path)
199-
if err != nil {
200-
return "", err
201-
}
202-
if len(stdout) == 0 {
203-
return "", ErrNotExist{ID: id.String()}
204-
}
205-
return strings.TrimSpace(stdout), nil
206-
}
207-
208145
// GetAnnotatedTag returns a Git tag by its SHA, must be an annotated tag
209146
func (repo *Repository) GetAnnotatedTag(sha string) (*Tag, error) {
210147
id, err := NewIDFromString(sha)

modules/git/repo_tag_gogit.go

+82
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ package git
1111
import (
1212
"strings"
1313

14+
"code.gitea.io/gitea/modules/log"
15+
1416
"github.com/go-git/go-git/v5/plumbing"
1517
)
1618

@@ -53,3 +55,83 @@ func (repo *Repository) GetTags(skip, limit int) ([]string, error) {
5355

5456
return tagNames, nil
5557
}
58+
59+
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
60+
func (repo *Repository) GetTagType(id SHA1) (string, error) {
61+
// Get tag type
62+
obj, err := repo.gogitRepo.Object(plumbing.AnyObject, id)
63+
if err != nil {
64+
if err == plumbing.ErrReferenceNotFound {
65+
return "", &ErrNotExist{ID: id.String()}
66+
}
67+
return "", err
68+
}
69+
70+
return obj.Type().String(), nil
71+
}
72+
73+
func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
74+
t, ok := repo.tagCache.Get(tagID.String())
75+
if ok {
76+
log.Debug("Hit cache: %s", tagID)
77+
tagClone := *t.(*Tag)
78+
tagClone.Name = name // This is necessary because lightweight tags may have same id
79+
return &tagClone, nil
80+
}
81+
82+
tp, err := repo.GetTagType(tagID)
83+
if err != nil {
84+
return nil, err
85+
}
86+
87+
// Get the commit ID and tag ID (may be different for annotated tag) for the returned tag object
88+
commitIDStr, err := repo.GetTagCommitID(name)
89+
if err != nil {
90+
// every tag should have a commit ID so return all errors
91+
return nil, err
92+
}
93+
commitID, err := NewIDFromString(commitIDStr)
94+
if err != nil {
95+
return nil, err
96+
}
97+
98+
// If type is "commit, the tag is a lightweight tag
99+
if ObjectType(tp) == ObjectCommit {
100+
commit, err := repo.GetCommit(commitIDStr)
101+
if err != nil {
102+
return nil, err
103+
}
104+
tag := &Tag{
105+
Name: name,
106+
ID: tagID,
107+
Object: commitID,
108+
Type: tp,
109+
Tagger: commit.Committer,
110+
Message: commit.Message(),
111+
}
112+
113+
repo.tagCache.Set(tagID.String(), tag)
114+
return tag, nil
115+
}
116+
117+
gogitTag, err := repo.gogitRepo.TagObject(tagID)
118+
if err != nil {
119+
if err == plumbing.ErrReferenceNotFound {
120+
return nil, &ErrNotExist{ID: tagID.String()}
121+
}
122+
123+
return nil, err
124+
}
125+
126+
tag := &Tag{
127+
Name: name,
128+
ID: tagID,
129+
Object: gogitTag.Target,
130+
Type: tp,
131+
Tagger: &gogitTag.Tagger,
132+
Message: gogitTag.Message,
133+
}
134+
135+
repo.tagCache.Set(tagID.String(), tag)
136+
return tag, nil
137+
}

0 commit comments

Comments
 (0)