aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plumbing/difftree/difftree.go46
-rw-r--r--plumbing/difftree/difftree_test.go24
-rw-r--r--plumbing/object/tree.go11
-rw-r--r--plumbing/storer/reference.go2
-rw-r--r--plumbing/storer/reference_test.go24
5 files changed, 97 insertions, 10 deletions
diff --git a/plumbing/difftree/difftree.go b/plumbing/difftree/difftree.go
index 76c5f27..ff1ceaf 100644
--- a/plumbing/difftree/difftree.go
+++ b/plumbing/difftree/difftree.go
@@ -2,20 +2,19 @@ package difftree
import (
"bytes"
+ "os"
"srcd.works/go-git.v4/plumbing/object"
"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 *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
@@ -23,3 +22,42 @@ func DiffTree(a, b *object.Tree) ([]*Change, error) {
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(object.FileMode)
+ fileDeprecated = modeToBytes(object.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/difftree/difftree_test.go
index e2519b3..24817f1 100644
--- a/plumbing/difftree/difftree_test.go
+++ b/plumbing/difftree/difftree_test.go
@@ -1,6 +1,7 @@
package difftree
import (
+ "os"
"sort"
"testing"
@@ -353,3 +354,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..436ac32 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.
diff --git a/plumbing/storer/reference.go b/plumbing/storer/reference.go
index 692fe88..6c2de0d 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"),