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

Fix issue 279 #285

Merged
merged 2 commits into from
Feb 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 42 additions & 4 deletions plumbing/difftree/difftree.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,62 @@ 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
}

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)
}
24 changes: 24 additions & 0 deletions plumbing/difftree/difftree_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package difftree

import (
"os"
"sort"
"testing"

Expand Down Expand Up @@ -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)
}
11 changes: 6 additions & 5 deletions plumbing/object/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down