195
"""Construct a WorkingTree instance. This is not a public API.
211
"""Construct a WorkingTree for basedir.
197
:param branch: A branch to override probing for the branch.
213
If the branch is not supplied, it is opened automatically.
214
If the branch is supplied, it must be the branch for this basedir.
215
(branch.base is not cross checked, because for remote branches that
216
would be meaningless).
199
218
self._format = _format
200
219
self.bzrdir = _bzrdir
201
220
if not _internal:
202
raise errors.BzrError("Please use bzrdir.open_workingtree or "
203
"WorkingTree.open() to obtain a WorkingTree.")
221
# not created via open etc.
222
warnings.warn("WorkingTree() is deprecated as of bzr version 0.8. "
223
"Please use bzrdir.open_workingtree or WorkingTree.open().",
226
wt = WorkingTree.open(basedir)
227
self._branch = wt.branch
228
self.basedir = wt.basedir
229
self._control_files = wt._control_files
230
self._hashcache = wt._hashcache
231
self._set_inventory(wt._inventory, dirty=False)
232
self._format = wt._format
233
self.bzrdir = wt.bzrdir
234
assert isinstance(basedir, basestring), \
235
"base directory %r is not a string" % basedir
204
236
basedir = safe_unicode(basedir)
205
237
mutter("opening working tree %r", basedir)
206
238
if deprecated_passed(branch):
240
warnings.warn("WorkingTree(..., branch=XXX) is deprecated"
241
" as of bzr 0.8. Please use bzrdir.open_workingtree() or"
242
" WorkingTree.open().",
207
246
self._branch = branch
209
248
self._branch = self.bzrdir.open_branch()
406
415
# the basis tree is a ghost so return an empty tree.
407
416
return self.branch.repository.revision_tree(None)
410
self._flush_ignore_list_cache()
419
@deprecated_method(zero_eight)
420
def create(branch, directory):
421
"""Create a workingtree for branch at directory.
423
If existing_directory already exists it must have a .bzr directory.
424
If it does not exist, it will be created.
426
This returns a new WorkingTree object for the new checkout.
428
TODO FIXME RBC 20060124 when we have checkout formats in place this
429
should accept an optional revisionid to checkout [and reject this if
430
checking out into the same dir as a pre-checkout-aware branch format.]
432
XXX: When BzrDir is present, these should be created through that
435
warnings.warn('delete WorkingTree.create', stacklevel=3)
436
transport = get_transport(directory)
437
if branch.bzrdir.root_transport.base == transport.base:
439
return branch.bzrdir.create_workingtree()
440
# different directory,
441
# create a branch reference
442
# and now a working tree.
443
raise NotImplementedError
446
@deprecated_method(zero_eight)
447
def create_standalone(directory):
448
"""Create a checkout and a branch and a repo at directory.
450
Directory must exist and be empty.
452
please use BzrDir.create_standalone_workingtree
454
return bzrdir.BzrDir.create_standalone_workingtree(directory)
412
456
def relpath(self, path):
413
457
"""Return the local path portion from a given path.
555
601
__contains__ = has_id
557
603
def get_file_size(self, file_id):
558
"""See Tree.get_file_size"""
560
return os.path.getsize(self.id2abspath(file_id))
562
if e.errno != errno.ENOENT:
604
file_id = osutils.safe_file_id(file_id)
605
return os.path.getsize(self.id2abspath(file_id))
568
608
def get_file_sha1(self, file_id, path=None, stat_value=None):
609
file_id = osutils.safe_file_id(file_id)
570
611
path = self._inventory.id2path(file_id)
571
612
return self._hashcache.get_sha1(path, stat_value)
573
614
def get_file_mtime(self, file_id, path=None):
615
file_id = osutils.safe_file_id(file_id)
575
617
path = self.inventory.id2path(file_id)
576
618
return os.lstat(self.abspath(path)).st_mtime
578
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
579
file_id = self.path2id(path)
580
return self._inventory[file_id].executable
582
def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
583
mode = stat_result.st_mode
584
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
586
620
if not supports_executable():
587
621
def is_executable(self, file_id, path=None):
622
file_id = osutils.safe_file_id(file_id)
588
623
return self._inventory[file_id].executable
590
_is_executable_from_path_and_stat = \
591
_is_executable_from_path_and_stat_from_basis
593
625
def is_executable(self, file_id, path=None):
627
file_id = osutils.safe_file_id(file_id)
595
628
path = self.id2path(file_id)
596
629
mode = os.lstat(self.abspath(path)).st_mode
597
630
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
599
_is_executable_from_path_and_stat = \
600
_is_executable_from_path_and_stat_from_stat
602
632
@needs_tree_write_lock
603
633
def _add(self, files, ids, kinds):
604
634
"""See MutableTree._add."""
679
711
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
681
def path_content_summary(self, path, _lstat=os.lstat,
682
_mapper=osutils.file_kind_from_stat_mode):
683
"""See Tree.path_content_summary."""
684
abspath = self.abspath(path)
686
stat_result = _lstat(abspath)
688
if getattr(e, 'errno', None) == errno.ENOENT:
690
return ('missing', None, None, None)
691
# propagate other errors
693
kind = _mapper(stat_result.st_mode)
695
size = stat_result.st_size
696
# try for a stat cache lookup
697
executable = self._is_executable_from_path_and_stat(path, stat_result)
698
return (kind, size, executable, self._sha_from_stat(
700
elif kind == 'directory':
701
# perhaps it looks like a plain directory, but it's really a
703
if self._directory_is_tree_reference(path):
704
kind = 'tree-reference'
705
return kind, None, None, None
706
elif kind == 'symlink':
707
return ('symlink', None, None, os.readlink(abspath))
709
return (kind, None, None, None)
713
@deprecated_method(zero_eleven)
715
def pending_merges(self):
716
"""Return a list of pending merges.
718
These are revisions that have been merged into the working
719
directory but not yet committed.
721
As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
722
instead - which is available on all tree objects.
724
return self.get_parent_ids()[1:]
711
726
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
712
727
"""Common ghost checking functionality from set_parent_*.
757
752
:param revision_ids: The revision_ids to set as the parent ids of this
758
753
working tree. Any of these may be ghosts.
755
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
760
756
self._check_parents_for_ghosts(revision_ids,
761
757
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
762
for revision_id in revision_ids:
763
_mod_revision.check_not_reserved_id(revision_id)
765
revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
767
759
if len(revision_ids) > 0:
768
760
self.set_last_revision(revision_ids[0])
770
self.set_last_revision(_mod_revision.NULL_REVISION)
762
self.set_last_revision(None)
772
764
self._set_merges_from_parent_ids(revision_ids)
774
766
@needs_tree_write_lock
775
767
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
776
768
"""See MutableTree.set_parent_trees."""
777
parent_ids = [rev for (rev, tree) in parents_list]
778
for revision_id in parent_ids:
779
_mod_revision.check_not_reserved_id(revision_id)
769
parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
781
771
self._check_parents_for_ghosts(parent_ids,
782
772
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
784
parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
786
774
if len(parent_ids) == 0:
787
leftmost_parent_id = _mod_revision.NULL_REVISION
775
leftmost_parent_id = None
788
776
leftmost_parent_tree = None
790
778
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
967
940
other_tree.unlock()
968
941
other_tree.bzrdir.retire_bzrdir()
970
def _setup_directory_is_tree_reference(self):
971
if self._branch.repository._format.supports_tree_reference:
972
self._directory_is_tree_reference = \
973
self._directory_may_be_tree_reference
975
self._directory_is_tree_reference = \
976
self._directory_is_never_tree_reference
978
def _directory_is_never_tree_reference(self, relpath):
981
def _directory_may_be_tree_reference(self, relpath):
982
# as a special case, if a directory contains control files then
983
# it's a tree reference, except that the root of the tree is not
984
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
985
# TODO: We could ask all the control formats whether they
986
# recognize this directory, but at the moment there's no cheap api
987
# to do that. Since we probably can only nest bzr checkouts and
988
# they always use this name it's ok for now. -- mbp 20060306
990
# FIXME: There is an unhandled case here of a subdirectory
991
# containing .bzr but not a branch; that will probably blow up
992
# when you try to commit it. It might happen if there is a
993
# checkout in a subdirectory. This can be avoided by not adding
996
943
@needs_tree_write_lock
997
944
def extract(self, file_id, format=None):
998
945
"""Extract a subtree from this tree.
1834
1783
if self._inventory_is_modified:
1835
1784
raise errors.InventoryModified(self)
1836
result = self._deserialize(self._transport.get('inventory'))
1785
result = self._deserialize(self._control_files.get('inventory'))
1837
1786
self._set_inventory(result, dirty=False)
1840
1789
@needs_tree_write_lock
1841
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1843
"""Remove nominated files from the working inventory.
1845
:files: File paths relative to the basedir.
1846
:keep_files: If true, the files will also be kept.
1847
:force: Delete files and directories, even if they are changed and
1848
even if the directories are not empty.
1790
def remove(self, files, verbose=False, to_file=None):
1791
"""Remove nominated files from the working inventory..
1793
This does not remove their text. This does not run on XXX on what? RBC
1795
TODO: Refuse to remove modified files unless --force is given?
1797
TODO: Do something useful with directories.
1799
TODO: Should this remove the text or not? Tough call; not
1800
removing may be useful and the user can just use use rm, and
1801
is the opposite of add. Removing it is consistent with most
1802
other tools. Maybe an option.
1804
## TODO: Normalize names
1805
## TODO: Remove nested loops; better scalability
1850
1806
if isinstance(files, basestring):
1851
1807
files = [files]
1856
unknown_nested_files=set()
1858
def recurse_directory_to_add_files(directory):
1859
# Recurse directory and add all files
1860
# so we can check if they have changed.
1861
for parent_info, file_infos in\
1862
self.walkdirs(directory):
1863
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1864
# Is it versioned or ignored?
1865
if self.path2id(relpath) or self.is_ignored(relpath):
1866
# Add nested content for deletion.
1867
new_files.add(relpath)
1869
# Files which are not versioned and not ignored
1870
# should be treated as unknown.
1871
unknown_nested_files.add((relpath, None, kind))
1873
for filename in files:
1874
# Get file name into canonical form.
1875
abspath = self.abspath(filename)
1876
filename = self.relpath(abspath)
1877
if len(filename) > 0:
1878
new_files.add(filename)
1879
recurse_directory_to_add_files(filename)
1881
files = list(new_files)
1884
return # nothing to do
1886
# Sort needed to first handle directory content before the directory
1887
files.sort(reverse=True)
1889
# Bail out if we are going to delete files we shouldn't
1890
if not keep_files and not force:
1891
has_changed_files = len(unknown_nested_files) > 0
1892
if not has_changed_files:
1893
for (file_id, path, content_change, versioned, parent_id, name,
1894
kind, executable) in self.iter_changes(self.basis_tree(),
1895
include_unchanged=True, require_versioned=False,
1896
want_unversioned=True, specific_files=files):
1897
if versioned == (False, False):
1898
# The record is unknown ...
1899
if not self.is_ignored(path[1]):
1900
# ... but not ignored
1901
has_changed_files = True
1903
elif content_change and (kind[1] is not None):
1904
# Versioned and changed, but not deleted
1905
has_changed_files = True
1908
if has_changed_files:
1909
# Make delta show ALL applicable changes in error message.
1910
tree_delta = self.changes_from(self.basis_tree(),
1911
require_versioned=False, want_unversioned=True,
1912
specific_files=files)
1913
for unknown_file in unknown_nested_files:
1914
if unknown_file not in tree_delta.unversioned:
1915
tree_delta.unversioned.extend((unknown_file,))
1916
raise errors.BzrRemoveChangedFilesError(tree_delta)
1918
# Build inv_delta and delete files where applicaple,
1919
# do this before any modifications to inventory.
1809
inv = self.inventory
1811
# do this before any modifications
1920
1812
for f in files:
1921
fid = self.path2id(f)
1813
fid = inv.path2id(f)
1924
message = "%s is not versioned." % (f,)
1815
note("%s is not versioned."%f)
1927
# having removed it, it must be either ignored or unknown
1818
# having remove it, it must be either ignored or unknown
1928
1819
if self.is_ignored(f):
1929
1820
new_status = 'I'
1931
1822
new_status = '?'
1932
textui.show_status(new_status, self.kind(fid), f,
1823
textui.show_status(new_status, inv[fid].kind, f,
1933
1824
to_file=to_file)
1935
inv_delta.append((f, None, fid, None))
1936
message = "removed %s" % (f,)
1939
abs_path = self.abspath(f)
1940
if osutils.lexists(abs_path):
1941
if (osutils.isdir(abs_path) and
1942
len(os.listdir(abs_path)) > 0):
1944
osutils.rmtree(abs_path)
1946
message = "%s is not an empty directory "\
1947
"and won't be deleted." % (f,)
1949
osutils.delete_any(abs_path)
1950
message = "deleted %s" % (f,)
1951
elif message is not None:
1952
# Only care if we haven't done anything yet.
1953
message = "%s does not exist." % (f,)
1955
# Print only one message (if any) per file.
1956
if message is not None:
1958
self.apply_inventory_delta(inv_delta)
1827
self._write_inventory(inv)
1960
1829
@needs_tree_write_lock
1961
def revert(self, filenames=None, old_tree=None, backups=True,
1830
def revert(self, filenames, old_tree=None, backups=True,
1962
1831
pb=DummyProgress(), report_changes=False):
1963
1832
from bzrlib.conflicts import resolve
1966
symbol_versioning.warn('Using [] to revert all files is deprecated'
1967
' as of bzr 0.91. Please use None (the default) instead.',
1968
DeprecationWarning, stacklevel=2)
1969
1833
if old_tree is None:
1970
basis_tree = self.basis_tree()
1971
basis_tree.lock_read()
1972
old_tree = basis_tree
1834
old_tree = self.basis_tree()
1835
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1837
if not len(filenames):
1838
self.set_parent_ids(self.get_parent_ids()[:1])
1976
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1978
if filenames is None and len(self.get_parent_ids()) > 1:
1980
last_revision = self.last_revision()
1981
if last_revision != NULL_REVISION:
1982
if basis_tree is None:
1983
basis_tree = self.basis_tree()
1984
basis_tree.lock_read()
1985
parent_trees.append((last_revision, basis_tree))
1986
self.set_parent_trees(parent_trees)
1989
resolve(self, filenames, ignore_misses=True, recursive=True)
1991
if basis_tree is not None:
1841
resolve(self, filenames, ignore_misses=True)
1993
1842
return conflicts
1995
1844
def revision_tree(self, revision_id):
2724
2528
sio = StringIO()
2725
2529
inv = Inventory()
2726
xml5.serializer_v5.write_inventory(inv, sio, working=True)
2530
xml5.serializer_v5.write_inventory(inv, sio)
2728
branch._transport.put_file('inventory', sio,
2729
mode=branch.control_files._file_mode)
2730
branch._transport.put_bytes('pending-merges', '',
2731
mode=branch.control_files._file_mode)
2532
control_files.put('inventory', sio)
2534
control_files.put_bytes('pending-merges', '')
2734
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2735
accelerator_tree=None, hardlink=False):
2537
def initialize(self, a_bzrdir, revision_id=None):
2736
2538
"""See WorkingTreeFormat.initialize()."""
2737
2539
if not isinstance(a_bzrdir.transport, LocalTransport):
2738
2540
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2739
if from_branch is not None:
2740
branch = from_branch
2742
branch = a_bzrdir.open_branch()
2743
if revision_id is None:
2744
revision_id = _mod_revision.ensure_null(branch.last_revision())
2747
branch.generate_revision_history(revision_id)
2541
branch = a_bzrdir.open_branch()
2542
if revision_id is not None:
2543
revision_id = osutils.safe_revision_id(revision_id)
2546
revision_history = branch.revision_history()
2548
position = revision_history.index(revision_id)
2550
raise errors.NoSuchRevision(branch, revision_id)
2551
branch.set_revision_history(revision_history[:position + 1])
2554
revision = branch.last_revision()
2750
2555
inv = Inventory()
2751
2556
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2926
2715
# and not independently creatable, so are not registered.
2927
2716
_legacy_formats = [WorkingTreeFormat2(),
2720
class WorkingTreeTestProviderAdapter(object):
2721
"""A tool to generate a suite testing multiple workingtree formats at once.
2723
This is done by copying the test once for each transport and injecting
2724
the transport_server, transport_readonly_server, and workingtree_format
2725
classes into each copy. Each copy is also given a new id() to make it
2729
def __init__(self, transport_server, transport_readonly_server, formats):
2730
self._transport_server = transport_server
2731
self._transport_readonly_server = transport_readonly_server
2732
self._formats = formats
2734
def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2735
"""Clone test for adaption."""
2736
new_test = deepcopy(test)
2737
new_test.transport_server = self._transport_server
2738
new_test.transport_readonly_server = self._transport_readonly_server
2739
new_test.bzrdir_format = bzrdir_format
2740
new_test.workingtree_format = workingtree_format
2741
def make_new_test_id():
2742
new_id = "%s(%s)" % (test.id(), variation)
2743
return lambda: new_id
2744
new_test.id = make_new_test_id()
2747
def adapt(self, test):
2748
from bzrlib.tests import TestSuite
2749
result = TestSuite()
2750
for workingtree_format, bzrdir_format in self._formats:
2751
new_test = self._clone_test(
2754
workingtree_format, workingtree_format.__class__.__name__)
2755
result.addTest(new_test)