diff options
-rw-r--r-- | lib/git/errors.py | 4 | ||||
-rw-r--r-- | lib/git/index.py | 81 | ||||
-rw-r--r-- | test/git/test_index.py | 21 |
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") |