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):
404
408
"""Inventory for the working copy.""")
407
def add(self, files, ids=None):
411
def add(self, files, verbose=False, ids=None):
408
412
"""Make files versioned.
410
Note that the command line normally calls smart_add instead,
411
which can automatically recurse.
414
Note that the command line normally calls smart_add instead.
413
416
This puts the files in the Added state, so that they will be
414
417
recorded by the next commit.
424
427
TODO: Perhaps have an option to add the ids even if the files do
427
TODO: Perhaps yield the ids and paths as they're added.
430
TODO: Perhaps return the ids of the files? But then again it
431
is easy to retrieve them if they're needed.
433
TODO: Adding a directory should optionally recurse down and
434
add all non-ignored children. Perhaps do that in a
429
437
# TODO: Re-adding a file that is removed in the working copy
430
438
# should probably put it back with the previous ID.
466
474
file_id = gen_file_id(f)
467
475
inv.add_path(f, kind=kind, file_id=file_id)
478
print 'added', quotefn(f)
469
480
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
471
482
self._write_inventory(inv)
796
807
if stop_revision is None:
797
808
stop_revision = other_len
798
809
elif stop_revision > other_len:
799
raise bzrlib.errors.NoSuchRevision(self, stop_revision)
810
raise NoSuchRevision(self, stop_revision)
801
812
return other_history[self_len:stop_revision]
804
815
def update_revisions(self, other, stop_revision=None):
805
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()
807
836
from bzrlib.fetch import greedy_fetch
809
pb = bzrlib.ui.ui_factory.progress_bar()
810
838
pb.update('comparing histories')
812
839
revision_ids = self.missing_revisions(other, stop_revision)
814
840
if len(revision_ids) > 0:
815
841
count = greedy_fetch(self, other, revision_ids[-1], pb)[0]
818
844
self.append_revision(*revision_ids)
819
## note("Added %d revisions." % count)
822
def install_revisions(self, other, revision_ids, pb):
845
print "Added %d revisions." % count
847
def install_revisions(self, other, revision_ids, pb=None):
823
850
if hasattr(other.revision_store, "prefetch"):
824
851
other.revision_store.prefetch(revision_ids)
825
852
if hasattr(other.inventory_store, "prefetch"):
826
853
inventory_ids = [other.get_revision(r).inventory_id
827
854
for r in revision_ids]
828
855
other.inventory_store.prefetch(inventory_ids)
831
pb = bzrlib.ui.ui_factory.progress_bar()
834
858
needed_texts = set()
838
861
for i, rev_id in enumerate(revision_ids):
839
862
pb.update('fetching revision', i+1, len(revision_ids))
856
878
count, cp_fail = self.text_store.copy_multi(other.text_store,
858
#print "Added %d texts." % count
880
print "Added %d texts." % count
859
881
inventory_ids = [ f.inventory_id for f in revisions ]
860
882
count, cp_fail = self.inventory_store.copy_multi(other.inventory_store,
862
#print "Added %d inventories." % count
884
print "Added %d inventories." % count
863
885
revision_ids = [ f.revision_id for f in revisions]
865
886
count, cp_fail = self.revision_store.copy_multi(other.revision_store,
867
888
permit_failure=True)
868
889
assert len(cp_fail) == 0
869
890
return count, failures
872
892
def commit(self, *args, **kw):
873
893
from bzrlib.commit import commit
874
894
commit(self, *args, **kw)
877
897
def lookup_revision(self, revision):
878
898
"""Return the revision identifier for a given revision information."""
879
revno, info = self.get_revision_info(revision)
899
revno, info = self._get_revision_info(revision)
883
def revision_id_to_revno(self, revision_id):
884
"""Given a revision id, return its revno"""
885
history = self.revision_history()
887
return history.index(revision_id) + 1
889
raise bzrlib.errors.NoSuchRevision(self, revision_id)
892
902
def get_revision_info(self, revision):
893
903
"""Return (revno, revision id) for revision identifier.
897
907
revision can also be a string, in which case it is parsed for something like
898
908
'date:' or 'revid:' etc.
910
revno, rev_id = self._get_revision_info(revision)
912
raise bzrlib.errors.NoSuchRevision(self, revision)
915
def get_rev_id(self, revno, history=None):
916
"""Find the revision id of the specified revno."""
920
history = self.revision_history()
921
elif revno <= 0 or revno > len(history):
922
raise bzrlib.errors.NoSuchRevision(self, revno)
923
return history[revno - 1]
925
def _get_revision_info(self, revision):
926
"""Return (revno, revision id) for revision specifier.
928
revision can be an integer, in which case it is assumed to be revno
929
(though this will translate negative values into positive ones)
930
revision can also be a string, in which case it is parsed for something
931
like 'date:' or 'revid:' etc.
933
A revid is always returned. If it is None, the specifier referred to
934
the null revision. If the revid does not occur in the revision
935
history, revno will be None.
900
938
if revision is None:
907
945
revs = self.revision_history()
908
946
if isinstance(revision, int):
911
# Mabye we should do this first, but we don't need it if revision == 0
913
948
revno = len(revs) + revision + 1
951
rev_id = self.get_rev_id(revno, revs)
916
952
elif isinstance(revision, basestring):
917
953
for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
918
954
if revision.startswith(prefix):
919
revno = func(self, revs, revision)
955
result = func(self, revs, revision)
957
revno, rev_id = result
960
rev_id = self.get_rev_id(revno, revs)
922
raise BzrError('No namespace registered for string: %r' % revision)
963
raise BzrError('No namespace registered for string: %r' %
966
raise TypeError('Unhandled revision type %s' % revision)
924
if revno is None or revno <= 0 or revno > len(revs):
925
raise BzrError("no such revision %s" % revision)
926
return revno, revs[revno-1]
970
raise bzrlib.errors.NoSuchRevision(self, revision)
928
973
def _namespace_revno(self, revs, revision):
929
974
"""Lookup a revision by revision number"""
930
975
assert revision.startswith('revno:')
932
return int(revision[6:])
977
return (int(revision[6:]),)
933
978
except ValueError:
935
980
REVISION_NAMESPACES['revno:'] = _namespace_revno
937
982
def _namespace_revid(self, revs, revision):
938
983
assert revision.startswith('revid:')
984
rev_id = revision[len('revid:'):]
940
return revs.index(revision[6:]) + 1
986
return revs.index(rev_id) + 1, rev_id
941
987
except ValueError:
943
989
REVISION_NAMESPACES['revid:'] = _namespace_revid
945
991
def _namespace_last(self, revs, revision):
948
994
offset = int(revision[5:])
949
995
except ValueError:
953
999
raise BzrError('You must supply a positive value for --revision last:XXX')
954
return len(revs) - offset + 1
1000
return (len(revs) - offset + 1,)
955
1001
REVISION_NAMESPACES['last:'] = _namespace_last
957
1003
def _namespace_tag(self, revs, revision):
1032
1078
# TODO: Handle timezone.
1033
1079
dt = datetime.datetime.fromtimestamp(r.timestamp)
1034
1080
if first >= dt and (last is None or dt >= last):
1037
1083
for i in range(len(revs)):
1038
1084
r = self.get_revision(revs[i])
1039
1085
# TODO: Handle timezone.
1040
1086
dt = datetime.datetime.fromtimestamp(r.timestamp)
1041
1087
if first <= dt and (last is None or dt <= last):
1043
1089
REVISION_NAMESPACES['date:'] = _namespace_date
1045
1091
def revision_tree(self, revision_id):
1179
1223
for f in from_paths:
1180
1224
name_tail = splitpath(f)[-1]
1181
1225
dest_path = appendpath(to_name, name_tail)
1182
result.append((f, dest_path))
1226
print "%s => %s" % (f, dest_path)
1183
1227
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1185
1229
os.rename(self.abspath(f), self.abspath(dest_path))
1402
1444
"""Return a new tree-root file id."""
1403
1445
return gen_file_id('TREE_ROOT')
1406
def pull_loc(branch):
1407
# TODO: Should perhaps just make attribute be 'base' in
1408
# RemoteBranch and Branch?
1409
if hasattr(branch, "baseurl"):
1410
return branch.baseurl
1415
def copy_branch(branch_from, to_location, revision=None):
1416
"""Copy branch_from into the existing directory to_location.
1418
If revision is not None, the head of the new branch will be revision.
1420
from bzrlib.merge import merge
1421
from bzrlib.branch import Branch
1422
br_to = Branch(to_location, init=True)
1423
br_to.set_root_id(branch_from.get_root_id())
1424
if revision is None:
1425
revno = branch_from.revno()
1427
revno, rev_id = branch_from.get_revision_info(revision)
1428
br_to.update_revisions(branch_from, stop_revision=revno)
1429
merge((to_location, -1), (to_location, 0), this_dir=to_location,
1430
check_clean=False, ignore_zero=True)
1431
from_location = pull_loc(branch_from)
1432
br_to.controlfile("x-pull", "wb").write(from_location + "\n")