diff options
Diffstat (limited to 'repository')
-rw-r--r-- | repository/git.go | 69 | ||||
-rw-r--r-- | repository/mock_repo.go | 118 | ||||
-rw-r--r-- | repository/repo.go | 51 |
3 files changed, 223 insertions, 15 deletions
diff --git a/repository/git.go b/repository/git.go index a55e451c..50806778 100644 --- a/repository/git.go +++ b/repository/git.go @@ -134,15 +134,26 @@ func (repo *GitRepo) StoreData(data []byte) (util.Hash, error) { return util.Hash(stdout), err } -// StoreTree will store a mapping key-->Hash as a Git tree -func (repo *GitRepo) StoreTree(mapping map[string]util.Hash) (util.Hash, error) { - var buffer bytes.Buffer +// ReadData will attempt to read arbitrary data from the given hash +func (repo *GitRepo) ReadData(hash util.Hash) ([]byte, error) { + var stdout bytes.Buffer + var stderr bytes.Buffer - for key, hash := range mapping { - buffer.WriteString(fmt.Sprintf("100644 blob %s\t%s\n", hash, key)) + 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) (util.Hash, error) { + buffer := prepareTreeEntries(entries) + stdout, err := repo.runGitCommandWithStdin(&buffer, "mktree") + if err != nil { return "", err } @@ -179,3 +190,51 @@ func (repo *GitRepo) UpdateRef(ref string, hash util.Hash) error { return err } + +// ListRefs will return a list of Git ref matching the given refspec +func (repo *GitRepo) ListRefs(refspec string) ([]string, error) { + // the format option will strip the ref name to keep only the last part (ie, the bug id) + stdout, err := repo.runGitCommand("for-each-ref", "--format=%(refname:lstrip=-1)", refspec) + + if err != nil { + return nil, err + } + + splitted := strings.Split(stdout, "\n") + + if len(splitted) == 1 && splitted[0] == "" { + return []string{}, nil + } + + return splitted, nil +} + +// ListCommits will return the list of commit hashes of a ref, in chronological order +func (repo *GitRepo) ListCommits(ref string) ([]util.Hash, error) { + stdout, err := repo.runGitCommand("rev-list", "--first-parent", "--reverse", ref) + + if err != nil { + return nil, err + } + + splitted := strings.Split(stdout, "\n") + + casted := make([]util.Hash, len(splitted)) + for i, line := range splitted { + casted[i] = util.Hash(line) + } + + return casted, nil + +} + +// ListEntries will return the list of entries in a Git tree +func (repo *GitRepo) ListEntries(hash util.Hash) ([]TreeEntry, error) { + stdout, err := repo.runGitCommand("ls-tree", string(hash)) + + if err != nil { + return nil, err + } + + return readTreeEntries(stdout) +} diff --git a/repository/mock_repo.go b/repository/mock_repo.go index f9b070b4..f526c3dc 100644 --- a/repository/mock_repo.go +++ b/repository/mock_repo.go @@ -1,14 +1,32 @@ package repository import ( + "crypto/sha1" + "fmt" "github.com/MichaelMure/git-bug/util" + "github.com/pkg/errors" ) // mockRepoForTest defines an instance of Repo that can be used for testing. -type mockRepoForTest struct{} +type mockRepoForTest struct { + blobs map[util.Hash][]byte + trees map[util.Hash]string + commits map[util.Hash]commit + refs map[string]util.Hash +} + +type commit struct { + treeHash util.Hash + parent util.Hash +} func NewMockRepoForTest() Repo { - return &mockRepoForTest{} + return &mockRepoForTest{ + blobs: make(map[util.Hash][]byte), + trees: make(map[util.Hash]string), + commits: make(map[util.Hash]commit), + refs: make(map[string]util.Hash), + } } // GetPath returns the path to the repo. @@ -39,22 +57,106 @@ func (r *mockRepoForTest) PullRefs(remote string, refPattern string, remoteRefPa return nil } -func (r *mockRepoForTest) StoreData([]byte) (util.Hash, error) { - return "", nil +func (r *mockRepoForTest) StoreData(data []byte) (util.Hash, error) { + rawHash := sha1.Sum(data) + hash := util.Hash(fmt.Sprintf("%x", rawHash)) + r.blobs[hash] = data + return hash, nil +} + +func (r *mockRepoForTest) ReadData(hash util.Hash) ([]byte, error) { + data, ok := r.blobs[hash] + + if !ok { + return nil, errors.New("unknown hash") + } + + return data, nil } -func (r *mockRepoForTest) StoreTree(mapping map[string]util.Hash) (util.Hash, error) { - return "", nil +func (r *mockRepoForTest) StoreTree(entries []TreeEntry) (util.Hash, error) { + buffer := prepareTreeEntries(entries) + rawHash := sha1.Sum(buffer.Bytes()) + hash := util.Hash(fmt.Sprintf("%x", rawHash)) + r.trees[hash] = buffer.String() + + return hash, nil } func (r *mockRepoForTest) StoreCommit(treeHash util.Hash) (util.Hash, error) { - return "", nil + rawHash := sha1.Sum([]byte(treeHash)) + hash := util.Hash(fmt.Sprintf("%x", rawHash)) + r.commits[hash] = commit{ + treeHash: treeHash, + } + return hash, nil } func (r *mockRepoForTest) StoreCommitWithParent(treeHash util.Hash, parent util.Hash) (util.Hash, error) { - return "", nil + rawHash := sha1.Sum([]byte(treeHash + parent)) + hash := util.Hash(fmt.Sprintf("%x", rawHash)) + r.commits[hash] = commit{ + treeHash: treeHash, + parent: parent, + } + return hash, nil } func (r *mockRepoForTest) UpdateRef(ref string, hash util.Hash) error { + r.refs[ref] = hash return nil } + +func (r *mockRepoForTest) ListRefs(refspec string) ([]string, error) { + keys := make([]string, len(r.refs)) + + i := 0 + for k := range r.refs { + keys[i] = k + i++ + } + + return keys, nil +} + +func (r *mockRepoForTest) ListCommits(ref string) ([]util.Hash, error) { + var hashes []util.Hash + + hash := r.refs[ref] + + for { + commit, ok := r.commits[hash] + + if !ok { + break + } + + hashes = append([]util.Hash{hash}, hashes...) + hash = commit.parent + } + + return hashes, nil +} + +func (r *mockRepoForTest) ListEntries(hash util.Hash) ([]TreeEntry, error) { + var data string + + data, ok := r.trees[hash] + + if !ok { + // Git will understand a commit hash to reach a tree + commit, ok := r.commits[hash] + + if !ok { + return nil, errors.New("unknown hash") + } + + data, ok = r.trees[commit.treeHash] + + if !ok { + return nil, errors.New("unknown hash") + } + } + + return readTreeEntries(data) +} diff --git a/repository/repo.go b/repository/repo.go index a58f35d9..26fe0fa6 100644 --- a/repository/repo.go +++ b/repository/repo.go @@ -1,7 +1,11 @@ // Package repository contains helper methods for working with a Git repo. package repository -import "github.com/MichaelMure/git-bug/util" +import ( + "bytes" + "github.com/MichaelMure/git-bug/util" + "strings" +) // Repo represents a source code repository. type Repo interface { @@ -26,8 +30,11 @@ type Repo interface { // StoreData will store arbitrary data and return the corresponding hash StoreData(data []byte) (util.Hash, error) + // ReadData will attempt to read arbitrary data from the given hash + ReadData(hash util.Hash) ([]byte, error) + // StoreTree will store a mapping key-->Hash as a Git tree - StoreTree(mapping map[string]util.Hash) (util.Hash, error) + StoreTree(mapping []TreeEntry) (util.Hash, error) // StoreCommit will store a Git commit with the given Git tree StoreCommit(treeHash util.Hash) (util.Hash, error) @@ -37,4 +44,44 @@ type Repo interface { // UpdateRef will create or update a Git reference UpdateRef(ref string, hash util.Hash) error + + // ListRefs will return a list of Git ref matching the given refspec + ListRefs(refspec string) ([]string, error) + + // ListCommits will return the list of tree hashes of a ref, in chronological order + ListCommits(ref string) ([]util.Hash, error) + + // ListEntries will return the list of entries in a Git tree + ListEntries(hash util.Hash) ([]TreeEntry, error) +} + +func prepareTreeEntries(entries []TreeEntry) bytes.Buffer { + var buffer bytes.Buffer + + for _, entry := range entries { + buffer.WriteString(entry.Format()) + } + + return buffer +} + +func readTreeEntries(s string) ([]TreeEntry, error) { + splitted := strings.Split(s, "\n") + + casted := make([]TreeEntry, len(splitted)) + for i, line := range splitted { + if line == "" { + continue + } + + entry, err := ParseTreeEntry(line) + + if err != nil { + return nil, err + } + + casted[i] = entry + } + + return casted, nil } |