387
417
# at this point ?
389
419
return self.branch.repository.revision_tree(revision_id)
390
except errors.RevisionNotPresent:
420
except (errors.RevisionNotPresent, errors.NoSuchRevision):
391
421
# the basis tree *may* be a ghost or a low level error may have
392
# occured. If the revision is present, its a problem, if its not
422
# occurred. If the revision is present, its a problem, if its not
394
424
if self.branch.repository.has_revision(revision_id):
396
426
# the basis tree is a ghost so return an empty tree.
397
return self.branch.repository.revision_tree(None)
400
@deprecated_method(zero_eight)
401
def create(branch, directory):
402
"""Create a workingtree for branch at directory.
404
If existing_directory already exists it must have a .bzr directory.
405
If it does not exist, it will be created.
407
This returns a new WorkingTree object for the new checkout.
409
TODO FIXME RBC 20060124 when we have checkout formats in place this
410
should accept an optional revisionid to checkout [and reject this if
411
checking out into the same dir as a pre-checkout-aware branch format.]
413
XXX: When BzrDir is present, these should be created through that
416
warnings.warn('delete WorkingTree.create', stacklevel=3)
417
transport = get_transport(directory)
418
if branch.bzrdir.root_transport.base == transport.base:
420
return branch.bzrdir.create_workingtree()
421
# different directory,
422
# create a branch reference
423
# and now a working tree.
424
raise NotImplementedError
427
@deprecated_method(zero_eight)
428
def create_standalone(directory):
429
"""Create a checkout and a branch and a repo at directory.
431
Directory must exist and be empty.
433
please use BzrDir.create_standalone_workingtree
435
return bzrdir.BzrDir.create_standalone_workingtree(directory)
427
return self.branch.repository.revision_tree(
428
_mod_revision.NULL_REVISION)
431
self._flush_ignore_list_cache()
437
433
def relpath(self, path):
438
434
"""Return the local path portion from a given path.
440
The path may be absolute or relative. If its a relative path it is
436
The path may be absolute or relative. If its a relative path it is
441
437
interpreted relative to the python current working directory.
443
439
return osutils.relpath(self.basedir, path)
445
441
def has_filename(self, filename):
446
442
return osutils.lexists(self.abspath(filename))
448
def get_file(self, file_id):
449
file_id = osutils.safe_file_id(file_id)
450
return self.get_file_byname(self.id2path(file_id))
452
def get_file_text(self, file_id):
453
file_id = osutils.safe_file_id(file_id)
454
return self.get_file(file_id).read()
456
def get_file_byname(self, filename):
457
return file(self.abspath(filename), 'rb')
444
def get_file(self, file_id, path=None, filtered=True):
445
return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
447
def get_file_with_stat(self, file_id, path=None, filtered=True,
449
"""See MutableTree.get_file_with_stat."""
451
path = self.id2path(file_id)
452
file_obj = self.get_file_byname(path, filtered=False)
453
stat_value = _fstat(file_obj.fileno())
454
if self.supports_content_filtering() and filtered:
455
filters = self._content_filter_stack(path)
456
file_obj = filtered_input_file(file_obj, filters)
457
return (file_obj, stat_value)
459
def get_file_text(self, file_id, path=None, filtered=True):
460
return self.get_file(file_id, path=path, filtered=filtered).read()
462
def get_file_byname(self, filename, filtered=True):
463
path = self.abspath(filename)
465
if self.supports_content_filtering() and filtered:
466
filters = self._content_filter_stack(filename)
467
return filtered_input_file(f, filters)
471
def get_file_lines(self, file_id, path=None, filtered=True):
472
"""See Tree.get_file_lines()"""
473
file = self.get_file(file_id, path, filtered=filtered)
475
return file.readlines()
460
def annotate_iter(self, file_id):
480
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
461
481
"""See Tree.annotate_iter
463
483
This implementation will use the basis tree implementation if possible.
578
600
__contains__ = has_id
580
602
def get_file_size(self, file_id):
581
file_id = osutils.safe_file_id(file_id)
582
return os.path.getsize(self.id2abspath(file_id))
603
"""See Tree.get_file_size"""
605
return os.path.getsize(self.id2abspath(file_id))
607
if e.errno != errno.ENOENT:
585
613
def get_file_sha1(self, file_id, path=None, stat_value=None):
586
file_id = osutils.safe_file_id(file_id)
588
615
path = self._inventory.id2path(file_id)
589
616
return self._hashcache.get_sha1(path, stat_value)
591
618
def get_file_mtime(self, file_id, path=None):
592
file_id = osutils.safe_file_id(file_id)
594
620
path = self.inventory.id2path(file_id)
595
621
return os.lstat(self.abspath(path)).st_mtime
623
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
624
file_id = self.path2id(path)
625
return self._inventory[file_id].executable
627
def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
628
mode = stat_result.st_mode
629
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
597
631
if not supports_executable():
598
632
def is_executable(self, file_id, path=None):
599
file_id = osutils.safe_file_id(file_id)
600
633
return self._inventory[file_id].executable
635
_is_executable_from_path_and_stat = \
636
_is_executable_from_path_and_stat_from_basis
602
638
def is_executable(self, file_id, path=None):
604
file_id = osutils.safe_file_id(file_id)
605
640
path = self.id2path(file_id)
606
641
mode = os.lstat(self.abspath(path)).st_mode
607
642
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
644
_is_executable_from_path_and_stat = \
645
_is_executable_from_path_and_stat_from_stat
609
647
@needs_tree_write_lock
610
648
def _add(self, files, ids, kinds):
611
649
"""See MutableTree._add."""
612
650
# TODO: Re-adding a file that is removed in the working copy
613
651
# should probably put it back with the previous ID.
614
# the read and write working inventory should not occur in this
652
# the read and write working inventory should not occur in this
615
653
# function - they should be part of lock_write and unlock.
616
inv = self.read_working_inventory()
617
655
for f, file_id, kind in zip(files, ids, kinds):
618
assert kind is not None
619
656
if file_id is None:
620
657
inv.add_path(f, kind=kind)
622
file_id = osutils.safe_file_id(file_id)
623
659
inv.add_path(f, kind=kind, file_id=file_id)
624
self._write_inventory(inv)
660
self._inventory_is_modified = True
626
662
@needs_tree_write_lock
627
663
def _gather_kinds(self, files, kinds):
688
724
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
690
@deprecated_method(zero_eleven)
692
def pending_merges(self):
693
"""Return a list of pending merges.
695
These are revisions that have been merged into the working
696
directory but not yet committed.
698
As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
699
instead - which is available on all tree objects.
701
return self.get_parent_ids()[1:]
726
def path_content_summary(self, path, _lstat=os.lstat,
727
_mapper=osutils.file_kind_from_stat_mode):
728
"""See Tree.path_content_summary."""
729
abspath = self.abspath(path)
731
stat_result = _lstat(abspath)
733
if getattr(e, 'errno', None) == errno.ENOENT:
735
return ('missing', None, None, None)
736
# propagate other errors
738
kind = _mapper(stat_result.st_mode)
740
size = stat_result.st_size
741
# try for a stat cache lookup
742
executable = self._is_executable_from_path_and_stat(path, stat_result)
743
return (kind, size, executable, self._sha_from_stat(
745
elif kind == 'directory':
746
# perhaps it looks like a plain directory, but it's really a
748
if self._directory_is_tree_reference(path):
749
kind = 'tree-reference'
750
return kind, None, None, None
751
elif kind == 'symlink':
752
return ('symlink', None, None,
753
os.readlink(abspath.encode(osutils._fs_enc)
754
).decode(osutils._fs_enc))
756
return (kind, None, None, None)
703
758
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
704
759
"""Common ghost checking functionality from set_parent_*.
715
770
def _set_merges_from_parent_ids(self, parent_ids):
716
771
merges = parent_ids[1:]
717
self._control_files.put_bytes('pending-merges', '\n'.join(merges))
772
self._transport.put_bytes('pending-merges', '\n'.join(merges),
773
mode=self.bzrdir._get_file_mode())
775
def _filter_parent_ids_by_ancestry(self, revision_ids):
776
"""Check that all merged revisions are proper 'heads'.
778
This will always return the first revision_id, and any merged revisions
781
if len(revision_ids) == 0:
783
graph = self.branch.repository.get_graph()
784
heads = graph.heads(revision_ids)
785
new_revision_ids = revision_ids[:1]
786
for revision_id in revision_ids[1:]:
787
if revision_id in heads and revision_id not in new_revision_ids:
788
new_revision_ids.append(revision_id)
789
if new_revision_ids != revision_ids:
790
trace.mutter('requested to set revision_ids = %s,'
791
' but filtered to %s', revision_ids, new_revision_ids)
792
return new_revision_ids
719
794
@needs_tree_write_lock
720
795
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
721
796
"""Set the parent ids to revision_ids.
723
798
See also set_parent_trees. This api will try to retrieve the tree data
724
799
for each element of revision_ids from the trees repository. If you have
725
800
tree data already available, it is more efficient to use
729
804
:param revision_ids: The revision_ids to set as the parent ids of this
730
805
working tree. Any of these may be ghosts.
732
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
733
807
self._check_parents_for_ghosts(revision_ids,
734
808
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
809
for revision_id in revision_ids:
810
_mod_revision.check_not_reserved_id(revision_id)
812
revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
736
814
if len(revision_ids) > 0:
737
815
self.set_last_revision(revision_ids[0])
739
self.set_last_revision(None)
817
self.set_last_revision(_mod_revision.NULL_REVISION)
741
819
self._set_merges_from_parent_ids(revision_ids)
743
821
@needs_tree_write_lock
744
822
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
745
823
"""See MutableTree.set_parent_trees."""
746
parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
824
parent_ids = [rev for (rev, tree) in parents_list]
825
for revision_id in parent_ids:
826
_mod_revision.check_not_reserved_id(revision_id)
748
828
self._check_parents_for_ghosts(parent_ids,
749
829
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
831
parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
751
833
if len(parent_ids) == 0:
752
leftmost_parent_id = None
834
leftmost_parent_id = _mod_revision.NULL_REVISION
753
835
leftmost_parent_tree = None
755
837
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
1744
1870
def read_basis_inventory(self):
1745
1871
"""Read the cached basis inventory."""
1746
1872
path = self._basis_inventory_name()
1747
return self._control_files.get(path).read()
1873
return self._transport.get_bytes(path)
1749
1875
@needs_read_lock
1750
1876
def read_working_inventory(self):
1751
1877
"""Read the working inventory.
1753
1879
:raises errors.InventoryModified: read_working_inventory will fail
1754
1880
when the current in memory inventory has been modified.
1756
# conceptually this should be an implementation detail of the tree.
1882
# conceptually this should be an implementation detail of the tree.
1757
1883
# XXX: Deprecate this.
1758
1884
# ElementTree does its own conversion from UTF-8, so open in
1760
1886
if self._inventory_is_modified:
1761
1887
raise errors.InventoryModified(self)
1762
result = self._deserialize(self._control_files.get('inventory'))
1888
result = self._deserialize(self._transport.get('inventory'))
1763
1889
self._set_inventory(result, dirty=False)
1766
1892
@needs_tree_write_lock
1767
def remove(self, files, verbose=False, to_file=None):
1768
"""Remove nominated files from the working inventory..
1770
This does not remove their text. This does not run on XXX on what? RBC
1772
TODO: Refuse to remove modified files unless --force is given?
1774
TODO: Do something useful with directories.
1776
TODO: Should this remove the text or not? Tough call; not
1777
removing may be useful and the user can just use use rm, and
1778
is the opposite of add. Removing it is consistent with most
1779
other tools. Maybe an option.
1893
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1895
"""Remove nominated files from the working inventory.
1897
:files: File paths relative to the basedir.
1898
:keep_files: If true, the files will also be kept.
1899
:force: Delete files and directories, even if they are changed and
1900
even if the directories are not empty.
1781
## TODO: Normalize names
1782
## TODO: Remove nested loops; better scalability
1783
1902
if isinstance(files, basestring):
1784
1903
files = [files]
1786
inv = self.inventory
1788
# do this before any modifications
1908
unknown_nested_files=set()
1910
def recurse_directory_to_add_files(directory):
1911
# Recurse directory and add all files
1912
# so we can check if they have changed.
1913
for parent_info, file_infos in\
1914
self.walkdirs(directory):
1915
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1916
# Is it versioned or ignored?
1917
if self.path2id(relpath) or self.is_ignored(relpath):
1918
# Add nested content for deletion.
1919
new_files.add(relpath)
1921
# Files which are not versioned and not ignored
1922
# should be treated as unknown.
1923
unknown_nested_files.add((relpath, None, kind))
1925
for filename in files:
1926
# Get file name into canonical form.
1927
abspath = self.abspath(filename)
1928
filename = self.relpath(abspath)
1929
if len(filename) > 0:
1930
new_files.add(filename)
1931
recurse_directory_to_add_files(filename)
1933
files = list(new_files)
1936
return # nothing to do
1938
# Sort needed to first handle directory content before the directory
1939
files.sort(reverse=True)
1941
# Bail out if we are going to delete files we shouldn't
1942
if not keep_files and not force:
1943
has_changed_files = len(unknown_nested_files) > 0
1944
if not has_changed_files:
1945
for (file_id, path, content_change, versioned, parent_id, name,
1946
kind, executable) in self.iter_changes(self.basis_tree(),
1947
include_unchanged=True, require_versioned=False,
1948
want_unversioned=True, specific_files=files):
1949
if versioned == (False, False):
1950
# The record is unknown ...
1951
if not self.is_ignored(path[1]):
1952
# ... but not ignored
1953
has_changed_files = True
1955
elif content_change and (kind[1] is not None):
1956
# Versioned and changed, but not deleted
1957
has_changed_files = True
1960
if has_changed_files:
1961
# Make delta show ALL applicable changes in error message.
1962
tree_delta = self.changes_from(self.basis_tree(),
1963
require_versioned=False, want_unversioned=True,
1964
specific_files=files)
1965
for unknown_file in unknown_nested_files:
1966
if unknown_file not in tree_delta.unversioned:
1967
tree_delta.unversioned.extend((unknown_file,))
1968
raise errors.BzrRemoveChangedFilesError(tree_delta)
1970
# Build inv_delta and delete files where applicable,
1971
# do this before any modifications to inventory.
1789
1972
for f in files:
1790
fid = inv.path2id(f)
1973
fid = self.path2id(f)
1792
note("%s is not versioned."%f)
1976
message = "%s is not versioned." % (f,)
1795
# having remove it, it must be either ignored or unknown
1979
# having removed it, it must be either ignored or unknown
1796
1980
if self.is_ignored(f):
1797
1981
new_status = 'I'
1799
1983
new_status = '?'
1800
textui.show_status(new_status, inv[fid].kind, f,
1984
textui.show_status(new_status, self.kind(fid), f,
1801
1985
to_file=to_file)
1804
self._write_inventory(inv)
1987
inv_delta.append((f, None, fid, None))
1988
message = "removed %s" % (f,)
1991
abs_path = self.abspath(f)
1992
if osutils.lexists(abs_path):
1993
if (osutils.isdir(abs_path) and
1994
len(os.listdir(abs_path)) > 0):
1996
osutils.rmtree(abs_path)
1998
message = "%s is not an empty directory "\
1999
"and won't be deleted." % (f,)
2001
osutils.delete_any(abs_path)
2002
message = "deleted %s" % (f,)
2003
elif message is not None:
2004
# Only care if we haven't done anything yet.
2005
message = "%s does not exist." % (f,)
2007
# Print only one message (if any) per file.
2008
if message is not None:
2010
self.apply_inventory_delta(inv_delta)
1806
2012
@needs_tree_write_lock
1807
def revert(self, filenames, old_tree=None, backups=True,
2013
def revert(self, filenames=None, old_tree=None, backups=True,
1808
2014
pb=DummyProgress(), report_changes=False):
1809
2015
from bzrlib.conflicts import resolve
2018
symbol_versioning.warn('Using [] to revert all files is deprecated'
2019
' as of bzr 0.91. Please use None (the default) instead.',
2020
DeprecationWarning, stacklevel=2)
1810
2021
if old_tree is None:
1811
old_tree = self.basis_tree()
1812
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1814
if not len(filenames):
1815
self.set_parent_ids(self.get_parent_ids()[:1])
2022
basis_tree = self.basis_tree()
2023
basis_tree.lock_read()
2024
old_tree = basis_tree
1818
resolve(self, filenames, ignore_misses=True)
2028
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
2030
if filenames is None and len(self.get_parent_ids()) > 1:
2032
last_revision = self.last_revision()
2033
if last_revision != NULL_REVISION:
2034
if basis_tree is None:
2035
basis_tree = self.basis_tree()
2036
basis_tree.lock_read()
2037
parent_trees.append((last_revision, basis_tree))
2038
self.set_parent_trees(parent_trees)
2041
resolve(self, filenames, ignore_misses=True, recursive=True)
2043
if basis_tree is not None:
1819
2045
return conflicts
1821
2047
def revision_tree(self, revision_id):
2509
2783
"""See WorkingTreeFormat.get_format_description()."""
2510
2784
return "Working tree format 2"
2512
def stub_initialize_remote(self, control_files):
2513
"""As a special workaround create critical control files for a remote working tree
2786
def _stub_initialize_on_transport(self, transport, file_mode):
2787
"""Workaround: create control files for a remote working tree.
2515
2789
This ensures that it can later be updated and dealt with locally,
2516
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2790
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2517
2791
no working tree. (See bug #43064).
2519
2793
sio = StringIO()
2520
2794
inv = Inventory()
2521
xml5.serializer_v5.write_inventory(inv, sio)
2795
xml5.serializer_v5.write_inventory(inv, sio, working=True)
2523
control_files.put('inventory', sio)
2525
control_files.put_bytes('pending-merges', '')
2528
def initialize(self, a_bzrdir, revision_id=None):
2797
transport.put_file('inventory', sio, file_mode)
2798
transport.put_bytes('pending-merges', '', file_mode)
2800
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2801
accelerator_tree=None, hardlink=False):
2529
2802
"""See WorkingTreeFormat.initialize()."""
2530
2803
if not isinstance(a_bzrdir.transport, LocalTransport):
2531
2804
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2532
branch = a_bzrdir.open_branch()
2533
if revision_id is not None:
2534
revision_id = osutils.safe_revision_id(revision_id)
2537
revision_history = branch.revision_history()
2539
position = revision_history.index(revision_id)
2541
raise errors.NoSuchRevision(branch, revision_id)
2542
branch.set_revision_history(revision_history[:position + 1])
2545
revision = branch.last_revision()
2805
if from_branch is not None:
2806
branch = from_branch
2808
branch = a_bzrdir.open_branch()
2809
if revision_id is None:
2810
revision_id = _mod_revision.ensure_null(branch.last_revision())
2813
branch.generate_revision_history(revision_id)
2546
2816
inv = Inventory()
2547
2817
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2704
2987
__default_format = WorkingTreeFormat4()
2705
2988
WorkingTreeFormat.register_format(__default_format)
2989
WorkingTreeFormat.register_format(WorkingTreeFormat6())
2990
WorkingTreeFormat.register_format(WorkingTreeFormat5())
2706
2991
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2707
2992
WorkingTreeFormat.set_default_format(__default_format)
2708
2993
# formats which have no format string are not discoverable
2709
2994
# and not independently creatable, so are not registered.
2710
2995
_legacy_formats = [WorkingTreeFormat2(),
2714
class WorkingTreeTestProviderAdapter(object):
2715
"""A tool to generate a suite testing multiple workingtree formats at once.
2717
This is done by copying the test once for each transport and injecting
2718
the transport_server, transport_readonly_server, and workingtree_format
2719
classes into each copy. Each copy is also given a new id() to make it
2723
def __init__(self, transport_server, transport_readonly_server, formats):
2724
self._transport_server = transport_server
2725
self._transport_readonly_server = transport_readonly_server
2726
self._formats = formats
2728
def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2729
"""Clone test for adaption."""
2730
new_test = deepcopy(test)
2731
new_test.transport_server = self._transport_server
2732
new_test.transport_readonly_server = self._transport_readonly_server
2733
new_test.bzrdir_format = bzrdir_format
2734
new_test.workingtree_format = workingtree_format
2735
def make_new_test_id():
2736
new_id = "%s(%s)" % (test.id(), variation)
2737
return lambda: new_id
2738
new_test.id = make_new_test_id()
2741
def adapt(self, test):
2742
from bzrlib.tests import TestSuite
2743
result = TestSuite()
2744
for workingtree_format, bzrdir_format in self._formats:
2745
new_test = self._clone_test(
2748
workingtree_format, workingtree_format.__class__.__name__)
2749
result.addTest(new_test)