39
36
# At the moment they may alias the inventory and have old copies of it in
40
37
# memory. (Now done? -- mbp 20060309)
42
from binascii import hexlify
39
from cStringIO import StringIO
43
from bzrlib.lazy_import import lazy_import
44
lazy_import(globals(), """
45
from bisect import bisect_left
44
from copy import deepcopy
45
from cStringIO import StringIO
55
from bzrlib import bzrdir, errors, ignores, osutils, urlutils
56
from bzrlib.atomicfile import AtomicFile
57
conflicts as _mod_conflicts,
66
revision as _mod_revision,
57
76
import bzrlib.branch
58
from bzrlib.conflicts import Conflict, ConflictList, CONFLICT_SUFFIXES
77
from bzrlib.transport import get_transport
78
from bzrlib.workingtree_4 import (
85
from bzrlib import symbol_versioning
59
86
from bzrlib.decorators import needs_read_lock, needs_write_lock
60
from bzrlib.errors import (BzrCheckError,
63
WeaveRevisionNotPresent,
67
MergeModifiedFormatError,
70
from bzrlib.inventory import InventoryEntry, Inventory
71
from bzrlib.lockable_files import LockableFiles, TransportLock
87
from bzrlib.lockable_files import LockableFiles
72
88
from bzrlib.lockdir import LockDir
73
from bzrlib.merge import merge_inner, transform_tree
89
import bzrlib.mutabletree
90
from bzrlib.mutabletree import needs_tree_write_lock
91
from bzrlib import osutils
74
92
from bzrlib.osutils import (
102
from bzrlib.filters import filtered_input_file
103
from bzrlib.trace import mutter, note
104
from bzrlib.transport.local import LocalTransport
91
105
from bzrlib.progress import DummyProgress, ProgressPhase
92
from bzrlib.revision import NULL_REVISION
106
from bzrlib.revision import CURRENT_REVISION
93
107
from bzrlib.rio import RioReader, rio_file, Stanza
94
from bzrlib.symbol_versioning import (deprecated_passed,
100
from bzrlib.trace import mutter, note
101
from bzrlib.transform import build_tree
102
from bzrlib.transport import get_transport
103
from bzrlib.transport.local import LocalTransport
104
from bzrlib.textui import show_status
110
# the regex removes any weird characters; we don't escape them
111
# but rather just pull them out
112
_gen_file_id_re = re.compile(r'[^\w.]')
113
_gen_id_suffix = None
117
def _next_id_suffix():
118
"""Create a new file id suffix that is reasonably unique.
120
On the first call we combine the current time with 64 bits of randomness
121
to give a highly probably globally unique number. Then each call in the same
122
process adds 1 to a serial number we append to that unique value.
124
# XXX TODO: change bzrlib.add.smart_add to call workingtree.add() rather
125
# than having to move the id randomness out of the inner loop like this.
126
# XXX TODO: for the global randomness this uses we should add the thread-id
127
# before the serial #.
128
global _gen_id_suffix, _gen_id_serial
129
if _gen_id_suffix is None:
130
_gen_id_suffix = "-%s-%s-" % (compact_date(time()), rand_chars(16))
132
return _gen_id_suffix + str(_gen_id_serial)
135
def gen_file_id(name):
136
"""Return new file id for the basename 'name'.
138
The uniqueness is supplied from _next_id_suffix.
140
# The real randomness is in the _next_id_suffix, the
141
# rest of the identifier is just to be nice.
143
# 1) Remove non-ascii word characters to keep the ids portable
144
# 2) squash to lowercase, so the file id doesn't have to
145
# be escaped (case insensitive filesystems would bork for ids
146
# that only differred in case without escaping).
147
# 3) truncate the filename to 20 chars. Long filenames also bork on some
149
# 4) Removing starting '.' characters to prevent the file ids from
150
# being considered hidden.
151
ascii_word_only = _gen_file_id_re.sub('', name.lower())
152
short_no_dots = ascii_word_only.lstrip('.')[:20]
153
return short_no_dots + _next_id_suffix()
157
"""Return a new tree-root file id."""
158
return gen_file_id('TREE_ROOT')
108
from bzrlib.symbol_versioning import (
110
DEPRECATED_PARAMETER,
114
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
115
CONFLICT_HEADER_1 = "BZR conflict list format 1"
117
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
161
120
class TreeEntry(object):
162
121
"""An entry that implements the minimum interface used by commands.
164
This needs further inspection, it may be better to have
123
This needs further inspection, it may be better to have
165
124
InventoryEntries without ids - though that seems wrong. For now,
166
125
this is a parallel hierarchy to InventoryEntry, and needs to become
167
126
one of several things: decorates to that hierarchy, children of, or
385
373
if osutils.lexists(self.abspath(path)):
376
def all_file_ids(self):
377
"""See Tree.iter_all_file_ids"""
378
return set(self.inventory)
388
380
def __repr__(self):
389
381
return "<%s of %s>" % (self.__class__.__name__,
390
382
getattr(self, 'basedir', None))
392
384
def abspath(self, filename):
393
385
return pathjoin(self.basedir, filename)
395
387
def basis_tree(self):
396
"""Return RevisionTree for the current last revision."""
397
revision_id = self.last_revision()
398
if revision_id is not None:
400
xml = self.read_basis_inventory()
401
inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
404
if inv is not None and inv.revision_id == revision_id:
405
return bzrlib.tree.RevisionTree(self.branch.repository, inv,
407
# FIXME? RBC 20060403 should we cache the inventory here ?
408
return self.branch.repository.revision_tree(revision_id)
411
@deprecated_method(zero_eight)
412
def create(branch, directory):
413
"""Create a workingtree for branch at directory.
415
If existing_directory already exists it must have a .bzr directory.
416
If it does not exist, it will be created.
418
This returns a new WorkingTree object for the new checkout.
420
TODO FIXME RBC 20060124 when we have checkout formats in place this
421
should accept an optional revisionid to checkout [and reject this if
422
checking out into the same dir as a pre-checkout-aware branch format.]
424
XXX: When BzrDir is present, these should be created through that
427
warnings.warn('delete WorkingTree.create', stacklevel=3)
428
transport = get_transport(directory)
429
if branch.bzrdir.root_transport.base == transport.base:
431
return branch.bzrdir.create_workingtree()
432
# different directory,
433
# create a branch reference
434
# and now a working tree.
435
raise NotImplementedError
438
@deprecated_method(zero_eight)
439
def create_standalone(directory):
440
"""Create a checkout and a branch and a repo at directory.
442
Directory must exist and be empty.
444
please use BzrDir.create_standalone_workingtree
446
return bzrdir.BzrDir.create_standalone_workingtree(directory)
388
"""Return RevisionTree for the current last revision.
390
If the left most parent is a ghost then the returned tree will be an
391
empty tree - one obtained by calling
392
repository.revision_tree(NULL_REVISION).
395
revision_id = self.get_parent_ids()[0]
397
# no parents, return an empty revision tree.
398
# in the future this should return the tree for
399
# 'empty:' - the implicit root empty tree.
400
return self.branch.repository.revision_tree(
401
_mod_revision.NULL_REVISION)
403
return self.revision_tree(revision_id)
404
except errors.NoSuchRevision:
406
# No cached copy available, retrieve from the repository.
407
# FIXME? RBC 20060403 should we cache the inventory locally
410
return self.branch.repository.revision_tree(revision_id)
411
except (errors.RevisionNotPresent, errors.NoSuchRevision):
412
# the basis tree *may* be a ghost or a low level error may have
413
# occurred. If the revision is present, its a problem, if its not
415
if self.branch.repository.has_revision(revision_id):
417
# the basis tree is a ghost so return an empty tree.
418
return self.branch.repository.revision_tree(
419
_mod_revision.NULL_REVISION)
422
self._flush_ignore_list_cache()
448
424
def relpath(self, path):
449
425
"""Return the local path portion from a given path.
451
The path may be absolute or relative. If its a relative path it is
427
The path may be absolute or relative. If its a relative path it is
452
428
interpreted relative to the python current working directory.
454
return relpath(self.basedir, path)
430
return osutils.relpath(self.basedir, path)
456
432
def has_filename(self, filename):
457
433
return osutils.lexists(self.abspath(filename))
459
def get_file(self, file_id):
460
return self.get_file_byname(self.id2path(file_id))
462
def get_file_text(self, file_id):
463
return self.get_file(file_id).read()
465
def get_file_byname(self, filename):
466
return file(self.abspath(filename), 'rb')
435
def get_file(self, file_id, path=None, filtered=True):
436
return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
438
def get_file_with_stat(self, file_id, path=None, filtered=True,
440
"""See Tree.get_file_with_stat."""
442
path = self.id2path(file_id)
443
file_obj = self.get_file_byname(path, filtered=False)
444
stat_value = _fstat(file_obj.fileno())
445
if filtered and self.supports_content_filtering():
446
filters = self._content_filter_stack(path)
447
file_obj = filtered_input_file(file_obj, filters)
448
return (file_obj, stat_value)
450
def get_file_text(self, file_id, path=None, filtered=True):
451
return self.get_file(file_id, path=path, filtered=filtered).read()
453
def get_file_byname(self, filename, filtered=True):
454
path = self.abspath(filename)
456
if filtered and self.supports_content_filtering():
457
filters = self._content_filter_stack(filename)
458
return filtered_input_file(f, filters)
462
def get_file_lines(self, file_id, path=None, filtered=True):
463
"""See Tree.get_file_lines()"""
464
file = self.get_file(file_id, path, filtered=filtered)
466
return file.readlines()
471
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
472
"""See Tree.annotate_iter
474
This implementation will use the basis tree implementation if possible.
475
Lines not in the basis are attributed to CURRENT_REVISION
477
If there are pending merges, lines added by those merges will be
478
incorrectly attributed to CURRENT_REVISION (but after committing, the
479
attribution will be correct).
481
maybe_file_parent_keys = []
482
for parent_id in self.get_parent_ids():
484
parent_tree = self.revision_tree(parent_id)
485
except errors.NoSuchRevisionInTree:
486
parent_tree = self.branch.repository.revision_tree(parent_id)
487
parent_tree.lock_read()
489
if file_id not in parent_tree:
491
ie = parent_tree.inventory[file_id]
492
if ie.kind != 'file':
493
# Note: this is slightly unnecessary, because symlinks and
494
# directories have a "text" which is the empty text, and we
495
# know that won't mess up annotations. But it seems cleaner
497
parent_text_key = (file_id, ie.revision)
498
if parent_text_key not in maybe_file_parent_keys:
499
maybe_file_parent_keys.append(parent_text_key)
502
graph = _mod_graph.Graph(self.branch.repository.texts)
503
heads = graph.heads(maybe_file_parent_keys)
504
file_parent_keys = []
505
for key in maybe_file_parent_keys:
507
file_parent_keys.append(key)
509
# Now we have the parents of this content
510
annotator = self.branch.repository.texts.get_annotator()
511
text = self.get_file(file_id).read()
512
this_key =(file_id, default_revision)
513
annotator.add_special_text(this_key, file_parent_keys, text)
514
annotations = [(key[-1], line)
515
for key, line in annotator.annotate_flat(this_key)]
518
def _get_ancestors(self, default_revision):
519
ancestors = set([default_revision])
520
for parent_id in self.get_parent_ids():
521
ancestors.update(self.branch.repository.get_ancestry(
522
parent_id, topo_sorted=False))
468
525
def get_parent_ids(self):
469
526
"""See Tree.get_parent_ids.
471
528
This implementation reads the pending merges list and last_revision
472
529
value and uses that to decide what the parents list should be.
474
last_rev = self.last_revision()
531
last_rev = _mod_revision.ensure_null(self._last_revision())
532
if _mod_revision.NULL_REVISION == last_rev:
478
535
parents = [last_rev]
479
other_parents = self.pending_merges()
480
return parents + other_parents
537
merges_file = self._transport.get('pending-merges')
538
except errors.NoSuchFile:
541
for l in merges_file.readlines():
542
revision_id = l.rstrip('\n')
543
parents.append(revision_id)
482
547
def get_root_id(self):
483
548
"""Return the id of this trees root"""
484
inv = self.read_working_inventory()
485
return inv.root.file_id
549
return self._inventory.root.file_id
487
551
def _get_store_filename(self, file_id):
488
552
## XXX: badly named; this is not in the store at all
489
553
return self.abspath(self.id2path(file_id))
492
def clone(self, to_bzrdir, revision_id=None, basis=None):
556
def clone(self, to_bzrdir, revision_id=None):
493
557
"""Duplicate this working tree into to_bzr, including all state.
495
559
Specifically modified files are kept as modified, but
496
560
ignored and unknown files are discarded.
498
562
If you want to make a new line of development, see bzrdir.sprout()
501
If not None, the cloned tree will have its last revision set to
502
revision, and and difference between the source trees last revision
565
If not None, the cloned tree will have its last revision set to
566
revision, and difference between the source trees last revision
503
567
and this one merged in.
506
If not None, a closer copy of a tree which may have some files in
507
common, and which file content should be preferentially copied from.
509
569
# assumes the target bzr dir format is compatible.
510
result = self._format.initialize(to_bzrdir)
570
result = to_bzrdir.create_workingtree()
511
571
self.copy_content_into(result, revision_id)
515
575
def copy_content_into(self, tree, revision_id=None):
516
576
"""Copy the current content and user files of this tree into tree."""
577
tree.set_root_id(self.get_root_id())
517
578
if revision_id is None:
518
transform_tree(tree, self)
579
merge.transform_tree(tree, self)
520
# TODO now merge from tree.last_revision to revision
521
transform_tree(tree, self)
522
tree.set_last_revision(revision_id)
525
def commit(self, message=None, revprops=None, *args, **kwargs):
526
# avoid circular imports
527
from bzrlib.commit import Commit
530
if not 'branch-nick' in revprops:
531
revprops['branch-nick'] = self.branch.nick
532
# args for wt.commit start at message from the Commit.commit method,
533
# but with branch a kwarg now, passing in args as is results in the
534
#message being used for the branch
535
args = (DEPRECATED_PARAMETER, message, ) + args
536
committed_id = Commit().commit( working_tree=self, revprops=revprops,
538
self._set_inventory(self.read_working_inventory())
581
# TODO now merge from tree.last_revision to revision (to preserve
582
# user local changes)
583
merge.transform_tree(tree, self)
584
tree.set_parent_ids([revision_id])
541
586
def id2abspath(self, file_id):
542
587
return self.abspath(self.id2path(file_id))
544
589
def has_id(self, file_id):
545
590
# files that have been deleted are excluded
546
inv = self._inventory
547
592
if not inv.has_id(file_id):
549
594
path = inv.id2path(file_id)
557
602
__contains__ = has_id
559
604
def get_file_size(self, file_id):
560
return os.path.getsize(self.id2abspath(file_id))
605
"""See Tree.get_file_size"""
607
return os.path.getsize(self.id2abspath(file_id))
609
if e.errno != errno.ENOENT:
563
def get_file_sha1(self, file_id, path=None):
615
def get_file_sha1(self, file_id, path=None, stat_value=None):
565
617
path = self._inventory.id2path(file_id)
566
return self._hashcache.get_sha1(path)
618
return self._hashcache.get_sha1(path, stat_value)
568
620
def get_file_mtime(self, file_id, path=None):
570
path = self._inventory.id2path(file_id)
622
path = self.inventory.id2path(file_id)
571
623
return os.lstat(self.abspath(path)).st_mtime
625
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
626
file_id = self.path2id(path)
627
return self._inventory[file_id].executable
629
def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
630
mode = stat_result.st_mode
631
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
573
633
if not supports_executable():
574
634
def is_executable(self, file_id, path=None):
575
635
return self._inventory[file_id].executable
637
_is_executable_from_path_and_stat = \
638
_is_executable_from_path_and_stat_from_basis
577
640
def is_executable(self, file_id, path=None):
579
path = self._inventory.id2path(file_id)
642
path = self.id2path(file_id)
580
643
mode = os.lstat(self.abspath(path)).st_mode
581
644
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
584
def add(self, files, ids=None):
585
"""Make files versioned.
587
Note that the command line normally calls smart_add instead,
588
which can automatically recurse.
590
This adds the files to the inventory, so that they will be
591
recorded by the next commit.
594
List of paths to add, relative to the base of the tree.
597
If set, use these instead of automatically generated ids.
598
Must be the same length as the list of files, but may
599
contain None for ids that are to be autogenerated.
601
TODO: Perhaps have an option to add the ids even if the files do
604
TODO: Perhaps callback with the ids and paths as they're added.
646
_is_executable_from_path_and_stat = \
647
_is_executable_from_path_and_stat_from_stat
649
@needs_tree_write_lock
650
def _add(self, files, ids, kinds):
651
"""See MutableTree._add."""
606
652
# TODO: Re-adding a file that is removed in the working copy
607
653
# should probably put it back with the previous ID.
608
if isinstance(files, basestring):
609
assert(ids is None or isinstance(ids, basestring))
615
ids = [None] * len(files)
617
assert(len(ids) == len(files))
619
inv = self.read_working_inventory()
620
for f,file_id in zip(files, ids):
621
if self.is_control_filename(f):
622
raise errors.ForbiddenControlFileError(filename=f)
627
raise BzrError("cannot add top-level %r" % f)
629
fullpath = normpath(self.abspath(f))
631
kind = file_kind(fullpath)
633
if e.errno == errno.ENOENT:
634
raise NoSuchFile(fullpath)
635
if not InventoryEntry.versionable_kind(kind):
636
raise errors.BadFileKindError(filename=f, kind=kind)
654
# the read and write working inventory should not occur in this
655
# function - they should be part of lock_write and unlock.
657
for f, file_id, kind in zip(files, ids, kinds):
637
658
if file_id is None:
638
659
inv.add_path(f, kind=kind)
640
661
inv.add_path(f, kind=kind, file_id=file_id)
662
self._inventory_is_modified = True
642
self._write_inventory(inv)
664
@needs_tree_write_lock
665
def _gather_kinds(self, files, kinds):
666
"""See MutableTree._gather_kinds."""
667
for pos, f in enumerate(files):
668
if kinds[pos] is None:
669
fullpath = normpath(self.abspath(f))
671
kinds[pos] = file_kind(fullpath)
673
if e.errno == errno.ENOENT:
674
raise errors.NoSuchFile(fullpath)
644
676
@needs_write_lock
677
def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
678
"""Add revision_id as a parent.
680
This is equivalent to retrieving the current list of parent ids
681
and setting the list to its value plus revision_id.
683
:param revision_id: The revision id to add to the parent list. It may
684
be a ghost revision as long as its not the first parent to be added,
685
or the allow_leftmost_as_ghost parameter is set True.
686
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
688
parents = self.get_parent_ids() + [revision_id]
689
self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
690
or allow_leftmost_as_ghost)
692
@needs_tree_write_lock
693
def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
694
"""Add revision_id, tree tuple as a parent.
696
This is equivalent to retrieving the current list of parent trees
697
and setting the list to its value plus parent_tuple. See also
698
add_parent_tree_id - if you only have a parent id available it will be
699
simpler to use that api. If you have the parent already available, using
700
this api is preferred.
702
:param parent_tuple: The (revision id, tree) to add to the parent list.
703
If the revision_id is a ghost, pass None for the tree.
704
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
706
parent_ids = self.get_parent_ids() + [parent_tuple[0]]
707
if len(parent_ids) > 1:
708
# the leftmost may have already been a ghost, preserve that if it
710
allow_leftmost_as_ghost = True
711
self.set_parent_ids(parent_ids,
712
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
714
@needs_tree_write_lock
645
715
def add_pending_merge(self, *revision_ids):
646
716
# TODO: Perhaps should check at this point that the
647
717
# history of the revision is actually present?
648
p = self.pending_merges()
718
parents = self.get_parent_ids()
650
720
for rev_id in revision_ids:
721
if rev_id in parents:
723
parents.append(rev_id)
656
self.set_pending_merges(p)
659
def pending_merges(self):
660
"""Return a list of pending merges.
662
These are revisions that have been merged into the working
663
directory but not yet committed.
726
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
728
def path_content_summary(self, path, _lstat=os.lstat,
729
_mapper=osutils.file_kind_from_stat_mode):
730
"""See Tree.path_content_summary."""
731
abspath = self.abspath(path)
666
merges_file = self._control_files.get_utf8('pending-merges')
670
for l in merges_file.readlines():
671
p.append(l.rstrip('\n'))
733
stat_result = _lstat(abspath)
735
if getattr(e, 'errno', None) == errno.ENOENT:
737
return ('missing', None, None, None)
738
# propagate other errors
740
kind = _mapper(stat_result.st_mode)
742
size = stat_result.st_size
743
# try for a stat cache lookup
744
executable = self._is_executable_from_path_and_stat(path, stat_result)
745
return (kind, size, executable, self._sha_from_stat(
747
elif kind == 'directory':
748
# perhaps it looks like a plain directory, but it's really a
750
if self._directory_is_tree_reference(path):
751
kind = 'tree-reference'
752
return kind, None, None, None
753
elif kind == 'symlink':
754
target = osutils.readlink(abspath)
755
return ('symlink', None, None, target)
757
return (kind, None, None, None)
759
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
760
"""Common ghost checking functionality from set_parent_*.
762
This checks that the left hand-parent exists if there are any
765
if len(revision_ids) > 0:
766
leftmost_id = revision_ids[0]
767
if (not allow_leftmost_as_ghost and not
768
self.branch.repository.has_revision(leftmost_id)):
769
raise errors.GhostRevisionUnusableHere(leftmost_id)
771
def _set_merges_from_parent_ids(self, parent_ids):
772
merges = parent_ids[1:]
773
self._transport.put_bytes('pending-merges', '\n'.join(merges),
774
mode=self.bzrdir._get_file_mode())
776
def _filter_parent_ids_by_ancestry(self, revision_ids):
777
"""Check that all merged revisions are proper 'heads'.
779
This will always return the first revision_id, and any merged revisions
782
if len(revision_ids) == 0:
784
graph = self.branch.repository.get_graph()
785
heads = graph.heads(revision_ids)
786
new_revision_ids = revision_ids[:1]
787
for revision_id in revision_ids[1:]:
788
if revision_id in heads and revision_id not in new_revision_ids:
789
new_revision_ids.append(revision_id)
790
if new_revision_ids != revision_ids:
791
trace.mutter('requested to set revision_ids = %s,'
792
' but filtered to %s', revision_ids, new_revision_ids)
793
return new_revision_ids
795
@needs_tree_write_lock
796
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
797
"""Set the parent ids to revision_ids.
799
See also set_parent_trees. This api will try to retrieve the tree data
800
for each element of revision_ids from the trees repository. If you have
801
tree data already available, it is more efficient to use
802
set_parent_trees rather than set_parent_ids. set_parent_ids is however
803
an easier API to use.
805
:param revision_ids: The revision_ids to set as the parent ids of this
806
working tree. Any of these may be ghosts.
808
self._check_parents_for_ghosts(revision_ids,
809
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
810
for revision_id in revision_ids:
811
_mod_revision.check_not_reserved_id(revision_id)
813
revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
815
if len(revision_ids) > 0:
816
self.set_last_revision(revision_ids[0])
818
self.set_last_revision(_mod_revision.NULL_REVISION)
820
self._set_merges_from_parent_ids(revision_ids)
822
@needs_tree_write_lock
823
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
824
"""See MutableTree.set_parent_trees."""
825
parent_ids = [rev for (rev, tree) in parents_list]
826
for revision_id in parent_ids:
827
_mod_revision.check_not_reserved_id(revision_id)
829
self._check_parents_for_ghosts(parent_ids,
830
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
832
parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
834
if len(parent_ids) == 0:
835
leftmost_parent_id = _mod_revision.NULL_REVISION
836
leftmost_parent_tree = None
838
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
840
if self._change_last_revision(leftmost_parent_id):
841
if leftmost_parent_tree is None:
842
# If we don't have a tree, fall back to reading the
843
# parent tree from the repository.
844
self._cache_basis_inventory(leftmost_parent_id)
846
inv = leftmost_parent_tree.inventory
847
xml = self._create_basis_xml_from_inventory(
848
leftmost_parent_id, inv)
849
self._write_basis_inventory(xml)
850
self._set_merges_from_parent_ids(parent_ids)
852
@needs_tree_write_lock
675
853
def set_pending_merges(self, rev_list):
676
self._control_files.put_utf8('pending-merges', '\n'.join(rev_list))
854
parents = self.get_parent_ids()
855
leftmost = parents[:1]
856
new_parents = leftmost + rev_list
857
self.set_parent_ids(new_parents)
859
@needs_tree_write_lock
679
860
def set_merge_modified(self, modified_hashes):
680
861
def iter_stanzas():
681
862
for file_id, hash in modified_hashes.iteritems():
682
yield Stanza(file_id=file_id, hash=hash)
863
yield Stanza(file_id=file_id.decode('utf8'), hash=hash)
683
864
self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
866
def _sha_from_stat(self, path, stat_result):
867
"""Get a sha digest from the tree's stat cache.
869
The default implementation assumes no stat cache is present.
871
:param path: The path.
872
:param stat_result: The stat result being looked up.
686
876
def _put_rio(self, filename, stanzas, header):
877
self._must_be_locked()
687
878
my_file = rio_file(stanzas, header)
688
self._control_files.put(filename, my_file)
879
self._transport.put_file(filename, my_file,
880
mode=self.bzrdir._get_file_mode())
882
@needs_write_lock # because merge pulls data into the branch.
883
def merge_from_branch(self, branch, to_revision=None, from_revision=None,
885
"""Merge from a branch into this working tree.
887
:param branch: The branch to merge from.
888
:param to_revision: If non-None, the merge will merge to to_revision,
889
but not beyond it. to_revision does not need to be in the history
890
of the branch when it is supplied. If None, to_revision defaults to
891
branch.last_revision().
893
from bzrlib.merge import Merger, Merge3Merger
894
pb = ui.ui_factory.nested_progress_bar()
896
merger = Merger(self.branch, this_tree=self, pb=pb)
897
merger.pp = ProgressPhase("Merge phase", 5, pb)
898
merger.pp.next_phase()
899
# check that there are no
901
merger.check_basis(check_clean=True, require_commits=False)
902
if to_revision is None:
903
to_revision = _mod_revision.ensure_null(branch.last_revision())
904
merger.other_rev_id = to_revision
905
if _mod_revision.is_null(merger.other_rev_id):
906
raise errors.NoCommits(branch)
907
self.branch.fetch(branch, last_revision=merger.other_rev_id)
908
merger.other_basis = merger.other_rev_id
909
merger.other_tree = self.branch.repository.revision_tree(
911
merger.other_branch = branch
912
merger.pp.next_phase()
913
if from_revision is None:
916
merger.set_base_revision(from_revision, branch)
917
if merger.base_rev_id == merger.other_rev_id:
918
raise errors.PointlessMerge
919
merger.backup_files = False
920
if merge_type is None:
921
merger.merge_type = Merge3Merger
923
merger.merge_type = merge_type
924
merger.set_interesting_files(None)
925
merger.show_base = False
926
merger.reprocess = False
927
conflicts = merger.do_merge()
691
934
def merge_modified(self):
935
"""Return a dictionary of files modified by a merge.
937
The list is initialized by WorkingTree.set_merge_modified, which is
938
typically called after we make some automatic updates to the tree
941
This returns a map of file_id->sha1, containing only files which are
942
still in the working inventory and have that text hash.
693
hashfile = self._control_files.get('merge-hashes')
945
hashfile = self._transport.get('merge-hashes')
946
except errors.NoSuchFile:
698
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
699
raise MergeModifiedFormatError()
700
except StopIteration:
701
raise MergeModifiedFormatError()
702
for s in RioReader(hashfile):
703
file_id = s.get("file_id")
704
if file_id not in self.inventory:
707
if hash == self.get_file_sha1(file_id):
708
merge_hashes[file_id] = hash
951
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
952
raise errors.MergeModifiedFormatError()
953
except StopIteration:
954
raise errors.MergeModifiedFormatError()
955
for s in RioReader(hashfile):
956
# RioReader reads in Unicode, so convert file_ids back to utf8
957
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
958
if file_id not in self.inventory:
960
text_hash = s.get("hash")
961
if text_hash == self.get_file_sha1(file_id):
962
merge_hashes[file_id] = text_hash
968
def mkdir(self, path, file_id=None):
969
"""See MutableTree.mkdir()."""
971
file_id = generate_ids.gen_file_id(os.path.basename(path))
972
os.mkdir(self.abspath(path))
973
self.add(path, file_id, 'directory')
711
976
def get_symlink_target(self, file_id):
712
return os.readlink(self.id2abspath(file_id))
714
def file_class(self, filename):
715
if self.path2id(filename):
717
elif self.is_ignored(filename):
722
def list_files(self):
723
"""Recursively list all files as (path, class, kind, id, entry).
977
abspath = self.id2abspath(file_id)
978
target = osutils.readlink(abspath)
982
def subsume(self, other_tree):
983
def add_children(inventory, entry):
984
for child_entry in entry.children.values():
985
inventory._byid[child_entry.file_id] = child_entry
986
if child_entry.kind == 'directory':
987
add_children(inventory, child_entry)
988
if other_tree.get_root_id() == self.get_root_id():
989
raise errors.BadSubsumeSource(self, other_tree,
990
'Trees have the same root')
992
other_tree_path = self.relpath(other_tree.basedir)
993
except errors.PathNotChild:
994
raise errors.BadSubsumeSource(self, other_tree,
995
'Tree is not contained by the other')
996
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
997
if new_root_parent is None:
998
raise errors.BadSubsumeSource(self, other_tree,
999
'Parent directory is not versioned.')
1000
# We need to ensure that the result of a fetch will have a
1001
# versionedfile for the other_tree root, and only fetching into
1002
# RepositoryKnit2 guarantees that.
1003
if not self.branch.repository.supports_rich_root():
1004
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
1005
other_tree.lock_tree_write()
1007
new_parents = other_tree.get_parent_ids()
1008
other_root = other_tree.inventory.root
1009
other_root.parent_id = new_root_parent
1010
other_root.name = osutils.basename(other_tree_path)
1011
self.inventory.add(other_root)
1012
add_children(self.inventory, other_root)
1013
self._write_inventory(self.inventory)
1014
# normally we don't want to fetch whole repositories, but i think
1015
# here we really do want to consolidate the whole thing.
1016
for parent_id in other_tree.get_parent_ids():
1017
self.branch.fetch(other_tree.branch, parent_id)
1018
self.add_parent_tree_id(parent_id)
1021
other_tree.bzrdir.retire_bzrdir()
1023
def _setup_directory_is_tree_reference(self):
1024
if self._branch.repository._format.supports_tree_reference:
1025
self._directory_is_tree_reference = \
1026
self._directory_may_be_tree_reference
1028
self._directory_is_tree_reference = \
1029
self._directory_is_never_tree_reference
1031
def _directory_is_never_tree_reference(self, relpath):
1034
def _directory_may_be_tree_reference(self, relpath):
1035
# as a special case, if a directory contains control files then
1036
# it's a tree reference, except that the root of the tree is not
1037
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
1038
# TODO: We could ask all the control formats whether they
1039
# recognize this directory, but at the moment there's no cheap api
1040
# to do that. Since we probably can only nest bzr checkouts and
1041
# they always use this name it's ok for now. -- mbp 20060306
1043
# FIXME: There is an unhandled case here of a subdirectory
1044
# containing .bzr but not a branch; that will probably blow up
1045
# when you try to commit it. It might happen if there is a
1046
# checkout in a subdirectory. This can be avoided by not adding
1049
@needs_tree_write_lock
1050
def extract(self, file_id, format=None):
1051
"""Extract a subtree from this tree.
1053
A new branch will be created, relative to the path for this tree.
1057
segments = osutils.splitpath(path)
1058
transport = self.branch.bzrdir.root_transport
1059
for name in segments:
1060
transport = transport.clone(name)
1061
transport.ensure_base()
1064
sub_path = self.id2path(file_id)
1065
branch_transport = mkdirs(sub_path)
1067
format = self.bzrdir.cloning_metadir()
1068
branch_transport.ensure_base()
1069
branch_bzrdir = format.initialize_on_transport(branch_transport)
1071
repo = branch_bzrdir.find_repository()
1072
except errors.NoRepositoryPresent:
1073
repo = branch_bzrdir.create_repository()
1074
if not repo.supports_rich_root():
1075
raise errors.RootNotRich()
1076
new_branch = branch_bzrdir.create_branch()
1077
new_branch.pull(self.branch)
1078
for parent_id in self.get_parent_ids():
1079
new_branch.fetch(self.branch, parent_id)
1080
tree_transport = self.bzrdir.root_transport.clone(sub_path)
1081
if tree_transport.base != branch_transport.base:
1082
tree_bzrdir = format.initialize_on_transport(tree_transport)
1083
branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
1085
tree_bzrdir = branch_bzrdir
1086
wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
1087
wt.set_parent_ids(self.get_parent_ids())
1088
my_inv = self.inventory
1089
child_inv = inventory.Inventory(root_id=None)
1090
new_root = my_inv[file_id]
1091
my_inv.remove_recursive_id(file_id)
1092
new_root.parent_id = None
1093
child_inv.add(new_root)
1094
self._write_inventory(my_inv)
1095
wt._write_inventory(child_inv)
1098
def _serialize(self, inventory, out_file):
1099
xml5.serializer_v5.write_inventory(self._inventory, out_file,
1102
def _deserialize(selt, in_file):
1103
return xml5.serializer_v5.read_inventory(in_file)
1106
"""Write the in memory inventory to disk."""
1107
# TODO: Maybe this should only write on dirty ?
1108
if self._control_files._lock_mode != 'w':
1109
raise errors.NotWriteLocked(self)
1111
self._serialize(self._inventory, sio)
1113
self._transport.put_file('inventory', sio,
1114
mode=self.bzrdir._get_file_mode())
1115
self._inventory_is_modified = False
1117
def _kind(self, relpath):
1118
return osutils.file_kind(self.abspath(relpath))
1120
def list_files(self, include_root=False, from_dir=None, recursive=True):
1121
"""List all files as (path, class, kind, id, entry).
725
1123
Lists, but does not descend into unversioned directories.
727
1124
This does not include files that have been deleted in this
1125
tree. Skips the control directory.
730
Skips the control directory.
1127
:param include_root: if True, do not return an entry for the root
1128
:param from_dir: start from this directory or None for the root
1129
:param recursive: whether to recurse into subdirectories or not
732
inv = self._inventory
1131
# list_files is an iterator, so @needs_read_lock doesn't work properly
1132
# with it. So callers should be careful to always read_lock the tree.
1133
if not self.is_locked():
1134
raise errors.ObjectNotLocked(self)
1136
inv = self.inventory
1137
if from_dir is None and include_root is True:
1138
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
733
1139
# Convert these into local objects to save lookup times
734
1140
pathjoin = osutils.pathjoin
735
file_kind = osutils.file_kind
1141
file_kind = self._kind
737
1143
# transport.base ends in a slash, we want the piece
738
1144
# between the last two slashes
813
1222
except KeyError:
814
1223
yield fp[1:], c, fk, None, TreeEntry()
817
1226
if fk != 'directory':
820
# But do this child first
821
new_children = os.listdir(fap)
823
new_children = collections.deque(new_children)
824
stack.append((f_ie.file_id, fp, fap, new_children))
825
# Break out of inner loop, so that we start outer loop with child
1229
# But do this child first if recursing down
1231
new_children = os.listdir(fap)
1233
new_children = collections.deque(new_children)
1234
stack.append((f_ie.file_id, fp, fap, new_children))
1235
# Break out of inner loop,
1236
# so that we start outer loop with child
828
1239
# if we finished all children, pop it off the stack
833
def move(self, from_paths, to_name):
1242
@needs_tree_write_lock
1243
def move(self, from_paths, to_dir=None, after=False, **kwargs):
834
1244
"""Rename files.
836
to_name must exist in the inventory.
838
If to_name exists and is a directory, the files are moved into
839
it, keeping their old names.
841
Note that to_name is only the last component of the new name;
1246
to_dir must exist in the inventory.
1248
If to_dir exists and is a directory, the files are moved into
1249
it, keeping their old names.
1251
Note that to_dir is only the last component of the new name;
842
1252
this doesn't change the directory.
1254
For each entry in from_paths the move mode will be determined
1257
The first mode moves the file in the filesystem and updates the
1258
inventory. The second mode only updates the inventory without
1259
touching the file on the filesystem. This is the new mode introduced
1262
move uses the second mode if 'after == True' and the target is not
1263
versioned but present in the working tree.
1265
move uses the second mode if 'after == False' and the source is
1266
versioned but no longer in the working tree, and the target is not
1267
versioned but present in the working tree.
1269
move uses the first mode if 'after == False' and the source is
1270
versioned and present in the working tree, and the target is not
1271
versioned and not present in the working tree.
1273
Everything else results in an error.
844
1275
This returns a list of (from_path, to_path) pairs for each
845
1276
entry that is moved.
848
## TODO: Option to move IDs only
849
assert not isinstance(from_paths, basestring)
1281
# check for deprecated use of signature
1283
to_dir = kwargs.get('to_name', None)
1285
raise TypeError('You must supply a target directory')
1287
symbol_versioning.warn('The parameter to_name was deprecated'
1288
' in version 0.13. Use to_dir instead',
1291
# check destination directory
1292
if isinstance(from_paths, basestring):
850
1294
inv = self.inventory
851
to_abs = self.abspath(to_name)
1295
to_abs = self.abspath(to_dir)
852
1296
if not isdir(to_abs):
853
raise BzrError("destination %r is not a directory" % to_abs)
854
if not self.has_filename(to_name):
855
raise BzrError("destination %r not in working directory" % to_abs)
856
to_dir_id = inv.path2id(to_name)
857
if to_dir_id == None and to_name != '':
858
raise BzrError("destination %r is not a versioned directory" % to_name)
1297
raise errors.BzrMoveFailedError('',to_dir,
1298
errors.NotADirectory(to_abs))
1299
if not self.has_filename(to_dir):
1300
raise errors.BzrMoveFailedError('',to_dir,
1301
errors.NotInWorkingDirectory(to_dir))
1302
to_dir_id = inv.path2id(to_dir)
1303
if to_dir_id is None:
1304
raise errors.BzrMoveFailedError('',to_dir,
1305
errors.NotVersionedError(path=str(to_dir)))
859
1307
to_dir_ie = inv[to_dir_id]
860
1308
if to_dir_ie.kind != 'directory':
861
raise BzrError("destination %r is not a directory" % to_abs)
863
to_idpath = inv.get_idpath(to_dir_id)
866
if not self.has_filename(f):
867
raise BzrError("%r does not exist in working tree" % f)
868
f_id = inv.path2id(f)
870
raise BzrError("%r is not versioned" % f)
871
name_tail = splitpath(f)[-1]
872
dest_path = pathjoin(to_name, name_tail)
873
if self.has_filename(dest_path):
874
raise BzrError("destination %r already exists" % dest_path)
875
if f_id in to_idpath:
876
raise BzrError("can't move %r to a subdirectory of itself" % f)
878
# OK, so there's a race here, it's possible that someone will
879
# create a file in this interval and then the rename might be
880
# left half-done. But we should have caught most problems.
881
orig_inv = deepcopy(self.inventory)
1309
raise errors.BzrMoveFailedError('',to_dir,
1310
errors.NotADirectory(to_abs))
1312
# create rename entries and tuples
1313
for from_rel in from_paths:
1314
from_tail = splitpath(from_rel)[-1]
1315
from_id = inv.path2id(from_rel)
1317
raise errors.BzrMoveFailedError(from_rel,to_dir,
1318
errors.NotVersionedError(path=str(from_rel)))
1320
from_entry = inv[from_id]
1321
from_parent_id = from_entry.parent_id
1322
to_rel = pathjoin(to_dir, from_tail)
1323
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1325
from_tail=from_tail,
1326
from_parent_id=from_parent_id,
1327
to_rel=to_rel, to_tail=from_tail,
1328
to_parent_id=to_dir_id)
1329
rename_entries.append(rename_entry)
1330
rename_tuples.append((from_rel, to_rel))
1332
# determine which move mode to use. checks also for movability
1333
rename_entries = self._determine_mv_mode(rename_entries, after)
1335
original_modified = self._inventory_is_modified
884
name_tail = splitpath(f)[-1]
885
dest_path = pathjoin(to_name, name_tail)
886
result.append((f, dest_path))
887
inv.rename(inv.path2id(f), to_dir_id, name_tail)
889
rename(self.abspath(f), self.abspath(dest_path))
891
raise BzrError("failed to rename %r to %r: %s" %
892
(f, dest_path, e[1]),
893
["rename rolled back"])
1338
self._inventory_is_modified = True
1339
self._move(rename_entries)
895
1341
# restore the inventory on error
896
self._set_inventory(orig_inv)
1342
self._inventory_is_modified = original_modified
898
1344
self._write_inventory(inv)
902
def rename_one(self, from_rel, to_rel):
1345
return rename_tuples
1347
def _determine_mv_mode(self, rename_entries, after=False):
1348
"""Determines for each from-to pair if both inventory and working tree
1349
or only the inventory has to be changed.
1351
Also does basic plausability tests.
1353
inv = self.inventory
1355
for rename_entry in rename_entries:
1356
# store to local variables for easier reference
1357
from_rel = rename_entry.from_rel
1358
from_id = rename_entry.from_id
1359
to_rel = rename_entry.to_rel
1360
to_id = inv.path2id(to_rel)
1361
only_change_inv = False
1363
# check the inventory for source and destination
1365
raise errors.BzrMoveFailedError(from_rel,to_rel,
1366
errors.NotVersionedError(path=str(from_rel)))
1367
if to_id is not None:
1368
raise errors.BzrMoveFailedError(from_rel,to_rel,
1369
errors.AlreadyVersionedError(path=str(to_rel)))
1371
# try to determine the mode for rename (only change inv or change
1372
# inv and file system)
1374
if not self.has_filename(to_rel):
1375
raise errors.BzrMoveFailedError(from_id,to_rel,
1376
errors.NoSuchFile(path=str(to_rel),
1377
extra="New file has not been created yet"))
1378
only_change_inv = True
1379
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
1380
only_change_inv = True
1381
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1382
only_change_inv = False
1383
elif (not self.case_sensitive
1384
and from_rel.lower() == to_rel.lower()
1385
and self.has_filename(from_rel)):
1386
only_change_inv = False
1388
# something is wrong, so lets determine what exactly
1389
if not self.has_filename(from_rel) and \
1390
not self.has_filename(to_rel):
1391
raise errors.BzrRenameFailedError(from_rel,to_rel,
1392
errors.PathsDoNotExist(paths=(str(from_rel),
1395
raise errors.RenameFailedFilesExist(from_rel, to_rel)
1396
rename_entry.only_change_inv = only_change_inv
1397
return rename_entries
1399
def _move(self, rename_entries):
1400
"""Moves a list of files.
1402
Depending on the value of the flag 'only_change_inv', the
1403
file will be moved on the file system or not.
1405
inv = self.inventory
1408
for entry in rename_entries:
1410
self._move_entry(entry)
1412
self._rollback_move(moved)
1416
def _rollback_move(self, moved):
1417
"""Try to rollback a previous move in case of an filesystem error."""
1418
inv = self.inventory
1421
self._move_entry(WorkingTree._RenameEntry(
1422
entry.to_rel, entry.from_id,
1423
entry.to_tail, entry.to_parent_id, entry.from_rel,
1424
entry.from_tail, entry.from_parent_id,
1425
entry.only_change_inv))
1426
except errors.BzrMoveFailedError, e:
1427
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
1428
" The working tree is in an inconsistent state."
1429
" Please consider doing a 'bzr revert'."
1430
" Error message is: %s" % e)
1432
def _move_entry(self, entry):
1433
inv = self.inventory
1434
from_rel_abs = self.abspath(entry.from_rel)
1435
to_rel_abs = self.abspath(entry.to_rel)
1436
if from_rel_abs == to_rel_abs:
1437
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
1438
"Source and target are identical.")
1440
if not entry.only_change_inv:
1442
osutils.rename(from_rel_abs, to_rel_abs)
1444
raise errors.BzrMoveFailedError(entry.from_rel,
1446
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
1448
@needs_tree_write_lock
1449
def rename_one(self, from_rel, to_rel, after=False):
903
1450
"""Rename one file.
905
1452
This can change the directory or the filename or both.
1454
rename_one has several 'modes' to work. First, it can rename a physical
1455
file and change the file_id. That is the normal mode. Second, it can
1456
only change the file_id without touching any physical file. This is
1457
the new mode introduced in version 0.15.
1459
rename_one uses the second mode if 'after == True' and 'to_rel' is not
1460
versioned but present in the working tree.
1462
rename_one uses the second mode if 'after == False' and 'from_rel' is
1463
versioned but no longer in the working tree, and 'to_rel' is not
1464
versioned but present in the working tree.
1466
rename_one uses the first mode if 'after == False' and 'from_rel' is
1467
versioned and present in the working tree, and 'to_rel' is not
1468
versioned and not present in the working tree.
1470
Everything else results in an error.
907
1472
inv = self.inventory
908
if not self.has_filename(from_rel):
909
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
910
if self.has_filename(to_rel):
911
raise BzrError("can't rename: new working file %r already exists" % to_rel)
913
file_id = inv.path2id(from_rel)
915
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
918
from_parent = entry.parent_id
919
from_name = entry.name
921
if inv.path2id(to_rel):
922
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1475
# create rename entries and tuples
1476
from_tail = splitpath(from_rel)[-1]
1477
from_id = inv.path2id(from_rel)
1479
# if file is missing in the inventory maybe it's in the basis_tree
1480
basis_tree = self.branch.basis_tree()
1481
from_id = basis_tree.path2id(from_rel)
1483
raise errors.BzrRenameFailedError(from_rel,to_rel,
1484
errors.NotVersionedError(path=str(from_rel)))
1485
# put entry back in the inventory so we can rename it
1486
from_entry = basis_tree.inventory[from_id].copy()
1489
from_entry = inv[from_id]
1490
from_parent_id = from_entry.parent_id
924
1491
to_dir, to_tail = os.path.split(to_rel)
925
1492
to_dir_id = inv.path2id(to_dir)
926
if to_dir_id == None and to_dir != '':
927
raise BzrError("can't determine destination directory id for %r" % to_dir)
929
mutter("rename_one:")
930
mutter(" file_id {%s}" % file_id)
931
mutter(" from_rel %r" % from_rel)
932
mutter(" to_rel %r" % to_rel)
933
mutter(" to_dir %r" % to_dir)
934
mutter(" to_dir_id {%s}" % to_dir_id)
936
inv.rename(file_id, to_dir_id, to_tail)
938
from_abs = self.abspath(from_rel)
939
to_abs = self.abspath(to_rel)
941
rename(from_abs, to_abs)
943
inv.rename(file_id, from_parent, from_name)
944
raise BzrError("failed to rename %r to %r: %s"
945
% (from_abs, to_abs, e[1]),
946
["rename rolled back"])
1493
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1495
from_tail=from_tail,
1496
from_parent_id=from_parent_id,
1497
to_rel=to_rel, to_tail=to_tail,
1498
to_parent_id=to_dir_id)
1499
rename_entries.append(rename_entry)
1501
# determine which move mode to use. checks also for movability
1502
rename_entries = self._determine_mv_mode(rename_entries, after)
1504
# check if the target changed directory and if the target directory is
1506
if to_dir_id is None:
1507
raise errors.BzrMoveFailedError(from_rel,to_rel,
1508
errors.NotVersionedError(path=str(to_dir)))
1510
# all checks done. now we can continue with our actual work
1511
mutter('rename_one:\n'
1516
' to_dir_id {%s}\n',
1517
from_id, from_rel, to_rel, to_dir, to_dir_id)
1519
self._move(rename_entries)
947
1520
self._write_inventory(inv)
1522
class _RenameEntry(object):
1523
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
1524
to_rel, to_tail, to_parent_id, only_change_inv=False):
1525
self.from_rel = from_rel
1526
self.from_id = from_id
1527
self.from_tail = from_tail
1528
self.from_parent_id = from_parent_id
1529
self.to_rel = to_rel
1530
self.to_tail = to_tail
1531
self.to_parent_id = to_parent_id
1532
self.only_change_inv = only_change_inv
949
1534
@needs_read_lock
950
1535
def unknowns(self):
951
1536
"""Return all unknown files.
1231
1851
self.branch.set_revision_history([new_revision])
1854
def _write_basis_inventory(self, xml):
1855
"""Write the basis inventory XML to the basis-inventory file"""
1856
path = self._basis_inventory_name()
1858
self._transport.put_file(path, sio,
1859
mode=self.bzrdir._get_file_mode())
1861
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1862
"""Create the text that will be saved in basis-inventory"""
1863
inventory.revision_id = revision_id
1864
return xml7.serializer_v7.write_inventory_to_string(inventory)
1234
1866
def _cache_basis_inventory(self, new_revision):
1235
1867
"""Cache new_revision as the basis inventory."""
1236
1868
# TODO: this should allow the ready-to-use inventory to be passed in,
1237
1869
# as commit already has that ready-to-use [while the format is the
1238
1870
# same, that is].
1240
# this double handles the inventory - unpack and repack -
1872
# this double handles the inventory - unpack and repack -
1241
1873
# but is easier to understand. We can/should put a conditional
1242
1874
# in here based on whether the inventory is in the latest format
1243
1875
# - perhaps we should repack all inventories on a repository
1245
1877
# the fast path is to copy the raw xml from the repository. If the
1246
# xml contains 'revision_id="', then we assume the right
1878
# xml contains 'revision_id="', then we assume the right
1247
1879
# revision_id is set. We must check for this full string, because a
1248
1880
# root node id can legitimately look like 'revision_id' but cannot
1249
1881
# contain a '"'.
1250
1882
xml = self.branch.repository.get_inventory_xml(new_revision)
1251
if not 'revision_id="' in xml.split('\n', 1)[0]:
1883
firstline = xml.split('\n', 1)[0]
1884
if (not 'revision_id="' in firstline or
1885
'format="7"' not in firstline):
1252
1886
inv = self.branch.repository.deserialise_inventory(
1253
1887
new_revision, xml)
1254
inv.revision_id = new_revision
1255
xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1256
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1257
path = self._basis_inventory_name()
1259
self._control_files.put(path, sio)
1888
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1889
self._write_basis_inventory(xml)
1260
1890
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1263
1893
def read_basis_inventory(self):
1264
1894
"""Read the cached basis inventory."""
1265
1895
path = self._basis_inventory_name()
1266
return self._control_files.get(path).read()
1896
return self._transport.get_bytes(path)
1268
1898
@needs_read_lock
1269
1899
def read_working_inventory(self):
1270
"""Read the working inventory."""
1900
"""Read the working inventory.
1902
:raises errors.InventoryModified: read_working_inventory will fail
1903
when the current in memory inventory has been modified.
1905
# conceptually this should be an implementation detail of the tree.
1906
# XXX: Deprecate this.
1271
1907
# ElementTree does its own conversion from UTF-8, so open in
1273
result = bzrlib.xml5.serializer_v5.read_inventory(
1274
self._control_files.get('inventory'))
1275
self._set_inventory(result)
1909
if self._inventory_is_modified:
1910
raise errors.InventoryModified(self)
1911
result = self._deserialize(self._transport.get('inventory'))
1912
self._set_inventory(result, dirty=False)
1279
def remove(self, files, verbose=False, to_file=None):
1280
"""Remove nominated files from the working inventory..
1282
This does not remove their text. This does not run on XXX on what? RBC
1284
TODO: Refuse to remove modified files unless --force is given?
1286
TODO: Do something useful with directories.
1288
TODO: Should this remove the text or not? Tough call; not
1289
removing may be useful and the user can just use use rm, and
1290
is the opposite of add. Removing it is consistent with most
1291
other tools. Maybe an option.
1915
@needs_tree_write_lock
1916
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1918
"""Remove nominated files from the working inventory.
1920
:files: File paths relative to the basedir.
1921
:keep_files: If true, the files will also be kept.
1922
:force: Delete files and directories, even if they are changed and
1923
even if the directories are not empty.
1293
## TODO: Normalize names
1294
## TODO: Remove nested loops; better scalability
1295
1925
if isinstance(files, basestring):
1296
1926
files = [files]
1298
inv = self.inventory
1300
# do this before any modifications
1931
unknown_nested_files=set()
1933
def recurse_directory_to_add_files(directory):
1934
# Recurse directory and add all files
1935
# so we can check if they have changed.
1936
for parent_info, file_infos in\
1937
self.walkdirs(directory):
1938
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1939
# Is it versioned or ignored?
1940
if self.path2id(relpath) or self.is_ignored(relpath):
1941
# Add nested content for deletion.
1942
new_files.add(relpath)
1944
# Files which are not versioned and not ignored
1945
# should be treated as unknown.
1946
unknown_nested_files.add((relpath, None, kind))
1948
for filename in files:
1949
# Get file name into canonical form.
1950
abspath = self.abspath(filename)
1951
filename = self.relpath(abspath)
1952
if len(filename) > 0:
1953
new_files.add(filename)
1954
recurse_directory_to_add_files(filename)
1956
files = list(new_files)
1959
return # nothing to do
1961
# Sort needed to first handle directory content before the directory
1962
files.sort(reverse=True)
1964
# Bail out if we are going to delete files we shouldn't
1965
if not keep_files and not force:
1966
has_changed_files = len(unknown_nested_files) > 0
1967
if not has_changed_files:
1968
for (file_id, path, content_change, versioned, parent_id, name,
1969
kind, executable) in self.iter_changes(self.basis_tree(),
1970
include_unchanged=True, require_versioned=False,
1971
want_unversioned=True, specific_files=files):
1972
if versioned == (False, False):
1973
# The record is unknown ...
1974
if not self.is_ignored(path[1]):
1975
# ... but not ignored
1976
has_changed_files = True
1978
elif content_change and (kind[1] is not None):
1979
# Versioned and changed, but not deleted
1980
has_changed_files = True
1983
if has_changed_files:
1984
# Make delta show ALL applicable changes in error message.
1985
tree_delta = self.changes_from(self.basis_tree(),
1986
require_versioned=False, want_unversioned=True,
1987
specific_files=files)
1988
for unknown_file in unknown_nested_files:
1989
if unknown_file not in tree_delta.unversioned:
1990
tree_delta.unversioned.extend((unknown_file,))
1991
raise errors.BzrRemoveChangedFilesError(tree_delta)
1993
# Build inv_delta and delete files where applicable,
1994
# do this before any modifications to inventory.
1301
1995
for f in files:
1302
fid = inv.path2id(f)
1996
fid = self.path2id(f)
1304
# TODO: Perhaps make this just a warning, and continue?
1305
# This tends to happen when
1306
raise NotVersionedError(path=f)
1308
# having remove it, it must be either ignored or unknown
1309
if self.is_ignored(f):
1313
show_status(new_status, inv[fid].kind, f, to_file=to_file)
1316
self._write_inventory(inv)
1319
def revert(self, filenames, old_tree=None, backups=True,
1320
pb=DummyProgress()):
1321
from transform import revert
1322
from conflicts import resolve
1999
message = "%s is not versioned." % (f,)
2002
# having removed it, it must be either ignored or unknown
2003
if self.is_ignored(f):
2007
textui.show_status(new_status, self.kind(fid), f,
2010
inv_delta.append((f, None, fid, None))
2011
message = "removed %s" % (f,)
2014
abs_path = self.abspath(f)
2015
if osutils.lexists(abs_path):
2016
if (osutils.isdir(abs_path) and
2017
len(os.listdir(abs_path)) > 0):
2019
osutils.rmtree(abs_path)
2021
message = "%s is not an empty directory "\
2022
"and won't be deleted." % (f,)
2024
osutils.delete_any(abs_path)
2025
message = "deleted %s" % (f,)
2026
elif message is not None:
2027
# Only care if we haven't done anything yet.
2028
message = "%s does not exist." % (f,)
2030
# Print only one message (if any) per file.
2031
if message is not None:
2033
self.apply_inventory_delta(inv_delta)
2035
@needs_tree_write_lock
2036
def revert(self, filenames=None, old_tree=None, backups=True,
2037
pb=DummyProgress(), report_changes=False):
2038
from bzrlib.conflicts import resolve
2041
symbol_versioning.warn('Using [] to revert all files is deprecated'
2042
' as of bzr 0.91. Please use None (the default) instead.',
2043
DeprecationWarning, stacklevel=2)
1323
2044
if old_tree is None:
1324
old_tree = self.basis_tree()
1325
conflicts = revert(self, old_tree, filenames, backups, pb)
1326
if not len(filenames):
1327
self.set_pending_merges([])
2045
basis_tree = self.basis_tree()
2046
basis_tree.lock_read()
2047
old_tree = basis_tree
1330
resolve(self, filenames, ignore_misses=True)
2051
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
2053
if filenames is None and len(self.get_parent_ids()) > 1:
2055
last_revision = self.last_revision()
2056
if last_revision != _mod_revision.NULL_REVISION:
2057
if basis_tree is None:
2058
basis_tree = self.basis_tree()
2059
basis_tree.lock_read()
2060
parent_trees.append((last_revision, basis_tree))
2061
self.set_parent_trees(parent_trees)
2064
resolve(self, filenames, ignore_misses=True, recursive=True)
2066
if basis_tree is not None:
1331
2068
return conflicts
2070
def revision_tree(self, revision_id):
2071
"""See Tree.revision_tree.
2073
WorkingTree can supply revision_trees for the basis revision only
2074
because there is only one cached inventory in the bzr directory.
2076
if revision_id == self.last_revision():
2078
xml = self.read_basis_inventory()
2079
except errors.NoSuchFile:
2083
inv = xml7.serializer_v7.read_inventory_from_string(xml)
2084
# dont use the repository revision_tree api because we want
2085
# to supply the inventory.
2086
if inv.revision_id == revision_id:
2087
return revisiontree.RevisionTree(self.branch.repository,
2089
except errors.BadInventoryFormat:
2091
# raise if there was no inventory, or if we read the wrong inventory.
2092
raise errors.NoSuchRevisionInTree(self, revision_id)
1333
2094
# XXX: This method should be deprecated in favour of taking in a proper
1334
2095
# new Inventory object.
2096
@needs_tree_write_lock
1336
2097
def set_inventory(self, new_inventory_list):
1337
2098
from bzrlib.inventory import (Inventory,
1338
2099
InventoryDirectory,
1342
2102
inv = Inventory(self.get_root_id())
1352
2112
elif kind == 'symlink':
1353
2113
inv.add(InventoryLink(file_id, name, parent))
1355
raise BzrError("unknown kind %r" % kind)
2115
raise errors.BzrError("unknown kind %r" % kind)
1356
2116
self._write_inventory(inv)
2118
@needs_tree_write_lock
1359
2119
def set_root_id(self, file_id):
1360
2120
"""Set the root id for this tree."""
1361
inv = self.read_working_inventory()
2124
'WorkingTree.set_root_id with fileid=None')
2125
file_id = osutils.safe_file_id(file_id)
2126
self._set_root_id(file_id)
2128
def _set_root_id(self, file_id):
2129
"""Set the root id for this tree, in a format specific manner.
2131
:param file_id: The file id to assign to the root. It must not be
2132
present in the current inventory or an error will occur. It must
2133
not be None, but rather a valid file id.
2135
inv = self._inventory
1362
2136
orig_root_id = inv.root.file_id
2137
# TODO: it might be nice to exit early if there was nothing
2138
# to do, saving us from trigger a sync on unlock.
2139
self._inventory_is_modified = True
2140
# we preserve the root inventory entry object, but
2141
# unlinkit from the byid index
1363
2142
del inv._byid[inv.root.file_id]
1364
2143
inv.root.file_id = file_id
2144
# and link it into the index with the new changed id.
1365
2145
inv._byid[inv.root.file_id] = inv.root
2146
# and finally update all children to reference the new id.
2147
# XXX: this should be safe to just look at the root.children
2148
# list, not the WHOLE INVENTORY.
1366
2149
for fid in inv:
1367
2150
entry = inv[fid]
1368
2151
if entry.parent_id == orig_root_id:
1369
2152
entry.parent_id = inv.root.file_id
1370
self._write_inventory(inv)
1372
2154
def unlock(self):
1373
2155
"""See Branch.unlock.
1375
2157
WorkingTree locking just uses the Branch locking facilities.
1376
2158
This is current because all working trees have an embedded branch
1377
2159
within them. IF in the future, we were to make branch data shareable
1378
between multiple working trees, i.e. via shared storage, then we
2160
between multiple working trees, i.e. via shared storage, then we
1379
2161
would probably want to lock both the local tree, and the branch.
1381
2163
raise NotImplementedError(self.unlock)
2165
def update(self, change_reporter=None, possible_transports=None):
1385
2166
"""Update a working tree along its branch.
1387
This will update the branch if its bound too, which means we have multiple trees involved:
1388
The new basis tree of the master.
1389
The old basis tree of the branch.
1390
The old basis tree of the working tree.
1391
The current working tree state.
1392
pathologically all three may be different, and non ancestors of each other.
1393
Conceptually we want to:
1394
Preserve the wt.basis->wt.state changes
1395
Transform the wt.basis to the new master basis.
1396
Apply a merge of the old branch basis to get any 'local' changes from it into the tree.
1397
Restore the wt.basis->wt.state changes.
2168
This will update the branch if its bound too, which means we have
2169
multiple trees involved:
2171
- The new basis tree of the master.
2172
- The old basis tree of the branch.
2173
- The old basis tree of the working tree.
2174
- The current working tree state.
2176
Pathologically, all three may be different, and non-ancestors of each
2177
other. Conceptually we want to:
2179
- Preserve the wt.basis->wt.state changes
2180
- Transform the wt.basis to the new master basis.
2181
- Apply a merge of the old branch basis to get any 'local' changes from
2183
- Restore the wt.basis->wt.state changes.
1399
2185
There isn't a single operation at the moment to do that, so we:
1400
Merge current state -> basis tree of the master w.r.t. the old tree basis.
1401
Do a 'normal' merge of the old branch basis if it is relevant.
1403
old_tip = self.branch.update()
1404
if old_tip is not None:
1405
self.add_pending_merge(old_tip)
1406
self.branch.lock_read()
1409
if self.last_revision() != self.branch.last_revision():
1410
# merge tree state up to new branch tip.
1411
basis = self.basis_tree()
2186
- Merge current state -> basis tree of the master w.r.t. the old tree
2188
- Do a 'normal' merge of the old branch basis if it is relevant.
2190
if self.branch.get_bound_location() is not None:
2192
update_branch = True
2194
self.lock_tree_write()
2195
update_branch = False
2198
old_tip = self.branch.update(possible_transports)
2201
return self._update_tree(old_tip, change_reporter)
2205
@needs_tree_write_lock
2206
def _update_tree(self, old_tip=None, change_reporter=None):
2207
"""Update a tree to the master branch.
2209
:param old_tip: if supplied, the previous tip revision the branch,
2210
before it was changed to the master branch's tip.
2212
# here if old_tip is not None, it is the old tip of the branch before
2213
# it was updated from the master branch. This should become a pending
2214
# merge in the working tree to preserve the user existing work. we
2215
# cant set that until we update the working trees last revision to be
2216
# one from the new branch, because it will just get absorbed by the
2217
# parent de-duplication logic.
2219
# We MUST save it even if an error occurs, because otherwise the users
2220
# local work is unreferenced and will appear to have been lost.
2224
last_rev = self.get_parent_ids()[0]
2226
last_rev = _mod_revision.NULL_REVISION
2227
if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
2228
# merge tree state up to new branch tip.
2229
basis = self.basis_tree()
1412
2232
to_tree = self.branch.basis_tree()
1413
result += merge_inner(self.branch,
2233
if basis.inventory.root is None:
2234
self.set_root_id(to_tree.get_root_id())
2236
result += merge.merge_inner(
1417
self.set_last_revision(self.branch.last_revision())
1418
if old_tip and old_tip != self.last_revision():
1419
# our last revision was not the prior branch last revision
1420
# and we have converted that last revision to a pending merge.
1421
# base is somewhere between the branch tip now
1422
# and the now pending merge
1423
from bzrlib.revision import common_ancestor
1425
base_rev_id = common_ancestor(self.branch.last_revision(),
1427
self.branch.repository)
1428
except errors.NoCommonAncestor:
1430
base_tree = self.branch.repository.revision_tree(base_rev_id)
1431
other_tree = self.branch.repository.revision_tree(old_tip)
1432
result += merge_inner(self.branch,
1438
self.branch.unlock()
2241
change_reporter=change_reporter)
2244
# TODO - dedup parents list with things merged by pull ?
2245
# reuse the tree we've updated to to set the basis:
2246
parent_trees = [(self.branch.last_revision(), to_tree)]
2247
merges = self.get_parent_ids()[1:]
2248
# Ideally we ask the tree for the trees here, that way the working
2249
# tree can decide whether to give us the entire tree or give us a
2250
# lazy initialised tree. dirstate for instance will have the trees
2251
# in ram already, whereas a last-revision + basis-inventory tree
2252
# will not, but also does not need them when setting parents.
2253
for parent in merges:
2254
parent_trees.append(
2255
(parent, self.branch.repository.revision_tree(parent)))
2256
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2257
parent_trees.append(
2258
(old_tip, self.branch.repository.revision_tree(old_tip)))
2259
self.set_parent_trees(parent_trees)
2260
last_rev = parent_trees[0][0]
2262
# the working tree had the same last-revision as the master
2263
# branch did. We may still have pivot local work from the local
2264
# branch into old_tip:
2265
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2266
self.add_parent_tree_id(old_tip)
2267
if (old_tip is not None and not _mod_revision.is_null(old_tip)
2268
and old_tip != last_rev):
2269
# our last revision was not the prior branch last revision
2270
# and we have converted that last revision to a pending merge.
2271
# base is somewhere between the branch tip now
2272
# and the now pending merge
2274
# Since we just modified the working tree and inventory, flush out
2275
# the current state, before we modify it again.
2276
# TODO: jam 20070214 WorkingTree3 doesn't require this, dirstate
2277
# requires it only because TreeTransform directly munges the
2278
# inventory and calls tree._write_inventory(). Ultimately we
2279
# should be able to remove this extra flush.
2281
graph = self.branch.repository.get_graph()
2282
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
2284
base_tree = self.branch.repository.revision_tree(base_rev_id)
2285
other_tree = self.branch.repository.revision_tree(old_tip)
2286
result += merge.merge_inner(
2291
change_reporter=change_reporter)
2294
def _write_hashcache_if_dirty(self):
2295
"""Write out the hashcache if it is dirty."""
2296
if self._hashcache.needs_write:
2298
self._hashcache.write()
2300
if e.errno not in (errno.EPERM, errno.EACCES):
2302
# TODO: jam 20061219 Should this be a warning? A single line
2303
# warning might be sufficient to let the user know what
2305
mutter('Could not write hashcache for %s\nError: %s',
2306
self._hashcache.cache_file_name(), e)
2308
@needs_tree_write_lock
1441
2309
def _write_inventory(self, inv):
1442
2310
"""Write inventory as the current inventory."""
1444
bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
1446
self._control_files.put('inventory', sio)
1447
self._set_inventory(inv)
1448
mutter('wrote working inventory')
2311
self._set_inventory(inv, dirty=True)
1450
2314
def set_conflicts(self, arg):
1451
raise UnsupportedOperation(self.set_conflicts, self)
2315
raise errors.UnsupportedOperation(self.set_conflicts, self)
1453
2317
def add_conflicts(self, arg):
1454
raise UnsupportedOperation(self.add_conflicts, self)
2318
raise errors.UnsupportedOperation(self.add_conflicts, self)
1456
2320
@needs_read_lock
1457
2321
def conflicts(self):
1458
conflicts = ConflictList()
2322
conflicts = _mod_conflicts.ConflictList()
1459
2323
for conflicted in self._iter_conflicts():
1474
2338
if text == False:
1476
2340
ctype = {True: 'text conflict', False: 'contents conflict'}[text]
1477
conflicts.append(Conflict.factory(ctype, path=conflicted,
2341
conflicts.append(_mod_conflicts.Conflict.factory(ctype,
1478
2343
file_id=self.path2id(conflicted)))
1479
2344
return conflicts
2346
def walkdirs(self, prefix=""):
2347
"""Walk the directories of this tree.
2349
returns a generator which yields items in the form:
2350
((curren_directory_path, fileid),
2351
[(file1_path, file1_name, file1_kind, (lstat), file1_id,
2354
This API returns a generator, which is only valid during the current
2355
tree transaction - within a single lock_read or lock_write duration.
2357
If the tree is not locked, it may cause an error to be raised,
2358
depending on the tree implementation.
2360
disk_top = self.abspath(prefix)
2361
if disk_top.endswith('/'):
2362
disk_top = disk_top[:-1]
2363
top_strip_len = len(disk_top) + 1
2364
inventory_iterator = self._walkdirs(prefix)
2365
disk_iterator = osutils.walkdirs(disk_top, prefix)
2367
current_disk = disk_iterator.next()
2368
disk_finished = False
2370
if not (e.errno == errno.ENOENT or
2371
(sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
2374
disk_finished = True
2376
current_inv = inventory_iterator.next()
2377
inv_finished = False
2378
except StopIteration:
2381
while not inv_finished or not disk_finished:
2383
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
2384
cur_disk_dir_content) = current_disk
2386
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
2387
cur_disk_dir_content) = ((None, None), None)
2388
if not disk_finished:
2389
# strip out .bzr dirs
2390
if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
2391
len(cur_disk_dir_content) > 0):
2392
# osutils.walkdirs can be made nicer -
2393
# yield the path-from-prefix rather than the pathjoined
2395
bzrdir_loc = bisect_left(cur_disk_dir_content,
2397
if (bzrdir_loc < len(cur_disk_dir_content)
2398
and self.bzrdir.is_control_filename(
2399
cur_disk_dir_content[bzrdir_loc][0])):
2400
# we dont yield the contents of, or, .bzr itself.
2401
del cur_disk_dir_content[bzrdir_loc]
2403
# everything is unknown
2406
# everything is missing
2409
direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
2411
# disk is before inventory - unknown
2412
dirblock = [(relpath, basename, kind, stat, None, None) for
2413
relpath, basename, kind, stat, top_path in
2414
cur_disk_dir_content]
2415
yield (cur_disk_dir_relpath, None), dirblock
2417
current_disk = disk_iterator.next()
2418
except StopIteration:
2419
disk_finished = True
2421
# inventory is before disk - missing.
2422
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
2423
for relpath, basename, dkind, stat, fileid, kind in
2425
yield (current_inv[0][0], current_inv[0][1]), dirblock
2427
current_inv = inventory_iterator.next()
2428
except StopIteration:
2431
# versioned present directory
2432
# merge the inventory and disk data together
2434
for relpath, subiterator in itertools.groupby(sorted(
2435
current_inv[1] + cur_disk_dir_content,
2436
key=operator.itemgetter(0)), operator.itemgetter(1)):
2437
path_elements = list(subiterator)
2438
if len(path_elements) == 2:
2439
inv_row, disk_row = path_elements
2440
# versioned, present file
2441
dirblock.append((inv_row[0],
2442
inv_row[1], disk_row[2],
2443
disk_row[3], inv_row[4],
2445
elif len(path_elements[0]) == 5:
2447
dirblock.append((path_elements[0][0],
2448
path_elements[0][1], path_elements[0][2],
2449
path_elements[0][3], None, None))
2450
elif len(path_elements[0]) == 6:
2451
# versioned, absent file.
2452
dirblock.append((path_elements[0][0],
2453
path_elements[0][1], 'unknown', None,
2454
path_elements[0][4], path_elements[0][5]))
2456
raise NotImplementedError('unreachable code')
2457
yield current_inv[0], dirblock
2459
current_inv = inventory_iterator.next()
2460
except StopIteration:
2463
current_disk = disk_iterator.next()
2464
except StopIteration:
2465
disk_finished = True
2467
def _walkdirs(self, prefix=""):
2468
"""Walk the directories of this tree.
2470
:prefix: is used as the directrory to start with.
2471
returns a generator which yields items in the form:
2472
((curren_directory_path, fileid),
2473
[(file1_path, file1_name, file1_kind, None, file1_id,
2476
_directory = 'directory'
2477
# get the root in the inventory
2478
inv = self.inventory
2479
top_id = inv.path2id(prefix)
2483
pending = [(prefix, '', _directory, None, top_id, None)]
2486
currentdir = pending.pop()
2487
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2488
top_id = currentdir[4]
2490
relroot = currentdir[0] + '/'
2493
# FIXME: stash the node in pending
2495
if entry.kind == 'directory':
2496
for name, child in entry.sorted_children():
2497
dirblock.append((relroot + name, name, child.kind, None,
2498
child.file_id, child.kind
2500
yield (currentdir[0], entry.file_id), dirblock
2501
# push the user specified dirs from dirblock
2502
for dir in reversed(dirblock):
2503
if dir[2] == _directory:
2506
@needs_tree_write_lock
2507
def auto_resolve(self):
2508
"""Automatically resolve text conflicts according to contents.
2510
Only text conflicts are auto_resolvable. Files with no conflict markers
2511
are considered 'resolved', because bzr always puts conflict markers
2512
into files that have text conflicts. The corresponding .THIS .BASE and
2513
.OTHER files are deleted, as per 'resolve'.
2514
:return: a tuple of ConflictLists: (un_resolved, resolved).
2516
un_resolved = _mod_conflicts.ConflictList()
2517
resolved = _mod_conflicts.ConflictList()
2518
conflict_re = re.compile('^(<{7}|={7}|>{7})')
2519
for conflict in self.conflicts():
2520
if (conflict.typestring != 'text conflict' or
2521
self.kind(conflict.file_id) != 'file'):
2522
un_resolved.append(conflict)
2524
my_file = open(self.id2abspath(conflict.file_id), 'rb')
2526
for line in my_file:
2527
if conflict_re.search(line):
2528
un_resolved.append(conflict)
2531
resolved.append(conflict)
2534
resolved.remove_files(self)
2535
self.set_conflicts(un_resolved)
2536
return un_resolved, resolved
2540
tree_basis = self.basis_tree()
2541
tree_basis.lock_read()
2543
repo_basis = self.branch.repository.revision_tree(
2544
self.last_revision())
2545
if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2546
raise errors.BzrCheckError(
2547
"Mismatched basis inventory content.")
2552
def _validate(self):
2553
"""Validate internal structures.
2555
This is meant mostly for the test suite. To give it a chance to detect
2556
corruption after actions have occurred. The default implementation is a
2559
:return: None. An exception should be raised if there is an error.
2564
def _get_rules_searcher(self, default_searcher):
2565
"""See Tree._get_rules_searcher."""
2566
if self._rules_searcher is None:
2567
self._rules_searcher = super(WorkingTree,
2568
self)._get_rules_searcher(default_searcher)
2569
return self._rules_searcher
2571
def get_shelf_manager(self):
2572
"""Return the ShelfManager for this WorkingTree."""
2573
from bzrlib.shelf import ShelfManager
2574
return ShelfManager(self, self._transport)
1482
2577
class WorkingTree2(WorkingTree):
1483
2578
"""This is the Format 2 working tree.
1485
This was the first weave based working tree.
2580
This was the first weave based working tree.
1486
2581
- uses os locks for locking.
1487
2582
- uses the branch last-revision.
2585
def __init__(self, *args, **kwargs):
2586
super(WorkingTree2, self).__init__(*args, **kwargs)
2587
# WorkingTree2 has more of a constraint that self._inventory must
2588
# exist. Because this is an older format, we don't mind the overhead
2589
# caused by the extra computation here.
2591
# Newer WorkingTree's should only have self._inventory set when they
2593
if self._inventory is None:
2594
self.read_working_inventory()
2596
def lock_tree_write(self):
2597
"""See WorkingTree.lock_tree_write().
2599
In Format2 WorkingTrees we have a single lock for the branch and tree
2600
so lock_tree_write() degrades to lock_write().
2602
self.branch.lock_write()
2604
return self._control_files.lock_write()
2606
self.branch.unlock()
1490
2609
def unlock(self):
2610
# do non-implementation specific cleanup
1491
2613
# we share control files:
1492
if self._hashcache.needs_write and self._control_files._lock_count==3:
1493
self._hashcache.write()
2614
if self._control_files._lock_count == 3:
2615
# _inventory_is_modified is always False during a read lock.
2616
if self._inventory_is_modified:
2618
self._write_hashcache_if_dirty()
1494
2620
# reverse order of locking.
1496
2622
return self._control_files.unlock()