1
# Copyright (C) 2005-2011 Canonical Ltd
1
# Copyright (C) 2005 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""WorkingTree object and friends.
19
A WorkingTree represents the editable working copy of a branch.
20
Operations which represent the WorkingTree are also done here,
21
such as renaming or adding files. The WorkingTree has an inventory
22
which is updated by these operations. A commit produces a
23
new revision based on the workingtree and its inventory.
25
At the moment every WorkingTree has its own branch. Remote
26
WorkingTrees aren't supported.
28
To get a WorkingTree, call bzrdir.open_workingtree() or
29
WorkingTree.open(dir).
33
from cStringIO import StringIO
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
# TODO: Don't allow WorkingTrees to be constructed for remote branches.
19
# FIXME: I don't know if writing out the cache from the destructor is really a
20
# good idea, because destructors are considered poor taste in Python, and
21
# it's not predictable when it will be written out.
37
from bzrlib.lazy_import import lazy_import
38
lazy_import(globals(), """
39
from bisect import bisect_left
50
conflicts as _mod_conflicts,
53
filters as _mod_filters,
61
revision as _mod_revision,
73
from bzrlib import symbol_versioning
74
from bzrlib.decorators import needs_read_lock, needs_write_lock
75
from bzrlib.lock import LogicalLockResult
76
from bzrlib.lockable_files import LockableFiles
77
from bzrlib.lockdir import LockDir
78
import bzrlib.mutabletree
79
from bzrlib.mutabletree import needs_tree_write_lock
80
from bzrlib import osutils
81
from bzrlib.osutils import (
91
from bzrlib.trace import mutter, note
92
from bzrlib.transport.local import LocalTransport
93
from bzrlib.revision import CURRENT_REVISION
94
from bzrlib.symbol_versioning import (
100
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
101
# TODO: Modifying the conflict objects or their type is currently nearly
102
# impossible as there is no clear relationship between the working tree format
103
# and the conflict list file format.
104
CONFLICT_HEADER_1 = "BZR conflict list format 1"
106
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
109
class TreeEntry(object):
110
"""An entry that implements the minimum interface used by commands.
112
This needs further inspection, it may be better to have
113
InventoryEntries without ids - though that seems wrong. For now,
114
this is a parallel hierarchy to InventoryEntry, and needs to become
115
one of several things: decorates to that hierarchy, children of, or
117
Another note is that these objects are currently only used when there is
118
no InventoryEntry available - i.e. for unversioned objects.
119
Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
122
def __eq__(self, other):
123
# yes, this us ugly, TODO: best practice __eq__ style.
124
return (isinstance(other, TreeEntry)
125
and other.__class__ == self.__class__)
127
def kind_character(self):
131
class TreeDirectory(TreeEntry):
132
"""See TreeEntry. This is a directory in a working tree."""
134
def __eq__(self, other):
135
return (isinstance(other, TreeDirectory)
136
and other.__class__ == self.__class__)
138
def kind_character(self):
142
class TreeFile(TreeEntry):
143
"""See TreeEntry. This is a regular file in a working tree."""
145
def __eq__(self, other):
146
return (isinstance(other, TreeFile)
147
and other.__class__ == self.__class__)
149
def kind_character(self):
153
class TreeLink(TreeEntry):
154
"""See TreeEntry. This is a symlink in a working tree."""
156
def __eq__(self, other):
157
return (isinstance(other, TreeLink)
158
and other.__class__ == self.__class__)
160
def kind_character(self):
164
class WorkingTree(bzrlib.mutabletree.MutableTree,
165
controldir.ControlComponent):
27
from bzrlib.osutils import appendpath, file_kind, isdir, splitpath
28
from bzrlib.errors import BzrCheckError
29
from bzrlib.trace import mutter
31
class WorkingTree(bzrlib.tree.Tree):
166
32
"""Working copy tree.
168
:ivar basedir: The root of the tree on disk. This is a unicode path object
169
(as opposed to a URL).
34
The inventory is held in the `Branch` working-inventory, and the
35
files are in a directory on disk.
37
It is possible for a `WorkingTree` to have a filename which is
38
not listed in the Inventory and vice versa.
172
# override this to set the strategy for storing views
173
def _make_views(self):
174
return views.DisabledViews(self)
176
def __init__(self, basedir='.',
177
branch=DEPRECATED_PARAMETER,
182
"""Construct a WorkingTree instance. This is not a public API.
184
:param branch: A branch to override probing for the branch.
186
self._format = _format
187
self.bzrdir = _bzrdir
189
raise errors.BzrError("Please use bzrdir.open_workingtree or "
190
"WorkingTree.open() to obtain a WorkingTree.")
191
basedir = safe_unicode(basedir)
192
mutter("opening working tree %r", basedir)
193
if deprecated_passed(branch):
194
self._branch = branch
196
self._branch = self.bzrdir.open_branch()
197
self.basedir = realpath(basedir)
198
self._control_files = _control_files
199
self._transport = self._control_files._transport
40
def __init__(self, basedir, inv):
41
from bzrlib.hashcache import HashCache
42
from bzrlib.trace import note, mutter
45
self.basedir = basedir
46
self.path2id = inv.path2id
200
48
# update the whole cache up front and write to disk if anything changed;
201
49
# in the future we might want to do this more selectively
202
# two possible ways offer themselves : in self._unlock, write the cache
203
# if needed, or, when the cache sees a change, append it to the hash
204
# cache file, and have the parser take the most recent entry for a
206
wt_trans = self.bzrdir.get_workingtree_transport(None)
207
cache_filename = wt_trans.local_abspath('stat-cache')
208
self._hashcache = hashcache.HashCache(basedir, cache_filename,
209
self.bzrdir._get_file_mode(),
210
self._content_filter_stack_provider())
50
hc = self._hashcache = HashCache(basedir)
213
# is this scan needed ? it makes things kinda slow.
216
54
if hc.needs_write:
217
55
mutter("write hc")
220
self._detect_case_handling()
221
self._rules_searcher = None
222
self.views = self._make_views()
225
def user_transport(self):
226
return self.bzrdir.user_transport
229
def control_transport(self):
230
return self._transport
232
def is_control_filename(self, filename):
233
"""True if filename is the name of a control file in this tree.
235
:param filename: A filename within the tree. This is a relative path
236
from the root of this tree.
238
This is true IF and ONLY IF the filename is part of the meta data
239
that bzr controls in this tree. I.E. a random .bzr directory placed
240
on disk will not be a control file for this tree.
242
return self.bzrdir.is_control_filename(filename)
244
def _detect_case_handling(self):
245
wt_trans = self.bzrdir.get_workingtree_transport(None)
247
wt_trans.stat(self._format.case_sensitive_filename)
248
except errors.NoSuchFile:
249
self.case_sensitive = True
251
self.case_sensitive = False
253
self._setup_directory_is_tree_reference()
256
fget=lambda self: self._branch,
257
doc="""The branch this WorkingTree is connected to.
259
This cannot be set - it is reflective of the actual disk structure
260
the working tree has been constructed from.
263
def break_lock(self):
264
"""Break a lock if one is present from another instance.
266
Uses the ui factory to ask for confirmation if the lock may be from
269
This will probe the repository for its lock as well.
271
self._control_files.break_lock()
272
self.branch.break_lock()
274
def _get_check_refs(self):
275
"""Return the references needed to perform a check of this tree.
277
The default implementation returns no refs, and is only suitable for
278
trees that have no local caching and can commit on ghosts at any time.
280
:seealso: bzrlib.check for details about check_refs.
284
def requires_rich_root(self):
285
return self._format.requires_rich_root
287
def supports_tree_reference(self):
290
def supports_content_filtering(self):
291
return self._format.supports_content_filtering()
293
def supports_views(self):
294
return self.views.supports_views()
297
def open(path=None, _unsupported=False):
298
"""Open an existing working tree at path.
302
path = osutils.getcwd()
303
control = bzrdir.BzrDir.open(path, _unsupported)
304
return control.open_workingtree(_unsupported)
307
def open_containing(path=None):
308
"""Open an existing working tree which has its root about path.
310
This probes for a working tree at path and searches upwards from there.
312
Basically we keep looking up until we find the control directory or
313
run into /. If there isn't one, raises NotBranchError.
314
TODO: give this a new exception.
315
If there is one, it is returned, along with the unused portion of path.
317
:return: The WorkingTree that contains 'path', and the rest of path
320
path = osutils.getcwd()
321
control, relpath = bzrdir.BzrDir.open_containing(path)
322
return control.open_workingtree(), relpath
325
def open_containing_paths(file_list, default_directory=None,
326
canonicalize=True, apply_view=True):
327
"""Open the WorkingTree that contains a set of paths.
329
Fail if the paths given are not all in a single tree.
331
This is used for the many command-line interfaces that take a list of
332
any number of files and that require they all be in the same tree.
334
if default_directory is None:
335
default_directory = u'.'
336
# recommended replacement for builtins.internal_tree_files
337
if file_list is None or len(file_list) == 0:
338
tree = WorkingTree.open_containing(default_directory)[0]
339
# XXX: doesn't really belong here, and seems to have the strange
340
# side effect of making it return a bunch of files, not the whole
341
# tree -- mbp 20100716
342
if tree.supports_views() and apply_view:
343
view_files = tree.views.lookup_view()
345
file_list = view_files
346
view_str = views.view_display_str(view_files)
347
note("Ignoring files outside view. View is %s" % view_str)
348
return tree, file_list
349
if default_directory == u'.':
352
seed = default_directory
353
file_list = [osutils.pathjoin(default_directory, f)
355
tree = WorkingTree.open_containing(seed)[0]
356
return tree, tree.safe_relpath_files(file_list, canonicalize,
357
apply_view=apply_view)
359
def safe_relpath_files(self, file_list, canonicalize=True, apply_view=True):
360
"""Convert file_list into a list of relpaths in tree.
362
:param self: A tree to operate on.
363
:param file_list: A list of user provided paths or None.
364
:param apply_view: if True and a view is set, apply it or check that
365
specified files are within it
366
:return: A list of relative paths.
367
:raises errors.PathNotChild: When a provided path is in a different self
370
if file_list is None:
372
if self.supports_views() and apply_view:
373
view_files = self.views.lookup_view()
377
# self.relpath exists as a "thunk" to osutils, but canonical_relpath
378
# doesn't - fix that up here before we enter the loop.
380
fixer = lambda p: osutils.canonical_relpath(self.basedir, p)
383
for filename in file_list:
384
relpath = fixer(osutils.dereference_path(filename))
385
if view_files and not osutils.is_inside_any(view_files, relpath):
386
raise errors.FileOutsideView(filename, view_files)
387
new_list.append(relpath)
391
def open_downlevel(path=None):
392
"""Open an unsupported working tree.
394
Only intended for advanced situations like upgrading part of a bzrdir.
396
return WorkingTree.open(path, _unsupported=True)
399
def find_trees(location):
400
def list_current(transport):
401
return [d for d in transport.list_dir('') if d != '.bzr']
402
def evaluate(bzrdir):
404
tree = bzrdir.open_workingtree()
405
except errors.NoWorkingTree:
409
t = transport.get_transport(location)
410
iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
411
list_current=list_current)
412
return [tr for tr in iterator if tr is not None]
414
def all_file_ids(self):
415
"""See Tree.iter_all_file_ids"""
416
raise NotImplementedError(self.all_file_ids)
60
if self._hashcache.needs_write:
61
self._hashcache.write()
65
"""Iterate through file_ids for this tree.
67
file_ids are in a WorkingTree if they are in the working inventory
68
and the working file exists.
71
for path, ie in inv.iter_entries():
72
if os.path.exists(self.abspath(path)):
418
76
def __repr__(self):
419
77
return "<%s of %s>" % (self.__class__.__name__,
420
78
getattr(self, 'basedir', None))
422
82
def abspath(self, filename):
423
return pathjoin(self.basedir, filename)
425
def basis_tree(self):
426
"""Return RevisionTree for the current last revision.
428
If the left most parent is a ghost then the returned tree will be an
429
empty tree - one obtained by calling
430
repository.revision_tree(NULL_REVISION).
433
revision_id = self.get_parent_ids()[0]
435
# no parents, return an empty revision tree.
436
# in the future this should return the tree for
437
# 'empty:' - the implicit root empty tree.
438
return self.branch.repository.revision_tree(
439
_mod_revision.NULL_REVISION)
441
return self.revision_tree(revision_id)
442
except errors.NoSuchRevision:
444
# No cached copy available, retrieve from the repository.
445
# FIXME? RBC 20060403 should we cache the inventory locally
448
return self.branch.repository.revision_tree(revision_id)
449
except (errors.RevisionNotPresent, errors.NoSuchRevision):
450
# the basis tree *may* be a ghost or a low level error may have
451
# occurred. If the revision is present, its a problem, if its not
453
if self.branch.repository.has_revision(revision_id):
455
# the basis tree is a ghost so return an empty tree.
456
return self.branch.repository.revision_tree(
457
_mod_revision.NULL_REVISION)
460
self._flush_ignore_list_cache()
462
def relpath(self, path):
463
"""Return the local path portion from a given path.
465
The path may be absolute or relative. If its a relative path it is
466
interpreted relative to the python current working directory.
468
return osutils.relpath(self.basedir, path)
83
return os.path.join(self.basedir, filename)
470
85
def has_filename(self, filename):
471
return osutils.lexists(self.abspath(filename))
473
def get_file(self, file_id, path=None, filtered=True):
474
return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
476
def get_file_with_stat(self, file_id, path=None, filtered=True,
477
_fstat=osutils.fstat):
478
"""See Tree.get_file_with_stat."""
480
path = self.id2path(file_id)
481
file_obj = self.get_file_byname(path, filtered=False)
482
stat_value = _fstat(file_obj.fileno())
483
if filtered and self.supports_content_filtering():
484
filters = self._content_filter_stack(path)
485
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
486
return (file_obj, stat_value)
488
def get_file_text(self, file_id, path=None, filtered=True):
489
my_file = self.get_file(file_id, path=path, filtered=filtered)
491
return my_file.read()
495
def get_file_byname(self, filename, filtered=True):
496
path = self.abspath(filename)
498
if filtered and self.supports_content_filtering():
499
filters = self._content_filter_stack(filename)
500
return _mod_filters.filtered_input_file(f, filters)
504
def get_file_lines(self, file_id, path=None, filtered=True):
505
"""See Tree.get_file_lines()"""
506
file = self.get_file(file_id, path, filtered=filtered)
508
return file.readlines()
512
def _get_ancestors(self, default_revision):
513
ancestors = set([default_revision])
514
for parent_id in self.get_parent_ids():
515
ancestors.update(self.branch.repository.get_ancestry(
516
parent_id, topo_sorted=False))
519
def get_parent_ids(self):
520
"""See Tree.get_parent_ids.
522
This implementation reads the pending merges list and last_revision
523
value and uses that to decide what the parents list should be.
525
last_rev = _mod_revision.ensure_null(self._last_revision())
526
if _mod_revision.NULL_REVISION == last_rev:
531
merges_bytes = self._transport.get_bytes('pending-merges')
532
except errors.NoSuchFile:
535
for l in osutils.split_lines(merges_bytes):
536
revision_id = l.rstrip('\n')
537
parents.append(revision_id)
540
def get_root_id(self):
541
"""Return the id of this trees root"""
542
raise NotImplementedError(self.get_root_id)
545
def clone(self, to_bzrdir, revision_id=None):
546
"""Duplicate this working tree into to_bzr, including all state.
548
Specifically modified files are kept as modified, but
549
ignored and unknown files are discarded.
551
If you want to make a new line of development, see bzrdir.sprout()
554
If not None, the cloned tree will have its last revision set to
555
revision, and difference between the source trees last revision
556
and this one merged in.
558
# assumes the target bzr dir format is compatible.
559
result = to_bzrdir.create_workingtree()
560
self.copy_content_into(result, revision_id)
564
def copy_content_into(self, tree, revision_id=None):
565
"""Copy the current content and user files of this tree into tree."""
566
tree.set_root_id(self.get_root_id())
567
if revision_id is None:
568
merge.transform_tree(tree, self)
570
# TODO now merge from tree.last_revision to revision (to preserve
571
# user local changes)
572
merge.transform_tree(tree, self)
573
tree.set_parent_ids([revision_id])
575
def id2abspath(self, file_id):
86
return os.path.exists(self.abspath(filename))
88
def get_file(self, file_id):
89
return self.get_file_byname(self.id2path(file_id))
91
def get_file_byname(self, filename):
92
return file(self.abspath(filename), 'rb')
94
def _get_store_filename(self, file_id):
95
## XXX: badly named; this isn't in the store at all
576
96
return self.abspath(self.id2path(file_id))
578
def get_file_size(self, file_id):
579
"""See Tree.get_file_size"""
580
# XXX: this returns the on-disk size; it should probably return the
583
return os.path.getsize(self.id2abspath(file_id))
585
if e.errno != errno.ENOENT:
590
def get_file_sha1(self, file_id, path=None, stat_value=None):
591
# FIXME: Shouldn't this be in Tree?
592
raise NotImplementedError(self.get_file_sha1)
594
@needs_tree_write_lock
595
def _gather_kinds(self, files, kinds):
596
"""See MutableTree._gather_kinds."""
597
for pos, f in enumerate(files):
598
if kinds[pos] is None:
599
fullpath = normpath(self.abspath(f))
601
kinds[pos] = file_kind(fullpath)
603
if e.errno == errno.ENOENT:
604
raise errors.NoSuchFile(fullpath)
607
def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
608
"""Add revision_id as a parent.
610
This is equivalent to retrieving the current list of parent ids
611
and setting the list to its value plus revision_id.
613
:param revision_id: The revision id to add to the parent list. It may
614
be a ghost revision as long as its not the first parent to be added,
615
or the allow_leftmost_as_ghost parameter is set True.
616
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
618
parents = self.get_parent_ids() + [revision_id]
619
self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
620
or allow_leftmost_as_ghost)
622
@needs_tree_write_lock
623
def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
624
"""Add revision_id, tree tuple as a parent.
626
This is equivalent to retrieving the current list of parent trees
627
and setting the list to its value plus parent_tuple. See also
628
add_parent_tree_id - if you only have a parent id available it will be
629
simpler to use that api. If you have the parent already available, using
630
this api is preferred.
632
:param parent_tuple: The (revision id, tree) to add to the parent list.
633
If the revision_id is a ghost, pass None for the tree.
634
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
636
parent_ids = self.get_parent_ids() + [parent_tuple[0]]
637
if len(parent_ids) > 1:
638
# the leftmost may have already been a ghost, preserve that if it
640
allow_leftmost_as_ghost = True
641
self.set_parent_ids(parent_ids,
642
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
644
@needs_tree_write_lock
645
def add_pending_merge(self, *revision_ids):
646
# TODO: Perhaps should check at this point that the
647
# history of the revision is actually present?
648
parents = self.get_parent_ids()
650
for rev_id in revision_ids:
651
if rev_id in parents:
653
parents.append(rev_id)
656
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
658
def path_content_summary(self, path, _lstat=os.lstat,
659
_mapper=osutils.file_kind_from_stat_mode):
660
"""See Tree.path_content_summary."""
661
abspath = self.abspath(path)
663
stat_result = _lstat(abspath)
665
if getattr(e, 'errno', None) == errno.ENOENT:
667
return ('missing', None, None, None)
668
# propagate other errors
670
kind = _mapper(stat_result.st_mode)
672
return self._file_content_summary(path, stat_result)
673
elif kind == 'directory':
674
# perhaps it looks like a plain directory, but it's really a
676
if self._directory_is_tree_reference(path):
677
kind = 'tree-reference'
678
return kind, None, None, None
679
elif kind == 'symlink':
680
target = osutils.readlink(abspath)
681
return ('symlink', None, None, target)
683
return (kind, None, None, None)
685
def _file_content_summary(self, path, stat_result):
686
size = stat_result.st_size
687
executable = self._is_executable_from_path_and_stat(path, stat_result)
688
# try for a stat cache lookup
689
return ('file', size, executable, self._sha_from_stat(
692
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
693
"""Common ghost checking functionality from set_parent_*.
695
This checks that the left hand-parent exists if there are any
698
if len(revision_ids) > 0:
699
leftmost_id = revision_ids[0]
700
if (not allow_leftmost_as_ghost and not
701
self.branch.repository.has_revision(leftmost_id)):
702
raise errors.GhostRevisionUnusableHere(leftmost_id)
704
def _set_merges_from_parent_ids(self, parent_ids):
705
merges = parent_ids[1:]
706
self._transport.put_bytes('pending-merges', '\n'.join(merges),
707
mode=self.bzrdir._get_file_mode())
709
def _filter_parent_ids_by_ancestry(self, revision_ids):
710
"""Check that all merged revisions are proper 'heads'.
712
This will always return the first revision_id, and any merged revisions
715
if len(revision_ids) == 0:
717
graph = self.branch.repository.get_graph()
718
heads = graph.heads(revision_ids)
719
new_revision_ids = revision_ids[:1]
720
for revision_id in revision_ids[1:]:
721
if revision_id in heads and revision_id not in new_revision_ids:
722
new_revision_ids.append(revision_id)
723
if new_revision_ids != revision_ids:
724
mutter('requested to set revision_ids = %s,'
725
' but filtered to %s', revision_ids, new_revision_ids)
726
return new_revision_ids
728
@needs_tree_write_lock
729
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
730
"""Set the parent ids to revision_ids.
732
See also set_parent_trees. This api will try to retrieve the tree data
733
for each element of revision_ids from the trees repository. If you have
734
tree data already available, it is more efficient to use
735
set_parent_trees rather than set_parent_ids. set_parent_ids is however
736
an easier API to use.
738
:param revision_ids: The revision_ids to set as the parent ids of this
739
working tree. Any of these may be ghosts.
741
self._check_parents_for_ghosts(revision_ids,
742
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
743
for revision_id in revision_ids:
744
_mod_revision.check_not_reserved_id(revision_id)
746
revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
748
if len(revision_ids) > 0:
749
self.set_last_revision(revision_ids[0])
751
self.set_last_revision(_mod_revision.NULL_REVISION)
753
self._set_merges_from_parent_ids(revision_ids)
755
@needs_tree_write_lock
756
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
757
"""See MutableTree.set_parent_trees."""
758
parent_ids = [rev for (rev, tree) in parents_list]
759
for revision_id in parent_ids:
760
_mod_revision.check_not_reserved_id(revision_id)
762
self._check_parents_for_ghosts(parent_ids,
763
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
765
parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
767
if len(parent_ids) == 0:
768
leftmost_parent_id = _mod_revision.NULL_REVISION
769
leftmost_parent_tree = None
771
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
773
if self._change_last_revision(leftmost_parent_id):
774
if leftmost_parent_tree is None:
775
# If we don't have a tree, fall back to reading the
776
# parent tree from the repository.
777
self._cache_basis_inventory(leftmost_parent_id)
779
inv = leftmost_parent_tree.inventory
780
xml = self._create_basis_xml_from_inventory(
781
leftmost_parent_id, inv)
782
self._write_basis_inventory(xml)
783
self._set_merges_from_parent_ids(parent_ids)
785
@needs_tree_write_lock
786
def set_pending_merges(self, rev_list):
787
parents = self.get_parent_ids()
788
leftmost = parents[:1]
789
new_parents = leftmost + rev_list
790
self.set_parent_ids(new_parents)
792
@needs_tree_write_lock
793
def set_merge_modified(self, modified_hashes):
795
for file_id, hash in modified_hashes.iteritems():
796
yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
798
self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
800
def _sha_from_stat(self, path, stat_result):
801
"""Get a sha digest from the tree's stat cache.
803
The default implementation assumes no stat cache is present.
805
:param path: The path.
806
:param stat_result: The stat result being looked up.
810
def _put_rio(self, filename, stanzas, header):
811
self._must_be_locked()
812
my_file = _mod_rio.rio_file(stanzas, header)
813
self._transport.put_file(filename, my_file,
814
mode=self.bzrdir._get_file_mode())
816
@needs_write_lock # because merge pulls data into the branch.
817
def merge_from_branch(self, branch, to_revision=None, from_revision=None,
818
merge_type=None, force=False):
819
"""Merge from a branch into this working tree.
821
:param branch: The branch to merge from.
822
:param to_revision: If non-None, the merge will merge to to_revision,
823
but not beyond it. to_revision does not need to be in the history
824
of the branch when it is supplied. If None, to_revision defaults to
825
branch.last_revision().
827
from bzrlib.merge import Merger, Merge3Merger
828
merger = Merger(self.branch, this_tree=self)
829
# check that there are no local alterations
830
if not force and self.has_changes():
831
raise errors.UncommittedChanges(self)
832
if to_revision is None:
833
to_revision = _mod_revision.ensure_null(branch.last_revision())
834
merger.other_rev_id = to_revision
835
if _mod_revision.is_null(merger.other_rev_id):
836
raise errors.NoCommits(branch)
837
self.branch.fetch(branch, last_revision=merger.other_rev_id)
838
merger.other_basis = merger.other_rev_id
839
merger.other_tree = self.branch.repository.revision_tree(
841
merger.other_branch = branch
842
if from_revision is None:
845
merger.set_base_revision(from_revision, branch)
846
if merger.base_rev_id == merger.other_rev_id:
847
raise errors.PointlessMerge
848
merger.backup_files = False
849
if merge_type is None:
850
merger.merge_type = Merge3Merger
852
merger.merge_type = merge_type
853
merger.set_interesting_files(None)
854
merger.show_base = False
855
merger.reprocess = False
856
conflicts = merger.do_merge()
860
def merge_modified(self):
861
"""Return a dictionary of files modified by a merge.
863
The list is initialized by WorkingTree.set_merge_modified, which is
864
typically called after we make some automatic updates to the tree
867
This returns a map of file_id->sha1, containing only files which are
868
still in the working inventory and have that text hash.
870
raise NotImplementedError(self.merge_modified)
873
def mkdir(self, path, file_id=None):
874
"""See MutableTree.mkdir()."""
876
file_id = generate_ids.gen_file_id(os.path.basename(path))
877
os.mkdir(self.abspath(path))
878
self.add(path, file_id, 'directory')
881
def get_symlink_target(self, file_id):
882
abspath = self.id2abspath(file_id)
883
target = osutils.readlink(abspath)
886
def subsume(self, other_tree):
887
raise NotImplementedError(self.subsume)
889
def _setup_directory_is_tree_reference(self):
890
if self._branch.repository._format.supports_tree_reference:
891
self._directory_is_tree_reference = \
892
self._directory_may_be_tree_reference
894
self._directory_is_tree_reference = \
895
self._directory_is_never_tree_reference
897
def _directory_is_never_tree_reference(self, relpath):
900
def _directory_may_be_tree_reference(self, relpath):
901
# as a special case, if a directory contains control files then
902
# it's a tree reference, except that the root of the tree is not
903
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
904
# TODO: We could ask all the control formats whether they
905
# recognize this directory, but at the moment there's no cheap api
906
# to do that. Since we probably can only nest bzr checkouts and
907
# they always use this name it's ok for now. -- mbp 20060306
909
# FIXME: There is an unhandled case here of a subdirectory
910
# containing .bzr but not a branch; that will probably blow up
911
# when you try to commit it. It might happen if there is a
912
# checkout in a subdirectory. This can be avoided by not adding
915
def extract(self, file_id, format=None):
916
"""Extract a subtree from this tree.
918
A new branch will be created, relative to the path for this tree.
920
raise NotImplementedError(self.extract)
923
"""Write the in memory meta data to disk."""
924
raise NotImplementedError(self.flush)
926
def _kind(self, relpath):
927
return osutils.file_kind(self.abspath(relpath))
929
def list_files(self, include_root=False, from_dir=None, recursive=True):
930
"""List all files as (path, class, kind, id, entry).
932
Lists, but does not descend into unversioned directories.
933
This does not include files that have been deleted in this
934
tree. Skips the control directory.
936
:param include_root: if True, return an entry for the root
937
:param from_dir: start from this directory or None for the root
938
:param recursive: whether to recurse into subdirectories or not
940
raise NotImplementedError(self.list_files)
942
def move(self, from_paths, to_dir=None, after=False):
945
to_dir must be known to the working tree.
947
If to_dir exists and is a directory, the files are moved into
948
it, keeping their old names.
950
Note that to_dir is only the last component of the new name;
951
this doesn't change the directory.
953
For each entry in from_paths the move mode will be determined
956
The first mode moves the file in the filesystem and updates the
957
working tree metadata. The second mode only updates the working tree
958
metadata without touching the file on the filesystem.
960
move uses the second mode if 'after == True' and the target is not
961
versioned but present in the working tree.
963
move uses the second mode if 'after == False' and the source is
964
versioned but no longer in the working tree, and the target is not
965
versioned but present in the working tree.
967
move uses the first mode if 'after == False' and the source is
968
versioned and present in the working tree, and the target is not
969
versioned and not present in the working tree.
971
Everything else results in an error.
973
This returns a list of (from_path, to_path) pairs for each
976
raise NotImplementedError(self.move)
978
@needs_tree_write_lock
979
def rename_one(self, from_rel, to_rel, after=False):
982
This can change the directory or the filename or both.
984
rename_one has several 'modes' to work. First, it can rename a physical
985
file and change the file_id. That is the normal mode. Second, it can
986
only change the file_id without touching any physical file.
988
rename_one uses the second mode if 'after == True' and 'to_rel' is not
989
versioned but present in the working tree.
991
rename_one uses the second mode if 'after == False' and 'from_rel' is
992
versioned but no longer in the working tree, and 'to_rel' is not
993
versioned but present in the working tree.
995
rename_one uses the first mode if 'after == False' and 'from_rel' is
996
versioned and present in the working tree, and 'to_rel' is not
997
versioned and not present in the working tree.
999
Everything else results in an error.
1001
raise NotImplementedError(self.rename_one)
1005
"""Return all unknown files.
1007
These are files in the working directory that are not versioned or
1008
control files or ignored.
1010
# force the extras method to be fully executed before returning, to
1011
# prevent race conditions with the lock
1013
[subp for subp in self.extras() if not self.is_ignored(subp)])
1015
def unversion(self, file_ids):
1016
"""Remove the file ids in file_ids from the current versioned set.
1018
When a file_id is unversioned, all of its children are automatically
1021
:param file_ids: The file ids to stop versioning.
1022
:raises: NoSuchId if any fileid is not currently versioned.
1024
raise NotImplementedError(self.unversion)
1027
def pull(self, source, overwrite=False, stop_revision=None,
1028
change_reporter=None, possible_transports=None, local=False,
1032
old_revision_info = self.branch.last_revision_info()
1033
basis_tree = self.basis_tree()
1034
count = self.branch.pull(source, overwrite, stop_revision,
1035
possible_transports=possible_transports,
1037
new_revision_info = self.branch.last_revision_info()
1038
if new_revision_info != old_revision_info:
1039
repository = self.branch.repository
1040
basis_tree.lock_read()
1042
new_basis_tree = self.branch.basis_tree()
1049
change_reporter=change_reporter,
1050
show_base=show_base)
1051
basis_root_id = basis_tree.get_root_id()
1052
new_root_id = new_basis_tree.get_root_id()
1053
if basis_root_id != new_root_id:
1054
self.set_root_id(new_root_id)
1057
# TODO - dedup parents list with things merged by pull ?
1058
# reuse the revisiontree we merged against to set the new
1060
parent_trees = [(self.branch.last_revision(), new_basis_tree)]
1061
# we have to pull the merge trees out again, because
1062
# merge_inner has set the ids. - this corner is not yet
1063
# layered well enough to prevent double handling.
1064
# XXX TODO: Fix the double handling: telling the tree about
1065
# the already known parent data is wasteful.
1066
merges = self.get_parent_ids()[1:]
1067
parent_trees.extend([
1068
(parent, repository.revision_tree(parent)) for
1070
self.set_parent_trees(parent_trees)
1076
def put_file_bytes_non_atomic(self, file_id, bytes):
1077
"""See MutableTree.put_file_bytes_non_atomic."""
1078
stream = file(self.id2abspath(file_id), 'wb')
1083
# TODO: update the hashcache here ?
1086
"""Yield all unversioned files in this WorkingTree.
1088
If there are any unversioned directories then only the directory is
1089
returned, not all its children. But if there are unversioned files
1090
under a versioned subdirectory, they are returned.
1092
Currently returned depth-first, sorted by name within directories.
1093
This is the same order used by 'osutils.walkdirs'.
1095
raise NotImplementedError(self.extras)
1097
def ignored_files(self):
1098
"""Yield list of PATH, IGNORE_PATTERN"""
1099
for subp in self.extras():
1100
pat = self.is_ignored(subp)
1104
def get_ignore_list(self):
1105
"""Return list of ignore patterns.
1107
Cached in the Tree object after the first call.
1109
ignoreset = getattr(self, '_ignoreset', None)
1110
if ignoreset is not None:
1113
ignore_globs = set()
1114
ignore_globs.update(ignores.get_runtime_ignores())
1115
ignore_globs.update(ignores.get_user_ignores())
1116
if self.has_filename(bzrlib.IGNORE_FILENAME):
1117
f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
1119
ignore_globs.update(ignores.parse_ignore_file(f))
1122
self._ignoreset = ignore_globs
1125
def _flush_ignore_list_cache(self):
1126
"""Resets the cached ignore list to force a cache rebuild."""
1127
self._ignoreset = None
1128
self._ignoreglobster = None
1130
def is_ignored(self, filename):
1131
r"""Check whether the filename matches an ignore pattern.
1133
Patterns containing '/' or '\' need to match the whole path;
1134
others match against only the last component. Patterns starting
1135
with '!' are ignore exceptions. Exceptions take precedence
1136
over regular patterns and cause the filename to not be ignored.
1138
If the file is ignored, returns the pattern which caused it to
1139
be ignored, otherwise None. So this can simply be used as a
1140
boolean if desired."""
1141
if getattr(self, '_ignoreglobster', None) is None:
1142
self._ignoreglobster = globbing.ExceptionGlobster(self.get_ignore_list())
1143
return self._ignoreglobster.match(filename)
1145
def kind(self, file_id):
1146
return file_kind(self.id2abspath(file_id))
1148
def stored_kind(self, file_id):
1149
"""See Tree.stored_kind"""
1150
raise NotImplementedError(self.stored_kind)
1152
def _comparison_data(self, entry, path):
1153
abspath = self.abspath(path)
1155
stat_value = os.lstat(abspath)
1157
if getattr(e, 'errno', None) == errno.ENOENT:
1164
mode = stat_value.st_mode
1165
kind = osutils.file_kind_from_stat_mode(mode)
1166
if not supports_executable():
1167
executable = entry is not None and entry.executable
1169
executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1170
return kind, executable, stat_value
1172
def _file_size(self, entry, stat_value):
1173
return stat_value.st_size
1175
def last_revision(self):
1176
"""Return the last revision of the branch for this tree.
1178
This format tree does not support a separate marker for last-revision
1179
compared to the branch.
1181
See MutableTree.last_revision
1183
return self._last_revision()
1186
def _last_revision(self):
1187
"""helper for get_parent_ids."""
1188
return _mod_revision.ensure_null(self.branch.last_revision())
1190
def is_locked(self):
1191
return self._control_files.is_locked()
1193
def _must_be_locked(self):
1194
if not self.is_locked():
1195
raise errors.ObjectNotLocked(self)
1197
def lock_read(self):
1198
"""Lock the tree for reading.
1200
This also locks the branch, and can be unlocked via self.unlock().
1202
:return: A bzrlib.lock.LogicalLockResult.
1204
if not self.is_locked():
1206
self.branch.lock_read()
1208
self._control_files.lock_read()
1209
return LogicalLockResult(self.unlock)
1211
self.branch.unlock()
1214
def lock_tree_write(self):
1215
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
1217
:return: A bzrlib.lock.LogicalLockResult.
1219
if not self.is_locked():
1221
self.branch.lock_read()
1223
self._control_files.lock_write()
1224
return LogicalLockResult(self.unlock)
1226
self.branch.unlock()
1229
def lock_write(self):
1230
"""See MutableTree.lock_write, and WorkingTree.unlock.
1232
:return: A bzrlib.lock.LogicalLockResult.
1234
if not self.is_locked():
1236
self.branch.lock_write()
1238
self._control_files.lock_write()
1239
return LogicalLockResult(self.unlock)
1241
self.branch.unlock()
1244
def get_physical_lock_status(self):
1245
return self._control_files.get_physical_lock_status()
1247
def _reset_data(self):
1248
"""Reset transient data that cannot be revalidated."""
1249
raise NotImplementedError(self._reset_data)
1251
def set_last_revision(self, new_revision):
1252
"""Change the last revision in the working tree."""
1253
raise NotImplementedError(self.set_last_revision)
1255
def _change_last_revision(self, new_revision):
1256
"""Template method part of set_last_revision to perform the change.
1258
This is used to allow WorkingTree3 instances to not affect branch
1259
when their last revision is set.
1261
if _mod_revision.is_null(new_revision):
1262
self.branch.set_last_revision_info(0, new_revision)
1265
self.branch.generate_revision_history(new_revision)
1266
except errors.NoSuchRevision:
1267
# not present in the repo - dont try to set it deeper than the tip
1268
self.branch._set_revision_history([new_revision])
1271
@needs_tree_write_lock
1272
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1274
"""Remove nominated files from the working tree metadata.
1276
:files: File paths relative to the basedir.
1277
:keep_files: If true, the files will also be kept.
1278
:force: Delete files and directories, even if they are changed and
1279
even if the directories are not empty.
1281
if isinstance(files, basestring):
1286
all_files = set() # specified and nested files
1287
unknown_nested_files=set()
1289
to_file = sys.stdout
1291
files_to_backup = []
1293
def recurse_directory_to_add_files(directory):
1294
# Recurse directory and add all files
1295
# so we can check if they have changed.
1296
for parent_info, file_infos in self.walkdirs(directory):
1297
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1298
# Is it versioned or ignored?
1299
if self.path2id(relpath):
1300
# Add nested content for deletion.
1301
all_files.add(relpath)
1303
# Files which are not versioned
1304
# should be treated as unknown.
1305
files_to_backup.append(relpath)
1307
for filename in files:
1308
# Get file name into canonical form.
1309
abspath = self.abspath(filename)
1310
filename = self.relpath(abspath)
1311
if len(filename) > 0:
1312
all_files.add(filename)
1313
recurse_directory_to_add_files(filename)
1315
files = list(all_files)
1318
return # nothing to do
1320
# Sort needed to first handle directory content before the directory
1321
files.sort(reverse=True)
1323
# Bail out if we are going to delete files we shouldn't
1324
if not keep_files and not force:
1325
for (file_id, path, content_change, versioned, parent_id, name,
1326
kind, executable) in self.iter_changes(self.basis_tree(),
1327
include_unchanged=True, require_versioned=False,
1328
want_unversioned=True, specific_files=files):
1329
if versioned[0] == False:
1330
# The record is unknown or newly added
1331
files_to_backup.append(path[1])
1332
elif (content_change and (kind[1] is not None) and
1333
osutils.is_inside_any(files, path[1])):
1334
# Versioned and changed, but not deleted, and still
1335
# in one of the dirs to be deleted.
1336
files_to_backup.append(path[1])
1338
def backup(file_to_backup):
1339
backup_name = self.bzrdir._available_backup_name(file_to_backup)
1340
osutils.rename(abs_path, self.abspath(backup_name))
1341
return "removed %s (but kept a copy: %s)" % (file_to_backup,
1344
# Build inv_delta and delete files where applicable,
1345
# do this before any modifications to meta data.
1347
fid = self.path2id(f)
1350
message = "%s is not versioned." % (f,)
1353
# having removed it, it must be either ignored or unknown
1354
if self.is_ignored(f):
1358
# XXX: Really should be a more abstract reporter interface
1359
kind_ch = osutils.kind_marker(self.kind(fid))
1360
to_file.write(new_status + ' ' + f + kind_ch + '\n')
1362
inv_delta.append((f, None, fid, None))
1363
message = "removed %s" % (f,)
1366
abs_path = self.abspath(f)
1367
if osutils.lexists(abs_path):
1368
if (osutils.isdir(abs_path) and
1369
len(os.listdir(abs_path)) > 0):
1371
osutils.rmtree(abs_path)
1372
message = "deleted %s" % (f,)
1376
if f in files_to_backup:
1379
osutils.delete_any(abs_path)
1380
message = "deleted %s" % (f,)
1381
elif message is not None:
1382
# Only care if we haven't done anything yet.
1383
message = "%s does not exist." % (f,)
1385
# Print only one message (if any) per file.
1386
if message is not None:
1388
self.apply_inventory_delta(inv_delta)
1390
@needs_tree_write_lock
1391
def revert(self, filenames=None, old_tree=None, backups=True,
1392
pb=None, report_changes=False):
1393
from bzrlib.conflicts import resolve
1396
symbol_versioning.warn('Using [] to revert all files is deprecated'
1397
' as of bzr 0.91. Please use None (the default) instead.',
1398
DeprecationWarning, stacklevel=2)
1399
if old_tree is None:
1400
basis_tree = self.basis_tree()
1401
basis_tree.lock_read()
1402
old_tree = basis_tree
1406
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1408
if filenames is None and len(self.get_parent_ids()) > 1:
1410
last_revision = self.last_revision()
1411
if last_revision != _mod_revision.NULL_REVISION:
1412
if basis_tree is None:
1413
basis_tree = self.basis_tree()
1414
basis_tree.lock_read()
1415
parent_trees.append((last_revision, basis_tree))
1416
self.set_parent_trees(parent_trees)
1419
resolve(self, filenames, ignore_misses=True, recursive=True)
1421
if basis_tree is not None:
1425
def revision_tree(self, revision_id):
1426
"""See Tree.revision_tree.
1428
WorkingTree can supply revision_trees for the basis revision only
1429
because there is only one cached inventory in the bzr directory.
1431
raise NotImplementedError(self.revision_tree)
1433
@needs_tree_write_lock
1434
def set_root_id(self, file_id):
1435
"""Set the root id for this tree."""
1439
'WorkingTree.set_root_id with fileid=None')
1440
file_id = osutils.safe_file_id(file_id)
1441
self._set_root_id(file_id)
1443
def _set_root_id(self, file_id):
1444
"""Set the root id for this tree, in a format specific manner.
1446
:param file_id: The file id to assign to the root. It must not be
1447
present in the current inventory or an error will occur. It must
1448
not be None, but rather a valid file id.
1450
raise NotImplementedError(self._set_root_id)
1453
"""See Branch.unlock.
1455
WorkingTree locking just uses the Branch locking facilities.
1456
This is current because all working trees have an embedded branch
1457
within them. IF in the future, we were to make branch data shareable
1458
between multiple working trees, i.e. via shared storage, then we
1459
would probably want to lock both the local tree, and the branch.
1461
raise NotImplementedError(self.unlock)
1465
def update(self, change_reporter=None, possible_transports=None,
1466
revision=None, old_tip=_marker, show_base=False):
1467
"""Update a working tree along its branch.
1469
This will update the branch if its bound too, which means we have
1470
multiple trees involved:
1472
- The new basis tree of the master.
1473
- The old basis tree of the branch.
1474
- The old basis tree of the working tree.
1475
- The current working tree state.
1477
Pathologically, all three may be different, and non-ancestors of each
1478
other. Conceptually we want to:
1480
- Preserve the wt.basis->wt.state changes
1481
- Transform the wt.basis to the new master basis.
1482
- Apply a merge of the old branch basis to get any 'local' changes from
1484
- Restore the wt.basis->wt.state changes.
1486
There isn't a single operation at the moment to do that, so we:
1487
- Merge current state -> basis tree of the master w.r.t. the old tree
1489
- Do a 'normal' merge of the old branch basis if it is relevant.
1491
:param revision: The target revision to update to. Must be in the
1493
:param old_tip: If branch.update() has already been run, the value it
1494
returned (old tip of the branch or None). _marker is used
1497
if self.branch.get_bound_location() is not None:
1499
update_branch = (old_tip is self._marker)
1501
self.lock_tree_write()
1502
update_branch = False
1505
old_tip = self.branch.update(possible_transports)
1507
if old_tip is self._marker:
1509
return self._update_tree(old_tip, change_reporter, revision, show_base)
1513
@needs_tree_write_lock
1514
def _update_tree(self, old_tip=None, change_reporter=None, revision=None,
1516
"""Update a tree to the master branch.
1518
:param old_tip: if supplied, the previous tip revision the branch,
1519
before it was changed to the master branch's tip.
1521
# here if old_tip is not None, it is the old tip of the branch before
1522
# it was updated from the master branch. This should become a pending
1523
# merge in the working tree to preserve the user existing work. we
1524
# cant set that until we update the working trees last revision to be
1525
# one from the new branch, because it will just get absorbed by the
1526
# parent de-duplication logic.
1528
# We MUST save it even if an error occurs, because otherwise the users
1529
# local work is unreferenced and will appear to have been lost.
1533
last_rev = self.get_parent_ids()[0]
1535
last_rev = _mod_revision.NULL_REVISION
1536
if revision is None:
1537
revision = self.branch.last_revision()
1539
old_tip = old_tip or _mod_revision.NULL_REVISION
1541
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1542
# the branch we are bound to was updated
1543
# merge those changes in first
1544
base_tree = self.basis_tree()
1545
other_tree = self.branch.repository.revision_tree(old_tip)
1546
nb_conflicts = merge.merge_inner(self.branch, other_tree,
1547
base_tree, this_tree=self,
1548
change_reporter=change_reporter,
1549
show_base=show_base)
1551
self.add_parent_tree((old_tip, other_tree))
1552
note('Rerun update after fixing the conflicts.')
1555
if last_rev != _mod_revision.ensure_null(revision):
1556
# the working tree is up to date with the branch
1557
# we can merge the specified revision from master
1558
to_tree = self.branch.repository.revision_tree(revision)
1559
to_root_id = to_tree.get_root_id()
1561
basis = self.basis_tree()
1564
if (basis.get_root_id() is None or basis.get_root_id() != to_root_id):
1565
self.set_root_id(to_root_id)
1570
# determine the branch point
1571
graph = self.branch.repository.get_graph()
1572
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1574
base_tree = self.branch.repository.revision_tree(base_rev_id)
1576
nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
1578
change_reporter=change_reporter,
1579
show_base=show_base)
1580
self.set_last_revision(revision)
1581
# TODO - dedup parents list with things merged by pull ?
1582
# reuse the tree we've updated to to set the basis:
1583
parent_trees = [(revision, to_tree)]
1584
merges = self.get_parent_ids()[1:]
1585
# Ideally we ask the tree for the trees here, that way the working
1586
# tree can decide whether to give us the entire tree or give us a
1587
# lazy initialised tree. dirstate for instance will have the trees
1588
# in ram already, whereas a last-revision + basis-inventory tree
1589
# will not, but also does not need them when setting parents.
1590
for parent in merges:
1591
parent_trees.append(
1592
(parent, self.branch.repository.revision_tree(parent)))
1593
if not _mod_revision.is_null(old_tip):
1594
parent_trees.append(
1595
(old_tip, self.branch.repository.revision_tree(old_tip)))
1596
self.set_parent_trees(parent_trees)
1597
last_rev = parent_trees[0][0]
1600
def _write_hashcache_if_dirty(self):
1601
"""Write out the hashcache if it is dirty."""
1602
if self._hashcache.needs_write:
1604
self._hashcache.write()
1606
if e.errno not in (errno.EPERM, errno.EACCES):
1608
# TODO: jam 20061219 Should this be a warning? A single line
1609
# warning might be sufficient to let the user know what
1611
mutter('Could not write hashcache for %s\nError: %s',
1612
self._hashcache.cache_file_name(), e)
1614
def set_conflicts(self, arg):
1615
raise errors.UnsupportedOperation(self.set_conflicts, self)
1617
def add_conflicts(self, arg):
1618
raise errors.UnsupportedOperation(self.add_conflicts, self)
1620
def conflicts(self):
1621
raise NotImplementedError(self.conflicts)
1623
def walkdirs(self, prefix=""):
1624
"""Walk the directories of this tree.
1626
returns a generator which yields items in the form:
1627
((curren_directory_path, fileid),
1628
[(file1_path, file1_name, file1_kind, (lstat), file1_id,
1631
This API returns a generator, which is only valid during the current
1632
tree transaction - within a single lock_read or lock_write duration.
1634
If the tree is not locked, it may cause an error to be raised,
1635
depending on the tree implementation.
1637
disk_top = self.abspath(prefix)
1638
if disk_top.endswith('/'):
1639
disk_top = disk_top[:-1]
1640
top_strip_len = len(disk_top) + 1
1641
inventory_iterator = self._walkdirs(prefix)
1642
disk_iterator = osutils.walkdirs(disk_top, prefix)
1644
current_disk = disk_iterator.next()
1645
disk_finished = False
1647
if not (e.errno == errno.ENOENT or
1648
(sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
1651
disk_finished = True
1653
current_inv = inventory_iterator.next()
1654
inv_finished = False
1655
except StopIteration:
1658
while not inv_finished or not disk_finished:
1660
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1661
cur_disk_dir_content) = current_disk
1663
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1664
cur_disk_dir_content) = ((None, None), None)
1665
if not disk_finished:
1666
# strip out .bzr dirs
1667
if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
1668
len(cur_disk_dir_content) > 0):
1669
# osutils.walkdirs can be made nicer -
1670
# yield the path-from-prefix rather than the pathjoined
1672
bzrdir_loc = bisect_left(cur_disk_dir_content,
1674
if (bzrdir_loc < len(cur_disk_dir_content)
1675
and self.bzrdir.is_control_filename(
1676
cur_disk_dir_content[bzrdir_loc][0])):
1677
# we dont yield the contents of, or, .bzr itself.
1678
del cur_disk_dir_content[bzrdir_loc]
1680
# everything is unknown
1683
# everything is missing
1686
direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
1688
# disk is before inventory - unknown
1689
dirblock = [(relpath, basename, kind, stat, None, None) for
1690
relpath, basename, kind, stat, top_path in
1691
cur_disk_dir_content]
1692
yield (cur_disk_dir_relpath, None), dirblock
1694
current_disk = disk_iterator.next()
1695
except StopIteration:
1696
disk_finished = True
1698
# inventory is before disk - missing.
1699
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
1700
for relpath, basename, dkind, stat, fileid, kind in
1702
yield (current_inv[0][0], current_inv[0][1]), dirblock
1704
current_inv = inventory_iterator.next()
1705
except StopIteration:
1708
# versioned present directory
1709
# merge the inventory and disk data together
1711
for relpath, subiterator in itertools.groupby(sorted(
1712
current_inv[1] + cur_disk_dir_content,
1713
key=operator.itemgetter(0)), operator.itemgetter(1)):
1714
path_elements = list(subiterator)
1715
if len(path_elements) == 2:
1716
inv_row, disk_row = path_elements
1717
# versioned, present file
1718
dirblock.append((inv_row[0],
1719
inv_row[1], disk_row[2],
1720
disk_row[3], inv_row[4],
1722
elif len(path_elements[0]) == 5:
1724
dirblock.append((path_elements[0][0],
1725
path_elements[0][1], path_elements[0][2],
1726
path_elements[0][3], None, None))
1727
elif len(path_elements[0]) == 6:
1728
# versioned, absent file.
1729
dirblock.append((path_elements[0][0],
1730
path_elements[0][1], 'unknown', None,
1731
path_elements[0][4], path_elements[0][5]))
1733
raise NotImplementedError('unreachable code')
1734
yield current_inv[0], dirblock
1736
current_inv = inventory_iterator.next()
1737
except StopIteration:
1740
current_disk = disk_iterator.next()
1741
except StopIteration:
1742
disk_finished = True
1744
def _walkdirs(self, prefix=""):
1745
"""Walk the directories of this tree.
1747
:prefix: is used as the directrory to start with.
1748
returns a generator which yields items in the form:
1749
((curren_directory_path, fileid),
1750
[(file1_path, file1_name, file1_kind, None, file1_id,
1753
raise NotImplementedError(self._walkdirs)
1755
@needs_tree_write_lock
1756
def auto_resolve(self):
1757
"""Automatically resolve text conflicts according to contents.
1759
Only text conflicts are auto_resolvable. Files with no conflict markers
1760
are considered 'resolved', because bzr always puts conflict markers
1761
into files that have text conflicts. The corresponding .THIS .BASE and
1762
.OTHER files are deleted, as per 'resolve'.
1763
:return: a tuple of ConflictLists: (un_resolved, resolved).
1765
un_resolved = _mod_conflicts.ConflictList()
1766
resolved = _mod_conflicts.ConflictList()
1767
conflict_re = re.compile('^(<{7}|={7}|>{7})')
1768
for conflict in self.conflicts():
1769
if (conflict.typestring != 'text conflict' or
1770
self.kind(conflict.file_id) != 'file'):
1771
un_resolved.append(conflict)
1773
my_file = open(self.id2abspath(conflict.file_id), 'rb')
1775
for line in my_file:
1776
if conflict_re.search(line):
1777
un_resolved.append(conflict)
1780
resolved.append(conflict)
1783
resolved.remove_files(self)
1784
self.set_conflicts(un_resolved)
1785
return un_resolved, resolved
1788
def _check(self, references):
1789
"""Check the tree for consistency.
1791
:param references: A dict with keys matching the items returned by
1792
self._get_check_refs(), and values from looking those keys up in
1795
tree_basis = self.basis_tree()
1796
tree_basis.lock_read()
1798
repo_basis = references[('trees', self.last_revision())]
1799
if len(list(repo_basis.iter_changes(tree_basis))) > 0:
1800
raise errors.BzrCheckError(
1801
"Mismatched basis inventory content.")
1806
def _validate(self):
1807
"""Validate internal structures.
1809
This is meant mostly for the test suite. To give it a chance to detect
1810
corruption after actions have occurred. The default implementation is a
1813
:return: None. An exception should be raised if there is an error.
1818
def check_state(self):
1819
"""Check that the working state is/isn't valid."""
1820
check_refs = self._get_check_refs()
1822
for ref in check_refs:
1825
refs[ref] = self.branch.repository.revision_tree(value)
1828
def reset_state(self, revision_ids=None):
1829
"""Reset the state of the working tree.
1831
This does a hard-reset to a last-known-good state. This is a way to
1832
fix if something got corrupted (like the .bzr/checkout/dirstate file)
1834
raise NotImplementedError(self.reset_state)
1836
def _get_rules_searcher(self, default_searcher):
1837
"""See Tree._get_rules_searcher."""
1838
if self._rules_searcher is None:
1839
self._rules_searcher = super(WorkingTree,
1840
self)._get_rules_searcher(default_searcher)
1841
return self._rules_searcher
1843
def get_shelf_manager(self):
1844
"""Return the ShelfManager for this WorkingTree."""
1845
from bzrlib.shelf import ShelfManager
1846
return ShelfManager(self, self._transport)
1849
class InventoryWorkingTree(WorkingTree,
1850
bzrlib.mutabletree.MutableInventoryTree):
1851
"""Base class for working trees that are inventory-oriented.
1853
The inventory is held in the `Branch` working-inventory, and the
1854
files are in a directory on disk.
1856
It is possible for a `WorkingTree` to have a filename which is
1857
not listed in the Inventory and vice versa.
1860
def __init__(self, basedir='.',
1861
branch=DEPRECATED_PARAMETER,
1863
_control_files=None,
1867
"""Construct a InventoryWorkingTree instance. This is not a public API.
1869
:param branch: A branch to override probing for the branch.
1871
super(InventoryWorkingTree, self).__init__(basedir=basedir,
1872
branch=branch, _control_files=_control_files, _internal=_internal,
1873
_format=_format, _bzrdir=_bzrdir)
1875
if _inventory is None:
1876
# This will be acquired on lock_read() or lock_write()
1877
self._inventory_is_modified = False
1878
self._inventory = None
1880
# the caller of __init__ has provided an inventory,
1881
# we assume they know what they are doing - as its only
1882
# the Format factory and creation methods that are
1883
# permitted to do this.
1884
self._set_inventory(_inventory, dirty=False)
1886
def _set_inventory(self, inv, dirty):
1887
"""Set the internal cached inventory.
1889
:param inv: The inventory to set.
1890
:param dirty: A boolean indicating whether the inventory is the same
1891
logical inventory as whats on disk. If True the inventory is not
1892
the same and should be written to disk or data will be lost, if
1893
False then the inventory is the same as that on disk and any
1894
serialisation would be unneeded overhead.
1896
self._inventory = inv
1897
self._inventory_is_modified = dirty
1899
def _serialize(self, inventory, out_file):
1900
xml5.serializer_v5.write_inventory(self._inventory, out_file,
1903
def _deserialize(selt, in_file):
1904
return xml5.serializer_v5.read_inventory(in_file)
1906
@needs_tree_write_lock
1907
def _write_inventory(self, inv):
1908
"""Write inventory as the current inventory."""
1909
self._set_inventory(inv, dirty=True)
1912
# XXX: This method should be deprecated in favour of taking in a proper
1913
# new Inventory object.
1914
@needs_tree_write_lock
1915
def set_inventory(self, new_inventory_list):
1916
from bzrlib.inventory import (Inventory,
1920
inv = Inventory(self.get_root_id())
1921
for path, file_id, parent, kind in new_inventory_list:
1922
name = os.path.basename(path)
1925
# fixme, there should be a factory function inv,add_??
1926
if kind == 'directory':
1927
inv.add(InventoryDirectory(file_id, name, parent))
1928
elif kind == 'file':
1929
inv.add(InventoryFile(file_id, name, parent))
1930
elif kind == 'symlink':
1931
inv.add(InventoryLink(file_id, name, parent))
1933
raise errors.BzrError("unknown kind %r" % kind)
1934
self._write_inventory(inv)
1936
def _write_basis_inventory(self, xml):
1937
"""Write the basis inventory XML to the basis-inventory file"""
1938
path = self._basis_inventory_name()
1940
self._transport.put_file(path, sio,
1941
mode=self.bzrdir._get_file_mode())
1943
def _reset_data(self):
1944
"""Reset transient data that cannot be revalidated."""
1945
self._inventory_is_modified = False
1946
f = self._transport.get('inventory')
1948
result = self._deserialize(f)
1951
self._set_inventory(result, dirty=False)
1953
def _set_root_id(self, file_id):
1954
"""Set the root id for this tree, in a format specific manner.
1956
:param file_id: The file id to assign to the root. It must not be
1957
present in the current inventory or an error will occur. It must
1958
not be None, but rather a valid file id.
1960
inv = self._inventory
1961
orig_root_id = inv.root.file_id
1962
# TODO: it might be nice to exit early if there was nothing
1963
# to do, saving us from trigger a sync on unlock.
1964
self._inventory_is_modified = True
1965
# we preserve the root inventory entry object, but
1966
# unlinkit from the byid index
1967
del inv._byid[inv.root.file_id]
1968
inv.root.file_id = file_id
1969
# and link it into the index with the new changed id.
1970
inv._byid[inv.root.file_id] = inv.root
1971
# and finally update all children to reference the new id.
1972
# XXX: this should be safe to just look at the root.children
1973
# list, not the WHOLE INVENTORY.
1976
if entry.parent_id == orig_root_id:
1977
entry.parent_id = inv.root.file_id
1979
def all_file_ids(self):
1980
"""See Tree.iter_all_file_ids"""
1981
return set(self.inventory)
1983
def _cache_basis_inventory(self, new_revision):
1984
"""Cache new_revision as the basis inventory."""
1985
# TODO: this should allow the ready-to-use inventory to be passed in,
1986
# as commit already has that ready-to-use [while the format is the
1989
# this double handles the inventory - unpack and repack -
1990
# but is easier to understand. We can/should put a conditional
1991
# in here based on whether the inventory is in the latest format
1992
# - perhaps we should repack all inventories on a repository
1994
# the fast path is to copy the raw xml from the repository. If the
1995
# xml contains 'revision_id="', then we assume the right
1996
# revision_id is set. We must check for this full string, because a
1997
# root node id can legitimately look like 'revision_id' but cannot
1999
xml = self.branch.repository._get_inventory_xml(new_revision)
2000
firstline = xml.split('\n', 1)[0]
2001
if (not 'revision_id="' in firstline or
2002
'format="7"' not in firstline):
2003
inv = self.branch.repository._serializer.read_inventory_from_string(
2005
xml = self._create_basis_xml_from_inventory(new_revision, inv)
2006
self._write_basis_inventory(xml)
2007
except (errors.NoSuchRevision, errors.RevisionNotPresent):
2010
def _basis_inventory_name(self):
2011
return 'basis-inventory-cache'
2013
def _create_basis_xml_from_inventory(self, revision_id, inventory):
2014
"""Create the text that will be saved in basis-inventory"""
2015
inventory.revision_id = revision_id
2016
return xml7.serializer_v7.write_inventory_to_string(inventory)
2018
def read_basis_inventory(self):
2019
"""Read the cached basis inventory."""
2020
path = self._basis_inventory_name()
2021
return self._transport.get_bytes(path)
2024
def read_working_inventory(self):
2025
"""Read the working inventory.
2027
:raises errors.InventoryModified: read_working_inventory will fail
2028
when the current in memory inventory has been modified.
2030
# conceptually this should be an implementation detail of the tree.
2031
# XXX: Deprecate this.
2032
# ElementTree does its own conversion from UTF-8, so open in
2034
if self._inventory_is_modified:
2035
raise errors.InventoryModified(self)
2036
f = self._transport.get('inventory')
2038
result = self._deserialize(f)
2041
self._set_inventory(result, dirty=False)
2045
def get_root_id(self):
2046
"""Return the id of this trees root"""
2047
return self._inventory.root.file_id
2049
99
def has_id(self, file_id):
2050
100
# files that have been deleted are excluded
2051
inv = self.inventory
101
inv = self._inventory
2052
102
if not inv.has_id(file_id):
2054
104
path = inv.id2path(file_id)
2055
return osutils.lexists(self.abspath(path))
105
return os.path.exists(self.abspath(path))
2057
def has_or_had_id(self, file_id):
2058
if file_id == self.inventory.root.file_id:
2060
return self.inventory.has_id(file_id)
2062
108
__contains__ = has_id
2064
# should be deprecated - this is slow and in any case treating them as a
2065
# container is (we now know) bad style -- mbp 20070302
2066
## @deprecated_method(zero_fifteen)
2068
"""Iterate through file_ids for this tree.
2070
file_ids are in a WorkingTree if they are in the working inventory
2071
and the working file exists.
2073
inv = self._inventory
2074
for path, ie in inv.iter_entries():
2075
if osutils.lexists(self.abspath(path)):
2078
@needs_tree_write_lock
2079
def set_last_revision(self, new_revision):
2080
"""Change the last revision in the working tree."""
2081
if self._change_last_revision(new_revision):
2082
self._cache_basis_inventory(new_revision)
2084
@needs_tree_write_lock
2085
def reset_state(self, revision_ids=None):
2086
"""Reset the state of the working tree.
2088
This does a hard-reset to a last-known-good state. This is a way to
2089
fix if something got corrupted (like the .bzr/checkout/dirstate file)
2091
if revision_ids is None:
2092
revision_ids = self.get_parent_ids()
2093
if not revision_ids:
2094
rt = self.branch.repository.revision_tree(
2095
_mod_revision.NULL_REVISION)
2097
rt = self.branch.repository.revision_tree(revision_ids[0])
2098
self._write_inventory(rt.inventory)
2099
self.set_parent_ids(revision_ids)
2102
"""Write the in memory inventory to disk."""
2103
# TODO: Maybe this should only write on dirty ?
2104
if self._control_files._lock_mode != 'w':
2105
raise errors.NotWriteLocked(self)
2107
self._serialize(self._inventory, sio)
2109
self._transport.put_file('inventory', sio,
2110
mode=self.bzrdir._get_file_mode())
2111
self._inventory_is_modified = False
2114
def get_file_sha1(self, file_id, path=None, stat_value=None):
2116
path = self._inventory.id2path(file_id)
2117
return self._hashcache.get_sha1(path, stat_value)
2119
def get_file_mtime(self, file_id, path=None):
2120
"""See Tree.get_file_mtime."""
2122
path = self.inventory.id2path(file_id)
2123
return os.lstat(self.abspath(path)).st_mtime
2125
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2126
file_id = self.path2id(path)
2128
# For unversioned files on win32, we just assume they are not
2131
return self._inventory[file_id].executable
2133
def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2134
mode = stat_result.st_mode
2135
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2137
if not supports_executable():
2138
def is_executable(self, file_id, path=None):
2139
return self._inventory[file_id].executable
2141
_is_executable_from_path_and_stat = \
2142
_is_executable_from_path_and_stat_from_basis
2144
def is_executable(self, file_id, path=None):
2146
path = self.id2path(file_id)
2147
mode = os.lstat(self.abspath(path)).st_mode
2148
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2150
_is_executable_from_path_and_stat = \
2151
_is_executable_from_path_and_stat_from_stat
2153
@needs_tree_write_lock
2154
def _add(self, files, ids, kinds):
2155
"""See MutableTree._add."""
2156
# TODO: Re-adding a file that is removed in the working copy
2157
# should probably put it back with the previous ID.
2158
# the read and write working inventory should not occur in this
2159
# function - they should be part of lock_write and unlock.
2160
inv = self.inventory
2161
for f, file_id, kind in zip(files, ids, kinds):
2163
inv.add_path(f, kind=kind)
2165
inv.add_path(f, kind=kind, file_id=file_id)
2166
self._inventory_is_modified = True
2168
def revision_tree(self, revision_id):
2169
"""See WorkingTree.revision_id."""
2170
if revision_id == self.last_revision():
2172
xml = self.read_basis_inventory()
2173
except errors.NoSuchFile:
2177
inv = xml7.serializer_v7.read_inventory_from_string(xml)
2178
# dont use the repository revision_tree api because we want
2179
# to supply the inventory.
2180
if inv.revision_id == revision_id:
2181
return revisiontree.InventoryRevisionTree(
2182
self.branch.repository, inv, revision_id)
2183
except errors.BadInventoryFormat:
2185
# raise if there was no inventory, or if we read the wrong inventory.
2186
raise errors.NoSuchRevisionInTree(self, revision_id)
2189
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
2190
"""See Tree.annotate_iter
2192
This implementation will use the basis tree implementation if possible.
2193
Lines not in the basis are attributed to CURRENT_REVISION
2195
If there are pending merges, lines added by those merges will be
2196
incorrectly attributed to CURRENT_REVISION (but after committing, the
2197
attribution will be correct).
2199
maybe_file_parent_keys = []
2200
for parent_id in self.get_parent_ids():
2202
parent_tree = self.revision_tree(parent_id)
2203
except errors.NoSuchRevisionInTree:
2204
parent_tree = self.branch.repository.revision_tree(parent_id)
2205
parent_tree.lock_read()
2207
if file_id not in parent_tree:
2209
ie = parent_tree.inventory[file_id]
2210
if ie.kind != 'file':
2211
# Note: this is slightly unnecessary, because symlinks and
2212
# directories have a "text" which is the empty text, and we
2213
# know that won't mess up annotations. But it seems cleaner
2215
parent_text_key = (file_id, ie.revision)
2216
if parent_text_key not in maybe_file_parent_keys:
2217
maybe_file_parent_keys.append(parent_text_key)
2219
parent_tree.unlock()
2220
graph = _mod_graph.Graph(self.branch.repository.texts)
2221
heads = graph.heads(maybe_file_parent_keys)
2222
file_parent_keys = []
2223
for key in maybe_file_parent_keys:
2225
file_parent_keys.append(key)
2227
# Now we have the parents of this content
2228
annotator = self.branch.repository.texts.get_annotator()
2229
text = self.get_file_text(file_id)
2230
this_key =(file_id, default_revision)
2231
annotator.add_special_text(this_key, file_parent_keys, text)
2232
annotations = [(key[-1], line)
2233
for key, line in annotator.annotate_flat(this_key)]
2237
def merge_modified(self):
2238
"""Return a dictionary of files modified by a merge.
2240
The list is initialized by WorkingTree.set_merge_modified, which is
2241
typically called after we make some automatic updates to the tree
2244
This returns a map of file_id->sha1, containing only files which are
2245
still in the working inventory and have that text hash.
2248
hashfile = self._transport.get('merge-hashes')
2249
except errors.NoSuchFile:
2254
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
2255
raise errors.MergeModifiedFormatError()
2256
except StopIteration:
2257
raise errors.MergeModifiedFormatError()
2258
for s in _mod_rio.RioReader(hashfile):
2259
# RioReader reads in Unicode, so convert file_ids back to utf8
2260
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2261
if file_id not in self.inventory:
2263
text_hash = s.get("hash")
2264
if text_hash == self.get_file_sha1(file_id):
2265
merge_hashes[file_id] = text_hash
2271
def subsume(self, other_tree):
2272
def add_children(inventory, entry):
2273
for child_entry in entry.children.values():
2274
inventory._byid[child_entry.file_id] = child_entry
2275
if child_entry.kind == 'directory':
2276
add_children(inventory, child_entry)
2277
if other_tree.get_root_id() == self.get_root_id():
2278
raise errors.BadSubsumeSource(self, other_tree,
2279
'Trees have the same root')
2281
other_tree_path = self.relpath(other_tree.basedir)
2282
except errors.PathNotChild:
2283
raise errors.BadSubsumeSource(self, other_tree,
2284
'Tree is not contained by the other')
2285
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
2286
if new_root_parent is None:
2287
raise errors.BadSubsumeSource(self, other_tree,
2288
'Parent directory is not versioned.')
2289
# We need to ensure that the result of a fetch will have a
2290
# versionedfile for the other_tree root, and only fetching into
2291
# RepositoryKnit2 guarantees that.
2292
if not self.branch.repository.supports_rich_root():
2293
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
2294
other_tree.lock_tree_write()
2296
new_parents = other_tree.get_parent_ids()
2297
other_root = other_tree.inventory.root
2298
other_root.parent_id = new_root_parent
2299
other_root.name = osutils.basename(other_tree_path)
2300
self.inventory.add(other_root)
2301
add_children(self.inventory, other_root)
2302
self._write_inventory(self.inventory)
2303
# normally we don't want to fetch whole repositories, but i think
2304
# here we really do want to consolidate the whole thing.
2305
for parent_id in other_tree.get_parent_ids():
2306
self.branch.fetch(other_tree.branch, parent_id)
2307
self.add_parent_tree_id(parent_id)
2310
other_tree.bzrdir.retire_bzrdir()
2312
@needs_tree_write_lock
2313
def extract(self, file_id, format=None):
2314
"""Extract a subtree from this tree.
2316
A new branch will be created, relative to the path for this tree.
2320
segments = osutils.splitpath(path)
2321
transport = self.branch.bzrdir.root_transport
2322
for name in segments:
2323
transport = transport.clone(name)
2324
transport.ensure_base()
2327
sub_path = self.id2path(file_id)
2328
branch_transport = mkdirs(sub_path)
2330
format = self.bzrdir.cloning_metadir()
2331
branch_transport.ensure_base()
2332
branch_bzrdir = format.initialize_on_transport(branch_transport)
2334
repo = branch_bzrdir.find_repository()
2335
except errors.NoRepositoryPresent:
2336
repo = branch_bzrdir.create_repository()
2337
if not repo.supports_rich_root():
2338
raise errors.RootNotRich()
2339
new_branch = branch_bzrdir.create_branch()
2340
new_branch.pull(self.branch)
2341
for parent_id in self.get_parent_ids():
2342
new_branch.fetch(self.branch, parent_id)
2343
tree_transport = self.bzrdir.root_transport.clone(sub_path)
2344
if tree_transport.base != branch_transport.base:
2345
tree_bzrdir = format.initialize_on_transport(tree_transport)
2346
branch.BranchReferenceFormat().initialize(tree_bzrdir,
2347
target_branch=new_branch)
2349
tree_bzrdir = branch_bzrdir
2350
wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2351
wt.set_parent_ids(self.get_parent_ids())
2352
my_inv = self.inventory
2353
child_inv = inventory.Inventory(root_id=None)
2354
new_root = my_inv[file_id]
2355
my_inv.remove_recursive_id(file_id)
2356
new_root.parent_id = None
2357
child_inv.add(new_root)
2358
self._write_inventory(my_inv)
2359
wt._write_inventory(child_inv)
2362
def list_files(self, include_root=False, from_dir=None, recursive=True):
2363
"""List all files as (path, class, kind, id, entry).
111
def get_file_size(self, file_id):
112
# is this still called?
113
raise NotImplementedError()
116
def get_file_sha1(self, file_id):
117
path = self._inventory.id2path(file_id)
118
return self._hashcache.get_sha1(path)
121
def file_class(self, filename):
122
if self.path2id(filename):
124
elif self.is_ignored(filename):
130
def list_files(self):
131
"""Recursively list all files as (path, class, kind, id).
2365
133
Lists, but does not descend into unversioned directories.
2366
135
This does not include files that have been deleted in this
2367
tree. Skips the control directory.
2369
:param include_root: if True, return an entry for the root
2370
:param from_dir: start from this directory or None for the root
2371
:param recursive: whether to recurse into subdirectories or not
138
Skips the control directory.
2373
# list_files is an iterator, so @needs_read_lock doesn't work properly
2374
# with it. So callers should be careful to always read_lock the tree.
2375
if not self.is_locked():
2376
raise errors.ObjectNotLocked(self)
2378
inv = self.inventory
2379
if from_dir is None and include_root is True:
2380
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2381
# Convert these into local objects to save lookup times
2382
pathjoin = osutils.pathjoin
2383
file_kind = self._kind
2385
# transport.base ends in a slash, we want the piece
2386
# between the last two slashes
2387
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
2389
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
2391
# directory file_id, relative path, absolute path, reverse sorted children
2392
if from_dir is not None:
2393
from_dir_id = inv.path2id(from_dir)
2394
if from_dir_id is None:
2395
# Directory not versioned
2397
from_dir_abspath = pathjoin(self.basedir, from_dir)
2399
from_dir_id = inv.root.file_id
2400
from_dir_abspath = self.basedir
2401
children = os.listdir(from_dir_abspath)
2403
# jam 20060527 The kernel sized tree seems equivalent whether we
2404
# use a deque and popleft to keep them sorted, or if we use a plain
2405
# list and just reverse() them.
2406
children = collections.deque(children)
2407
stack = [(from_dir_id, u'', from_dir_abspath, children)]
2409
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
2412
f = children.popleft()
140
inv = self._inventory
142
def descend(from_dir_relpath, from_dir_id, dp):
2413
146
## TODO: If we find a subdirectory with its own .bzr
2414
147
## directory, then that is a separate tree and we
2415
148
## should exclude it.
2417
# the bzrdir for this tree
2418
if transport_base_dir == f:
149
if bzrlib.BZRDIR == f:
2421
# we know that from_dir_relpath and from_dir_abspath never end in a slash
2422
# and 'f' doesn't begin with one, we can do a string op, rather
2423
# than the checks of pathjoin(), all relative paths will have an extra slash
2425
fp = from_dir_relpath + '/' + f
153
fp = appendpath(from_dir_relpath, f)
2428
fap = from_dir_abspath + '/' + f
2430
dir_ie = inv[from_dir_id]
2431
if dir_ie.kind == 'directory':
2432
f_ie = dir_ie.children.get(f)
156
fap = appendpath(dp, f)
158
f_ie = inv.get_child(from_dir_id, f)
2437
elif self.is_ignored(fp[1:]):
161
elif self.is_ignored(fp):
2440
# we may not have found this file, because of a unicode
2441
# issue, or because the directory was actually a symlink.
2442
f_norm, can_access = osutils.normalized_filename(f)
2443
if f == f_norm or not can_access:
2444
# No change, so treat this file normally
2447
# this file can be accessed by a normalized path
2448
# check again if it is versioned
2449
# these lines are repeated here for performance
2451
fp = from_dir_relpath + '/' + f
2452
fap = from_dir_abspath + '/' + f
2453
f_ie = inv.get_child(from_dir_id, f)
2456
elif self.is_ignored(fp[1:]):
2461
166
fk = file_kind(fap)
2463
# make a last minute entry
2465
yield fp[1:], c, fk, f_ie.file_id, f_ie
2468
yield fp[1:], c, fk, None, fk_entries[fk]()
2470
yield fp[1:], c, fk, None, TreeEntry()
170
raise BzrCheckError("file %r entered as kind %r id %r, "
172
% (fap, f_ie.kind, f_ie.file_id, fk))
174
yield fp, c, fk, (f_ie and f_ie.file_id)
2473
176
if fk != 'directory':
2476
# But do this child first if recursing down
2478
new_children = os.listdir(fap)
2480
new_children = collections.deque(new_children)
2481
stack.append((f_ie.file_id, fp, fap, new_children))
2482
# Break out of inner loop,
2483
# so that we start outer loop with child
2486
# if we finished all children, pop it off the stack
2489
@needs_tree_write_lock
2490
def move(self, from_paths, to_dir=None, after=False):
2493
to_dir must exist in the inventory.
2495
If to_dir exists and is a directory, the files are moved into
2496
it, keeping their old names.
2498
Note that to_dir is only the last component of the new name;
2499
this doesn't change the directory.
2501
For each entry in from_paths the move mode will be determined
2504
The first mode moves the file in the filesystem and updates the
2505
inventory. The second mode only updates the inventory without
2506
touching the file on the filesystem.
2508
move uses the second mode if 'after == True' and the target is not
2509
versioned but present in the working tree.
2511
move uses the second mode if 'after == False' and the source is
2512
versioned but no longer in the working tree, and the target is not
2513
versioned but present in the working tree.
2515
move uses the first mode if 'after == False' and the source is
2516
versioned and present in the working tree, and the target is not
2517
versioned and not present in the working tree.
2519
Everything else results in an error.
2521
This returns a list of (from_path, to_path) pairs for each
2522
entry that is moved.
2527
# check for deprecated use of signature
2529
raise TypeError('You must supply a target directory')
2530
# check destination directory
2531
if isinstance(from_paths, basestring):
2533
inv = self.inventory
2534
to_abs = self.abspath(to_dir)
2535
if not isdir(to_abs):
2536
raise errors.BzrMoveFailedError('',to_dir,
2537
errors.NotADirectory(to_abs))
2538
if not self.has_filename(to_dir):
2539
raise errors.BzrMoveFailedError('',to_dir,
2540
errors.NotInWorkingDirectory(to_dir))
2541
to_dir_id = inv.path2id(to_dir)
2542
if to_dir_id is None:
2543
raise errors.BzrMoveFailedError('',to_dir,
2544
errors.NotVersionedError(path=to_dir))
2546
to_dir_ie = inv[to_dir_id]
2547
if to_dir_ie.kind != 'directory':
2548
raise errors.BzrMoveFailedError('',to_dir,
2549
errors.NotADirectory(to_abs))
2551
# create rename entries and tuples
2552
for from_rel in from_paths:
2553
from_tail = splitpath(from_rel)[-1]
2554
from_id = inv.path2id(from_rel)
2556
raise errors.BzrMoveFailedError(from_rel,to_dir,
2557
errors.NotVersionedError(path=from_rel))
2559
from_entry = inv[from_id]
2560
from_parent_id = from_entry.parent_id
2561
to_rel = pathjoin(to_dir, from_tail)
2562
rename_entry = InventoryWorkingTree._RenameEntry(
2565
from_tail=from_tail,
2566
from_parent_id=from_parent_id,
2567
to_rel=to_rel, to_tail=from_tail,
2568
to_parent_id=to_dir_id)
2569
rename_entries.append(rename_entry)
2570
rename_tuples.append((from_rel, to_rel))
2572
# determine which move mode to use. checks also for movability
2573
rename_entries = self._determine_mv_mode(rename_entries, after)
2575
original_modified = self._inventory_is_modified
2578
self._inventory_is_modified = True
2579
self._move(rename_entries)
2581
# restore the inventory on error
2582
self._inventory_is_modified = original_modified
2584
self._write_inventory(inv)
2585
return rename_tuples
2587
@needs_tree_write_lock
2588
def rename_one(self, from_rel, to_rel, after=False):
2591
This can change the directory or the filename or both.
2593
rename_one has several 'modes' to work. First, it can rename a physical
2594
file and change the file_id. That is the normal mode. Second, it can
2595
only change the file_id without touching any physical file.
2597
rename_one uses the second mode if 'after == True' and 'to_rel' is not
2598
versioned but present in the working tree.
2600
rename_one uses the second mode if 'after == False' and 'from_rel' is
2601
versioned but no longer in the working tree, and 'to_rel' is not
2602
versioned but present in the working tree.
2604
rename_one uses the first mode if 'after == False' and 'from_rel' is
2605
versioned and present in the working tree, and 'to_rel' is not
2606
versioned and not present in the working tree.
2608
Everything else results in an error.
2610
inv = self.inventory
2613
# create rename entries and tuples
2614
from_tail = splitpath(from_rel)[-1]
2615
from_id = inv.path2id(from_rel)
2617
# if file is missing in the inventory maybe it's in the basis_tree
2618
basis_tree = self.branch.basis_tree()
2619
from_id = basis_tree.path2id(from_rel)
2621
raise errors.BzrRenameFailedError(from_rel,to_rel,
2622
errors.NotVersionedError(path=from_rel))
2623
# put entry back in the inventory so we can rename it
2624
from_entry = basis_tree.inventory[from_id].copy()
2627
from_entry = inv[from_id]
2628
from_parent_id = from_entry.parent_id
2629
to_dir, to_tail = os.path.split(to_rel)
2630
to_dir_id = inv.path2id(to_dir)
2631
rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2633
from_tail=from_tail,
2634
from_parent_id=from_parent_id,
2635
to_rel=to_rel, to_tail=to_tail,
2636
to_parent_id=to_dir_id)
2637
rename_entries.append(rename_entry)
2639
# determine which move mode to use. checks also for movability
2640
rename_entries = self._determine_mv_mode(rename_entries, after)
2642
# check if the target changed directory and if the target directory is
2644
if to_dir_id is None:
2645
raise errors.BzrMoveFailedError(from_rel,to_rel,
2646
errors.NotVersionedError(path=to_dir))
2648
# all checks done. now we can continue with our actual work
2649
mutter('rename_one:\n'
2654
' to_dir_id {%s}\n',
2655
from_id, from_rel, to_rel, to_dir, to_dir_id)
2657
self._move(rename_entries)
2658
self._write_inventory(inv)
2660
class _RenameEntry(object):
2661
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2662
to_rel, to_tail, to_parent_id, only_change_inv=False):
2663
self.from_rel = from_rel
2664
self.from_id = from_id
2665
self.from_tail = from_tail
2666
self.from_parent_id = from_parent_id
2667
self.to_rel = to_rel
2668
self.to_tail = to_tail
2669
self.to_parent_id = to_parent_id
2670
self.only_change_inv = only_change_inv
2672
def _determine_mv_mode(self, rename_entries, after=False):
2673
"""Determines for each from-to pair if both inventory and working tree
2674
or only the inventory has to be changed.
2676
Also does basic plausability tests.
2678
inv = self.inventory
2680
for rename_entry in rename_entries:
2681
# store to local variables for easier reference
2682
from_rel = rename_entry.from_rel
2683
from_id = rename_entry.from_id
2684
to_rel = rename_entry.to_rel
2685
to_id = inv.path2id(to_rel)
2686
only_change_inv = False
2688
# check the inventory for source and destination
2690
raise errors.BzrMoveFailedError(from_rel,to_rel,
2691
errors.NotVersionedError(path=from_rel))
2692
if to_id is not None:
2693
raise errors.BzrMoveFailedError(from_rel,to_rel,
2694
errors.AlreadyVersionedError(path=to_rel))
2696
# try to determine the mode for rename (only change inv or change
2697
# inv and file system)
2699
if not self.has_filename(to_rel):
2700
raise errors.BzrMoveFailedError(from_id,to_rel,
2701
errors.NoSuchFile(path=to_rel,
2702
extra="New file has not been created yet"))
2703
only_change_inv = True
2704
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
2705
only_change_inv = True
2706
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
2707
only_change_inv = False
2708
elif (not self.case_sensitive
2709
and from_rel.lower() == to_rel.lower()
2710
and self.has_filename(from_rel)):
2711
only_change_inv = False
2713
# something is wrong, so lets determine what exactly
2714
if not self.has_filename(from_rel) and \
2715
not self.has_filename(to_rel):
2716
raise errors.BzrRenameFailedError(from_rel,to_rel,
2717
errors.PathsDoNotExist(paths=(str(from_rel),
2720
raise errors.RenameFailedFilesExist(from_rel, to_rel)
2721
rename_entry.only_change_inv = only_change_inv
2722
return rename_entries
2724
def _move(self, rename_entries):
2725
"""Moves a list of files.
2727
Depending on the value of the flag 'only_change_inv', the
2728
file will be moved on the file system or not.
2730
inv = self.inventory
2733
for entry in rename_entries:
2735
self._move_entry(entry)
2737
self._rollback_move(moved)
2741
def _rollback_move(self, moved):
2742
"""Try to rollback a previous move in case of an filesystem error."""
2743
inv = self.inventory
2746
self._move_entry(WorkingTree._RenameEntry(
2747
entry.to_rel, entry.from_id,
2748
entry.to_tail, entry.to_parent_id, entry.from_rel,
2749
entry.from_tail, entry.from_parent_id,
2750
entry.only_change_inv))
2751
except errors.BzrMoveFailedError, e:
2752
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
2753
" The working tree is in an inconsistent state."
2754
" Please consider doing a 'bzr revert'."
2755
" Error message is: %s" % e)
2757
def _move_entry(self, entry):
2758
inv = self.inventory
2759
from_rel_abs = self.abspath(entry.from_rel)
2760
to_rel_abs = self.abspath(entry.to_rel)
2761
if from_rel_abs == to_rel_abs:
2762
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
2763
"Source and target are identical.")
2765
if not entry.only_change_inv:
2767
osutils.rename(from_rel_abs, to_rel_abs)
2769
raise errors.BzrMoveFailedError(entry.from_rel,
2771
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
2773
@needs_tree_write_lock
2774
def unversion(self, file_ids):
2775
"""Remove the file ids in file_ids from the current versioned set.
2777
When a file_id is unversioned, all of its children are automatically
2780
:param file_ids: The file ids to stop versioning.
2781
:raises: NoSuchId if any fileid is not currently versioned.
2783
for file_id in file_ids:
2784
if file_id not in self._inventory:
2785
raise errors.NoSuchId(self, file_id)
2786
for file_id in file_ids:
2787
if self._inventory.has_id(file_id):
2788
self._inventory.remove_recursive_id(file_id)
2790
# in the future this should just set a dirty bit to wait for the
2791
# final unlock. However, until all methods of workingtree start
2792
# with the current in -memory inventory rather than triggering
2793
# a read, it is more complex - we need to teach read_inventory
2794
# to know when to read, and when to not read first... and possibly
2795
# to save first when the in memory one may be corrupted.
2796
# so for now, we just only write it if it is indeed dirty.
2798
self._write_inventory(self._inventory)
2800
def stored_kind(self, file_id):
2801
"""See Tree.stored_kind"""
2802
return self.inventory[file_id].kind
180
# don't descend unversioned directories
183
for ff in descend(fp, f_ie.file_id, fap):
186
for f in descend('', inv.root.file_id, self.basedir):
192
for subp in self.extras():
193
if not self.is_ignored(subp):
2804
197
def extras(self):
2805
"""Yield all unversioned files in this WorkingTree.
198
"""Yield all unknown files in this WorkingTree.
2807
If there are any unversioned directories then only the directory is
2808
returned, not all its children. But if there are unversioned files
200
If there are any unknown directories then only the directory is
201
returned, not all its children. But if there are unknown files
2809
202
under a versioned subdirectory, they are returned.
2811
204
Currently returned depth-first, sorted by name within directories.
2812
This is the same order used by 'osutils.walkdirs'.
2814
206
## TODO: Work from given directory downwards
2815
207
for path, dir_entry in self.inventory.directories():
2816
# mutter("search for unknowns in %r", path)
208
mutter("search for unknowns in %r" % path)
2817
209
dirabs = self.abspath(path)
2818
210
if not isdir(dirabs):
2819
211
# e.g. directory deleted