29
29
WorkingTree.open(dir).
32
# TODO: Give the workingtree sole responsibility for the working inventory;
33
# remove the variable and references to it from the branch. This may require
34
# updating the commit code so as to update the inventory within the working
35
# copy, and making sure there's only one WorkingTree for any directory on disk.
36
# At the moment they may alias the inventory and have old copies of it in
37
# memory. (Now done? -- mbp 20060309)
39
33
from cStringIO import StringIO
101
96
from bzrlib.filters import filtered_input_file
102
97
from bzrlib.trace import mutter, note
103
98
from bzrlib.transport.local import LocalTransport
104
from bzrlib.progress import DummyProgress, ProgressPhase
105
99
from bzrlib.revision import CURRENT_REVISION
106
100
from bzrlib.rio import RioReader, rio_file, Stanza
107
101
from bzrlib.symbol_versioning import (
113
107
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
108
# TODO: Modifying the conflict objects or their type is currently nearly
109
# impossible as there is no clear relationship between the working tree format
110
# and the conflict list file format.
114
111
CONFLICT_HEADER_1 = "BZR conflict list format 1"
116
113
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
250
248
self._rules_searcher = None
251
249
self.views = self._make_views()
252
def user_transport(self):
253
return self.bzrdir.user_transport
256
def control_transport(self):
257
return self._transport
253
259
def _detect_case_handling(self):
254
260
wt_trans = self.bzrdir.get_workingtree_transport(None)
457
463
return (file_obj, stat_value)
459
465
def get_file_text(self, file_id, path=None, filtered=True):
460
return self.get_file(file_id, path=path, filtered=filtered).read()
466
my_file = self.get_file(file_id, path=path, filtered=filtered)
468
return my_file.read()
462
472
def get_file_byname(self, filename, filtered=True):
463
473
path = self.abspath(filename)
518
528
# Now we have the parents of this content
519
529
annotator = self.branch.repository.texts.get_annotator()
520
text = self.get_file(file_id).read()
530
text = self.get_file_text(file_id)
521
531
this_key =(file_id, default_revision)
522
532
annotator.add_special_text(this_key, file_parent_keys, text)
523
533
annotations = [(key[-1], line)
909
919
branch.last_revision().
911
921
from bzrlib.merge import Merger, Merge3Merger
912
pb = ui.ui_factory.nested_progress_bar()
914
merger = Merger(self.branch, this_tree=self, pb=pb)
915
merger.pp = ProgressPhase("Merge phase", 5, pb)
916
merger.pp.next_phase()
917
# check that there are no local alterations
918
if not force and self.has_changes():
919
raise errors.UncommittedChanges(self)
920
if to_revision is None:
921
to_revision = _mod_revision.ensure_null(branch.last_revision())
922
merger.other_rev_id = to_revision
923
if _mod_revision.is_null(merger.other_rev_id):
924
raise errors.NoCommits(branch)
925
self.branch.fetch(branch, last_revision=merger.other_rev_id)
926
merger.other_basis = merger.other_rev_id
927
merger.other_tree = self.branch.repository.revision_tree(
929
merger.other_branch = branch
930
merger.pp.next_phase()
931
if from_revision is None:
934
merger.set_base_revision(from_revision, branch)
935
if merger.base_rev_id == merger.other_rev_id:
936
raise errors.PointlessMerge
937
merger.backup_files = False
938
if merge_type is None:
939
merger.merge_type = Merge3Merger
941
merger.merge_type = merge_type
942
merger.set_interesting_files(None)
943
merger.show_base = False
944
merger.reprocess = False
945
conflicts = merger.do_merge()
922
merger = Merger(self.branch, this_tree=self)
923
# check that there are no local alterations
924
if not force and self.has_changes():
925
raise errors.UncommittedChanges(self)
926
if to_revision is None:
927
to_revision = _mod_revision.ensure_null(branch.last_revision())
928
merger.other_rev_id = to_revision
929
if _mod_revision.is_null(merger.other_rev_id):
930
raise errors.NoCommits(branch)
931
self.branch.fetch(branch, last_revision=merger.other_rev_id)
932
merger.other_basis = merger.other_rev_id
933
merger.other_tree = self.branch.repository.revision_tree(
935
merger.other_branch = branch
936
if from_revision is None:
939
merger.set_base_revision(from_revision, branch)
940
if merger.base_rev_id == merger.other_rev_id:
941
raise errors.PointlessMerge
942
merger.backup_files = False
943
if merge_type is None:
944
merger.merge_type = Merge3Merger
946
merger.merge_type = merge_type
947
merger.set_interesting_files(None)
948
merger.show_base = False
949
merger.reprocess = False
950
conflicts = merger.do_merge()
1098
1101
tree_transport = self.bzrdir.root_transport.clone(sub_path)
1099
1102
if tree_transport.base != branch_transport.base:
1100
1103
tree_bzrdir = format.initialize_on_transport(tree_transport)
1101
branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
1104
branch.BranchReferenceFormat().initialize(tree_bzrdir,
1105
target_branch=new_branch)
1103
1107
tree_bzrdir = branch_bzrdir
1104
1108
wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
1142
1146
This does not include files that have been deleted in this
1143
1147
tree. Skips the control directory.
1145
:param include_root: if True, do not return an entry for the root
1149
:param include_root: if True, return an entry for the root
1146
1150
:param from_dir: start from this directory or None for the root
1147
1151
:param recursive: whether to recurse into subdirectories or not
1602
1606
@needs_write_lock
1603
1607
def pull(self, source, overwrite=False, stop_revision=None,
1604
1608
change_reporter=None, possible_transports=None, local=False):
1605
top_pb = ui.ui_factory.nested_progress_bar()
1606
1609
source.lock_read()
1608
pp = ProgressPhase("Pull phase", 2, top_pb)
1610
1611
old_revision_info = self.branch.last_revision_info()
1611
1612
basis_tree = self.basis_tree()
1612
1613
count = self.branch.pull(source, overwrite, stop_revision,
1625
1624
new_basis_tree,
1627
1626
this_tree=self,
1629
1628
change_reporter=change_reporter)
1630
1629
basis_root_id = basis_tree.get_root_id()
1631
1630
new_root_id = new_basis_tree.get_root_id()
1632
1631
if basis_root_id != new_root_id:
1633
1632
self.set_root_id(new_root_id)
1636
1634
basis_tree.unlock()
1637
1635
# TODO - dedup parents list with things merged by pull ?
1638
1636
# reuse the revisiontree we merged against to set the new
1806
1803
raise errors.ObjectNotLocked(self)
1808
1805
def lock_read(self):
1809
"""See Branch.lock_read, and WorkingTree.unlock."""
1806
"""Lock the tree for reading.
1808
This also locks the branch, and can be unlocked via self.unlock().
1810
:return: A bzrlib.lock.LogicalLockResult.
1810
1812
if not self.is_locked():
1811
1813
self._reset_data()
1812
1814
self.branch.lock_read()
1814
return self._control_files.lock_read()
1816
self._control_files.lock_read()
1817
return LogicalLockResult(self.unlock)
1816
1819
self.branch.unlock()
1819
1822
def lock_tree_write(self):
1820
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
1823
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
1825
:return: A bzrlib.lock.LogicalLockResult.
1821
1827
if not self.is_locked():
1822
1828
self._reset_data()
1823
1829
self.branch.lock_read()
1825
return self._control_files.lock_write()
1831
self._control_files.lock_write()
1832
return LogicalLockResult(self.unlock)
1827
1834
self.branch.unlock()
1830
1837
def lock_write(self):
1831
"""See MutableTree.lock_write, and WorkingTree.unlock."""
1838
"""See MutableTree.lock_write, and WorkingTree.unlock.
1840
:return: A bzrlib.lock.LogicalLockResult.
1832
1842
if not self.is_locked():
1833
1843
self._reset_data()
1834
1844
self.branch.lock_write()
1836
return self._control_files.lock_write()
1846
self._control_files.lock_write()
1847
return LogicalLockResult(self.unlock)
1838
1849
self.branch.unlock()
1904
1915
# revision_id is set. We must check for this full string, because a
1905
1916
# root node id can legitimately look like 'revision_id' but cannot
1906
1917
# contain a '"'.
1907
xml = self.branch.repository.get_inventory_xml(new_revision)
1918
xml = self.branch.repository._get_inventory_xml(new_revision)
1908
1919
firstline = xml.split('\n', 1)[0]
1909
1920
if (not 'revision_id="' in firstline or
1910
1921
'format="7"' not in firstline):
1964
1975
def recurse_directory_to_add_files(directory):
1965
1976
# Recurse directory and add all files
1966
1977
# so we can check if they have changed.
1967
for parent_info, file_infos in\
1968
self.walkdirs(directory):
1978
for parent_info, file_infos in self.walkdirs(directory):
1969
1979
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1970
1980
# Is it versioned or ignored?
1971
1981
if self.path2id(relpath) or self.is_ignored(relpath):
2006
2016
# ... but not ignored
2007
2017
has_changed_files = True
2009
elif content_change and (kind[1] is not None):
2010
# Versioned and changed, but not deleted
2019
elif (content_change and (kind[1] is not None) and
2020
osutils.is_inside_any(files, path[1])):
2021
# Versioned and changed, but not deleted, and still
2022
# in one of the dirs to be deleted.
2011
2023
has_changed_files = True
2067
2079
@needs_tree_write_lock
2068
2080
def revert(self, filenames=None, old_tree=None, backups=True,
2069
pb=DummyProgress(), report_changes=False):
2081
pb=None, report_changes=False):
2070
2082
from bzrlib.conflicts import resolve
2071
2083
if filenames == []:
2072
2084
filenames = None
2261
2273
# We MUST save it even if an error occurs, because otherwise the users
2262
2274
# local work is unreferenced and will appear to have been lost.
2266
2278
last_rev = self.get_parent_ids()[0]
2267
2279
except IndexError:
2268
2280
last_rev = _mod_revision.NULL_REVISION
2269
2281
if revision is None:
2270
2282
revision = self.branch.last_revision()
2272
if revision not in self.branch.revision_history():
2273
raise errors.NoSuchRevision(self.branch, revision)
2284
old_tip = old_tip or _mod_revision.NULL_REVISION
2286
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
2287
# the branch we are bound to was updated
2288
# merge those changes in first
2289
base_tree = self.basis_tree()
2290
other_tree = self.branch.repository.revision_tree(old_tip)
2291
nb_conflicts = merge.merge_inner(self.branch, other_tree,
2292
base_tree, this_tree=self,
2293
change_reporter=change_reporter)
2295
self.add_parent_tree((old_tip, other_tree))
2296
trace.note('Rerun update after fixing the conflicts.')
2274
2299
if last_rev != _mod_revision.ensure_null(revision):
2275
# merge tree state up to specified revision.
2300
# the working tree is up to date with the branch
2301
# we can merge the specified revision from master
2302
to_tree = self.branch.repository.revision_tree(revision)
2303
to_root_id = to_tree.get_root_id()
2276
2305
basis = self.basis_tree()
2277
2306
basis.lock_read()
2279
to_tree = self.branch.repository.revision_tree(revision)
2280
to_root_id = to_tree.get_root_id()
2281
2308
if (basis.inventory.root is None
2282
2309
or basis.inventory.root.file_id != to_root_id):
2283
2310
self.set_root_id(to_root_id)
2285
result += merge.merge_inner(
2290
change_reporter=change_reporter)
2291
self.set_last_revision(revision)
2315
# determine the branch point
2316
graph = self.branch.repository.get_graph()
2317
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
2319
base_tree = self.branch.repository.revision_tree(base_rev_id)
2321
nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
2323
change_reporter=change_reporter)
2324
self.set_last_revision(revision)
2294
2325
# TODO - dedup parents list with things merged by pull ?
2295
2326
# reuse the tree we've updated to to set the basis:
2296
2327
parent_trees = [(revision, to_tree)]
2303
2334
for parent in merges:
2304
2335
parent_trees.append(
2305
2336
(parent, self.branch.repository.revision_tree(parent)))
2306
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2337
if not _mod_revision.is_null(old_tip):
2307
2338
parent_trees.append(
2308
2339
(old_tip, self.branch.repository.revision_tree(old_tip)))
2309
2340
self.set_parent_trees(parent_trees)
2310
2341
last_rev = parent_trees[0][0]
2312
# the working tree had the same last-revision as the master
2313
# branch did. We may still have pivot local work from the local
2314
# branch into old_tip:
2315
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2316
self.add_parent_tree_id(old_tip)
2317
if (old_tip is not None and not _mod_revision.is_null(old_tip)
2318
and old_tip != last_rev):
2319
# our last revision was not the prior branch last revision
2320
# and we have converted that last revision to a pending merge.
2321
# base is somewhere between the branch tip now
2322
# and the now pending merge
2324
# Since we just modified the working tree and inventory, flush out
2325
# the current state, before we modify it again.
2326
# TODO: jam 20070214 WorkingTree3 doesn't require this, dirstate
2327
# requires it only because TreeTransform directly munges the
2328
# inventory and calls tree._write_inventory(). Ultimately we
2329
# should be able to remove this extra flush.
2331
graph = self.branch.repository.get_graph()
2332
base_rev_id = graph.find_unique_lca(revision, old_tip)
2333
base_tree = self.branch.repository.revision_tree(base_rev_id)
2334
other_tree = self.branch.repository.revision_tree(old_tip)
2335
result += merge.merge_inner(
2340
change_reporter=change_reporter)
2343
2344
def _write_hashcache_if_dirty(self):
2344
2345
"""Write out the hashcache if it is dirty."""