417
387
# at this point ?
419
389
return self.branch.repository.revision_tree(revision_id)
420
except (errors.RevisionNotPresent, errors.NoSuchRevision):
390
except errors.RevisionNotPresent:
421
391
# the basis tree *may* be a ghost or a low level error may have
422
# occurred. If the revision is present, its a problem, if its not
392
# occured. If the revision is present, its a problem, if its not
424
394
if self.branch.repository.has_revision(revision_id):
426
396
# the basis tree is a ghost so return an empty tree.
427
return self.branch.repository.revision_tree(
428
_mod_revision.NULL_REVISION)
431
self._flush_ignore_list_cache()
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)
433
437
def relpath(self, path):
434
438
"""Return the local path portion from a given path.
436
The path may be absolute or relative. If its a relative path it is
440
The path may be absolute or relative. If its a relative path it is
437
441
interpreted relative to the python current working directory.
439
443
return osutils.relpath(self.basedir, path)
441
445
def has_filename(self, filename):
442
446
return osutils.lexists(self.abspath(filename))
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 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()
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')
480
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
460
def annotate_iter(self, file_id):
481
461
"""See Tree.annotate_iter
483
463
This implementation will use the basis tree implementation if possible.
600
578
__contains__ = has_id
602
580
def get_file_size(self, file_id):
603
"""See Tree.get_file_size"""
605
return os.path.getsize(self.id2abspath(file_id))
607
if e.errno != errno.ENOENT:
581
file_id = osutils.safe_file_id(file_id)
582
return os.path.getsize(self.id2abspath(file_id))
613
585
def get_file_sha1(self, file_id, path=None, stat_value=None):
586
file_id = osutils.safe_file_id(file_id)
615
588
path = self._inventory.id2path(file_id)
616
589
return self._hashcache.get_sha1(path, stat_value)
618
591
def get_file_mtime(self, file_id, path=None):
592
file_id = osutils.safe_file_id(file_id)
620
594
path = self.inventory.id2path(file_id)
621
595
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)
631
597
if not supports_executable():
632
598
def is_executable(self, file_id, path=None):
599
file_id = osutils.safe_file_id(file_id)
633
600
return self._inventory[file_id].executable
635
_is_executable_from_path_and_stat = \
636
_is_executable_from_path_and_stat_from_basis
638
602
def is_executable(self, file_id, path=None):
604
file_id = osutils.safe_file_id(file_id)
640
605
path = self.id2path(file_id)
641
606
mode = os.lstat(self.abspath(path)).st_mode
642
607
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
647
609
@needs_tree_write_lock
648
610
def _add(self, files, ids, kinds):
649
611
"""See MutableTree._add."""
650
612
# TODO: Re-adding a file that is removed in the working copy
651
613
# should probably put it back with the previous ID.
652
# the read and write working inventory should not occur in this
614
# the read and write working inventory should not occur in this
653
615
# function - they should be part of lock_write and unlock.
616
inv = self.read_working_inventory()
655
617
for f, file_id, kind in zip(files, ids, kinds):
618
assert kind is not None
656
619
if file_id is None:
657
620
inv.add_path(f, kind=kind)
622
file_id = osutils.safe_file_id(file_id)
659
623
inv.add_path(f, kind=kind, file_id=file_id)
660
self._inventory_is_modified = True
624
self._write_inventory(inv)
662
626
@needs_tree_write_lock
663
627
def _gather_kinds(self, files, kinds):
724
688
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
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)
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:]
757
703
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
758
704
"""Common ghost checking functionality from set_parent_*.
769
715
def _set_merges_from_parent_ids(self, parent_ids):
770
716
merges = parent_ids[1:]
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
717
self._control_files.put_bytes('pending-merges', '\n'.join(merges))
793
719
@needs_tree_write_lock
794
720
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
795
721
"""Set the parent ids to revision_ids.
797
723
See also set_parent_trees. This api will try to retrieve the tree data
798
724
for each element of revision_ids from the trees repository. If you have
799
725
tree data already available, it is more efficient to use
803
729
:param revision_ids: The revision_ids to set as the parent ids of this
804
730
working tree. Any of these may be ghosts.
732
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
806
733
self._check_parents_for_ghosts(revision_ids,
807
734
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)
813
736
if len(revision_ids) > 0:
814
737
self.set_last_revision(revision_ids[0])
816
self.set_last_revision(_mod_revision.NULL_REVISION)
739
self.set_last_revision(None)
818
741
self._set_merges_from_parent_ids(revision_ids)
820
743
@needs_tree_write_lock
821
744
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
822
745
"""See MutableTree.set_parent_trees."""
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)
746
parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
827
748
self._check_parents_for_ghosts(parent_ids,
828
749
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
830
parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
832
751
if len(parent_ids) == 0:
833
leftmost_parent_id = _mod_revision.NULL_REVISION
752
leftmost_parent_id = None
834
753
leftmost_parent_tree = None
836
755
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
1871
1744
def read_basis_inventory(self):
1872
1745
"""Read the cached basis inventory."""
1873
1746
path = self._basis_inventory_name()
1874
return self._transport.get_bytes(path)
1747
return self._control_files.get(path).read()
1876
1749
@needs_read_lock
1877
1750
def read_working_inventory(self):
1878
1751
"""Read the working inventory.
1880
1753
:raises errors.InventoryModified: read_working_inventory will fail
1881
1754
when the current in memory inventory has been modified.
1883
# conceptually this should be an implementation detail of the tree.
1756
# conceptually this should be an implementation detail of the tree.
1884
1757
# XXX: Deprecate this.
1885
1758
# ElementTree does its own conversion from UTF-8, so open in
1887
1760
if self._inventory_is_modified:
1888
1761
raise errors.InventoryModified(self)
1889
result = self._deserialize(self._transport.get('inventory'))
1762
result = self._deserialize(self._control_files.get('inventory'))
1890
1763
self._set_inventory(result, dirty=False)
1893
1766
@needs_tree_write_lock
1894
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1896
"""Remove nominated files from the working inventory.
1898
:files: File paths relative to the basedir.
1899
:keep_files: If true, the files will also be kept.
1900
:force: Delete files and directories, even if they are changed and
1901
even if the directories are not empty.
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.
1781
## TODO: Normalize names
1782
## TODO: Remove nested loops; better scalability
1903
1783
if isinstance(files, basestring):
1904
1784
files = [files]
1909
unknown_nested_files=set()
1911
def recurse_directory_to_add_files(directory):
1912
# Recurse directory and add all files
1913
# so we can check if they have changed.
1914
for parent_info, file_infos in\
1915
self.walkdirs(directory):
1916
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1917
# Is it versioned or ignored?
1918
if self.path2id(relpath) or self.is_ignored(relpath):
1919
# Add nested content for deletion.
1920
new_files.add(relpath)
1922
# Files which are not versioned and not ignored
1923
# should be treated as unknown.
1924
unknown_nested_files.add((relpath, None, kind))
1926
for filename in files:
1927
# Get file name into canonical form.
1928
abspath = self.abspath(filename)
1929
filename = self.relpath(abspath)
1930
if len(filename) > 0:
1931
new_files.add(filename)
1932
recurse_directory_to_add_files(filename)
1934
files = list(new_files)
1937
return # nothing to do
1939
# Sort needed to first handle directory content before the directory
1940
files.sort(reverse=True)
1942
# Bail out if we are going to delete files we shouldn't
1943
if not keep_files and not force:
1944
has_changed_files = len(unknown_nested_files) > 0
1945
if not has_changed_files:
1946
for (file_id, path, content_change, versioned, parent_id, name,
1947
kind, executable) in self.iter_changes(self.basis_tree(),
1948
include_unchanged=True, require_versioned=False,
1949
want_unversioned=True, specific_files=files):
1950
if versioned == (False, False):
1951
# The record is unknown ...
1952
if not self.is_ignored(path[1]):
1953
# ... but not ignored
1954
has_changed_files = True
1956
elif content_change and (kind[1] is not None):
1957
# Versioned and changed, but not deleted
1958
has_changed_files = True
1961
if has_changed_files:
1962
# Make delta show ALL applicable changes in error message.
1963
tree_delta = self.changes_from(self.basis_tree(),
1964
require_versioned=False, want_unversioned=True,
1965
specific_files=files)
1966
for unknown_file in unknown_nested_files:
1967
if unknown_file not in tree_delta.unversioned:
1968
tree_delta.unversioned.extend((unknown_file,))
1969
raise errors.BzrRemoveChangedFilesError(tree_delta)
1971
# Build inv_delta and delete files where applicable,
1972
# do this before any modifications to inventory.
1786
inv = self.inventory
1788
# do this before any modifications
1973
1789
for f in files:
1974
fid = self.path2id(f)
1790
fid = inv.path2id(f)
1977
message = "%s is not versioned." % (f,)
1792
note("%s is not versioned."%f)
1980
# having removed it, it must be either ignored or unknown
1795
# having remove it, it must be either ignored or unknown
1981
1796
if self.is_ignored(f):
1982
1797
new_status = 'I'
1984
1799
new_status = '?'
1985
textui.show_status(new_status, self.kind(fid), f,
1800
textui.show_status(new_status, inv[fid].kind, f,
1986
1801
to_file=to_file)
1988
inv_delta.append((f, None, fid, None))
1989
message = "removed %s" % (f,)
1992
abs_path = self.abspath(f)
1993
if osutils.lexists(abs_path):
1994
if (osutils.isdir(abs_path) and
1995
len(os.listdir(abs_path)) > 0):
1997
osutils.rmtree(abs_path)
1999
message = "%s is not an empty directory "\
2000
"and won't be deleted." % (f,)
2002
osutils.delete_any(abs_path)
2003
message = "deleted %s" % (f,)
2004
elif message is not None:
2005
# Only care if we haven't done anything yet.
2006
message = "%s does not exist." % (f,)
2008
# Print only one message (if any) per file.
2009
if message is not None:
2011
self.apply_inventory_delta(inv_delta)
1804
self._write_inventory(inv)
2013
1806
@needs_tree_write_lock
2014
def revert(self, filenames=None, old_tree=None, backups=True,
1807
def revert(self, filenames, old_tree=None, backups=True,
2015
1808
pb=DummyProgress(), report_changes=False):
2016
1809
from bzrlib.conflicts import resolve
2019
symbol_versioning.warn('Using [] to revert all files is deprecated'
2020
' as of bzr 0.91. Please use None (the default) instead.',
2021
DeprecationWarning, stacklevel=2)
2022
1810
if old_tree is None:
2023
basis_tree = self.basis_tree()
2024
basis_tree.lock_read()
2025
old_tree = basis_tree
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])
2029
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
2031
if filenames is None and len(self.get_parent_ids()) > 1:
2033
last_revision = self.last_revision()
2034
if last_revision != NULL_REVISION:
2035
if basis_tree is None:
2036
basis_tree = self.basis_tree()
2037
basis_tree.lock_read()
2038
parent_trees.append((last_revision, basis_tree))
2039
self.set_parent_trees(parent_trees)
2042
resolve(self, filenames, ignore_misses=True, recursive=True)
2044
if basis_tree is not None:
1818
resolve(self, filenames, ignore_misses=True)
2046
1819
return conflicts
2048
1821
def revision_tree(self, revision_id):
2785
2509
"""See WorkingTreeFormat.get_format_description()."""
2786
2510
return "Working tree format 2"
2788
def _stub_initialize_on_transport(self, transport, file_mode):
2789
"""Workaround: create control files for a remote working tree.
2512
def stub_initialize_remote(self, control_files):
2513
"""As a special workaround create critical control files for a remote working tree
2791
2515
This ensures that it can later be updated and dealt with locally,
2792
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2516
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2793
2517
no working tree. (See bug #43064).
2795
2519
sio = StringIO()
2796
2520
inv = Inventory()
2797
xml5.serializer_v5.write_inventory(inv, sio, working=True)
2521
xml5.serializer_v5.write_inventory(inv, sio)
2799
transport.put_file('inventory', sio, file_mode)
2800
transport.put_bytes('pending-merges', '', file_mode)
2802
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2803
accelerator_tree=None, hardlink=False):
2523
control_files.put('inventory', sio)
2525
control_files.put_bytes('pending-merges', '')
2528
def initialize(self, a_bzrdir, revision_id=None):
2804
2529
"""See WorkingTreeFormat.initialize()."""
2805
2530
if not isinstance(a_bzrdir.transport, LocalTransport):
2806
2531
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2807
if from_branch is not None:
2808
branch = from_branch
2810
branch = a_bzrdir.open_branch()
2811
if revision_id is None:
2812
revision_id = _mod_revision.ensure_null(branch.last_revision())
2815
branch.generate_revision_history(revision_id)
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
2546
inv = Inventory()
2819
2547
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2989
2704
__default_format = WorkingTreeFormat4()
2990
2705
WorkingTreeFormat.register_format(__default_format)
2991
WorkingTreeFormat.register_format(WorkingTreeFormat6())
2992
WorkingTreeFormat.register_format(WorkingTreeFormat5())
2993
2706
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2994
2707
WorkingTreeFormat.set_default_format(__default_format)
2995
2708
# formats which have no format string are not discoverable
2996
2709
# and not independently creatable, so are not registered.
2997
2710
_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)