805
809
if stop_revision is None:
806
810
stop_revision = other_len
807
811
elif stop_revision > other_len:
808
raise bzrlib.errors.NoSuchRevision(self, stop_revision)
812
raise NoSuchRevision(self, stop_revision)
810
814
return other_history[self_len:stop_revision]
813
817
def update_revisions(self, other, stop_revision=None):
814
818
"""Pull in all new revisions from other branch.
820
>>> from bzrlib.commit import commit
821
>>> bzrlib.trace.silent = True
822
>>> br1 = ScratchBranch(files=['foo', 'bar'])
825
>>> commit(br1, "lala!", rev_id="REVISION-ID-1", verbose=False)
826
>>> br2 = ScratchBranch()
827
>>> br2.update_revisions(br1)
831
>>> br2.revision_history()
833
>>> br2.update_revisions(br1)
837
>>> br1.text_store.total_size() == br2.text_store.total_size()
816
from bzrlib.fetch import greedy_fetch
818
pb = bzrlib.ui.ui_factory.progress_bar()
840
from bzrlib.progress import ProgressBar
819
844
pb.update('comparing histories')
821
845
revision_ids = self.missing_revisions(other, stop_revision)
823
if len(revision_ids) > 0:
824
count = greedy_fetch(self, other, revision_ids[-1], pb)[0]
827
self.append_revision(*revision_ids)
828
## note("Added %d revisions." % count)
831
def install_revisions(self, other, revision_ids, pb):
832
847
if hasattr(other.revision_store, "prefetch"):
833
848
other.revision_store.prefetch(revision_ids)
834
849
if hasattr(other.inventory_store, "prefetch"):
835
850
inventory_ids = [other.get_revision(r).inventory_id
836
851
for r in revision_ids]
837
852
other.inventory_store.prefetch(inventory_ids)
840
pb = bzrlib.ui.ui_factory.progress_bar()
843
855
needed_texts = set()
847
for i, rev_id in enumerate(revision_ids):
848
pb.update('fetching revision', i+1, len(revision_ids))
850
rev = other.get_revision(rev_id)
851
except bzrlib.errors.NoSuchRevision:
857
for rev_id in revision_ids:
859
pb.update('fetching revision', i, len(revision_ids))
860
rev = other.get_revision(rev_id)
855
861
revisions.append(rev)
856
862
inv = other.get_inventory(str(rev.inventory_id))
857
863
for key, entry in inv.iter_entries():
865
count, cp_fail = self.text_store.copy_multi(other.text_store,
867
#print "Added %d texts." % count
871
count = self.text_store.copy_multi(other.text_store, needed_texts)
872
print "Added %d texts." % count
868
873
inventory_ids = [ f.inventory_id for f in revisions ]
869
count, cp_fail = self.inventory_store.copy_multi(other.inventory_store,
871
#print "Added %d inventories." % count
874
count = self.inventory_store.copy_multi(other.inventory_store,
876
print "Added %d inventories." % count
872
877
revision_ids = [ f.revision_id for f in revisions]
874
count, cp_fail = self.revision_store.copy_multi(other.revision_store,
877
assert len(cp_fail) == 0
878
return count, failures
878
count = self.revision_store.copy_multi(other.revision_store,
880
for revision_id in revision_ids:
881
self.append_revision(revision_id)
882
print "Added %d revisions." % count
881
885
def commit(self, *args, **kw):
882
886
from bzrlib.commit import commit
883
887
commit(self, *args, **kw)
886
890
def lookup_revision(self, revision):
887
"""Return the revision identifier for a given revision specifier."""
888
# XXX: I'm not sure this method belongs here; I'd rather have the
889
# revision spec stuff be an UI thing, and branch blissfully unaware
891
# Also, I'm not entirely happy with this method returning None
892
# when the revision doesn't exist.
893
# But I'm keeping the contract I found, because this seems to be
894
# used in a lot of places - and when I do change these, I'd rather
895
# figure out case-by-case which ones actually want to care about
896
# revision specs (eg, they are UI-level) and which ones should trust
897
# that they have a revno/revid.
898
# -- lalo@exoweb.net, 2005-09-07
899
from bzrlib.errors import NoSuchRevision
900
from bzrlib.revisionspec import RevisionSpec
902
spec = RevisionSpec(self, revision)
903
except NoSuchRevision:
908
def revision_id_to_revno(self, revision_id):
909
"""Given a revision id, return its revno"""
910
history = self.revision_history()
912
return history.index(revision_id) + 1
914
raise bzrlib.errors.NoSuchRevision(self, revision_id)
917
def get_rev_id(self, revno, history=None):
918
"""Find the revision id of the specified revno."""
922
history = self.revision_history()
923
elif revno <= 0 or revno > len(history):
924
raise bzrlib.errors.NoSuchRevision(self, revno)
925
return history[revno - 1]
891
"""Return the revision identifier for a given revision information."""
892
revno, info = self.get_revision_info(revision)
895
def get_revision_info(self, revision):
896
"""Return (revno, revision id) for revision identifier.
898
revision can be an integer, in which case it is assumed to be revno (though
899
this will translate negative values into positive ones)
900
revision can also be a string, in which case it is parsed for something like
901
'date:' or 'revid:' etc.
906
try:# Convert to int if possible
907
revision = int(revision)
910
revs = self.revision_history()
911
if isinstance(revision, int):
914
# Mabye we should do this first, but we don't need it if revision == 0
916
revno = len(revs) + revision + 1
919
elif isinstance(revision, basestring):
920
for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
921
if revision.startswith(prefix):
922
revno = func(self, revs, revision)
925
raise BzrError('No namespace registered for string: %r' % revision)
927
if revno is None or revno <= 0 or revno > len(revs):
928
raise BzrError("no such revision %s" % revision)
929
return revno, revs[revno-1]
931
def _namespace_revno(self, revs, revision):
932
"""Lookup a revision by revision number"""
933
assert revision.startswith('revno:')
935
return int(revision[6:])
938
REVISION_NAMESPACES['revno:'] = _namespace_revno
940
def _namespace_revid(self, revs, revision):
941
assert revision.startswith('revid:')
943
return revs.index(revision[6:]) + 1
946
REVISION_NAMESPACES['revid:'] = _namespace_revid
948
def _namespace_last(self, revs, revision):
949
assert revision.startswith('last:')
951
offset = int(revision[5:])
956
raise BzrError('You must supply a positive value for --revision last:XXX')
957
return len(revs) - offset + 1
958
REVISION_NAMESPACES['last:'] = _namespace_last
960
def _namespace_tag(self, revs, revision):
961
assert revision.startswith('tag:')
962
raise BzrError('tag: namespace registered, but not implemented.')
963
REVISION_NAMESPACES['tag:'] = _namespace_tag
965
def _namespace_date(self, revs, revision):
966
assert revision.startswith('date:')
968
# Spec for date revisions:
970
# value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
971
# it can also start with a '+/-/='. '+' says match the first
972
# entry after the given date. '-' is match the first entry before the date
973
# '=' is match the first entry after, but still on the given date.
975
# +2005-05-12 says find the first matching entry after May 12th, 2005 at 0:00
976
# -2005-05-12 says find the first matching entry before May 12th, 2005 at 0:00
977
# =2005-05-12 says find the first match after May 12th, 2005 at 0:00 but before
978
# May 13th, 2005 at 0:00
980
# So the proper way of saying 'give me all entries for today' is:
981
# -r {date:+today}:{date:-tomorrow}
982
# The default is '=' when not supplied
985
if val[:1] in ('+', '-', '='):
986
match_style = val[:1]
989
today = datetime.datetime.today().replace(hour=0,minute=0,second=0,microsecond=0)
990
if val.lower() == 'yesterday':
991
dt = today - datetime.timedelta(days=1)
992
elif val.lower() == 'today':
994
elif val.lower() == 'tomorrow':
995
dt = today + datetime.timedelta(days=1)
998
# This should be done outside the function to avoid recompiling it.
999
_date_re = re.compile(
1000
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
1002
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
1004
m = _date_re.match(val)
1005
if not m or (not m.group('date') and not m.group('time')):
1006
raise BzrError('Invalid revision date %r' % revision)
1009
year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
1011
year, month, day = today.year, today.month, today.day
1013
hour = int(m.group('hour'))
1014
minute = int(m.group('minute'))
1015
if m.group('second'):
1016
second = int(m.group('second'))
1020
hour, minute, second = 0,0,0
1022
dt = datetime.datetime(year=year, month=month, day=day,
1023
hour=hour, minute=minute, second=second)
1027
if match_style == '-':
1029
elif match_style == '=':
1030
last = dt + datetime.timedelta(days=1)
1033
for i in range(len(revs)-1, -1, -1):
1034
r = self.get_revision(revs[i])
1035
# TODO: Handle timezone.
1036
dt = datetime.datetime.fromtimestamp(r.timestamp)
1037
if first >= dt and (last is None or dt >= last):
1040
for i in range(len(revs)):
1041
r = self.get_revision(revs[i])
1042
# TODO: Handle timezone.
1043
dt = datetime.datetime.fromtimestamp(r.timestamp)
1044
if first <= dt and (last is None or dt <= last):
1046
REVISION_NAMESPACES['date:'] = _namespace_date
927
1048
def revision_tree(self, revision_id):
928
1049
"""Return Tree for a revision on this branch.