Skip to content

Commit aea0243

Browse files
committed
Added test for aggressive_tree_merge
1 parent c0ef65b commit aea0243

File tree

4 files changed

+175
-70
lines changed

4 files changed

+175
-70
lines changed

lib/git/index/fun.py

Lines changed: 65 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -218,71 +218,72 @@ def aggressive_tree_merge(odb, tree_shas):
218218
for entry in traverse_tree_recursive(odb, tree_shas[-1], ''):
219219
out_append(_tree_entry_to_baseindexentry(entry, 0))
220220
# END for each entry
221-
elif len(tree_shas) == 3:
222-
for base, ours, theirs in traverse_trees_recursive(odb, tree_shas, ''):
223-
if base is not None:
224-
# base version exists
225-
if ours is not None:
226-
# ours exists
227-
if theirs is not None:
228-
# it exists in all branches, if it was changed in both
229-
# its a conflict, otherwise we take the changed version
230-
# This should be the most common branch, so it comes first
231-
if( base[0] != ours[0] and base[0] != theirs[0] and ours[0] != theirs[0] ) or \
232-
( base[1] != ours[1] and base[1] != theirs[1] and ourse[1] != theirs[1] ):
233-
# changed by both
234-
out_append(_tree_entry_to_baseindexentry(base, 1))
235-
out_append(_tree_entry_to_baseindexentry(ours, 2))
236-
out_append(_tree_entry_to_baseindexentry(theirs, 3))
237-
elif base[0] != ours[0] or base[1] != ours[1]:
238-
# only we changed it
239-
out_append(_tree_entry_to_baseindexentry(ours, 0))
240-
else:
241-
# either nobody changed it, or they did. In either
242-
# case, use theirs
243-
out_append(_tree_entry_to_baseindexentry(theirs, 0))
244-
# END handle modification
221+
return out
222+
# END handle single tree
223+
224+
if len(tree_shas) > 3:
225+
raise ValueError("Cannot handle %i trees at once" % len(tree_shas))
226+
227+
# three trees
228+
for base, ours, theirs in traverse_trees_recursive(odb, tree_shas, ''):
229+
if base is not None:
230+
# base version exists
231+
if ours is not None:
232+
# ours exists
233+
if theirs is not None:
234+
# it exists in all branches, if it was changed in both
235+
# its a conflict, otherwise we take the changed version
236+
# This should be the most common branch, so it comes first
237+
if( base[0] != ours[0] and base[0] != theirs[0] and ours[0] != theirs[0] ) or \
238+
( base[1] != ours[1] and base[1] != theirs[1] and ours[1] != theirs[1] ):
239+
# changed by both
240+
out_append(_tree_entry_to_baseindexentry(base, 1))
241+
out_append(_tree_entry_to_baseindexentry(ours, 2))
242+
out_append(_tree_entry_to_baseindexentry(theirs, 3))
243+
elif base[0] != ours[0] or base[1] != ours[1]:
244+
# only we changed it
245+
out_append(_tree_entry_to_baseindexentry(ours, 0))
245246
else:
246-
247-
if ours[0] != base[0] or ours[1] != base[1]:
248-
# they deleted it, we changed it, conflict
249-
out_append(_tree_entry_to_baseindexentry(base, 1))
250-
out_append(_tree_entry_to_baseindexentry(ours, 2))
251-
out_append(_tree_entry_to_baseindexentry(theirs, 3))
252-
# else:
253-
# we didn't change it, ignore
254-
# pass
255-
# END handle our change
256-
# END handle theirs
247+
# either nobody changed it, or they did. In either
248+
# case, use theirs
249+
out_append(_tree_entry_to_baseindexentry(theirs, 0))
250+
# END handle modification
257251
else:
258-
if theirs is None:
259-
# deleted in both, its fine - its out
260-
pass
261-
else:
262-
if theirs[0] != base[0] or theirs[1] != base[1]:
263-
# deleted in ours, changed theirs, conflict
264-
out_append(_tree_entry_to_baseindexentry(base, 1))
265-
out_append(_tree_entry_to_baseindexentry(ours, 2))
266-
out_append(_tree_entry_to_baseindexentry(theirs, 3))
267-
# END theirs changed
268-
#else:
269-
# theirs didnt change
270-
# pass
271-
# END handle theirs
272-
# END handle ours
252+
253+
if ours[0] != base[0] or ours[1] != base[1]:
254+
# they deleted it, we changed it, conflict
255+
out_append(_tree_entry_to_baseindexentry(base, 1))
256+
out_append(_tree_entry_to_baseindexentry(ours, 2))
257+
# else:
258+
# we didn't change it, ignore
259+
# pass
260+
# END handle our change
261+
# END handle theirs
273262
else:
274-
# all three can't be None
275-
if ours is None:
276-
# added in their branch
277-
out_append(_tree_entry_to_baseindexentry(theirs, 0))
278-
elif theirs is None:
279-
# added in our branch
280-
out_append(_tree_entry_to_baseindexentry(ours, 0))
281-
# END hanle heads
282-
# END handle base exists
283-
# END for each entries tuple
284-
else:
285-
raise ValueError("Cannot handle %i trees at once" % len(tree_shas))
286-
# END handle tree shas
287-
263+
if theirs is None:
264+
# deleted in both, its fine - its out
265+
pass
266+
else:
267+
if theirs[0] != base[0] or theirs[1] != base[1]:
268+
# deleted in ours, changed theirs, conflict
269+
out_append(_tree_entry_to_baseindexentry(base, 1))
270+
out_append(_tree_entry_to_baseindexentry(theirs, 3))
271+
# END theirs changed
272+
#else:
273+
# theirs didnt change
274+
# pass
275+
# END handle theirs
276+
# END handle ours
277+
else:
278+
# all three can't be None
279+
if ours is None:
280+
# added in their branch
281+
out_append(_tree_entry_to_baseindexentry(theirs, 0))
282+
elif theirs is None:
283+
# added in our branch
284+
out_append(_tree_entry_to_baseindexentry(ours, 0))
285+
# END hanle heads
286+
# END handle base exists
287+
# END for each entries tuple
288+
288289
return out

lib/git/index/typ.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ class BaseIndexEntry(tuple):
5656

5757
def __str__(self):
5858
return "%o %s %i\t%s" % (self.mode, self.hexsha, self.stage, self.path)
59+
60+
def __repr__(self):
61+
return "(%o, %s, %i, %s)" % (self.mode, self.hexsha, self.stage, self.path)
5962

6063
@property
6164
def mode(self):

lib/git/repo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,7 @@ def clone(self, path, **kwargs):
742742
# we at least give a proper error instead of letting git fail
743743
prev_cwd = None
744744
prev_path = None
745-
odbt = kwargs.pop('odbt', GitCmdObjectDB)
745+
odbt = kwargs.pop('odbt', type(self.odb))
746746
if os.name == 'nt':
747747
if '~' in path:
748748
raise OSError("Git cannot handle the ~ character in path %r correctly" % path)

test/git/test_fun.py

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
from test.testlib import *
22
from git.objects.fun import (
33
traverse_tree_recursive,
4-
traverse_trees_recursive
4+
traverse_trees_recursive,
5+
tree_to_stream
56
)
67

78
from git.index.fun import (
89
aggressive_tree_merge
910
)
1011

11-
from git.index import IndexFile
12+
from gitdb.base import IStream
13+
from gitdb.typ import str_tree_type
14+
1215
from stat import (
1316
S_IFDIR,
1417
S_IFREG,
1518
S_IFLNK
1619
)
1720

21+
from git.index import IndexFile
22+
from cStringIO import StringIO
23+
1824
class TestFun(TestBase):
1925

2026
def _assert_index_entries(self, entries, trees):
@@ -55,18 +61,113 @@ def test_aggressive_tree_merge(self):
5561
trees = [B.sha, H.sha, M.sha]
5662
self._assert_index_entries(aggressive_tree_merge(odb, trees), trees)
5763

58-
def make_tree(odb, entries):
64+
def mktree(self, odb, entries):
5965
"""create a tree from the given tree entries and safe it to the database"""
60-
66+
sio = StringIO()
67+
tree_to_stream(entries, sio.write)
68+
sio.seek(0)
69+
istream = odb.store(IStream(str_tree_type, len(sio.getvalue()), sio))
70+
return istream.sha
6171

6272
@with_rw_repo('0.1.6')
6373
def test_three_way_merge(self, rwrepo):
6474
def mkfile(name, sha, executable=0):
65-
return (sha, S_IFREG | 644 | executable*0111, name)
75+
return (sha, S_IFREG | 0644 | executable*0111, name)
6676
def mkcommit(name, sha):
6777
return (sha, S_IFDIR | S_IFLNK, name)
78+
def assert_entries(entries, num_entries, has_conflict=False):
79+
assert len(entries) == num_entries
80+
assert has_conflict == (len([e for e in entries if e.stage != 0]) > 0)
81+
mktree = self.mktree
82+
83+
shaa = "\1"*20
84+
shab = "\2"*20
85+
shac = "\3"*20
86+
6887
odb = rwrepo.odb
6988

89+
# base tree
90+
bfn = 'basefile'
91+
fbase = mkfile(bfn, shaa)
92+
tb = mktree(odb, [fbase])
93+
94+
# non-conflicting new files, same data
95+
fa = mkfile('1', shab)
96+
th = mktree(odb, [fbase, fa])
97+
fb = mkfile('2', shac)
98+
tm = mktree(odb, [fbase, fb])
99+
100+
# two new files, same base file
101+
trees = [tb, th, tm]
102+
assert_entries(aggressive_tree_merge(odb, trees), 3)
103+
104+
# both delete same file, add own one
105+
fa = mkfile('1', shab)
106+
th = mktree(odb, [fa])
107+
fb = mkfile('2', shac)
108+
tm = mktree(odb, [fb])
109+
110+
# two new files
111+
trees = [tb, th, tm]
112+
assert_entries(aggressive_tree_merge(odb, trees), 2)
113+
114+
# modify same base file, differently
115+
fa = mkfile(bfn, shab)
116+
th = mktree(odb, [fa])
117+
fb = mkfile(bfn, shac)
118+
tm = mktree(odb, [fb])
119+
120+
# conflict, 3 versions on 3 stages
121+
trees = [tb, th, tm]
122+
assert_entries(aggressive_tree_merge(odb, trees), 3, True)
123+
124+
125+
# change mode on same base file, by making one a commit, the other executable
126+
# no content change ( this is totally unlikely to happen in the real world )
127+
fa = mkcommit(bfn, shaa)
128+
th = mktree(odb, [fa])
129+
fb = mkfile(bfn, shaa, executable=1)
130+
tm = mktree(odb, [fb])
131+
132+
# conflict, 3 versions on 3 stages, because of different mode
133+
trees = [tb, th, tm]
134+
assert_entries(aggressive_tree_merge(odb, trees), 3, True)
135+
136+
for is_them in range(2):
137+
# only we/they change contents
138+
fa = mkfile(bfn, shab)
139+
th = mktree(odb, [fa])
140+
141+
trees = [tb, th, tb]
142+
if is_them:
143+
trees = [tb, tb, th]
144+
entries = aggressive_tree_merge(odb, trees)
145+
assert len(entries) == 1 and entries[0].binsha == shab
146+
147+
# only we/they change the mode
148+
fa = mkcommit(bfn, shaa)
149+
th = mktree(odb, [fa])
150+
151+
trees = [tb, th, tb]
152+
if is_them:
153+
trees = [tb, tb, th]
154+
entries = aggressive_tree_merge(odb, trees)
155+
assert len(entries) == 1 and entries[0].binsha == shaa and entries[0].mode == fa[1]
156+
157+
# one side deletes, the other changes = conflict
158+
fa = mkfile(bfn, shab)
159+
th = mktree(odb, [fa])
160+
tm = mktree(odb, [])
161+
trees = [tb, th, tm]
162+
if is_them:
163+
trees = [tb, tm, th]
164+
# as one is deleted, there are only 2 entries
165+
assert_entries(aggressive_tree_merge(odb, trees), 2, True)
166+
# END handle ours, theirs
167+
168+
169+
170+
70171

71172
def _assert_tree_entries(self, entries, num_trees):
72173
assert len(entries[0]) == num_trees

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy