diff options
-rw-r--r-- | plumbing/difftree/difftree.go | 46 | ||||
-rw-r--r-- | plumbing/difftree/difftree_test.go | 24 | ||||
-rw-r--r-- | plumbing/object/tree.go | 11 |
3 files changed, 72 insertions, 9 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. |