diff options
author | Santiago M. Mola <santi@mola.io> | 2016-12-14 23:12:44 +0100 |
---|---|---|
committer | Máximo Cuadros <mcuadros@gmail.com> | 2016-12-14 23:12:44 +0100 |
commit | 0af572dd21c0aa79d13745b633ee24ba6c4d6cf1 (patch) | |
tree | 49e81e74e82d84fd88b2fc1e4b0dc7c7bfe9c40f /plumbing/revlist/revlist.go | |
parent | df0f38af83f972f026d7e14150f3d37b95f13484 (diff) | |
download | go-git-0af572dd21c0aa79d13745b633ee24ba6c4d6cf1.tar.gz |
move plumbing from top level package to plumbing (#183)
* plumbing: rename Object -> EncodedObject.
* plumbing/storer: rename ObjectStorer -> EncodedObjectStorer.
* move difftree to plumbing/difftree.
* move diff -> utils/diff
* make Object/Tag/Blob/Tree/Commit/File depend on storer.
* Object and its implementations now depend only on
storer.EncodedObjectStorer, not git.Repository.
* Tests are decoupled accordingly.
* move Object/Commit/File/Tag/Tree to plumbing/object.
* move Object/Commit/File/Tag/Tree to plumbing/object.
* move checkClose to utils/ioutil.
* move RevListObjects to plumbing/revlist.Objects.
* move DiffTree to plumbing/difftree package.
* rename files with plural nouns to singular
* plumbing/object: add GetBlob/GetCommit/GetTag/GetTree.
Diffstat (limited to 'plumbing/revlist/revlist.go')
-rw-r--r-- | plumbing/revlist/revlist.go | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/plumbing/revlist/revlist.go b/plumbing/revlist/revlist.go new file mode 100644 index 0000000..106e78b --- /dev/null +++ b/plumbing/revlist/revlist.go @@ -0,0 +1,128 @@ +// Package revlist implements functions to walk the objects referenced by a +// commit history. Roughly equivalent to git-rev-list command. +package revlist + +import ( + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/object" + "gopkg.in/src-d/go-git.v4/plumbing/storer" +) + +// Objects applies a complementary set. It gets all the hashes from all +// the reachable objects from the given commits. Ignore param are object hashes +// that we want to ignore on the result. It is a list because is +// easier to interact with other porcelain elements, but internally it is +// converted to a map. All that objects must be accessible from the object +// storer. +func Objects( + s storer.EncodedObjectStorer, + commits []*object.Commit, + ignore []plumbing.Hash) ([]plumbing.Hash, error) { + + seen := hashListToSet(ignore) + result := make(map[plumbing.Hash]bool) + for _, c := range commits { + err := reachableObjects(s, c, seen, func(h plumbing.Hash) error { + if !seen[h] { + result[h] = true + seen[h] = true + } + + return nil + }) + + if err != nil { + return nil, err + } + } + + return hashSetToList(result), nil +} + +// reachableObjects returns, using the callback function, all the reachable +// objects from the specified commit. To avoid to iterate over seen commits, +// if a commit hash is into the 'seen' set, we will not iterate all his trees +// and blobs objects. +func reachableObjects( + s storer.EncodedObjectStorer, + commit *object.Commit, + seen map[plumbing.Hash]bool, + cb func(h plumbing.Hash) error) error { + + return iterateCommits(commit, func(commit *object.Commit) error { + if seen[commit.Hash] { + return nil + } + + if err := cb(commit.Hash); err != nil { + return err + } + + return iterateCommitTrees(s, commit, func(h plumbing.Hash) error { + return cb(h) + }) + }) +} + +// iterateCommits iterate all reachable commits from the given one +func iterateCommits(commit *object.Commit, cb func(c *object.Commit) error) error { + if err := cb(commit); err != nil { + return err + } + + return object.WalkCommitHistory(commit, func(c *object.Commit) error { + return cb(c) + }) +} + +// iterateCommitTrees iterate all reachable trees from the given commit +func iterateCommitTrees( + s storer.EncodedObjectStorer, + commit *object.Commit, + cb func(h plumbing.Hash) error) error { + + tree, err := commit.Tree() + if err != nil { + return err + } + if err := cb(tree.Hash); err != nil { + return err + } + + treeWalker := object.NewTreeWalker(tree, true) + + for { + _, e, err := treeWalker.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + if err := cb(e.Hash); err != nil { + return err + } + } + + return nil +} + +func hashSetToList(hashes map[plumbing.Hash]bool) []plumbing.Hash { + var result []plumbing.Hash + for key := range hashes { + result = append(result, key) + } + + return result +} + +func hashListToSet(hashes []plumbing.Hash) map[plumbing.Hash]bool { + result := make(map[plumbing.Hash]bool) + for _, h := range hashes { + result[h] = true + } + + return result +} |