Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit c1e0932

Browse files
ajnavarrosmola
authored andcommitted
rev-list command implementation for objects (#135)
* rev-list command implementation for objects - Stateless method that with a commit list and a repository object get all the reachable objects, ignoring elements into ignore hash list. - Added tests using basic repository commit tree.
1 parent 9e34f68 commit c1e0932

File tree

2 files changed

+270
-0
lines changed

2 files changed

+270
-0
lines changed

revlist.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package git
2+
3+
import (
4+
"io"
5+
6+
"gopkg.in/src-d/go-git.v4/plumbing"
7+
)
8+
9+
// RevListObjects applies a complementary set. It gets all the hashes from all
10+
// the reachable objects from the given commits. Ignore param are object hashes
11+
// that we want to ignore on the result. It is a list because is
12+
// easier to interact with other porcelain elements, but internally it is
13+
// converted to a map. All that objects must be accessible from the Repository.
14+
func RevListObjects(
15+
r *Repository,
16+
commits []*Commit,
17+
ignore []plumbing.Hash) ([]plumbing.Hash, error) {
18+
19+
seen := hashListToSet(ignore)
20+
result := make(map[plumbing.Hash]bool)
21+
for _, c := range commits {
22+
err := reachableObjects(r, c, seen, func(h plumbing.Hash) error {
23+
if !seen[h] {
24+
result[h] = true
25+
seen[h] = true
26+
}
27+
28+
return nil
29+
})
30+
31+
if err != nil {
32+
return nil, err
33+
}
34+
}
35+
36+
return hashSetToList(result), nil
37+
}
38+
39+
// reachableObjects returns, using the callback function, all the reachable
40+
// objects from the specified commit. To avoid to iterate over seen commits,
41+
// if a commit hash is into the 'seen' set, we will not iterate all his trees
42+
// and blobs objects.
43+
func reachableObjects(
44+
r *Repository,
45+
commit *Commit,
46+
seen map[plumbing.Hash]bool,
47+
cb func(h plumbing.Hash) error) error {
48+
49+
return iterateCommits(commit, func(commit *Commit) error {
50+
if seen[commit.Hash] {
51+
return nil
52+
}
53+
54+
if err := cb(commit.Hash); err != nil {
55+
return err
56+
}
57+
58+
return iterateCommitTrees(r, commit, func(h plumbing.Hash) error {
59+
return cb(h)
60+
})
61+
})
62+
}
63+
64+
// iterateCommits iterate all reachable commits from the given one
65+
func iterateCommits(commit *Commit, cb func(c *Commit) error) error {
66+
if err := cb(commit); err != nil {
67+
return err
68+
}
69+
70+
return WalkCommitHistory(commit, func(c *Commit) error {
71+
return cb(c)
72+
})
73+
}
74+
75+
// iterateCommitTrees iterate all reachable trees from the given commit
76+
func iterateCommitTrees(
77+
repository *Repository,
78+
commit *Commit,
79+
cb func(h plumbing.Hash) error) error {
80+
81+
tree, err := commit.Tree()
82+
if err != nil {
83+
return err
84+
}
85+
if err := cb(tree.Hash); err != nil {
86+
return err
87+
}
88+
89+
treeWalker := NewTreeWalker(repository, tree, true)
90+
91+
for {
92+
_, e, err := treeWalker.Next()
93+
if err == io.EOF {
94+
break
95+
}
96+
if err != nil {
97+
return err
98+
}
99+
if err := cb(e.Hash); err != nil {
100+
return err
101+
}
102+
}
103+
104+
return nil
105+
}
106+
107+
func hashSetToList(hashes map[plumbing.Hash]bool) []plumbing.Hash {
108+
var result []plumbing.Hash
109+
for key := range hashes {
110+
result = append(result, key)
111+
}
112+
113+
return result
114+
}
115+
116+
func hashListToSet(hashes []plumbing.Hash) map[plumbing.Hash]bool {
117+
result := make(map[plumbing.Hash]bool)
118+
for _, h := range hashes {
119+
result[h] = true
120+
}
121+
122+
return result
123+
}

revlist_test.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package git
2+
3+
import (
4+
"gopkg.in/src-d/go-git.v4/fixtures"
5+
"gopkg.in/src-d/go-git.v4/plumbing"
6+
7+
. "gopkg.in/check.v1"
8+
)
9+
10+
type RevListSuite struct {
11+
BaseSuite
12+
r *Repository
13+
}
14+
15+
var _ = Suite(&RevListSuite{})
16+
17+
const (
18+
initialCommit = "b029517f6300c2da0f4b651b8642506cd6aaf45d"
19+
secondCommit = "b8e471f58bcbca63b07bda20e428190409c2db47"
20+
21+
someCommit = "918c48b83bd081e863dbe1b80f8998f058cd8294"
22+
someCommitBranch = "e8d3ffab552895c19b9fcf7aa264d277cde33881"
23+
someCommitOtherBranch = "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"
24+
)
25+
26+
// Created using: git log --graph --oneline --all
27+
//
28+
// Basic fixture repository commits tree:
29+
//
30+
// * 6ecf0ef vendor stuff
31+
// | * e8d3ffa some code in a branch
32+
// |/
33+
// * 918c48b some code
34+
// * af2d6a6 some json
35+
// * 1669dce Merge branch 'master'
36+
// |\
37+
// | * a5b8b09 Merge pull request #1
38+
// | |\
39+
// | | * b8e471f Creating changelog
40+
// | |/
41+
// * | 35e8510 binary file
42+
// |/
43+
// * b029517 Initial commit
44+
45+
func (s *RevListSuite) SetUpTest(c *C) {
46+
r, err := NewFilesystemRepository(fixtures.Basic().One().DotGit().Base())
47+
c.Assert(err, IsNil)
48+
s.r = r
49+
}
50+
51+
// ---
52+
// | |\
53+
// | | * b8e471f Creating changelog
54+
// | |/
55+
// * | 35e8510 binary file
56+
// |/
57+
// * b029517 Initial commit
58+
func (s *RevListSuite) TestRevListObjects(c *C) {
59+
revList := map[string]bool{
60+
"b8e471f58bcbca63b07bda20e428190409c2db47": true, // second commit
61+
"c2d30fa8ef288618f65f6eed6e168e0d514886f4": true, // init tree
62+
"d3ff53e0564a9f87d8e84b6e28e5060e517008aa": true, // CHANGELOG
63+
}
64+
65+
initCommit, err := s.r.Commit(plumbing.NewHash(initialCommit))
66+
c.Assert(err, IsNil)
67+
secondCommit, err := s.r.Commit(plumbing.NewHash(secondCommit))
68+
c.Assert(err, IsNil)
69+
70+
localHist, err := RevListObjects(s.r, []*Commit{initCommit}, nil)
71+
c.Assert(err, IsNil)
72+
73+
remoteHist, err := RevListObjects(s.r, []*Commit{secondCommit}, localHist)
74+
c.Assert(err, IsNil)
75+
76+
for _, h := range remoteHist {
77+
c.Assert(revList[h.String()], Equals, true)
78+
}
79+
c.Assert(len(remoteHist), Equals, len(revList))
80+
}
81+
82+
func (s *RevListSuite) TestRevListObjectsReverse(c *C) {
83+
initCommit, err := s.r.Commit(plumbing.NewHash(initialCommit))
84+
c.Assert(err, IsNil)
85+
86+
secondCommit, err := s.r.Commit(plumbing.NewHash(secondCommit))
87+
c.Assert(err, IsNil)
88+
89+
localHist, err := RevListObjects(s.r, []*Commit{secondCommit}, nil)
90+
c.Assert(err, IsNil)
91+
92+
remoteHist, err := RevListObjects(s.r, []*Commit{initCommit}, localHist)
93+
c.Assert(err, IsNil)
94+
95+
c.Assert(len(remoteHist), Equals, 0)
96+
}
97+
98+
func (s *RevListSuite) TestRevListObjectsSameCommit(c *C) {
99+
commit, err := s.r.Commit(plumbing.NewHash(secondCommit))
100+
c.Assert(err, IsNil)
101+
102+
localHist, err := RevListObjects(s.r, []*Commit{commit}, nil)
103+
c.Assert(err, IsNil)
104+
105+
remoteHist, err := RevListObjects(s.r, []*Commit{commit}, localHist)
106+
c.Assert(err, IsNil)
107+
108+
c.Assert(len(remoteHist), Equals, 0)
109+
}
110+
111+
// * 6ecf0ef vendor stuff
112+
// | * e8d3ffa some code in a branch
113+
// |/
114+
// * 918c48b some code
115+
// -----
116+
func (s *RevListSuite) TestRevListObjectsNewBranch(c *C) {
117+
someCommit, err := s.r.Commit(plumbing.NewHash(someCommit))
118+
c.Assert(err, IsNil)
119+
120+
someCommitBranch, err := s.r.Commit(plumbing.NewHash(someCommitBranch))
121+
c.Assert(err, IsNil)
122+
123+
someCommitOtherBranch, err := s.r.Commit(plumbing.NewHash(someCommitOtherBranch))
124+
c.Assert(err, IsNil)
125+
126+
localHist, err := RevListObjects(s.r, []*Commit{someCommit}, nil)
127+
c.Assert(err, IsNil)
128+
129+
remoteHist, err := RevListObjects(
130+
s.r, []*Commit{someCommitBranch, someCommitOtherBranch}, localHist)
131+
c.Assert(err, IsNil)
132+
133+
revList := map[string]bool{
134+
"a8d315b2b1c615d43042c3a62402b8a54288cf5c": true, // init tree
135+
"cf4aa3b38974fb7d81f367c0830f7d78d65ab86b": true, // vendor folder
136+
"9dea2395f5403188298c1dabe8bdafe562c491e3": true, // foo.go
137+
"e8d3ffab552895c19b9fcf7aa264d277cde33881": true, // branch commit
138+
"dbd3641b371024f44d0e469a9c8f5457b0660de1": true, // init tree
139+
"7e59600739c96546163833214c36459e324bad0a": true, // README
140+
"6ecf0ef2c2dffb796033e5a02219af86ec6584e5": true, // otherBranch commit
141+
}
142+
143+
for _, h := range remoteHist {
144+
c.Assert(revList[h.String()], Equals, true)
145+
}
146+
c.Assert(len(remoteHist), Equals, len(revList))
147+
}

0 commit comments

Comments
 (0)