37
38
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
38
ReusingTransform, NotVersionedError, CantMoveRoot,
39
ReusingTransform, CantMoveRoot,
39
40
ExistingLimbo, ImmortalLimbo, NoFinalPath,
40
41
UnableCreateSymlink)
41
42
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
315
316
def delete_contents(self, trans_id):
316
317
"""Schedule the contents of a path entry for deletion"""
317
# Ensure that the object exists in the WorkingTree, this will raise an
318
# exception if there is a problem
319
self.tree_kind(trans_id)
320
self._removed_contents.add(trans_id)
318
kind = self.tree_kind(trans_id)
320
self._removed_contents.add(trans_id)
322
322
def cancel_deletion(self, trans_id):
323
323
"""Cancel a scheduled deletion"""
388
388
changed_kind = set(self._removed_contents)
389
389
changed_kind.intersection_update(self._new_contents)
390
390
changed_kind.difference_update(new_ids)
391
changed_kind = (t for t in changed_kind if self.tree_kind(t) !=
391
changed_kind = (t for t in changed_kind
392
if self.tree_kind(t) != self.final_kind(t))
393
393
new_ids.update(changed_kind)
394
394
return sorted(FinalPaths(self).get_paths(new_ids))
396
396
def final_kind(self, trans_id):
397
397
"""Determine the final file kind, after any changes applied.
399
Raises NoSuchFile if the file does not exist/has no contents.
400
(It is conceivable that a path would be created without the
401
corresponding contents insertion command)
399
:return: None if the file does not exist/has no contents. (It is
400
conceivable that a path would be created without the corresponding
401
contents insertion command)
403
403
if trans_id in self._new_contents:
404
404
return self._new_contents[trans_id]
405
405
elif trans_id in self._removed_contents:
406
raise NoSuchFile(None)
408
408
return self.tree_kind(trans_id)
597
597
for trans_id in self._new_id.iterkeys():
599
kind = self.final_kind(trans_id)
598
kind = self.final_kind(trans_id)
601
600
conflicts.append(('versioning no contents', trans_id))
603
602
if not InventoryEntry.versionable_kind(kind):
617
616
if self.final_file_id(trans_id) is None:
618
617
conflicts.append(('unversioned executability', trans_id))
621
non_file = self.final_kind(trans_id) != "file"
619
if self.final_kind(trans_id) != "file":
625
620
conflicts.append(('non-file executability', trans_id))
629
624
"""Check for overwrites (not permitted on Win32)"""
631
626
for trans_id in self._new_contents:
633
self.tree_kind(trans_id)
627
if self.tree_kind(trans_id) is None:
636
629
if trans_id not in self._removed_contents:
637
630
conflicts.append(('overwrite', trans_id,
652
645
last_trans_id = None
653
646
for name, trans_id in name_ids:
655
kind = self.final_kind(trans_id)
647
kind = self.final_kind(trans_id)
658
648
file_id = self.final_file_id(trans_id)
659
649
if kind is None and file_id is None:
687
677
if not self._any_contents(children):
689
for child in children:
691
self.final_kind(child)
695
kind = self.final_kind(parent_id)
679
kind = self.final_kind(parent_id)
699
681
conflicts.append(('missing parent', parent_id))
700
682
elif kind != "directory":
704
686
def _any_contents(self, trans_ids):
705
687
"""Return true if any of the trans_ids, will have contents."""
706
688
for trans_id in trans_ids:
708
kind = self.final_kind(trans_id)
689
if self.final_kind(trans_id) is not None:
714
693
def _set_executability(self, path, trans_id):
844
823
Return a (name, parent, kind, executable) tuple
846
825
to_name = self.final_name(to_trans_id)
848
to_kind = self.final_kind(to_trans_id)
826
to_kind = self.final_kind(to_trans_id)
851
827
to_parent = self.final_file_id(self.final_parent(to_trans_id))
852
828
if to_trans_id in self._new_executability:
853
829
to_executable = self._new_executability[to_trans_id]
928
904
return _PreviewTree(self)
930
def commit(self, branch, message, merge_parents=None, strict=False):
906
def commit(self, branch, message, merge_parents=None, strict=False,
907
timestamp=None, timezone=None, committer=None, authors=None,
908
revprops=None, revision_id=None):
931
909
"""Commit the result of this TreeTransform to a branch.
933
911
:param branch: The branch to commit to.
934
912
:param message: The message to attach to the commit.
935
:param merge_parents: Additional parents specified by pending merges.
913
:param merge_parents: Additional parent revision-ids specified by
915
:param strict: If True, abort the commit if there are unversioned
917
:param timestamp: if not None, seconds-since-epoch for the time and
918
date. (May be a float.)
919
:param timezone: Optional timezone for timestamp, as an offset in
921
:param committer: Optional committer in email-id format.
922
(e.g. "J Random Hacker <jrandom@example.com>")
923
:param authors: Optional list of authors in email-id format.
924
:param revprops: Optional dictionary of revision properties.
925
:param revision_id: Optional revision id. (Specifying a revision-id
926
may reduce performance for some non-native formats.)
936
927
:return: The revision_id of the revision committed.
938
929
self._check_malformed()
955
946
if self._tree.get_revision_id() != last_rev_id:
956
947
raise ValueError('TreeTransform not based on branch basis: %s' %
957
948
self._tree.get_revision_id())
958
builder = branch.get_commit_builder(parent_ids)
949
revprops = commit.Commit.update_revprops(revprops, branch, authors)
950
builder = branch.get_commit_builder(parent_ids,
955
revision_id=revision_id)
959
956
preview = self.get_preview_tree()
960
957
list(builder.record_iter_changes(preview, last_rev_id,
961
958
self.iter_changes()))
1397
1394
def tree_kind(self, trans_id):
1398
1395
"""Determine the file kind in the working tree.
1400
Raises NoSuchFile if the file does not exist
1397
:returns: The file kind or None if the file does not exist
1402
1399
path = self._tree_id_paths.get(trans_id)
1403
1400
if path is None:
1404
raise NoSuchFile(None)
1406
1403
return file_kind(self._tree.abspath(path))
1408
if e.errno != errno.ENOENT:
1411
raise NoSuchFile(path)
1404
except errors.NoSuchFile:
1413
1407
def _set_mode(self, trans_id, mode_id, typefunc):
1414
1408
"""Set the mode of new file contents.
1583
1577
if file_id is None:
1585
1579
needs_entry = False
1587
kind = self.final_kind(trans_id)
1580
kind = self.final_kind(trans_id)
1589
1582
kind = self._tree.stored_kind(file_id)
1590
1583
parent_trans_id = self.final_parent(trans_id)
1591
1584
parent_file_id = new_path_file_ids.get(parent_trans_id)
1635
1628
or trans_id in self._new_parent):
1637
1630
mover.rename(full_path, self._limbo_name(trans_id))
1631
except errors.TransformRenameFailed, e:
1639
1632
if e.errno != errno.ENOENT:
1666
1659
if trans_id in self._needs_rename:
1668
1661
mover.rename(self._limbo_name(trans_id), full_path)
1662
except errors.TransformRenameFailed, e:
1670
1663
# We may be renaming a dangling inventory id
1671
1664
if e.errno != errno.ENOENT:
1703
1696
def tree_kind(self, trans_id):
1704
1697
path = self._tree_id_paths.get(trans_id)
1705
1698
if path is None:
1706
raise NoSuchFile(None)
1707
1700
file_id = self._tree.path2id(path)
1708
return self._tree.kind(file_id)
1702
return self._tree.kind(file_id)
1703
except errors.NoSuchFile:
1710
1706
def _set_mode(self, trans_id, mode_id, typefunc):
1711
1707
"""Set the mode of new file contents.
1770
1766
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
1771
1767
self._iter_parent_trees()]
1772
1768
vf.add_lines((file_id, tree_revision), parent_keys,
1773
self.get_file(file_id).readlines())
1769
self.get_file_lines(file_id))
1774
1770
repo = self._get_repository()
1775
1771
base_vf = repo.texts
1776
1772
if base_vf not in vf.fallback_versionedfiles:
1798
1794
executable = self.is_executable(file_id, path)
1799
1795
return kind, executable, None
1797
def is_locked(self):
1801
1800
def lock_read(self):
1802
1801
# Perhaps in theory, this should lock the TreeTransform?
1805
1804
def unlock(self):
1904
1903
if (specific_file_ids is not None
1905
1904
and file_id not in specific_file_ids):
1908
kind = self._transform.final_kind(trans_id)
1906
kind = self._transform.final_kind(trans_id)
1910
1908
kind = self._transform._tree.stored_kind(file_id)
1911
1909
new_entry = inventory.make_entry(
2144
2142
path_from_root = self._final_paths.get_path(child_id)
2145
2143
basename = self._transform.final_name(child_id)
2146
2144
file_id = self._transform.final_file_id(child_id)
2148
kind = self._transform.final_kind(child_id)
2145
kind = self._transform.final_kind(child_id)
2146
if kind is not None:
2149
2147
versioned_kind = kind
2151
2149
kind = 'unknown'
2152
2150
versioned_kind = self._transform._tree.stored_kind(file_id)
2153
2151
if versioned_kind == 'directory':
2266
2264
for num, _unused in enumerate(wt.all_file_ids()):
2267
2265
if num > 0: # more than just a root
2268
2266
raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
2269
existing_files = set()
2270
for dir, files in wt.walkdirs():
2271
existing_files.update(f[0] for f in files)
2272
2267
file_trans_id = {}
2273
2268
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2274
2269
pp = ProgressPhase("Build phase", 2, top_pb)
2298
2293
precomputed_delta = []
2300
2295
precomputed_delta = None
2296
# Check if tree inventory has content. If so, we populate
2297
# existing_files with the directory content. If there are no
2298
# entries we skip populating existing_files as its not used.
2299
# This improves performance and unncessary work on large
2300
# directory trees. (#501307)
2302
existing_files = set()
2303
for dir, files in wt.walkdirs():
2304
existing_files.update(f[0] for f in files)
2301
2305
for num, (tree_path, entry) in \
2302
2306
enumerate(tree.inventory.iter_entries_by_dir()):
2303
2307
pb.update("Building tree", num - len(deferred_contents), total)
2435
2439
if entry.kind == "directory":
2437
2441
if entry.kind == "file":
2438
if tree.get_file(file_id).read() == file(target_path, 'rb').read():
2442
f = file(target_path, 'rb')
2444
if tree.get_file_text(file_id) == f.read():
2440
2448
elif entry.kind == "symlink":
2441
2449
if tree.get_symlink_target(file_id) == os.readlink(target_path):
2494
2502
raise errors.BadFileKindError(name, kind)
2497
@deprecated_function(deprecated_in((1, 9, 0)))
2498
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
2499
"""Create new file contents according to an inventory entry.
2501
DEPRECATED. Use create_from_tree instead.
2503
if entry.kind == "file":
2505
lines = tree.get_file(entry.file_id).readlines()
2506
tt.create_file(lines, trans_id, mode_id=mode_id)
2507
elif entry.kind == "symlink":
2508
tt.create_symlink(tree.get_symlink_target(entry.file_id), trans_id)
2509
elif entry.kind == "directory":
2510
tt.create_directory(trans_id)
2513
2505
def create_from_tree(tt, trans_id, tree, file_id, bytes=None,
2514
2506
filter_tree_path=None):
2515
2507
"""Create new file contents according to tree contents.
2898
2890
self.pending_deletions = []
2900
2892
def rename(self, from_, to):
2901
"""Rename a file from one path to another. Functions like os.rename"""
2893
"""Rename a file from one path to another."""
2903
2895
os.rename(from_, to)
2904
2896
except OSError, e:
2905
2897
if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2906
2898
raise errors.FileExists(to, str(e))
2899
# normal OSError doesn't include filenames so it's hard to see where
2900
# the problem is, see https://bugs.launchpad.net/bzr/+bug/491763
2901
raise errors.TransformRenameFailed(from_, to, str(e), e.errno)
2908
2902
self.past_renames.append((from_, to))
2910
2904
def pre_delete(self, from_, to):
2920
2914
def rollback(self):
2921
2915
"""Reverse all renames that have been performed"""
2922
2916
for from_, to in reversed(self.past_renames):
2923
os.rename(to, from_)
2918
os.rename(to, from_)
2920
raise errors.TransformRenameFailed(to, from_, str(e), e.errno)
2924
2921
# after rollback, don't reuse _FileMover
2925
2922
past_renames = None
2926
2923
pending_deletions = None