23
24
def apply(self, filename, conflict_handler, reverse=False):
24
25
new_file = filename+".new"
27
other = self.other_file
29
base = self.other_file
30
other = self.base_file
31
m3 = Merge3(file(base, "rb").readlines(),
32
file(filename, "rb").readlines(),
33
file(other, "rb").readlines())
33
if self.file_id not in tree:
34
raise Exception("%s not in tree" % self.file_id)
36
return tree.get_file(self.file_id).readlines()
37
base_lines = get_lines(base)
38
other_lines = get_lines(other)
39
m3 = Merge3(base_lines, file(filename, "rb").readlines(), other_lines)
35
41
new_conflicts = False
36
42
output_file = file(new_file, "wb")
69
76
self.contents_change.apply(filename, conflict_handler, reverse)
72
class ThreewayInventory(object):
73
def __init__(self, this_inventory, base_inventory, other_inventory):
74
self.this = this_inventory
75
self.base = base_inventory
76
self.other = other_inventory
77
79
def invert_invent(inventory):
79
for key, value in inventory.iteritems():
80
invert_invent[value.id] = key
81
for file_id in inventory:
82
path = inventory.id2path(file_id)
87
invert_invent[file_id] = path
81
88
return invert_invent
83
def make_inv(inventory):
84
return Inventory(invert_invent(inventory))
87
91
def merge_flex(this, base, other, changeset_function, inventory_function,
88
conflict_handler, merge_factory):
89
this_inventory = inventory_function(this)
90
base_inventory = inventory_function(base)
91
other_inventory = inventory_function(other)
92
inventory = ThreewayInventory(make_inv(this_inventory),
93
make_inv(base_inventory),
94
make_inv(other_inventory))
95
cset = changeset_function(base, other, base_inventory, other_inventory)
96
new_cset = make_merge_changeset(cset, inventory, this, base, other,
92
conflict_handler, merge_factory, interesting_ids):
93
cset = changeset_function(base, other, interesting_ids)
94
new_cset = make_merge_changeset(cset, this, base, other,
97
95
conflict_handler, merge_factory)
98
result = apply_changeset(new_cset, invert_invent(this_inventory),
96
result = apply_changeset(new_cset, invert_invent(this.tree.inventory),
99
97
this.root, conflict_handler, False)
100
98
conflict_handler.finalize()
105
def make_merge_changeset(cset, inventory, this, base, other,
103
def make_merge_changeset(cset, this, base, other,
106
104
conflict_handler, merge_factory):
107
105
new_cset = changeset.Changeset()
108
106
def get_this_contents(id):
109
path = os.path.join(this.root, inventory.this.get_path(id))
107
path = this.readonly_path(id)
110
108
if os.path.isdir(path):
111
109
return changeset.dir_create
129
def make_merged_entry(entry, inventory, conflict_handler):
128
class ThreeWayConflict(Exception):
129
def __init__(self, this, base, other):
133
msg = "Conflict merging %s %s and %s" % (this, base, other)
134
Exception.__init__(self, msg)
136
def threeway_select(this, base, other):
137
"""Returns a value selected by the three-way algorithm.
138
Raises ThreewayConflict if the algorithm yields a conflict"""
146
raise ThreeWayConflict(this, base, other)
149
def make_merged_entry(entry, this, base, other, conflict_handler):
130
150
from bzrlib.trace import mutter
131
this_name = inventory.this.get_name(entry.id)
132
this_parent = inventory.this.get_parent(entry.id)
133
this_dir = inventory.this.get_dir(entry.id)
137
base_name = inventory.base.get_name(entry.id)
138
base_parent = inventory.base.get_parent(entry.id)
139
base_dir = inventory.base.get_dir(entry.id)
142
other_name = inventory.other.get_name(entry.id)
143
other_parent = inventory.other.get_parent(entry.id)
144
other_dir = inventory.base.get_dir(entry.id)
145
if other_dir is None:
151
def entry_data(file_id, tree):
152
assert hasattr(tree, "__contains__"), "%s" % tree
153
if file_id not in tree:
154
return (None, None, "")
155
entry = tree.tree.inventory[file_id]
156
my_dir = tree.id2path(entry.parent_id)
159
return entry.name, entry.parent_id, my_dir
160
this_name, this_parent, this_dir = entry_data(entry.id, this)
161
base_name, base_parent, base_dir = entry_data(entry.id, base)
162
other_name, other_parent, other_dir = entry_data(entry.id, other)
147
163
mutter("Dirs: this, base, other %r %r %r" % (this_dir, base_dir, other_dir))
148
164
mutter("Names: this, base, other %r %r %r" % (this_name, base_name, other_name))
149
if base_name == other_name:
153
if this_name != base_name and this_name != other_name:
154
conflict_handler.rename_conflict(entry.id, this_name, base_name,
158
new_name = other_name
160
if base_parent == other_parent:
161
old_parent = this_parent
162
new_parent = this_parent
166
if this_parent != base_parent and this_parent != other_parent:
167
conflict_handler.move_conflict(entry.id, inventory)
169
old_parent = this_parent
171
new_parent = other_parent
173
if old_name is not None and old_parent is not None:
174
old_path = os.path.join(old_dir, old_name)
167
new_name = threeway_select(this_name, base_name, other_name)
168
except ThreeWayConflict:
169
new_name = conflict_handler.rename_conflict(entry.id, this_name,
170
base_name, other_name)
172
old_parent = this_parent
174
new_parent = threeway_select(this_parent, base_parent, other_parent)
175
except ThreeWayConflict:
176
new_parent = conflict_handler.move_conflict(entry.id, this_dir,
178
def get_path(name, parent):
179
if name is not None and parent is not None:
180
parent_dir = {this_parent: this_dir, other_parent: other_dir,
181
base_parent: base_dir}
182
directory = parent_dir[parent]
183
return os.path.join(directory, name)
185
assert name is None and parent is None
188
old_path = get_path(old_name, old_parent)
177
190
new_entry = changeset.ChangesetEntry(entry.id, old_parent, old_path)
178
if new_name is not None and new_parent is not None:
179
new_entry.new_path = os.path.join(new_dir, new_name)
181
new_entry.new_path = None
191
new_entry.new_path = get_path(new_name, new_parent)
182
192
new_entry.new_parent = new_parent
183
193
mutter(repr(new_entry))
187
def make_merged_contents(entry, this, base, other, inventory, conflict_handler,
197
def make_merged_contents(entry, this, base, other, conflict_handler,
189
199
contents = entry.contents_change
190
200
if contents is None:
270
from bzrlib.inventory import InventoryEntry, RootEntry
271
from osutils import file_kind
272
class FalseTree(object):
273
def __init__(self, realtree):
274
self._realtree = realtree
275
self.inventory = self
277
def __getitem__(self, file_id):
278
entry = self.make_inventory_entry(file_id)
280
raise KeyError(file_id)
283
def make_inventory_entry(self, file_id):
284
path = self._realtree.inventory.get(file_id)
288
return RootEntry(file_id)
289
dir, name = os.path.split(path)
290
kind = file_kind(self._realtree.abs_path(path))
291
for parent_id, path in self._realtree.inventory.iteritems():
295
raise Exception("Can't find parent for %s" % name)
296
return InventoryEntry(file_id, name, kind, parent_id)
262
299
class MergeTree(object):
263
300
def __init__(self, dir):
266
303
self.inventory = {'0': ""}
304
self.tree = FalseTree(self)
268
306
def child_path(self, parent, name):
269
307
return os.path.join(self.inventory[parent], name)
432
490
os.chmod(tree.full_path(id), mode)
434
492
def merge_changeset(self, merge_factory):
435
all_inventory = ThreewayInventory(Inventory(self.this.inventory),
436
Inventory(self.base.inventory),
437
Inventory(self.other.inventory))
438
493
conflict_handler = changeset.ExceptionConflictHandler(self.this.dir)
439
return make_merge_changeset(self.cset, all_inventory, self.this,
440
self.base, self.other, conflict_handler,
494
return make_merge_changeset(self.cset, self.this, self.base,
495
self.other, conflict_handler,
443
498
def apply_inv_change(self, inventory_change, orig_inventory):
594
649
builder.add_file("2", "0", "name3", "text2", 0655)
595
650
builder.change_contents("2", base="text5")
596
651
builder.add_file("3", "0", "name5", "text3", 0744)
652
builder.add_file("4", "0", "name6", "text4", 0744)
653
builder.remove_file("4", base=True)
654
assert not builder.cset.entries["4"].is_boring()
597
655
builder.change_contents("3", this="text6")
598
656
cset = builder.merge_changeset(merge_factory)
599
657
assert(cset.entries["1"].contents_change is not None)