1
from merge_core import merge_flex, ApplyMerge3, BackupBeforeChange
2
from changeset import generate_changeset, ExceptionConflictHandler
3
from changeset import Inventory, Diff3Merge
1
from bzrlib.merge_core import merge_flex, ApplyMerge3, BackupBeforeChange
2
from bzrlib.changeset import generate_changeset, ExceptionConflictHandler
3
from bzrlib.changeset import Inventory, Diff3Merge
4
4
from bzrlib import find_branch
5
5
import bzrlib.osutils
6
6
from bzrlib.errors import BzrCommandError
15
# comments from abentley on irc: merge happens in two stages, each
16
# of which generates a changeset object
18
# stage 1: generate OLD->OTHER,
19
# stage 2: use MINE and OLD->OTHER to generate MINE -> RESULT
14
21
class UnrelatedBranches(BzrCommandError):
15
22
def __init__(self):
16
23
msg = "Branches have no common ancestor, and no base revision"\
21
28
class MergeConflictHandler(ExceptionConflictHandler):
22
"""Handle conflicts encountered while merging"""
29
"""Handle conflicts encountered while merging.
31
This subclasses ExceptionConflictHandler, so that any types of
32
conflict that are not explicitly handled cause an exception and
23
35
def __init__(self, dir, ignore_zero=False):
24
36
ExceptionConflictHandler.__init__(self, dir)
75
87
os.rename(new_file, this_path)
76
88
self.conflict("Diff3 conflict encountered in %s" % this_path)
90
def new_contents_conflict(self, filename, other_contents):
91
"""Conflicting contents for newly added file."""
92
self.copy(other_contents, filename + ".OTHER")
93
self.conflict("Conflict in newly added file %s" % filename)
78
96
def target_exists(self, entry, target, old_path):
79
97
"""Handle the case when the target file or dir exists"""
80
98
moved_path = self.add_suffix(target, ".moved")
145
163
os.mkdir(os.path.join(self.tempdir, "texts"))
166
def __contains__(self, file_id):
167
return id in self.tree
169
def get_file_sha1(self, id):
170
return self.tree.get_file_sha1(id)
148
172
def readonly_path(self, id):
149
173
if id not in self.tree:
232
256
source_file.interesting = source_file.id in interesting_ids
235
def set_optimized(tree_a, tree_b, inventory_a, inventory_b):
236
"""Mark files that have changed texts as interesting
238
for file_id in tree_a.tree.inventory:
239
if file_id not in tree_b.tree.inventory:
241
entry_a = tree_a.tree.inventory[file_id]
242
entry_b = tree_b.tree.inventory[file_id]
243
if (entry_a.kind, entry_b.kind) != ("file", "file"):
245
if None in (entry_a.text_id, entry_b.text_id):
247
if entry_a.text_id != entry_b.text_id:
249
inventory_a[abspath(tree_a.tree, file_id)].interesting = False
250
inventory_b[abspath(tree_b.tree, file_id)].interesting = False
253
259
def generate_cset_optimized(tree_a, tree_b, inventory_a, inventory_b,
254
260
interesting_ids=None):
255
"""Generate a changeset, with preprocessing to select interesting files.
256
using the text_id to mark really-changed files.
257
This permits blazing comparisons when text_ids are present. It also
258
disables metadata comparison for files with identical texts.
261
"""Generate a changeset. If interesting_ids is supplied, only changes
262
to those files will be shown. Metadata changes are stripped.
260
if interesting_ids is None:
261
set_optimized(tree_a, tree_b, inventory_a, inventory_b)
264
if interesting_ids is not None:
263
265
set_interesting(inventory_a, inventory_b, interesting_ids)
264
266
cset = generate_changeset(tree_a, tree_b, inventory_a, inventory_b)
265
267
for entry in cset.entries.itervalues():
301
303
assert path.startswith('./')
303
305
adjust_ids.append((path, id))
304
this_branch.set_inventory(regen_inventory(this_branch, this_tree.root, adjust_ids))
306
if len(adjust_ids) > 0:
307
this_branch.set_inventory(regen_inventory(this_branch, this_tree.root,
307
311
def regen_inventory(this_branch, root, new_entries):