35
42
raise Exception("%s not in tree" % self.file_id)
37
44
return tree.get_file(self.file_id).readlines()
39
other_entry = other.tree.inventory[self.file_id]
40
if other_entry.kind == 'symlink':
41
self.apply_symlink(other_entry, base, other, filename)
43
45
base_lines = get_lines(base)
44
46
other_lines = get_lines(other)
45
47
m3 = Merge3(base_lines, file(filename, "rb").readlines(), other_lines)
63
65
conflict_handler.merge_conflict(new_file, filename, base_lines,
66
def apply_symlink(self, other_entry, base, other, filename):
67
if self.file_id in base:
68
base_entry = base.tree.inventory[self.file_id]
69
base_entry._read_tree_state(base.tree)
72
other_entry._read_tree_state(other.tree)
73
if not base_entry or other_entry.detect_changes(base_entry):
77
this_link = os.readlink(filename)
78
if not base_entry or base_entry.symlink_target != this_link:
82
if this_change and not other_change:
84
elif not this_change and other_change:
86
os.symlink(other_entry.symlink_target, filename)
87
elif this_change and other_change:
90
os.symlink(other_entry.symlink_target, filename + '.OTHER')
91
os.symlink(this_link, filename + '.THIS')
92
if base_entry is not None:
93
os.symlink(other_entry.symlink_target, filename + '.BASE')
94
note("merge3 conflict in '%s'.\n", filename)
97
69
class BackupBeforeChange:
98
70
"""Contents-change wrapper to back up file first"""
99
71
def __init__(self, contents_change):
100
72
self.contents_change = contents_change
74
def is_creation(self):
75
return self.contents_change.is_creation()
77
def is_deletion(self):
78
return self.contents_change.is_deletion()
102
80
def __eq__(self, other):
103
81
if not isinstance(other, BackupBeforeChange):
129
107
cset = changeset_function(base, other, interesting_ids)
130
108
new_cset = make_merge_changeset(cset, this, base, other,
131
109
conflict_handler, merge_factory)
132
result = apply_changeset(new_cset, invert_invent(this.tree.inventory),
133
this.root, conflict_handler, False)
110
result = apply_changeset(new_cset, invert_invent(this.inventory),
111
this.basedir, conflict_handler, False)
134
112
conflict_handler.finalize()
139
117
def make_merge_changeset(cset, this, base, other,
140
118
conflict_handler, merge_factory):
141
119
new_cset = changeset.Changeset()
142
def get_this_contents(id):
143
path = this.readonly_path(id)
144
if os.path.isdir(path):
145
return changeset.dir_create
147
return changeset.FileCreate(file(path, "rb").read())
149
121
for entry in cset.entries.itervalues():
150
122
if entry.is_boring():
236
def get_contents(entry, tree):
237
return get_id_contents(entry.id, tree)
239
def get_id_contents(file_id, tree):
240
"""Get a contents change element suitable for use with ReplaceContents
242
tree_entry = tree.tree.inventory[file_id]
243
if tree_entry.kind == "file":
244
return changeset.FileCreate(tree.get_file(file_id).read())
245
elif tree_entry.kind == "symlink":
246
return changeset.SymlinkCreate(tree.get_symlink_target(file_id))
248
assert tree_entry.kind in ("root_directory", "directory")
249
return changeset.dir_create
251
208
def make_merged_contents(entry, this, base, other, conflict_handler,
253
210
contents = entry.contents_change
254
211
if contents is None:
256
this_path = this.readonly_path(entry.id)
214
this_path = this.id2abspath(entry.id)
257
217
def make_merge():
258
218
if this_path is None:
259
219
return conflict_handler.missing_for_merge(entry.id,
276
236
if this_path is None or not bzrlib.osutils.lexists(this_path):
279
this_contents = get_contents(entry, this)
239
this_contents = get_contents(this, entry.id)
280
240
if this_contents == contents.new_contents:
283
other_path = other.readonly_path(entry.id)
284
243
conflict_handler.new_contents_conflict(this_path,
286
elif (isinstance(contents.old_contents, changeset.FileCreate)
287
and isinstance(contents.new_contents, changeset.FileCreate)):
289
elif (isinstance(contents.old_contents, changeset.SymlinkCreate)
290
and isinstance(contents.new_contents, changeset.SymlinkCreate)):
245
elif isinstance(contents.old_contents, changeset.TreeFileCreate) and \
246
isinstance(contents.new_contents, changeset.TreeFileCreate):
291
247
return make_merge()
293
raise Exception("Unhandled merge scenario")
249
this_contents = get_contents(this, entry.id)
250
if this_contents == contents.old_contents:
252
elif this_contents == contents.new_contents:
254
elif contents.old_contents == contents.new_contents:
257
conflict_handler.threeway_contents_conflict(this_path,
258
this_contents, contents.old_contents,
259
contents.new_contents)
295
262
def make_merged_metadata(entry, base, other):
296
263
metadata = entry.metadata_change
297
264
if metadata is None:
299
if isinstance(metadata, changeset.ChangeExecFlag):
300
if metadata.new_exec_flag is None:
302
elif metadata.old_exec_flag is None:
305
base_path = base.readonly_path(entry.id)
306
other_path = other.readonly_path(entry.id)
307
return ExecFlagMerge(base_path, other_path)
266
assert isinstance(metadata, changeset.ChangeExecFlag)
267
if metadata.new_exec_flag is None:
269
elif metadata.old_exec_flag is None:
272
return ExecFlagMerge(base, other, entry.id)
310
275
class ExecFlagMerge(object):
311
def __init__(self, base_path, other_path):
312
self.base_path = base_path
313
self.other_path = other_path
276
def __init__(self, base_tree, other_tree, file_id):
277
self.base_tree = base_tree
278
self.other_tree = other_tree
279
self.file_id = file_id
315
281
def apply(self, filename, conflict_handler, reverse=False):
317
base = self.base_path
318
other = self.other_path
283
base = self.base_tree
284
other = self.other_tree
320
base = self.other_path
321
other = self.base_path
322
base_mode = os.stat(base).st_mode
323
base_exec_flag = bool(base_mode & 0111)
324
other_mode = os.stat(other).st_mode
325
other_exec_flag = bool(other_mode & 0111)
286
base = self.other_tree
287
other = self.base_tree
288
base_exec_flag = base.is_executable(self.file_id)
289
other_exec_flag = other.is_executable(self.file_id)
326
290
this_mode = os.stat(filename).st_mode
327
291
this_exec_flag = bool(this_mode & 0111)
328
292
if (base_exec_flag != other_exec_flag and