1package git
2
3import (
4 "path/filepath"
5 "strings"
6
7 "github.com/gogs/git-module"
8)
9
10var (
11 // DiffMaxFile 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.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 Hash: Hash(hash),
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 Hash: Hash(ref.ID),
96 path: r.Path,
97 })
98 }
99 return rrefs, nil
100}
101
102// LsTree returns the tree for the given reference.
103func (r *Repository) LsTree(ref string) (*Tree, error) {
104 tree, err := r.Repository.LsTree(ref)
105 if err != nil {
106 return nil, err
107 }
108 return &Tree{
109 Tree: tree,
110 Path: "",
111 Repository: r,
112 }, nil
113}
114
115// Tree returns the tree for the given reference.
116func (r *Repository) Tree(ref *Reference) (*Tree, error) {
117 if ref == nil {
118 rref, err := r.HEAD()
119 if err != nil {
120 return nil, err
121 }
122 ref = rref
123 }
124 return r.LsTree(ref.Hash.String())
125}
126
127// TreePath returns the tree for the given path.
128func (r *Repository) TreePath(ref *Reference, path string) (*Tree, error) {
129 path = filepath.Clean(path)
130 if path == "." {
131 path = ""
132 }
133 if path == "" {
134 return r.Tree(ref)
135 }
136 t, err := r.Tree(ref)
137 if err != nil {
138 return nil, err
139 }
140 return t.SubTree(path)
141}
142
143// Diff returns the diff for the given commit.
144func (r *Repository) Diff(commit *Commit) (*Diff, error) {
145 ddiff, err := r.Repository.Diff(commit.Hash.String(), DiffMaxFiles, DiffMaxFileLines, DiffMaxLineChars, git.DiffOptions{
146 CommandOptions: git.CommandOptions{
147 Envs: []string{"GIT_CONFIG_GLOBAL=/dev/null"},
148 },
149 })
150 if err != nil {
151 return nil, err
152 }
153 files := make([]*DiffFile, 0, len(ddiff.Files))
154 for _, df := range ddiff.Files {
155 sections := make([]*DiffSection, 0, len(df.Sections))
156 for _, ds := range df.Sections {
157 sections = append(sections, &DiffSection{
158 DiffSection: ds,
159 })
160 }
161 files = append(files, &DiffFile{
162 DiffFile: df,
163 Sections: sections,
164 })
165 }
166 diff := &Diff{
167 Diff: ddiff,
168 Files: files,
169 }
170 return diff, nil
171}
172
173// Patch returns the patch for the given reference.
174func (r *Repository) Patch(commit *Commit) (string, error) {
175 diff, err := r.Diff(commit)
176 if err != nil {
177 return "", err
178 }
179 return diff.Patch(), err
180}
181
182// CountCommits returns the number of commits in the repository.
183func (r *Repository) CountCommits(ref *Reference) (int64, error) {
184 return r.RevListCount([]string{ref.Name().String()})
185}
186
187// CommitsByPage returns the commits for a given page and size.
188func (r *Repository) CommitsByPage(ref *Reference, page, size int) (Commits, error) {
189 cs, err := r.Repository.CommitsByPage(ref.Name().String(), page, size)
190 if err != nil {
191 return nil, err
192 }
193 commits := make(Commits, len(cs))
194 for i, c := range cs {
195 commits[i] = &Commit{
196 Commit: c,
197 Hash: Hash(c.ID.String()),
198 }
199 }
200 return commits, nil
201}
202
203// Config returns the config value for the given key.
204func (r *Repository) Config(key string, opts ...ConfigOptions) (string, error) {
205 dir, err := gitDir(r.Repository)
206 if err != nil {
207 return "", err
208 }
209 var opt ConfigOptions
210 if len(opts) > 0 {
211 opt = opts[0]
212 }
213 opt.File = filepath.Join(dir, "config")
214 return Config(key, opt)
215}
216
217// SetConfig sets the config value for the given key.
218func (r *Repository) SetConfig(key, value string, opts ...ConfigOptions) error {
219 dir, err := gitDir(r.Repository)
220 if err != nil {
221 return err
222 }
223 var opt ConfigOptions
224 if len(opts) > 0 {
225 opt = opts[0]
226 }
227 opt.File = filepath.Join(dir, "config")
228 return SetConfig(key, value, opt)
229}
230
231// SymbolicRef returns or updates the symbolic reference for the given name.
232// Both name and ref can be empty.
233func (r *Repository) SymbolicRef(name string, ref string, opts ...git.SymbolicRefOptions) (string, error) {
234 var opt git.SymbolicRefOptions
235 if len(opts) > 0 {
236 opt = opts[0]
237 }
238
239 opt.Name = name
240 opt.Ref = ref
241 return r.Repository.SymbolicRef(opt)
242}