diff options
author | Sebastian Thiel <byronimo@gmail.com> | 2009-11-24 11:28:11 +0100 |
---|---|---|
committer | Sebastian Thiel <byronimo@gmail.com> | 2009-11-24 11:28:11 +0100 |
commit | f6c8072daa942e9850b9d7a78bd2a9851c48cd2c (patch) | |
tree | 56a782b44479f42e1816bb571bc7dddf69538e32 | |
parent | f73385a5f62a211c19d90d3a7c06dcd693b3652d (diff) | |
download | gitpython-f6c8072daa942e9850b9d7a78bd2a9851c48cd2c.tar.gz |
index.merge_tree function added including test.
index.write_tree may now use the missing_ok argument which writes trees faster and allows you to operate without objects to do a true index based merge
-rw-r--r-- | lib/git/index.py | 55 | ||||
-rw-r--r-- | test/git/test_index.py | 52 |
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 |