1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from bzrlib.lazy_import import lazy_import
19
lazy_import(globals(), """
20
from itertools import chain
24
config as _mod_config,
30
revision as _mod_revision,
36
from bzrlib.config import BranchConfig
37
from bzrlib.repofmt.pack_repo import RepositoryFormatPackDevelopment1Subtree
38
from bzrlib.tag import (
44
from bzrlib.decorators import needs_read_lock, needs_write_lock
45
from bzrlib.hooks import Hooks
46
from bzrlib.symbol_versioning import (
50
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
53
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
54
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
55
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
58
# TODO: Maybe include checks for common corruption of newlines, etc?
60
# TODO: Some operations like log might retrieve the same revisions
61
# repeatedly to calculate deltas. We could perhaps have a weakref
62
# cache in memory to make this faster. In general anything can be
63
# cached in memory between lock and unlock operations. .. nb thats
64
# what the transaction identity map provides
67
######################################################################
71
"""Branch holding a history of revisions.
74
Base directory/url of the branch.
76
hooks: An instance of BranchHooks.
78
# this is really an instance variable - FIXME move it there
82
# override this to set the strategy for storing tags
84
return DisabledTags(self)
86
def __init__(self, *ignored, **ignored_too):
87
self.tags = self._make_tags()
88
self._revision_history_cache = None
89
self._revision_id_to_revno_cache = None
93
"""Called by init to allow simpler extension of the base class."""
96
"""Break a lock if one is present from another instance.
98
Uses the ui factory to ask for confirmation if the lock may be from
101
This will probe the repository for its lock as well.
103
self.control_files.break_lock()
104
self.repository.break_lock()
105
master = self.get_master_branch()
106
if master is not None:
110
def open(base, _unsupported=False, possible_transports=None):
111
"""Open the branch rooted at base.
113
For instance, if the branch is at URL/.bzr/branch,
114
Branch.open(URL) -> a Branch instance.
116
control = bzrdir.BzrDir.open(base, _unsupported,
117
possible_transports=possible_transports)
118
return control.open_branch(_unsupported)
121
def open_from_transport(transport, _unsupported=False):
122
"""Open the branch rooted at transport"""
123
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
124
return control.open_branch(_unsupported)
127
def open_containing(url, possible_transports=None):
128
"""Open an existing branch which contains url.
130
This probes for a branch at url, and searches upwards from there.
132
Basically we keep looking up until we find the control directory or
133
run into the root. If there isn't one, raises NotBranchError.
134
If there is one and it is either an unrecognised format or an unsupported
135
format, UnknownFormatError or UnsupportedFormatError are raised.
136
If there is one, it is returned, along with the unused portion of url.
138
control, relpath = bzrdir.BzrDir.open_containing(url,
140
return control.open_branch(), relpath
142
def get_config(self):
143
return BranchConfig(self)
146
return self.get_config().get_nickname()
148
def _set_nick(self, nick):
149
self.get_config().set_user_option('nickname', nick, warn_masked=True)
151
nick = property(_get_nick, _set_nick)
154
raise NotImplementedError(self.is_locked)
156
def lock_write(self):
157
raise NotImplementedError(self.lock_write)
160
raise NotImplementedError(self.lock_read)
163
raise NotImplementedError(self.unlock)
165
def peek_lock_mode(self):
166
"""Return lock mode for the Branch: 'r', 'w' or None"""
167
raise NotImplementedError(self.peek_lock_mode)
169
def get_physical_lock_status(self):
170
raise NotImplementedError(self.get_physical_lock_status)
173
def get_revision_id_to_revno_map(self):
174
"""Return the revision_id => dotted revno map.
176
This will be regenerated on demand, but will be cached.
178
:return: A dictionary mapping revision_id => dotted revno.
179
This dictionary should not be modified by the caller.
181
if self._revision_id_to_revno_cache is not None:
182
mapping = self._revision_id_to_revno_cache
184
mapping = self._gen_revno_map()
185
self._cache_revision_id_to_revno(mapping)
186
# TODO: jam 20070417 Since this is being cached, should we be returning
188
# I would rather not, and instead just declare that users should not
189
# modify the return value.
192
def _gen_revno_map(self):
193
"""Create a new mapping from revision ids to dotted revnos.
195
Dotted revnos are generated based on the current tip in the revision
197
This is the worker function for get_revision_id_to_revno_map, which
198
just caches the return value.
200
:return: A dictionary mapping revision_id => dotted revno.
202
last_revision = self.last_revision()
203
revision_graph = repository._old_get_graph(self.repository,
205
merge_sorted_revisions = tsort.merge_sort(
210
revision_id_to_revno = dict((rev_id, revno)
211
for seq_num, rev_id, depth, revno, end_of_merge
212
in merge_sorted_revisions)
213
return revision_id_to_revno
215
def leave_lock_in_place(self):
216
"""Tell this branch object not to release the physical lock when this
219
If lock_write doesn't return a token, then this method is not supported.
221
self.control_files.leave_in_place()
223
def dont_leave_lock_in_place(self):
224
"""Tell this branch object to release the physical lock when this
225
object is unlocked, even if it didn't originally acquire it.
227
If lock_write doesn't return a token, then this method is not supported.
229
self.control_files.dont_leave_in_place()
231
@deprecated_method(deprecated_in((0, 16, 0)))
232
def abspath(self, name):
233
"""Return absolute filename for something in the branch
235
XXX: Robert Collins 20051017 what is this used for? why is it a branch
236
method and not a tree method.
238
raise NotImplementedError(self.abspath)
240
def bind(self, other):
241
"""Bind the local branch the other branch.
243
:param other: The branch to bind to
246
raise errors.UpgradeRequired(self.base)
249
def fetch(self, from_branch, last_revision=None, pb=None):
250
"""Copy revisions from from_branch into this branch.
252
:param from_branch: Where to copy from.
253
:param last_revision: What revision to stop at (None for at the end
255
:param pb: An optional progress bar to use.
257
Returns the copied revision count and the failed revisions in a tuple:
260
if self.base == from_branch.base:
263
nested_pb = ui.ui_factory.nested_progress_bar()
268
from_branch.lock_read()
270
if last_revision is None:
271
pb.update('get source history')
272
last_revision = from_branch.last_revision()
273
last_revision = _mod_revision.ensure_null(last_revision)
274
return self.repository.fetch(from_branch.repository,
275
revision_id=last_revision,
278
if nested_pb is not None:
282
def get_bound_location(self):
283
"""Return the URL of the branch we are bound to.
285
Older format branches cannot bind, please be sure to use a metadir
290
def get_old_bound_location(self):
291
"""Return the URL of the branch we used to be bound to
293
raise errors.UpgradeRequired(self.base)
295
def get_commit_builder(self, parents, config=None, timestamp=None,
296
timezone=None, committer=None, revprops=None,
298
"""Obtain a CommitBuilder for this branch.
300
:param parents: Revision ids of the parents of the new revision.
301
:param config: Optional configuration to use.
302
:param timestamp: Optional timestamp recorded for commit.
303
:param timezone: Optional timezone for timestamp.
304
:param committer: Optional committer to set for commit.
305
:param revprops: Optional dictionary of revision properties.
306
:param revision_id: Optional revision id.
310
config = self.get_config()
312
return self.repository.get_commit_builder(self, parents, config,
313
timestamp, timezone, committer, revprops, revision_id)
315
def get_master_branch(self, possible_transports=None):
316
"""Return the branch we are bound to.
318
:return: Either a Branch, or None
322
def get_revision_delta(self, revno):
323
"""Return the delta for one revision.
325
The delta is relative to its mainline predecessor, or the
326
empty tree for revision 1.
328
rh = self.revision_history()
329
if not (1 <= revno <= len(rh)):
330
raise errors.InvalidRevisionNumber(revno)
331
return self.repository.get_revision_delta(rh[revno-1])
333
def get_stacked_on(self):
334
"""Get the URL this branch is stacked against.
336
:raises NotStacked: If the branch is not stacked.
337
:raises UnstackableBranchFormat: If the branch does not support
340
raise NotImplementedError(self.get_stacked_on)
342
def print_file(self, file, revision_id):
343
"""Print `file` to stdout."""
344
raise NotImplementedError(self.print_file)
346
def set_revision_history(self, rev_history):
347
raise NotImplementedError(self.set_revision_history)
349
def set_stacked_on(self, url):
350
"""Set the URL this branch is stacked against.
352
:raises UnstackableBranchFormat: If the branch does not support
354
:raises UnstackableRepositoryFormat: If the repository does not support
357
raise NotImplementedError(self.set_stacked_on)
359
def _cache_revision_history(self, rev_history):
360
"""Set the cached revision history to rev_history.
362
The revision_history method will use this cache to avoid regenerating
363
the revision history.
365
This API is semi-public; it only for use by subclasses, all other code
366
should consider it to be private.
368
self._revision_history_cache = rev_history
370
def _cache_revision_id_to_revno(self, revision_id_to_revno):
371
"""Set the cached revision_id => revno map to revision_id_to_revno.
373
This API is semi-public; it only for use by subclasses, all other code
374
should consider it to be private.
376
self._revision_id_to_revno_cache = revision_id_to_revno
378
def _clear_cached_state(self):
379
"""Clear any cached data on this branch, e.g. cached revision history.
381
This means the next call to revision_history will need to call
382
_gen_revision_history.
384
This API is semi-public; it only for use by subclasses, all other code
385
should consider it to be private.
387
self._revision_history_cache = None
388
self._revision_id_to_revno_cache = None
390
def _gen_revision_history(self):
391
"""Return sequence of revision hashes on to this branch.
393
Unlike revision_history, this method always regenerates or rereads the
394
revision history, i.e. it does not cache the result, so repeated calls
397
Concrete subclasses should override this instead of revision_history so
398
that subclasses do not need to deal with caching logic.
400
This API is semi-public; it only for use by subclasses, all other code
401
should consider it to be private.
403
raise NotImplementedError(self._gen_revision_history)
406
def revision_history(self):
407
"""Return sequence of revision ids on this branch.
409
This method will cache the revision history for as long as it is safe to
412
if 'evil' in debug.debug_flags:
413
mutter_callsite(3, "revision_history scales with history.")
414
if self._revision_history_cache is not None:
415
history = self._revision_history_cache
417
history = self._gen_revision_history()
418
self._cache_revision_history(history)
422
"""Return current revision number for this branch.
424
That is equivalent to the number of revisions committed to
427
return self.last_revision_info()[0]
430
"""Older format branches cannot bind or unbind."""
431
raise errors.UpgradeRequired(self.base)
433
def set_append_revisions_only(self, enabled):
434
"""Older format branches are never restricted to append-only"""
435
raise errors.UpgradeRequired(self.base)
437
def last_revision(self):
438
"""Return last revision id, or NULL_REVISION."""
439
return self.last_revision_info()[1]
441
def last_revision_info(self):
442
"""Return information about the last revision.
444
:return: A tuple (revno, last_revision_id).
446
rh = self.revision_history()
449
return (revno, rh[-1])
451
return (0, _mod_revision.NULL_REVISION)
453
@deprecated_method(deprecated_in((1, 6, 0)))
454
def missing_revisions(self, other, stop_revision=None):
455
"""Return a list of new revisions that would perfectly fit.
457
If self and other have not diverged, return a list of the revisions
458
present in other, but missing from self.
460
self_history = self.revision_history()
461
self_len = len(self_history)
462
other_history = other.revision_history()
463
other_len = len(other_history)
464
common_index = min(self_len, other_len) -1
465
if common_index >= 0 and \
466
self_history[common_index] != other_history[common_index]:
467
raise errors.DivergedBranches(self, other)
469
if stop_revision is None:
470
stop_revision = other_len
472
if stop_revision > other_len:
473
raise errors.NoSuchRevision(self, stop_revision)
474
return other_history[self_len:stop_revision]
477
def update_revisions(self, other, stop_revision=None, overwrite=False,
479
"""Pull in new perfect-fit revisions.
481
:param other: Another Branch to pull from
482
:param stop_revision: Updated until the given revision
483
:param overwrite: Always set the branch pointer, rather than checking
484
to see if it is a proper descendant.
485
:param graph: A Graph object that can be used to query history
486
information. This can be None.
491
other_revno, other_last_revision = other.last_revision_info()
492
stop_revno = None # unknown
493
if stop_revision is None:
494
stop_revision = other_last_revision
495
if _mod_revision.is_null(stop_revision):
496
# if there are no commits, we're done.
498
stop_revno = other_revno
500
# what's the current last revision, before we fetch [and change it
502
last_rev = _mod_revision.ensure_null(self.last_revision())
503
# we fetch here so that we don't process data twice in the common
504
# case of having something to pull, and so that the check for
505
# already merged can operate on the just fetched graph, which will
506
# be cached in memory.
507
self.fetch(other, stop_revision)
508
# Check to see if one is an ancestor of the other
511
graph = self.repository.get_graph()
512
heads = graph.heads([stop_revision, last_rev])
513
if heads == set([last_rev]):
514
# The current revision is a decendent of the target,
517
elif heads == set([stop_revision, last_rev]):
518
# These branches have diverged
519
raise errors.DivergedBranches(self, other)
520
elif heads != set([stop_revision]):
521
raise AssertionError("invalid heads: %r" % heads)
522
if stop_revno is None:
524
graph = self.repository.get_graph()
525
this_revno, this_last_revision = self.last_revision_info()
526
stop_revno = graph.find_distance_to_null(stop_revision,
527
[(other_last_revision, other_revno),
528
(this_last_revision, this_revno)])
529
self.set_last_revision_info(stop_revno, stop_revision)
535
def revision_id_to_revno(self, revision_id):
536
"""Given a revision id, return its revno"""
537
if _mod_revision.is_null(revision_id):
539
history = self.revision_history()
541
return history.index(revision_id) + 1
543
raise errors.NoSuchRevision(self, revision_id)
545
def get_rev_id(self, revno, history=None):
546
"""Find the revision id of the specified revno."""
548
return _mod_revision.NULL_REVISION
550
history = self.revision_history()
551
if revno <= 0 or revno > len(history):
552
raise errors.NoSuchRevision(self, revno)
553
return history[revno - 1]
555
def pull(self, source, overwrite=False, stop_revision=None,
556
possible_transports=None):
557
"""Mirror source into this branch.
559
This branch is considered to be 'local', having low latency.
561
:returns: PullResult instance
563
raise NotImplementedError(self.pull)
565
def push(self, target, overwrite=False, stop_revision=None):
566
"""Mirror this branch into target.
568
This branch is considered to be 'local', having low latency.
570
raise NotImplementedError(self.push)
572
def basis_tree(self):
573
"""Return `Tree` object for last revision."""
574
return self.repository.revision_tree(self.last_revision())
576
def rename_one(self, from_rel, to_rel):
579
This can change the directory or the filename or both.
581
raise NotImplementedError(self.rename_one)
583
def move(self, from_paths, to_name):
586
to_name must exist as a versioned directory.
588
If to_name exists and is a directory, the files are moved into
589
it, keeping their old names. If it is a directory,
591
Note that to_name is only the last component of the new name;
592
this doesn't change the directory.
594
This returns a list of (from_path, to_path) pairs for each
597
raise NotImplementedError(self.move)
599
def get_parent(self):
600
"""Return the parent location of the branch.
602
This is the default location for push/pull/missing. The usual
603
pattern is that the user can override it by specifying a
606
raise NotImplementedError(self.get_parent)
608
def _set_config_location(self, name, url, config=None,
609
make_relative=False):
611
config = self.get_config()
615
url = urlutils.relative_url(self.base, url)
616
config.set_user_option(name, url, warn_masked=True)
618
def _get_config_location(self, name, config=None):
620
config = self.get_config()
621
location = config.get_user_option(name)
626
def get_submit_branch(self):
627
"""Return the submit location of the branch.
629
This is the default location for bundle. The usual
630
pattern is that the user can override it by specifying a
633
return self.get_config().get_user_option('submit_branch')
635
def set_submit_branch(self, location):
636
"""Return the submit location of the branch.
638
This is the default location for bundle. The usual
639
pattern is that the user can override it by specifying a
642
self.get_config().set_user_option('submit_branch', location,
645
def get_public_branch(self):
646
"""Return the public location of the branch.
648
This is is used by merge directives.
650
return self._get_config_location('public_branch')
652
def set_public_branch(self, location):
653
"""Return the submit location of the branch.
655
This is the default location for bundle. The usual
656
pattern is that the user can override it by specifying a
659
self._set_config_location('public_branch', location)
661
def get_push_location(self):
662
"""Return the None or the location to push this branch to."""
663
push_loc = self.get_config().get_user_option('push_location')
666
def set_push_location(self, location):
667
"""Set a new push location for this branch."""
668
raise NotImplementedError(self.set_push_location)
670
def set_parent(self, url):
671
raise NotImplementedError(self.set_parent)
675
"""Synchronise this branch with the master branch if any.
677
:return: None or the last_revision pivoted out during the update.
681
def check_revno(self, revno):
683
Check whether a revno corresponds to any revision.
684
Zero (the NULL revision) is considered valid.
687
self.check_real_revno(revno)
689
def check_real_revno(self, revno):
691
Check whether a revno corresponds to a real revision.
692
Zero (the NULL revision) is considered invalid
694
if revno < 1 or revno > self.revno():
695
raise errors.InvalidRevisionNumber(revno)
698
def clone(self, to_bzrdir, revision_id=None):
699
"""Clone this branch into to_bzrdir preserving all semantic values.
701
revision_id: if not None, the revision history in the new branch will
702
be truncated to end with revision_id.
704
result = self._format.initialize(to_bzrdir)
705
self.copy_content_into(result, revision_id=revision_id)
709
def sprout(self, to_bzrdir, revision_id=None):
710
"""Create a new line of development from the branch, into to_bzrdir.
712
revision_id: if not None, the revision history in the new branch will
713
be truncated to end with revision_id.
715
result = self._format.initialize(to_bzrdir)
716
self.copy_content_into(result, revision_id=revision_id)
717
result.set_parent(self.bzrdir.root_transport.base)
720
def _synchronize_history(self, destination, revision_id):
721
"""Synchronize last revision and revision history between branches.
723
This version is most efficient when the destination is also a
724
BzrBranch5, but works for BzrBranch6 as long as the revision
725
history is the true lefthand parent history, and all of the revisions
726
are in the destination's repository. If not, set_revision_history
729
:param destination: The branch to copy the history into
730
:param revision_id: The revision-id to truncate history at. May
731
be None to copy complete history.
733
if revision_id == _mod_revision.NULL_REVISION:
735
new_history = self.revision_history()
736
if revision_id is not None and new_history != []:
738
new_history = new_history[:new_history.index(revision_id) + 1]
740
rev = self.repository.get_revision(revision_id)
741
new_history = rev.get_history(self.repository)[1:]
742
destination.set_revision_history(new_history)
745
def copy_content_into(self, destination, revision_id=None):
746
"""Copy the content of self into destination.
748
revision_id: if not None, the revision history in the new branch will
749
be truncated to end with revision_id.
751
self._synchronize_history(destination, revision_id)
753
parent = self.get_parent()
754
except errors.InaccessibleParent, e:
755
mutter('parent was not accessible to copy: %s', e)
758
destination.set_parent(parent)
759
self.tags.merge_to(destination.tags)
763
"""Check consistency of the branch.
765
In particular this checks that revisions given in the revision-history
766
do actually match up in the revision graph, and that they're all
767
present in the repository.
769
Callers will typically also want to check the repository.
771
:return: A BranchCheckResult.
773
mainline_parent_id = None
774
last_revno, last_revision_id = self.last_revision_info()
775
real_rev_history = list(self.repository.iter_reverse_revision_history(
777
real_rev_history.reverse()
778
if len(real_rev_history) != last_revno:
779
raise errors.BzrCheckError('revno does not match len(mainline)'
780
' %s != %s' % (last_revno, len(real_rev_history)))
781
# TODO: We should probably also check that real_rev_history actually
782
# matches self.revision_history()
783
for revision_id in real_rev_history:
785
revision = self.repository.get_revision(revision_id)
786
except errors.NoSuchRevision, e:
787
raise errors.BzrCheckError("mainline revision {%s} not in repository"
789
# In general the first entry on the revision history has no parents.
790
# But it's not illegal for it to have parents listed; this can happen
791
# in imports from Arch when the parents weren't reachable.
792
if mainline_parent_id is not None:
793
if mainline_parent_id not in revision.parent_ids:
794
raise errors.BzrCheckError("previous revision {%s} not listed among "
796
% (mainline_parent_id, revision_id))
797
mainline_parent_id = revision_id
798
return BranchCheckResult(self)
800
def _get_checkout_format(self):
801
"""Return the most suitable metadir for a checkout of this branch.
802
Weaves are used if this branch's repository uses weaves.
804
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
805
from bzrlib.repofmt import weaverepo
806
format = bzrdir.BzrDirMetaFormat1()
807
format.repository_format = weaverepo.RepositoryFormat7()
809
format = self.repository.bzrdir.checkout_metadir()
810
format.set_branch_format(self._format)
813
def create_checkout(self, to_location, revision_id=None,
814
lightweight=False, accelerator_tree=None,
816
"""Create a checkout of a branch.
818
:param to_location: The url to produce the checkout at
819
:param revision_id: The revision to check out
820
:param lightweight: If True, produce a lightweight checkout, otherwise,
821
produce a bound branch (heavyweight checkout)
822
:param accelerator_tree: A tree which can be used for retrieving file
823
contents more quickly than the revision tree, i.e. a workingtree.
824
The revision tree will be used for cases where accelerator_tree's
825
content is different.
826
:param hardlink: If true, hard-link files from accelerator_tree,
828
:return: The tree of the created checkout
830
t = transport.get_transport(to_location)
833
format = self._get_checkout_format()
834
checkout = format.initialize_on_transport(t)
835
from_branch = BranchReferenceFormat().initialize(checkout, self)
837
format = self._get_checkout_format()
838
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
839
to_location, force_new_tree=False, format=format)
840
checkout = checkout_branch.bzrdir
841
checkout_branch.bind(self)
842
# pull up to the specified revision_id to set the initial
843
# branch tip correctly, and seed it with history.
844
checkout_branch.pull(self, stop_revision=revision_id)
846
tree = checkout.create_workingtree(revision_id,
847
from_branch=from_branch,
848
accelerator_tree=accelerator_tree,
850
basis_tree = tree.basis_tree()
851
basis_tree.lock_read()
853
for path, file_id in basis_tree.iter_references():
854
reference_parent = self.reference_parent(file_id, path)
855
reference_parent.create_checkout(tree.abspath(path),
856
basis_tree.get_reference_revision(file_id, path),
863
def reconcile(self, thorough=True):
864
"""Make sure the data stored in this branch is consistent."""
865
from bzrlib.reconcile import BranchReconciler
866
reconciler = BranchReconciler(self, thorough=thorough)
867
reconciler.reconcile()
870
def reference_parent(self, file_id, path):
871
"""Return the parent branch for a tree-reference file_id
872
:param file_id: The file_id of the tree reference
873
:param path: The path of the file_id in the tree
874
:return: A branch associated with the file_id
876
# FIXME should provide multiple branches, based on config
877
return Branch.open(self.bzrdir.root_transport.clone(path).base)
879
def supports_tags(self):
880
return self._format.supports_tags()
883
class BranchFormat(object):
884
"""An encapsulation of the initialization and open routines for a format.
886
Formats provide three things:
887
* An initialization routine,
891
Formats are placed in an dict by their format string for reference
892
during branch opening. Its not required that these be instances, they
893
can be classes themselves with class methods - it simply depends on
894
whether state is needed for a given format or not.
896
Once a format is deprecated, just deprecate the initialize and open
897
methods on the format class. Do not deprecate the object, as the
898
object will be created every time regardless.
901
_default_format = None
902
"""The default format used for new branches."""
905
"""The known formats."""
907
def __eq__(self, other):
908
return self.__class__ is other.__class__
910
def __ne__(self, other):
911
return not (self == other)
914
def find_format(klass, a_bzrdir):
915
"""Return the format for the branch object in a_bzrdir."""
917
transport = a_bzrdir.get_branch_transport(None)
918
format_string = transport.get("format").read()
919
return klass._formats[format_string]
920
except errors.NoSuchFile:
921
raise errors.NotBranchError(path=transport.base)
923
raise errors.UnknownFormatError(format=format_string, kind='branch')
926
def get_default_format(klass):
927
"""Return the current default format."""
928
return klass._default_format
930
def get_reference(self, a_bzrdir):
931
"""Get the target reference of the branch in a_bzrdir.
933
format probing must have been completed before calling
934
this method - it is assumed that the format of the branch
935
in a_bzrdir is correct.
937
:param a_bzrdir: The bzrdir to get the branch data from.
938
:return: None if the branch is not a reference branch.
943
def set_reference(self, a_bzrdir, to_branch):
944
"""Set the target reference of the branch in a_bzrdir.
946
format probing must have been completed before calling
947
this method - it is assumed that the format of the branch
948
in a_bzrdir is correct.
950
:param a_bzrdir: The bzrdir to set the branch reference for.
951
:param to_branch: branch that the checkout is to reference
953
raise NotImplementedError(self.set_reference)
955
def get_format_string(self):
956
"""Return the ASCII format string that identifies this format."""
957
raise NotImplementedError(self.get_format_string)
959
def get_format_description(self):
960
"""Return the short format description for this format."""
961
raise NotImplementedError(self.get_format_description)
963
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
965
"""Initialize a branch in a bzrdir, with specified files
967
:param a_bzrdir: The bzrdir to initialize the branch in
968
:param utf8_files: The files to create as a list of
969
(filename, content) tuples
970
:param set_format: If True, set the format with
971
self.get_format_string. (BzrBranch4 has its format set
973
:return: a branch in this format
975
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
976
branch_transport = a_bzrdir.get_branch_transport(self)
978
'metadir': ('lock', lockdir.LockDir),
979
'branch4': ('branch-lock', lockable_files.TransportLock),
981
lock_name, lock_class = lock_map[lock_type]
982
control_files = lockable_files.LockableFiles(branch_transport,
983
lock_name, lock_class)
984
control_files.create_lock()
985
control_files.lock_write()
987
utf8_files += [('format', self.get_format_string())]
989
for (filename, content) in utf8_files:
990
branch_transport.put_bytes(
992
mode=a_bzrdir._get_file_mode())
994
control_files.unlock()
995
return self.open(a_bzrdir, _found=True)
997
def initialize(self, a_bzrdir):
998
"""Create a branch of this format in a_bzrdir."""
999
raise NotImplementedError(self.initialize)
1001
def is_supported(self):
1002
"""Is this format supported?
1004
Supported formats can be initialized and opened.
1005
Unsupported formats may not support initialization or committing or
1006
some other features depending on the reason for not being supported.
1010
def open(self, a_bzrdir, _found=False):
1011
"""Return the branch object for a_bzrdir
1013
_found is a private parameter, do not use it. It is used to indicate
1014
if format probing has already be done.
1016
raise NotImplementedError(self.open)
1019
def register_format(klass, format):
1020
klass._formats[format.get_format_string()] = format
1023
def set_default_format(klass, format):
1024
klass._default_format = format
1027
def unregister_format(klass, format):
1028
del klass._formats[format.get_format_string()]
1031
return self.get_format_string().rstrip()
1033
def supports_tags(self):
1034
"""True if this format supports tags stored in the branch"""
1035
return False # by default
1038
class BranchHooks(Hooks):
1039
"""A dictionary mapping hook name to a list of callables for branch hooks.
1041
e.g. ['set_rh'] Is the list of items to be called when the
1042
set_revision_history function is invoked.
1046
"""Create the default hooks.
1048
These are all empty initially, because by default nothing should get
1051
Hooks.__init__(self)
1052
# Introduced in 0.15:
1053
# invoked whenever the revision history has been set
1054
# with set_revision_history. The api signature is
1055
# (branch, revision_history), and the branch will
1058
# invoked after a push operation completes.
1059
# the api signature is
1061
# containing the members
1062
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1063
# where local is the local target branch or None, master is the target
1064
# master branch, and the rest should be self explanatory. The source
1065
# is read locked and the target branches write locked. Source will
1066
# be the local low-latency branch.
1067
self['post_push'] = []
1068
# invoked after a pull operation completes.
1069
# the api signature is
1071
# containing the members
1072
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1073
# where local is the local branch or None, master is the target
1074
# master branch, and the rest should be self explanatory. The source
1075
# is read locked and the target branches write locked. The local
1076
# branch is the low-latency branch.
1077
self['post_pull'] = []
1078
# invoked before a commit operation takes place.
1079
# the api signature is
1080
# (local, master, old_revno, old_revid, future_revno, future_revid,
1081
# tree_delta, future_tree).
1082
# old_revid is NULL_REVISION for the first commit to a branch
1083
# tree_delta is a TreeDelta object describing changes from the basis
1084
# revision, hooks MUST NOT modify this delta
1085
# future_tree is an in-memory tree obtained from
1086
# CommitBuilder.revision_tree() and hooks MUST NOT modify this tree
1087
self['pre_commit'] = []
1088
# invoked after a commit operation completes.
1089
# the api signature is
1090
# (local, master, old_revno, old_revid, new_revno, new_revid)
1091
# old_revid is NULL_REVISION for the first commit to a branch.
1092
self['post_commit'] = []
1093
# invoked after a uncommit operation completes.
1094
# the api signature is
1095
# (local, master, old_revno, old_revid, new_revno, new_revid) where
1096
# local is the local branch or None, master is the target branch,
1097
# and an empty branch recieves new_revno of 0, new_revid of None.
1098
self['post_uncommit'] = []
1100
# Invoked after the tip of a branch changes.
1101
# the api signature is
1102
# (params) where params is a ChangeBranchTipParams with the members
1103
# (branch, old_revno, new_revno, old_revid, new_revid)
1104
self['post_change_branch_tip'] = []
1107
# install the default hooks into the Branch class.
1108
Branch.hooks = BranchHooks()
1111
class ChangeBranchTipParams(object):
1112
"""Object holding parameters passed to *_change_branch_tip hooks.
1114
There are 5 fields that hooks may wish to access:
1116
:ivar branch: the branch being changed
1117
:ivar old_revno: revision number before the change
1118
:ivar new_revno: revision number after the change
1119
:ivar old_revid: revision id before the change
1120
:ivar new_revid: revision id after the change
1122
The revid fields are strings. The revno fields are integers.
1125
def __init__(self, branch, old_revno, new_revno, old_revid, new_revid):
1126
"""Create a group of ChangeBranchTip parameters.
1128
:param branch: The branch being changed.
1129
:param old_revno: Revision number before the change.
1130
:param new_revno: Revision number after the change.
1131
:param old_revid: Tip revision id before the change.
1132
:param new_revid: Tip revision id after the change.
1134
self.branch = branch
1135
self.old_revno = old_revno
1136
self.new_revno = new_revno
1137
self.old_revid = old_revid
1138
self.new_revid = new_revid
1141
class BzrBranchFormat4(BranchFormat):
1142
"""Bzr branch format 4.
1145
- a revision-history file.
1146
- a branch-lock lock file [ to be shared with the bzrdir ]
1149
def get_format_description(self):
1150
"""See BranchFormat.get_format_description()."""
1151
return "Branch format 4"
1153
def initialize(self, a_bzrdir):
1154
"""Create a branch of this format in a_bzrdir."""
1155
utf8_files = [('revision-history', ''),
1156
('branch-name', ''),
1158
return self._initialize_helper(a_bzrdir, utf8_files,
1159
lock_type='branch4', set_format=False)
1162
super(BzrBranchFormat4, self).__init__()
1163
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1165
def open(self, a_bzrdir, _found=False):
1166
"""Return the branch object for a_bzrdir
1168
_found is a private parameter, do not use it. It is used to indicate
1169
if format probing has already be done.
1172
# we are being called directly and must probe.
1173
raise NotImplementedError
1174
return BzrBranch(_format=self,
1175
_control_files=a_bzrdir._control_files,
1177
_repository=a_bzrdir.open_repository())
1180
return "Bazaar-NG branch format 4"
1183
class BranchFormatMetadir(BranchFormat):
1184
"""Common logic for meta-dir based branch formats."""
1186
def _branch_class(self):
1187
"""What class to instantiate on open calls."""
1188
raise NotImplementedError(self._branch_class)
1190
def open(self, a_bzrdir, _found=False):
1191
"""Return the branch object for a_bzrdir.
1193
_found is a private parameter, do not use it. It is used to indicate
1194
if format probing has already be done.
1197
format = BranchFormat.find_format(a_bzrdir)
1198
if format.__class__ != self.__class__:
1199
raise AssertionError("wrong format %r found for %r" %
1202
transport = a_bzrdir.get_branch_transport(None)
1203
control_files = lockable_files.LockableFiles(transport, 'lock',
1205
return self._branch_class()(_format=self,
1206
_control_files=control_files,
1208
_repository=a_bzrdir.find_repository())
1209
except errors.NoSuchFile:
1210
raise errors.NotBranchError(path=transport.base)
1213
super(BranchFormatMetadir, self).__init__()
1214
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1216
def supports_tags(self):
1220
class BzrBranchFormat5(BranchFormatMetadir):
1221
"""Bzr branch format 5.
1224
- a revision-history file.
1226
- a lock dir guarding the branch itself
1227
- all of this stored in a branch/ subdirectory
1228
- works with shared repositories.
1230
This format is new in bzr 0.8.
1233
def _branch_class(self):
1236
def get_format_string(self):
1237
"""See BranchFormat.get_format_string()."""
1238
return "Bazaar-NG branch format 5\n"
1240
def get_format_description(self):
1241
"""See BranchFormat.get_format_description()."""
1242
return "Branch format 5"
1244
def initialize(self, a_bzrdir):
1245
"""Create a branch of this format in a_bzrdir."""
1246
utf8_files = [('revision-history', ''),
1247
('branch-name', ''),
1249
return self._initialize_helper(a_bzrdir, utf8_files)
1251
def supports_tags(self):
1255
class BzrBranchFormat6(BranchFormatMetadir):
1256
"""Branch format with last-revision and tags.
1258
Unlike previous formats, this has no explicit revision history. Instead,
1259
this just stores the last-revision, and the left-hand history leading
1260
up to there is the history.
1262
This format was introduced in bzr 0.15
1263
and became the default in 0.91.
1266
def _branch_class(self):
1269
def get_format_string(self):
1270
"""See BranchFormat.get_format_string()."""
1271
return "Bazaar Branch Format 6 (bzr 0.15)\n"
1273
def get_format_description(self):
1274
"""See BranchFormat.get_format_description()."""
1275
return "Branch format 6"
1277
def initialize(self, a_bzrdir):
1278
"""Create a branch of this format in a_bzrdir."""
1279
utf8_files = [('last-revision', '0 null:\n'),
1280
('branch.conf', ''),
1283
return self._initialize_helper(a_bzrdir, utf8_files)
1286
class BzrBranchFormat7(BranchFormatMetadir):
1287
"""Branch format with last-revision, tags, and a stacked location pointer.
1289
The stacked location pointer is passed down to the repository and requires
1290
a repository format with supports_external_lookups = True.
1292
This format was introduced in bzr 1.6.
1295
def _branch_class(self):
1298
def get_format_string(self):
1299
"""See BranchFormat.get_format_string()."""
1300
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
1302
def get_format_description(self):
1303
"""See BranchFormat.get_format_description()."""
1304
return "Branch format 7"
1306
def initialize(self, a_bzrdir):
1307
"""Create a branch of this format in a_bzrdir."""
1308
utf8_files = [('last-revision', '0 null:\n'),
1309
('branch.conf', ''),
1312
return self._initialize_helper(a_bzrdir, utf8_files)
1315
super(BzrBranchFormat7, self).__init__()
1316
self._matchingbzrdir.repository_format = \
1317
RepositoryFormatPackDevelopment1Subtree()
1320
class BranchReferenceFormat(BranchFormat):
1321
"""Bzr branch reference format.
1323
Branch references are used in implementing checkouts, they
1324
act as an alias to the real branch which is at some other url.
1331
def get_format_string(self):
1332
"""See BranchFormat.get_format_string()."""
1333
return "Bazaar-NG Branch Reference Format 1\n"
1335
def get_format_description(self):
1336
"""See BranchFormat.get_format_description()."""
1337
return "Checkout reference format 1"
1339
def get_reference(self, a_bzrdir):
1340
"""See BranchFormat.get_reference()."""
1341
transport = a_bzrdir.get_branch_transport(None)
1342
return transport.get('location').read()
1344
def set_reference(self, a_bzrdir, to_branch):
1345
"""See BranchFormat.set_reference()."""
1346
transport = a_bzrdir.get_branch_transport(None)
1347
location = transport.put_bytes('location', to_branch.base)
1349
def initialize(self, a_bzrdir, target_branch=None):
1350
"""Create a branch of this format in a_bzrdir."""
1351
if target_branch is None:
1352
# this format does not implement branch itself, thus the implicit
1353
# creation contract must see it as uninitializable
1354
raise errors.UninitializableFormat(self)
1355
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1356
branch_transport = a_bzrdir.get_branch_transport(self)
1357
branch_transport.put_bytes('location',
1358
target_branch.bzrdir.root_transport.base)
1359
branch_transport.put_bytes('format', self.get_format_string())
1361
a_bzrdir, _found=True,
1362
possible_transports=[target_branch.bzrdir.root_transport])
1365
super(BranchReferenceFormat, self).__init__()
1366
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1368
def _make_reference_clone_function(format, a_branch):
1369
"""Create a clone() routine for a branch dynamically."""
1370
def clone(to_bzrdir, revision_id=None):
1371
"""See Branch.clone()."""
1372
return format.initialize(to_bzrdir, a_branch)
1373
# cannot obey revision_id limits when cloning a reference ...
1374
# FIXME RBC 20060210 either nuke revision_id for clone, or
1375
# emit some sort of warning/error to the caller ?!
1378
def open(self, a_bzrdir, _found=False, location=None,
1379
possible_transports=None):
1380
"""Return the branch that the branch reference in a_bzrdir points at.
1382
_found is a private parameter, do not use it. It is used to indicate
1383
if format probing has already be done.
1386
format = BranchFormat.find_format(a_bzrdir)
1387
if format.__class__ != self.__class__:
1388
raise AssertionError("wrong format %r found for %r" %
1390
if location is None:
1391
location = self.get_reference(a_bzrdir)
1392
real_bzrdir = bzrdir.BzrDir.open(
1393
location, possible_transports=possible_transports)
1394
result = real_bzrdir.open_branch()
1395
# this changes the behaviour of result.clone to create a new reference
1396
# rather than a copy of the content of the branch.
1397
# I did not use a proxy object because that needs much more extensive
1398
# testing, and we are only changing one behaviour at the moment.
1399
# If we decide to alter more behaviours - i.e. the implicit nickname
1400
# then this should be refactored to introduce a tested proxy branch
1401
# and a subclass of that for use in overriding clone() and ....
1403
result.clone = self._make_reference_clone_function(result)
1407
# formats which have no format string are not discoverable
1408
# and not independently creatable, so are not registered.
1409
__format5 = BzrBranchFormat5()
1410
__format6 = BzrBranchFormat6()
1411
__format7 = BzrBranchFormat7()
1412
BranchFormat.register_format(__format5)
1413
BranchFormat.register_format(BranchReferenceFormat())
1414
BranchFormat.register_format(__format6)
1415
BranchFormat.register_format(__format7)
1416
BranchFormat.set_default_format(__format6)
1417
_legacy_formats = [BzrBranchFormat4(),
1420
class BzrBranch(Branch):
1421
"""A branch stored in the actual filesystem.
1423
Note that it's "local" in the context of the filesystem; it doesn't
1424
really matter if it's on an nfs/smb/afs/coda/... share, as long as
1425
it's writable, and can be accessed via the normal filesystem API.
1427
:ivar _transport: Transport for file operations on this branch's
1428
control files, typically pointing to the .bzr/branch directory.
1429
:ivar repository: Repository for this branch.
1430
:ivar base: The url of the base directory for this branch; the one
1431
containing the .bzr directory.
1434
def __init__(self, _format=None,
1435
_control_files=None, a_bzrdir=None, _repository=None):
1436
"""Create new branch object at a particular location."""
1437
if a_bzrdir is None:
1438
raise ValueError('a_bzrdir must be supplied')
1440
self.bzrdir = a_bzrdir
1441
self._base = self.bzrdir.transport.clone('..').base
1442
# XXX: We should be able to just do
1443
# self.base = self.bzrdir.root_transport.base
1444
# but this does not quite work yet -- mbp 20080522
1445
self._format = _format
1446
if _control_files is None:
1447
raise ValueError('BzrBranch _control_files is None')
1448
self.control_files = _control_files
1449
self._transport = _control_files._transport
1450
self.repository = _repository
1451
Branch.__init__(self)
1454
return '%s(%r)' % (self.__class__.__name__, self.base)
1458
def _get_base(self):
1459
"""Returns the directory containing the control directory."""
1462
base = property(_get_base, doc="The URL for the root of this branch.")
1464
@deprecated_method(deprecated_in((0, 16, 0)))
1465
def abspath(self, name):
1466
"""See Branch.abspath."""
1467
return self._transport.abspath(name)
1469
def is_locked(self):
1470
return self.control_files.is_locked()
1472
def lock_write(self, token=None):
1473
repo_token = self.repository.lock_write()
1475
token = self.control_files.lock_write(token=token)
1477
self.repository.unlock()
1481
def lock_read(self):
1482
self.repository.lock_read()
1484
self.control_files.lock_read()
1486
self.repository.unlock()
1490
# TODO: test for failed two phase locks. This is known broken.
1492
self.control_files.unlock()
1494
self.repository.unlock()
1495
if not self.control_files.is_locked():
1496
# we just released the lock
1497
self._clear_cached_state()
1499
def peek_lock_mode(self):
1500
if self.control_files._lock_count == 0:
1503
return self.control_files._lock_mode
1505
def get_physical_lock_status(self):
1506
return self.control_files.get_physical_lock_status()
1509
def print_file(self, file, revision_id):
1510
"""See Branch.print_file."""
1511
return self.repository.print_file(file, revision_id)
1513
def _write_revision_history(self, history):
1514
"""Factored out of set_revision_history.
1516
This performs the actual writing to disk.
1517
It is intended to be called by BzrBranch5.set_revision_history."""
1518
self._transport.put_bytes(
1519
'revision-history', '\n'.join(history),
1520
mode=self.bzrdir._get_file_mode())
1523
def set_revision_history(self, rev_history):
1524
"""See Branch.set_revision_history."""
1525
if 'evil' in debug.debug_flags:
1526
mutter_callsite(3, "set_revision_history scales with history.")
1527
self._write_revision_history(rev_history)
1528
self._clear_cached_state()
1529
self._cache_revision_history(rev_history)
1530
for hook in Branch.hooks['set_rh']:
1531
hook(self, rev_history)
1533
def _run_post_change_branch_tip_hooks(self, old_revno, old_revid):
1534
"""Run the post_change_branch_tip hooks."""
1535
hooks = Branch.hooks['post_change_branch_tip']
1538
new_revno, new_revid = self.last_revision_info()
1539
params = ChangeBranchTipParams(
1540
self, old_revno, new_revno, old_revid, new_revid)
1545
def set_last_revision_info(self, revno, revision_id):
1546
"""Set the last revision of this branch.
1548
The caller is responsible for checking that the revno is correct
1549
for this revision id.
1551
It may be possible to set the branch last revision to an id not
1552
present in the repository. However, branches can also be
1553
configured to check constraints on history, in which case this may not
1556
revision_id = _mod_revision.ensure_null(revision_id)
1557
old_revno, old_revid = self.last_revision_info()
1558
# this old format stores the full history, but this api doesn't
1559
# provide it, so we must generate, and might as well check it's
1561
history = self._lefthand_history(revision_id)
1562
if len(history) != revno:
1563
raise AssertionError('%d != %d' % (len(history), revno))
1564
self.set_revision_history(history)
1565
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1567
def _gen_revision_history(self):
1568
history = self._transport.get_bytes('revision-history').split('\n')
1569
if history[-1:] == ['']:
1570
# There shouldn't be a trailing newline, but just in case.
1574
def _lefthand_history(self, revision_id, last_rev=None,
1576
if 'evil' in debug.debug_flags:
1577
mutter_callsite(4, "_lefthand_history scales with history.")
1578
# stop_revision must be a descendant of last_revision
1579
graph = self.repository.get_graph()
1580
if last_rev is not None:
1581
if not graph.is_ancestor(last_rev, revision_id):
1582
# our previous tip is not merged into stop_revision
1583
raise errors.DivergedBranches(self, other_branch)
1584
# make a new revision history from the graph
1585
parents_map = graph.get_parent_map([revision_id])
1586
if revision_id not in parents_map:
1587
raise errors.NoSuchRevision(self, revision_id)
1588
current_rev_id = revision_id
1590
# Do not include ghosts or graph origin in revision_history
1591
while (current_rev_id in parents_map and
1592
len(parents_map[current_rev_id]) > 0):
1593
new_history.append(current_rev_id)
1594
current_rev_id = parents_map[current_rev_id][0]
1595
parents_map = graph.get_parent_map([current_rev_id])
1596
new_history.reverse()
1600
def generate_revision_history(self, revision_id, last_rev=None,
1602
"""Create a new revision history that will finish with revision_id.
1604
:param revision_id: the new tip to use.
1605
:param last_rev: The previous last_revision. If not None, then this
1606
must be a ancestory of revision_id, or DivergedBranches is raised.
1607
:param other_branch: The other branch that DivergedBranches should
1608
raise with respect to.
1610
self.set_revision_history(self._lefthand_history(revision_id,
1611
last_rev, other_branch))
1613
def basis_tree(self):
1614
"""See Branch.basis_tree."""
1615
return self.repository.revision_tree(self.last_revision())
1618
def pull(self, source, overwrite=False, stop_revision=None,
1619
_hook_master=None, run_hooks=True, possible_transports=None):
1622
:param _hook_master: Private parameter - set the branch to
1623
be supplied as the master to push hooks.
1624
:param run_hooks: Private parameter - if false, this branch
1625
is being called because it's the master of the primary branch,
1626
so it should not run its hooks.
1628
result = PullResult()
1629
result.source_branch = source
1630
result.target_branch = self
1633
# We assume that during 'pull' the local repository is closer than
1635
graph = self.repository.get_graph(source.repository)
1636
result.old_revno, result.old_revid = self.last_revision_info()
1637
self.update_revisions(source, stop_revision, overwrite=overwrite,
1639
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
1640
result.new_revno, result.new_revid = self.last_revision_info()
1642
result.master_branch = _hook_master
1643
result.local_branch = self
1645
result.master_branch = self
1646
result.local_branch = None
1648
for hook in Branch.hooks['post_pull']:
1654
def _get_parent_location(self):
1655
_locs = ['parent', 'pull', 'x-pull']
1658
return self._transport.get_bytes(l).strip('\n')
1659
except errors.NoSuchFile:
1664
def push(self, target, overwrite=False, stop_revision=None,
1665
_override_hook_source_branch=None):
1668
This is the basic concrete implementation of push()
1670
:param _override_hook_source_branch: If specified, run
1671
the hooks passing this Branch as the source, rather than self.
1672
This is for use of RemoteBranch, where push is delegated to the
1673
underlying vfs-based Branch.
1675
# TODO: Public option to disable running hooks - should be trivial but
1679
result = self._push_with_bound_branches(target, overwrite,
1681
_override_hook_source_branch=_override_hook_source_branch)
1686
def _push_with_bound_branches(self, target, overwrite,
1688
_override_hook_source_branch=None):
1689
"""Push from self into target, and into target's master if any.
1691
This is on the base BzrBranch class even though it doesn't support
1692
bound branches because the *target* might be bound.
1695
if _override_hook_source_branch:
1696
result.source_branch = _override_hook_source_branch
1697
for hook in Branch.hooks['post_push']:
1700
bound_location = target.get_bound_location()
1701
if bound_location and target.base != bound_location:
1702
# there is a master branch.
1704
# XXX: Why the second check? Is it even supported for a branch to
1705
# be bound to itself? -- mbp 20070507
1706
master_branch = target.get_master_branch()
1707
master_branch.lock_write()
1709
# push into the master from this branch.
1710
self._basic_push(master_branch, overwrite, stop_revision)
1711
# and push into the target branch from this. Note that we push from
1712
# this branch again, because its considered the highest bandwidth
1714
result = self._basic_push(target, overwrite, stop_revision)
1715
result.master_branch = master_branch
1716
result.local_branch = target
1720
master_branch.unlock()
1723
result = self._basic_push(target, overwrite, stop_revision)
1724
# TODO: Why set master_branch and local_branch if there's no
1725
# binding? Maybe cleaner to just leave them unset? -- mbp
1727
result.master_branch = target
1728
result.local_branch = None
1732
def _basic_push(self, target, overwrite, stop_revision):
1733
"""Basic implementation of push without bound branches or hooks.
1735
Must be called with self read locked and target write locked.
1737
result = PushResult()
1738
result.source_branch = self
1739
result.target_branch = target
1740
result.old_revno, result.old_revid = target.last_revision_info()
1742
# We assume that during 'push' this repository is closer than
1744
graph = self.repository.get_graph(target.repository)
1745
target.update_revisions(self, stop_revision, overwrite=overwrite,
1747
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
1748
result.new_revno, result.new_revid = target.last_revision_info()
1751
def get_parent(self):
1752
"""See Branch.get_parent."""
1753
parent = self._get_parent_location()
1756
# This is an old-format absolute path to a local branch
1757
# turn it into a url
1758
if parent.startswith('/'):
1759
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1761
return urlutils.join(self.base[:-1], parent)
1762
except errors.InvalidURLJoin, e:
1763
raise errors.InaccessibleParent(parent, self.base)
1765
def get_stacked_on(self):
1766
raise errors.UnstackableBranchFormat(self._format, self.base)
1768
def set_push_location(self, location):
1769
"""See Branch.set_push_location."""
1770
self.get_config().set_user_option(
1771
'push_location', location,
1772
store=_mod_config.STORE_LOCATION_NORECURSE)
1775
def set_parent(self, url):
1776
"""See Branch.set_parent."""
1777
# TODO: Maybe delete old location files?
1778
# URLs should never be unicode, even on the local fs,
1779
# FIXUP this and get_parent in a future branch format bump:
1780
# read and rewrite the file. RBC 20060125
1782
if isinstance(url, unicode):
1784
url = url.encode('ascii')
1785
except UnicodeEncodeError:
1786
raise errors.InvalidURL(url,
1787
"Urls must be 7-bit ascii, "
1788
"use bzrlib.urlutils.escape")
1789
url = urlutils.relative_url(self.base, url)
1790
self._set_parent_location(url)
1792
def _set_parent_location(self, url):
1794
self._transport.delete('parent')
1796
self._transport.put_bytes('parent', url + '\n',
1797
mode=self.bzrdir._get_file_mode())
1799
def set_stacked_on(self, url):
1800
raise errors.UnstackableBranchFormat(self._format, self.base)
1803
class BzrBranch5(BzrBranch):
1804
"""A format 5 branch. This supports new features over plain branches.
1806
It has support for a master_branch which is the data for bound branches.
1810
def pull(self, source, overwrite=False, stop_revision=None,
1811
run_hooks=True, possible_transports=None):
1812
"""Pull from source into self, updating my master if any.
1814
:param run_hooks: Private parameter - if false, this branch
1815
is being called because it's the master of the primary branch,
1816
so it should not run its hooks.
1818
bound_location = self.get_bound_location()
1819
master_branch = None
1820
if bound_location and source.base != bound_location:
1821
# not pulling from master, so we need to update master.
1822
master_branch = self.get_master_branch(possible_transports)
1823
master_branch.lock_write()
1826
# pull from source into master.
1827
master_branch.pull(source, overwrite, stop_revision,
1829
return super(BzrBranch5, self).pull(source, overwrite,
1830
stop_revision, _hook_master=master_branch,
1831
run_hooks=run_hooks)
1834
master_branch.unlock()
1836
def get_bound_location(self):
1838
return self._transport.get_bytes('bound')[:-1]
1839
except errors.NoSuchFile:
1843
def get_master_branch(self, possible_transports=None):
1844
"""Return the branch we are bound to.
1846
:return: Either a Branch, or None
1848
This could memoise the branch, but if thats done
1849
it must be revalidated on each new lock.
1850
So for now we just don't memoise it.
1851
# RBC 20060304 review this decision.
1853
bound_loc = self.get_bound_location()
1857
return Branch.open(bound_loc,
1858
possible_transports=possible_transports)
1859
except (errors.NotBranchError, errors.ConnectionError), e:
1860
raise errors.BoundBranchConnectionFailure(
1864
def set_bound_location(self, location):
1865
"""Set the target where this branch is bound to.
1867
:param location: URL to the target branch
1870
self._transport.put_bytes('bound', location+'\n',
1871
mode=self.bzrdir._get_file_mode())
1874
self._transport.delete('bound')
1875
except errors.NoSuchFile:
1880
def bind(self, other):
1881
"""Bind this branch to the branch other.
1883
This does not push or pull data between the branches, though it does
1884
check for divergence to raise an error when the branches are not
1885
either the same, or one a prefix of the other. That behaviour may not
1886
be useful, so that check may be removed in future.
1888
:param other: The branch to bind to
1891
# TODO: jam 20051230 Consider checking if the target is bound
1892
# It is debatable whether you should be able to bind to
1893
# a branch which is itself bound.
1894
# Committing is obviously forbidden,
1895
# but binding itself may not be.
1896
# Since we *have* to check at commit time, we don't
1897
# *need* to check here
1899
# we want to raise diverged if:
1900
# last_rev is not in the other_last_rev history, AND
1901
# other_last_rev is not in our history, and do it without pulling
1903
self.set_bound_location(other.base)
1907
"""If bound, unbind"""
1908
return self.set_bound_location(None)
1911
def update(self, possible_transports=None):
1912
"""Synchronise this branch with the master branch if any.
1914
:return: None or the last_revision that was pivoted out during the
1917
master = self.get_master_branch(possible_transports)
1918
if master is not None:
1919
old_tip = _mod_revision.ensure_null(self.last_revision())
1920
self.pull(master, overwrite=True)
1921
if self.repository.get_graph().is_ancestor(old_tip,
1922
_mod_revision.ensure_null(self.last_revision())):
1928
class BzrBranch7(BzrBranch5):
1930
def _get_fallback_repository(self, url):
1931
"""Get the repository we fallback to at url."""
1932
return bzrdir.BzrDir.open(url).open_branch().repository
1934
def _activate_fallback_location(self, url):
1935
"""Activate the branch/repository from url as a fallback repository."""
1936
self.repository.add_fallback_repository(
1937
self._get_fallback_repository(url))
1939
def _open_hook(self):
1941
url = self.get_stacked_on()
1942
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
1943
errors.UnstackableBranchFormat):
1946
self._activate_fallback_location(url)
1948
def _check_stackable_repo(self):
1949
if not self.repository._format.supports_external_lookups:
1950
raise errors.UnstackableRepositoryFormat(self.repository._format,
1951
self.repository.base)
1953
def __init__(self, *args, **kwargs):
1954
super(BzrBranch7, self).__init__(*args, **kwargs)
1955
self._last_revision_info_cache = None
1956
self._partial_revision_history_cache = []
1958
def _clear_cached_state(self):
1959
super(BzrBranch7, self)._clear_cached_state()
1960
self._last_revision_info_cache = None
1961
self._partial_revision_history_cache = []
1964
def last_revision_info(self):
1965
"""Return information about the last revision.
1967
:return: A tuple (revno, revision_id).
1969
if self._last_revision_info_cache is None:
1970
self._last_revision_info_cache = self._last_revision_info()
1971
return self._last_revision_info_cache
1973
def _last_revision_info(self):
1974
revision_string = self._transport.get_bytes('last-revision')
1975
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1976
revision_id = cache_utf8.get_cached_utf8(revision_id)
1978
return revno, revision_id
1980
def _write_last_revision_info(self, revno, revision_id):
1981
"""Simply write out the revision id, with no checks.
1983
Use set_last_revision_info to perform this safely.
1985
Does not update the revision_history cache.
1986
Intended to be called by set_last_revision_info and
1987
_write_revision_history.
1989
revision_id = _mod_revision.ensure_null(revision_id)
1990
out_string = '%d %s\n' % (revno, revision_id)
1991
self._transport.put_bytes('last-revision', out_string,
1992
mode=self.bzrdir._get_file_mode())
1995
def set_last_revision_info(self, revno, revision_id):
1996
revision_id = _mod_revision.ensure_null(revision_id)
1997
old_revno, old_revid = self.last_revision_info()
1998
if self._get_append_revisions_only():
1999
self._check_history_violation(revision_id)
2000
self._write_last_revision_info(revno, revision_id)
2001
self._clear_cached_state()
2002
self._last_revision_info_cache = revno, revision_id
2003
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2005
def _check_history_violation(self, revision_id):
2006
last_revision = _mod_revision.ensure_null(self.last_revision())
2007
if _mod_revision.is_null(last_revision):
2009
if last_revision not in self._lefthand_history(revision_id):
2010
raise errors.AppendRevisionsOnlyViolation(self.base)
2012
def _gen_revision_history(self):
2013
"""Generate the revision history from last revision
2015
self._extend_partial_history()
2016
return list(reversed(self._partial_revision_history_cache))
2018
def _extend_partial_history(self, stop_index=None, stop_revision=None):
2019
"""Extend the partial history to include a given index
2021
If a stop_index is supplied, stop when that index has been reached.
2022
If a stop_revision is supplied, stop when that revision is
2023
encountered. Otherwise, stop when the beginning of history is
2026
:param stop_index: The index which should be present. When it is
2027
present, history extension will stop.
2028
:param revision_id: The revision id which should be present. When
2029
it is encountered, history extension will stop.
2031
repo = self.repository
2032
if len(self._partial_revision_history_cache) == 0:
2033
iterator = repo.iter_reverse_revision_history(self.last_revision())
2035
start_revision = self._partial_revision_history_cache[-1]
2036
iterator = repo.iter_reverse_revision_history(start_revision)
2037
#skip the last revision in the list
2038
next_revision = iterator.next()
2039
for revision_id in iterator:
2040
self._partial_revision_history_cache.append(revision_id)
2041
if (stop_index is not None and
2042
len(self._partial_revision_history_cache) > stop_index):
2044
if revision_id == stop_revision:
2047
def _write_revision_history(self, history):
2048
"""Factored out of set_revision_history.
2050
This performs the actual writing to disk, with format-specific checks.
2051
It is intended to be called by BzrBranch5.set_revision_history.
2053
if len(history) == 0:
2054
last_revision = 'null:'
2056
if history != self._lefthand_history(history[-1]):
2057
raise errors.NotLefthandHistory(history)
2058
last_revision = history[-1]
2059
if self._get_append_revisions_only():
2060
self._check_history_violation(last_revision)
2061
self._write_last_revision_info(len(history), last_revision)
2064
def _set_parent_location(self, url):
2065
"""Set the parent branch"""
2066
self._set_config_location('parent_location', url, make_relative=True)
2069
def _get_parent_location(self):
2070
"""Set the parent branch"""
2071
return self._get_config_location('parent_location')
2073
def set_push_location(self, location):
2074
"""See Branch.set_push_location."""
2075
self._set_config_location('push_location', location)
2077
def set_bound_location(self, location):
2078
"""See Branch.set_push_location."""
2080
config = self.get_config()
2081
if location is None:
2082
if config.get_user_option('bound') != 'True':
2085
config.set_user_option('bound', 'False', warn_masked=True)
2088
self._set_config_location('bound_location', location,
2090
config.set_user_option('bound', 'True', warn_masked=True)
2093
def _get_bound_location(self, bound):
2094
"""Return the bound location in the config file.
2096
Return None if the bound parameter does not match"""
2097
config = self.get_config()
2098
config_bound = (config.get_user_option('bound') == 'True')
2099
if config_bound != bound:
2101
return self._get_config_location('bound_location', config=config)
2103
def get_bound_location(self):
2104
"""See Branch.set_push_location."""
2105
return self._get_bound_location(True)
2107
def get_old_bound_location(self):
2108
"""See Branch.get_old_bound_location"""
2109
return self._get_bound_location(False)
2111
def get_stacked_on(self):
2112
self._check_stackable_repo()
2113
stacked_url = self._get_config_location('stacked_on_location')
2114
if stacked_url is None:
2115
raise errors.NotStacked(self)
2118
def set_append_revisions_only(self, enabled):
2123
self.get_config().set_user_option('append_revisions_only', value,
2126
def set_stacked_on(self, url):
2127
self._check_stackable_repo()
2130
old_url = self.get_stacked_on()
2131
except (errors.NotStacked, errors.UnstackableBranchFormat,
2132
errors.UnstackableRepositoryFormat):
2135
# repositories don't offer an interface to remove fallback
2136
# repositories today; take the conceptually simpler option and just
2138
self.repository = self.bzrdir.find_repository()
2139
# for every revision reference the branch has, ensure it is pulled
2141
source_repository = self._get_fallback_repository(old_url)
2142
for revision_id in chain([self.last_revision()],
2143
self.tags.get_reverse_tag_dict()):
2144
self.repository.fetch(source_repository, revision_id,
2147
self._activate_fallback_location(url)
2148
# write this out after the repository is stacked to avoid setting a
2149
# stacked config that doesn't work.
2150
self._set_config_location('stacked_on_location', url)
2152
def _get_append_revisions_only(self):
2153
value = self.get_config().get_user_option('append_revisions_only')
2154
return value == 'True'
2156
def _synchronize_history(self, destination, revision_id):
2157
"""Synchronize last revision and revision history between branches.
2159
This version is most efficient when the destination is also a
2160
BzrBranch6, but works for BzrBranch5, as long as the destination's
2161
repository contains all the lefthand ancestors of the intended
2162
last_revision. If not, set_last_revision_info will fail.
2164
:param destination: The branch to copy the history into
2165
:param revision_id: The revision-id to truncate history at. May
2166
be None to copy complete history.
2168
source_revno, source_revision_id = self.last_revision_info()
2169
if revision_id is None:
2170
revno, revision_id = source_revno, source_revision_id
2171
elif source_revision_id == revision_id:
2172
# we know the revno without needing to walk all of history
2173
revno = source_revno
2175
# To figure out the revno for a random revision, we need to build
2176
# the revision history, and count its length.
2177
# We don't care about the order, just how long it is.
2178
# Alternatively, we could start at the current location, and count
2179
# backwards. But there is no guarantee that we will find it since
2180
# it may be a merged revision.
2181
revno = len(list(self.repository.iter_reverse_revision_history(
2183
destination.set_last_revision_info(revno, revision_id)
2185
def _make_tags(self):
2186
return BasicTags(self)
2189
def generate_revision_history(self, revision_id, last_rev=None,
2191
"""See BzrBranch5.generate_revision_history"""
2192
history = self._lefthand_history(revision_id, last_rev, other_branch)
2193
revno = len(history)
2194
self.set_last_revision_info(revno, revision_id)
2197
def get_rev_id(self, revno, history=None):
2198
"""Find the revision id of the specified revno."""
2200
return _mod_revision.NULL_REVISION
2202
last_revno, last_revision_id = self.last_revision_info()
2203
if revno <= 0 or revno > last_revno:
2204
raise errors.NoSuchRevision(self, revno)
2206
if history is not None:
2207
return history[revno - 1]
2209
index = last_revno - revno
2210
if len(self._partial_revision_history_cache) <= index:
2211
self._extend_partial_history(stop_index=index)
2212
if len(self._partial_revision_history_cache) > index:
2213
return self._partial_revision_history_cache[index]
2215
raise errors.NoSuchRevision(self, revno)
2218
def revision_id_to_revno(self, revision_id):
2219
"""Given a revision id, return its revno"""
2220
if _mod_revision.is_null(revision_id):
2223
index = self._partial_revision_history_cache.index(revision_id)
2225
self._extend_partial_history(stop_revision=revision_id)
2226
index = len(self._partial_revision_history_cache) - 1
2227
if self._partial_revision_history_cache[index] != revision_id:
2228
raise errors.NoSuchRevision(self, revision_id)
2229
return self.revno() - index
2232
class BzrBranch6(BzrBranch7):
2233
"""See BzrBranchFormat6 for the capabilities of this branch.
2235
This subclass of BzrBranch7 disables the new features BzrBranch7 added,
2239
def get_stacked_on(self):
2240
raise errors.UnstackableBranchFormat(self._format, self.base)
2242
def set_stacked_on(self, url):
2243
raise errors.UnstackableBranchFormat(self._format, self.base)
2246
######################################################################
2247
# results of operations
2250
class _Result(object):
2252
def _show_tag_conficts(self, to_file):
2253
if not getattr(self, 'tag_conflicts', None):
2255
to_file.write('Conflicting tags:\n')
2256
for name, value1, value2 in self.tag_conflicts:
2257
to_file.write(' %s\n' % (name, ))
2260
class PullResult(_Result):
2261
"""Result of a Branch.pull operation.
2263
:ivar old_revno: Revision number before pull.
2264
:ivar new_revno: Revision number after pull.
2265
:ivar old_revid: Tip revision id before pull.
2266
:ivar new_revid: Tip revision id after pull.
2267
:ivar source_branch: Source (local) branch object.
2268
:ivar master_branch: Master branch of the target, or the target if no
2270
:ivar local_branch: target branch if there is a Master, else None
2271
:ivar target_branch: Target/destination branch object.
2272
:ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
2276
# DEPRECATED: pull used to return the change in revno
2277
return self.new_revno - self.old_revno
2279
def report(self, to_file):
2281
if self.old_revid == self.new_revid:
2282
to_file.write('No revisions to pull.\n')
2284
to_file.write('Now on revision %d.\n' % self.new_revno)
2285
self._show_tag_conficts(to_file)
2288
class PushResult(_Result):
2289
"""Result of a Branch.push operation.
2291
:ivar old_revno: Revision number before push.
2292
:ivar new_revno: Revision number after push.
2293
:ivar old_revid: Tip revision id before push.
2294
:ivar new_revid: Tip revision id after push.
2295
:ivar source_branch: Source branch object.
2296
:ivar master_branch: Master branch of the target, or None.
2297
:ivar target_branch: Target/destination branch object.
2301
# DEPRECATED: push used to return the change in revno
2302
return self.new_revno - self.old_revno
2304
def report(self, to_file):
2305
"""Write a human-readable description of the result."""
2306
if self.old_revid == self.new_revid:
2307
to_file.write('No new revisions to push.\n')
2309
to_file.write('Pushed up to revision %d.\n' % self.new_revno)
2310
self._show_tag_conficts(to_file)
2313
class BranchCheckResult(object):
2314
"""Results of checking branch consistency.
2319
def __init__(self, branch):
2320
self.branch = branch
2322
def report_results(self, verbose):
2323
"""Report the check results via trace.note.
2325
:param verbose: Requests more detailed display of what was checked,
2328
note('checked branch %s format %s',
2330
self.branch._format)
2333
class Converter5to6(object):
2334
"""Perform an in-place upgrade of format 5 to format 6"""
2336
def convert(self, branch):
2337
# Data for 5 and 6 can peacefully coexist.
2338
format = BzrBranchFormat6()
2339
new_branch = format.open(branch.bzrdir, _found=True)
2341
# Copy source data into target
2342
new_branch._write_last_revision_info(*branch.last_revision_info())
2343
new_branch.set_parent(branch.get_parent())
2344
new_branch.set_bound_location(branch.get_bound_location())
2345
new_branch.set_push_location(branch.get_push_location())
2347
# New branch has no tags by default
2348
new_branch.tags._set_tag_dict({})
2350
# Copying done; now update target format
2351
new_branch._transport.put_bytes('format',
2352
format.get_format_string(),
2353
mode=new_branch.bzrdir._get_file_mode())
2355
# Clean up old files
2356
new_branch._transport.delete('revision-history')
2358
branch.set_parent(None)
2359
except errors.NoSuchFile:
2361
branch.set_bound_location(None)
2364
class Converter6to7(object):
2365
"""Perform an in-place upgrade of format 6 to format 7"""
2367
def convert(self, branch):
2368
format = BzrBranchFormat7()
2369
branch._set_config_location('stacked_on_location', '')
2370
# update target format
2371
branch._transport.put_bytes('format', format.get_format_string())