diff options
author | Sebastian Thiel <byronimo@gmail.com> | 2010-06-23 13:49:05 +0200 |
---|---|---|
committer | Sebastian Thiel <byronimo@gmail.com> | 2010-06-23 13:49:05 +0200 |
commit | aea0243840a46021e6f77c759c960a06151d91c9 (patch) | |
tree | 54009e17043813dbab91df474e78b364be0e8f4c | |
parent | c0ef65b43688b1a4615a1e7332f6215f9a8abb19 (diff) | |
download | gitpython-aea0243840a46021e6f77c759c960a06151d91c9.tar.gz |
Added test for aggressive_tree_merge
-rw-r--r-- | lib/git/index/fun.py | 129 | ||||
-rw-r--r-- | lib/git/index/typ.py | 3 | ||||
-rw-r--r-- | lib/git/repo.py | 2 | ||||
-rw-r--r-- | test/git/test_fun.py | 111 |
4 files changed, 175 insertions, 70 deletions
diff --git a/lib/git/index/fun.py b/lib/git/index/fun.py index 79fcfddb..b04d018f 100644 --- a/lib/git/index/fun.py +++ b/lib/git/index/fun.py @@ -218,71 +218,72 @@ def aggressive_tree_merge(odb, tree_shas): for entry in traverse_tree_recursive(odb, tree_shas[-1], ''): out_append(_tree_entry_to_baseindexentry(entry, 0)) # END for each entry - elif len(tree_shas) == 3: - for base, ours, theirs in traverse_trees_recursive(odb, tree_shas, ''): - if base is not None: - # base version exists - if ours is not None: - # ours exists - if theirs is not None: - # it exists in all branches, if it was changed in both - # its a conflict, otherwise we take the changed version - # This should be the most common branch, so it comes first - if( base[0] != ours[0] and base[0] != theirs[0] and ours[0] != theirs[0] ) or \ - ( base[1] != ours[1] and base[1] != theirs[1] and ourse[1] != theirs[1] ): - # changed by both - out_append(_tree_entry_to_baseindexentry(base, 1)) - out_append(_tree_entry_to_baseindexentry(ours, 2)) - out_append(_tree_entry_to_baseindexentry(theirs, 3)) - elif base[0] != ours[0] or base[1] != ours[1]: - # only we changed it - out_append(_tree_entry_to_baseindexentry(ours, 0)) - else: - # either nobody changed it, or they did. In either - # case, use theirs - out_append(_tree_entry_to_baseindexentry(theirs, 0)) - # END handle modification + return out + # END handle single tree + + if len(tree_shas) > 3: + raise ValueError("Cannot handle %i trees at once" % len(tree_shas)) + + # three trees + for base, ours, theirs in traverse_trees_recursive(odb, tree_shas, ''): + if base is not None: + # base version exists + if ours is not None: + # ours exists + if theirs is not None: + # it exists in all branches, if it was changed in both + # its a conflict, otherwise we take the changed version + # This should be the most common branch, so it comes first + if( base[0] != ours[0] and base[0] != theirs[0] and ours[0] != theirs[0] ) or \ + ( base[1] != ours[1] and base[1] != theirs[1] and ours[1] != theirs[1] ): + # changed by both + out_append(_tree_entry_to_baseindexentry(base, 1)) + out_append(_tree_entry_to_baseindexentry(ours, 2)) + out_append(_tree_entry_to_baseindexentry(theirs, 3)) + elif base[0] != ours[0] or base[1] != ours[1]: + # only we changed it + out_append(_tree_entry_to_baseindexentry(ours, 0)) else: - - if ours[0] != base[0] or ours[1] != base[1]: - # they deleted it, we changed it, conflict - out_append(_tree_entry_to_baseindexentry(base, 1)) - out_append(_tree_entry_to_baseindexentry(ours, 2)) - out_append(_tree_entry_to_baseindexentry(theirs, 3)) - # else: - # we didn't change it, ignore - # pass - # END handle our change - # END handle theirs + # either nobody changed it, or they did. In either + # case, use theirs + out_append(_tree_entry_to_baseindexentry(theirs, 0)) + # END handle modification else: - if theirs is None: - # deleted in both, its fine - its out - pass - else: - if theirs[0] != base[0] or theirs[1] != base[1]: - # deleted in ours, changed theirs, conflict - out_append(_tree_entry_to_baseindexentry(base, 1)) - out_append(_tree_entry_to_baseindexentry(ours, 2)) - out_append(_tree_entry_to_baseindexentry(theirs, 3)) - # END theirs changed - #else: - # theirs didnt change - # pass - # END handle theirs - # END handle ours + + if ours[0] != base[0] or ours[1] != base[1]: + # they deleted it, we changed it, conflict + out_append(_tree_entry_to_baseindexentry(base, 1)) + out_append(_tree_entry_to_baseindexentry(ours, 2)) + # else: + # we didn't change it, ignore + # pass + # END handle our change + # END handle theirs else: - # all three can't be None - if ours is None: - # added in their branch - out_append(_tree_entry_to_baseindexentry(theirs, 0)) - elif theirs is None: - # added in our branch - out_append(_tree_entry_to_baseindexentry(ours, 0)) - # END hanle heads - # END handle base exists - # END for each entries tuple - else: - raise ValueError("Cannot handle %i trees at once" % len(tree_shas)) - # END handle tree shas - + if theirs is None: + # deleted in both, its fine - its out + pass + else: + if theirs[0] != base[0] or theirs[1] != base[1]: + # deleted in ours, changed theirs, conflict + out_append(_tree_entry_to_baseindexentry(base, 1)) + out_append(_tree_entry_to_baseindexentry(theirs, 3)) + # END theirs changed + #else: + # theirs didnt change + # pass + # END handle theirs + # END handle ours + else: + # all three can't be None + if ours is None: + # added in their branch + out_append(_tree_entry_to_baseindexentry(theirs, 0)) + elif theirs is None: + # added in our branch + out_append(_tree_entry_to_baseindexentry(ours, 0)) + # END hanle heads + # END handle base exists + # END for each entries tuple + return out diff --git a/lib/git/index/typ.py b/lib/git/index/typ.py index 6ef1d2f2..d9cafa2e 100644 --- a/lib/git/index/typ.py +++ b/lib/git/index/typ.py @@ -56,6 +56,9 @@ class BaseIndexEntry(tuple): def __str__(self): return "%o %s %i\t%s" % (self.mode, self.hexsha, self.stage, self.path) + + def __repr__(self): + return "(%o, %s, %i, %s)" % (self.mode, self.hexsha, self.stage, self.path) @property def mode(self): diff --git a/lib/git/repo.py b/lib/git/repo.py index f97126ea..79275106 100644 --- a/lib/git/repo.py +++ b/lib/git/repo.py @@ -742,7 +742,7 @@ class Repo(object): # we at least give a proper error instead of letting git fail prev_cwd = None prev_path = None - odbt = kwargs.pop('odbt', GitCmdObjectDB) + odbt = kwargs.pop('odbt', type(self.odb)) if os.name == 'nt': if '~' in path: raise OSError("Git cannot handle the ~ character in path %r correctly" % path) diff --git a/test/git/test_fun.py b/test/git/test_fun.py index 4ddc1910..ce610014 100644 --- a/test/git/test_fun.py +++ b/test/git/test_fun.py @@ -1,20 +1,26 @@ from test.testlib import * from git.objects.fun import ( traverse_tree_recursive, - traverse_trees_recursive + traverse_trees_recursive, + tree_to_stream ) from git.index.fun import ( aggressive_tree_merge ) -from git.index import IndexFile +from gitdb.base import IStream +from gitdb.typ import str_tree_type + from stat import ( S_IFDIR, S_IFREG, S_IFLNK ) +from git.index import IndexFile +from cStringIO import StringIO + class TestFun(TestBase): def _assert_index_entries(self, entries, trees): @@ -55,18 +61,113 @@ class TestFun(TestBase): trees = [B.sha, H.sha, M.sha] self._assert_index_entries(aggressive_tree_merge(odb, trees), trees) - def make_tree(odb, entries): + def mktree(self, odb, entries): """create a tree from the given tree entries and safe it to the database""" - + sio = StringIO() + tree_to_stream(entries, sio.write) + sio.seek(0) + istream = odb.store(IStream(str_tree_type, len(sio.getvalue()), sio)) + return istream.sha @with_rw_repo('0.1.6') def test_three_way_merge(self, rwrepo): def mkfile(name, sha, executable=0): - return (sha, S_IFREG | 644 | executable*0111, name) + return (sha, S_IFREG | 0644 | executable*0111, name) def mkcommit(name, sha): return (sha, S_IFDIR | S_IFLNK, name) + def assert_entries(entries, num_entries, has_conflict=False): + assert len(entries) == num_entries + assert has_conflict == (len([e for e in entries if e.stage != 0]) > 0) + mktree = self.mktree + + shaa = "\1"*20 + shab = "\2"*20 + shac = "\3"*20 + odb = rwrepo.odb + # base tree + bfn = 'basefile' + fbase = mkfile(bfn, shaa) + tb = mktree(odb, [fbase]) + + # non-conflicting new files, same data + fa = mkfile('1', shab) + th = mktree(odb, [fbase, fa]) + fb = mkfile('2', shac) + tm = mktree(odb, [fbase, fb]) + + # two new files, same base file + trees = [tb, th, tm] + assert_entries(aggressive_tree_merge(odb, trees), 3) + + # both delete same file, add own one + fa = mkfile('1', shab) + th = mktree(odb, [fa]) + fb = mkfile('2', shac) + tm = mktree(odb, [fb]) + + # two new files + trees = [tb, th, tm] + assert_entries(aggressive_tree_merge(odb, trees), 2) + + # modify same base file, differently + fa = mkfile(bfn, shab) + th = mktree(odb, [fa]) + fb = mkfile(bfn, shac) + tm = mktree(odb, [fb]) + + # conflict, 3 versions on 3 stages + trees = [tb, th, tm] + assert_entries(aggressive_tree_merge(odb, trees), 3, True) + + + # change mode on same base file, by making one a commit, the other executable + # no content change ( this is totally unlikely to happen in the real world ) + fa = mkcommit(bfn, shaa) + th = mktree(odb, [fa]) + fb = mkfile(bfn, shaa, executable=1) + tm = mktree(odb, [fb]) + + # conflict, 3 versions on 3 stages, because of different mode + trees = [tb, th, tm] + assert_entries(aggressive_tree_merge(odb, trees), 3, True) + + for is_them in range(2): + # only we/they change contents + fa = mkfile(bfn, shab) + th = mktree(odb, [fa]) + + trees = [tb, th, tb] + if is_them: + trees = [tb, tb, th] + entries = aggressive_tree_merge(odb, trees) + assert len(entries) == 1 and entries[0].binsha == shab + + # only we/they change the mode + fa = mkcommit(bfn, shaa) + th = mktree(odb, [fa]) + + trees = [tb, th, tb] + if is_them: + trees = [tb, tb, th] + entries = aggressive_tree_merge(odb, trees) + assert len(entries) == 1 and entries[0].binsha == shaa and entries[0].mode == fa[1] + + # one side deletes, the other changes = conflict + fa = mkfile(bfn, shab) + th = mktree(odb, [fa]) + tm = mktree(odb, []) + trees = [tb, th, tm] + if is_them: + trees = [tb, tm, th] + # as one is deleted, there are only 2 entries + assert_entries(aggressive_tree_merge(odb, trees), 2, True) + # END handle ours, theirs + + + + def _assert_tree_entries(self, entries, num_trees): assert len(entries[0]) == num_trees |