55from stat import S_IFDIR
66from cStringIO import StringIO
77
8+ from git .utils import IndexFileSHA1Writer
89from 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+ )
1315
1416from typ import (
17+ BaseIndexEntry ,
1518 IndexEntry ,
16- CE_NAMEMASK
19+ CE_NAMEMASK ,
20+ CE_STAGESHIFT
1721 )
1822
1923from util import (
2327
2428from gitdb .base import IStream
2529from gitdb .typ import str_tree_type
26- from binascii import a2b_hex
2730
2831__all__ = ('write_cache' , 'read_cache' , 'write_tree_from_cache' , 'entry_key' )
2932
@@ -75,15 +78,16 @@ def write_cache(entries, stream, extension_data=None, ShaStreamCls=IndexFileSHA1
7578def read_entry (stream ):
7679 """Return: One entry of the given stream"""
7780 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 ]
8084 (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 ))
8286 path_size = flags & CE_NAMEMASK
83- path = stream . read (path_size )
87+ path = read (path_size )
8488
8589 real_size = ((stream .tell () - beginoffset + 8 ) & ~ 7 )
86- data = stream . read ((beginoffset + real_size ) - stream .tell ())
90+ data = read ((beginoffset + real_size ) - stream .tell ())
8791 return IndexEntry ((mode , sha , flags , path , ctime , mtime , dev , ino , uid , gid , size ))
8892
8993def read_header (stream ):
@@ -150,6 +154,7 @@ def write_tree_from_cache(entries, odb, sl, si=0):
150154 :return: tuple(binsha, list(tree_entry, ...)) a tuple of a sha and a list of
151155 tree entries being a tuple of hexsha, mode, name"""
152156 tree_items = list ()
157+ tree_items_append = tree_items .append
153158 ci = sl .start
154159 end = sl .stop
155160 while ci < end :
@@ -161,7 +166,7 @@ def write_tree_from_cache(entries, odb, sl, si=0):
161166 rbound = entry .path .find ('/' , si )
162167 if rbound == - 1 :
163168 # 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 :]))
165170 else :
166171 # find common base range
167172 base = entry .path [si :rbound ]
@@ -178,7 +183,7 @@ def write_tree_from_cache(entries, odb, sl, si=0):
178183 # enter recursion
179184 # ci - 1 as we want to count our current item as well
180185 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 ))
182187
183188 # skip ahead
184189 ci = xi
@@ -193,5 +198,93 @@ def write_tree_from_cache(entries, odb, sl, si=0):
193198 istream = odb .store (IStream (str_tree_type , len (sio .getvalue ()), sio ))
194199 return (istream .binsha , tree_items )
195200
201+ def _tree_entry_to_baseindexentry (tree_entry , stage ):
202+ return BaseIndexEntry ((tree_entry [1 ], tree_entry [0 ], stage << CE_STAGESHIFT , tree_entry [2 ]))
196203
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
197215
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