soft-serve

git clone git://git.lin.moe/fork/soft-serve.git

  1package git
  2
  3import (
  4	"crypto/sha1"
  5	"hash"
  6	"path/filepath"
  7	"strings"
  8
  9	"github.com/aymanbagabas/git-module"
 10)
 11
 12var (
 13	// DiffMaxFile is the maximum number of files to show in a diff.
 14	DiffMaxFiles = 1000
 15	// DiffMaxFileLines is the maximum number of lines to show in a file diff.
 16	DiffMaxFileLines = 1000
 17	// DiffMaxLineChars is the maximum number of characters to show in a line diff.
 18	DiffMaxLineChars = 1000
 19)
 20
 21// Repository is a wrapper around git.Repository with helper methods.
 22type Repository struct {
 23	*git.Repository
 24	Path   string
 25	IsBare bool
 26}
 27
 28// Clone clones a repository.
 29func Clone(src, dst string, opts ...git.CloneOptions) error {
 30	return git.Clone(src, dst, opts...)
 31}
 32
 33// Init initializes and opens a new git repository.
 34func Init(path string, bare bool) (*Repository, error) {
 35	if bare {
 36		path = strings.TrimSuffix(path, ".git") + ".git"
 37	}
 38
 39	err := git.Init(path, git.InitOptions{Bare: bare})
 40	if err != nil {
 41		return nil, err
 42	}
 43	return Open(path)
 44}
 45
 46func gitDir(r *git.Repository) (string, error) {
 47	return r.RevParse("--git-dir")
 48}
 49
 50// Open opens a git repository at the given path.
 51func Open(path string) (*Repository, error) {
 52	repo, err := git.Open(path)
 53	if err != nil {
 54		return nil, err
 55	}
 56	gp, err := gitDir(repo)
 57	if err != nil || (gp != "." && gp != ".git") {
 58		return nil, ErrNotAGitRepository
 59	}
 60	return &Repository{
 61		Repository: repo,
 62		Path:       path,
 63		IsBare:     gp == ".",
 64	}, nil
 65}
 66
 67// HEAD returns the HEAD reference for a repository.
 68func (r *Repository) HEAD() (*Reference, error) {
 69	rn, err := r.Repository.SymbolicRef(git.SymbolicRefOptions{Name: "HEAD"})
 70	if err != nil {
 71		return nil, err
 72	}
 73	hash, err := r.ShowRefVerify(rn)
 74	if err != nil {
 75		return nil, err
 76	}
 77	return &Reference{
 78		Reference: &git.Reference{
 79			ID:      hash,
 80			Refspec: rn,
 81		},
 82		path: r.Path,
 83	}, nil
 84}
 85
 86// References returns the references for a repository.
 87func (r *Repository) References() ([]*Reference, error) {
 88	refs, err := r.ShowRef()
 89	if err != nil {
 90		return nil, err
 91	}
 92	rrefs := make([]*Reference, 0, len(refs))
 93	for _, ref := range refs {
 94		rrefs = append(rrefs, &Reference{
 95			Reference: ref,
 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.ID)
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	diff, err := r.Repository.Diff(commit.ID.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	return toDiff(diff), nil
154}
155
156// Patch returns the patch for the given reference.
157func (r *Repository) Patch(commit *Commit) (string, error) {
158	diff, err := r.Diff(commit)
159	if err != nil {
160		return "", err
161	}
162	return diff.Patch(), err
163}
164
165// CountCommits returns the number of commits in the repository.
166func (r *Repository) CountCommits(ref *Reference) (int64, error) {
167	return r.RevListCount([]string{ref.Name().String()})
168}
169
170// CommitsByPage returns the commits for a given page and size.
171func (r *Repository) CommitsByPage(ref *Reference, page, size int) (Commits, error) {
172	cs, err := r.Repository.CommitsByPage(ref.Name().String(), page, size)
173	if err != nil {
174		return nil, err
175	}
176	commits := make(Commits, len(cs))
177	copy(commits, cs)
178	return commits, nil
179}
180
181// SymbolicRef returns or updates the symbolic reference for the given name.
182// Both name and ref can be empty.
183func (r *Repository) SymbolicRef(name string, ref string, opts ...git.SymbolicRefOptions) (string, error) {
184	var opt git.SymbolicRefOptions
185	if len(opts) > 0 {
186		opt = opts[0]
187	}
188
189	opt.Name = name
190	opt.Ref = ref
191	return r.Repository.SymbolicRef(opt)
192}
193
194// Hasher returns the object format algorithem factory of repository
195// Only support sha1 currently
196func (r *Repository) Hasher() func() hash.Hash {
197	return sha1.New
198}