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// Name returns the name of the repository.
66func (r *Repository) Name() string {
67 return filepath.Base(r.Path)
68}
69
70// HEAD returns the HEAD reference for a repository.
71func (r *Repository) HEAD() (*Reference, error) {
72 rn, err := r.Repository.SymbolicRef(git.SymbolicRefOptions{Name: "HEAD"})
73 if err != nil {
74 return nil, err
75 }
76 hash, err := r.ShowRefVerify(rn)
77 if err != nil {
78 return nil, err
79 }
80 return &Reference{
81 Reference: &git.Reference{
82 ID: hash,
83 Refspec: rn,
84 },
85 Hash: Hash(hash),
86 path: r.Path,
87 }, nil
88}
89
90// References returns the references for a repository.
91func (r *Repository) References() ([]*Reference, error) {
92 refs, err := r.ShowRef()
93 if err != nil {
94 return nil, err
95 }
96 rrefs := make([]*Reference, 0, len(refs))
97 for _, ref := range refs {
98 rrefs = append(rrefs, &Reference{
99 Reference: ref,
100 Hash: Hash(ref.ID),
101 path: r.Path,
102 })
103 }
104 return rrefs, nil
105}
106
107// LsTree returns the tree for the given reference.
108func (r *Repository) LsTree(ref string) (*Tree, error) {
109 tree, err := r.Repository.LsTree(ref)
110 if err != nil {
111 return nil, err
112 }
113 return &Tree{
114 Tree: tree,
115 Path: "",
116 Repository: r,
117 }, nil
118}
119
120// Tree returns the tree for the given reference.
121func (r *Repository) Tree(ref *Reference) (*Tree, error) {
122 if ref == nil {
123 rref, err := r.HEAD()
124 if err != nil {
125 return nil, err
126 }
127 ref = rref
128 }
129 return r.LsTree(ref.Hash.String())
130}
131
132// TreePath returns the tree for the given path.
133func (r *Repository) TreePath(ref *Reference, path string) (*Tree, error) {
134 path = filepath.Clean(path)
135 if path == "." {
136 path = ""
137 }
138 if path == "" {
139 return r.Tree(ref)
140 }
141 t, err := r.Tree(ref)
142 if err != nil {
143 return nil, err
144 }
145 return t.SubTree(path)
146}
147
148// Diff returns the diff for the given commit.
149func (r *Repository) Diff(commit *Commit) (*Diff, error) {
150 ddiff, err := r.Repository.Diff(commit.Hash.String(), DiffMaxFiles, DiffMaxFileLines, DiffMaxLineChars)
151 if err != nil {
152 return nil, err
153 }
154 files := make([]*DiffFile, 0, len(ddiff.Files))
155 for _, df := range ddiff.Files {
156 sections := make([]*DiffSection, 0, len(df.Sections))
157 for _, ds := range df.Sections {
158 sections = append(sections, &DiffSection{
159 DiffSection: ds,
160 })
161 }
162 files = append(files, &DiffFile{
163 DiffFile: df,
164 Sections: sections,
165 })
166 }
167 diff := &Diff{
168 Diff: ddiff,
169 Files: files,
170 }
171 return diff, nil
172}
173
174// Patch returns the patch for the given reference.
175func (r *Repository) Patch(commit *Commit) (string, error) {
176 diff, err := r.Diff(commit)
177 if err != nil {
178 return "", err
179 }
180 return diff.Patch(), err
181}
182
183// CountCommits returns the number of commits in the repository.
184func (r *Repository) CountCommits(ref *Reference) (int64, error) {
185 return r.RevListCount([]string{ref.Name().String()})
186}
187
188// CommitsByPage returns the commits for a given page and size.
189func (r *Repository) CommitsByPage(ref *Reference, page, size int) (Commits, error) {
190 cs, err := r.Repository.CommitsByPage(ref.Name().String(), page, size)
191 if err != nil {
192 return nil, err
193 }
194 commits := make(Commits, len(cs))
195 for i, c := range cs {
196 commits[i] = &Commit{
197 Commit: c,
198 Hash: Hash(c.ID.String()),
199 }
200 }
201 return commits, nil
202}
203
204// UpdateServerInfo updates the repository server info.
205func (r *Repository) UpdateServerInfo() error {
206 cmd := git.NewCommand("update-server-info")
207 _, err := cmd.RunInDir(r.Path)
208 return err
209}
210
211// Config returns the config value for the given key.
212func (r *Repository) Config(key string, opts ...ConfigOptions) (string, error) {
213 dir, err := gitDir(r.Repository)
214 if err != nil {
215 return "", err
216 }
217 var opt ConfigOptions
218 if len(opts) > 0 {
219 opt = opts[0]
220 }
221 opt.File = filepath.Join(dir, "config")
222 return Config(key, opt)
223}
224
225// SetConfig sets the config value for the given key.
226func (r *Repository) SetConfig(key, value string, opts ...ConfigOptions) error {
227 dir, err := gitDir(r.Repository)
228 if err != nil {
229 return err
230 }
231 var opt ConfigOptions
232 if len(opts) > 0 {
233 opt = opts[0]
234 }
235 opt.File = filepath.Join(dir, "config")
236 return SetConfig(key, value, opt)
237}
238
239// SymbolicRef returns or updates the symbolic reference for the given name.
240// Both name and ref can be empty.
241func (r *Repository) SymbolicRef(name string, ref string) (string, error) {
242 opt := git.SymbolicRefOptions{
243 Name: name,
244 Ref: ref,
245 }
246 return r.Repository.SymbolicRef(opt)
247}