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
31
30
from bzrlib.xml import unpack_xml
32
31
from bzrlib.delta import compare_trees
33
32
from bzrlib.tree import EmptyTree, RevisionTree
33
from bzrlib.progress import ProgressBar
38
36
BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
39
37
## TODO: Maybe include checks for common corruption of newlines, etc?
128
125
head, tail = os.path.split(f)
130
127
# reached the root, whatever that may be
131
raise bzrlib.errors.NotBranchError('%s is not in a branch' % orig_f)
128
raise BzrError('%r is not in a branch' % orig_f)
136
# XXX: move into bzrlib.errors; subclass BzrError
137
131
class DivergedBranches(Exception):
138
132
def __init__(self, branch1, branch2):
139
133
self.branch1 = branch1
258
256
self._lock = None
259
257
self._lock_mode = self._lock_count = None
261
260
def abspath(self, name):
262
261
"""Return absolute filename for something in the branch"""
263
262
return os.path.join(self.base, name)
265
265
def relpath(self, path):
266
266
"""Return path relative to this branch of something inside it.
268
268
Raises an error if path is not in this branch."""
269
269
return _relpath(self.base, path)
271
272
def controlfilename(self, file_or_path):
272
273
"""Return location relative to branch."""
273
274
if isinstance(file_or_path, basestring):
803
807
if stop_revision is None:
804
808
stop_revision = other_len
805
809
elif stop_revision > other_len:
806
raise bzrlib.errors.NoSuchRevision(self, stop_revision)
810
raise NoSuchRevision(self, stop_revision)
808
812
return other_history[self_len:stop_revision]
811
815
def update_revisions(self, other, stop_revision=None):
812
816
"""Pull in all new revisions from other branch.
818
>>> from bzrlib.commit import commit
819
>>> bzrlib.trace.silent = True
820
>>> br1 = ScratchBranch(files=['foo', 'bar'])
823
>>> commit(br1, "lala!", rev_id="REVISION-ID-1", verbose=False)
824
>>> br2 = ScratchBranch()
825
>>> br2.update_revisions(br1)
829
>>> br2.revision_history()
831
>>> br2.update_revisions(br1)
833
>>> br1.text_store.total_size() == br2.text_store.total_size()
814
836
from bzrlib.fetch import greedy_fetch
816
pb = bzrlib.ui.ui_factory.progress_bar()
817
838
pb.update('comparing histories')
819
839
revision_ids = self.missing_revisions(other, stop_revision)
821
840
if len(revision_ids) > 0:
822
841
count = greedy_fetch(self, other, revision_ids[-1], pb)[0]
825
844
self.append_revision(*revision_ids)
826
## note("Added %d revisions." % count)
829
def install_revisions(self, other, revision_ids, pb):
845
print "Added %d revisions." % count
847
def install_revisions(self, other, revision_ids, pb=None):
830
850
if hasattr(other.revision_store, "prefetch"):
831
851
other.revision_store.prefetch(revision_ids)
832
852
if hasattr(other.inventory_store, "prefetch"):
833
853
inventory_ids = [other.get_revision(r).inventory_id
834
854
for r in revision_ids]
835
855
other.inventory_store.prefetch(inventory_ids)
838
pb = bzrlib.ui.ui_factory.progress_bar()
841
858
needed_texts = set()
845
861
for i, rev_id in enumerate(revision_ids):
846
862
pb.update('fetching revision', i+1, len(revision_ids))
863
878
count, cp_fail = self.text_store.copy_multi(other.text_store,
865
#print "Added %d texts." % count
880
print "Added %d texts." % count
866
881
inventory_ids = [ f.inventory_id for f in revisions ]
867
882
count, cp_fail = self.inventory_store.copy_multi(other.inventory_store,
869
#print "Added %d inventories." % count
884
print "Added %d inventories." % count
870
885
revision_ids = [ f.revision_id for f in revisions]
872
886
count, cp_fail = self.revision_store.copy_multi(other.revision_store,
874
888
permit_failure=True)
875
889
assert len(cp_fail) == 0
876
890
return count, failures
879
892
def commit(self, *args, **kw):
880
893
from bzrlib.commit import commit
881
894
commit(self, *args, **kw)
884
897
def lookup_revision(self, revision):
885
898
"""Return the revision identifier for a given revision information."""
886
revno, info = self._get_revision_info(revision)
899
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
902
def get_revision_info(self, revision):
900
903
"""Return (revno, revision id) for revision identifier.
904
907
revision can also be a string, in which case it is parsed for something like
905
908
'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
910
if revision is None:
942
917
revs = self.revision_history()
943
918
if isinstance(revision, int):
921
# Mabye we should do this first, but we don't need it if revision == 0
945
923
revno = len(revs) + revision + 1
948
rev_id = self.get_rev_id(revno, revs)
949
926
elif isinstance(revision, basestring):
950
927
for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
951
928
if revision.startswith(prefix):
952
result = func(self, revs, revision)
954
revno, rev_id = result
957
rev_id = self.get_rev_id(revno, revs)
929
revno = func(self, revs, revision)
960
raise BzrError('No namespace registered for string: %r' %
963
raise TypeError('Unhandled revision type %s' % revision)
932
raise BzrError('No namespace registered for string: %r' % revision)
967
raise bzrlib.errors.NoSuchRevision(self, revision)
934
if revno is None or revno <= 0 or revno > len(revs):
935
raise BzrError("no such revision %s" % revision)
936
return revno, revs[revno-1]
970
938
def _namespace_revno(self, revs, revision):
971
939
"""Lookup a revision by revision number"""
972
940
assert revision.startswith('revno:')
974
return (int(revision[6:]),)
942
return int(revision[6:])
975
943
except ValueError:
977
945
REVISION_NAMESPACES['revno:'] = _namespace_revno
979
947
def _namespace_revid(self, revs, revision):
980
948
assert revision.startswith('revid:')
981
rev_id = revision[len('revid:'):]
983
return revs.index(rev_id) + 1, rev_id
950
return revs.index(revision[6:]) + 1
984
951
except ValueError:
986
953
REVISION_NAMESPACES['revid:'] = _namespace_revid
988
955
def _namespace_last(self, revs, revision):
991
958
offset = int(revision[5:])
992
959
except ValueError:
996
963
raise BzrError('You must supply a positive value for --revision last:XXX')
997
return (len(revs) - offset + 1,)
964
return len(revs) - offset + 1
998
965
REVISION_NAMESPACES['last:'] = _namespace_last
1000
967
def _namespace_tag(self, revs, revision):
1075
1042
# TODO: Handle timezone.
1076
1043
dt = datetime.datetime.fromtimestamp(r.timestamp)
1077
1044
if first >= dt and (last is None or dt >= last):
1080
1047
for i in range(len(revs)):
1081
1048
r = self.get_revision(revs[i])
1082
1049
# TODO: Handle timezone.
1083
1050
dt = datetime.datetime.fromtimestamp(r.timestamp)
1084
1051
if first <= dt and (last is None or dt <= last):
1086
1053
REVISION_NAMESPACES['date:'] = _namespace_date
1088
1055
def revision_tree(self, revision_id):
1441
1408
"""Return a new tree-root file id."""
1442
1409
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")