399
399
# the basis tree is a ghost so return an empty tree.
400
400
return self.branch.repository.revision_tree(None)
403
self._flush_ignore_list_cache()
403
406
@deprecated_method(zero_eight)
404
407
def create(branch, directory):
460
463
return file(self.abspath(filename), 'rb')
463
def annotate_iter(self, file_id):
466
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
464
467
"""See Tree.annotate_iter
466
469
This implementation will use the basis tree implementation if possible.
494
497
old.append(list(tree.annotate_iter(file_id)))
495
498
return annotate.reannotate(old, self.get_file(file_id).readlines(),
503
def _get_ancestors(self, default_revision):
504
ancestors = set([default_revision])
505
for parent_id in self.get_parent_ids():
506
ancestors.update(self.branch.repository.get_ancestry(
507
parent_id, topo_sorted=False))
500
510
def get_parent_ids(self):
501
511
"""See Tree.get_parent_ids.
503
513
This implementation reads the pending merges list and last_revision
504
514
value and uses that to decide what the parents list should be.
506
last_rev = self._last_revision()
516
last_rev = _mod_revision.ensure_null(self._last_revision())
517
if _mod_revision.NULL_REVISION == last_rev:
510
520
parents = [last_rev]
616
626
# should probably put it back with the previous ID.
617
627
# the read and write working inventory should not occur in this
618
628
# function - they should be part of lock_write and unlock.
619
inv = self.read_working_inventory()
620
630
for f, file_id, kind in zip(files, ids, kinds):
621
631
assert kind is not None
622
632
if file_id is None:
625
635
file_id = osutils.safe_file_id(file_id)
626
636
inv.add_path(f, kind=kind, file_id=file_id)
627
self._write_inventory(inv)
637
self._inventory_is_modified = True
629
639
@needs_tree_write_lock
630
640
def _gather_kinds(self, files, kinds):
735
745
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
736
746
self._check_parents_for_ghosts(revision_ids,
737
747
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
748
for revision_id in revision_ids:
749
_mod_revision.check_not_reserved_id(revision_id)
739
751
if len(revision_ids) > 0:
740
752
self.set_last_revision(revision_ids[0])
742
self.set_last_revision(None)
754
self.set_last_revision(_mod_revision.NULL_REVISION)
744
756
self._set_merges_from_parent_ids(revision_ids)
747
759
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
748
760
"""See MutableTree.set_parent_trees."""
749
761
parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
762
for revision_id in parent_ids:
763
_mod_revision.check_not_reserved_id(revision_id)
751
765
self._check_parents_for_ghosts(parent_ids,
752
766
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
754
768
if len(parent_ids) == 0:
755
leftmost_parent_id = None
769
leftmost_parent_id = _mod_revision.NULL_REVISION
756
770
leftmost_parent_tree = None
758
772
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
789
803
self._control_files.put(filename, my_file)
791
805
@needs_write_lock # because merge pulls data into the branch.
792
def merge_from_branch(self, branch, to_revision=None):
806
def merge_from_branch(self, branch, to_revision=None, from_revision=None,
793
808
"""Merge from a branch into this working tree.
795
810
:param branch: The branch to merge from.
808
823
# local alterations
809
824
merger.check_basis(check_clean=True, require_commits=False)
810
825
if to_revision is None:
811
to_revision = branch.last_revision()
826
to_revision = _mod_revision.ensure_null(branch.last_revision())
813
828
to_revision = osutils.safe_revision_id(to_revision)
814
829
merger.other_rev_id = to_revision
815
if merger.other_rev_id is None:
816
raise error.NoCommits(branch)
830
if _mod_revision.is_null(merger.other_rev_id):
831
raise errors.NoCommits(branch)
817
832
self.branch.fetch(branch, last_revision=merger.other_rev_id)
818
833
merger.other_basis = merger.other_rev_id
819
834
merger.other_tree = self.branch.repository.revision_tree(
820
835
merger.other_rev_id)
821
836
merger.other_branch = branch
822
837
merger.pp.next_phase()
838
if from_revision is None:
841
merger.set_base_revision(from_revision, branch)
824
842
if merger.base_rev_id == merger.other_rev_id:
825
843
raise errors.PointlessMerge
826
844
merger.backup_files = False
827
merger.merge_type = Merge3Merger
845
if merge_type is None:
846
merger.merge_type = Merge3Merger
848
merger.merge_type = merge_type
828
849
merger.set_interesting_files(None)
829
850
merger.show_base = False
830
851
merger.reprocess = False
1550
1571
if ignoreset is not None:
1551
1572
return ignoreset
1553
ignore_globs = set(bzrlib.DEFAULT_IGNORE)
1574
ignore_globs = set()
1554
1575
ignore_globs.update(ignores.get_runtime_ignores())
1555
1576
ignore_globs.update(ignores.get_user_ignores())
1556
1577
if self.has_filename(bzrlib.IGNORE_FILENAME):
1763
1784
@needs_tree_write_lock
1764
1785
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1766
"""Remove nominated files from the working inventor.
1787
"""Remove nominated files from the working inventory.
1768
1789
:files: File paths relative to the basedir.
1769
1790
:keep_files: If true, the files will also be kept.
1804
1825
recurse_directory_to_add_files(filename)
1805
1826
files = [f for f in new_files]
1829
return # nothing to do
1807
1831
# Sort needed to first handle directory content before the directory
1808
1832
files.sort(reverse=True)
1809
1833
if not keep_files and not force:
1810
tree_delta = self.changes_from(self.basis_tree(),
1811
specific_files=files)
1812
for unknown_file in unknown_files_in_directory:
1813
tree_delta.unversioned.extend((unknown_file,))
1814
if bool(tree_delta.modified
1816
or tree_delta.renamed
1817
or tree_delta.kind_changed
1818
or tree_delta.unversioned):
1834
has_changed_files = len(unknown_files_in_directory) > 0
1835
if not has_changed_files:
1836
for (file_id, path, content_change, versioned, parent_id, name,
1837
kind, executable) in self._iter_changes(self.basis_tree(),
1838
include_unchanged=True, require_versioned=False,
1839
want_unversioned=True, specific_files=files):
1840
# check if it's unknown OR changed but not deleted:
1841
if (versioned == (False, False)
1842
or (content_change and kind[1] != None)):
1843
has_changed_files = True
1846
if has_changed_files:
1847
# make delta to show ALL applicable changes in error message.
1848
tree_delta = self.changes_from(self.basis_tree(),
1849
specific_files=files)
1850
for unknown_file in unknown_files_in_directory:
1851
tree_delta.unversioned.extend((unknown_file,))
1819
1852
raise errors.BzrRemoveChangedFilesError(tree_delta)
1821
1854
# do this before any modifications
1972
2005
raise NotImplementedError(self.unlock)
2007
def update(self, change_reporter=None):
1975
2008
"""Update a working tree along its branch.
1977
2010
This will update the branch if its bound too, which means we have
2007
2040
old_tip = self.branch.update()
2010
return self._update_tree(old_tip)
2043
return self._update_tree(old_tip, change_reporter)
2014
2047
@needs_tree_write_lock
2015
def _update_tree(self, old_tip=None):
2048
def _update_tree(self, old_tip=None, change_reporter=None):
2016
2049
"""Update a tree to the master branch.
2018
2051
:param old_tip: if supplied, the previous tip revision the branch,
2033
2066
last_rev = self.get_parent_ids()[0]
2034
2067
except IndexError:
2036
if last_rev != self.branch.last_revision():
2068
last_rev = _mod_revision.NULL_REVISION
2069
if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
2037
2070
# merge tree state up to new branch tip.
2038
2071
basis = self.basis_tree()
2039
2072
basis.lock_read()
2061
2095
for parent in merges:
2062
2096
parent_trees.append(
2063
2097
(parent, self.branch.repository.revision_tree(parent)))
2064
if old_tip is not None:
2098
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2065
2099
parent_trees.append(
2066
2100
(old_tip, self.branch.repository.revision_tree(old_tip)))
2067
2101
self.set_parent_trees(parent_trees)
2070
2104
# the working tree had the same last-revision as the master
2071
2105
# branch did. We may still have pivot local work from the local
2072
2106
# branch into old_tip:
2073
if old_tip is not None:
2107
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2074
2108
self.add_parent_tree_id(old_tip)
2075
if old_tip and old_tip != last_rev:
2109
if (old_tip is not None and not _mod_revision.is_null(old_tip)
2110
and old_tip != last_rev):
2076
2111
# our last revision was not the prior branch last revision
2077
2112
# and we have converted that last revision to a pending merge.
2078
2113
# base is somewhere between the branch tip now
2085
2120
# inventory and calls tree._write_inventory(). Ultimately we
2086
2121
# should be able to remove this extra flush.
2088
from bzrlib.revision import common_ancestor
2090
base_rev_id = common_ancestor(self.branch.last_revision(),
2092
self.branch.repository)
2093
except errors.NoCommonAncestor:
2123
graph = self.branch.repository.get_graph()
2124
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
2095
2126
base_tree = self.branch.repository.revision_tree(base_rev_id)
2096
2127
other_tree = self.branch.repository.revision_tree(old_tip)
2097
2128
result += merge.merge_inner(
2133
change_reporter=change_reporter)
2104
2136
def _write_hashcache_if_dirty(self):
2448
2483
return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2450
2485
def unlock(self):
2486
# do non-implementation specific cleanup
2451
2488
if self._control_files._lock_count == 1:
2452
2489
# _inventory_is_modified is always False during a read lock.
2453
2490
if self._inventory_is_modified:
2597
2634
if not isinstance(a_bzrdir.transport, LocalTransport):
2598
2635
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2599
2636
branch = a_bzrdir.open_branch()
2600
if revision_id is not None:
2637
if revision_id is None:
2638
revision_id = _mod_revision.ensure_null(branch.last_revision())
2601
2640
revision_id = osutils.safe_revision_id(revision_id)
2604
revision_history = branch.revision_history()
2606
position = revision_history.index(revision_id)
2608
raise errors.NoSuchRevision(branch, revision_id)
2609
branch.set_revision_history(revision_history[:position + 1])
2612
revision = branch.last_revision()
2643
branch.generate_revision_history(revision_id)
2613
2646
inv = Inventory()
2614
2647
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2617
2650
_internal=True,
2619
2652
_bzrdir=a_bzrdir)
2620
basis_tree = branch.repository.revision_tree(revision)
2653
basis_tree = branch.repository.revision_tree(revision_id)
2621
2654
if basis_tree.inventory.root is not None:
2622
2655
wt.set_root_id(basis_tree.inventory.root.file_id)
2623
2656
# set the parent list and cache the basis tree.
2624
wt.set_parent_trees([(revision, basis_tree)])
2657
if _mod_revision.is_null(revision_id):
2660
parent_trees = [(revision_id, basis_tree)]
2661
wt.set_parent_trees(parent_trees)
2625
2662
transform.build_tree(basis_tree, wt)
2698
2735
control_files.put_utf8('format', self.get_format_string())
2699
2736
branch = a_bzrdir.open_branch()
2700
2737
if revision_id is None:
2701
revision_id = branch.last_revision()
2738
revision_id = _mod_revision.ensure_null(branch.last_revision())
2703
2740
revision_id = osutils.safe_revision_id(revision_id)
2704
2741
# WorkingTree3 can handle an inventory which has a unique root id.
2776
2813
# and not independently creatable, so are not registered.
2777
2814
_legacy_formats = [WorkingTreeFormat2(),
2781
class WorkingTreeTestProviderAdapter(object):
2782
"""A tool to generate a suite testing multiple workingtree formats at once.
2784
This is done by copying the test once for each transport and injecting
2785
the transport_server, transport_readonly_server, and workingtree_format
2786
classes into each copy. Each copy is also given a new id() to make it
2790
def __init__(self, transport_server, transport_readonly_server, formats):
2791
self._transport_server = transport_server
2792
self._transport_readonly_server = transport_readonly_server
2793
self._formats = formats
2795
def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2796
"""Clone test for adaption."""
2797
new_test = deepcopy(test)
2798
new_test.transport_server = self._transport_server
2799
new_test.transport_readonly_server = self._transport_readonly_server
2800
new_test.bzrdir_format = bzrdir_format
2801
new_test.workingtree_format = workingtree_format
2802
def make_new_test_id():
2803
new_id = "%s(%s)" % (test.id(), variation)
2804
return lambda: new_id
2805
new_test.id = make_new_test_id()
2808
def adapt(self, test):
2809
from bzrlib.tests import TestSuite
2810
result = TestSuite()
2811
for workingtree_format, bzrdir_format in self._formats:
2812
new_test = self._clone_test(
2815
workingtree_format, workingtree_format.__class__.__name__)
2816
result.addTest(new_test)