23
23
from bzrlib.osutils import isdir, quotefn, compact_date, rand_bytes, \
25
25
sha_file, appendpath, file_kind
27
26
from bzrlib.errors import BzrError, InvalidRevisionNumber, InvalidRevisionId
28
27
import bzrlib.errors
29
28
from bzrlib.textui import show_status
128
120
head, tail = os.path.split(f)
130
122
# reached the root, whatever that may be
131
raise bzrlib.errors.NotBranchError('%s is not in a branch' % orig_f)
123
raise BzrError('%r is not in a branch' % orig_f)
136
# XXX: move into bzrlib.errors; subclass BzrError
137
126
class DivergedBranches(Exception):
138
127
def __init__(self, branch1, branch2):
139
128
self.branch1 = branch1
141
130
Exception.__init__(self, "These branches have diverged.")
133
class NoSuchRevision(BzrError):
134
def __init__(self, branch, revision):
136
self.revision = revision
137
msg = "Branch %s has no revision %d" % (branch, revision)
138
BzrError.__init__(self, msg)
144
141
######################################################################
258
259
self._lock = None
259
260
self._lock_mode = self._lock_count = None
261
263
def abspath(self, name):
262
264
"""Return absolute filename for something in the branch"""
263
265
return os.path.join(self.base, name)
265
268
def relpath(self, path):
266
269
"""Return path relative to this branch of something inside it.
268
271
Raises an error if path is not in this branch."""
269
272
return _relpath(self.base, path)
271
275
def controlfilename(self, file_or_path):
272
276
"""Return location relative to branch."""
273
277
if isinstance(file_or_path, basestring):
318
324
self.controlfile(f, 'w').write('')
319
325
mutter('created control directory in ' + self.base)
321
# if we want per-tree root ids then this is the place to set
322
# them; they're not needed for now and so ommitted for
324
pack_xml(Inventory(), self.controlfile('inventory','w'))
327
pack_xml(Inventory(gen_root_id()), self.controlfile('inventory','w'))
326
330
def _check_format(self):
327
331
"""Check this branch format is supported.
661
665
from bzrlib.inventory import Inventory
662
666
from bzrlib.xml import unpack_xml
664
return unpack_xml(Inventory, self.get_inventory_xml(inventory_id))
667
def get_inventory_xml(self, inventory_id):
668
"""Get inventory XML as a file object."""
669
return self.inventory_store[inventory_id]
668
return unpack_xml(Inventory, self.inventory_store[inventory_id])
672
671
def get_inventory_sha1(self, inventory_id):
673
672
"""Return the sha1 hash of the inventory entry
675
return sha_file(self.get_inventory_xml(inventory_id))
674
return sha_file(self.inventory_store[inventory_id])
678
677
def get_revision_inventory(self, revision_id):
767
def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
766
def missing_revisions(self, other, stop_revision=None):
769
768
If self and other have not diverged, return a list of the revisions
770
769
present in other, but missing from self.
803
802
if stop_revision is None:
804
803
stop_revision = other_len
805
804
elif stop_revision > other_len:
806
raise bzrlib.errors.NoSuchRevision(self, stop_revision)
805
raise NoSuchRevision(self, stop_revision)
808
807
return other_history[self_len:stop_revision]
811
810
def update_revisions(self, other, stop_revision=None):
812
811
"""Pull in all new revisions from other branch.
813
>>> from bzrlib.commit import commit
814
>>> bzrlib.trace.silent = True
815
>>> br1 = ScratchBranch(files=['foo', 'bar'])
818
>>> commit(br1, "lala!", rev_id="REVISION-ID-1", verbose=False)
819
>>> br2 = ScratchBranch()
820
>>> br2.update_revisions(br1)
824
>>> br2.revision_history()
826
>>> br2.update_revisions(br1)
830
>>> br1.text_store.total_size() == br2.text_store.total_size()
814
from bzrlib.fetch import greedy_fetch
816
pb = bzrlib.ui.ui_factory.progress_bar()
833
from bzrlib.progress import ProgressBar
817
837
pb.update('comparing histories')
819
838
revision_ids = self.missing_revisions(other, stop_revision)
821
if len(revision_ids) > 0:
822
count = greedy_fetch(self, other, revision_ids[-1], pb)[0]
825
self.append_revision(*revision_ids)
826
## note("Added %d revisions." % count)
829
def install_revisions(self, other, revision_ids, pb):
830
840
if hasattr(other.revision_store, "prefetch"):
831
841
other.revision_store.prefetch(revision_ids)
832
842
if hasattr(other.inventory_store, "prefetch"):
833
843
inventory_ids = [other.get_revision(r).inventory_id
834
844
for r in revision_ids]
835
845
other.inventory_store.prefetch(inventory_ids)
838
pb = bzrlib.ui.ui_factory.progress_bar()
841
848
needed_texts = set()
845
for i, rev_id in enumerate(revision_ids):
846
pb.update('fetching revision', i+1, len(revision_ids))
848
rev = other.get_revision(rev_id)
849
except bzrlib.errors.NoSuchRevision:
850
for rev_id in revision_ids:
852
pb.update('fetching revision', i, len(revision_ids))
853
rev = other.get_revision(rev_id)
853
854
revisions.append(rev)
854
855
inv = other.get_inventory(str(rev.inventory_id))
855
856
for key, entry in inv.iter_entries():
863
count, cp_fail = self.text_store.copy_multi(other.text_store,
865
#print "Added %d texts." % count
864
count = self.text_store.copy_multi(other.text_store, needed_texts)
865
print "Added %d texts." % count
866
866
inventory_ids = [ f.inventory_id for f in revisions ]
867
count, cp_fail = self.inventory_store.copy_multi(other.inventory_store,
869
#print "Added %d inventories." % count
867
count = self.inventory_store.copy_multi(other.inventory_store,
869
print "Added %d inventories." % count
870
870
revision_ids = [ f.revision_id for f in revisions]
872
count, cp_fail = self.revision_store.copy_multi(other.revision_store,
875
assert len(cp_fail) == 0
876
return count, failures
871
count = self.revision_store.copy_multi(other.revision_store,
873
for revision_id in revision_ids:
874
self.append_revision(revision_id)
875
print "Added %d revisions." % count
879
878
def commit(self, *args, **kw):
880
879
from bzrlib.commit import commit
881
880
commit(self, *args, **kw)
884
883
def lookup_revision(self, revision):
885
884
"""Return the revision identifier for a given revision information."""
886
revno, info = self._get_revision_info(revision)
885
revno, info = self.get_revision_info(revision)
890
def revision_id_to_revno(self, revision_id):
891
"""Given a revision id, return its revno"""
892
history = self.revision_history()
894
return history.index(revision_id) + 1
896
raise bzrlib.errors.NoSuchRevision(self, revision_id)
899
888
def get_revision_info(self, revision):
900
889
"""Return (revno, revision id) for revision identifier.
904
893
revision can also be a string, in which case it is parsed for something like
905
894
'date:' or 'revid:' etc.
907
revno, rev_id = self._get_revision_info(revision)
909
raise bzrlib.errors.NoSuchRevision(self, revision)
912
def get_rev_id(self, revno, history=None):
913
"""Find the revision id of the specified revno."""
917
history = self.revision_history()
918
elif revno <= 0 or revno > len(history):
919
raise bzrlib.errors.NoSuchRevision(self, revno)
920
return history[revno - 1]
922
def _get_revision_info(self, revision):
923
"""Return (revno, revision id) for revision specifier.
925
revision can be an integer, in which case it is assumed to be revno
926
(though this will translate negative values into positive ones)
927
revision can also be a string, in which case it is parsed for something
928
like 'date:' or 'revid:' etc.
930
A revid is always returned. If it is None, the specifier referred to
931
the null revision. If the revid does not occur in the revision
932
history, revno will be None.
935
896
if revision is None:
942
903
revs = self.revision_history()
943
904
if isinstance(revision, int):
907
# Mabye we should do this first, but we don't need it if revision == 0
945
909
revno = len(revs) + revision + 1
948
rev_id = self.get_rev_id(revno, revs)
949
912
elif isinstance(revision, basestring):
950
913
for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
951
914
if revision.startswith(prefix):
952
result = func(self, revs, revision)
954
revno, rev_id = result
957
rev_id = self.get_rev_id(revno, revs)
915
revno = func(self, revs, revision)
960
raise BzrError('No namespace registered for string: %r' %
963
raise TypeError('Unhandled revision type %s' % revision)
918
raise BzrError('No namespace registered for string: %r' % revision)
967
raise bzrlib.errors.NoSuchRevision(self, revision)
920
if revno is None or revno <= 0 or revno > len(revs):
921
raise BzrError("no such revision %s" % revision)
922
return revno, revs[revno-1]
970
924
def _namespace_revno(self, revs, revision):
971
925
"""Lookup a revision by revision number"""
972
926
assert revision.startswith('revno:')
974
return (int(revision[6:]),)
928
return int(revision[6:])
975
929
except ValueError:
977
931
REVISION_NAMESPACES['revno:'] = _namespace_revno
979
933
def _namespace_revid(self, revs, revision):
980
934
assert revision.startswith('revid:')
981
rev_id = revision[len('revid:'):]
983
return revs.index(rev_id) + 1, rev_id
936
return revs.index(revision[6:]) + 1
984
937
except ValueError:
986
939
REVISION_NAMESPACES['revid:'] = _namespace_revid
988
941
def _namespace_last(self, revs, revision):
991
944
offset = int(revision[5:])
992
945
except ValueError:
996
949
raise BzrError('You must supply a positive value for --revision last:XXX')
997
return (len(revs) - offset + 1,)
950
return len(revs) - offset + 1
998
951
REVISION_NAMESPACES['last:'] = _namespace_last
1000
953
def _namespace_tag(self, revs, revision):
1075
1028
# TODO: Handle timezone.
1076
1029
dt = datetime.datetime.fromtimestamp(r.timestamp)
1077
1030
if first >= dt and (last is None or dt >= last):
1080
1033
for i in range(len(revs)):
1081
1034
r = self.get_revision(revs[i])
1082
1035
# TODO: Handle timezone.
1083
1036
dt = datetime.datetime.fromtimestamp(r.timestamp)
1084
1037
if first <= dt and (last is None or dt <= last):
1086
1039
REVISION_NAMESPACES['date:'] = _namespace_date
1088
1041
def revision_tree(self, revision_id):
1441
1394
"""Return a new tree-root file id."""
1442
1395
return gen_file_id('TREE_ROOT')
1445
def pull_loc(branch):
1446
# TODO: Should perhaps just make attribute be 'base' in
1447
# RemoteBranch and Branch?
1448
if hasattr(branch, "baseurl"):
1449
return branch.baseurl
1454
def copy_branch(branch_from, to_location, revision=None):
1455
"""Copy branch_from into the existing directory to_location.
1457
If revision is not None, the head of the new branch will be revision.
1459
from bzrlib.merge import merge
1460
from bzrlib.branch import Branch
1461
br_to = Branch(to_location, init=True)
1462
br_to.set_root_id(branch_from.get_root_id())
1463
if revision is None:
1464
revno = branch_from.revno()
1466
revno, rev_id = branch_from.get_revision_info(revision)
1467
br_to.update_revisions(branch_from, stop_revision=revno)
1468
merge((to_location, -1), (to_location, 0), this_dir=to_location,
1469
check_clean=False, ignore_zero=True)
1470
from_location = pull_loc(branch_from)
1471
br_to.controlfile("x-pull", "wb").write(from_location + "\n")