210
"""Construct a WorkingTree for basedir.
196
"""Construct a WorkingTree instance. This is not a public API.
212
If the branch is not supplied, it is opened automatically.
213
If the branch is supplied, it must be the branch for this basedir.
214
(branch.base is not cross checked, because for remote branches that
215
would be meaningless).
198
:param branch: A branch to override probing for the branch.
217
200
self._format = _format
218
201
self.bzrdir = _bzrdir
219
202
if not _internal:
220
# not created via open etc.
221
warn("WorkingTree() is deprecated as of bzr version 0.8. "
222
"Please use bzrdir.open_workingtree or WorkingTree.open().",
225
wt = WorkingTree.open(basedir)
226
self.branch = wt.branch
227
self.basedir = wt.basedir
228
self._control_files = wt._control_files
229
self._hashcache = wt._hashcache
230
self._set_inventory(wt._inventory)
231
self._format = wt._format
232
self.bzrdir = wt.bzrdir
233
from bzrlib.hashcache import HashCache
234
from bzrlib.trace import note, mutter
235
assert isinstance(basedir, basestring), \
236
"base directory %r is not a string" % basedir
203
raise errors.BzrError("Please use bzrdir.open_workingtree or "
204
"WorkingTree.open() to obtain a WorkingTree.")
237
205
basedir = safe_unicode(basedir)
238
206
mutter("opening working tree %r", basedir)
239
207
if deprecated_passed(branch):
241
warn("WorkingTree(..., branch=XXX) is deprecated as of bzr 0.8."
242
" Please use bzrdir.open_workingtree() or"
243
" WorkingTree.open().",
208
self._branch = branch
249
self.branch = self.bzrdir.open_branch()
250
assert isinstance(self.branch, Branch), \
251
"branch %r is not a Branch" % self.branch
210
self._branch = self.bzrdir.open_branch()
252
211
self.basedir = realpath(basedir)
253
212
# if branch is at our basedir and is a format 6 or less
254
213
if isinstance(self._format, WorkingTreeFormat2):
255
214
# share control object
256
215
self._control_files = self.branch.control_files
258
# only ready for format 3
259
assert isinstance(self._format, WorkingTreeFormat3)
260
assert isinstance(_control_files, LockableFiles), \
261
"_control_files must be a LockableFiles, not %r" \
217
# assume all other formats have their own control files.
263
218
self._control_files = _control_files
219
self._transport = self._control_files._transport
264
220
# update the whole cache up front and write to disk if anything changed;
265
221
# in the future we might want to do this more selectively
266
222
# two possible ways offer themselves : in self._unlock, write the cache
267
223
# if needed, or, when the cache sees a change, append it to the hash
268
224
# cache file, and have the parser take the most recent entry for a
269
225
# given path only.
270
cache_filename = self.bzrdir.get_workingtree_transport(None).abspath('stat-cache')
271
hc = self._hashcache = HashCache(basedir, cache_filename, self._control_files._file_mode)
226
wt_trans = self.bzrdir.get_workingtree_transport(None)
227
cache_filename = wt_trans.local_abspath('stat-cache')
228
self._hashcache = hashcache.HashCache(basedir, cache_filename,
229
self.bzrdir._get_file_mode())
273
232
# is this scan needed ? it makes things kinda slow.
276
235
if hc.needs_write:
277
236
mutter("write hc")
280
239
if _inventory is None:
281
self._set_inventory(self.read_working_inventory())
283
self._set_inventory(_inventory)
285
def _set_inventory(self, inv):
240
# This will be acquired on lock_read() or lock_write()
241
self._inventory_is_modified = False
242
self._inventory = None
244
# the caller of __init__ has provided an inventory,
245
# we assume they know what they are doing - as its only
246
# the Format factory and creation methods that are
247
# permitted to do this.
248
self._set_inventory(_inventory, dirty=False)
249
self._detect_case_handling()
250
self._rules_searcher = None
252
def _detect_case_handling(self):
253
wt_trans = self.bzrdir.get_workingtree_transport(None)
255
wt_trans.stat("FoRMaT")
256
except errors.NoSuchFile:
257
self.case_sensitive = True
259
self.case_sensitive = False
261
self._setup_directory_is_tree_reference()
264
fget=lambda self: self._branch,
265
doc="""The branch this WorkingTree is connected to.
267
This cannot be set - it is reflective of the actual disk structure
268
the working tree has been constructed from.
271
def break_lock(self):
272
"""Break a lock if one is present from another instance.
274
Uses the ui factory to ask for confirmation if the lock may be from
277
This will probe the repository for its lock as well.
279
self._control_files.break_lock()
280
self.branch.break_lock()
282
def requires_rich_root(self):
283
return self._format.requires_rich_root
285
def supports_tree_reference(self):
288
def supports_content_filtering(self):
289
return self._format.supports_content_filtering()
291
def supports_views(self):
292
return self._format.supports_views()
294
def _set_inventory(self, inv, dirty):
295
"""Set the internal cached inventory.
297
:param inv: The inventory to set.
298
:param dirty: A boolean indicating whether the inventory is the same
299
logical inventory as whats on disk. If True the inventory is not
300
the same and should be written to disk or data will be lost, if
301
False then the inventory is the same as that on disk and any
302
serialisation would be unneeded overhead.
286
304
self._inventory = inv
287
self.path2id = self._inventory.path2id
289
def is_control_filename(self, filename):
290
"""True if filename is the name of a control file in this tree.
292
This is true IF and ONLY IF the filename is part of the meta data
293
that bzr controls in this tree. I.E. a random .bzr directory placed
294
on disk will not be a control file for this tree.
297
self.bzrdir.transport.relpath(self.abspath(filename))
299
except errors.PathNotChild:
305
self._inventory_is_modified = dirty
303
308
def open(path=None, _unsupported=False):
342
369
inv = self._inventory
343
370
for path, ie in inv.iter_entries():
344
if bzrlib.osutils.lexists(self.abspath(path)):
371
if osutils.lexists(self.abspath(path)):
374
def all_file_ids(self):
375
"""See Tree.iter_all_file_ids"""
376
return set(self.inventory)
347
378
def __repr__(self):
348
379
return "<%s of %s>" % (self.__class__.__name__,
349
380
getattr(self, 'basedir', None))
351
382
def abspath(self, filename):
352
383
return pathjoin(self.basedir, filename)
354
385
def basis_tree(self):
355
"""Return RevisionTree for the current last revision."""
356
revision_id = self.last_revision()
357
if revision_id is not None:
359
xml = self.read_basis_inventory()
360
inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
363
if inv is not None and inv.revision_id == revision_id:
364
return bzrlib.tree.RevisionTree(self.branch.repository, inv,
366
# FIXME? RBC 20060403 should we cache the inventory here ?
367
return self.branch.repository.revision_tree(revision_id)
370
@deprecated_method(zero_eight)
371
def create(branch, directory):
372
"""Create a workingtree for branch at directory.
374
If existing_directory already exists it must have a .bzr directory.
375
If it does not exist, it will be created.
377
This returns a new WorkingTree object for the new checkout.
379
TODO FIXME RBC 20060124 when we have checkout formats in place this
380
should accept an optional revisionid to checkout [and reject this if
381
checking out into the same dir as a pre-checkout-aware branch format.]
383
XXX: When BzrDir is present, these should be created through that
386
warn('delete WorkingTree.create', stacklevel=3)
387
transport = get_transport(directory)
388
if branch.bzrdir.root_transport.base == transport.base:
390
return branch.bzrdir.create_workingtree()
391
# different directory,
392
# create a branch reference
393
# and now a working tree.
394
raise NotImplementedError
397
@deprecated_method(zero_eight)
398
def create_standalone(directory):
399
"""Create a checkout and a branch and a repo at directory.
401
Directory must exist and be empty.
403
please use BzrDir.create_standalone_workingtree
405
return bzrdir.BzrDir.create_standalone_workingtree(directory)
407
def relpath(self, abs):
408
"""Return the local path portion from a given absolute path."""
409
return relpath(self.basedir, abs)
386
"""Return RevisionTree for the current last revision.
388
If the left most parent is a ghost then the returned tree will be an
389
empty tree - one obtained by calling
390
repository.revision_tree(NULL_REVISION).
393
revision_id = self.get_parent_ids()[0]
395
# no parents, return an empty revision tree.
396
# in the future this should return the tree for
397
# 'empty:' - the implicit root empty tree.
398
return self.branch.repository.revision_tree(
399
_mod_revision.NULL_REVISION)
401
return self.revision_tree(revision_id)
402
except errors.NoSuchRevision:
404
# No cached copy available, retrieve from the repository.
405
# FIXME? RBC 20060403 should we cache the inventory locally
408
return self.branch.repository.revision_tree(revision_id)
409
except (errors.RevisionNotPresent, errors.NoSuchRevision):
410
# the basis tree *may* be a ghost or a low level error may have
411
# occured. If the revision is present, its a problem, if its not
413
if self.branch.repository.has_revision(revision_id):
415
# the basis tree is a ghost so return an empty tree.
416
return self.branch.repository.revision_tree(
417
_mod_revision.NULL_REVISION)
420
self._flush_ignore_list_cache()
422
def relpath(self, path):
423
"""Return the local path portion from a given path.
425
The path may be absolute or relative. If its a relative path it is
426
interpreted relative to the python current working directory.
428
return osutils.relpath(self.basedir, path)
411
430
def has_filename(self, filename):
412
return bzrlib.osutils.lexists(self.abspath(filename))
414
def get_file(self, file_id):
415
return self.get_file_byname(self.id2path(file_id))
431
return osutils.lexists(self.abspath(filename))
433
def get_file(self, file_id, path=None):
434
return self.get_file_with_stat(file_id, path)[0]
436
def get_file_with_stat(self, file_id, path=None, _fstat=os.fstat):
437
"""See MutableTree.get_file_with_stat."""
439
path = self.id2path(file_id)
440
file_obj = self.get_file_byname(path)
441
return (file_obj, _fstat(file_obj.fileno()))
417
443
def get_file_byname(self, filename):
418
444
return file(self.abspath(filename), 'rb')
446
def get_file_lines(self, file_id, path=None):
447
"""See Tree.get_file_lines()"""
448
file = self.get_file(file_id, path)
450
return file.readlines()
455
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
456
"""See Tree.annotate_iter
458
This implementation will use the basis tree implementation if possible.
459
Lines not in the basis are attributed to CURRENT_REVISION
461
If there are pending merges, lines added by those merges will be
462
incorrectly attributed to CURRENT_REVISION (but after committing, the
463
attribution will be correct).
465
basis = self.basis_tree()
468
changes = self.iter_changes(basis, True, [self.id2path(file_id)],
469
require_versioned=True).next()
470
changed_content, kind = changes[2], changes[6]
471
if not changed_content:
472
return basis.annotate_iter(file_id)
476
if kind[0] != 'file':
479
old_lines = list(basis.annotate_iter(file_id))
481
for tree in self.branch.repository.revision_trees(
482
self.get_parent_ids()[1:]):
483
if file_id not in tree:
485
old.append(list(tree.annotate_iter(file_id)))
486
return annotate.reannotate(old, self.get_file(file_id).readlines(),
491
def _get_ancestors(self, default_revision):
492
ancestors = set([default_revision])
493
for parent_id in self.get_parent_ids():
494
ancestors.update(self.branch.repository.get_ancestry(
495
parent_id, topo_sorted=False))
498
def get_parent_ids(self):
499
"""See Tree.get_parent_ids.
501
This implementation reads the pending merges list and last_revision
502
value and uses that to decide what the parents list should be.
504
last_rev = _mod_revision.ensure_null(self._last_revision())
505
if _mod_revision.NULL_REVISION == last_rev:
510
merges_file = self._transport.get('pending-merges')
511
except errors.NoSuchFile:
514
for l in merges_file.readlines():
515
revision_id = l.rstrip('\n')
516
parents.append(revision_id)
420
520
def get_root_id(self):
421
521
"""Return the id of this trees root"""
422
inv = self.read_working_inventory()
423
return inv.root.file_id
522
return self._inventory.root.file_id
425
524
def _get_store_filename(self, file_id):
426
525
## XXX: badly named; this is not in the store at all
427
526
return self.abspath(self.id2path(file_id))
430
def clone(self, to_bzrdir, revision_id=None, basis=None):
529
def clone(self, to_bzrdir, revision_id=None):
431
530
"""Duplicate this working tree into to_bzr, including all state.
433
532
Specifically modified files are kept as modified, but
493
575
__contains__ = has_id
495
577
def get_file_size(self, file_id):
496
return os.path.getsize(self.id2abspath(file_id))
578
"""See Tree.get_file_size"""
580
return os.path.getsize(self.id2abspath(file_id))
582
if e.errno != errno.ENOENT:
499
def get_file_sha1(self, file_id):
500
path = self._inventory.id2path(file_id)
501
return self._hashcache.get_sha1(path)
503
def is_executable(self, file_id):
504
if not supports_executable():
588
def get_file_sha1(self, file_id, path=None, stat_value=None):
590
path = self._inventory.id2path(file_id)
591
return self._hashcache.get_sha1(path, stat_value)
593
def get_file_mtime(self, file_id, path=None):
595
path = self.inventory.id2path(file_id)
596
return os.lstat(self.abspath(path)).st_mtime
598
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
599
file_id = self.path2id(path)
600
return self._inventory[file_id].executable
602
def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
603
mode = stat_result.st_mode
604
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
606
if not supports_executable():
607
def is_executable(self, file_id, path=None):
505
608
return self._inventory[file_id].executable
507
path = self._inventory.id2path(file_id)
610
_is_executable_from_path_and_stat = \
611
_is_executable_from_path_and_stat_from_basis
613
def is_executable(self, file_id, path=None):
615
path = self.id2path(file_id)
508
616
mode = os.lstat(self.abspath(path)).st_mode
509
return bool(stat.S_ISREG(mode) and stat.S_IEXEC&mode)
512
def add(self, files, ids=None):
513
"""Make files versioned.
515
Note that the command line normally calls smart_add instead,
516
which can automatically recurse.
518
This adds the files to the inventory, so that they will be
519
recorded by the next commit.
522
List of paths to add, relative to the base of the tree.
525
If set, use these instead of automatically generated ids.
526
Must be the same length as the list of files, but may
527
contain None for ids that are to be autogenerated.
529
TODO: Perhaps have an option to add the ids even if the files do
532
TODO: Perhaps callback with the ids and paths as they're added.
617
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
619
_is_executable_from_path_and_stat = \
620
_is_executable_from_path_and_stat_from_stat
622
@needs_tree_write_lock
623
def _add(self, files, ids, kinds):
624
"""See MutableTree._add."""
534
625
# TODO: Re-adding a file that is removed in the working copy
535
626
# should probably put it back with the previous ID.
536
if isinstance(files, basestring):
537
assert(ids is None or isinstance(ids, basestring))
543
ids = [None] * len(files)
545
assert(len(ids) == len(files))
547
inv = self.read_working_inventory()
548
for f,file_id in zip(files, ids):
549
if self.is_control_filename(f):
550
raise BzrError("cannot add control file %s" % quotefn(f))
555
raise BzrError("cannot add top-level %r" % f)
557
fullpath = normpath(self.abspath(f))
560
kind = file_kind(fullpath)
562
if e.errno == errno.ENOENT:
563
raise NoSuchFile(fullpath)
564
# maybe something better?
565
raise BzrError('cannot add: not a regular file, symlink or directory: %s' % quotefn(f))
567
if not InventoryEntry.versionable_kind(kind):
568
raise BzrError('cannot add: not a versionable file ('
569
'i.e. regular file, symlink or directory): %s' % quotefn(f))
627
# the read and write working inventory should not occur in this
628
# function - they should be part of lock_write and unlock.
630
for f, file_id, kind in zip(files, ids, kinds):
571
631
if file_id is None:
572
file_id = gen_file_id(f)
573
inv.add_path(f, kind=kind, file_id=file_id)
632
inv.add_path(f, kind=kind)
634
inv.add_path(f, kind=kind, file_id=file_id)
635
self._inventory_is_modified = True
575
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
576
self._write_inventory(inv)
637
@needs_tree_write_lock
638
def _gather_kinds(self, files, kinds):
639
"""See MutableTree._gather_kinds."""
640
for pos, f in enumerate(files):
641
if kinds[pos] is None:
642
fullpath = normpath(self.abspath(f))
644
kinds[pos] = file_kind(fullpath)
646
if e.errno == errno.ENOENT:
647
raise errors.NoSuchFile(fullpath)
578
649
@needs_write_lock
650
def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
651
"""Add revision_id as a parent.
653
This is equivalent to retrieving the current list of parent ids
654
and setting the list to its value plus revision_id.
656
:param revision_id: The revision id to add to the parent list. It may
657
be a ghost revision as long as its not the first parent to be added,
658
or the allow_leftmost_as_ghost parameter is set True.
659
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
661
parents = self.get_parent_ids() + [revision_id]
662
self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
663
or allow_leftmost_as_ghost)
665
@needs_tree_write_lock
666
def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
667
"""Add revision_id, tree tuple as a parent.
669
This is equivalent to retrieving the current list of parent trees
670
and setting the list to its value plus parent_tuple. See also
671
add_parent_tree_id - if you only have a parent id available it will be
672
simpler to use that api. If you have the parent already available, using
673
this api is preferred.
675
:param parent_tuple: The (revision id, tree) to add to the parent list.
676
If the revision_id is a ghost, pass None for the tree.
677
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
679
parent_ids = self.get_parent_ids() + [parent_tuple[0]]
680
if len(parent_ids) > 1:
681
# the leftmost may have already been a ghost, preserve that if it
683
allow_leftmost_as_ghost = True
684
self.set_parent_ids(parent_ids,
685
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
687
@needs_tree_write_lock
579
688
def add_pending_merge(self, *revision_ids):
580
689
# TODO: Perhaps should check at this point that the
581
690
# history of the revision is actually present?
582
p = self.pending_merges()
691
parents = self.get_parent_ids()
584
693
for rev_id in revision_ids:
694
if rev_id in parents:
696
parents.append(rev_id)
590
self.set_pending_merges(p)
593
def pending_merges(self):
594
"""Return a list of pending merges.
596
These are revisions that have been merged into the working
597
directory but not yet committed.
699
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
701
def path_content_summary(self, path, _lstat=os.lstat,
702
_mapper=osutils.file_kind_from_stat_mode):
703
"""See Tree.path_content_summary."""
704
abspath = self.abspath(path)
600
merges_file = self._control_files.get_utf8('pending-merges')
706
stat_result = _lstat(abspath)
601
707
except OSError, e:
602
if e.errno != errno.ENOENT:
606
for l in merges_file.readlines():
607
p.append(l.rstrip('\n'))
708
if getattr(e, 'errno', None) == errno.ENOENT:
710
return ('missing', None, None, None)
711
# propagate other errors
713
kind = _mapper(stat_result.st_mode)
715
size = stat_result.st_size
716
# try for a stat cache lookup
717
executable = self._is_executable_from_path_and_stat(path, stat_result)
718
return (kind, size, executable, self._sha_from_stat(
720
elif kind == 'directory':
721
# perhaps it looks like a plain directory, but it's really a
723
if self._directory_is_tree_reference(path):
724
kind = 'tree-reference'
725
return kind, None, None, None
726
elif kind == 'symlink':
727
return ('symlink', None, None, os.readlink(abspath))
729
return (kind, None, None, None)
731
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
732
"""Common ghost checking functionality from set_parent_*.
734
This checks that the left hand-parent exists if there are any
737
if len(revision_ids) > 0:
738
leftmost_id = revision_ids[0]
739
if (not allow_leftmost_as_ghost and not
740
self.branch.repository.has_revision(leftmost_id)):
741
raise errors.GhostRevisionUnusableHere(leftmost_id)
743
def _set_merges_from_parent_ids(self, parent_ids):
744
merges = parent_ids[1:]
745
self._transport.put_bytes('pending-merges', '\n'.join(merges),
746
mode=self._control_files._file_mode)
748
def _filter_parent_ids_by_ancestry(self, revision_ids):
749
"""Check that all merged revisions are proper 'heads'.
751
This will always return the first revision_id, and any merged revisions
754
if len(revision_ids) == 0:
756
graph = self.branch.repository.get_graph()
757
heads = graph.heads(revision_ids)
758
new_revision_ids = revision_ids[:1]
759
for revision_id in revision_ids[1:]:
760
if revision_id in heads and revision_id not in new_revision_ids:
761
new_revision_ids.append(revision_id)
762
if new_revision_ids != revision_ids:
763
trace.mutter('requested to set revision_ids = %s,'
764
' but filtered to %s', revision_ids, new_revision_ids)
765
return new_revision_ids
767
@needs_tree_write_lock
768
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
769
"""Set the parent ids to revision_ids.
771
See also set_parent_trees. This api will try to retrieve the tree data
772
for each element of revision_ids from the trees repository. If you have
773
tree data already available, it is more efficient to use
774
set_parent_trees rather than set_parent_ids. set_parent_ids is however
775
an easier API to use.
777
:param revision_ids: The revision_ids to set as the parent ids of this
778
working tree. Any of these may be ghosts.
780
self._check_parents_for_ghosts(revision_ids,
781
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
782
for revision_id in revision_ids:
783
_mod_revision.check_not_reserved_id(revision_id)
785
revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
787
if len(revision_ids) > 0:
788
self.set_last_revision(revision_ids[0])
790
self.set_last_revision(_mod_revision.NULL_REVISION)
792
self._set_merges_from_parent_ids(revision_ids)
794
@needs_tree_write_lock
795
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
796
"""See MutableTree.set_parent_trees."""
797
parent_ids = [rev for (rev, tree) in parents_list]
798
for revision_id in parent_ids:
799
_mod_revision.check_not_reserved_id(revision_id)
801
self._check_parents_for_ghosts(parent_ids,
802
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
804
parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
806
if len(parent_ids) == 0:
807
leftmost_parent_id = _mod_revision.NULL_REVISION
808
leftmost_parent_tree = None
810
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
812
if self._change_last_revision(leftmost_parent_id):
813
if leftmost_parent_tree is None:
814
# If we don't have a tree, fall back to reading the
815
# parent tree from the repository.
816
self._cache_basis_inventory(leftmost_parent_id)
818
inv = leftmost_parent_tree.inventory
819
xml = self._create_basis_xml_from_inventory(
820
leftmost_parent_id, inv)
821
self._write_basis_inventory(xml)
822
self._set_merges_from_parent_ids(parent_ids)
824
@needs_tree_write_lock
611
825
def set_pending_merges(self, rev_list):
612
self._control_files.put_utf8('pending-merges', '\n'.join(rev_list))
826
parents = self.get_parent_ids()
827
leftmost = parents[:1]
828
new_parents = leftmost + rev_list
829
self.set_parent_ids(new_parents)
831
@needs_tree_write_lock
615
832
def set_merge_modified(self, modified_hashes):
616
833
def iter_stanzas():
617
834
for file_id, hash in modified_hashes.iteritems():
618
yield Stanza(file_id=file_id, hash=hash)
835
yield Stanza(file_id=file_id.decode('utf8'), hash=hash)
619
836
self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
838
def _sha_from_stat(self, path, stat_result):
839
"""Get a sha digest from the tree's stat cache.
841
The default implementation assumes no stat cache is present.
843
:param path: The path.
844
:param stat_result: The stat result being looked up.
622
848
def _put_rio(self, filename, stanzas, header):
849
self._must_be_locked()
623
850
my_file = rio_file(stanzas, header)
624
self._control_files.put(filename, my_file)
851
self._transport.put_file(filename, my_file,
852
mode=self._control_files._file_mode)
854
@needs_write_lock # because merge pulls data into the branch.
855
def merge_from_branch(self, branch, to_revision=None, from_revision=None,
857
"""Merge from a branch into this working tree.
859
:param branch: The branch to merge from.
860
:param to_revision: If non-None, the merge will merge to to_revision,
861
but not beyond it. to_revision does not need to be in the history
862
of the branch when it is supplied. If None, to_revision defaults to
863
branch.last_revision().
865
from bzrlib.merge import Merger, Merge3Merger
866
pb = bzrlib.ui.ui_factory.nested_progress_bar()
868
merger = Merger(self.branch, this_tree=self, pb=pb)
869
merger.pp = ProgressPhase("Merge phase", 5, pb)
870
merger.pp.next_phase()
871
# check that there are no
873
merger.check_basis(check_clean=True, require_commits=False)
874
if to_revision is None:
875
to_revision = _mod_revision.ensure_null(branch.last_revision())
876
merger.other_rev_id = to_revision
877
if _mod_revision.is_null(merger.other_rev_id):
878
raise errors.NoCommits(branch)
879
self.branch.fetch(branch, last_revision=merger.other_rev_id)
880
merger.other_basis = merger.other_rev_id
881
merger.other_tree = self.branch.repository.revision_tree(
883
merger.other_branch = branch
884
merger.pp.next_phase()
885
if from_revision is None:
888
merger.set_base_revision(from_revision, branch)
889
if merger.base_rev_id == merger.other_rev_id:
890
raise errors.PointlessMerge
891
merger.backup_files = False
892
if merge_type is None:
893
merger.merge_type = Merge3Merger
895
merger.merge_type = merge_type
896
merger.set_interesting_files(None)
897
merger.show_base = False
898
merger.reprocess = False
899
conflicts = merger.do_merge()
627
906
def merge_modified(self):
907
"""Return a dictionary of files modified by a merge.
909
The list is initialized by WorkingTree.set_merge_modified, which is
910
typically called after we make some automatic updates to the tree
913
This returns a map of file_id->sha1, containing only files which are
914
still in the working inventory and have that text hash.
629
hashfile = self._control_files.get('merge-hashes')
917
hashfile = self._transport.get('merge-hashes')
918
except errors.NoSuchFile:
634
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
635
raise MergeModifiedFormatError()
636
except StopIteration:
637
raise MergeModifiedFormatError()
638
for s in RioReader(hashfile):
639
file_id = s.get("file_id")
640
if file_id not in self.inventory:
643
if hash == self.get_file_sha1(file_id):
644
merge_hashes[file_id] = hash
923
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
924
raise errors.MergeModifiedFormatError()
925
except StopIteration:
926
raise errors.MergeModifiedFormatError()
927
for s in RioReader(hashfile):
928
# RioReader reads in Unicode, so convert file_ids back to utf8
929
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
930
if file_id not in self.inventory:
932
text_hash = s.get("hash")
933
if text_hash == self.get_file_sha1(file_id):
934
merge_hashes[file_id] = text_hash
940
def mkdir(self, path, file_id=None):
941
"""See MutableTree.mkdir()."""
943
file_id = generate_ids.gen_file_id(os.path.basename(path))
944
os.mkdir(self.abspath(path))
945
self.add(path, file_id, 'directory')
647
948
def get_symlink_target(self, file_id):
648
949
return os.readlink(self.id2abspath(file_id))
650
def file_class(self, filename):
651
if self.path2id(filename):
653
elif self.is_ignored(filename):
658
def list_files(self):
659
"""Recursively list all files as (path, class, kind, id).
952
def subsume(self, other_tree):
953
def add_children(inventory, entry):
954
for child_entry in entry.children.values():
955
inventory._byid[child_entry.file_id] = child_entry
956
if child_entry.kind == 'directory':
957
add_children(inventory, child_entry)
958
if other_tree.get_root_id() == self.get_root_id():
959
raise errors.BadSubsumeSource(self, other_tree,
960
'Trees have the same root')
962
other_tree_path = self.relpath(other_tree.basedir)
963
except errors.PathNotChild:
964
raise errors.BadSubsumeSource(self, other_tree,
965
'Tree is not contained by the other')
966
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
967
if new_root_parent is None:
968
raise errors.BadSubsumeSource(self, other_tree,
969
'Parent directory is not versioned.')
970
# We need to ensure that the result of a fetch will have a
971
# versionedfile for the other_tree root, and only fetching into
972
# RepositoryKnit2 guarantees that.
973
if not self.branch.repository.supports_rich_root():
974
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
975
other_tree.lock_tree_write()
977
new_parents = other_tree.get_parent_ids()
978
other_root = other_tree.inventory.root
979
other_root.parent_id = new_root_parent
980
other_root.name = osutils.basename(other_tree_path)
981
self.inventory.add(other_root)
982
add_children(self.inventory, other_root)
983
self._write_inventory(self.inventory)
984
# normally we don't want to fetch whole repositories, but i think
985
# here we really do want to consolidate the whole thing.
986
for parent_id in other_tree.get_parent_ids():
987
self.branch.fetch(other_tree.branch, parent_id)
988
self.add_parent_tree_id(parent_id)
991
other_tree.bzrdir.retire_bzrdir()
993
def _setup_directory_is_tree_reference(self):
994
if self._branch.repository._format.supports_tree_reference:
995
self._directory_is_tree_reference = \
996
self._directory_may_be_tree_reference
998
self._directory_is_tree_reference = \
999
self._directory_is_never_tree_reference
1001
def _directory_is_never_tree_reference(self, relpath):
1004
def _directory_may_be_tree_reference(self, relpath):
1005
# as a special case, if a directory contains control files then
1006
# it's a tree reference, except that the root of the tree is not
1007
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
1008
# TODO: We could ask all the control formats whether they
1009
# recognize this directory, but at the moment there's no cheap api
1010
# to do that. Since we probably can only nest bzr checkouts and
1011
# they always use this name it's ok for now. -- mbp 20060306
1013
# FIXME: There is an unhandled case here of a subdirectory
1014
# containing .bzr but not a branch; that will probably blow up
1015
# when you try to commit it. It might happen if there is a
1016
# checkout in a subdirectory. This can be avoided by not adding
1019
@needs_tree_write_lock
1020
def extract(self, file_id, format=None):
1021
"""Extract a subtree from this tree.
1023
A new branch will be created, relative to the path for this tree.
1027
segments = osutils.splitpath(path)
1028
transport = self.branch.bzrdir.root_transport
1029
for name in segments:
1030
transport = transport.clone(name)
1031
transport.ensure_base()
1034
sub_path = self.id2path(file_id)
1035
branch_transport = mkdirs(sub_path)
1037
format = self.bzrdir.cloning_metadir()
1038
branch_transport.ensure_base()
1039
branch_bzrdir = format.initialize_on_transport(branch_transport)
1041
repo = branch_bzrdir.find_repository()
1042
except errors.NoRepositoryPresent:
1043
repo = branch_bzrdir.create_repository()
1044
if not repo.supports_rich_root():
1045
raise errors.RootNotRich()
1046
new_branch = branch_bzrdir.create_branch()
1047
new_branch.pull(self.branch)
1048
for parent_id in self.get_parent_ids():
1049
new_branch.fetch(self.branch, parent_id)
1050
tree_transport = self.bzrdir.root_transport.clone(sub_path)
1051
if tree_transport.base != branch_transport.base:
1052
tree_bzrdir = format.initialize_on_transport(tree_transport)
1053
branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
1055
tree_bzrdir = branch_bzrdir
1056
wt = tree_bzrdir.create_workingtree(NULL_REVISION)
1057
wt.set_parent_ids(self.get_parent_ids())
1058
my_inv = self.inventory
1059
child_inv = Inventory(root_id=None)
1060
new_root = my_inv[file_id]
1061
my_inv.remove_recursive_id(file_id)
1062
new_root.parent_id = None
1063
child_inv.add(new_root)
1064
self._write_inventory(my_inv)
1065
wt._write_inventory(child_inv)
1068
def _serialize(self, inventory, out_file):
1069
xml5.serializer_v5.write_inventory(self._inventory, out_file,
1072
def _deserialize(selt, in_file):
1073
return xml5.serializer_v5.read_inventory(in_file)
1076
"""Write the in memory inventory to disk."""
1077
# TODO: Maybe this should only write on dirty ?
1078
if self._control_files._lock_mode != 'w':
1079
raise errors.NotWriteLocked(self)
1081
self._serialize(self._inventory, sio)
1083
self._transport.put_file('inventory', sio,
1084
mode=self._control_files._file_mode)
1085
self._inventory_is_modified = False
1087
def _kind(self, relpath):
1088
return osutils.file_kind(self.abspath(relpath))
1090
def list_files(self, include_root=False):
1091
"""Recursively list all files as (path, class, kind, id, entry).
661
1093
Lists, but does not descend into unversioned directories.
666
1098
Skips the control directory.
668
inv = self._inventory
670
def descend(from_dir_relpath, from_dir_id, dp):
1100
# list_files is an iterator, so @needs_read_lock doesn't work properly
1101
# with it. So callers should be careful to always read_lock the tree.
1102
if not self.is_locked():
1103
raise errors.ObjectNotLocked(self)
1105
inv = self.inventory
1106
if include_root is True:
1107
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
1108
# Convert these into local objects to save lookup times
1109
pathjoin = osutils.pathjoin
1110
file_kind = self._kind
1112
# transport.base ends in a slash, we want the piece
1113
# between the last two slashes
1114
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
1116
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
1118
# directory file_id, relative path, absolute path, reverse sorted children
1119
children = os.listdir(self.basedir)
1121
# jam 20060527 The kernel sized tree seems equivalent whether we
1122
# use a deque and popleft to keep them sorted, or if we use a plain
1123
# list and just reverse() them.
1124
children = collections.deque(children)
1125
stack = [(inv.root.file_id, u'', self.basedir, children)]
1127
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
1130
f = children.popleft()
674
1131
## TODO: If we find a subdirectory with its own .bzr
675
1132
## directory, then that is a separate tree and we
676
1133
## should exclude it.
678
1135
# the bzrdir for this tree
679
if self.bzrdir.transport.base.endswith(f + '/'):
1136
if transport_base_dir == f:
683
fp = appendpath(from_dir_relpath, f)
1139
# we know that from_dir_relpath and from_dir_abspath never end in a slash
1140
# and 'f' doesn't begin with one, we can do a string op, rather
1141
# than the checks of pathjoin(), all relative paths will have an extra slash
1143
fp = from_dir_relpath + '/' + f
686
fap = appendpath(dp, f)
1146
fap = from_dir_abspath + '/' + f
688
1148
f_ie = inv.get_child(from_dir_id, f)
691
elif self.is_ignored(fp):
1151
elif self.is_ignored(fp[1:]):
1154
# we may not have found this file, because of a unicode issue
1155
f_norm, can_access = osutils.normalized_filename(f)
1156
if f == f_norm or not can_access:
1157
# No change, so treat this file normally
1160
# this file can be accessed by a normalized path
1161
# check again if it is versioned
1162
# these lines are repeated here for performance
1164
fp = from_dir_relpath + '/' + f
1165
fap = from_dir_abspath + '/' + f
1166
f_ie = inv.get_child(from_dir_id, f)
1169
elif self.is_ignored(fp[1:]):
696
1174
fk = file_kind(fap)
700
raise BzrCheckError("file %r entered as kind %r id %r, "
702
% (fap, f_ie.kind, f_ie.file_id, fk))
704
1176
# make a last minute entry
1178
yield fp[1:], c, fk, f_ie.file_id, f_ie
708
if fk == 'directory':
709
entry = TreeDirectory()
712
elif fk == 'symlink':
1181
yield fp[1:], c, fk, None, fk_entries[fk]()
1183
yield fp[1:], c, fk, None, TreeEntry()
717
yield fp, c, fk, (f_ie and f_ie.file_id), entry
719
1186
if fk != 'directory':
723
# don't descend unversioned directories
726
for ff in descend(fp, f_ie.file_id, fap):
729
for f in descend(u'', inv.root.file_id, self.basedir):
733
def move(self, from_paths, to_name):
1189
# But do this child first
1190
new_children = os.listdir(fap)
1192
new_children = collections.deque(new_children)
1193
stack.append((f_ie.file_id, fp, fap, new_children))
1194
# Break out of inner loop,
1195
# so that we start outer loop with child
1198
# if we finished all children, pop it off the stack
1201
@needs_tree_write_lock
1202
def move(self, from_paths, to_dir=None, after=False, **kwargs):
734
1203
"""Rename files.
736
to_name must exist in the inventory.
1205
to_dir must exist in the inventory.
738
If to_name exists and is a directory, the files are moved into
1207
If to_dir exists and is a directory, the files are moved into
739
1208
it, keeping their old names.
741
Note that to_name is only the last component of the new name;
1210
Note that to_dir is only the last component of the new name;
742
1211
this doesn't change the directory.
1213
For each entry in from_paths the move mode will be determined
1216
The first mode moves the file in the filesystem and updates the
1217
inventory. The second mode only updates the inventory without
1218
touching the file on the filesystem. This is the new mode introduced
1221
move uses the second mode if 'after == True' and the target is not
1222
versioned but present in the working tree.
1224
move uses the second mode if 'after == False' and the source is
1225
versioned but no longer in the working tree, and the target is not
1226
versioned but present in the working tree.
1228
move uses the first mode if 'after == False' and the source is
1229
versioned and present in the working tree, and the target is not
1230
versioned and not present in the working tree.
1232
Everything else results in an error.
744
1234
This returns a list of (from_path, to_path) pairs for each
745
1235
entry that is moved.
748
## TODO: Option to move IDs only
749
assert not isinstance(from_paths, basestring)
1240
# check for deprecated use of signature
1242
to_dir = kwargs.get('to_name', None)
1244
raise TypeError('You must supply a target directory')
1246
symbol_versioning.warn('The parameter to_name was deprecated'
1247
' in version 0.13. Use to_dir instead',
1250
# check destination directory
1251
if isinstance(from_paths, basestring):
750
1253
inv = self.inventory
751
to_abs = self.abspath(to_name)
1254
to_abs = self.abspath(to_dir)
752
1255
if not isdir(to_abs):
753
raise BzrError("destination %r is not a directory" % to_abs)
754
if not self.has_filename(to_name):
755
raise BzrError("destination %r not in working directory" % to_abs)
756
to_dir_id = inv.path2id(to_name)
757
if to_dir_id == None and to_name != '':
758
raise BzrError("destination %r is not a versioned directory" % to_name)
1256
raise errors.BzrMoveFailedError('',to_dir,
1257
errors.NotADirectory(to_abs))
1258
if not self.has_filename(to_dir):
1259
raise errors.BzrMoveFailedError('',to_dir,
1260
errors.NotInWorkingDirectory(to_dir))
1261
to_dir_id = inv.path2id(to_dir)
1262
if to_dir_id is None:
1263
raise errors.BzrMoveFailedError('',to_dir,
1264
errors.NotVersionedError(path=str(to_dir)))
759
1266
to_dir_ie = inv[to_dir_id]
760
if to_dir_ie.kind not in ('directory', 'root_directory'):
761
raise BzrError("destination %r is not a directory" % to_abs)
763
to_idpath = inv.get_idpath(to_dir_id)
766
if not self.has_filename(f):
767
raise BzrError("%r does not exist in working tree" % f)
768
f_id = inv.path2id(f)
770
raise BzrError("%r is not versioned" % f)
771
name_tail = splitpath(f)[-1]
772
dest_path = appendpath(to_name, name_tail)
773
if self.has_filename(dest_path):
774
raise BzrError("destination %r already exists" % dest_path)
775
if f_id in to_idpath:
776
raise BzrError("can't move %r to a subdirectory of itself" % f)
778
# OK, so there's a race here, it's possible that someone will
779
# create a file in this interval and then the rename might be
780
# left half-done. But we should have caught most problems.
781
orig_inv = deepcopy(self.inventory)
1267
if to_dir_ie.kind != 'directory':
1268
raise errors.BzrMoveFailedError('',to_dir,
1269
errors.NotADirectory(to_abs))
1271
# create rename entries and tuples
1272
for from_rel in from_paths:
1273
from_tail = splitpath(from_rel)[-1]
1274
from_id = inv.path2id(from_rel)
1276
raise errors.BzrMoveFailedError(from_rel,to_dir,
1277
errors.NotVersionedError(path=str(from_rel)))
1279
from_entry = inv[from_id]
1280
from_parent_id = from_entry.parent_id
1281
to_rel = pathjoin(to_dir, from_tail)
1282
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1284
from_tail=from_tail,
1285
from_parent_id=from_parent_id,
1286
to_rel=to_rel, to_tail=from_tail,
1287
to_parent_id=to_dir_id)
1288
rename_entries.append(rename_entry)
1289
rename_tuples.append((from_rel, to_rel))
1291
# determine which move mode to use. checks also for movability
1292
rename_entries = self._determine_mv_mode(rename_entries, after)
1294
original_modified = self._inventory_is_modified
784
name_tail = splitpath(f)[-1]
785
dest_path = appendpath(to_name, name_tail)
786
result.append((f, dest_path))
787
inv.rename(inv.path2id(f), to_dir_id, name_tail)
789
rename(self.abspath(f), self.abspath(dest_path))
791
raise BzrError("failed to rename %r to %r: %s" %
792
(f, dest_path, e[1]),
793
["rename rolled back"])
1297
self._inventory_is_modified = True
1298
self._move(rename_entries)
795
1300
# restore the inventory on error
796
self._set_inventory(orig_inv)
1301
self._inventory_is_modified = original_modified
798
1303
self._write_inventory(inv)
802
def rename_one(self, from_rel, to_rel):
1304
return rename_tuples
1306
def _determine_mv_mode(self, rename_entries, after=False):
1307
"""Determines for each from-to pair if both inventory and working tree
1308
or only the inventory has to be changed.
1310
Also does basic plausability tests.
1312
inv = self.inventory
1314
for rename_entry in rename_entries:
1315
# store to local variables for easier reference
1316
from_rel = rename_entry.from_rel
1317
from_id = rename_entry.from_id
1318
to_rel = rename_entry.to_rel
1319
to_id = inv.path2id(to_rel)
1320
only_change_inv = False
1322
# check the inventory for source and destination
1324
raise errors.BzrMoveFailedError(from_rel,to_rel,
1325
errors.NotVersionedError(path=str(from_rel)))
1326
if to_id is not None:
1327
raise errors.BzrMoveFailedError(from_rel,to_rel,
1328
errors.AlreadyVersionedError(path=str(to_rel)))
1330
# try to determine the mode for rename (only change inv or change
1331
# inv and file system)
1333
if not self.has_filename(to_rel):
1334
raise errors.BzrMoveFailedError(from_id,to_rel,
1335
errors.NoSuchFile(path=str(to_rel),
1336
extra="New file has not been created yet"))
1337
only_change_inv = True
1338
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
1339
only_change_inv = True
1340
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1341
only_change_inv = False
1342
elif (not self.case_sensitive
1343
and from_rel.lower() == to_rel.lower()
1344
and self.has_filename(from_rel)):
1345
only_change_inv = False
1347
# something is wrong, so lets determine what exactly
1348
if not self.has_filename(from_rel) and \
1349
not self.has_filename(to_rel):
1350
raise errors.BzrRenameFailedError(from_rel,to_rel,
1351
errors.PathsDoNotExist(paths=(str(from_rel),
1354
raise errors.RenameFailedFilesExist(from_rel, to_rel)
1355
rename_entry.only_change_inv = only_change_inv
1356
return rename_entries
1358
def _move(self, rename_entries):
1359
"""Moves a list of files.
1361
Depending on the value of the flag 'only_change_inv', the
1362
file will be moved on the file system or not.
1364
inv = self.inventory
1367
for entry in rename_entries:
1369
self._move_entry(entry)
1371
self._rollback_move(moved)
1375
def _rollback_move(self, moved):
1376
"""Try to rollback a previous move in case of an filesystem error."""
1377
inv = self.inventory
1380
self._move_entry(_RenameEntry(entry.to_rel, entry.from_id,
1381
entry.to_tail, entry.to_parent_id, entry.from_rel,
1382
entry.from_tail, entry.from_parent_id,
1383
entry.only_change_inv))
1384
except errors.BzrMoveFailedError, e:
1385
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
1386
" The working tree is in an inconsistent state."
1387
" Please consider doing a 'bzr revert'."
1388
" Error message is: %s" % e)
1390
def _move_entry(self, entry):
1391
inv = self.inventory
1392
from_rel_abs = self.abspath(entry.from_rel)
1393
to_rel_abs = self.abspath(entry.to_rel)
1394
if from_rel_abs == to_rel_abs:
1395
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
1396
"Source and target are identical.")
1398
if not entry.only_change_inv:
1400
osutils.rename(from_rel_abs, to_rel_abs)
1402
raise errors.BzrMoveFailedError(entry.from_rel,
1404
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
1406
@needs_tree_write_lock
1407
def rename_one(self, from_rel, to_rel, after=False):
803
1408
"""Rename one file.
805
1410
This can change the directory or the filename or both.
1412
rename_one has several 'modes' to work. First, it can rename a physical
1413
file and change the file_id. That is the normal mode. Second, it can
1414
only change the file_id without touching any physical file. This is
1415
the new mode introduced in version 0.15.
1417
rename_one uses the second mode if 'after == True' and 'to_rel' is not
1418
versioned but present in the working tree.
1420
rename_one uses the second mode if 'after == False' and 'from_rel' is
1421
versioned but no longer in the working tree, and 'to_rel' is not
1422
versioned but present in the working tree.
1424
rename_one uses the first mode if 'after == False' and 'from_rel' is
1425
versioned and present in the working tree, and 'to_rel' is not
1426
versioned and not present in the working tree.
1428
Everything else results in an error.
807
1430
inv = self.inventory
808
if not self.has_filename(from_rel):
809
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
810
if self.has_filename(to_rel):
811
raise BzrError("can't rename: new working file %r already exists" % to_rel)
813
file_id = inv.path2id(from_rel)
815
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
818
from_parent = entry.parent_id
819
from_name = entry.name
821
if inv.path2id(to_rel):
822
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1433
# create rename entries and tuples
1434
from_tail = splitpath(from_rel)[-1]
1435
from_id = inv.path2id(from_rel)
1437
raise errors.BzrRenameFailedError(from_rel,to_rel,
1438
errors.NotVersionedError(path=str(from_rel)))
1439
from_entry = inv[from_id]
1440
from_parent_id = from_entry.parent_id
824
1441
to_dir, to_tail = os.path.split(to_rel)
825
1442
to_dir_id = inv.path2id(to_dir)
826
if to_dir_id == None and to_dir != '':
827
raise BzrError("can't determine destination directory id for %r" % to_dir)
829
mutter("rename_one:")
830
mutter(" file_id {%s}" % file_id)
831
mutter(" from_rel %r" % from_rel)
832
mutter(" to_rel %r" % to_rel)
833
mutter(" to_dir %r" % to_dir)
834
mutter(" to_dir_id {%s}" % to_dir_id)
836
inv.rename(file_id, to_dir_id, to_tail)
838
from_abs = self.abspath(from_rel)
839
to_abs = self.abspath(to_rel)
841
rename(from_abs, to_abs)
843
inv.rename(file_id, from_parent, from_name)
844
raise BzrError("failed to rename %r to %r: %s"
845
% (from_abs, to_abs, e[1]),
846
["rename rolled back"])
1443
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1445
from_tail=from_tail,
1446
from_parent_id=from_parent_id,
1447
to_rel=to_rel, to_tail=to_tail,
1448
to_parent_id=to_dir_id)
1449
rename_entries.append(rename_entry)
1451
# determine which move mode to use. checks also for movability
1452
rename_entries = self._determine_mv_mode(rename_entries, after)
1454
# check if the target changed directory and if the target directory is
1456
if to_dir_id is None:
1457
raise errors.BzrMoveFailedError(from_rel,to_rel,
1458
errors.NotVersionedError(path=str(to_dir)))
1460
# all checks done. now we can continue with our actual work
1461
mutter('rename_one:\n'
1466
' to_dir_id {%s}\n',
1467
from_id, from_rel, to_rel, to_dir, to_dir_id)
1469
self._move(rename_entries)
847
1470
self._write_inventory(inv)
1472
class _RenameEntry(object):
1473
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
1474
to_rel, to_tail, to_parent_id, only_change_inv=False):
1475
self.from_rel = from_rel
1476
self.from_id = from_id
1477
self.from_tail = from_tail
1478
self.from_parent_id = from_parent_id
1479
self.to_rel = to_rel
1480
self.to_tail = to_tail
1481
self.to_parent_id = to_parent_id
1482
self.only_change_inv = only_change_inv
849
1484
@needs_read_lock
850
1485
def unknowns(self):
851
1486
"""Return all unknown files.
853
1488
These are files in the working directory that are not versioned or
854
1489
control files or ignored.
856
>>> from bzrlib.bzrdir import ScratchDir
857
>>> d = ScratchDir(files=['foo', 'foo~'])
858
>>> b = d.open_branch()
859
>>> tree = d.open_workingtree()
860
>>> map(str, tree.unknowns())
863
>>> list(b.unknowns())
865
>>> tree.remove('foo')
866
>>> list(b.unknowns())
869
for subp in self.extras():
870
if not self.is_ignored(subp):
873
@deprecated_method(zero_eight)
874
def iter_conflicts(self):
875
"""List all files in the tree that have text or content conflicts.
876
DEPRECATED. Use conflicts instead."""
877
return self._iter_conflicts()
1491
# force the extras method to be fully executed before returning, to
1492
# prevent race conditions with the lock
1494
[subp for subp in self.extras() if not self.is_ignored(subp)])
1496
@needs_tree_write_lock
1497
def unversion(self, file_ids):
1498
"""Remove the file ids in file_ids from the current versioned set.
1500
When a file_id is unversioned, all of its children are automatically
1503
:param file_ids: The file ids to stop versioning.
1504
:raises: NoSuchId if any fileid is not currently versioned.
1506
for file_id in file_ids:
1507
if self._inventory.has_id(file_id):
1508
self._inventory.remove_recursive_id(file_id)
1510
raise errors.NoSuchId(self, file_id)
1512
# in the future this should just set a dirty bit to wait for the
1513
# final unlock. However, until all methods of workingtree start
1514
# with the current in -memory inventory rather than triggering
1515
# a read, it is more complex - we need to teach read_inventory
1516
# to know when to read, and when to not read first... and possibly
1517
# to save first when the in memory one may be corrupted.
1518
# so for now, we just only write it if it is indeed dirty.
1520
self._write_inventory(self._inventory)
879
1522
def _iter_conflicts(self):
880
1523
conflicted = set()
881
for path in (s[0] for s in self.list_files()):
1524
for info in self.list_files():
882
1526
stem = get_conflicted_stem(path)
883
1527
if stem is None:
1056
1789
This is used to allow WorkingTree3 instances to not affect branch
1057
1790
when their last revision is set.
1059
if new_revision is None:
1792
if _mod_revision.is_null(new_revision):
1060
1793
self.branch.set_revision_history([])
1062
# current format is locked in with the branch
1063
revision_history = self.branch.revision_history()
1065
position = revision_history.index(new_revision)
1067
raise errors.NoSuchRevision(self.branch, new_revision)
1068
self.branch.set_revision_history(revision_history[:position + 1])
1796
self.branch.generate_revision_history(new_revision)
1797
except errors.NoSuchRevision:
1798
# not present in the repo - dont try to set it deeper than the tip
1799
self.branch.set_revision_history([new_revision])
1802
def _write_basis_inventory(self, xml):
1803
"""Write the basis inventory XML to the basis-inventory file"""
1804
path = self._basis_inventory_name()
1806
self._transport.put_file(path, sio,
1807
mode=self._control_files._file_mode)
1809
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1810
"""Create the text that will be saved in basis-inventory"""
1811
inventory.revision_id = revision_id
1812
return xml7.serializer_v7.write_inventory_to_string(inventory)
1071
1814
def _cache_basis_inventory(self, new_revision):
1072
1815
"""Cache new_revision as the basis inventory."""
1816
# TODO: this should allow the ready-to-use inventory to be passed in,
1817
# as commit already has that ready-to-use [while the format is the
1074
1820
# this double handles the inventory - unpack and repack -
1075
1821
# but is easier to understand. We can/should put a conditional
1076
1822
# in here based on whether the inventory is in the latest format
1077
1823
# - perhaps we should repack all inventories on a repository
1079
inv = self.branch.repository.get_inventory(new_revision)
1080
inv.revision_id = new_revision
1081
xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1083
path = self._basis_inventory_name()
1084
self._control_files.put_utf8(path, xml)
1085
except WeaveRevisionNotPresent:
1825
# the fast path is to copy the raw xml from the repository. If the
1826
# xml contains 'revision_id="', then we assume the right
1827
# revision_id is set. We must check for this full string, because a
1828
# root node id can legitimately look like 'revision_id' but cannot
1830
xml = self.branch.repository.get_inventory_xml(new_revision)
1831
firstline = xml.split('\n', 1)[0]
1832
if (not 'revision_id="' in firstline or
1833
'format="7"' not in firstline):
1834
inv = self.branch.repository.deserialise_inventory(
1836
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1837
self._write_basis_inventory(xml)
1838
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1088
1841
def read_basis_inventory(self):
1089
1842
"""Read the cached basis inventory."""
1090
1843
path = self._basis_inventory_name()
1091
return self._control_files.get_utf8(path).read()
1844
return self._transport.get_bytes(path)
1093
1846
@needs_read_lock
1094
1847
def read_working_inventory(self):
1095
"""Read the working inventory."""
1848
"""Read the working inventory.
1850
:raises errors.InventoryModified: read_working_inventory will fail
1851
when the current in memory inventory has been modified.
1853
# conceptually this should be an implementation detail of the tree.
1854
# XXX: Deprecate this.
1096
1855
# ElementTree does its own conversion from UTF-8, so open in
1098
result = bzrlib.xml5.serializer_v5.read_inventory(
1099
self._control_files.get('inventory'))
1100
self._set_inventory(result)
1857
if self._inventory_is_modified:
1858
raise errors.InventoryModified(self)
1859
result = self._deserialize(self._transport.get('inventory'))
1860
self._set_inventory(result, dirty=False)
1104
def remove(self, files, verbose=False):
1105
"""Remove nominated files from the working inventory..
1107
This does not remove their text. This does not run on XXX on what? RBC
1109
TODO: Refuse to remove modified files unless --force is given?
1111
TODO: Do something useful with directories.
1113
TODO: Should this remove the text or not? Tough call; not
1114
removing may be useful and the user can just use use rm, and
1115
is the opposite of add. Removing it is consistent with most
1116
other tools. Maybe an option.
1863
@needs_tree_write_lock
1864
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1866
"""Remove nominated files from the working inventory.
1868
:files: File paths relative to the basedir.
1869
:keep_files: If true, the files will also be kept.
1870
:force: Delete files and directories, even if they are changed and
1871
even if the directories are not empty.
1118
## TODO: Normalize names
1119
## TODO: Remove nested loops; better scalability
1120
1873
if isinstance(files, basestring):
1121
1874
files = [files]
1123
inv = self.inventory
1125
# do this before any modifications
1879
unknown_nested_files=set()
1881
def recurse_directory_to_add_files(directory):
1882
# Recurse directory and add all files
1883
# so we can check if they have changed.
1884
for parent_info, file_infos in\
1885
self.walkdirs(directory):
1886
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1887
# Is it versioned or ignored?
1888
if self.path2id(relpath) or self.is_ignored(relpath):
1889
# Add nested content for deletion.
1890
new_files.add(relpath)
1892
# Files which are not versioned and not ignored
1893
# should be treated as unknown.
1894
unknown_nested_files.add((relpath, None, kind))
1896
for filename in files:
1897
# Get file name into canonical form.
1898
abspath = self.abspath(filename)
1899
filename = self.relpath(abspath)
1900
if len(filename) > 0:
1901
new_files.add(filename)
1902
recurse_directory_to_add_files(filename)
1904
files = list(new_files)
1907
return # nothing to do
1909
# Sort needed to first handle directory content before the directory
1910
files.sort(reverse=True)
1912
# Bail out if we are going to delete files we shouldn't
1913
if not keep_files and not force:
1914
has_changed_files = len(unknown_nested_files) > 0
1915
if not has_changed_files:
1916
for (file_id, path, content_change, versioned, parent_id, name,
1917
kind, executable) in self.iter_changes(self.basis_tree(),
1918
include_unchanged=True, require_versioned=False,
1919
want_unversioned=True, specific_files=files):
1920
if versioned == (False, False):
1921
# The record is unknown ...
1922
if not self.is_ignored(path[1]):
1923
# ... but not ignored
1924
has_changed_files = True
1926
elif content_change and (kind[1] is not None):
1927
# Versioned and changed, but not deleted
1928
has_changed_files = True
1931
if has_changed_files:
1932
# Make delta show ALL applicable changes in error message.
1933
tree_delta = self.changes_from(self.basis_tree(),
1934
require_versioned=False, want_unversioned=True,
1935
specific_files=files)
1936
for unknown_file in unknown_nested_files:
1937
if unknown_file not in tree_delta.unversioned:
1938
tree_delta.unversioned.extend((unknown_file,))
1939
raise errors.BzrRemoveChangedFilesError(tree_delta)
1941
# Build inv_delta and delete files where applicaple,
1942
# do this before any modifications to inventory.
1126
1943
for f in files:
1127
fid = inv.path2id(f)
1944
fid = self.path2id(f)
1129
# TODO: Perhaps make this just a warning, and continue?
1130
# This tends to happen when
1131
raise NotVersionedError(path=f)
1132
mutter("remove inventory entry %s {%s}", quotefn(f), fid)
1134
# having remove it, it must be either ignored or unknown
1135
if self.is_ignored(f):
1139
show_status(new_status, inv[fid].kind, quotefn(f))
1142
self._write_inventory(inv)
1145
def revert(self, filenames, old_tree=None, backups=True,
1146
pb=DummyProgress()):
1147
from transform import revert
1148
from conflicts import resolve
1947
message = "%s is not versioned." % (f,)
1950
# having removed it, it must be either ignored or unknown
1951
if self.is_ignored(f):
1955
textui.show_status(new_status, self.kind(fid), f,
1958
inv_delta.append((f, None, fid, None))
1959
message = "removed %s" % (f,)
1962
abs_path = self.abspath(f)
1963
if osutils.lexists(abs_path):
1964
if (osutils.isdir(abs_path) and
1965
len(os.listdir(abs_path)) > 0):
1967
osutils.rmtree(abs_path)
1969
message = "%s is not an empty directory "\
1970
"and won't be deleted." % (f,)
1972
osutils.delete_any(abs_path)
1973
message = "deleted %s" % (f,)
1974
elif message is not None:
1975
# Only care if we haven't done anything yet.
1976
message = "%s does not exist." % (f,)
1978
# Print only one message (if any) per file.
1979
if message is not None:
1981
self.apply_inventory_delta(inv_delta)
1983
@needs_tree_write_lock
1984
def revert(self, filenames=None, old_tree=None, backups=True,
1985
pb=DummyProgress(), report_changes=False):
1986
from bzrlib.conflicts import resolve
1989
symbol_versioning.warn('Using [] to revert all files is deprecated'
1990
' as of bzr 0.91. Please use None (the default) instead.',
1991
DeprecationWarning, stacklevel=2)
1149
1992
if old_tree is None:
1150
old_tree = self.basis_tree()
1151
conflicts = revert(self, old_tree, filenames, backups, pb)
1152
if not len(filenames):
1153
self.set_pending_merges([])
1993
basis_tree = self.basis_tree()
1994
basis_tree.lock_read()
1995
old_tree = basis_tree
1156
resolve(self, filenames, ignore_misses=True)
1999
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
2001
if filenames is None and len(self.get_parent_ids()) > 1:
2003
last_revision = self.last_revision()
2004
if last_revision != NULL_REVISION:
2005
if basis_tree is None:
2006
basis_tree = self.basis_tree()
2007
basis_tree.lock_read()
2008
parent_trees.append((last_revision, basis_tree))
2009
self.set_parent_trees(parent_trees)
2012
resolve(self, filenames, ignore_misses=True, recursive=True)
2014
if basis_tree is not None:
1157
2016
return conflicts
2018
def revision_tree(self, revision_id):
2019
"""See Tree.revision_tree.
2021
WorkingTree can supply revision_trees for the basis revision only
2022
because there is only one cached inventory in the bzr directory.
2024
if revision_id == self.last_revision():
2026
xml = self.read_basis_inventory()
2027
except errors.NoSuchFile:
2031
inv = xml7.serializer_v7.read_inventory_from_string(xml)
2032
# dont use the repository revision_tree api because we want
2033
# to supply the inventory.
2034
if inv.revision_id == revision_id:
2035
return revisiontree.RevisionTree(self.branch.repository,
2037
except errors.BadInventoryFormat:
2039
# raise if there was no inventory, or if we read the wrong inventory.
2040
raise errors.NoSuchRevisionInTree(self, revision_id)
1159
2042
# XXX: This method should be deprecated in favour of taking in a proper
1160
2043
# new Inventory object.
2044
@needs_tree_write_lock
1162
2045
def set_inventory(self, new_inventory_list):
1163
2046
from bzrlib.inventory import (Inventory,
1164
2047
InventoryDirectory,
1204
2109
between multiple working trees, i.e. via shared storage, then we
1205
2110
would probably want to lock both the local tree, and the branch.
1207
# FIXME: We want to write out the hashcache only when the last lock on
1208
# this working copy is released. Peeking at the lock count is a bit
1209
# of a nasty hack; probably it's better to have a transaction object,
1210
# which can do some finalization when it's either successfully or
1211
# unsuccessfully completed. (Denys's original patch did that.)
1212
# RBC 20060206 hookinhg into transaction will couple lock and transaction
1213
# wrongly. Hookinh into unllock on the control files object is fine though.
1215
# TODO: split this per format so there is no ugly if block
1216
if self._hashcache.needs_write and (
1217
# dedicated lock files
1218
self._control_files._lock_count==1 or
1220
(self._control_files is self.branch.control_files and
1221
self._control_files._lock_count==3)):
1222
self._hashcache.write()
1223
# reverse order of locking.
1224
result = self._control_files.unlock()
1226
self.branch.unlock()
2112
raise NotImplementedError(self.unlock)
2114
def update(self, change_reporter=None, possible_transports=None):
1232
2115
"""Update a working tree along its branch.
1234
This will update the branch if its bound too, which means we have multiple trees involved:
1235
The new basis tree of the master.
1236
The old basis tree of the branch.
1237
The old basis tree of the working tree.
1238
The current working tree state.
1239
pathologically all three may be different, and non ancestors of each other.
1240
Conceptually we want to:
1241
Preserve the wt.basis->wt.state changes
1242
Transform the wt.basis to the new master basis.
1243
Apply a merge of the old branch basis to get any 'local' changes from it into the tree.
1244
Restore the wt.basis->wt.state changes.
2117
This will update the branch if its bound too, which means we have
2118
multiple trees involved:
2120
- The new basis tree of the master.
2121
- The old basis tree of the branch.
2122
- The old basis tree of the working tree.
2123
- The current working tree state.
2125
Pathologically, all three may be different, and non-ancestors of each
2126
other. Conceptually we want to:
2128
- Preserve the wt.basis->wt.state changes
2129
- Transform the wt.basis to the new master basis.
2130
- Apply a merge of the old branch basis to get any 'local' changes from
2132
- Restore the wt.basis->wt.state changes.
1246
2134
There isn't a single operation at the moment to do that, so we:
1247
Merge current state -> basis tree of the master w.r.t. the old tree basis.
1248
Do a 'normal' merge of the old branch basis if it is relevant.
1250
old_tip = self.branch.update()
1251
if old_tip is not None:
1252
self.add_pending_merge(old_tip)
1253
self.branch.lock_read()
1256
if self.last_revision() != self.branch.last_revision():
1257
# merge tree state up to new branch tip.
1258
basis = self.basis_tree()
2135
- Merge current state -> basis tree of the master w.r.t. the old tree
2137
- Do a 'normal' merge of the old branch basis if it is relevant.
2139
if self.branch.get_bound_location() is not None:
2141
update_branch = True
2143
self.lock_tree_write()
2144
update_branch = False
2147
old_tip = self.branch.update(possible_transports)
2150
return self._update_tree(old_tip, change_reporter)
2154
@needs_tree_write_lock
2155
def _update_tree(self, old_tip=None, change_reporter=None):
2156
"""Update a tree to the master branch.
2158
:param old_tip: if supplied, the previous tip revision the branch,
2159
before it was changed to the master branch's tip.
2161
# here if old_tip is not None, it is the old tip of the branch before
2162
# it was updated from the master branch. This should become a pending
2163
# merge in the working tree to preserve the user existing work. we
2164
# cant set that until we update the working trees last revision to be
2165
# one from the new branch, because it will just get absorbed by the
2166
# parent de-duplication logic.
2168
# We MUST save it even if an error occurs, because otherwise the users
2169
# local work is unreferenced and will appear to have been lost.
2173
last_rev = self.get_parent_ids()[0]
2175
last_rev = _mod_revision.NULL_REVISION
2176
if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
2177
# merge tree state up to new branch tip.
2178
basis = self.basis_tree()
1259
2181
to_tree = self.branch.basis_tree()
1260
result += merge_inner(self.branch,
2182
if basis.inventory.root is None:
2183
self.set_root_id(to_tree.get_root_id())
2185
result += merge.merge_inner(
1264
self.set_last_revision(self.branch.last_revision())
1265
if old_tip and old_tip != self.last_revision():
1266
# our last revision was not the prior branch last reivison
1267
# and we have converted that last revision to a pending merge.
1268
# base is somewhere between the branch tip now
1269
# and the now pending merge
1270
from bzrlib.revision import common_ancestor
1272
base_rev_id = common_ancestor(self.branch.last_revision(),
1274
self.branch.repository)
1275
except errors.NoCommonAncestor:
1277
base_tree = self.branch.repository.revision_tree(base_rev_id)
1278
other_tree = self.branch.repository.revision_tree(old_tip)
1279
result += merge_inner(self.branch,
1285
self.branch.unlock()
2190
change_reporter=change_reporter)
2193
# TODO - dedup parents list with things merged by pull ?
2194
# reuse the tree we've updated to to set the basis:
2195
parent_trees = [(self.branch.last_revision(), to_tree)]
2196
merges = self.get_parent_ids()[1:]
2197
# Ideally we ask the tree for the trees here, that way the working
2198
# tree can decide whether to give us teh entire tree or give us a
2199
# lazy initialised tree. dirstate for instance will have the trees
2200
# in ram already, whereas a last-revision + basis-inventory tree
2201
# will not, but also does not need them when setting parents.
2202
for parent in merges:
2203
parent_trees.append(
2204
(parent, self.branch.repository.revision_tree(parent)))
2205
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2206
parent_trees.append(
2207
(old_tip, self.branch.repository.revision_tree(old_tip)))
2208
self.set_parent_trees(parent_trees)
2209
last_rev = parent_trees[0][0]
2211
# the working tree had the same last-revision as the master
2212
# branch did. We may still have pivot local work from the local
2213
# branch into old_tip:
2214
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2215
self.add_parent_tree_id(old_tip)
2216
if (old_tip is not None and not _mod_revision.is_null(old_tip)
2217
and old_tip != last_rev):
2218
# our last revision was not the prior branch last revision
2219
# and we have converted that last revision to a pending merge.
2220
# base is somewhere between the branch tip now
2221
# and the now pending merge
2223
# Since we just modified the working tree and inventory, flush out
2224
# the current state, before we modify it again.
2225
# TODO: jam 20070214 WorkingTree3 doesn't require this, dirstate
2226
# requires it only because TreeTransform directly munges the
2227
# inventory and calls tree._write_inventory(). Ultimately we
2228
# should be able to remove this extra flush.
2230
graph = self.branch.repository.get_graph()
2231
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
2233
base_tree = self.branch.repository.revision_tree(base_rev_id)
2234
other_tree = self.branch.repository.revision_tree(old_tip)
2235
result += merge.merge_inner(
2240
change_reporter=change_reporter)
2243
def _write_hashcache_if_dirty(self):
2244
"""Write out the hashcache if it is dirty."""
2245
if self._hashcache.needs_write:
2247
self._hashcache.write()
2249
if e.errno not in (errno.EPERM, errno.EACCES):
2251
# TODO: jam 20061219 Should this be a warning? A single line
2252
# warning might be sufficient to let the user know what
2254
mutter('Could not write hashcache for %s\nError: %s',
2255
self._hashcache.cache_file_name(), e)
2257
@needs_tree_write_lock
1288
2258
def _write_inventory(self, inv):
1289
2259
"""Write inventory as the current inventory."""
1291
bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
1293
self._control_files.put('inventory', sio)
1294
self._set_inventory(inv)
1295
mutter('wrote working inventory')
2260
self._set_inventory(inv, dirty=True)
1297
2263
def set_conflicts(self, arg):
1298
raise UnsupportedOperation(self.set_conflicts, self)
2264
raise errors.UnsupportedOperation(self.set_conflicts, self)
2266
def add_conflicts(self, arg):
2267
raise errors.UnsupportedOperation(self.add_conflicts, self)
1300
2269
@needs_read_lock
1301
2270
def conflicts(self):
1302
conflicts = ConflictList()
2271
conflicts = _mod_conflicts.ConflictList()
1303
2272
for conflicted in self._iter_conflicts():
1306
2275
if file_kind(self.abspath(conflicted)) != "file":
1309
if e.errno == errno.ENOENT:
2277
except errors.NoSuchFile:
1313
2279
if text is True:
1314
2280
for suffix in ('.THIS', '.OTHER'):
1316
2282
kind = file_kind(self.abspath(conflicted+suffix))
1318
if e.errno == errno.ENOENT:
2285
except errors.NoSuchFile:
1326
2289
ctype = {True: 'text conflict', False: 'contents conflict'}[text]
1327
conflicts.append(Conflict.factory(ctype, path=conflicted,
2290
conflicts.append(_mod_conflicts.Conflict.factory(ctype,
1328
2292
file_id=self.path2id(conflicted)))
1329
2293
return conflicts
2295
def walkdirs(self, prefix=""):
2296
"""Walk the directories of this tree.
2298
returns a generator which yields items in the form:
2299
((curren_directory_path, fileid),
2300
[(file1_path, file1_name, file1_kind, (lstat), file1_id,
2303
This API returns a generator, which is only valid during the current
2304
tree transaction - within a single lock_read or lock_write duration.
2306
If the tree is not locked, it may cause an error to be raised,
2307
depending on the tree implementation.
2309
disk_top = self.abspath(prefix)
2310
if disk_top.endswith('/'):
2311
disk_top = disk_top[:-1]
2312
top_strip_len = len(disk_top) + 1
2313
inventory_iterator = self._walkdirs(prefix)
2314
disk_iterator = osutils.walkdirs(disk_top, prefix)
2316
current_disk = disk_iterator.next()
2317
disk_finished = False
2319
if not (e.errno == errno.ENOENT or
2320
(sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
2323
disk_finished = True
2325
current_inv = inventory_iterator.next()
2326
inv_finished = False
2327
except StopIteration:
2330
while not inv_finished or not disk_finished:
2332
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
2333
cur_disk_dir_content) = current_disk
2335
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
2336
cur_disk_dir_content) = ((None, None), None)
2337
if not disk_finished:
2338
# strip out .bzr dirs
2339
if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
2340
len(cur_disk_dir_content) > 0):
2341
# osutils.walkdirs can be made nicer -
2342
# yield the path-from-prefix rather than the pathjoined
2344
bzrdir_loc = bisect_left(cur_disk_dir_content,
2346
if (bzrdir_loc < len(cur_disk_dir_content)
2347
and cur_disk_dir_content[bzrdir_loc][0] == '.bzr'):
2348
# we dont yield the contents of, or, .bzr itself.
2349
del cur_disk_dir_content[bzrdir_loc]
2351
# everything is unknown
2354
# everything is missing
2357
direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
2359
# disk is before inventory - unknown
2360
dirblock = [(relpath, basename, kind, stat, None, None) for
2361
relpath, basename, kind, stat, top_path in
2362
cur_disk_dir_content]
2363
yield (cur_disk_dir_relpath, None), dirblock
2365
current_disk = disk_iterator.next()
2366
except StopIteration:
2367
disk_finished = True
2369
# inventory is before disk - missing.
2370
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
2371
for relpath, basename, dkind, stat, fileid, kind in
2373
yield (current_inv[0][0], current_inv[0][1]), dirblock
2375
current_inv = inventory_iterator.next()
2376
except StopIteration:
2379
# versioned present directory
2380
# merge the inventory and disk data together
2382
for relpath, subiterator in itertools.groupby(sorted(
2383
current_inv[1] + cur_disk_dir_content,
2384
key=operator.itemgetter(0)), operator.itemgetter(1)):
2385
path_elements = list(subiterator)
2386
if len(path_elements) == 2:
2387
inv_row, disk_row = path_elements
2388
# versioned, present file
2389
dirblock.append((inv_row[0],
2390
inv_row[1], disk_row[2],
2391
disk_row[3], inv_row[4],
2393
elif len(path_elements[0]) == 5:
2395
dirblock.append((path_elements[0][0],
2396
path_elements[0][1], path_elements[0][2],
2397
path_elements[0][3], None, None))
2398
elif len(path_elements[0]) == 6:
2399
# versioned, absent file.
2400
dirblock.append((path_elements[0][0],
2401
path_elements[0][1], 'unknown', None,
2402
path_elements[0][4], path_elements[0][5]))
2404
raise NotImplementedError('unreachable code')
2405
yield current_inv[0], dirblock
2407
current_inv = inventory_iterator.next()
2408
except StopIteration:
2411
current_disk = disk_iterator.next()
2412
except StopIteration:
2413
disk_finished = True
2415
def _walkdirs(self, prefix=""):
2416
"""Walk the directories of this tree.
2418
:prefix: is used as the directrory to start with.
2419
returns a generator which yields items in the form:
2420
((curren_directory_path, fileid),
2421
[(file1_path, file1_name, file1_kind, None, file1_id,
2424
_directory = 'directory'
2425
# get the root in the inventory
2426
inv = self.inventory
2427
top_id = inv.path2id(prefix)
2431
pending = [(prefix, '', _directory, None, top_id, None)]
2434
currentdir = pending.pop()
2435
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2436
top_id = currentdir[4]
2438
relroot = currentdir[0] + '/'
2441
# FIXME: stash the node in pending
2443
if entry.kind == 'directory':
2444
for name, child in entry.sorted_children():
2445
dirblock.append((relroot + name, name, child.kind, None,
2446
child.file_id, child.kind
2448
yield (currentdir[0], entry.file_id), dirblock
2449
# push the user specified dirs from dirblock
2450
for dir in reversed(dirblock):
2451
if dir[2] == _directory:
2454
@needs_tree_write_lock
2455
def auto_resolve(self):
2456
"""Automatically resolve text conflicts according to contents.
2458
Only text conflicts are auto_resolvable. Files with no conflict markers
2459
are considered 'resolved', because bzr always puts conflict markers
2460
into files that have text conflicts. The corresponding .THIS .BASE and
2461
.OTHER files are deleted, as per 'resolve'.
2462
:return: a tuple of ConflictLists: (un_resolved, resolved).
2464
un_resolved = _mod_conflicts.ConflictList()
2465
resolved = _mod_conflicts.ConflictList()
2466
conflict_re = re.compile('^(<{7}|={7}|>{7})')
2467
for conflict in self.conflicts():
2468
if (conflict.typestring != 'text conflict' or
2469
self.kind(conflict.file_id) != 'file'):
2470
un_resolved.append(conflict)
2472
my_file = open(self.id2abspath(conflict.file_id), 'rb')
2474
for line in my_file:
2475
if conflict_re.search(line):
2476
un_resolved.append(conflict)
2479
resolved.append(conflict)
2482
resolved.remove_files(self)
2483
self.set_conflicts(un_resolved)
2484
return un_resolved, resolved
2488
tree_basis = self.basis_tree()
2489
tree_basis.lock_read()
2491
repo_basis = self.branch.repository.revision_tree(
2492
self.last_revision())
2493
if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2494
raise errors.BzrCheckError(
2495
"Mismatched basis inventory content.")
2500
def _validate(self):
2501
"""Validate internal structures.
2503
This is meant mostly for the test suite. To give it a chance to detect
2504
corruption after actions have occurred. The default implementation is a
2507
:return: None. An exception should be raised if there is an error.
2512
def _get_rules_searcher(self, default_searcher):
2513
"""See Tree._get_rules_searcher."""
2514
if self._rules_searcher is None:
2515
self._rules_searcher = super(WorkingTree,
2516
self)._get_rules_searcher(default_searcher)
2517
return self._rules_searcher
2519
def get_shelf_manager(self):
2520
"""Return the ShelfManager for this WorkingTree."""
2521
from bzrlib.shelf import ShelfManager
2522
return ShelfManager(self, self._transport)
2525
class WorkingTree2(WorkingTree):
2526
"""This is the Format 2 working tree.
2528
This was the first weave based working tree.
2529
- uses os locks for locking.
2530
- uses the branch last-revision.
2533
def __init__(self, *args, **kwargs):
2534
super(WorkingTree2, self).__init__(*args, **kwargs)
2535
# WorkingTree2 has more of a constraint that self._inventory must
2536
# exist. Because this is an older format, we don't mind the overhead
2537
# caused by the extra computation here.
2539
# Newer WorkingTree's should only have self._inventory set when they
2541
if self._inventory is None:
2542
self.read_working_inventory()
2544
def lock_tree_write(self):
2545
"""See WorkingTree.lock_tree_write().
2547
In Format2 WorkingTrees we have a single lock for the branch and tree
2548
so lock_tree_write() degrades to lock_write().
2550
self.branch.lock_write()
2552
return self._control_files.lock_write()
2554
self.branch.unlock()
2558
# do non-implementation specific cleanup
2561
# we share control files:
2562
if self._control_files._lock_count == 3:
2563
# _inventory_is_modified is always False during a read lock.
2564
if self._inventory_is_modified:
2566
self._write_hashcache_if_dirty()
2568
# reverse order of locking.
2570
return self._control_files.unlock()
2572
self.branch.unlock()
1332
2575
class WorkingTree3(WorkingTree):
1333
2576
"""This is the Format 3 working tree.