49
50
def find_branch(f, **args):
50
51
if f and (f.startswith('http://') or f.startswith('https://')):
52
return remotebranch.RemoteBranch(f, **args)
52
from bzrlib.remotebranch import RemoteBranch
53
return RemoteBranch(f, **args)
54
return Branch(f, **args)
55
return LocalBranch(f, **args)
57
58
def find_cached_branch(f, cache_root, **args):
58
from remotebranch import RemoteBranch
59
from bzrlib.remotebranch import RemoteBranch
59
60
br = find_branch(f, **args)
60
61
def cacheify(br, store_name):
61
from meta_store import CachedStore
62
from bzrlib.meta_store import CachedStore
62
63
cache_path = os.path.join(cache_root, store_name)
63
64
os.mkdir(cache_path)
64
65
new_store = CachedStore(getattr(br, store_name), cache_path)
702
705
def common_ancestor(self, other, self_revno=None, other_revno=None):
707
>>> from bzrlib.commit import commit
705
708
>>> sb = ScratchBranch(files=['foo', 'foo~'])
706
709
>>> sb.common_ancestor(sb) == (None, None)
708
>>> commit.commit(sb, "Committing first revision", verbose=False)
711
>>> commit(sb, "Committing first revision", verbose=False)
709
712
>>> sb.common_ancestor(sb)[0]
711
714
>>> clone = sb.clone()
712
>>> commit.commit(sb, "Committing second revision", verbose=False)
715
>>> commit(sb, "Committing second revision", verbose=False)
713
716
>>> sb.common_ancestor(sb)[0]
715
718
>>> sb.common_ancestor(clone)[0]
717
>>> commit.commit(clone, "Committing divergent second revision",
720
>>> commit(clone, "Committing divergent second revision",
718
721
... verbose=False)
719
722
>>> sb.common_ancestor(clone)[0]
810
813
def update_revisions(self, other, stop_revision=None):
811
814
"""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()
833
progress = bzrlib.ui.ui_factory.progress_bar()
834
progress.update('comparing histories')
816
from bzrlib.fetch import greedy_fetch
818
pb = bzrlib.ui.ui_factory.progress_bar()
819
pb.update('comparing histories')
835
821
revision_ids = self.missing_revisions(other, stop_revision)
836
count = self.install_revisions(other, revision_ids, progress=progress)
823
if len(revision_ids) > 0:
824
count = greedy_fetch(self, other, revision_ids[-1], pb)[0]
837
827
self.append_revision(*revision_ids)
838
print "Added %d revisions." % count
828
## note("Added %d revisions." % count)
841
def install_revisions(self, other, revision_ids, progress=None):
831
def install_revisions(self, other, revision_ids, pb):
842
832
if hasattr(other.revision_store, "prefetch"):
843
833
other.revision_store.prefetch(revision_ids)
844
834
if hasattr(other.inventory_store, "prefetch"):
845
835
inventory_ids = [other.get_revision(r).inventory_id
846
836
for r in revision_ids]
847
837
other.inventory_store.prefetch(inventory_ids)
840
pb = bzrlib.ui.ui_factory.progress_bar()
850
843
needed_texts = set()
852
for rev_id in revision_ids:
855
progress.update('fetching revision', i, len(revision_ids))
856
rev = other.get_revision(rev_id)
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
855
revisions.append(rev)
858
856
inv = other.get_inventory(str(rev.inventory_id))
859
857
for key, entry in inv.iter_entries():
862
860
if entry.text_id not in self.text_store:
863
861
needed_texts.add(entry.text_id)
868
count = self.text_store.copy_multi(other.text_store, needed_texts)
869
print "Added %d texts." % count
865
count, cp_fail = self.text_store.copy_multi(other.text_store,
867
#print "Added %d texts." % count
870
868
inventory_ids = [ f.inventory_id for f in revisions ]
871
count = self.inventory_store.copy_multi(other.inventory_store,
873
print "Added %d inventories." % count
869
count, cp_fail = self.inventory_store.copy_multi(other.inventory_store,
871
#print "Added %d inventories." % count
874
872
revision_ids = [ f.revision_id for f in revisions]
875
count = self.revision_store.copy_multi(other.revision_store,
874
count, cp_fail = self.revision_store.copy_multi(other.revision_store,
877
assert len(cp_fail) == 0
878
return count, failures
879
881
def commit(self, *args, **kw):
880
882
from bzrlib.commit import commit
881
883
commit(self, *args, **kw)
884
886
def lookup_revision(self, revision):
885
"""Return the revision identifier for a given revision information."""
886
revno, info = self.get_revision_info(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:
890
908
def revision_id_to_revno(self, revision_id):
896
914
raise bzrlib.errors.NoSuchRevision(self, revision_id)
899
def get_revision_info(self, revision):
900
"""Return (revno, revision id) for revision identifier.
902
revision can be an integer, in which case it is assumed to be revno (though
903
this will translate negative values into positive ones)
904
revision can also be a string, in which case it is parsed for something like
905
'date:' or 'revid:' etc.
910
try:# Convert to int if possible
911
revision = int(revision)
914
revs = self.revision_history()
915
if isinstance(revision, int):
918
# Mabye we should do this first, but we don't need it if revision == 0
920
revno = len(revs) + revision + 1
923
elif isinstance(revision, basestring):
924
for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
925
if revision.startswith(prefix):
926
revno = func(self, revs, revision)
929
raise BzrError('No namespace registered for string: %r' % revision)
931
if revno is None or revno <= 0 or revno > len(revs):
932
raise BzrError("no such revision %s" % revision)
933
return revno, revs[revno-1]
935
def _namespace_revno(self, revs, revision):
936
"""Lookup a revision by revision number"""
937
assert revision.startswith('revno:')
939
return int(revision[6:])
942
REVISION_NAMESPACES['revno:'] = _namespace_revno
944
def _namespace_revid(self, revs, revision):
945
assert revision.startswith('revid:')
947
return revs.index(revision[6:]) + 1
950
REVISION_NAMESPACES['revid:'] = _namespace_revid
952
def _namespace_last(self, revs, revision):
953
assert revision.startswith('last:')
955
offset = int(revision[5:])
960
raise BzrError('You must supply a positive value for --revision last:XXX')
961
return len(revs) - offset + 1
962
REVISION_NAMESPACES['last:'] = _namespace_last
964
def _namespace_tag(self, revs, revision):
965
assert revision.startswith('tag:')
966
raise BzrError('tag: namespace registered, but not implemented.')
967
REVISION_NAMESPACES['tag:'] = _namespace_tag
969
def _namespace_date(self, revs, revision):
970
assert revision.startswith('date:')
972
# Spec for date revisions:
974
# value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
975
# it can also start with a '+/-/='. '+' says match the first
976
# entry after the given date. '-' is match the first entry before the date
977
# '=' is match the first entry after, but still on the given date.
979
# +2005-05-12 says find the first matching entry after May 12th, 2005 at 0:00
980
# -2005-05-12 says find the first matching entry before May 12th, 2005 at 0:00
981
# =2005-05-12 says find the first match after May 12th, 2005 at 0:00 but before
982
# May 13th, 2005 at 0:00
984
# So the proper way of saying 'give me all entries for today' is:
985
# -r {date:+today}:{date:-tomorrow}
986
# The default is '=' when not supplied
989
if val[:1] in ('+', '-', '='):
990
match_style = val[:1]
993
today = datetime.datetime.today().replace(hour=0,minute=0,second=0,microsecond=0)
994
if val.lower() == 'yesterday':
995
dt = today - datetime.timedelta(days=1)
996
elif val.lower() == 'today':
998
elif val.lower() == 'tomorrow':
999
dt = today + datetime.timedelta(days=1)
1002
# This should be done outside the function to avoid recompiling it.
1003
_date_re = re.compile(
1004
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
1006
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
1008
m = _date_re.match(val)
1009
if not m or (not m.group('date') and not m.group('time')):
1010
raise BzrError('Invalid revision date %r' % revision)
1013
year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
1015
year, month, day = today.year, today.month, today.day
1017
hour = int(m.group('hour'))
1018
minute = int(m.group('minute'))
1019
if m.group('second'):
1020
second = int(m.group('second'))
1024
hour, minute, second = 0,0,0
1026
dt = datetime.datetime(year=year, month=month, day=day,
1027
hour=hour, minute=minute, second=second)
1031
if match_style == '-':
1033
elif match_style == '=':
1034
last = dt + datetime.timedelta(days=1)
1037
for i in range(len(revs)-1, -1, -1):
1038
r = self.get_revision(revs[i])
1039
# TODO: Handle timezone.
1040
dt = datetime.datetime.fromtimestamp(r.timestamp)
1041
if first >= dt and (last is None or dt >= last):
1044
for i in range(len(revs)):
1045
r = self.get_revision(revs[i])
1046
# TODO: Handle timezone.
1047
dt = datetime.datetime.fromtimestamp(r.timestamp)
1048
if first <= dt and (last is None or dt <= last):
1050
REVISION_NAMESPACES['date:'] = _namespace_date
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]
1052
927
def revision_tree(self, revision_id):
1053
928
"""Return Tree for a revision on this branch.
1287
class ScratchBranch(Branch):
1165
def get_parent(self):
1166
"""Return the parent location of the branch.
1168
This is the default location for push/pull/missing. The usual
1169
pattern is that the user can override it by specifying a
1173
_locs = ['parent', 'pull', 'x-pull']
1176
return self.controlfile(l, 'r').read().strip('\n')
1178
if e.errno != errno.ENOENT:
1183
def set_parent(self, url):
1184
# TODO: Maybe delete old location files?
1185
from bzrlib.atomicfile import AtomicFile
1188
f = AtomicFile(self.controlfilename('parent'))
1197
def check_revno(self, revno):
1199
Check whether a revno corresponds to any revision.
1200
Zero (the NULL revision) is considered valid.
1203
self.check_real_revno(revno)
1205
def check_real_revno(self, revno):
1207
Check whether a revno corresponds to a real revision.
1208
Zero (the NULL revision) is considered invalid
1210
if revno < 1 or revno > self.revno():
1211
raise InvalidRevisionNumber(revno)
1216
class ScratchBranch(LocalBranch):
1288
1217
"""Special test class: a branch that cleans up after itself.
1290
1219
>>> b = ScratchBranch()
1406
1337
return gen_file_id('TREE_ROOT')
1409
def pull_loc(branch):
1410
# TODO: Should perhaps just make attribute be 'base' in
1411
# RemoteBranch and Branch?
1412
if hasattr(branch, "baseurl"):
1413
return branch.baseurl
1418
1340
def copy_branch(branch_from, to_location, revision=None):
1419
1341
"""Copy branch_from into the existing directory to_location.
1421
If revision is not None, the head of the new branch will be revision.
1344
If not None, only revisions up to this point will be copied.
1345
The head of the new branch will be that revision.
1348
The name of a local directory that exists but is empty.
1423
1350
from bzrlib.merge import merge
1424
from bzrlib.branch import Branch
1425
from shutil import rmtree
1351
from bzrlib.revisionspec import RevisionSpec
1353
assert isinstance(branch_from, Branch)
1354
assert isinstance(to_location, basestring)
1426
1356
br_to = Branch(to_location, init=True)
1427
1357
br_to.set_root_id(branch_from.get_root_id())
1428
1358
if revision is None:
1429
1359
revno = branch_from.revno()
1431
revno, rev_id = branch_from.get_revision_info(revision)
1361
revno, rev_id = RevisionSpec(branch_from, revision)
1432
1362
br_to.update_revisions(branch_from, stop_revision=revno)
1433
1363
merge((to_location, -1), (to_location, 0), this_dir=to_location,
1434
1364
check_clean=False, ignore_zero=True)
1435
from_location = pull_loc(branch_from)
1436
br_to.controlfile("x-pull", "wb").write(from_location + "\n")
1366
from_location = branch_from.base
1367
br_to.set_parent(branch_from.base)