| 1 |
|
package gitx |
| 2 |
|
|
| 3 |
|
import ( |
| 4 |
|
"context" |
| 5 |
|
"sort" |
| 6 |
|
"time" |
| 7 |
|
|
| 8 |
|
"github.com/go-git/go-git/v5/plumbing" |
| 9 |
|
) |
| 10 |
|
|
| 11 |
|
// Ref is a named git reference paired with the object id it points at (the tag |
| 12 |
|
// object for annotated tags, matching git for-each-ref's %(objectname)). |
| 13 |
|
type Ref struct { |
| 14 |
|
Name string |
| 15 |
|
SHA string |
| 16 |
|
} |
| 17 |
|
|
| 18 |
|
// Refs lists the repository's branches and tags. Branches are returned with the |
| 19 |
|
// default (HEAD) branch first and the remainder alphabetical; tags are ordered |
| 20 |
|
// newest-first by creator date (tagger date for annotated tags, committer date |
| 21 |
|
// for lightweight tags). |
| 22 |
1 |
func (r *Repo) Refs(ctx context.Context) (branches, tags []Ref, err error) { |
| 23 |
1 |
_, cancel := r.withTimeout(ctx) |
| 24 |
1 |
defer cancel() |
| 25 |
1 |
|
| 26 |
1 |
bIter, err := r.repo.Branches() |
| 27 |
1 |
if err != nil { |
| 28 |
0 |
return nil, nil, err |
| 29 |
0 |
} |
| 30 |
2 |
err = bIter.ForEach(func(ref *plumbing.Reference) error { |
| 31 |
2 |
branches = append(branches, Ref{Name: ref.Name().Short(), SHA: ref.Hash().String()}) |
| 32 |
2 |
return nil |
| 33 |
2 |
}) |
| 34 |
1 |
if err != nil { |
| 35 |
0 |
return nil, nil, err |
| 36 |
0 |
} |
| 37 |
1 |
def, _ := r.DefaultBranch(ctx) |
| 38 |
1 |
sortBranches(branches, def) |
| 39 |
1 |
|
| 40 |
1 |
type tagRef struct { |
| 41 |
1 |
ref Ref |
| 42 |
1 |
when time.Time |
| 43 |
1 |
} |
| 44 |
1 |
var trefs []tagRef |
| 45 |
1 |
tIter, err := r.repo.Tags() |
| 46 |
1 |
if err != nil { |
| 47 |
0 |
return nil, nil, err |
| 48 |
0 |
} |
| 49 |
2 |
err = tIter.ForEach(func(ref *plumbing.Reference) error { |
| 50 |
2 |
trefs = append(trefs, tagRef{ |
| 51 |
2 |
ref: Ref{Name: ref.Name().Short(), SHA: ref.Hash().String()}, |
| 52 |
2 |
when: r.tagWhen(ref.Hash()), |
| 53 |
2 |
}) |
| 54 |
2 |
return nil |
| 55 |
2 |
}) |
| 56 |
1 |
if err != nil { |
| 57 |
0 |
return nil, nil, err |
| 58 |
0 |
} |
| 59 |
1 |
sort.SliceStable(trefs, func(i, j int) bool { |
| 60 |
1 |
if !trefs[i].when.Equal(trefs[j].when) { |
| 61 |
1 |
return trefs[i].when.After(trefs[j].when) |
| 62 |
1 |
} |
| 63 |
0 |
return trefs[i].ref.Name > trefs[j].ref.Name |
| 64 |
|
}) |
| 65 |
2 |
for _, t := range trefs { |
| 66 |
2 |
tags = append(tags, t.ref) |
| 67 |
2 |
} |
| 68 |
|
|
| 69 |
1 |
return branches, tags, nil |
| 70 |
|
} |
| 71 |
|
|
| 72 |
|
// DefaultBranch returns the short name of the branch HEAD points at (e.g. |
| 73 |
|
// "main"). It fails if HEAD is detached (not a symbolic reference). |
| 74 |
2 |
func (r *Repo) DefaultBranch(ctx context.Context) (string, error) { |
| 75 |
2 |
_, cancel := r.withTimeout(ctx) |
| 76 |
2 |
defer cancel() |
| 77 |
2 |
|
| 78 |
2 |
ref, err := r.repo.Reference(plumbing.HEAD, false) |
| 79 |
2 |
if err != nil { |
| 80 |
0 |
return "", err |
| 81 |
0 |
} |
| 82 |
2 |
if ref.Type() != plumbing.SymbolicReference { |
| 83 |
0 |
return "", plumbing.ErrReferenceNotFound |
| 84 |
0 |
} |
| 85 |
2 |
return ref.Target().Short(), nil |
| 86 |
|
} |
| 87 |
|
|
| 88 |
|
// tagWhen returns the creation time of a tag reference: the tagger time for an |
| 89 |
|
// annotated tag, else the committer time of the pointed-at commit. A zero time |
| 90 |
|
// is returned when neither can be resolved. |
| 91 |
2 |
func (r *Repo) tagWhen(h plumbing.Hash) time.Time { |
| 92 |
2 |
if t, err := r.repo.TagObject(h); err == nil { |
| 93 |
2 |
return t.Tagger.When |
| 94 |
2 |
} |
| 95 |
0 |
if c, err := r.repo.CommitObject(h); err == nil { |
| 96 |
0 |
return c.Committer.When |
| 97 |
0 |
} |
| 98 |
0 |
return time.Time{} |
| 99 |
|
} |
| 100 |
|
|
| 101 |
|
// sortBranches orders refs alphabetically but floats the default branch to the |
| 102 |
|
// front. |
| 103 |
1 |
func sortBranches(refs []Ref, defaultBranch string) { |
| 104 |
1 |
sort.SliceStable(refs, func(i, j int) bool { |
| 105 |
1 |
if refs[i].Name == defaultBranch { |
| 106 |
1 |
return refs[j].Name != defaultBranch |
| 107 |
1 |
} |
| 108 |
0 |
if refs[j].Name == defaultBranch { |
| 109 |
0 |
return false |
| 110 |
0 |
} |
| 111 |
0 |
return refs[i].Name < refs[j].Name |
| 112 |
|
}) |
| 113 |
|
} |