5
5
from stat import S_IFDIR
6
6
from cStringIO import StringIO
7
7
8
+ from git .utils import IndexFileSHA1Writer
8
9
from git .errors import UnmergedEntriesError
9
- from git .objects .fun import tree_to_stream
10
- from git .utils import (
11
- IndexFileSHA1Writer ,
12
- )
10
+ from git .objects .fun import (
11
+ tree_to_stream ,
12
+ traverse_tree_recursive ,
13
+ traverse_trees_recursive
14
+ )
13
15
14
16
from typ import (
17
+ BaseIndexEntry ,
15
18
IndexEntry ,
16
- CE_NAMEMASK
19
+ CE_NAMEMASK ,
20
+ CE_STAGESHIFT
17
21
)
18
22
19
23
from util import (
23
27
24
28
from gitdb .base import IStream
25
29
from gitdb .typ import str_tree_type
26
- from binascii import a2b_hex
27
30
28
31
__all__ = ('write_cache' , 'read_cache' , 'write_tree_from_cache' , 'entry_key' )
29
32
@@ -75,15 +78,16 @@ def write_cache(entries, stream, extension_data=None, ShaStreamCls=IndexFileSHA1
75
78
def read_entry (stream ):
76
79
"""Return: One entry of the given stream"""
77
80
beginoffset = stream .tell ()
78
- ctime = unpack (">8s" , stream .read (8 ))[0 ]
79
- mtime = unpack (">8s" , stream .read (8 ))[0 ]
81
+ read = stream .read
82
+ ctime = unpack (">8s" , read (8 ))[0 ]
83
+ mtime = unpack (">8s" , read (8 ))[0 ]
80
84
(dev , ino , mode , uid , gid , size , sha , flags ) = \
81
- unpack (">LLLLLL20sH" , stream . read (20 + 4 * 6 + 2 ))
85
+ unpack (">LLLLLL20sH" , read (20 + 4 * 6 + 2 ))
82
86
path_size = flags & CE_NAMEMASK
83
- path = stream . read (path_size )
87
+ path = read (path_size )
84
88
85
89
real_size = ((stream .tell () - beginoffset + 8 ) & ~ 7 )
86
- data = stream . read ((beginoffset + real_size ) - stream .tell ())
90
+ data = read ((beginoffset + real_size ) - stream .tell ())
87
91
return IndexEntry ((mode , sha , flags , path , ctime , mtime , dev , ino , uid , gid , size ))
88
92
89
93
def read_header (stream ):
@@ -150,6 +154,7 @@ def write_tree_from_cache(entries, odb, sl, si=0):
150
154
:return: tuple(binsha, list(tree_entry, ...)) a tuple of a sha and a list of
151
155
tree entries being a tuple of hexsha, mode, name"""
152
156
tree_items = list ()
157
+ tree_items_append = tree_items .append
153
158
ci = sl .start
154
159
end = sl .stop
155
160
while ci < end :
@@ -161,7 +166,7 @@ def write_tree_from_cache(entries, odb, sl, si=0):
161
166
rbound = entry .path .find ('/' , si )
162
167
if rbound == - 1 :
163
168
# its not a tree
164
- tree_items . append ((entry .binsha , entry .mode , entry .path [si :]))
169
+ tree_items_append ((entry .binsha , entry .mode , entry .path [si :]))
165
170
else :
166
171
# find common base range
167
172
base = entry .path [si :rbound ]
@@ -178,7 +183,7 @@ def write_tree_from_cache(entries, odb, sl, si=0):
178
183
# enter recursion
179
184
# ci - 1 as we want to count our current item as well
180
185
sha , tree_entry_list = write_tree_from_cache (entries , odb , slice (ci - 1 , xi ), rbound + 1 )
181
- tree_items . append ((sha , S_IFDIR , base ))
186
+ tree_items_append ((sha , S_IFDIR , base ))
182
187
183
188
# skip ahead
184
189
ci = xi
@@ -193,5 +198,93 @@ def write_tree_from_cache(entries, odb, sl, si=0):
193
198
istream = odb .store (IStream (str_tree_type , len (sio .getvalue ()), sio ))
194
199
return (istream .binsha , tree_items )
195
200
201
+ def _tree_entry_to_baseindexentry (tree_entry , stage ):
202
+ return BaseIndexEntry ((tree_entry [1 ], tree_entry [0 ], stage << CE_STAGESHIFT , tree_entry [2 ]))
196
203
204
+ def aggressive_tree_merge (odb , tree_shas ):
205
+ """
206
+ :return: list of BaseIndexEntries representing the aggressive merge of the given
207
+ trees. All valid entries are on stage 0, whereas the conflicting ones are left
208
+ on stage 1, 2 or 3, whereas stage 1 corresponds to the common ancestor tree,
209
+ 2 to our tree and 3 to 'their' tree.
210
+ :param tree_shas: 1, 2 or 3 trees as identified by their shas
211
+ If 1 or two, the entries will effectively correspond to the last given tree
212
+ If 3 are given, a 3 way merge is performed"""
213
+ out = list ()
214
+ out_append = out .append
197
215
216
+ # one and two way is the same for us, as we don't have to handle an existing
217
+ # index, instrea
218
+ if len (tree_shas ) in (1 ,2 ):
219
+ for entry in traverse_tree_recursive (odb , tree_shas [- 1 ], '' ):
220
+ out_append (_tree_entry_to_baseindexentry (entry , 0 ))
221
+ # END for each entry
222
+ return out
223
+ # END handle single tree
224
+
225
+ if len (tree_shas ) > 3 :
226
+ raise ValueError ("Cannot handle %i trees at once" % len (tree_shas ))
227
+
228
+ # three trees
229
+ for base , ours , theirs in traverse_trees_recursive (odb , tree_shas , '' ):
230
+ if base is not None :
231
+ # base version exists
232
+ if ours is not None :
233
+ # ours exists
234
+ if theirs is not None :
235
+ # it exists in all branches, if it was changed in both
236
+ # its a conflict, otherwise we take the changed version
237
+ # This should be the most common branch, so it comes first
238
+ if ( base [0 ] != ours [0 ] and base [0 ] != theirs [0 ] and ours [0 ] != theirs [0 ] ) or \
239
+ ( base [1 ] != ours [1 ] and base [1 ] != theirs [1 ] and ours [1 ] != theirs [1 ] ):
240
+ # changed by both
241
+ out_append (_tree_entry_to_baseindexentry (base , 1 ))
242
+ out_append (_tree_entry_to_baseindexentry (ours , 2 ))
243
+ out_append (_tree_entry_to_baseindexentry (theirs , 3 ))
244
+ elif base [0 ] != ours [0 ] or base [1 ] != ours [1 ]:
245
+ # only we changed it
246
+ out_append (_tree_entry_to_baseindexentry (ours , 0 ))
247
+ else :
248
+ # either nobody changed it, or they did. In either
249
+ # case, use theirs
250
+ out_append (_tree_entry_to_baseindexentry (theirs , 0 ))
251
+ # END handle modification
252
+ else :
253
+
254
+ if ours [0 ] != base [0 ] or ours [1 ] != base [1 ]:
255
+ # they deleted it, we changed it, conflict
256
+ out_append (_tree_entry_to_baseindexentry (base , 1 ))
257
+ out_append (_tree_entry_to_baseindexentry (ours , 2 ))
258
+ # else:
259
+ # we didn't change it, ignore
260
+ # pass
261
+ # END handle our change
262
+ # END handle theirs
263
+ else :
264
+ if theirs is None :
265
+ # deleted in both, its fine - its out
266
+ pass
267
+ else :
268
+ if theirs [0 ] != base [0 ] or theirs [1 ] != base [1 ]:
269
+ # deleted in ours, changed theirs, conflict
270
+ out_append (_tree_entry_to_baseindexentry (base , 1 ))
271
+ out_append (_tree_entry_to_baseindexentry (theirs , 3 ))
272
+ # END theirs changed
273
+ #else:
274
+ # theirs didnt change
275
+ # pass
276
+ # END handle theirs
277
+ # END handle ours
278
+ else :
279
+ # all three can't be None
280
+ if ours is None :
281
+ # added in their branch
282
+ out_append (_tree_entry_to_baseindexentry (theirs , 0 ))
283
+ elif theirs is None :
284
+ # added in our branch
285
+ out_append (_tree_entry_to_baseindexentry (ours , 0 ))
286
+ # END hanle heads
287
+ # END handle base exists
288
+ # END for each entries tuple
289
+
290
+ return out
0 commit comments