From c6164ece0a1a178d99f56de23d989baea637c457 Mon Sep 17 00:00:00 2001 From: oppiliappan Date: Wed, 16 Jul 2025 10:59:48 +0100 Subject: [PATCH] knotserver/git: refactor GitRepo.Branches to use `git for-each-ref` Change-Id: nrqkzvsznxlpmvmrslqusyyrotqzyyzk Signed-off-by: oppiliappan --- knotserver/git/branch.go | 112 +++++++++++++++++++++++++++++++++++++++ knotserver/git/git.go | 34 ------------ 2 files changed, 112 insertions(+), 34 deletions(-) create mode 100644 knotserver/git/branch.go diff --git a/knotserver/git/branch.go b/knotserver/git/branch.go new file mode 100644 index 0000000..e1cd2b8 --- /dev/null +++ b/knotserver/git/branch.go @@ -0,0 +1,112 @@ +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" + "tangled.sh/tangled.sh/core/types" +) + +func (g *GitRepo) Branches() ([]types.Branch, error) { + fields := []string{ + "refname:short", + "objectname", + "authorname", + "authoremail", + "authordate:unix", + "committername", + "committeremail", + "committerdate:unix", + "tree", + "parent", + "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/heads") + if err != nil { + return nil, fmt.Errorf("failed to get branches: %w", err) + } + + records := strings.Split(strings.TrimSpace(string(output)), recordSeparator) + if len(records) == 1 && records[0] == "" { + return nil, nil + } + + branches := make([]types.Branch, 0, len(records)) + + // ignore errors here + defaultBranch, _ := g.FindMainBranch() + + for _, line := range records { + parts := strings.SplitN(strings.TrimSpace(line), fieldSeparator, len(fields)) + if len(parts) < 6 { + continue + } + + branchName := parts[0] + commitHash := plumbing.NewHash(parts[1]) + authorName := parts[2] + authorEmail := strings.TrimSuffix(strings.TrimPrefix(parts[3], "<"), ">") + authorDate := parts[4] + committerName := parts[5] + committerEmail := strings.TrimSuffix(strings.TrimPrefix(parts[6], "<"), ">") + committerDate := parts[7] + treeHash := plumbing.NewHash(parts[8]) + parentHash := plumbing.NewHash(parts[9]) + message := parts[10] + + // parse creation time + var authoredAt, committedAt time.Time + if unix, err := strconv.ParseInt(authorDate, 10, 64); err == nil { + authoredAt = time.Unix(unix, 0) + } + if unix, err := strconv.ParseInt(committerDate, 10, 64); err == nil { + committedAt = time.Unix(unix, 0) + } + + branch := types.Branch{ + IsDefault: branchName == defaultBranch, + Reference: types.Reference{ + Name: branchName, + Hash: commitHash.String(), + }, + Commit: &object.Commit{ + Hash: commitHash, + Author: object.Signature{ + Name: authorName, + Email: authorEmail, + When: authoredAt, + }, + Committer: object.Signature{ + Name: committerName, + Email: committerEmail, + When: committedAt, + }, + TreeHash: treeHash, + ParentHashes: []plumbing.Hash{parentHash}, + Message: message, + }, + } + + branches = append(branches, branch) + } + + slices.Reverse(branches) + return branches, nil +} diff --git a/knotserver/git/git.go b/knotserver/git/git.go index 154f519..a5fc5ef 100644 --- a/knotserver/git/git.go +++ b/knotserver/git/git.go @@ -14,7 +14,6 @@ import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" - "tangled.sh/tangled.sh/core/types" ) var ( @@ -264,39 +263,6 @@ func (g *GitRepo) RawContent(path string) ([]byte, error) { return io.ReadAll(reader) } -func (g *GitRepo) Branches() ([]types.Branch, error) { - bi, err := g.r.Branches() - if err != nil { - return nil, fmt.Errorf("branchs: %w", err) - } - - branches := []types.Branch{} - - defaultBranch, err := g.FindMainBranch() - - _ = bi.ForEach(func(ref *plumbing.Reference) error { - b := types.Branch{} - b.Hash = ref.Hash().String() - b.Name = ref.Name().Short() - - // resolve commit that this branch points to - commit, _ := g.Commit(ref.Hash()) - if commit != nil { - b.Commit = commit - } - - if defaultBranch != "" && defaultBranch == b.Name { - b.IsDefault = true - } - - branches = append(branches, b) - - return nil - }) - - return branches, nil -} - func (g *GitRepo) Branch(name string) (*plumbing.Reference, error) { ref, err := g.r.Reference(plumbing.NewBranchReferenceName(name), false) if err != nil { -- 2.43.0