311
331
def read_working_inventory(self):
312
332
"""Read the working inventory."""
313
333
before = time.time()
314
334
# ElementTree does its own conversion from UTF-8, so open in
318
inv = Inventory.read_xml(self.controlfile('inventory', 'rb'))
319
mutter("loaded inventory of %d items in %f"
320
% (len(inv), time.time() - before))
336
inv = Inventory.read_xml(self.controlfile('inventory', 'rb'))
337
mutter("loaded inventory of %d items in %f"
338
% (len(inv), time.time() - before))
326
342
def _write_inventory(self, inv):
386
403
assert(len(ids) == len(files))
390
inv = self.read_working_inventory()
391
for f,file_id in zip(files, ids):
392
if is_control_file(f):
393
raise BzrError("cannot add control file %s" % quotefn(f))
398
raise BzrError("cannot add top-level %r" % f)
400
fullpath = os.path.normpath(self.abspath(f))
403
kind = file_kind(fullpath)
405
# maybe something better?
406
raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
408
if kind != 'file' and kind != 'directory':
409
raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
412
file_id = gen_file_id(f)
413
inv.add_path(f, kind=kind, file_id=file_id)
416
show_status('A', kind, quotefn(f))
418
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
420
self._write_inventory(inv)
405
inv = self.read_working_inventory()
406
for f,file_id in zip(files, ids):
407
if is_control_file(f):
408
raise BzrError("cannot add control file %s" % quotefn(f))
413
raise BzrError("cannot add top-level %r" % f)
415
fullpath = os.path.normpath(self.abspath(f))
418
kind = file_kind(fullpath)
420
# maybe something better?
421
raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
423
if kind != 'file' and kind != 'directory':
424
raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
427
file_id = gen_file_id(f)
428
inv.add_path(f, kind=kind, file_id=file_id)
431
show_status('A', kind, quotefn(f))
433
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
435
self._write_inventory(inv)
425
438
def print_file(self, file, revno):
426
439
"""Print `file` to stdout."""
429
tree = self.revision_tree(self.lookup_revision(revno))
430
# use inventory as it was in that revision
431
file_id = tree.inventory.path2id(file)
433
raise BzrError("%r is not present in revision %d" % (file, revno))
434
tree.print_file(file_id)
440
tree = self.revision_tree(self.lookup_revision(revno))
441
# use inventory as it was in that revision
442
file_id = tree.inventory.path2id(file)
444
raise BzrError("%r is not present in revision %d" % (file, revno))
445
tree.print_file(file_id)
439
449
def remove(self, files, verbose=False):
440
450
"""Mark nominated files for removal from the inventory.
455
465
if isinstance(files, types.StringTypes):
461
tree = self.working_tree()
464
# do this before any modifications
468
raise BzrError("cannot remove unversioned file %s" % quotefn(f))
469
mutter("remove inventory entry %s {%s}" % (quotefn(f), fid))
471
# having remove it, it must be either ignored or unknown
472
if tree.is_ignored(f):
476
show_status(new_status, inv[fid].kind, quotefn(f))
479
self._write_inventory(inv)
484
# FIXME: this doesn't need to be a branch method
468
tree = self.working_tree()
471
# do this before any modifications
475
raise BzrError("cannot remove unversioned file %s" % quotefn(f))
476
mutter("remove inventory entry %s {%s}" % (quotefn(f), fid))
478
# having remove it, it must be either ignored or unknown
479
if tree.is_ignored(f):
483
show_status(new_status, inv[fid].kind, quotefn(f))
486
self._write_inventory(inv)
485
489
def set_inventory(self, new_inventory_list):
486
490
inv = Inventory()
487
491
for path, file_id, parent, kind in new_inventory_list:
555
559
return self.get_inventory(self.get_revision(revision_id).inventory_id)
558
563
def revision_history(self):
559
564
"""Return sequence of revision hashes on to this branch.
561
566
>>> ScratchBranch().revision_history()
566
return [l.rstrip('\r\n') for l in
567
self.controlfile('revision-history', 'r').readlines()]
572
def common_ancestor(self, other, self_revno=None, other_revno=None):
575
>>> sb = ScratchBranch(files=['foo', 'foo~'])
576
>>> sb.common_ancestor(sb) == (None, None)
578
>>> commit.commit(sb, "Committing first revision", verbose=False)
579
>>> sb.common_ancestor(sb)[0]
581
>>> clone = sb.clone()
582
>>> commit.commit(sb, "Committing second revision", verbose=False)
583
>>> sb.common_ancestor(sb)[0]
585
>>> sb.common_ancestor(clone)[0]
587
>>> commit.commit(clone, "Committing divergent second revision",
589
>>> sb.common_ancestor(clone)[0]
591
>>> sb.common_ancestor(clone) == clone.common_ancestor(sb)
593
>>> sb.common_ancestor(sb) != clone.common_ancestor(clone)
595
>>> clone2 = sb.clone()
596
>>> sb.common_ancestor(clone2)[0]
598
>>> sb.common_ancestor(clone2, self_revno=1)[0]
600
>>> sb.common_ancestor(clone2, other_revno=1)[0]
603
my_history = self.revision_history()
604
other_history = other.revision_history()
605
if self_revno is None:
606
self_revno = len(my_history)
607
if other_revno is None:
608
other_revno = len(other_history)
609
indices = range(min((self_revno, other_revno)))
612
if my_history[r] == other_history[r]:
613
return r+1, my_history[r]
569
return [l.rstrip('\r\n') for l in self.controlfile('revision-history', 'r').readlines()]
616
572
def enum_history(self, direction):
617
573
"""Return (revno, revision_id) for history of branch.
657
def missing_revisions(self, other):
659
If self and other have not diverged, return a list of the revisions
660
present in other, but missing from self.
662
>>> from bzrlib.commit import commit
663
>>> bzrlib.trace.silent = True
664
>>> br1 = ScratchBranch()
665
>>> br2 = ScratchBranch()
666
>>> br1.missing_revisions(br2)
668
>>> commit(br2, "lala!", rev_id="REVISION-ID-1")
669
>>> br1.missing_revisions(br2)
671
>>> br2.missing_revisions(br1)
673
>>> commit(br1, "lala!", rev_id="REVISION-ID-1")
674
>>> br1.missing_revisions(br2)
676
>>> commit(br2, "lala!", rev_id="REVISION-ID-2A")
677
>>> br1.missing_revisions(br2)
679
>>> commit(br1, "lala!", rev_id="REVISION-ID-2B")
680
>>> br1.missing_revisions(br2)
681
Traceback (most recent call last):
682
DivergedBranches: These branches have diverged.
684
self_history = self.revision_history()
685
self_len = len(self_history)
686
other_history = other.revision_history()
687
other_len = len(other_history)
688
common_index = min(self_len, other_len) -1
689
if common_index >= 0 and \
690
self_history[common_index] != other_history[common_index]:
691
raise DivergedBranches(self, other)
692
if self_len < other_len:
693
return other_history[self_len:]
697
def update_revisions(self, other):
698
"""If self and other have not diverged, ensure self has all the
701
>>> from bzrlib.commit import commit
702
>>> bzrlib.trace.silent = True
703
>>> br1 = ScratchBranch(files=['foo', 'bar'])
706
>>> commit(br1, "lala!", rev_id="REVISION-ID-1", verbose=False)
707
>>> br2 = ScratchBranch()
708
>>> br2.update_revisions(br1)
712
>>> br2.revision_history()
714
>>> br2.update_revisions(br1)
718
>>> br1.text_store.total_size() == br2.text_store.total_size()
721
revision_ids = self.missing_revisions(other)
722
revisions = [other.get_revision(f) for f in revision_ids]
723
needed_texts = sets.Set()
724
for rev in revisions:
725
inv = other.get_inventory(str(rev.inventory_id))
726
for key, entry in inv.iter_entries():
727
if entry.text_id is None:
729
if entry.text_id not in self.text_store:
730
needed_texts.add(entry.text_id)
731
count = self.text_store.copy_multi(other.text_store, needed_texts)
732
print "Added %d texts." % count
733
inventory_ids = [ f.inventory_id for f in revisions ]
734
count = self.inventory_store.copy_multi(other.inventory_store,
736
print "Added %d inventories." % count
737
revision_ids = [ f.revision_id for f in revisions]
738
count = self.revision_store.copy_multi(other.revision_store,
740
for revision_id in revision_ids:
741
self.append_revision(revision_id)
742
print "Added %d revisions." % count
745
613
def commit(self, *args, **kw):
747
615
from bzrlib.commit import commit
796
665
def rename_one(self, from_rel, to_rel):
797
666
"""Rename one file.
799
668
This can change the directory or the filename or both.
670
tree = self.working_tree()
672
if not tree.has_filename(from_rel):
673
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
674
if tree.has_filename(to_rel):
675
raise BzrError("can't rename: new working file %r already exists" % to_rel)
677
file_id = inv.path2id(from_rel)
679
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
681
if inv.path2id(to_rel):
682
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
684
to_dir, to_tail = os.path.split(to_rel)
685
to_dir_id = inv.path2id(to_dir)
686
if to_dir_id == None and to_dir != '':
687
raise BzrError("can't determine destination directory id for %r" % to_dir)
689
mutter("rename_one:")
690
mutter(" file_id {%s}" % file_id)
691
mutter(" from_rel %r" % from_rel)
692
mutter(" to_rel %r" % to_rel)
693
mutter(" to_dir %r" % to_dir)
694
mutter(" to_dir_id {%s}" % to_dir_id)
696
inv.rename(file_id, to_dir_id, to_tail)
698
print "%s => %s" % (from_rel, to_rel)
700
from_abs = self.abspath(from_rel)
701
to_abs = self.abspath(to_rel)
803
tree = self.working_tree()
805
if not tree.has_filename(from_rel):
806
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
807
if tree.has_filename(to_rel):
808
raise BzrError("can't rename: new working file %r already exists" % to_rel)
810
file_id = inv.path2id(from_rel)
812
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
814
if inv.path2id(to_rel):
815
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
817
to_dir, to_tail = os.path.split(to_rel)
818
to_dir_id = inv.path2id(to_dir)
819
if to_dir_id == None and to_dir != '':
820
raise BzrError("can't determine destination directory id for %r" % to_dir)
822
mutter("rename_one:")
823
mutter(" file_id {%s}" % file_id)
824
mutter(" from_rel %r" % from_rel)
825
mutter(" to_rel %r" % to_rel)
826
mutter(" to_dir %r" % to_dir)
827
mutter(" to_dir_id {%s}" % to_dir_id)
829
inv.rename(file_id, to_dir_id, to_tail)
831
print "%s => %s" % (from_rel, to_rel)
833
from_abs = self.abspath(from_rel)
834
to_abs = self.abspath(to_rel)
836
os.rename(from_abs, to_abs)
838
raise BzrError("failed to rename %r to %r: %s"
839
% (from_abs, to_abs, e[1]),
840
["rename rolled back"])
842
self._write_inventory(inv)
703
os.rename(from_abs, to_abs)
705
raise BzrError("failed to rename %r to %r: %s"
706
% (from_abs, to_abs, e[1]),
707
["rename rolled back"])
709
self._write_inventory(inv)
847
714
def move(self, from_paths, to_name):
855
722
Note that to_name is only the last component of the new name;
856
723
this doesn't change the directory.
860
## TODO: Option to move IDs only
861
assert not isinstance(from_paths, basestring)
862
tree = self.working_tree()
864
to_abs = self.abspath(to_name)
865
if not isdir(to_abs):
866
raise BzrError("destination %r is not a directory" % to_abs)
867
if not tree.has_filename(to_name):
868
raise BzrError("destination %r not in working directory" % to_abs)
869
to_dir_id = inv.path2id(to_name)
870
if to_dir_id == None and to_name != '':
871
raise BzrError("destination %r is not a versioned directory" % to_name)
872
to_dir_ie = inv[to_dir_id]
873
if to_dir_ie.kind not in ('directory', 'root_directory'):
874
raise BzrError("destination %r is not a directory" % to_abs)
876
to_idpath = inv.get_idpath(to_dir_id)
879
if not tree.has_filename(f):
880
raise BzrError("%r does not exist in working tree" % f)
881
f_id = inv.path2id(f)
883
raise BzrError("%r is not versioned" % f)
884
name_tail = splitpath(f)[-1]
885
dest_path = appendpath(to_name, name_tail)
886
if tree.has_filename(dest_path):
887
raise BzrError("destination %r already exists" % dest_path)
888
if f_id in to_idpath:
889
raise BzrError("can't move %r to a subdirectory of itself" % f)
891
# OK, so there's a race here, it's possible that someone will
892
# create a file in this interval and then the rename might be
893
# left half-done. But we should have caught most problems.
896
name_tail = splitpath(f)[-1]
897
dest_path = appendpath(to_name, name_tail)
898
print "%s => %s" % (f, dest_path)
899
inv.rename(inv.path2id(f), to_dir_id, name_tail)
901
os.rename(self.abspath(f), self.abspath(dest_path))
903
raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
904
["rename rolled back"])
906
self._write_inventory(inv)
725
## TODO: Option to move IDs only
726
assert not isinstance(from_paths, basestring)
727
tree = self.working_tree()
729
to_abs = self.abspath(to_name)
730
if not isdir(to_abs):
731
raise BzrError("destination %r is not a directory" % to_abs)
732
if not tree.has_filename(to_name):
733
raise BzrError("destination %r not in working directory" % to_abs)
734
to_dir_id = inv.path2id(to_name)
735
if to_dir_id == None and to_name != '':
736
raise BzrError("destination %r is not a versioned directory" % to_name)
737
to_dir_ie = inv[to_dir_id]
738
if to_dir_ie.kind not in ('directory', 'root_directory'):
739
raise BzrError("destination %r is not a directory" % to_abs)
741
to_idpath = inv.get_idpath(to_dir_id)
744
if not tree.has_filename(f):
745
raise BzrError("%r does not exist in working tree" % f)
746
f_id = inv.path2id(f)
748
raise BzrError("%r is not versioned" % f)
749
name_tail = splitpath(f)[-1]
750
dest_path = appendpath(to_name, name_tail)
751
if tree.has_filename(dest_path):
752
raise BzrError("destination %r already exists" % dest_path)
753
if f_id in to_idpath:
754
raise BzrError("can't move %r to a subdirectory of itself" % f)
756
# OK, so there's a race here, it's possible that someone will
757
# create a file in this interval and then the rename might be
758
# left half-done. But we should have caught most problems.
761
name_tail = splitpath(f)[-1]
762
dest_path = appendpath(to_name, name_tail)
763
print "%s => %s" % (f, dest_path)
764
inv.rename(inv.path2id(f), to_dir_id, name_tail)
766
os.rename(self.abspath(f), self.abspath(dest_path))
768
raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
769
["rename rolled back"])
771
self._write_inventory(inv)