aboutsummaryrefslogtreecommitdiffstats
path: root/repository/git.go
diff options
context:
space:
mode:
Diffstat (limited to 'repository/git.go')
-rw-r--r--repository/git.go500
1 files changed, 0 insertions, 500 deletions
diff --git a/repository/git.go b/repository/git.go
deleted file mode 100644
index bc9d8772..00000000
--- a/repository/git.go
+++ /dev/null
@@ -1,500 +0,0 @@
-// Package repository contains helper methods for working with the Git repo.
-package repository
-
-import (
- "bytes"
- "fmt"
- "os"
- "path/filepath"
- "strings"
- "sync"
-
- "github.com/blevesearch/bleve"
- "github.com/go-git/go-billy/v5"
- "github.com/go-git/go-billy/v5/osfs"
-
- "github.com/MichaelMure/git-bug/util/lamport"
-)
-
-const (
- clockPath = "git-bug"
-)
-
-var _ ClockedRepo = &GitRepo{}
-var _ TestedRepo = &GitRepo{}
-
-// GitRepo represents an instance of a (local) git repository.
-type GitRepo struct {
- gitCli
- path string
-
- clocksMutex sync.Mutex
- clocks map[string]lamport.Clock
-
- indexesMutex sync.Mutex
- indexes map[string]bleve.Index
-
- keyring Keyring
-}
-
-// OpenGitRepo determines if the given working directory is inside of a git repository,
-// and returns the corresponding GitRepo instance if it is.
-func OpenGitRepo(path string, clockLoaders []ClockLoader) (*GitRepo, error) {
- k, err := defaultKeyring()
- if err != nil {
- return nil, err
- }
-
- repo := &GitRepo{
- gitCli: gitCli{path: path},
- path: path,
- clocks: make(map[string]lamport.Clock),
- indexes: make(map[string]bleve.Index),
- keyring: k,
- }
-
- // Check the repo and retrieve the root path
- stdout, err := repo.runGitCommand("rev-parse", "--absolute-git-dir")
-
- // Now dir is fetched with "git rev-parse --git-dir". May be it can
- // still return nothing in some cases. Then empty stdout check is
- // kept.
- if err != nil || stdout == "" {
- return nil, ErrNotARepo
- }
-
- // Fix the path to be sure we are at the root
- repo.path = stdout
- repo.gitCli.path = stdout
-
- for _, loader := range clockLoaders {
- allExist := true
- for _, name := range loader.Clocks {
- if _, err := repo.getClock(name); err != nil {
- allExist = false
- }
- }
-
- if !allExist {
- err = loader.Witnesser(repo)
- if err != nil {
- return nil, err
- }
- }
- }
-
- return repo, nil
-}
-
-// InitGitRepo create a new empty git repo at the given path
-func InitGitRepo(path string) (*GitRepo, error) {
- repo := &GitRepo{
- gitCli: gitCli{path: path},
- path: path + "/.git",
- clocks: make(map[string]lamport.Clock),
- indexes: make(map[string]bleve.Index),
- }
-
- _, err := repo.runGitCommand("init", path)
- if err != nil {
- return nil, err
- }
-
- return repo, nil
-}
-
-// InitBareGitRepo create a new --bare empty git repo at the given path
-func InitBareGitRepo(path string) (*GitRepo, error) {
- repo := &GitRepo{
- gitCli: gitCli{path: path},
- path: path,
- clocks: make(map[string]lamport.Clock),
- indexes: make(map[string]bleve.Index),
- }
-
- _, err := repo.runGitCommand("init", "--bare", path)
- if err != nil {
- return nil, err
- }
-
- return repo, nil
-}
-
-func (repo *GitRepo) Close() error {
- var firstErr error
- for _, index := range repo.indexes {
- err := index.Close()
- if err != nil && firstErr == nil {
- firstErr = err
- }
- }
- return firstErr
-}
-
-// LocalConfig give access to the repository scoped configuration
-func (repo *GitRepo) LocalConfig() Config {
- return newGitConfig(repo.gitCli, false)
-}
-
-// GlobalConfig give access to the global scoped configuration
-func (repo *GitRepo) GlobalConfig() Config {
- return newGitConfig(repo.gitCli, true)
-}
-
-// AnyConfig give access to a merged local/global configuration
-func (repo *GitRepo) AnyConfig() ConfigRead {
- return mergeConfig(repo.LocalConfig(), repo.GlobalConfig())
-}
-
-// Keyring give access to a user-wide storage for secrets
-func (repo *GitRepo) Keyring() Keyring {
- return repo.keyring
-}
-
-// GetPath returns the path to the repo.
-func (repo *GitRepo) GetPath() string {
- return repo.path
-}
-
-// GetUserName returns the name the the user has used to configure git
-func (repo *GitRepo) GetUserName() (string, error) {
- return repo.runGitCommand("config", "user.name")
-}
-
-// GetUserEmail returns the email address that the user has used to configure git.
-func (repo *GitRepo) GetUserEmail() (string, error) {
- return repo.runGitCommand("config", "user.email")
-}
-
-// GetCoreEditor returns the name of the editor that the user has used to configure git.
-func (repo *GitRepo) GetCoreEditor() (string, error) {
- return repo.runGitCommand("var", "GIT_EDITOR")
-}
-
-// GetRemotes returns the configured remotes repositories.
-func (repo *GitRepo) GetRemotes() (map[string]string, error) {
- stdout, err := repo.runGitCommand("remote", "--verbose")
- if err != nil {
- return nil, err
- }
-
- lines := strings.Split(stdout, "\n")
- remotes := make(map[string]string, len(lines))
-
- for _, line := range lines {
- if strings.TrimSpace(line) == "" {
- continue
- }
- elements := strings.Fields(line)
- if len(elements) != 3 {
- return nil, fmt.Errorf("git remote: unexpected output format: %s", line)
- }
-
- remotes[elements[0]] = elements[1]
- }
-
- return remotes, nil
-}
-
-// LocalStorage return a billy.Filesystem giving access to $RepoPath/.git/git-bug
-func (repo *GitRepo) LocalStorage() billy.Filesystem {
- return osfs.New(repo.path)
-}
-
-// GetBleveIndex return a bleve.Index that can be used to index documents
-func (repo *GitRepo) GetBleveIndex(name string) (bleve.Index, error) {
- repo.indexesMutex.Lock()
- defer repo.indexesMutex.Unlock()
-
- if index, ok := repo.indexes[name]; ok {
- return index, nil
- }
-
- path := filepath.Join(repo.path, "indexes", name)
-
- index, err := bleve.Open(path)
- if err == nil {
- repo.indexes[name] = index
- return index, nil
- }
-
- err = os.MkdirAll(path, os.ModeDir)
- if err != nil {
- return nil, err
- }
-
- mapping := bleve.NewIndexMapping()
- mapping.DefaultAnalyzer = "en"
-
- index, err = bleve.New(path, mapping)
- if err != nil {
- return nil, err
- }
-
- repo.indexes[name] = index
-
- return index, nil
-}
-
-// ClearBleveIndex will wipe the given index
-func (repo *GitRepo) ClearBleveIndex(name string) error {
- repo.indexesMutex.Lock()
- defer repo.indexesMutex.Unlock()
-
- path := filepath.Join(repo.path, "indexes", name)
-
- err := os.RemoveAll(path)
- if err != nil {
- return err
- }
-
- delete(repo.indexes, name)
-
- return nil
-}
-
-// FetchRefs fetch git refs from a remote
-func (repo *GitRepo) FetchRefs(remote, refSpec string) (string, error) {
- stdout, err := repo.runGitCommand("fetch", remote, refSpec)
-
- if err != nil {
- return stdout, fmt.Errorf("failed to fetch from the remote '%s': %v", remote, err)
- }
-
- return stdout, err
-}
-
-// PushRefs push git refs to a remote
-func (repo *GitRepo) PushRefs(remote string, refSpec string) (string, error) {
- stdout, stderr, err := repo.runGitCommandRaw(nil, "push", remote, refSpec)
-
- if err != nil {
- return stdout + stderr, fmt.Errorf("failed to push to the remote '%s': %v", remote, stderr)
- }
- return stdout + stderr, nil
-}
-
-// StoreData will store arbitrary data and return the corresponding hash
-func (repo *GitRepo) StoreData(data []byte) (Hash, error) {
- var stdin = bytes.NewReader(data)
-
- stdout, err := repo.runGitCommandWithStdin(stdin, "hash-object", "--stdin", "-w")
-
- return Hash(stdout), err
-}
-
-// ReadData will attempt to read arbitrary data from the given hash
-func (repo *GitRepo) ReadData(hash Hash) ([]byte, error) {
- var stdout bytes.Buffer
- var stderr bytes.Buffer
-
- err := repo.runGitCommandWithIO(nil, &stdout, &stderr, "cat-file", "-p", string(hash))
-
- if err != nil {
- return []byte{}, err
- }
-
- return stdout.Bytes(), nil
-}
-
-// StoreTree will store a mapping key-->Hash as a Git tree
-func (repo *GitRepo) StoreTree(entries []TreeEntry) (Hash, error) {
- buffer := prepareTreeEntries(entries)
-
- stdout, err := repo.runGitCommandWithStdin(&buffer, "mktree")
-
- if err != nil {
- return "", err
- }
-
- return Hash(stdout), nil
-}
-
-// StoreCommit will store a Git commit with the given Git tree
-func (repo *GitRepo) StoreCommit(treeHash Hash) (Hash, error) {
- stdout, err := repo.runGitCommand("commit-tree", string(treeHash))
-
- if err != nil {
- return "", err
- }
-
- return Hash(stdout), nil
-}
-
-// StoreCommitWithParent will store a Git commit with the given Git tree
-func (repo *GitRepo) StoreCommitWithParent(treeHash Hash, parent Hash) (Hash, error) {
- stdout, err := repo.runGitCommand("commit-tree", string(treeHash),
- "-p", string(parent))
-
- if err != nil {
- return "", err
- }
-
- return Hash(stdout), nil
-}
-
-// UpdateRef will create or update a Git reference
-func (repo *GitRepo) UpdateRef(ref string, hash Hash) error {
- _, err := repo.runGitCommand("update-ref", ref, string(hash))
-
- return err
-}
-
-// RemoveRef will remove a Git reference
-func (repo *GitRepo) RemoveRef(ref string) error {
- _, err := repo.runGitCommand("update-ref", "-d", ref)
-
- return err
-}
-
-// ListRefs will return a list of Git ref matching the given refspec
-func (repo *GitRepo) ListRefs(refPrefix string) ([]string, error) {
- stdout, err := repo.runGitCommand("for-each-ref", "--format=%(refname)", refPrefix)
-
- if err != nil {
- return nil, err
- }
-
- split := strings.Split(stdout, "\n")
-
- if len(split) == 1 && split[0] == "" {
- return []string{}, nil
- }
-
- return split, nil
-}
-
-// RefExist will check if a reference exist in Git
-func (repo *GitRepo) RefExist(ref string) (bool, error) {
- stdout, err := repo.runGitCommand("for-each-ref", ref)
-
- if err != nil {
- return false, err
- }
-
- return stdout != "", nil
-}
-
-// CopyRef will create a new reference with the same value as another one
-func (repo *GitRepo) CopyRef(source string, dest string) error {
- _, err := repo.runGitCommand("update-ref", dest, source)
-
- return err
-}
-
-// ListCommits will return the list of commit hashes of a ref, in chronological order
-func (repo *GitRepo) ListCommits(ref string) ([]Hash, error) {
- stdout, err := repo.runGitCommand("rev-list", "--first-parent", "--reverse", ref)
-
- if err != nil {
- return nil, err
- }
-
- split := strings.Split(stdout, "\n")
-
- casted := make([]Hash, len(split))
- for i, line := range split {
- casted[i] = Hash(line)
- }
-
- return casted, nil
-
-}
-
-// ReadTree will return the list of entries in a Git tree
-func (repo *GitRepo) ReadTree(hash Hash) ([]TreeEntry, error) {
- stdout, err := repo.runGitCommand("ls-tree", string(hash))
-
- if err != nil {
- return nil, err
- }
-
- return readTreeEntries(stdout)
-}
-
-// FindCommonAncestor will return the last common ancestor of two chain of commit
-func (repo *GitRepo) FindCommonAncestor(hash1 Hash, hash2 Hash) (Hash, error) {
- stdout, err := repo.runGitCommand("merge-base", string(hash1), string(hash2))
-
- if err != nil {
- return "", err
- }
-
- return Hash(stdout), nil
-}
-
-// GetTreeHash return the git tree hash referenced in a commit
-func (repo *GitRepo) GetTreeHash(commit Hash) (Hash, error) {
- stdout, err := repo.runGitCommand("rev-parse", string(commit)+"^{tree}")
-
- if err != nil {
- return "", err
- }
-
- return Hash(stdout), nil
-}
-
-// GetOrCreateClock return a Lamport clock stored in the Repo.
-// If the clock doesn't exist, it's created.
-func (repo *GitRepo) GetOrCreateClock(name string) (lamport.Clock, error) {
- repo.clocksMutex.Lock()
- defer repo.clocksMutex.Unlock()
-
- c, err := repo.getClock(name)
- if err == nil {
- return c, nil
- }
- if err != ErrClockNotExist {
- return nil, err
- }
-
- c, err = lamport.NewPersistedClock(repo.LocalStorage(), name+"-clock")
- if err != nil {
- return nil, err
- }
-
- repo.clocks[name] = c
- return c, nil
-}
-
-func (repo *GitRepo) getClock(name string) (lamport.Clock, error) {
- if c, ok := repo.clocks[name]; ok {
- return c, nil
- }
-
- c, err := lamport.LoadPersistedClock(repo.LocalStorage(), name+"-clock")
- if err == nil {
- repo.clocks[name] = c
- return c, nil
- }
- if err == lamport.ErrClockNotExist {
- return nil, ErrClockNotExist
- }
- return nil, err
-}
-
-// AddRemote add a new remote to the repository
-// Not in the interface because it's only used for testing
-func (repo *GitRepo) AddRemote(name string, url string) error {
- _, err := repo.runGitCommand("remote", "add", name, url)
-
- return err
-}
-
-// GetLocalRemote return the URL to use to add this repo as a local remote
-func (repo *GitRepo) GetLocalRemote() string {
- return repo.path
-}
-
-// EraseFromDisk delete this repository entirely from the disk
-func (repo *GitRepo) EraseFromDisk() error {
- err := repo.Close()
- if err != nil {
- return err
- }
-
- path := filepath.Clean(strings.TrimSuffix(repo.path, string(filepath.Separator)+".git"))
-
- // fmt.Println("Cleaning repo:", path)
- return os.RemoveAll(path)
-}