diff options
-rw-r--r-- | .travis.yml | 1 | ||||
-rw-r--r-- | plumbing/difftree/difftree.go | 25 | ||||
-rw-r--r-- | plumbing/object/change.go (renamed from plumbing/difftree/change.go) | 9 | ||||
-rw-r--r-- | plumbing/object/change_adaptor.go (renamed from plumbing/difftree/change_adaptor.go) | 11 | ||||
-rw-r--r-- | plumbing/object/change_adaptor_test.go (renamed from plumbing/difftree/change_adaptor_test.go) | 47 | ||||
-rw-r--r-- | plumbing/object/change_test.go (renamed from plumbing/difftree/change_test.go) | 19 | ||||
-rw-r--r-- | plumbing/object/difftree.go | 62 | ||||
-rw-r--r-- | plumbing/object/difftree_test.go (renamed from plumbing/difftree/difftree_test.go) | 42 | ||||
-rw-r--r-- | plumbing/object/tree.go | 16 | ||||
-rw-r--r-- | plumbing/object/treenoder.go (renamed from plumbing/difftree/treenoder.go) | 15 | ||||
-rw-r--r-- | plumbing/storer/reference.go | 2 | ||||
-rw-r--r-- | plumbing/storer/reference_test.go | 24 |
12 files changed, 181 insertions, 92 deletions
diff --git a/.travis.yml b/.travis.yml index 22c6629..e3c0085 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ env: - GIT_VERSION=v2.11.0 matrix: + fast_finish: true allow_failures: - go: tip diff --git a/plumbing/difftree/difftree.go b/plumbing/difftree/difftree.go deleted file mode 100644 index 76c5f27..0000000 --- a/plumbing/difftree/difftree.go +++ /dev/null @@ -1,25 +0,0 @@ -package difftree - -import ( - "bytes" - - "srcd.works/go-git.v4/plumbing/object" - "srcd.works/go-git.v4/utils/merkletrie" - "srcd.works/go-git.v4/utils/merkletrie/noder" -) - -func DiffTree(a, b *object.Tree) ([]*Change, error) { - from := newTreeNoder(a) - to := newTreeNoder(b) - - hashEqual := func(a, b noder.Hasher) bool { - return bytes.Equal(a.Hash(), b.Hash()) - } - - merkletrieChanges, err := merkletrie.DiffTree(from, to, hashEqual) - if err != nil { - return nil, err - } - - return newChanges(merkletrieChanges) -} diff --git a/plumbing/difftree/change.go b/plumbing/object/change.go index c636577..5e32e5b 100644 --- a/plumbing/difftree/change.go +++ b/plumbing/object/change.go @@ -1,11 +1,10 @@ -package difftree +package object import ( "bytes" "fmt" "strings" - "srcd.works/go-git.v4/plumbing/object" "srcd.works/go-git.v4/utils/merkletrie" ) @@ -39,7 +38,7 @@ func (c *Change) Action() (merkletrie.Action, error) { // Files return the files before and after a change. // For insertions from will be nil. For deletions to will be nil. -func (c *Change) Files() (from, to *object.File, err error) { +func (c *Change) Files() (from, to *File, err error) { action, err := c.Action() if err != nil { return @@ -84,9 +83,9 @@ type ChangeEntry struct { // Full path of the node using "/" as separator. Name string // Parent tree of the node that has changed. - Tree *object.Tree + Tree *Tree // The entry of the node. - TreeEntry object.TreeEntry + TreeEntry TreeEntry } // Changes represents a collection of changes between two git trees. diff --git a/plumbing/difftree/change_adaptor.go b/plumbing/object/change_adaptor.go index edc20b3..e97fd93 100644 --- a/plumbing/difftree/change_adaptor.go +++ b/plumbing/object/change_adaptor.go @@ -1,16 +1,15 @@ -package difftree - -// The folowing functions transform changes types form the merkletrie -// package to changes types from this package. +package object import ( "fmt" - "srcd.works/go-git.v4/plumbing/object" "srcd.works/go-git.v4/utils/merkletrie" "srcd.works/go-git.v4/utils/merkletrie/noder" ) +// The folowing functions transform changes types form the merkletrie +// package to changes types from this package. + func newChange(c merkletrie.Change) (*Change, error) { ret := &Change{} @@ -39,7 +38,7 @@ func newChangeEntry(p noder.Path) (ChangeEntry, error) { return ChangeEntry{ Name: p.String(), Tree: asTreeNoder.parent, - TreeEntry: object.TreeEntry{ + TreeEntry: TreeEntry{ Name: asTreeNoder.name, Mode: asTreeNoder.mode, Hash: asTreeNoder.hash, diff --git a/plumbing/difftree/change_adaptor_test.go b/plumbing/object/change_adaptor_test.go index 2ce2c54..fb2636b 100644 --- a/plumbing/difftree/change_adaptor_test.go +++ b/plumbing/object/change_adaptor_test.go @@ -1,11 +1,10 @@ -package difftree +package object import ( "os" "sort" "srcd.works/go-git.v4/plumbing" - "srcd.works/go-git.v4/plumbing/object" "srcd.works/go-git.v4/plumbing/storer" "srcd.works/go-git.v4/storage/filesystem" "srcd.works/go-git.v4/utils/merkletrie" @@ -29,8 +28,8 @@ func (s *ChangeAdaptorSuite) SetUpSuite(c *C) { s.Storer = sto } -func (s *ChangeAdaptorSuite) tree(c *C, h plumbing.Hash) *object.Tree { - t, err := object.GetTree(s.Storer, h) +func (s *ChangeAdaptorSuite) tree(c *C, h plumbing.Hash) *Tree { + t, err := GetTree(s.Storer, h) c.Assert(err, IsNil) return t } @@ -38,7 +37,7 @@ func (s *ChangeAdaptorSuite) tree(c *C, h plumbing.Hash) *object.Tree { var _ = Suite(&ChangeAdaptorSuite{}) // utility function to build Noders from a tree and an tree entry. -func newNoder(t *object.Tree, e object.TreeEntry) noder.Noder { +func newNoder(t *Tree, e TreeEntry) noder.Noder { return &treeNoder{ parent: t, name: e.Name, @@ -52,7 +51,7 @@ func newPath(nn ...noder.Noder) noder.Path { return noder.Path(nn) } func (s *ChangeAdaptorSuite) TestTreeNoderHashHasMode(c *C) { hash := plumbing.NewHash("aaaa") - mode := object.FileMode + mode := FileMode treeNoder := &treeNoder{ hash: hash, @@ -72,8 +71,8 @@ func (s *ChangeAdaptorSuite) TestTreeNoderHashHasMode(c *C) { } func (s *ChangeAdaptorSuite) TestNewChangeInsert(c *C) { - tree := &object.Tree{} - entry := object.TreeEntry{ + tree := &Tree{} + entry := TreeEntry{ Name: "name", Mode: os.FileMode(42), Hash: plumbing.NewHash("aaaaa"), @@ -98,8 +97,8 @@ func (s *ChangeAdaptorSuite) TestNewChangeInsert(c *C) { } func (s *ChangeAdaptorSuite) TestNewChangeDelete(c *C) { - tree := &object.Tree{} - entry := object.TreeEntry{ + tree := &Tree{} + entry := TreeEntry{ Name: "name", Mode: os.FileMode(42), Hash: plumbing.NewHash("aaaaa"), @@ -124,8 +123,8 @@ func (s *ChangeAdaptorSuite) TestNewChangeDelete(c *C) { } func (s *ChangeAdaptorSuite) TestNewChangeModify(c *C) { - treeA := &object.Tree{} - entryA := object.TreeEntry{ + treeA := &Tree{} + entryA := TreeEntry{ Name: "name", Mode: os.FileMode(42), Hash: plumbing.NewHash("aaaaa"), @@ -134,8 +133,8 @@ func (s *ChangeAdaptorSuite) TestNewChangeModify(c *C) { expectedFrom, err := newChangeEntry(pathA) c.Assert(err, IsNil) - treeB := &object.Tree{} - entryB := object.TreeEntry{ + treeB := &Tree{} + entryB := TreeEntry{ Name: "name", Mode: os.FileMode(42), Hash: plumbing.NewHash("bbbb"), @@ -293,8 +292,8 @@ func (s *ChangeAdaptorSuite) TestChangeEntryFromNilIsZero(c *C) { } func (s *ChangeAdaptorSuite) TestChangeEntryFromSortPath(c *C) { - tree := &object.Tree{} - entry := object.TreeEntry{ + tree := &Tree{} + entry := TreeEntry{ Name: "name", Mode: os.FileMode(42), Hash: plumbing.NewHash("aaaaa"), @@ -310,15 +309,15 @@ func (s *ChangeAdaptorSuite) TestChangeEntryFromSortPath(c *C) { } func (s *ChangeAdaptorSuite) TestChangeEntryFromLongPath(c *C) { - treeA := &object.Tree{} - entryA := object.TreeEntry{ + treeA := &Tree{} + entryA := TreeEntry{ Name: "nameA", Mode: os.FileMode(42), Hash: plumbing.NewHash("aaaa"), } - treeB := &object.Tree{} - entryB := object.TreeEntry{ + treeB := &Tree{} + entryB := TreeEntry{ Name: "nameB", Mode: os.FileMode(24), Hash: plumbing.NewHash("bbbb"), @@ -352,16 +351,16 @@ func (s *ChangeAdaptorSuite) TestNewChangesEmpty(c *C) { } func (s *ChangeAdaptorSuite) TestNewChanges(c *C) { - treeA := &object.Tree{} - entryA := object.TreeEntry{Name: "nameA"} + treeA := &Tree{} + entryA := TreeEntry{Name: "nameA"} pathA := newPath(newNoder(treeA, entryA)) changeA := merkletrie.Change{ From: nil, To: pathA, } - treeB := &object.Tree{} - entryB := object.TreeEntry{Name: "nameB"} + treeB := &Tree{} + entryB := TreeEntry{Name: "nameB"} pathB := newPath(newNoder(treeB, entryB)) changeB := merkletrie.Change{ From: pathB, diff --git a/plumbing/difftree/change_test.go b/plumbing/object/change_test.go index cda4ff9..b47e92a 100644 --- a/plumbing/difftree/change_test.go +++ b/plumbing/object/change_test.go @@ -1,11 +1,10 @@ -package difftree +package object import ( "os" "sort" "srcd.works/go-git.v4/plumbing" - "srcd.works/go-git.v4/plumbing/object" "srcd.works/go-git.v4/plumbing/storer" "srcd.works/go-git.v4/storage/filesystem" "srcd.works/go-git.v4/utils/merkletrie" @@ -29,8 +28,8 @@ func (s *ChangeSuite) SetUpSuite(c *C) { s.Storer = sto } -func (s *ChangeSuite) tree(c *C, h plumbing.Hash) *object.Tree { - t, err := object.GetTree(s.Storer, h) +func (s *ChangeSuite) tree(c *C, h plumbing.Hash) *Tree { + t, err := GetTree(s.Storer, h) c.Assert(err, IsNil) return t } @@ -58,7 +57,7 @@ func (s *ChangeSuite) TestInsert(c *C) { To: ChangeEntry{ Name: path, Tree: s.tree(c, tree), - TreeEntry: object.TreeEntry{ + TreeEntry: TreeEntry{ Name: name, Mode: mode, Hash: blob, @@ -103,7 +102,7 @@ func (s *ChangeSuite) TestDelete(c *C) { From: ChangeEntry{ Name: path, Tree: s.tree(c, tree), - TreeEntry: object.TreeEntry{ + TreeEntry: TreeEntry{ Name: name, Mode: mode, Hash: blob, @@ -153,7 +152,7 @@ func (s *ChangeSuite) TestModify(c *C) { From: ChangeEntry{ Name: path, Tree: s.tree(c, fromTree), - TreeEntry: object.TreeEntry{ + TreeEntry: TreeEntry{ Name: name, Mode: mode, Hash: fromBlob, @@ -162,7 +161,7 @@ func (s *ChangeSuite) TestModify(c *C) { To: ChangeEntry{ Name: path, Tree: s.tree(c, toTree), - TreeEntry: object.TreeEntry{ + TreeEntry: TreeEntry{ Name: name, Mode: mode, Hash: toBlob, @@ -226,7 +225,7 @@ func (s *ChangeSuite) TestErrorsFindingChildsAreDetected(c *C) { From: ChangeEntry{ Name: path, Tree: s.tree(c, fromTree), - TreeEntry: object.TreeEntry{ + TreeEntry: TreeEntry{ Name: name, Mode: mode, Hash: fromBlob, @@ -243,7 +242,7 @@ func (s *ChangeSuite) TestErrorsFindingChildsAreDetected(c *C) { To: ChangeEntry{ Name: path, Tree: s.tree(c, toTree), - TreeEntry: object.TreeEntry{ + TreeEntry: TreeEntry{ Name: name, Mode: mode, Hash: toBlob, diff --git a/plumbing/object/difftree.go b/plumbing/object/difftree.go new file mode 100644 index 0000000..e6e0cb0 --- /dev/null +++ b/plumbing/object/difftree.go @@ -0,0 +1,62 @@ +package object + +import ( + "bytes" + "os" + + "srcd.works/go-git.v4/utils/merkletrie" + "srcd.works/go-git.v4/utils/merkletrie/noder" +) + +// DiffTree compares the content and mode of the blobs found via two +// tree objects. +func DiffTree(a, b *Tree) (Changes, error) { + from := newTreeNoder(a) + to := newTreeNoder(b) + + merkletrieChanges, err := merkletrie.DiffTree(from, to, hashEqual) + if err != nil { + return nil, err + } + + return newChanges(merkletrieChanges) +} + +// check if the hash of the contents is different, if not, check if +// the permissions are different (but taking into account deprecated +// file modes). On a treenoder, the hash of the contents is codified +// in the first 20 bytes of the data returned by Hash() and the last +// 4 bytes is the mode. +func hashEqual(a, b noder.Hasher) bool { + hashA, hashB := a.Hash(), b.Hash() + contentsA, contentsB := hashA[:20], hashB[:20] + + sameContents := bytes.Equal(contentsA, contentsB) + if !sameContents { + return false + } + + modeA, modeB := hashA[20:], hashB[20:] + + return equivalentMode(modeA, modeB) +} + +func equivalentMode(a, b []byte) bool { + if isFilish(a) && isFilish(b) { + return true + } + return bytes.Equal(a, b) +} + +var ( + file = modeToBytes(FileMode) + fileDeprecated = modeToBytes(FileModeDeprecated) + // remove this by fixing plumbing.Object mode ASAP + fileGoGit = modeToBytes(os.FileMode(0644)) +) + +func isFilish(b []byte) bool { + return bytes.Equal(b, file) || + bytes.Equal(b, fileDeprecated) || + bytes.Equal(b, fileGoGit) +} diff --git a/plumbing/difftree/difftree_test.go b/plumbing/object/difftree_test.go index e2519b3..594c49d 100644 --- a/plumbing/difftree/difftree_test.go +++ b/plumbing/object/difftree_test.go @@ -1,12 +1,11 @@ -package difftree +package object import ( + "os" "sort" - "testing" "srcd.works/go-git.v4/plumbing" "srcd.works/go-git.v4/plumbing/format/packfile" - "srcd.works/go-git.v4/plumbing/object" "srcd.works/go-git.v4/plumbing/storer" "srcd.works/go-git.v4/storage/filesystem" "srcd.works/go-git.v4/storage/memory" @@ -16,8 +15,6 @@ import ( . "gopkg.in/check.v1" ) -func Test(t *testing.T) { TestingT(t) } - type DiffTreeSuite struct { fixtures.Suite Storer storer.EncodedObjectStorer @@ -35,9 +32,9 @@ func (s *DiffTreeSuite) SetUpSuite(c *C) { } func (s *DiffTreeSuite) commitFromStorer(c *C, sto storer.EncodedObjectStorer, - h plumbing.Hash) *object.Commit { + h plumbing.Hash) *Commit { - commit, err := object.GetCommit(sto, h) + commit, err := GetCommit(sto, h) c.Assert(err, IsNil) return commit } @@ -327,7 +324,7 @@ func (s *DiffTreeSuite) TestDiffTree(c *C) { f := fixtures.ByURL(t.repository).One() sto := s.storageFromPackfile(f) - var tree1, tree2 *object.Tree + var tree1, tree2 *Tree var err error if t.commit1 != "" { tree1, err = s.commitFromStorer(c, sto, @@ -346,6 +343,12 @@ func (s *DiffTreeSuite) TestDiffTree(c *C) { obtained, err := DiffTree(tree1, tree2) c.Assert(err, IsNil, Commentf("subtest %d: unable to calculate difftree: %s", i, err)) + obtainedFromMethod, err := tree1.Diff(tree2) + c.Assert(err, IsNil, + Commentf("subtest %d: unable to calculate difftree: %s. Result calling Diff method from Tree object returns an error", i, err)) + + c.Assert(obtained, DeepEquals, obtainedFromMethod) + c.Assert(equalChanges(obtained, t.expected, c), Equals, true, Commentf("subtest:%d\nrepo=%s\ncommit1=%s\ncommit2=%s\nexpected=%s\nobtained=%s", i, t.repository, t.commit1, t.commit2, t.expected, obtained)) @@ -353,3 +356,26 @@ func (s *DiffTreeSuite) TestDiffTree(c *C) { assertChanges(obtained, c) } } + +func (s *DiffTreeSuite) TestIssue279(c *C) { + // HashEqual should ignore files if the only change is from a 100664 + // mode to a 100644 or vice versa. + from := &treeNoder{ + hash: plumbing.NewHash("d08e895238bac36d8220586fdc28c27e1a7a76d3"), + mode: os.FileMode(0100664), + } + to := &treeNoder{ + hash: plumbing.NewHash("d08e895238bac36d8220586fdc28c27e1a7a76d3"), + mode: os.FileMode(0100644), + } + c.Assert(hashEqual(from, to), Equals, true) + c.Assert(hashEqual(to, from), Equals, true) + + // but should detect if the contents of the file also changed. + to = &treeNoder{ + hash: plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + mode: os.FileMode(0100644), + } + c.Assert(hashEqual(from, to), Equals, false) + c.Assert(hashEqual(to, from), Equals, false) +} diff --git a/plumbing/object/tree.go b/plumbing/object/tree.go index 27d8578..ef5e140 100644 --- a/plumbing/object/tree.go +++ b/plumbing/object/tree.go @@ -19,11 +19,12 @@ const ( maxTreeDepth = 1024 startingStackSize = 8 - FileMode os.FileMode = 0100644 - ExecutableMode os.FileMode = 0100755 - SubmoduleMode os.FileMode = 0160000 - SymlinkMode os.FileMode = 0120000 - TreeMode os.FileMode = 0040000 + FileMode os.FileMode = 0100644 + FileModeDeprecated os.FileMode = 0100664 + ExecutableMode os.FileMode = 0100755 + SubmoduleMode os.FileMode = 0160000 + SymlinkMode os.FileMode = 0120000 + TreeMode os.FileMode = 0040000 ) // New errors defined by this package. @@ -298,6 +299,11 @@ func (t *Tree) buildMap() { } } +// Diff returns a list of changes between this tree and the provided one +func (from *Tree) Diff(to *Tree) (Changes, error) { + return DiffTree(from, to) +} + // treeEntryIter facilitates iterating through the TreeEntry objects in a Tree. type treeEntryIter struct { t *Tree diff --git a/plumbing/difftree/treenoder.go b/plumbing/object/treenoder.go index c0ed948..73d96e2 100644 --- a/plumbing/difftree/treenoder.go +++ b/plumbing/object/treenoder.go @@ -1,4 +1,4 @@ -package difftree +package object // A treenoder is a helper type that wraps git trees into merkletrie // noders. @@ -14,19 +14,18 @@ import ( "os" "srcd.works/go-git.v4/plumbing" - "srcd.works/go-git.v4/plumbing/object" "srcd.works/go-git.v4/utils/merkletrie/noder" ) type treeNoder struct { - parent *object.Tree // the root node is its own parent - name string // empty string for the root node + parent *Tree // the root node is its own parent + name string // empty string for the root node mode os.FileMode hash plumbing.Hash children []noder.Noder // memoized } -func newTreeNoder(t *object.Tree) *treeNoder { +func newTreeNoder(t *Tree) *treeNoder { if t == nil { return &treeNoder{} } @@ -94,9 +93,9 @@ func (t *treeNoder) Children() ([]noder.Noder, error) { // Returns the children of a tree as treenoders. // Efficiency is key here. -func transformChildren(t *object.Tree) ([]noder.Noder, error) { +func transformChildren(t *Tree) ([]noder.Noder, error) { var err error - var e object.TreeEntry + var e TreeEntry // there will be more tree entries than children in the tree, // due to submodules and empty directories, but I think it is still @@ -104,7 +103,7 @@ func transformChildren(t *object.Tree) ([]noder.Noder, error) { // is bigger than needed. ret := make([]noder.Noder, 0, len(t.Entries)) - walker := object.NewTreeWalker(t, false) // don't recurse + walker := NewTreeWalker(t, false) // don't recurse // don't defer walker.Close() for efficiency reasons. for { _, e, err = walker.Next() diff --git a/plumbing/storer/reference.go b/plumbing/storer/reference.go index b408778..d821ecf 100644 --- a/plumbing/storer/reference.go +++ b/plumbing/storer/reference.go @@ -70,7 +70,7 @@ func (iter *ReferenceSliceIter) ForEach(cb func(*plumbing.Reference) error) erro return nil } - return nil + return err } } diff --git a/plumbing/storer/reference_test.go b/plumbing/storer/reference_test.go index f698820..ff7bd68 100644 --- a/plumbing/storer/reference_test.go +++ b/plumbing/storer/reference_test.go @@ -1,6 +1,7 @@ package storer import ( + "errors" "io" . "gopkg.in/check.v1" @@ -48,6 +49,29 @@ func (s *ReferenceSuite) TestReferenceSliceIterForEach(c *C) { c.Assert(count, Equals, 2) } +func (s *ReferenceSuite) TestReferenceSliceIterForEachError(c *C) { + slice := []*plumbing.Reference{ + plumbing.NewReferenceFromStrings("foo", "foo"), + plumbing.NewReferenceFromStrings("bar", "bar"), + } + + i := NewReferenceSliceIter(slice) + var count int + exampleErr := errors.New("SOME ERROR") + err := i.ForEach(func(r *plumbing.Reference) error { + c.Assert(r == slice[count], Equals, true) + count++ + if count == 2 { + return exampleErr + } + + return nil + }) + + c.Assert(err, Equals, exampleErr) + c.Assert(count, Equals, 2) +} + func (s *ReferenceSuite) TestReferenceSliceIterForEachStop(c *C) { slice := []*plumbing.Reference{ plumbing.NewReferenceFromStrings("foo", "foo"), |