Parent directory

repo.go

5589 bytes
  1package git
  2
  3import (
  4	gopath "path"
  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.
127// Git paths always use forward slashes regardless of OS.
128func (r *Repository) TreePath(ref *Reference, path string) (*Tree, error) {
129	path = gopath.Clean(filepath.ToSlash(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// DiffLimits bounds the amount of diff data Git parses for one request.
144type DiffLimits struct {
145	MaxFiles     int
146	MaxFileLines int
147	MaxLineChars int
148}
149
150// DiffWithLimits returns the diff for commit using request-local limits.
151// It does not modify the legacy package-level DiffMax* defaults.
152func (r *Repository) DiffWithLimits(commit *Commit, limits DiffLimits) (*Diff, error) {
153	diff, err := r.Repository.Diff(commit.ID.String(), limits.MaxFiles, limits.MaxFileLines, limits.MaxLineChars, git.DiffOptions{
154		CommandOptions: git.CommandOptions{
155			Envs: []string{"GIT_CONFIG_GLOBAL=/dev/null"},
156		},
157	})
158	if err != nil {
159		return nil, err
160	}
161	return toDiff(diff), nil
162}
163
164// Diff returns the diff for the given commit using the legacy default limits.
165func (r *Repository) Diff(commit *Commit) (*Diff, error) {
166	return r.DiffWithLimits(commit, DiffLimits{
167		MaxFiles:     DiffMaxFiles,
168		MaxFileLines: DiffMaxFileLines,
169		MaxLineChars: DiffMaxLineChars,
170	})
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	copy(commits, cs)
195	return commits, nil
196}
197
198// CommitByPath returns the newest commit reachable from ref that affects treePath.
199// The revision is the resolved reference object ID, never a caller-provided revision.
200func (r *Repository) CommitByPath(ref *Reference, treePath string) (*Commit, error) {
201	return r.Repository.CommitByRevision(ref.ID, git.CommitByRevisionOptions{
202		Path: treePath,
203		CommandOptions: git.CommandOptions{
204			Envs: []string{"GIT_CONFIG_GLOBAL=/dev/null"},
205		},
206	})
207}
208
209// SymbolicRef returns or updates the symbolic reference for the given name.
210// Both name and ref can be empty.
211func (r *Repository) SymbolicRef(name string, ref string, opts ...git.SymbolicRefOptions) (string, error) {
212	var opt git.SymbolicRefOptions
213	if len(opts) > 0 {
214		opt = opts[0]
215	}
216
217	opt.Name = name
218	opt.Ref = ref
219	return r.Repository.SymbolicRef(opt)
220}