summaryrefslogtreecommitdiff
path: root/git/diff.py
diff options
context:
space:
mode:
Diffstat (limited to 'git/diff.py')
-rw-r--r--git/diff.py167
1 files changed, 84 insertions, 83 deletions
diff --git a/git/diff.py b/git/diff.py
index e90fc1cf..d8424e71 100644
--- a/git/diff.py
+++ b/git/diff.py
@@ -6,26 +6,28 @@
import re
from objects.blob import Blob
-from objects.util import mode_str_to_int
+from objects.util import mode_str_to_int
from exc import GitCommandError
from gitdb.util import hex_to_bin
-
+
__all__ = ('Diffable', 'DiffIndex', 'Diff')
-
+
+
class Diffable(object):
+
"""Common interface for all object that can be diffed against another object of compatible type.
-
- :note:
- Subclasses require a repo member as it is the case for Object instances, for practical
+
+ :note:
+ Subclasses require a repo member as it is the case for Object instances, for practical
reasons we do not derive from Object."""
__slots__ = tuple()
-
+
# standin indicating you want to diff against the index
class Index(object):
- pass
-
+ pass
+
def _process_diff_args(self, args):
"""
:return:
@@ -33,13 +35,13 @@ class Diffable(object):
Method is called right before git command execution.
Subclasses can use it to alter the behaviour of the superclass"""
return args
-
+
def diff(self, other=Index, paths=None, create_patch=False, **kwargs):
- """Creates diffs between two items being trees, trees and index or an
+ """Creates diffs between two items being trees, trees and index or an
index and the working tree.
:param other:
- Is the item to compare us with.
+ Is the item to compare us with.
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.
@@ -56,62 +58,63 @@ class Diffable(object):
and diffed.
:param kwargs:
- Additional arguments passed to git-diff, such as
+ Additional arguments passed to git-diff, such as
R=True to swap both sides of the diff.
:return: git.DiffIndex
-
+
:note:
Rename detection will only work if create_patch is True.
-
- On a bare repository, 'other' needs to be provided as Index or as
+
+ On a bare repository, 'other' needs to be provided as Index or as
as Tree/Commit, or a git command error will occour"""
args = list()
- args.append( "--abbrev=40" ) # we need full shas
- args.append( "--full-index" ) # get full index paths, not only filenames
-
+ args.append("--abbrev=40") # we need full shas
+ args.append("--full-index") # get full index paths, not only filenames
+
if create_patch:
args.append("-p")
args.append("-M") # check for renames
else:
args.append("--raw")
- # in any way, assure we don't see colored output,
+ # in any way, assure we don't see colored output,
# fixes https://github.com/gitpython-developers/GitPython/issues/172
args.append('--no-color')
-
- if paths is not None and not isinstance(paths, (tuple,list)):
- paths = [ paths ]
+
+ 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)
if other is self.Index:
args.insert(0, "--cached")
-
- args.insert(0,self)
-
+
+ args.insert(0, self)
+
# paths is list here or None
if paths:
args.append("--")
args.extend(paths)
# END paths handling
-
+
kwargs['as_process'] = True
proc = self.repo.git.diff(*self._process_diff_args(args), **kwargs)
-
+
diff_method = Diff._index_from_raw_format
if create_patch:
diff_method = Diff._index_from_patch_format
index = diff_method(self.repo, proc.stdout)
-
+
status = proc.wait()
return index
class DiffIndex(list):
- """Implements an Index for diffs, allowing a list of Diffs to be queried by
+
+ """Implements an Index for diffs, allowing a list of Diffs to be queried by
the diff properties.
-
+
The class improves the diff handling convenience"""
# change type invariant identifying possible ways a blob can have changed
# A = Added
@@ -119,23 +122,22 @@ class DiffIndex(list):
# R = Renamed
# M = modified
change_type = ("A", "D", "R", "M")
-
-
+
def iter_change_type(self, change_type):
"""
:return:
iterator yieling Diff instances that match the given change_type
-
+
:param change_type:
Member of DiffIndex.change_type, namely:
-
+
* 'A' for added paths
* 'D' for deleted paths
* 'R' for renamed paths
* 'M' for paths with modified data"""
if change_type not in self.change_type:
- raise ValueError( "Invalid change type: %s" % change_type )
-
+ raise ValueError("Invalid change type: %s" % change_type)
+
for diff in self:
if change_type == "A" and diff.new_file:
yield diff
@@ -146,37 +148,38 @@ class DiffIndex(list):
elif change_type == "M" and diff.a_blob and diff.b_blob and diff.a_blob != diff.b_blob:
yield diff
# END for each diff
-
+
class Diff(object):
+
"""A Diff contains diff information between two Trees.
-
- It contains two sides a and b of the diff, members are prefixed with
+
+ It contains two sides a and b of the diff, members are prefixed with
"a" and "b" respectively to inidcate that.
-
- Diffs keep information about the changed blob objects, the file mode, renames,
+
+ Diffs keep information about the changed blob objects, the file mode, renames,
deletions and new files.
-
+
There are a few cases where None has to be expected as member variable value:
-
+
``New File``::
-
+
a_mode is None
a_blob is None
-
+
``Deleted File``::
-
+
b_mode is None
b_blob is None
-
+
``Working Tree Blobs``
-
+
When comparing to working trees, the working tree blob will have a null hexsha
as a corresponding object does not yet exist. The mode will be null as well.
- But the path will be available though.
- If it is listed in a diff the working tree version of the file must
+ But the path will be available though.
+ If it is listed in a diff the working tree version of the file must
be different to the version in the index or tree, and hence has been modified."""
-
+
# precompiled regex
re_header = re.compile(r"""
#^diff[ ]--git
@@ -192,24 +195,24 @@ class Diff(object):
\.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
""", re.VERBOSE | re.MULTILINE)
# can be used for comparisons
- NULL_HEX_SHA = "0"*40
- NULL_BIN_SHA = "\0"*20
-
- __slots__ = ("a_blob", "b_blob", "a_mode", "b_mode", "new_file", "deleted_file",
+ NULL_HEX_SHA = "0" * 40
+ NULL_BIN_SHA = "\0" * 20
+
+ __slots__ = ("a_blob", "b_blob", "a_mode", "b_mode", "new_file", "deleted_file",
"rename_from", "rename_to", "diff")
def __init__(self, repo, a_path, b_path, a_blob_id, b_blob_id, a_mode,
b_mode, new_file, deleted_file, rename_from,
rename_to, diff):
-
+
self.a_mode = a_mode
self.b_mode = b_mode
-
+
if self.a_mode:
self.a_mode = mode_str_to_int(self.a_mode)
if self.b_mode:
self.b_mode = mode_str_to_int(self.b_mode)
-
+
if a_blob_id is None:
self.a_blob = None
else:
@@ -218,16 +221,15 @@ class Diff(object):
self.b_blob = None
else:
self.b_blob = Blob(repo, hex_to_bin(b_blob_id), mode=self.b_mode, path=b_path)
-
+
self.new_file = new_file
self.deleted_file = deleted_file
-
+
# be clear and use None instead of empty strings
self.rename_from = rename_from or None
self.rename_to = rename_to or None
-
- self.diff = diff
+ self.diff = diff
def __eq__(self, other):
for name in self.__slots__:
@@ -235,24 +237,24 @@ class Diff(object):
return False
# END for each name
return True
-
+
def __ne__(self, other):
- return not ( self == other )
-
+ return not (self == other)
+
def __hash__(self):
- return hash(tuple(getattr(self,n) for n in self.__slots__))
+ return hash(tuple(getattr(self, n) for n in self.__slots__))
def __str__(self):
h = "%s"
if self.a_blob:
h %= self.a_blob.path
- elif self.b_blob:
+ elif self.b_blob:
h %= self.b_blob.path
-
+
msg = ''
l = None # temp line
ll = 0 # line length
- for b,n in zip((self.a_blob, self.b_blob), ('lhs', 'rhs')):
+ for b, n in zip((self.a_blob, self.b_blob), ('lhs', 'rhs')):
if b:
l = "\n%s: %o | %s" % (n, b.mode, b.hexsha)
else:
@@ -261,10 +263,10 @@ class Diff(object):
ll = max(len(l), ll)
msg += l
# END for each blob
-
+
# add headline
- h += '\n' + '='*ll
-
+ h += '\n' + '=' * ll
+
if self.deleted_file:
msg += '\nfile deleted in rhs'
if self.new_file:
@@ -278,7 +280,7 @@ class Diff(object):
msg += self.diff
msg += '\n---'
# END diff info
-
+
return h + msg
@property
@@ -289,7 +291,7 @@ class Diff(object):
@classmethod
def _index_from_patch_format(cls, repo, stream):
"""Create a new DiffIndex from the given text which must be in patch format
- :param repo: is the repository we are operating on - it is required
+ :param repo: is the repository we are operating on - it is required
:param stream: result of 'git diff' as a stream (supporting file protocol)
:return: git.DiffIndex """
# for now, we have to bake the stream
@@ -310,15 +312,15 @@ class Diff(object):
new_file, deleted_file, rename_from, rename_to, diff[header.end():]))
return index
-
+
@classmethod
def _index_from_raw_format(cls, repo, stream):
"""Create a new DiffIndex from the given stream which must be in raw format.
- :note:
- This format is inherently incapable of detecting renames, hence we only
+ :note:
+ This format is inherently incapable of detecting renames, hence we only
modify, delete and add files
:return: git.DiffIndex"""
- # handles
+ # handles
# :100644 100644 6870991011cc8d9853a7a8a6f02061512c6a8190 37c5e30c879213e9ae83b21e9d11e55fc20c54b7 M .gitignore
index = DiffIndex()
for line in stream:
@@ -331,7 +333,7 @@ class Diff(object):
b_path = path
deleted_file = False
new_file = False
-
+
# NOTE: We cannot conclude from the existance of a blob to change type
# as diffs with the working do not have blobs yet
if change_type == 'D':
@@ -341,11 +343,10 @@ class Diff(object):
a_blob_id = None
new_file = True
# END add/remove handling
-
+
diff = Diff(repo, a_path, b_path, a_blob_id, b_blob_id, old_mode, new_mode,
new_file, deleted_file, None, None, '')
index.append(diff)
# END for each line
-
- return index
+ return index