+ if recursive:
+ # this operation tracks renames but it is slower (used in
+ # general when pushing or picking patches)
+ try:
+ # use _output() to mask the verbose prints of the tool
+ _output('git-merge-recursive %s -- %s %s' % (base, head1, head2))
+ except GitException:
+ pass
+ else:
+ # the fast case where we don't track renames (used when the
+ # distance between base and heads is small, i.e. folding or
+ # synchronising patches)
+ if __run('git-read-tree -u -m --aggressive',
+ [base, head1, head2]) != 0:
+ raise GitException, 'git-read-tree failed (local changes maybe?)'
+
+ # check the index for unmerged entries
+ files = {}
+ stages_re = re.compile('^([0-7]+) ([0-9a-f]{40}) ([1-3])\t(.*)$', re.S)
+
+ for line in _output('git-ls-files --unmerged --stage -z').split('\0'):
+ if not line:
+ continue
+
+ mode, hash, stage, path = stages_re.findall(line)[0]
+
+ if not path in files:
+ files[path] = {}
+ files[path]['1'] = ('', '')
+ files[path]['2'] = ('', '')
+ files[path]['3'] = ('', '')
+
+ files[path][stage] = (mode, hash)
+
+ # merge the unmerged files
+ errors = False
+ for path in files:
+ # remove additional files that might be generated for some
+ # newer versions of GIT
+ for suffix in [base, head1, head2]:
+ if not suffix:
+ continue
+ fname = path + '~' + suffix
+ if os.path.exists(fname):
+ os.remove(fname)
+
+ stages = files[path]
+ if gitmergeonefile.merge(stages['1'][1], stages['2'][1],
+ stages['3'][1], path, stages['1'][0],
+ stages['2'][0], stages['3'][0]) != 0:
+ errors = True
+
+ if errors:
+ raise GitException, 'GIT index merging failed (possible conflicts)'