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