Skip to content

Add support for diffing against root commit #408

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 19, 2016
3 changes: 3 additions & 0 deletions doc/source/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Changelog

1.0.3 - Fixes
=============

* `Commit.diff()` now supports diffing the root commit via `Commit.diff(NULL_TREE)`.
* `Repo.blame()` now respects `incremental=True`, supporting incremental blames. Incremental blames are slightly faster since they don't include the file's contents in them.
* IMPORTANT: This release drops support for python 2.6, which is officially deprecated by the python maintainers.

1.0.2 - Fixes
Expand Down
14 changes: 5 additions & 9 deletions git/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
# flake8: noqa

import sys
import six

from gitdb.utils.compat import (
PY3,
Expand All @@ -34,6 +33,7 @@ def bchr(n):
return bytes([n])
def mviter(d):
return d.values()
range = xrange
unicode = str
else:
FileType = file
Expand All @@ -44,21 +44,17 @@ def mviter(d):
byte_ord = ord
bchr = chr
unicode = unicode
range = xrange
def mviter(d):
return d.itervalues()

PRE_PY27 = sys.version_info < (2, 7)


def safe_decode(s):
"""Safely decodes a binary string to unicode"""
if isinstance(s, six.text_type):
if isinstance(s, unicode):
return s
elif isinstance(s, six.binary_type):
if PRE_PY27:
return s.decode(defenc) # we're screwed
else:
return s.decode(defenc, errors='replace')
elif isinstance(s, bytes):
return s.decode(defenc, errors='replace')
raise TypeError('Expected bytes or text, but got %r' % (s,))


Expand Down
21 changes: 16 additions & 5 deletions git/diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
)


__all__ = ('Diffable', 'DiffIndex', 'Diff')
__all__ = ('Diffable', 'DiffIndex', 'Diff', 'NULL_TREE')

# Special object to compare against the empty tree in diffs
NULL_TREE = object()


class Diffable(object):
Expand Down Expand Up @@ -49,6 +52,7 @@ def diff(self, other=Index, paths=None, create_patch=False, **kwargs):
If None, we will be compared to the working tree.
If Treeish, it will be compared against the respective tree
If Index ( type ), it will be compared against the index.
If git.NULL_TREE, it will compare against the empty tree.
It defaults to Index to assure the method will not by-default fail
on bare repositories.

Expand Down Expand Up @@ -87,10 +91,17 @@ def diff(self, other=Index, paths=None, create_patch=False, **kwargs):
if paths is not None and not isinstance(paths, (tuple, list)):
paths = [paths]

if other is not None and other is not self.Index:
args.insert(0, other)
diff_cmd = self.repo.git.diff
if other is self.Index:
args.insert(0, "--cached")
args.insert(0, '--cached')
elif other is NULL_TREE:
args.insert(0, '-r') # recursive diff-tree
args.insert(0, '--root')
diff_cmd = self.repo.git.diff_tree
elif other is not None:
args.insert(0, '-r') # recursive diff-tree
args.insert(0, other)
diff_cmd = self.repo.git.diff_tree

args.insert(0, self)

Expand All @@ -101,7 +112,7 @@ def diff(self, other=Index, paths=None, create_patch=False, **kwargs):
# END paths handling

kwargs['as_process'] = True
proc = self.repo.git.diff(*self._process_diff_args(args), **kwargs)
proc = diff_cmd(*self._process_diff_args(args), **kwargs)

diff_method = Diff._index_from_raw_format
if create_patch:
Expand Down
2 changes: 1 addition & 1 deletion git/repo/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@
defenc,
PY3,
safe_decode,
range,
)

import os
import sys
import re
from six.moves import range

DefaultDBType = GitCmdObjectDB
if sys.version_info[:2] < (2, 5): # python 2.4 compatiblity
Expand Down
10 changes: 10 additions & 0 deletions git/test/fixtures/diff_initial
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,7 @@
+=======
+CHANGES
+=======
+
+0.1.0
+=====
+initial release
20 changes: 18 additions & 2 deletions git/test/test_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
Repo,
GitCommandError,
Diff,
DiffIndex
DiffIndex,
NULL_TREE,
)


Expand Down Expand Up @@ -128,6 +129,21 @@ def test_diff_index_raw_format(self):
assert res[0].deleted_file
assert res[0].b_path == ''

def test_diff_initial_commit(self):
initial_commit = self.rorepo.commit('33ebe7acec14b25c5f84f35a664803fcab2f7781')

# Without creating a patch...
diff_index = initial_commit.diff(NULL_TREE)
assert diff_index[0].b_path == 'CHANGES'
assert diff_index[0].new_file
assert diff_index[0].diff == ''

# ...and with creating a patch
diff_index = initial_commit.diff(NULL_TREE, create_patch=True)
assert diff_index[0].b_path == 'CHANGES'
assert diff_index[0].new_file
assert diff_index[0].diff == fixture('diff_initial')

def test_diff_patch_format(self):
# test all of the 'old' format diffs for completness - it should at least
# be able to deal with it
Expand All @@ -149,7 +165,7 @@ def test_diff_interface(self):
diff_item = commit.tree
# END use tree every second item

for other in (None, commit.Index, commit.parents[0]):
for other in (None, NULL_TREE, commit.Index, commit.parents[0]):
for paths in (None, "CHANGES", ("CHANGES", "lib")):
for create_patch in range(2):
diff_index = diff_item.diff(other=other, paths=paths, create_patch=create_patch)
Expand Down
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
[tox]
envlist = py26,py27,py33,py34,flake8
envlist = py26,py27,py33,py34,py35,flake8

[testenv]
commands = nosetests {posargs}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
passenv = HOME

[testenv:cover]
commands = nosetests --with-coverage {posargs}
Expand Down