1package git23import (4 "crypto/sha1"5 "hash"6 "path/filepath"7 "strings"89 "github.com/aymanbagabas/git-module"10)1112var (13 // DiffMaxFile is the maximum number of files to show in a diff.14 DiffMaxFiles = 100015 // DiffMaxFileLines is the maximum number of lines to show in a file diff.16 DiffMaxFileLines = 100017 // DiffMaxLineChars is the maximum number of characters to show in a line diff.18 DiffMaxLineChars = 100019)2021// Repository is a wrapper around git.Repository with helper methods.22type Repository struct {23 *git.Repository24 Path string25 IsBare bool26}2728// Clone clones a repository.29func Clone(src, dst string, opts ...git.CloneOptions) error {30 return git.Clone(src, dst, opts...)31}3233// 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 }3839 err := git.Init(path, git.InitOptions{Bare: bare})40 if err != nil {41 return nil, err42 }43 return Open(path)44}4546func gitDir(r *git.Repository) (string, error) {47 return r.RevParse("--git-dir")48}4950// 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, err55 }56 gp, err := gitDir(repo)57 if err != nil || (gp != "." && gp != ".git") {58 return nil, ErrNotAGitRepository59 }60 return &Repository{61 Repository: repo,62 Path: path,63 IsBare: gp == ".",64 }, nil65}6667// 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, err72 }73 hash, err := r.ShowRefVerify(rn)74 if err != nil {75 return nil, err76 }77 return &Reference{78 Reference: &git.Reference{79 ID: hash,80 Refspec: rn,81 },82 path: r.Path,83 }, nil84}8586// 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, err91 }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, nil100}101102// 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, err107 }108 return &Tree{109 Tree: tree,110 Path: "",111 Repository: r,112 }, nil113}114115// 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, err121 }122 ref = rref123 }124 return r.LsTree(ref.ID)125}126127// 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, err139 }140 return t.SubTree(path)141}142143// 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, err152 }153 return toDiff(diff), nil154}155156// 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 "", err161 }162 return diff.Patch(), err163}164165// 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}169170// 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, err175 }176 commits := make(Commits, len(cs))177 copy(commits, cs)178 return commits, nil179}180181// 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.SymbolicRefOptions185 if len(opts) > 0 {186 opt = opts[0]187 }188189 opt.Name = name190 opt.Ref = ref191 return r.Repository.SymbolicRef(opt)192}193194// Hasher returns the object format algorithem factory of repository195// Only support sha1 currently196func (r *Repository) Hasher() func() hash.Hash {197 return sha1.New198}