summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/git/index.py55
-rw-r--r--test/git/test_index.py52
2 files changed, 101 insertions, 6 deletions
diff --git a/lib/git/index.py b/lib/git/index.py
index 04ba9130..930f2c75 100644
--- a/lib/git/index.py
+++ b/lib/git/index.py
@@ -422,6 +422,44 @@ class IndexFile(LazyMixin, diff.Diffable):
if file_path is not None:
self._file_path = file_path
+ @clear_cache
+ @default_index
+ def merge_tree(self, rhs, base=None):
+ """Merge the given rhs treeish into the current index, possibly taking
+ a common base treeish into account.
+
+ As opposed to the from_tree_ method, this allows you to use an already
+ existing tree as the left side of the merge
+
+ ``rhs``
+ treeish reference pointing to the 'other' side of the merge.
+
+ ``base``
+ optional treeish reference pointing to the common base of 'rhs' and
+ this index which equals lhs
+
+ Returns
+ self ( containing the merge and possibly unmerged entries in case of
+ conflicts )
+
+ Raise
+ GitCommandError in case there is a merge conflict. The error will
+ be raised at the first conflicting path. If you want to have proper
+ merge resolution to be done by yourself, you have to commit the changed
+ index ( or make a valid tree from it ) and retry with a three-way
+ index.from_tree call.
+ """
+ # -i : ignore working tree status
+ # --aggressive : handle more merge cases
+ # -m : do an actual merge
+ args = ["--aggressive", "-i", "-m"]
+ if base is not None:
+ args.append(base)
+ args.append(rhs)
+
+ self.repo.git.read_tree(args)
+ return self
+
@classmethod
def from_tree(cls, repo, *treeish, **kwargs):
"""
@@ -611,15 +649,18 @@ class IndexFile(LazyMixin, diff.Diffable):
return path_map
@classmethod
- def get_entries_key(cls, entry):
+ def get_entries_key(cls, *entry):
"""
Returns
Key suitable to be used for the index.entries dictionary
``entry``
- Instance of type BaseIndexEntry
+ One instance of type BaseIndexEntry or the path and the stage
"""
- return (entry.path, entry.stage)
+ if len(entry) == 1:
+ return (entry[0].path, entry[0].stage)
+ else:
+ return tuple(entry)
def resolve_blobs(self, iter_blobs):
@@ -677,11 +718,15 @@ class IndexFile(LazyMixin, diff.Diffable):
# allows to lazily reread on demand
return self
- def write_tree(self):
+ def write_tree(self, missing_ok=False):
"""
Writes the Index in self to a corresponding Tree file into the repository
object database and returns it as corresponding Tree object.
+ ``missing_ok``
+ If True, missing objects referenced by this index will not result
+ in an error.
+
Returns
Tree object representing this index
"""
@@ -689,7 +734,7 @@ class IndexFile(LazyMixin, diff.Diffable):
tmp_index_mover = _TemporaryFileSwap(index_path)
self.write(index_path)
- tree_sha = self.repo.git.write_tree()
+ tree_sha = self.repo.git.write_tree(missing_ok=missing_ok)
return Tree(self.repo, tree_sha, 0, '')
diff --git a/test/git/test_index.py b/test/git/test_index.py
index da5ad02c..0161e9ec 100644
--- a/test/git/test_index.py
+++ b/test/git/test_index.py
@@ -137,6 +137,57 @@ class TestTree(TestBase):
assert num_blobs == len(three_way_index.entries)
@with_rw_repo('0.1.6')
+ def test_index_merge_tree(self, rw_repo):
+ # SINGLE TREE MERGE
+ # current index is at the (virtual) cur_commit
+ next_commit = "4c39f9da792792d4e73fc3a5effde66576ae128c"
+ parent_commit = rw_repo.head.commit.parents[0]
+ manifest_key = IndexFile.get_entries_key('MANIFEST.in', 0)
+ manifest_entry = rw_repo.index.entries[manifest_key]
+ rw_repo.index.merge_tree(next_commit)
+ # only one change should be recorded
+ assert manifest_entry.sha != rw_repo.index.entries[manifest_key].sha
+
+ rw_repo.index.reset(rw_repo.head)
+ assert rw_repo.index.entries[manifest_key].sha == manifest_entry.sha
+
+ # FAKE MERGE
+ #############
+ # Add a change with a NULL sha that should conflict with next_commit. We
+ # pretend there was a change, but we do not even bother adding a proper
+ # sha for it ( which makes things faster of course )
+ manifest_fake_entry = BaseIndexEntry((manifest_entry[0], Diff.null_hex_sha, 0, manifest_entry[3]))
+ rw_repo.index.add([manifest_fake_entry])
+ # add actually resolves the null-hex-sha for us as a feature, but we can
+ # edit the index manually
+ assert rw_repo.index.entries[manifest_key].sha != Diff.null_hex_sha
+ # must operate on the same index for this ! Its a bit problematic as
+ # it might confuse people
+ index = rw_repo.index
+ index.entries[manifest_key] = IndexEntry.from_base(manifest_fake_entry)
+ index.write()
+ assert rw_repo.index.entries[manifest_key].sha == Diff.null_hex_sha
+
+ # a three way merge would result in a conflict and fails as the command will
+ # not overwrite any entries in our index and hence leave them unmerged. This is
+ # mainly a protection feature as the current index is not yet in a tree
+ self.failUnlessRaises(GitCommandError, index.merge_tree, next_commit, base=parent_commit)
+
+ # the only way to get the merged entries is to safe the current index away into a tree,
+ # which is like a temporary commit for us. This fails as well as the NULL sha deos not
+ # have a corresponding object
+ self.failUnlessRaises(GitCommandError, index.write_tree)
+
+ # if missing objects are okay, this would work though
+ tree = index.write_tree(missing_ok = True)
+
+ # now make a proper three way merge with unmerged entries
+ unmerged_tree = IndexFile.from_tree(rw_repo, parent_commit, tree, next_commit)
+ unmerged_blobs = unmerged_tree.unmerged_blobs()
+ assert len(unmerged_blobs) == 1 and unmerged_blobs.keys()[0] == manifest_key[0]
+
+
+ @with_rw_repo('0.1.6')
def test_index_file_diffing(self, rw_repo):
# default Index instance points to our index
index = IndexFile(rw_repo)
@@ -397,7 +448,6 @@ class TestTree(TestBase):
entries = index.reset(new_commit).add([link_file], fprogress=self._fprogress_add)
self._assert_fprogress(entries)
assert len(entries) == 1 and S_ISLNK(entries[0].mode)
- print "%o" % entries[0].mode
# END real symlink test
# add fake symlink and assure it checks-our as symlink