1package git
2
3import (
4 "errors"
5 "fmt"
6 "io"
7
8 "github.com/go-git/go-git/v5"
9 "github.com/go-git/go-git/v5/plumbing"
10 "github.com/go-git/go-git/v5/plumbing/object"
11)
12
13var ()
14
15var (
16 MissingRefErr = errors.New("Reference not found")
17)
18
19type (
20 GitRepository struct {
21 path string
22 repository *git.Repository
23
24 ref plumbing.Hash
25 // this is setRef when ref is setRef
26 setRef bool
27 }
28)
29
30func OpenRepository(dir string) (*GitRepository, error) {
31 g := &GitRepository{
32 path: dir,
33 }
34
35 repo, err := git.PlainOpen(dir)
36 if err != nil {
37 return nil, err
38 }
39 g.repository = repo
40
41 return g, nil
42}
43
44func (g *GitRepository) SetRef(ref string) error {
45 if ref == "" {
46 head, err := g.repository.Head()
47 if err != nil {
48 return errors.Join(MissingRefErr, err)
49 }
50 g.ref = head.Hash()
51 } else {
52 hash, err := g.repository.ResolveRevision(plumbing.Revision(ref))
53 if err != nil {
54 return errors.Join(MissingRefErr, err)
55 }
56 g.ref = *hash
57 }
58 g.setRef = true
59 return nil
60}
61
62func (g *GitRepository) Path() string {
63 return g.path
64}
65
66func (g *GitRepository) LastCommit() (*object.Commit, error) {
67 err := g.validateRef()
68 if err != nil {
69 return nil, err
70 }
71
72 c, err := g.repository.CommitObject(g.ref)
73 if err != nil {
74 return nil, err
75 }
76 return c, nil
77}
78
79func (g *GitRepository) Commits() ([]*object.Commit, error) {
80 err := g.validateRef()
81 if err != nil {
82 return nil, err
83 }
84
85 ci, err := g.repository.Log(&git.LogOptions{From: g.ref})
86 if err != nil {
87 return nil, fmt.Errorf("commits from ref: %w", err)
88 }
89
90 commits := []*object.Commit{}
91 // TODO: for now only load first 1000
92 for x := 0; x < 1000; x++ {
93 c, err := ci.Next()
94 if err != nil && errors.Is(err, io.EOF) {
95 break
96 } else if err != nil {
97 return nil, err
98 }
99 commits = append(commits, c)
100 }
101 if err != nil {
102 return nil, err
103 }
104
105 return commits, nil
106}
107
108func (g *GitRepository) Head() (*plumbing.Reference, error) {
109 return g.repository.Head()
110}
111
112func (g *GitRepository) Tags() ([]*object.Tag, error) {
113 ti, err := g.repository.TagObjects()
114 if err != nil {
115 return nil, err
116 }
117
118 tags := []*object.Tag{}
119 err = ti.ForEach(func(t *object.Tag) error {
120 tags = append(tags, t)
121 return nil
122 })
123 if err != nil {
124 return nil, err
125 }
126
127 return tags, nil
128}
129
130func (g *GitRepository) Branches() ([]*plumbing.Reference, error) {
131 bs, err := g.repository.Branches()
132 if err != nil {
133 return nil, err
134 }
135
136 branches := []*plumbing.Reference{}
137 err = bs.ForEach(func(ref *plumbing.Reference) error {
138 branches = append(branches, ref)
139 return nil
140 })
141 if err != nil {
142 return nil, err
143 }
144
145 return branches, nil
146}
147
148func (g *GitRepository) validateRef() error {
149 if !g.setRef {
150 return g.SetRef("")
151 }
152 return nil
153}