summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO9
-rw-r--r--lib/git/diff.py37
-rw-r--r--lib/git/errors.py6
-rw-r--r--lib/git/index.py72
-rw-r--r--lib/git/objects/__init__.py1
-rw-r--r--test/git/test_diff.py13
-rw-r--r--test/git/test_index.py35
7 files changed, 152 insertions, 21 deletions
diff --git a/TODO b/TODO
index db77df67..3e743e65 100644
--- a/TODO
+++ b/TODO
@@ -58,6 +58,15 @@ Index
creating several tree objects, so in the end it might be slower.
Hmm, probably its okay to use the command unless we go c(++)
+
+Head.reset
+----------
+* Should better be an instance method. Problem was that there is no class specifying
+ the HEAD - in a way reset would always effect the active branch.
+ Probably it would be okay to have a special type called SymbolicReference
+ which represents items like HEAD. These could naturally carry the reset
+ instance method.
+
Remote
------
* 'push' method needs a test, a true test repository is required though, a fork
diff --git a/lib/git/diff.py b/lib/git/diff.py
index 9b884502..03e6709c 100644
--- a/lib/git/diff.py
+++ b/lib/git/diff.py
@@ -18,13 +18,18 @@ class Diffable(object):
"""
__slots__ = tuple()
- # subclasses provide additional arguments to the git-diff comamnd by supplynig
- # them in this tuple
- _diff_args = tuple()
-
- # Temporary standin for Index type until we have a real index type
+ # standin indicating you want to diff against the index
class Index(object):
pass
+
+ def _process_diff_args(self, args):
+ """
+ Returns
+ possibly altered version of the given args list.
+ 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):
"""
@@ -60,13 +65,13 @@ class Diffable(object):
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(self._diff_args[:])
+ args = list()
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
+ args.append("-M") # check for renames
else:
args.append("--raw")
@@ -87,7 +92,7 @@ class Diffable(object):
# END paths handling
kwargs['as_process'] = True
- proc = self.repo.git.diff(*args, **kwargs)
+ proc = self.repo.git.diff(*self._process_diff_args(args), **kwargs)
diff_method = Diff._index_from_raw_format
if create_patch:
@@ -96,7 +101,7 @@ class Diffable(object):
status = proc.wait()
if status != 0:
- raise GitCommandError("git-diff", status, proc.stderr )
+ raise GitCommandError(("git diff",)+tuple(args), status, proc.stderr.read())
return index
@@ -207,6 +212,20 @@ class Diff(object):
self.diff = diff
+
+ def __eq__(self, other):
+ for name in self.__slots__:
+ if getattr(self, name) != getattr(other, name):
+ return False
+ # END for each name
+ return True
+
+ def __ne__(self, other):
+ return not ( self == other )
+
+ def __hash__(self):
+ return hash(tuple(getattr(self,n) for n in self.__slots__))
+
@property
def renamed(self):
"""
diff --git a/lib/git/errors.py b/lib/git/errors.py
index 18c58073..cde2798a 100644
--- a/lib/git/errors.py
+++ b/lib/git/errors.py
@@ -25,8 +25,8 @@ class GitCommandError(Exception):
self.stderr = stderr
self.status = status
self.command = command
-
+
def __str__(self):
- return repr("'%s' returned exit status %d: %r" %
- (' '.join(self.command), self.status, str(self.stderr)))
+ return ("'%s' returned exit status %i: %s" %
+ (' '.join(str(i) for i in self.command), self.status, self.stderr))
diff --git a/lib/git/index.py b/lib/git/index.py
index 4eabab15..4217c9a2 100644
--- a/lib/git/index.py
+++ b/lib/git/index.py
@@ -14,9 +14,11 @@ import objects
import tempfile
import os
import stat
-from git.objects import Blob, Tree
+import git.diff as diff
+
+from git.objects import Blob, Tree, Object
from git.utils import SHA1Writer, LazyMixin, ConcurrentWriteOperation
-from git.diff import Diffable
+
class _TemporaryFileSwap(object):
"""
@@ -140,7 +142,7 @@ class IndexEntry(tuple):
return IndexEntry((time, time, 0, 0, blob.mode, 0, 0, blob.size, blob.id, 0, blob.path))
-class Index(LazyMixin):
+class Index(LazyMixin, diff.Diffable):
"""
Implements an Index that can be manipulated using a native implementation in
order to save git command function calls wherever possible.
@@ -154,7 +156,7 @@ class Index(LazyMixin):
The index contains an entries dict whose keys are tuples of type IndexEntry
to facilitate access.
"""
- __slots__ = ( "repo", "version", "entries", "_extension_data" )
+ __slots__ = ( "repo", "version", "entries", "_extension_data", "_is_default_index" )
_VERSION = 2 # latest version we support
S_IFGITLINK = 0160000
@@ -168,10 +170,13 @@ class Index(LazyMixin):
self.repo = repo
self.version = self._VERSION
self._extension_data = ''
+ self._is_default_index = True
if stream is not None:
+ self._is_default_index = False
self._read_from_stream(stream)
# END read from stream immediatly
+
def _set_cache_(self, attr):
if attr == "entries":
# read the current index
@@ -187,6 +192,18 @@ class Index(LazyMixin):
def _index_path(self):
return os.path.join(self.repo.path, "index")
+
+ @property
+ def path(self):
+ """
+ Returns
+ Path to the index file we are representing or None if we are
+ a loose index that was read from a stream.
+ """
+ if self._is_default_index:
+ return self._index_path()
+ return None
+
@classmethod
def _read_entry(cls, stream):
"""Return: One entry of the given stream"""
@@ -535,4 +552,51 @@ class Index(LazyMixin):
# END write tree handling
return Tree(self.repo, tree_sha, 0, '')
+
+ def _process_diff_args(self, args):
+ try:
+ args.pop(args.index(self))
+ except IndexError:
+ pass
+ # END remove self
+ return args
+
+ def diff(self, other=diff.Diffable.Index, paths=None, create_patch=False, **kwargs):
+ """
+ Diff this index against the working copy or a Tree or Commit object
+
+ For a documentation of the parameters and return values, see
+ Diffable.diff
+
+ Note
+ Will only work with indices that represent the default git index as
+ they have not been initialized with a stream.
+ """
+ if not self._is_default_index:
+ raise AssertionError( "Cannot diff custom indices as they do not represent the default git index" )
+
+ # index against index is always empty
+ if other is self.Index:
+ return diff.DiffIndex()
+
+ # index against anything but None is a reverse diff with the respective
+ # item. Handle existing -R flags properly. Transform strings to the object
+ # so that we can call diff on it
+ if isinstance(other, basestring):
+ other = Object.new(self.repo, other)
+ # END object conversion
+
+ if isinstance(other, Object):
+ # invert the existing R flag
+ cur_val = kwargs.get('R', False)
+ kwargs['R'] = not cur_val
+ return other.diff(self.Index, paths, create_patch, **kwargs)
+ # END diff against other item handlin
+
+ # if other is not None here, something is wrong
+ if other is not None:
+ raise ValueError( "other must be None, Diffable.Index, a Tree or Commit, was %r" % other )
+
+ # diff against working copy - can be handled by superclass natively
+ return super(Index, self).diff(other, paths, create_patch, **kwargs)
diff --git a/lib/git/objects/__init__.py b/lib/git/objects/__init__.py
index 39e650b7..192750e3 100644
--- a/lib/git/objects/__init__.py
+++ b/lib/git/objects/__init__.py
@@ -2,6 +2,7 @@
Import all submodules main classes into the package space
"""
import inspect
+from base import *
from tag import *
from blob import *
from tree import *
diff --git a/test/git/test_diff.py b/test/git/test_diff.py
index d7505987..ead231e5 100644
--- a/test/git/test_diff.py
+++ b/test/git/test_diff.py
@@ -59,6 +59,14 @@ class TestDiff(TestBase):
assertion_map.setdefault(key, 0)
assertion_map[key] = assertion_map[key]+len(list(diff_index.iter_change_type(ct)))
# END for each changetype
+
+ # check entries
+ diff_set = set()
+ diff_set.add(diff_index[0])
+ diff_set.add(diff_index[0])
+ assert len(diff_set) == 1
+ assert diff_index[0] == diff_index[0]
+ assert not (diff_index[0] != diff_index[0])
# END diff index checking
# END for each patch option
# END for each path option
@@ -71,7 +79,4 @@ class TestDiff(TestBase):
for key,value in assertion_map.items():
assert value, "Did not find diff for %s" % key
# END for each iteration type
-
- def test_diff_index_working_tree(self):
- self.fail("""Find a good way to diff an index against the working tree
-which is not possible with the current interface""")
+
diff --git a/test/git/test_index.py b/test/git/test_index.py
index 5c643a67..257acf10 100644
--- a/test/git/test_index.py
+++ b/test/git/test_index.py
@@ -102,12 +102,45 @@ class TestTree(TestBase):
assert num_blobs == len(three_way_index.entries)
@with_rw_repo('0.1.6')
- def test_from_index(self, rw_repo):
+ def test_from_index_and_diff(self, rw_repo):
# default Index instance points to our index
index = Index(rw_repo)
+ assert index.path is not None
assert len(index.entries)
# write the file back
index.write()
# could sha it, or check stats
+
+ # test diff
+ # resetting the head will leave the index in a different state, and the
+ # diff will yield a few changes
+ cur_head_commit = rw_repo.head.commit
+ ref = rw_repo.head.reset(rw_repo, 'HEAD~6', index=True, working_tree=False)
+
+ # diff against same index is 0
+ diff = index.diff()
+ assert len(diff) == 0
+
+ # against HEAD as string, must be the same as it matches index
+ diff = index.diff('HEAD')
+ assert len(diff) == 0
+
+ # against previous head, there must be a difference
+ diff = index.diff(cur_head_commit)
+ assert len(diff)
+
+ # we reverse the result
+ adiff = index.diff(str(cur_head_commit), R=True)
+ odiff = index.diff(cur_head_commit, R=False) # now its not reversed anymore
+ assert adiff != odiff
+ assert odiff == diff # both unreversed diffs against HEAD
+
+ # against working copy - its still at cur_commit
+ wdiff = index.diff(None)
+ assert wdiff != adiff
+ assert wdiff != odiff
+
+ # against something unusual
+ self.failUnlessRaises(ValueError, index.diff, int)