aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--plumbing/difftree/difftree.go25
-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.go62
-rw-r--r--plumbing/object/difftree_test.go (renamed from plumbing/difftree/difftree_test.go)42
-rw-r--r--plumbing/object/tree.go16
-rw-r--r--plumbing/object/treenoder.go (renamed from plumbing/difftree/treenoder.go)15
-rw-r--r--plumbing/storer/reference.go2
-rw-r--r--plumbing/storer/reference_test.go24
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"),