1package git
2
3import (
4 "path/filepath"
5 "strings"
6
7 git "github.com/aymanbagabas/git-module"
8)
9
10var (
11 // DiffMaxFiles is the maximum number of files to show in a diff.
12 DiffMaxFiles = 1000
13 // DiffMaxFileLines is the maximum number of lines to show in a file diff.
14 DiffMaxFileLines = 1000
15 // DiffMaxLineChars is the maximum number of characters to show in a line diff.
16 DiffMaxLineChars = 1000
17)
18
19// Repository is a wrapper around git.Repository with helper methods.
20type Repository struct {
21 *git.Repository
22 Path string
23 IsBare bool
24}
25
26// Clone clones a repository.
27func Clone(src, dst string, opts ...git.CloneOptions) error {
28 return git.Clone(src, dst, opts...)
29}
30
31// Init initializes and opens a new git repository.
32func Init(path string, bare bool) (*Repository, error) {
33 if bare {
34 path = strings.TrimSuffix(path, ".git") + ".git"
35 }
36
37 err := git.Init(path, git.InitOptions{Bare: bare})
38 if err != nil {
39 return nil, err
40 }
41 return Open(path)
42}
43
44func gitDir(r *git.Repository) (string, error) {
45 return r.RevParse("--git-dir")
46}
47
48// Open opens a git repository at the given path.
49func Open(path string) (*Repository, error) {
50 repo, err := git.Open(path)
51 if err != nil {
52 return nil, err
53 }
54 gp, err := gitDir(repo)
55 if err != nil || (gp != "." && gp != ".git") {
56 return nil, ErrNotAGitRepository
57 }
58 return &Repository{
59 Repository: repo,
60 Path: path,
61 IsBare: gp == ".",
62 }, nil
63}
64
65// HEAD returns the HEAD reference for a repository.
66func (r *Repository) HEAD() (*Reference, error) {
67 rn, err := r.Repository.SymbolicRef(git.SymbolicRefOptions{Name: "HEAD"})
68 if err != nil {
69 return nil, err
70 }
71 hash, err := r.Repository.ShowRefVerify(rn)
72 if err != nil {
73 return nil, err
74 }
75 return &Reference{
76 Reference: &git.Reference{
77 ID: hash,
78 Refspec: rn,
79 },
80 path: r.Path,
81 }, nil
82}
83
84// References returns the references for a repository.
85func (r *Repository) References() ([]*Reference, error) {
86 refs, err := r.Repository.ShowRef()
87 if err != nil {
88 return nil, err
89 }
90 rrefs := make([]*Reference, 0, len(refs))
91 for _, ref := range refs {
92 rrefs = append(rrefs, &Reference{
93 Reference: ref,
94 path: r.Path,
95 })
96 }
97 return rrefs, nil
98}
99
100// LsTree returns the tree for the given reference.
101func (r *Repository) LsTree(ref string) (*Tree, error) {
102 tree, err := r.Repository.LsTree(ref)
103 if err != nil {
104 return nil, err
105 }
106 return &Tree{
107 Tree: tree,
108 Path: "",
109 Repository: r,
110 }, nil
111}
112
113// Tree returns the tree for the given reference.
114func (r *Repository) Tree(ref *Reference) (*Tree, error) {
115 if ref == nil {
116 rref, err := r.HEAD()
117 if err != nil {
118 return nil, err
119 }
120 ref = rref
121 }
122 tree, err := r.Repository.LsTree(ref.Reference.ID)
123 if err != nil {
124 return nil, err
125 }
126 return &Tree{
127 Tree: tree,
128 Path: "",
129 Repository: r,
130 }, nil
131}
132
133// TreePath returns the tree for the given path.
134func (r *Repository) TreePath(ref *Reference, path string) (*Tree, error) {
135 path = filepath.Clean(path)
136 if path == "." {
137 path = ""
138 }
139 if path == "" {
140 return r.Tree(ref)
141 }
142 t, err := r.Tree(ref)
143 if err != nil {
144 return nil, err
145 }
146 return t.SubTree(path)
147}
148
149// Diff returns the diff for the given commit.
150func (r *Repository) Diff(commit *Commit) (*Diff, error) {
151 diff, err := r.Repository.Diff(commit.ID.String(), DiffMaxFiles, DiffMaxFileLines, DiffMaxLineChars, git.DiffOptions{
152 CommandOptions: git.CommandOptions{
153 Envs: []string{"GIT_CONFIG_GLOBAL=/dev/null"},
154 },
155 })
156 if err != nil {
157 return nil, err
158 }
159 return toDiff(diff), nil
160}
161
162// Patch returns the patch for the given reference.
163func (r *Repository) Patch(commit *Commit) (string, error) {
164 diff, err := r.Diff(commit)
165 if err != nil {
166 return "", err
167 }
168 return diff.Patch(), err
169}
170
171// CountCommits returns the number of commits in the repository.
172func (r *Repository) CountCommits(ref *Reference) (int64, error) {
173 return r.Repository.RevListCount([]string{ref.Name().String()})
174}
175
176// CommitsByPage returns the commits for a given page and size.
177func (r *Repository) CommitsByPage(ref *Reference, page, size int) (Commits, error) {
178 cs, err := r.Repository.CommitsByPage(ref.Name().String(), page, size)
179 if err != nil {
180 return nil, err
181 }
182 commits := make(Commits, len(cs))
183 copy(commits, cs)
184 return commits, nil
185}
186
187// SymbolicRef returns or updates the symbolic reference for the given name.
188// Both name and ref can be empty.
189func (r *Repository) SymbolicRef(name string, ref string, opts ...git.SymbolicRefOptions) (string, error) {
190 var opt git.SymbolicRefOptions
191 if len(opts) > 0 {
192 opt = opts[0]
193 }
194
195 opt.Name = name
196 opt.Ref = ref
197 return r.Repository.SymbolicRef(opt)
198}