21
from warnings import warn
22
25
from bzrlib.trace import mutter, note
23
from bzrlib.osutils import isdir, quotefn, compact_date, rand_bytes, \
25
sha_file, appendpath, file_kind
27
from bzrlib.store import copy_all
28
from bzrlib.errors import BzrError, InvalidRevisionNumber, InvalidRevisionId, \
29
DivergedBranches, NotBranchError, UnlistableStore, UnlistableBranch
26
from bzrlib.osutils import (isdir, quotefn, compact_date, rand_bytes,
27
rename, splitpath, sha_file, appendpath,
29
from bzrlib.errors import (BzrError, InvalidRevisionNumber, InvalidRevisionId,
30
NoSuchRevision, HistoryMissing, NotBranchError,
31
DivergedBranches, LockError, UnlistableStore,
30
33
from bzrlib.textui import show_status
31
from bzrlib.revision import Revision, is_ancestor
34
from bzrlib.revision import Revision, validate_revision_id, is_ancestor
32
35
from bzrlib.delta import compare_trees
33
36
from bzrlib.tree import EmptyTree, RevisionTree
37
from bzrlib.inventory import Inventory
38
from bzrlib.weavestore import WeaveStore
39
from bzrlib.store import copy_all, ImmutableStore
39
BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
44
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
45
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
40
46
## TODO: Maybe include checks for common corruption of newlines, etc?
43
49
# TODO: Some operations like log might retrieve the same revisions
44
50
# repeatedly to calculate deltas. We could perhaps have a weakref
45
# cache in memory to make this faster.
51
# cache in memory to make this faster. In general anything can be
52
# cached in memory between lock and unlock operations.
47
54
def find_branch(*ignored, **ignored_too):
48
55
# XXX: leave this here for about one release, then remove it
182
198
_lock_mode = None
183
199
_lock_count = None
186
def __init__(self, base, init=False, find_root=True):
201
_inventory_weave = None
203
# Map some sort of prefix into a namespace
204
# stuff like "revno:10", "revid:", etc.
205
# This should match a prefix with a function which accepts
206
REVISION_NAMESPACES = {}
208
def push_stores(self, branch_to):
209
"""Copy the content of this branches store to branch_to."""
210
if (self._branch_format != branch_to._branch_format
211
or self._branch_format != 4):
212
from bzrlib.fetch import greedy_fetch
213
mutter("falling back to fetch logic to push between %s(%s) and %s(%s)",
214
self, self._branch_format, branch_to, branch_to._branch_format)
215
greedy_fetch(to_branch=branch_to, from_branch=self,
216
revision=self.last_revision())
219
store_pairs = ((self.text_store, branch_to.text_store),
220
(self.inventory_store, branch_to.inventory_store),
221
(self.revision_store, branch_to.revision_store))
223
for from_store, to_store in store_pairs:
224
copy_all(from_store, to_store)
225
except UnlistableStore:
226
raise UnlistableBranch(from_store)
228
def __init__(self, base, init=False, find_root=True,
229
relax_version_check=False):
187
230
"""Create new branch object at a particular location.
189
232
base -- Base directory for the branch. May be a file:// url.
210
257
self.base = os.path.realpath(base)
211
258
if not isdir(self.controlfilename('.')):
212
raise NotBranchError("not a bzr branch: %s" % quotefn(base),
213
['use "bzr init" to initialize a new working tree',
214
'current bzr can only operate from top-of-tree'])
217
self.text_store = ImmutableStore(self.controlfilename('text-store'))
218
self.revision_store = ImmutableStore(self.controlfilename('revision-store'))
219
self.inventory_store = ImmutableStore(self.controlfilename('inventory-store'))
259
raise NotBranchError('not a bzr branch: %s' % quotefn(base),
260
['use "bzr init" to initialize a '
262
self._check_format(relax_version_check)
263
cfn = self.controlfilename
264
if self._branch_format == 4:
265
self.inventory_store = ImmutableStore(cfn('inventory-store'))
266
self.text_store = ImmutableStore(cfn('text-store'))
267
elif self._branch_format == 5:
268
self.control_weaves = WeaveStore(cfn([]))
269
self.weave_store = WeaveStore(cfn('weaves'))
271
# FIXME: Unify with make_control_files
272
self.control_weaves.put_empty_weave('inventory')
273
self.control_weaves.put_empty_weave('ancestry')
274
self.revision_store = ImmutableStore(cfn('revision-store'))
222
277
def __str__(self):
314
368
raise BzrError("invalid controlfile mode %r" % mode)
316
370
def _make_control(self):
317
from bzrlib.inventory import Inventory
319
371
os.mkdir(self.controlfilename([]))
320
372
self.controlfile('README', 'w').write(
321
373
"This is a Bazaar-NG control directory.\n"
322
374
"Do not change any files in this directory.\n")
323
self.controlfile('branch-format', 'w').write(BZR_BRANCH_FORMAT)
324
for d in ('text-store', 'inventory-store', 'revision-store'):
375
self.controlfile('branch-format', 'w').write(BZR_BRANCH_FORMAT_5)
376
for d in ('text-store', 'revision-store',
325
378
os.mkdir(self.controlfilename(d))
326
for f in ('revision-history', 'merged-patches',
327
'pending-merged-patches', 'branch-name',
379
for f in ('revision-history',
329
382
'pending-merges'):
330
383
self.controlfile(f, 'w').write('')
334
387
# them; they're not needed for now and so ommitted for
336
389
f = self.controlfile('inventory','w')
337
bzrlib.xml.serializer_v4.write_inventory(Inventory(), f)
340
def _check_format(self):
390
bzrlib.xml5.serializer_v5.write_inventory(Inventory(), f)
393
def _check_format(self, relax_version_check):
341
394
"""Check this branch format is supported.
343
The current tool only supports the current unstable format.
396
The format level is stored, as an integer, in
397
self._branch_format for code that needs to check it later.
345
399
In the future, we might need different in-memory Branch
346
400
classes to support downlevel branches. But not yet.
348
# This ignores newlines so that we can open branches created
349
# on Windows from Linux and so on. I think it might be better
350
# to always make all internal files in unix format.
351
fmt = self.controlfile('branch-format', 'r').read()
352
fmt = fmt.replace('\r\n', '\n')
353
if fmt != BZR_BRANCH_FORMAT:
403
fmt = self.controlfile('branch-format', 'r').read()
405
if e.errno == errno.ENOENT:
406
raise NotBranchError(self.base)
410
if fmt == BZR_BRANCH_FORMAT_5:
411
self._branch_format = 5
412
elif fmt == BZR_BRANCH_FORMAT_4:
413
self._branch_format = 4
415
if (not relax_version_check
416
and self._branch_format != 5):
354
417
raise BzrError('sorry, branch format %r not supported' % fmt,
355
418
['use a different bzr version',
356
419
'or remove the .bzr directory and "bzr init" again'])
644
715
return compare_trees(old_tree, new_tree)
648
718
def get_revision_sha1(self, revision_id):
649
719
"""Hash the stored value of a revision, and return it."""
650
# In the future, revision entries will be signed. At that
651
# point, it is probably best *not* to include the signature
652
# in the revision hash. Because that lets you re-sign
653
# the revision, (add signatures/remove signatures) and still
654
# have all hash pointers stay consistent.
655
# But for now, just hash the contents.
656
return bzrlib.osutils.sha_file(self.get_revision_xml(revision_id))
659
def get_inventory(self, inventory_id):
660
"""Get Inventory object by hash.
662
TODO: Perhaps for this and similar methods, take a revision
663
parameter which can be either an integer revno or a
665
from bzrlib.inventory import Inventory
667
f = self.get_inventory_xml_file(inventory_id)
668
return bzrlib.xml.serializer_v4.read_inventory(f)
671
def get_inventory_xml(self, inventory_id):
720
return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
723
def _get_ancestry_weave(self):
724
return self.control_weaves.get_weave('ancestry')
727
def get_ancestry(self, revision_id):
728
"""Return a list of revision-ids integrated by a revision.
731
if revision_id is None:
733
w = self._get_ancestry_weave()
734
return [None] + [l[:-1] for l in w.get_iter(w.lookup(revision_id))]
737
def get_inventory_weave(self):
738
return self.control_weaves.get_weave('inventory')
741
def get_inventory(self, revision_id):
742
"""Get Inventory object by hash."""
743
xml = self.get_inventory_xml(revision_id)
744
return bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
747
def get_inventory_xml(self, revision_id):
672
748
"""Get inventory XML as a file object."""
673
return self.inventory_store[inventory_id]
675
get_inventory_xml_file = get_inventory_xml
678
def get_inventory_sha1(self, inventory_id):
750
assert isinstance(revision_id, basestring), type(revision_id)
751
iw = self.get_inventory_weave()
752
return iw.get_text(iw.lookup(revision_id))
754
raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
757
def get_inventory_sha1(self, revision_id):
679
758
"""Return the sha1 hash of the inventory entry
681
return sha_file(self.get_inventory_xml(inventory_id))
760
return self.get_revision(revision_id).inventory_sha1
684
763
def get_revision_inventory(self, revision_id):
685
764
"""Return inventory of a past revision."""
686
# bzr 0.0.6 imposes the constraint that the inventory_id
765
# TODO: Unify this with get_inventory()
766
# bzr 0.0.6 and later imposes the constraint that the inventory_id
687
767
# must be the same as its revision, so this is trivial.
688
768
if revision_id == None:
689
from bzrlib.inventory import Inventory
690
769
return Inventory(self.get_root_id())
692
771
return self.get_inventory(revision_id)
695
774
def revision_history(self):
696
"""Return sequence of revision hashes on to this branch.
698
>>> ScratchBranch().revision_history()
775
"""Return sequence of revision hashes on to this branch."""
703
778
return [l.rstrip('\r\n') for l in
809
888
if stop_revision is None:
810
889
stop_revision = other_len
811
elif stop_revision > other_len:
812
raise bzrlib.errors.NoSuchRevision(self, stop_revision)
891
assert isinstance(stop_revision, int)
892
if stop_revision > other_len:
893
raise bzrlib.errors.NoSuchRevision(self, stop_revision)
814
894
return other_history[self_len:stop_revision]
817
896
def update_revisions(self, other, stop_revision=None):
818
"""Pull in all new revisions from other branch.
897
"""Pull in new perfect-fit revisions."""
820
898
from bzrlib.fetch import greedy_fetch
821
899
from bzrlib.revision import get_intervening_revisions
823
pb = bzrlib.ui.ui_factory.progress_bar()
824
pb.update('comparing histories')
825
900
if stop_revision is None:
826
other_revision = other.last_patch()
828
other_revision = other.get_rev_id(stop_revision)
829
count = greedy_fetch(self, other, other_revision, pb)[0]
831
revision_ids = self.missing_revisions(other, stop_revision)
832
except DivergedBranches, e:
834
revision_ids = get_intervening_revisions(self.last_patch(),
835
other_revision, self)
836
assert self.last_patch() not in revision_ids
837
except bzrlib.errors.NotAncestor:
838
if is_ancestor(self.last_patch(), other_revision, self):
843
self.append_revision(*revision_ids)
846
def install_revisions(self, other, revision_ids, pb):
847
if hasattr(other.revision_store, "prefetch"):
848
other.revision_store.prefetch(revision_ids)
849
if hasattr(other.inventory_store, "prefetch"):
851
for rev_id in revision_ids:
853
revision = other.get_revision(rev_id).inventory_id
854
inventory_ids.append(revision)
855
except bzrlib.errors.NoSuchRevision:
857
other.inventory_store.prefetch(inventory_ids)
860
pb = bzrlib.ui.ui_factory.progress_bar()
867
for i, rev_id in enumerate(revision_ids):
868
pb.update('fetching revision', i+1, len(revision_ids))
870
rev = other.get_revision(rev_id)
871
except bzrlib.errors.NoSuchRevision:
875
revisions.append(rev)
876
inv = other.get_inventory(str(rev.inventory_id))
877
for key, entry in inv.iter_entries():
878
if entry.text_id is None:
880
if entry.text_id not in self.text_store:
881
needed_texts.add(entry.text_id)
885
count, cp_fail = self.text_store.copy_multi(other.text_store,
887
#print "Added %d texts." % count
888
inventory_ids = [ f.inventory_id for f in revisions ]
889
count, cp_fail = self.inventory_store.copy_multi(other.inventory_store,
891
#print "Added %d inventories." % count
892
revision_ids = [ f.revision_id for f in revisions]
894
count, cp_fail = self.revision_store.copy_multi(other.revision_store,
897
assert len(cp_fail) == 0
898
return count, failures
901
stop_revision = other.last_revision()
902
greedy_fetch(to_branch=self, from_branch=other,
903
revision=stop_revision)
904
pullable_revs = self.missing_revisions(
905
other, other.revision_id_to_revno(stop_revision))
907
greedy_fetch(to_branch=self,
909
revision=pullable_revs[-1])
910
self.append_revision(*pullable_revs)
901
913
def commit(self, *args, **kw):
902
from bzrlib.commit import commit
903
commit(self, *args, **kw)
914
from bzrlib.commit import Commit
915
Commit().commit(self, *args, **kw)
905
917
def revision_id_to_revno(self, revision_id):
906
918
"""Given a revision id, return its revno"""
919
if revision_id is None:
907
921
history = self.revision_history()
909
923
return history.index(revision_id) + 1
1335
1346
return gen_file_id('TREE_ROOT')
1338
def copy_branch(branch_from, to_location, revno=None, basis_branch=None):
1339
"""Copy branch_from into the existing directory to_location.
1342
If not None, only revisions up to this point will be copied.
1343
The head of the new branch will be that revision.
1346
The name of a local directory that exists but is empty.
1349
The revision to copy up to
1352
A local branch to copy revisions from, related to branch_from
1354
from bzrlib.merge import merge
1356
assert isinstance(branch_from, Branch)
1357
assert isinstance(to_location, basestring)
1359
br_to = Branch.initialize(to_location)
1360
if basis_branch is not None:
1361
copy_stores(basis_branch, br_to)
1362
br_to.set_root_id(branch_from.get_root_id())
1364
revno = branch_from.revno()
1365
br_to.update_revisions(branch_from, stop_revision=revno)
1366
merge((to_location, -1), (to_location, 0), this_dir=to_location,
1367
check_clean=False, ignore_zero=True)
1368
br_to.set_parent(branch_from.base)
1371
def copy_stores(branch_from, branch_to):
1372
"""Copies all entries from branch stores to another branch's stores.
1374
store_pairs = ((branch_from.text_store, branch_to.text_store),
1375
(branch_from.inventory_store, branch_to.inventory_store),
1376
(branch_from.revision_store, branch_to.revision_store))
1378
for from_store, to_store in store_pairs:
1379
copy_all(from_store, to_store)
1380
except UnlistableStore:
1381
raise UnlistableBranch(from_store)