From 0950f59c3f45597bea0e1cf997fafe0f5d30f83a Mon Sep 17 00:00:00 2001 From: oppiliappan Date: Tue, 15 Jul 2025 21:44:47 +0100 Subject: [PATCH] knotserver/git: refactor GitRepo.Tags to use `git for-each-ref` Change-Id: rqxyyurmpkpsmvksnozxztlyqtryrnpm Signed-off-by: oppiliappan --- knotserver/git/git.go | 33 --------------- knotserver/git/tag.go | 99 +++++++++++++++++++++++++++++++++++++++++++ knotserver/routes.go | 30 ++++++++----- 3 files changed, 118 insertions(+), 44 deletions(-) create mode 100644 knotserver/git/tag.go diff --git a/knotserver/git/git.go b/knotserver/git/git.go index c7e1675..77bd1b3 100644 --- a/knotserver/git/git.go +++ b/knotserver/git/git.go @@ -264,39 +264,6 @@ func (g *GitRepo) RawContent(path string) ([]byte, error) { return io.ReadAll(reader) } -func (g *GitRepo) Tags() ([]*TagReference, error) { - iter, err := g.r.Tags() - if err != nil { - return nil, fmt.Errorf("tag objects: %w", err) - } - - tags := make([]*TagReference, 0) - - if err := iter.ForEach(func(ref *plumbing.Reference) error { - obj, err := g.r.TagObject(ref.Hash()) - switch err { - case nil: - tags = append(tags, &TagReference{ - ref: ref, - tag: obj, - }) - case plumbing.ErrObjectNotFound: - tags = append(tags, &TagReference{ - ref: ref, - }) - default: - return err - } - return nil - }); err != nil { - return nil, err - } - - tagList := &TagList{r: g.r, refs: tags} - sort.Sort(tagList) - return tags, nil -} - func (g *GitRepo) Branches() ([]types.Branch, error) { bi, err := g.r.Branches() if err != nil { diff --git a/knotserver/git/tag.go b/knotserver/git/tag.go new file mode 100644 index 0000000..db4ac32 --- /dev/null +++ b/knotserver/git/tag.go @@ -0,0 +1,99 @@ +package git + +import ( + "fmt" + "slices" + "strconv" + "strings" + "time" + + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" +) + +func (g *GitRepo) Tags() ([]object.Tag, error) { + fields := []string{ + "refname:short", + "objectname", + "objecttype", + "*objectname", + "*objecttype", + "taggername", + "taggeremail", + "taggerdate:unix", + "contents", + } + + var outFormat strings.Builder + outFormat.WriteString("--format=") + for i, f := range fields { + if i != 0 { + outFormat.WriteString(fieldSeparator) + } + outFormat.WriteString(fmt.Sprintf("%%(%s)", f)) + } + outFormat.WriteString("") + outFormat.WriteString(recordSeparator) + + output, err := g.forEachRef(outFormat.String(), "refs/tags") + if err != nil { + return nil, fmt.Errorf("failed to get tags: %w", err) + } + + records := strings.Split(strings.TrimSpace(string(output)), recordSeparator) + if len(records) == 1 && records[0] == "" { + return nil, nil + } + + tags := make([]object.Tag, 0, len(records)) + + for _, line := range records { + parts := strings.SplitN(strings.TrimSpace(line), fieldSeparator, len(fields)) + if len(parts) < 6 { + continue + } + + tagName := parts[0] + objectHash := parts[1] + objectType := parts[2] + targetHash := parts[3] // dereferenced object hash (empty for lightweight tags) + // targetType := parts[4] // dereferenced object type (empty for lightweight tags) + taggerName := parts[5] + taggerEmail := parts[6] + taggerDate := parts[7] + message := parts[8] + + // parse creation time + var createdAt time.Time + if unix, err := strconv.ParseInt(taggerDate, 10, 64); err == nil { + createdAt = time.Unix(unix, 0) + } + + // parse object type + typ, err := plumbing.ParseObjectType(objectType) + if err != nil { + return nil, err + } + + // strip email separators + taggerEmail = strings.TrimSuffix(strings.TrimPrefix(taggerEmail, "<"), ">") + + tag := object.Tag{ + Hash: plumbing.NewHash(objectHash), + Name: tagName, + Tagger: object.Signature{ + Name: taggerName, + Email: taggerEmail, + When: createdAt, + }, + Message: message, + TargetType: typ, + Target: plumbing.NewHash(targetHash), + } + + tags = append(tags, tag) + } + + slices.Reverse(tags) + return tags, nil +} diff --git a/knotserver/routes.go b/knotserver/routes.go index 1fe3891..0239fa6 100644 --- a/knotserver/routes.go +++ b/knotserver/routes.go @@ -96,7 +96,7 @@ func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) { total int branches []types.Branch files []types.NiceTree - tags []*git.TagReference + tags []object.Tag ) var wg sync.WaitGroup @@ -169,17 +169,21 @@ func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) { rtags := []*types.TagReference{} for _, tag := range tags { + var target *object.Tag + if tag.Target != plumbing.ZeroHash { + target = &tag + } tr := types.TagReference{ - Tag: tag.TagObject(), + Tag: target, } tr.Reference = types.Reference{ - Name: tag.Name(), - Hash: tag.Hash().String(), + Name: tag.Name, + Hash: tag.Hash.String(), } - if tag.Message() != "" { - tr.Message = tag.Message() + if tag.Message != "" { + tr.Message = tag.Message } rtags = append(rtags, &tr) @@ -488,17 +492,21 @@ func (h *Handle) Tags(w http.ResponseWriter, r *http.Request) { rtags := []*types.TagReference{} for _, tag := range tags { + var target *object.Tag + if tag.Target != plumbing.ZeroHash { + target = &tag + } tr := types.TagReference{ - Tag: tag.TagObject(), + Tag: target, } tr.Reference = types.Reference{ - Name: tag.Name(), - Hash: tag.Hash().String(), + Name: tag.Name, + Hash: tag.Hash.String(), } - if tag.Message() != "" { - tr.Message = tag.Message() + if tag.Message != "" { + tr.Message = tag.Message } rtags = append(rtags, &tr) -- 2.43.0