805
797
if stop_revision is None:
806
798
stop_revision = other_len
807
799
elif stop_revision > other_len:
808
raise bzrlib.errors.NoSuchRevision(self, stop_revision)
800
raise NoSuchRevision(self, stop_revision)
810
802
return other_history[self_len:stop_revision]
813
805
def update_revisions(self, other, stop_revision=None):
814
806
"""Pull in all new revisions from other branch.
808
>>> from bzrlib.commit import commit
809
>>> bzrlib.trace.silent = True
810
>>> br1 = ScratchBranch(files=['foo', 'bar'])
813
>>> commit(br1, "lala!", rev_id="REVISION-ID-1", verbose=False)
814
>>> br2 = ScratchBranch()
815
>>> br2.update_revisions(br1)
819
>>> br2.revision_history()
821
>>> br2.update_revisions(br1)
825
>>> 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()
828
from bzrlib.progress import ProgressBar
819
832
pb.update('comparing histories')
821
833
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
835
if hasattr(other.revision_store, "prefetch"):
833
836
other.revision_store.prefetch(revision_ids)
834
837
if hasattr(other.inventory_store, "prefetch"):
835
838
inventory_ids = [other.get_revision(r).inventory_id
836
839
for r in revision_ids]
837
840
other.inventory_store.prefetch(inventory_ids)
840
pb = bzrlib.ui.ui_factory.progress_bar()
843
843
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:
845
for rev_id in revision_ids:
847
pb.update('fetching revision', i, len(revision_ids))
848
rev = other.get_revision(rev_id)
855
849
revisions.append(rev)
856
850
inv = other.get_inventory(str(rev.inventory_id))
857
851
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
859
count = self.text_store.copy_multi(other.text_store, needed_texts)
860
print "Added %d texts." % count
868
861
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
862
count = self.inventory_store.copy_multi(other.inventory_store,
864
print "Added %d inventories." % count
872
865
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
866
count = self.revision_store.copy_multi(other.revision_store,
868
for revision_id in revision_ids:
869
self.append_revision(revision_id)
870
print "Added %d revisions." % count
881
873
def commit(self, *args, **kw):
882
874
from bzrlib.commit import commit
883
875
commit(self, *args, **kw)
886
878
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]
879
"""Return the revision identifier for a given revision information."""
880
revno, info = self.get_revision_info(revision)
883
def get_revision_info(self, revision):
884
"""Return (revno, revision id) for revision identifier.
886
revision can be an integer, in which case it is assumed to be revno (though
887
this will translate negative values into positive ones)
888
revision can also be a string, in which case it is parsed for something like
889
'date:' or 'revid:' etc.
894
try:# Convert to int if possible
895
revision = int(revision)
898
revs = self.revision_history()
899
if isinstance(revision, int):
902
# Mabye we should do this first, but we don't need it if revision == 0
904
revno = len(revs) + revision + 1
907
elif isinstance(revision, basestring):
908
for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
909
if revision.startswith(prefix):
910
revno = func(self, revs, revision)
913
raise BzrError('No namespace registered for string: %r' % revision)
915
if revno is None or revno <= 0 or revno > len(revs):
916
raise BzrError("no such revision %s" % revision)
917
return revno, revs[revno-1]
919
def _namespace_revno(self, revs, revision):
920
"""Lookup a revision by revision number"""
921
assert revision.startswith('revno:')
923
return int(revision[6:])
926
REVISION_NAMESPACES['revno:'] = _namespace_revno
928
def _namespace_revid(self, revs, revision):
929
assert revision.startswith('revid:')
931
return revs.index(revision[6:]) + 1
934
REVISION_NAMESPACES['revid:'] = _namespace_revid
936
def _namespace_last(self, revs, revision):
937
assert revision.startswith('last:')
939
offset = int(revision[5:])
944
raise BzrError('You must supply a positive value for --revision last:XXX')
945
return len(revs) - offset + 1
946
REVISION_NAMESPACES['last:'] = _namespace_last
948
def _namespace_tag(self, revs, revision):
949
assert revision.startswith('tag:')
950
raise BzrError('tag: namespace registered, but not implemented.')
951
REVISION_NAMESPACES['tag:'] = _namespace_tag
953
def _namespace_date(self, revs, revision):
954
assert revision.startswith('date:')
956
# Spec for date revisions:
958
# value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
959
# it can also start with a '+/-/='. '+' says match the first
960
# entry after the given date. '-' is match the first entry before the date
961
# '=' is match the first entry after, but still on the given date.
963
# +2005-05-12 says find the first matching entry after May 12th, 2005 at 0:00
964
# -2005-05-12 says find the first matching entry before May 12th, 2005 at 0:00
965
# =2005-05-12 says find the first match after May 12th, 2005 at 0:00 but before
966
# May 13th, 2005 at 0:00
968
# So the proper way of saying 'give me all entries for today' is:
969
# -r {date:+today}:{date:-tomorrow}
970
# The default is '=' when not supplied
973
if val[:1] in ('+', '-', '='):
974
match_style = val[:1]
977
today = datetime.datetime.today().replace(hour=0,minute=0,second=0,microsecond=0)
978
if val.lower() == 'yesterday':
979
dt = today - datetime.timedelta(days=1)
980
elif val.lower() == 'today':
982
elif val.lower() == 'tomorrow':
983
dt = today + datetime.timedelta(days=1)
986
# This should be done outside the function to avoid recompiling it.
987
_date_re = re.compile(
988
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
990
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
992
m = _date_re.match(val)
993
if not m or (not m.group('date') and not m.group('time')):
994
raise BzrError('Invalid revision date %r' % revision)
997
year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
999
year, month, day = today.year, today.month, today.day
1001
hour = int(m.group('hour'))
1002
minute = int(m.group('minute'))
1003
if m.group('second'):
1004
second = int(m.group('second'))
1008
hour, minute, second = 0,0,0
1010
dt = datetime.datetime(year=year, month=month, day=day,
1011
hour=hour, minute=minute, second=second)
1015
if match_style == '-':
1017
elif match_style == '=':
1018
last = dt + datetime.timedelta(days=1)
1021
for i in range(len(revs)-1, -1, -1):
1022
r = self.get_revision(revs[i])
1023
# TODO: Handle timezone.
1024
dt = datetime.datetime.fromtimestamp(r.timestamp)
1025
if first >= dt and (last is None or dt >= last):
1028
for i in range(len(revs)):
1029
r = self.get_revision(revs[i])
1030
# TODO: Handle timezone.
1031
dt = datetime.datetime.fromtimestamp(r.timestamp)
1032
if first <= dt and (last is None or dt <= last):
1034
REVISION_NAMESPACES['date:'] = _namespace_date
927
1036
def revision_tree(self, revision_id):
928
1037
"""Return Tree for a revision on this branch.