1package git23import (4 "path/filepath"5 "strings"67 "github.com/aymanbagabas/git-module"8)910var (11 // DiffMaxFile is the maximum number of files to show in a diff.12 DiffMaxFiles = 100013 // DiffMaxFileLines is the maximum number of lines to show in a file diff.14 DiffMaxFileLines = 100015 // DiffMaxLineChars is the maximum number of characters to show in a line diff.16 DiffMaxLineChars = 100017)1819// Repository is a wrapper around git.Repository with helper methods.20type Repository struct {21 *git.Repository22 Path string23 IsBare bool24}2526// Clone clones a repository.27func Clone(src, dst string, opts ...git.CloneOptions) error {28 return git.Clone(src, dst, opts...)29}3031// 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 }3637 err := git.Init(path, git.InitOptions{Bare: bare})38 if err != nil {39 return nil, err40 }41 return Open(path)42}4344func gitDir(r *git.Repository) (string, error) {45 return r.RevParse("--git-dir")46}4748// 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, err53 }54 gp, err := gitDir(repo)55 if err != nil || (gp != "." && gp != ".git") {56 return nil, ErrNotAGitRepository57 }58 return &Repository{59 Repository: repo,60 Path: path,61 IsBare: gp == ".",62 }, nil63}6465// 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, err70 }71 hash, err := r.ShowRefVerify(rn)72 if err != nil {73 return nil, err74 }75 return &Reference{76 Reference: &git.Reference{77 ID: hash,78 Refspec: rn,79 },80 path: r.Path,81 }, nil82}8384// 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, err89 }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, nil98}99100// 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, err105 }106 return &Tree{107 Tree: tree,108 Path: "",109 Repository: r,110 }, nil111}112113// 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, err119 }120 ref = rref121 }122 return r.LsTree(ref.ID)123}124125// 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, err137 }138 return t.SubTree(path)139}140141// 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, err150 }151 return toDiff(diff), nil152}153154// 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 "", err159 }160 return diff.Patch(), err161}162163// 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()})166}167168// 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, err173 }174 commits := make(Commits, len(cs))175 copy(commits, cs)176 return commits, nil177}178179// 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.SymbolicRefOptions183 if len(opts) > 0 {184 opt = opts[0]185 }186187 opt.Name = name188 opt.Ref = ref189 return r.Repository.SymbolicRef(opt)190}