diff options
Diffstat (limited to 'plumbing/object/difftree.go')
-rw-r--r-- | plumbing/object/difftree.go | 62 |
1 files changed, 62 insertions, 0 deletions
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) +} |