repo.go

  1package git
  2
  3import (
  4	"path/filepath"
  5	"strings"
  6
  7	"github.com/aymanbagabas/git-module"
  8)
  9
 10var (
 11	// DiffMaxFiles 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...) //nolint:wrapcheck
 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 //nolint:wrapcheck
 40	}
 41	return Open(path)
 42}
 43
 44func gitDir(r *git.Repository) (string, error) {
 45	return r.RevParse("--git-dir") //nolint:wrapcheck
 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 //nolint:wrapcheck
 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 //nolint:wrapcheck
 70	}
 71	hash, err := r.ShowRefVerify(rn)
 72	if err != nil {
 73		return nil, err //nolint:wrapcheck
 74	}
 75	return &Reference{
 76		Reference: &git.Reference{
 77			ID:      hash,
 78			Refspec: rn,
 79		},
 80		path: r.Path,
 81	}, nil
 82}
 83
 84// References returns the references for a repository.
 85func (r *Repository) References() ([]*Reference, error) {
 86	refs, err := r.ShowRef()
 87	if err != nil {
 88		return nil, err //nolint:wrapcheck
 89	}
 90	rrefs := make([]*Reference, 0, len(refs))
 91	for _, ref := range refs {
 92		rrefs = append(rrefs, &Reference{
 93			Reference: ref,
 94			path:      r.Path,
 95		})
 96	}
 97	return rrefs, nil
 98}
 99
100// LsTree returns the tree for the given reference.
101func (r *Repository) LsTree(ref string) (*Tree, error) {
102	tree, err := r.Repository.LsTree(ref)
103	if err != nil {
104		return nil, err //nolint:wrapcheck
105	}
106	return &Tree{
107		Tree:       tree,
108		Path:       "",
109		Repository: r,
110	}, nil
111}
112
113// Tree returns the tree for the given reference.
114func (r *Repository) Tree(ref *Reference) (*Tree, error) {
115	if ref == nil {
116		rref, err := r.HEAD()
117		if err != nil {
118			return nil, err
119		}
120		ref = rref
121	}
122	return r.LsTree(ref.ID)
123}
124
125// TreePath returns the tree for the given path.
126func (r *Repository) TreePath(ref *Reference, path string) (*Tree, error) {
127	path = filepath.Clean(path)
128	if path == "." {
129		path = ""
130	}
131	if path == "" {
132		return r.Tree(ref)
133	}
134	t, err := r.Tree(ref)
135	if err != nil {
136		return nil, err
137	}
138	return t.SubTree(path)
139}
140
141// Diff returns the diff for the given commit.
142func (r *Repository) Diff(commit *Commit) (*Diff, error) {
143	diff, err := r.Repository.Diff(commit.ID.String(), DiffMaxFiles, DiffMaxFileLines, DiffMaxLineChars, git.DiffOptions{
144		CommandOptions: git.CommandOptions{
145			Envs: []string{"GIT_CONFIG_GLOBAL=/dev/null"},
146		},
147	})
148	if err != nil {
149		return nil, err //nolint:wrapcheck
150	}
151	return toDiff(diff), nil
152}
153
154// Patch returns the patch for the given reference.
155func (r *Repository) Patch(commit *Commit) (string, error) {
156	diff, err := r.Diff(commit)
157	if err != nil {
158		return "", err
159	}
160	return diff.Patch(), err
161}
162
163// CountCommits returns the number of commits in the repository.
164func (r *Repository) CountCommits(ref *Reference) (int64, error) {
165	return r.RevListCount([]string{ref.Name().String()}) //nolint:wrapcheck
166}
167
168// CommitsByPage returns the commits for a given page and size.
169func (r *Repository) CommitsByPage(ref *Reference, page, size int) (Commits, error) {
170	cs, err := r.Repository.CommitsByPage(ref.Name().String(), page, size)
171	if err != nil {
172		return nil, err //nolint:wrapcheck
173	}
174	commits := make(Commits, len(cs))
175	copy(commits, cs)
176	return commits, nil
177}
178
179// SymbolicRef returns or updates the symbolic reference for the given name.
180// Both name and ref can be empty.
181func (r *Repository) SymbolicRef(name string, ref string, opts ...git.SymbolicRefOptions) (string, error) {
182	var opt git.SymbolicRefOptions
183	if len(opts) > 0 {
184		opt = opts[0]
185	}
186
187	opt.Name = name
188	opt.Ref = ref
189	return r.Repository.SymbolicRef(opt) //nolint:wrapcheck
190}