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 Tree.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 filtered and self.supports_content_filtering():
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 filtered and self.supports_content_filtering():
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
target = osutils.readlink(abspath)
753
return ('symlink', None, None, target)
755
return (kind, None, None, None)
703
757
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
704
758
"""Common ghost checking functionality from set_parent_*.
715
769
def _set_merges_from_parent_ids(self, parent_ids):
716
770
merges = parent_ids[1:]
717
self._control_files.put_bytes('pending-merges', '\n'.join(merges))
771
self._transport.put_bytes('pending-merges', '\n'.join(merges),
772
mode=self.bzrdir._get_file_mode())
774
def _filter_parent_ids_by_ancestry(self, revision_ids):
775
"""Check that all merged revisions are proper 'heads'.
777
This will always return the first revision_id, and any merged revisions
780
if len(revision_ids) == 0:
782
graph = self.branch.repository.get_graph()
783
heads = graph.heads(revision_ids)
784
new_revision_ids = revision_ids[:1]
785
for revision_id in revision_ids[1:]:
786
if revision_id in heads and revision_id not in new_revision_ids:
787
new_revision_ids.append(revision_id)
788
if new_revision_ids != revision_ids:
789
trace.mutter('requested to set revision_ids = %s,'
790
' but filtered to %s', revision_ids, new_revision_ids)
791
return new_revision_ids
719
793
@needs_tree_write_lock
720
794
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
721
795
"""Set the parent ids to revision_ids.
723
797
See also set_parent_trees. This api will try to retrieve the tree data
724
798
for each element of revision_ids from the trees repository. If you have
725
799
tree data already available, it is more efficient to use
729
803
:param revision_ids: The revision_ids to set as the parent ids of this
730
804
working tree. Any of these may be ghosts.
732
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
733
806
self._check_parents_for_ghosts(revision_ids,
734
807
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
808
for revision_id in revision_ids:
809
_mod_revision.check_not_reserved_id(revision_id)
811
revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
736
813
if len(revision_ids) > 0:
737
814
self.set_last_revision(revision_ids[0])
739
self.set_last_revision(None)
816
self.set_last_revision(_mod_revision.NULL_REVISION)
741
818
self._set_merges_from_parent_ids(revision_ids)
743
820
@needs_tree_write_lock
744
821
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
745
822
"""See MutableTree.set_parent_trees."""
746
parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
823
parent_ids = [rev for (rev, tree) in parents_list]
824
for revision_id in parent_ids:
825
_mod_revision.check_not_reserved_id(revision_id)
748
827
self._check_parents_for_ghosts(parent_ids,
749
828
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
830
parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
751
832
if len(parent_ids) == 0:
752
leftmost_parent_id = None
833
leftmost_parent_id = _mod_revision.NULL_REVISION
753
834
leftmost_parent_tree = None
755
836
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
988
1108
sio = StringIO()
989
1109
self._serialize(self._inventory, sio)
991
self._control_files.put('inventory', sio)
1111
self._transport.put_file('inventory', sio,
1112
mode=self.bzrdir._get_file_mode())
992
1113
self._inventory_is_modified = False
994
1115
def _kind(self, relpath):
995
1116
return osutils.file_kind(self.abspath(relpath))
997
def list_files(self, include_root=False):
998
"""Recursively list all files as (path, class, kind, id, entry).
1118
def list_files(self, include_root=False, from_dir=None, recursive=True):
1119
"""List all files as (path, class, kind, id, entry).
1000
1121
Lists, but does not descend into unversioned directories.
1002
1122
This does not include files that have been deleted in this
1123
tree. Skips the control directory.
1005
Skips the control directory.
1125
:param include_root: if True, do not return an entry for the root
1126
:param from_dir: start from this directory or None for the root
1127
:param recursive: whether to recurse into subdirectories or not
1007
1129
# list_files is an iterator, so @needs_read_lock doesn't work properly
1008
1130
# with it. So callers should be careful to always read_lock the tree.
1744
1882
def read_basis_inventory(self):
1745
1883
"""Read the cached basis inventory."""
1746
1884
path = self._basis_inventory_name()
1747
return self._control_files.get(path).read()
1885
return self._transport.get_bytes(path)
1749
1887
@needs_read_lock
1750
1888
def read_working_inventory(self):
1751
1889
"""Read the working inventory.
1753
1891
:raises errors.InventoryModified: read_working_inventory will fail
1754
1892
when the current in memory inventory has been modified.
1756
# conceptually this should be an implementation detail of the tree.
1894
# conceptually this should be an implementation detail of the tree.
1757
1895
# XXX: Deprecate this.
1758
1896
# ElementTree does its own conversion from UTF-8, so open in
1760
1898
if self._inventory_is_modified:
1761
1899
raise errors.InventoryModified(self)
1762
result = self._deserialize(self._control_files.get('inventory'))
1900
result = self._deserialize(self._transport.get('inventory'))
1763
1901
self._set_inventory(result, dirty=False)
1766
1904
@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.
1905
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1907
"""Remove nominated files from the working inventory.
1909
:files: File paths relative to the basedir.
1910
:keep_files: If true, the files will also be kept.
1911
:force: Delete files and directories, even if they are changed and
1912
even if the directories are not empty.
1781
## TODO: Normalize names
1782
## TODO: Remove nested loops; better scalability
1783
1914
if isinstance(files, basestring):
1784
1915
files = [files]
1786
inv = self.inventory
1788
# do this before any modifications
1920
unknown_nested_files=set()
1922
def recurse_directory_to_add_files(directory):
1923
# Recurse directory and add all files
1924
# so we can check if they have changed.
1925
for parent_info, file_infos in\
1926
self.walkdirs(directory):
1927
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1928
# Is it versioned or ignored?
1929
if self.path2id(relpath) or self.is_ignored(relpath):
1930
# Add nested content for deletion.
1931
new_files.add(relpath)
1933
# Files which are not versioned and not ignored
1934
# should be treated as unknown.
1935
unknown_nested_files.add((relpath, None, kind))
1937
for filename in files:
1938
# Get file name into canonical form.
1939
abspath = self.abspath(filename)
1940
filename = self.relpath(abspath)
1941
if len(filename) > 0:
1942
new_files.add(filename)
1943
recurse_directory_to_add_files(filename)
1945
files = list(new_files)
1948
return # nothing to do
1950
# Sort needed to first handle directory content before the directory
1951
files.sort(reverse=True)
1953
# Bail out if we are going to delete files we shouldn't
1954
if not keep_files and not force:
1955
has_changed_files = len(unknown_nested_files) > 0
1956
if not has_changed_files:
1957
for (file_id, path, content_change, versioned, parent_id, name,
1958
kind, executable) in self.iter_changes(self.basis_tree(),
1959
include_unchanged=True, require_versioned=False,
1960
want_unversioned=True, specific_files=files):
1961
if versioned == (False, False):
1962
# The record is unknown ...
1963
if not self.is_ignored(path[1]):
1964
# ... but not ignored
1965
has_changed_files = True
1967
elif content_change and (kind[1] is not None):
1968
# Versioned and changed, but not deleted
1969
has_changed_files = True
1972
if has_changed_files:
1973
# Make delta show ALL applicable changes in error message.
1974
tree_delta = self.changes_from(self.basis_tree(),
1975
require_versioned=False, want_unversioned=True,
1976
specific_files=files)
1977
for unknown_file in unknown_nested_files:
1978
if unknown_file not in tree_delta.unversioned:
1979
tree_delta.unversioned.extend((unknown_file,))
1980
raise errors.BzrRemoveChangedFilesError(tree_delta)
1982
# Build inv_delta and delete files where applicable,
1983
# do this before any modifications to inventory.
1789
1984
for f in files:
1790
fid = inv.path2id(f)
1985
fid = self.path2id(f)
1792
note("%s is not versioned."%f)
1988
message = "%s is not versioned." % (f,)
1795
# having remove it, it must be either ignored or unknown
1991
# having removed it, it must be either ignored or unknown
1796
1992
if self.is_ignored(f):
1797
1993
new_status = 'I'
1799
1995
new_status = '?'
1800
textui.show_status(new_status, inv[fid].kind, f,
1996
textui.show_status(new_status, self.kind(fid), f,
1801
1997
to_file=to_file)
1804
self._write_inventory(inv)
1999
inv_delta.append((f, None, fid, None))
2000
message = "removed %s" % (f,)
2003
abs_path = self.abspath(f)
2004
if osutils.lexists(abs_path):
2005
if (osutils.isdir(abs_path) and
2006
len(os.listdir(abs_path)) > 0):
2008
osutils.rmtree(abs_path)
2010
message = "%s is not an empty directory "\
2011
"and won't be deleted." % (f,)
2013
osutils.delete_any(abs_path)
2014
message = "deleted %s" % (f,)
2015
elif message is not None:
2016
# Only care if we haven't done anything yet.
2017
message = "%s does not exist." % (f,)
2019
# Print only one message (if any) per file.
2020
if message is not None:
2022
self.apply_inventory_delta(inv_delta)
1806
2024
@needs_tree_write_lock
1807
def revert(self, filenames, old_tree=None, backups=True,
2025
def revert(self, filenames=None, old_tree=None, backups=True,
1808
2026
pb=DummyProgress(), report_changes=False):
1809
2027
from bzrlib.conflicts import resolve
2030
symbol_versioning.warn('Using [] to revert all files is deprecated'
2031
' as of bzr 0.91. Please use None (the default) instead.',
2032
DeprecationWarning, stacklevel=2)
1810
2033
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])
2034
basis_tree = self.basis_tree()
2035
basis_tree.lock_read()
2036
old_tree = basis_tree
1818
resolve(self, filenames, ignore_misses=True)
2040
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
2042
if filenames is None and len(self.get_parent_ids()) > 1:
2044
last_revision = self.last_revision()
2045
if last_revision != NULL_REVISION:
2046
if basis_tree is None:
2047
basis_tree = self.basis_tree()
2048
basis_tree.lock_read()
2049
parent_trees.append((last_revision, basis_tree))
2050
self.set_parent_trees(parent_trees)
2053
resolve(self, filenames, ignore_misses=True, recursive=True)
2055
if basis_tree is not None:
1819
2057
return conflicts
1821
2059
def revision_tree(self, revision_id):
2509
2796
"""See WorkingTreeFormat.get_format_description()."""
2510
2797
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
2799
def _stub_initialize_on_transport(self, transport, file_mode):
2800
"""Workaround: create control files for a remote working tree.
2515
2802
This ensures that it can later be updated and dealt with locally,
2516
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2803
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2517
2804
no working tree. (See bug #43064).
2519
2806
sio = StringIO()
2520
2807
inv = Inventory()
2521
xml5.serializer_v5.write_inventory(inv, sio)
2808
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):
2810
transport.put_file('inventory', sio, file_mode)
2811
transport.put_bytes('pending-merges', '', file_mode)
2813
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2814
accelerator_tree=None, hardlink=False):
2529
2815
"""See WorkingTreeFormat.initialize()."""
2530
2816
if not isinstance(a_bzrdir.transport, LocalTransport):
2531
2817
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()
2818
if from_branch is not None:
2819
branch = from_branch
2821
branch = a_bzrdir.open_branch()
2822
if revision_id is None:
2823
revision_id = _mod_revision.ensure_null(branch.last_revision())
2826
branch.generate_revision_history(revision_id)
2546
2829
inv = Inventory()
2547
2830
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2704
3000
__default_format = WorkingTreeFormat4()
2705
3001
WorkingTreeFormat.register_format(__default_format)
3002
WorkingTreeFormat.register_format(WorkingTreeFormat6())
3003
WorkingTreeFormat.register_format(WorkingTreeFormat5())
2706
3004
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2707
3005
WorkingTreeFormat.set_default_format(__default_format)
2708
3006
# formats which have no format string are not discoverable
2709
3007
# and not independently creatable, so are not registered.
2710
3008
_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)