summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/git/errors.py4
-rw-r--r--lib/git/index.py81
-rw-r--r--test/git/test_index.py21
3 files changed, 101 insertions, 5 deletions
diff --git a/lib/git/errors.py b/lib/git/errors.py
index e9a637c0..18c58073 100644
--- a/lib/git/errors.py
+++ b/lib/git/errors.py
@@ -27,6 +27,6 @@ class GitCommandError(Exception):
self.command = command
def __str__(self):
- return repr("%s returned exit status %d" %
- (str(self.command), self.status))
+ return repr("'%s' returned exit status %d: %r" %
+ (' '.join(self.command), self.status, str(self.stderr)))
diff --git a/lib/git/index.py b/lib/git/index.py
index c137d4da..6b51c5c7 100644
--- a/lib/git/index.py
+++ b/lib/git/index.py
@@ -11,6 +11,8 @@ import struct
import binascii
import mmap
import objects
+import tempfile
+import os
class IndexEntry(tuple):
"""
@@ -115,7 +117,8 @@ class Index(object):
It provides custom merging facilities and to create custom commits.
``Entries``
- The index contains an entries dict whose keys are tuples of
+ The index contains an entries dict whose keys are tuples of type IndexEntry
+ to facilitate access.
"""
__slots__ = ( "version", "entries", "_extension_data" )
_VERSION = 2 # latest version we support
@@ -243,7 +246,6 @@ class Index(object):
real_size = ((stream.tell() - beginoffset + 8) & ~7)
stream.write("\0" * ((beginoffset + real_size) - stream.tell()))
-
def write(self, stream):
"""
Write the current state to the given stream
@@ -269,4 +271,77 @@ class Index(object):
# END for each entry
# write extension_data which we currently cannot interprete
stream.write(self._extension_data)
-
+
+
+ @classmethod
+ def from_tree(cls, repo, *treeish, **kwargs):
+ """
+ Merge the given treeish revisions into a new index which is returned.
+ The original index will remain unaltered
+
+ ``repo``
+ The repository treeish are located in.
+
+ ``*treeish``
+ One, two or three Tree Objects or Commits. The result changes according to the
+ amoutn of trees.
+ If 1 Tree is given, it will just be read into a new index
+ If 2 Trees are given, they will be merged into a new index using a
+ two way merge algorithm. Tree 1 is the 'current' tree, tree 2 is the 'other'
+ one.
+ If 3 Trees are given, a 3-way merge will be performed with the first tree
+ being the common ancestor of tree 2 and tree 3. Tree 2 is the 'current' tree,
+ tree 3 is the 'other' one
+
+ ``**kwargs``
+ Additional arguments passed to git-read-tree
+
+ Note:
+ In the three-way merge case, --aggressive will be specified to automatically
+ resolve more cases in a commonly correct manner. Specify trivial=True as kwarg
+ to override that.
+ """
+ if len(treeish) == 0 or len(treeish) > 3:
+ raise ValueError("Please specify between 1 and 3 treeish, got %i" % len(treeish))
+
+ arg_list = list()
+ # ignore that working tree and index possibly are out of date
+ if len(treeish)>1:
+ # drop unmerged entries when reading our index and merging
+ arg_list.append("--reset")
+ # handle non-trivial cases the way a real merge does
+ arg_list.append("--aggressive")
+ # END merge handling
+
+ # tmp file created in git home directory to be sure renaming
+ # works - /tmp/ dirs could be on another device
+ tmp_index = tempfile.mktemp('','',repo.path)
+ arg_list.append("--index-output=%s" % tmp_index)
+ arg_list.extend(treeish)
+
+ # move current index out of the way - otherwise the merge may fail
+ # as it considers existing entries. moving it essentially clears the index.
+ # Unfortunately there is no 'soft' way to do it
+ cur_index = os.path.join(repo.path, 'index')
+ moved_index = os.path.join(repo.path, 'index_moved'+tempfile.mktemp('','',''))
+ try:
+ os.rename(cur_index, moved_index)
+ repo.git.read_tree(*arg_list, **kwargs)
+ index = cls.from_file(tmp_index)
+ finally:
+ # put back the original index first !
+ if os.path.exists(moved_index):
+ os.rename(moved_index, cur_index)
+ if os.path.exists(tmp_index):
+ os.remove(tmp_index)
+ # END index merge handling
+
+ return index
+
+ def write_tree(self, stream):
+ """
+ Writes the
+ """
+ raise NotImplementedError("TODO")
+
+
diff --git a/test/git/test_index.py b/test/git/test_index.py
index 7a0e21eb..ead231d1 100644
--- a/test/git/test_index.py
+++ b/test/git/test_index.py
@@ -41,3 +41,24 @@ class TestTree(TestCase):
index_output.seek(0)
assert index_output.read() == fixture("index_merge")
+ def test_merge(self):
+ common_ancestor_sha = "5117c9c8a4d3af19a9958677e45cda9269de1541"
+ cur_sha = "4b43ca7ff72d5f535134241e7c797ddc9c7a3573"
+ other_sha = "39f85c4358b7346fee22169da9cad93901ea9eb9"
+
+ # simple index from tree
+ base_index = Index.from_tree(self.repo, common_ancestor_sha)
+ assert base_index.entries
+
+ # merge two trees
+ two_way_index = Index.from_tree(self.repo, common_ancestor_sha, cur_sha)
+ assert two_way_index.entries
+ for e in two_way_index.entries.values():
+ print "%i | %s" % ( e.stage, e.path )
+
+ # merge three trees - here we have a merge conflict
+ tree_way_index = Index.from_tree(self.repo, common_ancestor_sha, cur_sha, other_sha)
+ assert len(list(e for e in tree_way_index.entries.values() if e.stage != 0))
+
+ def test_custom_commit(self):
+ self.fail("Custom commit:write tree, make commit with custom parents")