1package git
2
3import (
4 "path/filepath"
5
6 "github.com/gogs/git-module"
7)
8
9var (
10 // DiffMaxFile is the maximum number of files to show in a diff.
11 DiffMaxFiles = 1000
12 // DiffMaxFileLines is the maximum number of lines to show in a file diff.
13 DiffMaxFileLines = 1000
14 // DiffMaxLineChars is the maximum number of characters to show in a line diff.
15 DiffMaxLineChars = 1000
16)
17
18// Repository is a wrapper around git.Repository with helper methods.
19type Repository struct {
20 *git.Repository
21 Path string
22 IsBare bool
23}
24
25// Clone clones a repository.
26func Clone(src, dst string, opts ...git.CloneOptions) error {
27 return git.Clone(src, dst, opts...)
28}
29
30// Init initializes and opens a new git repository.
31func Init(path string, bare bool) (*Repository, error) {
32 err := git.Init(path, git.InitOptions{Bare: bare})
33 if err != nil {
34 return nil, err
35 }
36 return Open(path)
37}
38
39func isInsideWorkTree(r *git.Repository) bool {
40 out, err := r.RevParse("--is-inside-work-tree")
41 return err == nil && out == "true"
42}
43
44func isInsideGitDir(r *git.Repository) bool {
45 out, err := r.RevParse("--is-inside-git-dir")
46 return err == nil && out == "true"
47}
48
49func gitDir(r *git.Repository) (string, error) {
50 return r.RevParse("--git-dir")
51}
52
53// Open opens a git repository at the given path.
54func Open(path string) (*Repository, error) {
55 repo, err := git.Open(path)
56 if err != nil {
57 return nil, err
58 }
59 gp, err := gitDir(repo)
60 if err != nil || (gp != "." && gp != ".git") {
61 return nil, ErrNotAGitRepository
62 }
63 return &Repository{
64 Repository: repo,
65 Path: path,
66 IsBare: gp == ".",
67 }, nil
68}
69
70// Name returns the name of the repository.
71func (r *Repository) Name() string {
72 return filepath.Base(r.Path)
73}
74
75// HEAD returns the HEAD reference for a repository.
76func (r *Repository) HEAD() (*Reference, error) {
77 rn, err := r.SymbolicRef(git.SymbolicRefOptions{Name: "HEAD"})
78 if err != nil {
79 return nil, err
80 }
81 hash, err := r.ShowRefVerify(rn)
82 if err != nil {
83 return nil, err
84 }
85 return &Reference{
86 Reference: &git.Reference{
87 ID: hash,
88 Refspec: rn,
89 },
90 Hash: Hash(hash),
91 path: r.Path,
92 }, nil
93}
94
95// References returns the references for a repository.
96func (r *Repository) References() ([]*Reference, error) {
97 refs, err := r.ShowRef()
98 if err != nil {
99 return nil, err
100 }
101 rrefs := make([]*Reference, 0, len(refs))
102 for _, ref := range refs {
103 rrefs = append(rrefs, &Reference{
104 Reference: ref,
105 Hash: Hash(ref.ID),
106 path: r.Path,
107 })
108 }
109 return rrefs, nil
110}
111
112// Tree returns the tree for the given reference.
113func (r *Repository) Tree(ref *Reference) (*Tree, error) {
114 if ref == nil {
115 rref, err := r.HEAD()
116 if err != nil {
117 return nil, err
118 }
119 ref = rref
120 }
121 tree, err := r.LsTree(ref.Hash.String())
122 if err != nil {
123 return nil, err
124 }
125 return &Tree{
126 Tree: tree,
127 Path: "",
128 Repository: r,
129 }, nil
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.Repository.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}