20
20
from bzrlib.trace import mutter
21
from bzrlib.osutils import rename
21
from bzrlib.osutils import rename, sha_file
23
from itertools import izip
24
25
# XXX: mbp: I'm not totally convinced that we should handle conflicts
25
26
# as part of changeset application, rather than only in the merge
234
class TreeFileCreate(object):
235
"""Create or delete a file (for use with ReplaceContents)"""
236
def __init__(self, tree, file_id):
239
:param contents: The contents of the file to write
243
self.file_id = file_id
246
return "TreeFileCreate(%i)" % self.id
248
def __eq__(self, other):
249
if not isinstance(other, TreeFileCreate):
251
return self.tree.get_file_sha1(self.file_id) == \
252
other.tree.get_file_sha1(other.file_id)
254
def __ne__(self, other):
255
return not (self == other)
257
def write_file(self, filename):
258
outfile = file(filename, "wb")
259
for line in self.tree.get_file(self.file_id):
262
def same_text(self, filename):
263
in_file = file(filename, "rb")
264
return sha_file(in_file) == self.tree.get_file_sha1(self.file_id)
266
def __call__(self, filename, conflict_handler, reverse):
267
"""Create or delete a file
269
:param filename: The name of the file to create
271
:param reverse: Delete the file instead of creating it
276
self.write_file(filename)
278
if e.errno == errno.ENOENT:
279
if conflict_handler.missing_parent(filename)=="continue":
280
self.write_file(filename)
286
if not self.same_text(filename):
287
direction = conflict_handler.wrong_old_contents(filename,
288
self.tree.get_file(self.file_id).read())
289
if direction != "continue":
293
if e.errno != errno.ENOENT:
295
if conflict_handler.missing_for_rm(filename, undo) == "skip":
233
300
def reversed(sequence):
234
301
max = len(sequence) - 1
235
302
for i in range(len(sequence)):
302
369
if mode is not None:
303
370
os.chmod(filename, mode)
372
def is_creation(self):
373
return self.new_contents is not None and self.old_contents is None
375
def is_deletion(self):
376
return self.old_contents is not None and self.new_contents is None
305
378
class ApplySequence(object):
306
379
def __init__(self, changes=None):
307
380
self.changes = []
412
491
return ReplaceContents(FileCreate(contents), None)
414
def ReplaceFileContents(old_contents, new_contents):
493
def ReplaceFileContents(old_tree, new_tree, file_id):
415
494
"""Convenience fucntion to replace the contents of a file.
417
496
:param old_contents: The contents of the file to replace
421
500
:return: A ReplaceContents that will replace the contents of a file a file
422
501
:rtype: `ReplaceContents`
424
return ReplaceContents(FileCreate(old_contents), FileCreate(new_contents))
503
return ReplaceContents(TreeFileCreate(old_tree, file_id),
504
TreeFileCreate(new_tree, file_id))
426
506
def CreateSymlink(target):
427
507
"""Convenience fucntion to create a symlink.
590
670
:param reverse: if true, the changeset is being applied in reverse
593
return ((self.new_parent is None and not reverse) or
594
(self.parent is None and reverse))
673
return self.is_creation(not reverse)
596
675
def is_creation(self, reverse):
597
676
"""Return true if applying the entry would create a file/directory.
599
678
:param reverse: if true, the changeset is being applied in reverse
602
return ((self.parent is None and not reverse) or
603
(self.new_parent is None and reverse))
681
if self.contents_change is None:
684
return self.contents_change.is_deletion()
686
return self.contents_change.is_creation()
605
688
def is_creation_or_deletion(self):
606
689
"""Return true if applying the entry would create or delete a
611
return self.parent is None or self.new_parent is None
694
return self.is_creation(False) or self.is_deletion(False)
613
696
def get_cset_path(self, mod=False):
614
697
"""Determine the path of the entry according to the changeset.
786
869
:rtype: (List, List)
788
871
source_entries = [x for x in changeset.entries.itervalues()
872
if x.needs_rename() or x.is_creation_or_deletion()]
790
873
# these are done from longest path to shortest, to avoid deleting a
791
874
# parent before its children are deleted/renamed
792
875
def longest_to_shortest(entry):
833
916
entry.apply(path, conflict_handler, reverse)
834
917
temp_name[entry.id] = None
919
elif entry.needs_rename():
837
920
to_name = os.path.join(temp_dir, str(i))
838
921
src_path = inventory.get(entry.id)
839
922
if src_path is not None:
878
961
if entry.is_creation(reverse):
879
962
entry.apply(new_path, conflict_handler, reverse)
880
963
changed_inventory[entry.id] = new_tree_path
964
elif entry.needs_rename():
882
965
if old_path is None:
1089
1172
#apply changes that don't affect filenames
1090
1173
for entry in changeset.entries.itervalues():
1091
if not entry.is_creation_or_deletion():
1174
if not entry.is_creation_or_deletion() and not entry.is_boring():
1092
1175
path = os.path.join(dir, inventory[entry.id])
1093
1176
entry.apply(path, conflict_handler, reverse)
1414
1497
kind = tree.kind(file_id)
1415
1498
if kind == "file":
1416
return FileCreate(tree.get_file(file_id).read())
1499
return TreeFileCreate(tree, file_id)
1417
1500
elif kind in ("directory", "root_directory"):
1418
1501
return dir_create
1419
1502
elif kind == "symlink":