1
# Copyright (C) 2005, 2006, 2007 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(), """
23
config as _mod_config,
29
revision as _mod_revision,
35
from bzrlib.config import BranchConfig
36
from bzrlib.tag import (
42
from bzrlib.decorators import needs_read_lock, needs_write_lock
43
from bzrlib.hooks import Hooks
44
from bzrlib.symbol_versioning import (deprecated_method,
47
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
50
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
51
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
52
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
55
# TODO: Maybe include checks for common corruption of newlines, etc?
57
# TODO: Some operations like log might retrieve the same revisions
58
# repeatedly to calculate deltas. We could perhaps have a weakref
59
# cache in memory to make this faster. In general anything can be
60
# cached in memory between lock and unlock operations. .. nb thats
61
# what the transaction identity map provides
64
######################################################################
68
"""Branch holding a history of revisions.
71
Base directory/url of the branch.
73
hooks: An instance of BranchHooks.
75
# this is really an instance variable - FIXME move it there
79
# override this to set the strategy for storing tags
81
return DisabledTags(self)
83
def __init__(self, *ignored, **ignored_too):
84
self.tags = self._make_tags()
85
self._revision_history_cache = None
86
self._revision_id_to_revno_cache = None
89
"""Break a lock if one is present from another instance.
91
Uses the ui factory to ask for confirmation if the lock may be from
94
This will probe the repository for its lock as well.
96
self.control_files.break_lock()
97
self.repository.break_lock()
98
master = self.get_master_branch()
99
if master is not None:
103
def open(base, _unsupported=False, possible_transports=None):
104
"""Open the branch rooted at base.
106
For instance, if the branch is at URL/.bzr/branch,
107
Branch.open(URL) -> a Branch instance.
109
control = bzrdir.BzrDir.open(base, _unsupported,
110
possible_transports=possible_transports)
111
return control.open_branch(_unsupported)
114
def open_from_transport(transport, _unsupported=False):
115
"""Open the branch rooted at transport"""
116
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
117
return control.open_branch(_unsupported)
120
def open_containing(url, possible_transports=None):
121
"""Open an existing branch which contains url.
123
This probes for a branch at url, and searches upwards from there.
125
Basically we keep looking up until we find the control directory or
126
run into the root. If there isn't one, raises NotBranchError.
127
If there is one and it is either an unrecognised format or an unsupported
128
format, UnknownFormatError or UnsupportedFormatError are raised.
129
If there is one, it is returned, along with the unused portion of url.
131
control, relpath = bzrdir.BzrDir.open_containing(url,
133
return control.open_branch(), relpath
135
def get_config(self):
136
return BranchConfig(self)
139
return self.get_config().get_nickname()
141
def _set_nick(self, nick):
142
self.get_config().set_user_option('nickname', nick, warn_masked=True)
144
nick = property(_get_nick, _set_nick)
147
raise NotImplementedError(self.is_locked)
149
def lock_write(self):
150
raise NotImplementedError(self.lock_write)
153
raise NotImplementedError(self.lock_read)
156
raise NotImplementedError(self.unlock)
158
def peek_lock_mode(self):
159
"""Return lock mode for the Branch: 'r', 'w' or None"""
160
raise NotImplementedError(self.peek_lock_mode)
162
def get_physical_lock_status(self):
163
raise NotImplementedError(self.get_physical_lock_status)
166
def get_revision_id_to_revno_map(self):
167
"""Return the revision_id => dotted revno map.
169
This will be regenerated on demand, but will be cached.
171
:return: A dictionary mapping revision_id => dotted revno.
172
This dictionary should not be modified by the caller.
174
if self._revision_id_to_revno_cache is not None:
175
mapping = self._revision_id_to_revno_cache
177
mapping = self._gen_revno_map()
178
self._cache_revision_id_to_revno(mapping)
179
# TODO: jam 20070417 Since this is being cached, should we be returning
181
# I would rather not, and instead just declare that users should not
182
# modify the return value.
185
def _gen_revno_map(self):
186
"""Create a new mapping from revision ids to dotted revnos.
188
Dotted revnos are generated based on the current tip in the revision
190
This is the worker function for get_revision_id_to_revno_map, which
191
just caches the return value.
193
:return: A dictionary mapping revision_id => dotted revno.
195
last_revision = self.last_revision()
196
revision_graph = repository._old_get_graph(self.repository,
198
merge_sorted_revisions = tsort.merge_sort(
203
revision_id_to_revno = dict((rev_id, revno)
204
for seq_num, rev_id, depth, revno, end_of_merge
205
in merge_sorted_revisions)
206
return revision_id_to_revno
208
def leave_lock_in_place(self):
209
"""Tell this branch object not to release the physical lock when this
212
If lock_write doesn't return a token, then this method is not supported.
214
self.control_files.leave_in_place()
216
def dont_leave_lock_in_place(self):
217
"""Tell this branch object to release the physical lock when this
218
object is unlocked, even if it didn't originally acquire it.
220
If lock_write doesn't return a token, then this method is not supported.
222
self.control_files.dont_leave_in_place()
224
def abspath(self, name):
225
"""Return absolute filename for something in the branch
227
XXX: Robert Collins 20051017 what is this used for? why is it a branch
228
method and not a tree method.
230
raise NotImplementedError(self.abspath)
232
def bind(self, other):
233
"""Bind the local branch the other branch.
235
:param other: The branch to bind to
238
raise errors.UpgradeRequired(self.base)
241
def fetch(self, from_branch, last_revision=None, pb=None):
242
"""Copy revisions from from_branch into this branch.
244
:param from_branch: Where to copy from.
245
:param last_revision: What revision to stop at (None for at the end
247
:param pb: An optional progress bar to use.
249
Returns the copied revision count and the failed revisions in a tuple:
252
if self.base == from_branch.base:
255
nested_pb = ui.ui_factory.nested_progress_bar()
260
from_branch.lock_read()
262
if last_revision is None:
263
pb.update('get source history')
264
last_revision = from_branch.last_revision()
265
last_revision = _mod_revision.ensure_null(last_revision)
266
return self.repository.fetch(from_branch.repository,
267
revision_id=last_revision,
270
if nested_pb is not None:
274
def get_bound_location(self):
275
"""Return the URL of the branch we are bound to.
277
Older format branches cannot bind, please be sure to use a metadir
282
def get_old_bound_location(self):
283
"""Return the URL of the branch we used to be bound to
285
raise errors.UpgradeRequired(self.base)
287
def get_commit_builder(self, parents, config=None, timestamp=None,
288
timezone=None, committer=None, revprops=None,
290
"""Obtain a CommitBuilder for this branch.
292
:param parents: Revision ids of the parents of the new revision.
293
:param config: Optional configuration to use.
294
:param timestamp: Optional timestamp recorded for commit.
295
:param timezone: Optional timezone for timestamp.
296
:param committer: Optional committer to set for commit.
297
:param revprops: Optional dictionary of revision properties.
298
:param revision_id: Optional revision id.
302
config = self.get_config()
304
return self.repository.get_commit_builder(self, parents, config,
305
timestamp, timezone, committer, revprops, revision_id)
307
def get_master_branch(self, possible_transports=None):
308
"""Return the branch we are bound to.
310
:return: Either a Branch, or None
314
def get_revision_delta(self, revno):
315
"""Return the delta for one revision.
317
The delta is relative to its mainline predecessor, or the
318
empty tree for revision 1.
320
rh = self.revision_history()
321
if not (1 <= revno <= len(rh)):
322
raise errors.InvalidRevisionNumber(revno)
323
return self.repository.get_revision_delta(rh[revno-1])
325
@deprecated_method(zero_sixteen)
326
def get_root_id(self):
327
"""Return the id of this branches root
329
Deprecated: branches don't have root ids-- trees do.
330
Use basis_tree().get_root_id() instead.
332
raise NotImplementedError(self.get_root_id)
334
def print_file(self, file, revision_id):
335
"""Print `file` to stdout."""
336
raise NotImplementedError(self.print_file)
338
def set_revision_history(self, rev_history):
339
raise NotImplementedError(self.set_revision_history)
341
def _cache_revision_history(self, rev_history):
342
"""Set the cached revision history to rev_history.
344
The revision_history method will use this cache to avoid regenerating
345
the revision history.
347
This API is semi-public; it only for use by subclasses, all other code
348
should consider it to be private.
350
self._revision_history_cache = rev_history
352
def _cache_revision_id_to_revno(self, revision_id_to_revno):
353
"""Set the cached revision_id => revno map to revision_id_to_revno.
355
This API is semi-public; it only for use by subclasses, all other code
356
should consider it to be private.
358
self._revision_id_to_revno_cache = revision_id_to_revno
360
def _clear_cached_state(self):
361
"""Clear any cached data on this branch, e.g. cached revision history.
363
This means the next call to revision_history will need to call
364
_gen_revision_history.
366
This API is semi-public; it only for use by subclasses, all other code
367
should consider it to be private.
369
self._revision_history_cache = None
370
self._revision_id_to_revno_cache = None
372
def _gen_revision_history(self):
373
"""Return sequence of revision hashes on to this branch.
375
Unlike revision_history, this method always regenerates or rereads the
376
revision history, i.e. it does not cache the result, so repeated calls
379
Concrete subclasses should override this instead of revision_history so
380
that subclasses do not need to deal with caching logic.
382
This API is semi-public; it only for use by subclasses, all other code
383
should consider it to be private.
385
raise NotImplementedError(self._gen_revision_history)
388
def revision_history(self):
389
"""Return sequence of revision ids on this branch.
391
This method will cache the revision history for as long as it is safe to
394
if 'evil' in debug.debug_flags:
395
mutter_callsite(3, "revision_history scales with history.")
396
if self._revision_history_cache is not None:
397
history = self._revision_history_cache
399
history = self._gen_revision_history()
400
self._cache_revision_history(history)
404
"""Return current revision number for this branch.
406
That is equivalent to the number of revisions committed to
409
return self.last_revision_info()[0]
412
"""Older format branches cannot bind or unbind."""
413
raise errors.UpgradeRequired(self.base)
415
def set_append_revisions_only(self, enabled):
416
"""Older format branches are never restricted to append-only"""
417
raise errors.UpgradeRequired(self.base)
419
def last_revision(self):
420
"""Return last revision id, or NULL_REVISION."""
421
return self.last_revision_info()[1]
423
def last_revision_info(self):
424
"""Return information about the last revision.
426
:return: A tuple (revno, last_revision_id).
428
rh = self.revision_history()
431
return (revno, rh[-1])
433
return (0, _mod_revision.NULL_REVISION)
435
def missing_revisions(self, other, stop_revision=None):
436
"""Return a list of new revisions that would perfectly fit.
438
If self and other have not diverged, return a list of the revisions
439
present in other, but missing from self.
441
self_history = self.revision_history()
442
self_len = len(self_history)
443
other_history = other.revision_history()
444
other_len = len(other_history)
445
common_index = min(self_len, other_len) -1
446
if common_index >= 0 and \
447
self_history[common_index] != other_history[common_index]:
448
raise errors.DivergedBranches(self, other)
450
if stop_revision is None:
451
stop_revision = other_len
453
if stop_revision > other_len:
454
raise errors.NoSuchRevision(self, stop_revision)
455
return other_history[self_len:stop_revision]
457
def update_revisions(self, other, stop_revision=None):
458
"""Pull in new perfect-fit revisions.
460
:param other: Another Branch to pull from
461
:param stop_revision: Updated until the given revision
464
raise NotImplementedError(self.update_revisions)
466
def revision_id_to_revno(self, revision_id):
467
"""Given a revision id, return its revno"""
468
if _mod_revision.is_null(revision_id):
470
history = self.revision_history()
472
return history.index(revision_id) + 1
474
raise errors.NoSuchRevision(self, revision_id)
476
def get_rev_id(self, revno, history=None):
477
"""Find the revision id of the specified revno."""
479
return _mod_revision.NULL_REVISION
481
history = self.revision_history()
482
if revno <= 0 or revno > len(history):
483
raise errors.NoSuchRevision(self, revno)
484
return history[revno - 1]
486
def pull(self, source, overwrite=False, stop_revision=None,
487
possible_transports=None):
488
"""Mirror source into this branch.
490
This branch is considered to be 'local', having low latency.
492
:returns: PullResult instance
494
raise NotImplementedError(self.pull)
496
def push(self, target, overwrite=False, stop_revision=None):
497
"""Mirror this branch into target.
499
This branch is considered to be 'local', having low latency.
501
raise NotImplementedError(self.push)
503
def basis_tree(self):
504
"""Return `Tree` object for last revision."""
505
return self.repository.revision_tree(self.last_revision())
507
def rename_one(self, from_rel, to_rel):
510
This can change the directory or the filename or both.
512
raise NotImplementedError(self.rename_one)
514
def move(self, from_paths, to_name):
517
to_name must exist as a versioned directory.
519
If to_name exists and is a directory, the files are moved into
520
it, keeping their old names. If it is a directory,
522
Note that to_name is only the last component of the new name;
523
this doesn't change the directory.
525
This returns a list of (from_path, to_path) pairs for each
528
raise NotImplementedError(self.move)
530
def get_parent(self):
531
"""Return the parent location of the branch.
533
This is the default location for push/pull/missing. The usual
534
pattern is that the user can override it by specifying a
537
raise NotImplementedError(self.get_parent)
539
def _set_config_location(self, name, url, config=None,
540
make_relative=False):
542
config = self.get_config()
546
url = urlutils.relative_url(self.base, url)
547
config.set_user_option(name, url, warn_masked=True)
549
def _get_config_location(self, name, config=None):
551
config = self.get_config()
552
location = config.get_user_option(name)
557
def get_submit_branch(self):
558
"""Return the submit location of the branch.
560
This is the default location for bundle. The usual
561
pattern is that the user can override it by specifying a
564
return self.get_config().get_user_option('submit_branch')
566
def set_submit_branch(self, location):
567
"""Return the submit location of the branch.
569
This is the default location for bundle. The usual
570
pattern is that the user can override it by specifying a
573
self.get_config().set_user_option('submit_branch', location,
576
def get_public_branch(self):
577
"""Return the public location of the branch.
579
This is is used by merge directives.
581
return self._get_config_location('public_branch')
583
def set_public_branch(self, location):
584
"""Return the submit location of the branch.
586
This is the default location for bundle. The usual
587
pattern is that the user can override it by specifying a
590
self._set_config_location('public_branch', location)
592
def get_push_location(self):
593
"""Return the None or the location to push this branch to."""
594
push_loc = self.get_config().get_user_option('push_location')
597
def set_push_location(self, location):
598
"""Set a new push location for this branch."""
599
raise NotImplementedError(self.set_push_location)
601
def set_parent(self, url):
602
raise NotImplementedError(self.set_parent)
606
"""Synchronise this branch with the master branch if any.
608
:return: None or the last_revision pivoted out during the update.
612
def check_revno(self, revno):
614
Check whether a revno corresponds to any revision.
615
Zero (the NULL revision) is considered valid.
618
self.check_real_revno(revno)
620
def check_real_revno(self, revno):
622
Check whether a revno corresponds to a real revision.
623
Zero (the NULL revision) is considered invalid
625
if revno < 1 or revno > self.revno():
626
raise errors.InvalidRevisionNumber(revno)
629
def clone(self, to_bzrdir, revision_id=None):
630
"""Clone this branch into to_bzrdir preserving all semantic values.
632
revision_id: if not None, the revision history in the new branch will
633
be truncated to end with revision_id.
635
result = self._format.initialize(to_bzrdir)
636
self.copy_content_into(result, revision_id=revision_id)
640
def sprout(self, to_bzrdir, revision_id=None):
641
"""Create a new line of development from the branch, into to_bzrdir.
643
revision_id: if not None, the revision history in the new branch will
644
be truncated to end with revision_id.
646
result = self._format.initialize(to_bzrdir)
647
self.copy_content_into(result, revision_id=revision_id)
648
result.set_parent(self.bzrdir.root_transport.base)
651
def _synchronize_history(self, destination, revision_id):
652
"""Synchronize last revision and revision history between branches.
654
This version is most efficient when the destination is also a
655
BzrBranch5, but works for BzrBranch6 as long as the revision
656
history is the true lefthand parent history, and all of the revisions
657
are in the destination's repository. If not, set_revision_history
660
:param destination: The branch to copy the history into
661
:param revision_id: The revision-id to truncate history at. May
662
be None to copy complete history.
664
if revision_id == _mod_revision.NULL_REVISION:
666
new_history = self.revision_history()
667
if revision_id is not None and new_history != []:
669
new_history = new_history[:new_history.index(revision_id) + 1]
671
rev = self.repository.get_revision(revision_id)
672
new_history = rev.get_history(self.repository)[1:]
673
destination.set_revision_history(new_history)
676
def copy_content_into(self, destination, revision_id=None):
677
"""Copy the content of self into destination.
679
revision_id: if not None, the revision history in the new branch will
680
be truncated to end with revision_id.
682
self._synchronize_history(destination, revision_id)
684
parent = self.get_parent()
685
except errors.InaccessibleParent, e:
686
mutter('parent was not accessible to copy: %s', e)
689
destination.set_parent(parent)
690
self.tags.merge_to(destination.tags)
694
"""Check consistency of the branch.
696
In particular this checks that revisions given in the revision-history
697
do actually match up in the revision graph, and that they're all
698
present in the repository.
700
Callers will typically also want to check the repository.
702
:return: A BranchCheckResult.
704
mainline_parent_id = None
705
for revision_id in self.revision_history():
707
revision = self.repository.get_revision(revision_id)
708
except errors.NoSuchRevision, e:
709
raise errors.BzrCheckError("mainline revision {%s} not in repository"
711
# In general the first entry on the revision history has no parents.
712
# But it's not illegal for it to have parents listed; this can happen
713
# in imports from Arch when the parents weren't reachable.
714
if mainline_parent_id is not None:
715
if mainline_parent_id not in revision.parent_ids:
716
raise errors.BzrCheckError("previous revision {%s} not listed among "
718
% (mainline_parent_id, revision_id))
719
mainline_parent_id = revision_id
720
return BranchCheckResult(self)
722
def _get_checkout_format(self):
723
"""Return the most suitable metadir for a checkout of this branch.
724
Weaves are used if this branch's repository uses weaves.
726
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
727
from bzrlib.repofmt import weaverepo
728
format = bzrdir.BzrDirMetaFormat1()
729
format.repository_format = weaverepo.RepositoryFormat7()
731
format = self.repository.bzrdir.checkout_metadir()
732
format.set_branch_format(self._format)
735
def create_checkout(self, to_location, revision_id=None,
736
lightweight=False, accelerator_tree=None,
738
"""Create a checkout of a branch.
740
:param to_location: The url to produce the checkout at
741
:param revision_id: The revision to check out
742
:param lightweight: If True, produce a lightweight checkout, otherwise,
743
produce a bound branch (heavyweight checkout)
744
:param accelerator_tree: A tree which can be used for retrieving file
745
contents more quickly than the revision tree, i.e. a workingtree.
746
The revision tree will be used for cases where accelerator_tree's
747
content is different.
748
:param hardlink: If true, hard-link files from accelerator_tree,
750
:return: The tree of the created checkout
752
t = transport.get_transport(to_location)
755
format = self._get_checkout_format()
756
checkout = format.initialize_on_transport(t)
757
from_branch = BranchReferenceFormat().initialize(checkout, self)
759
format = self._get_checkout_format()
760
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
761
to_location, force_new_tree=False, format=format)
762
checkout = checkout_branch.bzrdir
763
checkout_branch.bind(self)
764
# pull up to the specified revision_id to set the initial
765
# branch tip correctly, and seed it with history.
766
checkout_branch.pull(self, stop_revision=revision_id)
768
tree = checkout.create_workingtree(revision_id,
769
from_branch=from_branch,
770
accelerator_tree=accelerator_tree,
772
basis_tree = tree.basis_tree()
773
basis_tree.lock_read()
775
for path, file_id in basis_tree.iter_references():
776
reference_parent = self.reference_parent(file_id, path)
777
reference_parent.create_checkout(tree.abspath(path),
778
basis_tree.get_reference_revision(file_id, path),
784
def reference_parent(self, file_id, path):
785
"""Return the parent branch for a tree-reference file_id
786
:param file_id: The file_id of the tree reference
787
:param path: The path of the file_id in the tree
788
:return: A branch associated with the file_id
790
# FIXME should provide multiple branches, based on config
791
return Branch.open(self.bzrdir.root_transport.clone(path).base)
793
def supports_tags(self):
794
return self._format.supports_tags()
797
class BranchFormat(object):
798
"""An encapsulation of the initialization and open routines for a format.
800
Formats provide three things:
801
* An initialization routine,
805
Formats are placed in an dict by their format string for reference
806
during branch opening. Its not required that these be instances, they
807
can be classes themselves with class methods - it simply depends on
808
whether state is needed for a given format or not.
810
Once a format is deprecated, just deprecate the initialize and open
811
methods on the format class. Do not deprecate the object, as the
812
object will be created every time regardless.
815
_default_format = None
816
"""The default format used for new branches."""
819
"""The known formats."""
821
def __eq__(self, other):
822
return self.__class__ is other.__class__
824
def __ne__(self, other):
825
return not (self == other)
828
def find_format(klass, a_bzrdir):
829
"""Return the format for the branch object in a_bzrdir."""
831
transport = a_bzrdir.get_branch_transport(None)
832
format_string = transport.get("format").read()
833
return klass._formats[format_string]
834
except errors.NoSuchFile:
835
raise errors.NotBranchError(path=transport.base)
837
raise errors.UnknownFormatError(format=format_string, kind='branch')
840
def get_default_format(klass):
841
"""Return the current default format."""
842
return klass._default_format
844
def get_reference(self, a_bzrdir):
845
"""Get the target reference of the branch in a_bzrdir.
847
format probing must have been completed before calling
848
this method - it is assumed that the format of the branch
849
in a_bzrdir is correct.
851
:param a_bzrdir: The bzrdir to get the branch data from.
852
:return: None if the branch is not a reference branch.
857
def set_reference(self, a_bzrdir, to_branch):
858
"""Set the target reference of the branch in a_bzrdir.
860
format probing must have been completed before calling
861
this method - it is assumed that the format of the branch
862
in a_bzrdir is correct.
864
:param a_bzrdir: The bzrdir to set the branch reference for.
865
:param to_branch: branch that the checkout is to reference
867
raise NotImplementedError(self.set_reference)
869
def get_format_string(self):
870
"""Return the ASCII format string that identifies this format."""
871
raise NotImplementedError(self.get_format_string)
873
def get_format_description(self):
874
"""Return the short format description for this format."""
875
raise NotImplementedError(self.get_format_description)
877
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
879
"""Initialize a branch in a bzrdir, with specified files
881
:param a_bzrdir: The bzrdir to initialize the branch in
882
:param utf8_files: The files to create as a list of
883
(filename, content) tuples
884
:param set_format: If True, set the format with
885
self.get_format_string. (BzrBranch4 has its format set
887
:return: a branch in this format
889
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
890
branch_transport = a_bzrdir.get_branch_transport(self)
892
'metadir': ('lock', lockdir.LockDir),
893
'branch4': ('branch-lock', lockable_files.TransportLock),
895
lock_name, lock_class = lock_map[lock_type]
896
control_files = lockable_files.LockableFiles(branch_transport,
897
lock_name, lock_class)
898
control_files.create_lock()
899
control_files.lock_write()
901
control_files.put_utf8('format', self.get_format_string())
903
for file, content in utf8_files:
904
control_files.put_utf8(file, content)
906
control_files.unlock()
907
return self.open(a_bzrdir, _found=True)
909
def initialize(self, a_bzrdir):
910
"""Create a branch of this format in a_bzrdir."""
911
raise NotImplementedError(self.initialize)
913
def is_supported(self):
914
"""Is this format supported?
916
Supported formats can be initialized and opened.
917
Unsupported formats may not support initialization or committing or
918
some other features depending on the reason for not being supported.
922
def open(self, a_bzrdir, _found=False):
923
"""Return the branch object for a_bzrdir
925
_found is a private parameter, do not use it. It is used to indicate
926
if format probing has already be done.
928
raise NotImplementedError(self.open)
931
def register_format(klass, format):
932
klass._formats[format.get_format_string()] = format
935
def set_default_format(klass, format):
936
klass._default_format = format
939
def unregister_format(klass, format):
940
del klass._formats[format.get_format_string()]
943
return self.get_format_string().rstrip()
945
def supports_tags(self):
946
"""True if this format supports tags stored in the branch"""
947
return False # by default
949
# XXX: Probably doesn't really belong here -- mbp 20070212
950
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
952
branch_transport = a_bzrdir.get_branch_transport(self)
953
control_files = lockable_files.LockableFiles(branch_transport,
954
lock_filename, lock_class)
955
control_files.create_lock()
956
control_files.lock_write()
958
for filename, content in utf8_files:
959
control_files.put_utf8(filename, content)
961
control_files.unlock()
964
class BranchHooks(Hooks):
965
"""A dictionary mapping hook name to a list of callables for branch hooks.
967
e.g. ['set_rh'] Is the list of items to be called when the
968
set_revision_history function is invoked.
972
"""Create the default hooks.
974
These are all empty initially, because by default nothing should get
978
# Introduced in 0.15:
979
# invoked whenever the revision history has been set
980
# with set_revision_history. The api signature is
981
# (branch, revision_history), and the branch will
984
# invoked after a push operation completes.
985
# the api signature is
987
# containing the members
988
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
989
# where local is the local target branch or None, master is the target
990
# master branch, and the rest should be self explanatory. The source
991
# is read locked and the target branches write locked. Source will
992
# be the local low-latency branch.
993
self['post_push'] = []
994
# invoked after a pull operation completes.
995
# the api signature is
997
# containing the members
998
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
999
# where local is the local branch or None, master is the target
1000
# master branch, and the rest should be self explanatory. The source
1001
# is read locked and the target branches write locked. The local
1002
# branch is the low-latency branch.
1003
self['post_pull'] = []
1004
# invoked before a commit operation takes place.
1005
# the api signature is
1006
# (local, master, old_revno, old_revid, future_revno, future_revid,
1007
# tree_delta, future_tree).
1008
# old_revid is NULL_REVISION for the first commit to a branch
1009
# tree_delta is a TreeDelta object describing changes from the basis
1010
# revision, hooks MUST NOT modify this delta
1011
# future_tree is an in-memory tree obtained from
1012
# CommitBuilder.revision_tree() and hooks MUST NOT modify this tree
1013
self['pre_commit'] = []
1014
# invoked after a commit operation completes.
1015
# the api signature is
1016
# (local, master, old_revno, old_revid, new_revno, new_revid)
1017
# old_revid is NULL_REVISION for the first commit to a branch.
1018
self['post_commit'] = []
1019
# invoked after a uncommit operation completes.
1020
# the api signature is
1021
# (local, master, old_revno, old_revid, new_revno, new_revid) where
1022
# local is the local branch or None, master is the target branch,
1023
# and an empty branch recieves new_revno of 0, new_revid of None.
1024
self['post_uncommit'] = []
1026
# Invoked after the tip of a branch changes.
1027
# the api signature is
1028
# (params) where params is a ChangeBranchTipParams with the members
1029
# (branch, old_revno, new_revno, old_revid, new_revid)
1030
self['post_change_branch_tip'] = []
1033
# install the default hooks into the Branch class.
1034
Branch.hooks = BranchHooks()
1037
class ChangeBranchTipParams(object):
1038
"""Object holding parameters passed to *_change_branch_tip hooks.
1040
There are 5 fields that hooks may wish to access:
1042
:ivar branch: the branch being changed
1043
:ivar old_revno: revision number before the change
1044
:ivar new_revno: revision number after the change
1045
:ivar old_revid: revision id before the change
1046
:ivar new_revid: revision id after the change
1048
The revid fields are strings. The revno fields are integers.
1051
def __init__(self, branch, old_revno, new_revno, old_revid, new_revid):
1052
"""Create a group of ChangeBranchTip parameters.
1054
:param branch: The branch being changed.
1055
:param old_revno: Revision number before the change.
1056
:param new_revno: Revision number after the change.
1057
:param old_revid: Tip revision id before the change.
1058
:param new_revid: Tip revision id after the change.
1060
self.branch = branch
1061
self.old_revno = old_revno
1062
self.new_revno = new_revno
1063
self.old_revid = old_revid
1064
self.new_revid = new_revid
1067
class BzrBranchFormat4(BranchFormat):
1068
"""Bzr branch format 4.
1071
- a revision-history file.
1072
- a branch-lock lock file [ to be shared with the bzrdir ]
1075
def get_format_description(self):
1076
"""See BranchFormat.get_format_description()."""
1077
return "Branch format 4"
1079
def initialize(self, a_bzrdir):
1080
"""Create a branch of this format in a_bzrdir."""
1081
utf8_files = [('revision-history', ''),
1082
('branch-name', ''),
1084
return self._initialize_helper(a_bzrdir, utf8_files,
1085
lock_type='branch4', set_format=False)
1088
super(BzrBranchFormat4, self).__init__()
1089
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1091
def open(self, a_bzrdir, _found=False):
1092
"""Return the branch object for a_bzrdir
1094
_found is a private parameter, do not use it. It is used to indicate
1095
if format probing has already be done.
1098
# we are being called directly and must probe.
1099
raise NotImplementedError
1100
return BzrBranch(_format=self,
1101
_control_files=a_bzrdir._control_files,
1103
_repository=a_bzrdir.open_repository())
1106
return "Bazaar-NG branch format 4"
1109
class BzrBranchFormat5(BranchFormat):
1110
"""Bzr branch format 5.
1113
- a revision-history file.
1115
- a lock dir guarding the branch itself
1116
- all of this stored in a branch/ subdirectory
1117
- works with shared repositories.
1119
This format is new in bzr 0.8.
1122
def get_format_string(self):
1123
"""See BranchFormat.get_format_string()."""
1124
return "Bazaar-NG branch format 5\n"
1126
def get_format_description(self):
1127
"""See BranchFormat.get_format_description()."""
1128
return "Branch format 5"
1130
def initialize(self, a_bzrdir):
1131
"""Create a branch of this format in a_bzrdir."""
1132
utf8_files = [('revision-history', ''),
1133
('branch-name', ''),
1135
return self._initialize_helper(a_bzrdir, utf8_files)
1138
super(BzrBranchFormat5, self).__init__()
1139
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1141
def open(self, a_bzrdir, _found=False):
1142
"""Return the branch object for a_bzrdir
1144
_found is a private parameter, do not use it. It is used to indicate
1145
if format probing has already be done.
1148
format = BranchFormat.find_format(a_bzrdir)
1149
if format.__class__ != self.__class__:
1150
raise AssertionError("wrong format %r found for %r" %
1153
transport = a_bzrdir.get_branch_transport(None)
1154
control_files = lockable_files.LockableFiles(transport, 'lock',
1156
return BzrBranch5(_format=self,
1157
_control_files=control_files,
1159
_repository=a_bzrdir.find_repository())
1160
except errors.NoSuchFile:
1161
raise errors.NotBranchError(path=transport.base)
1164
class BzrBranchFormat6(BzrBranchFormat5):
1165
"""Branch format with last-revision and tags.
1167
Unlike previous formats, this has no explicit revision history. Instead,
1168
this just stores the last-revision, and the left-hand history leading
1169
up to there is the history.
1171
This format was introduced in bzr 0.15
1172
and became the default in 0.91.
1175
def get_format_string(self):
1176
"""See BranchFormat.get_format_string()."""
1177
return "Bazaar Branch Format 6 (bzr 0.15)\n"
1179
def get_format_description(self):
1180
"""See BranchFormat.get_format_description()."""
1181
return "Branch format 6"
1183
def initialize(self, a_bzrdir):
1184
"""Create a branch of this format in a_bzrdir."""
1185
utf8_files = [('last-revision', '0 null:\n'),
1186
('branch.conf', ''),
1189
return self._initialize_helper(a_bzrdir, utf8_files)
1191
def open(self, a_bzrdir, _found=False):
1192
"""Return the branch object for a_bzrdir
1194
_found is a private parameter, do not use it. It is used to indicate
1195
if format probing has already be done.
1198
format = BranchFormat.find_format(a_bzrdir)
1199
if format.__class__ != self.__class__:
1200
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 BzrBranch6(_format=self,
1206
_control_files=control_files,
1208
_repository=a_bzrdir.find_repository())
1210
def supports_tags(self):
1214
class BranchReferenceFormat(BranchFormat):
1215
"""Bzr branch reference format.
1217
Branch references are used in implementing checkouts, they
1218
act as an alias to the real branch which is at some other url.
1225
def get_format_string(self):
1226
"""See BranchFormat.get_format_string()."""
1227
return "Bazaar-NG Branch Reference Format 1\n"
1229
def get_format_description(self):
1230
"""See BranchFormat.get_format_description()."""
1231
return "Checkout reference format 1"
1233
def get_reference(self, a_bzrdir):
1234
"""See BranchFormat.get_reference()."""
1235
transport = a_bzrdir.get_branch_transport(None)
1236
return transport.get('location').read()
1238
def set_reference(self, a_bzrdir, to_branch):
1239
"""See BranchFormat.set_reference()."""
1240
transport = a_bzrdir.get_branch_transport(None)
1241
location = transport.put_bytes('location', to_branch.base)
1243
def initialize(self, a_bzrdir, target_branch=None):
1244
"""Create a branch of this format in a_bzrdir."""
1245
if target_branch is None:
1246
# this format does not implement branch itself, thus the implicit
1247
# creation contract must see it as uninitializable
1248
raise errors.UninitializableFormat(self)
1249
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1250
branch_transport = a_bzrdir.get_branch_transport(self)
1251
branch_transport.put_bytes('location',
1252
target_branch.bzrdir.root_transport.base)
1253
branch_transport.put_bytes('format', self.get_format_string())
1255
a_bzrdir, _found=True,
1256
possible_transports=[target_branch.bzrdir.root_transport])
1259
super(BranchReferenceFormat, self).__init__()
1260
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1262
def _make_reference_clone_function(format, a_branch):
1263
"""Create a clone() routine for a branch dynamically."""
1264
def clone(to_bzrdir, revision_id=None):
1265
"""See Branch.clone()."""
1266
return format.initialize(to_bzrdir, a_branch)
1267
# cannot obey revision_id limits when cloning a reference ...
1268
# FIXME RBC 20060210 either nuke revision_id for clone, or
1269
# emit some sort of warning/error to the caller ?!
1272
def open(self, a_bzrdir, _found=False, location=None,
1273
possible_transports=None):
1274
"""Return the branch that the branch reference in a_bzrdir points at.
1276
_found is a private parameter, do not use it. It is used to indicate
1277
if format probing has already be done.
1280
format = BranchFormat.find_format(a_bzrdir)
1281
if format.__class__ != self.__class__:
1282
raise AssertionError("wrong format %r found for %r" %
1284
if location is None:
1285
location = self.get_reference(a_bzrdir)
1286
real_bzrdir = bzrdir.BzrDir.open(
1287
location, possible_transports=possible_transports)
1288
result = real_bzrdir.open_branch()
1289
# this changes the behaviour of result.clone to create a new reference
1290
# rather than a copy of the content of the branch.
1291
# I did not use a proxy object because that needs much more extensive
1292
# testing, and we are only changing one behaviour at the moment.
1293
# If we decide to alter more behaviours - i.e. the implicit nickname
1294
# then this should be refactored to introduce a tested proxy branch
1295
# and a subclass of that for use in overriding clone() and ....
1297
result.clone = self._make_reference_clone_function(result)
1301
# formats which have no format string are not discoverable
1302
# and not independently creatable, so are not registered.
1303
__format5 = BzrBranchFormat5()
1304
__format6 = BzrBranchFormat6()
1305
BranchFormat.register_format(__format5)
1306
BranchFormat.register_format(BranchReferenceFormat())
1307
BranchFormat.register_format(__format6)
1308
BranchFormat.set_default_format(__format6)
1309
_legacy_formats = [BzrBranchFormat4(),
1312
class BzrBranch(Branch):
1313
"""A branch stored in the actual filesystem.
1315
Note that it's "local" in the context of the filesystem; it doesn't
1316
really matter if it's on an nfs/smb/afs/coda/... share, as long as
1317
it's writable, and can be accessed via the normal filesystem API.
1320
def __init__(self, _format=None,
1321
_control_files=None, a_bzrdir=None, _repository=None):
1322
"""Create new branch object at a particular location."""
1323
Branch.__init__(self)
1324
if a_bzrdir is None:
1325
raise ValueError('a_bzrdir must be supplied')
1327
self.bzrdir = a_bzrdir
1328
# self._transport used to point to the directory containing the
1329
# control directory, but was not used - now it's just the transport
1330
# for the branch control files. mbp 20070212
1331
self._base = self.bzrdir.transport.clone('..').base
1332
self._format = _format
1333
if _control_files is None:
1334
raise ValueError('BzrBranch _control_files is None')
1335
self.control_files = _control_files
1336
self._transport = _control_files._transport
1337
self.repository = _repository
1340
return '%s(%r)' % (self.__class__.__name__, self.base)
1344
def _get_base(self):
1345
"""Returns the directory containing the control directory."""
1348
base = property(_get_base, doc="The URL for the root of this branch.")
1350
def abspath(self, name):
1351
"""See Branch.abspath."""
1352
return self.control_files._transport.abspath(name)
1355
@deprecated_method(zero_sixteen)
1357
def get_root_id(self):
1358
"""See Branch.get_root_id."""
1359
tree = self.repository.revision_tree(self.last_revision())
1360
return tree.get_root_id()
1362
def is_locked(self):
1363
return self.control_files.is_locked()
1365
def lock_write(self, token=None):
1366
repo_token = self.repository.lock_write()
1368
token = self.control_files.lock_write(token=token)
1370
self.repository.unlock()
1374
def lock_read(self):
1375
self.repository.lock_read()
1377
self.control_files.lock_read()
1379
self.repository.unlock()
1383
# TODO: test for failed two phase locks. This is known broken.
1385
self.control_files.unlock()
1387
self.repository.unlock()
1388
if not self.control_files.is_locked():
1389
# we just released the lock
1390
self._clear_cached_state()
1392
def peek_lock_mode(self):
1393
if self.control_files._lock_count == 0:
1396
return self.control_files._lock_mode
1398
def get_physical_lock_status(self):
1399
return self.control_files.get_physical_lock_status()
1402
def print_file(self, file, revision_id):
1403
"""See Branch.print_file."""
1404
return self.repository.print_file(file, revision_id)
1406
def _write_revision_history(self, history):
1407
"""Factored out of set_revision_history.
1409
This performs the actual writing to disk.
1410
It is intended to be called by BzrBranch5.set_revision_history."""
1411
self.control_files.put_bytes(
1412
'revision-history', '\n'.join(history))
1415
def set_revision_history(self, rev_history):
1416
"""See Branch.set_revision_history."""
1417
if 'evil' in debug.debug_flags:
1418
mutter_callsite(3, "set_revision_history scales with history.")
1419
self._write_revision_history(rev_history)
1420
self._clear_cached_state()
1421
self._cache_revision_history(rev_history)
1422
for hook in Branch.hooks['set_rh']:
1423
hook(self, rev_history)
1425
def _run_post_change_branch_tip_hooks(self, old_revno, old_revid):
1426
"""Run the post_change_branch_tip hooks."""
1427
hooks = Branch.hooks['post_change_branch_tip']
1430
new_revno, new_revid = self.last_revision_info()
1431
params = ChangeBranchTipParams(
1432
self, old_revno, new_revno, old_revid, new_revid)
1437
def set_last_revision_info(self, revno, revision_id):
1438
"""Set the last revision of this branch.
1440
The caller is responsible for checking that the revno is correct
1441
for this revision id.
1443
It may be possible to set the branch last revision to an id not
1444
present in the repository. However, branches can also be
1445
configured to check constraints on history, in which case this may not
1448
revision_id = _mod_revision.ensure_null(revision_id)
1449
old_revno, old_revid = self.last_revision_info()
1450
# this old format stores the full history, but this api doesn't
1451
# provide it, so we must generate, and might as well check it's
1453
history = self._lefthand_history(revision_id)
1454
if len(history) != revno:
1455
raise AssertionError('%d != %d' % (len(history), revno))
1456
self.set_revision_history(history)
1457
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1459
def _gen_revision_history(self):
1460
history = self.control_files.get('revision-history').read().split('\n')
1461
if history[-1:] == ['']:
1462
# There shouldn't be a trailing newline, but just in case.
1466
def _lefthand_history(self, revision_id, last_rev=None,
1468
if 'evil' in debug.debug_flags:
1469
mutter_callsite(4, "_lefthand_history scales with history.")
1470
# stop_revision must be a descendant of last_revision
1471
graph = self.repository.get_graph()
1472
if last_rev is not None:
1473
if not graph.is_ancestor(last_rev, revision_id):
1474
# our previous tip is not merged into stop_revision
1475
raise errors.DivergedBranches(self, other_branch)
1476
# make a new revision history from the graph
1477
parents_map = graph.get_parent_map([revision_id])
1478
if revision_id not in parents_map:
1479
raise errors.NoSuchRevision(self, revision_id)
1480
current_rev_id = revision_id
1482
# Do not include ghosts or graph origin in revision_history
1483
while (current_rev_id in parents_map and
1484
len(parents_map[current_rev_id]) > 0):
1485
new_history.append(current_rev_id)
1486
current_rev_id = parents_map[current_rev_id][0]
1487
parents_map = graph.get_parent_map([current_rev_id])
1488
new_history.reverse()
1492
def generate_revision_history(self, revision_id, last_rev=None,
1494
"""Create a new revision history that will finish with revision_id.
1496
:param revision_id: the new tip to use.
1497
:param last_rev: The previous last_revision. If not None, then this
1498
must be a ancestory of revision_id, or DivergedBranches is raised.
1499
:param other_branch: The other branch that DivergedBranches should
1500
raise with respect to.
1502
self.set_revision_history(self._lefthand_history(revision_id,
1503
last_rev, other_branch))
1506
def update_revisions(self, other, stop_revision=None, overwrite=False):
1507
"""See Branch.update_revisions."""
1510
other_last_revno, other_last_revision = other.last_revision_info()
1511
if stop_revision is None:
1512
stop_revision = other_last_revision
1513
if _mod_revision.is_null(stop_revision):
1514
# if there are no commits, we're done.
1516
# whats the current last revision, before we fetch [and change it
1518
last_rev = _mod_revision.ensure_null(self.last_revision())
1519
# we fetch here so that we don't process data twice in the common
1520
# case of having something to pull, and so that the check for
1521
# already merged can operate on the just fetched graph, which will
1522
# be cached in memory.
1523
self.fetch(other, stop_revision)
1524
# Check to see if one is an ancestor of the other
1526
heads = self.repository.get_graph().heads([stop_revision,
1528
if heads == set([last_rev]):
1529
# The current revision is a decendent of the target,
1532
elif heads == set([stop_revision, last_rev]):
1533
# These branches have diverged
1534
raise errors.DivergedBranches(self, other)
1535
elif heads != set([stop_revision]):
1536
raise AssertionError("invalid heads: %r" % heads)
1537
if other_last_revision == stop_revision:
1538
self.set_last_revision_info(other_last_revno,
1539
other_last_revision)
1541
# TODO: jam 2007-11-29 Is there a way to determine the
1542
# revno without searching all of history??
1544
self.generate_revision_history(stop_revision)
1546
self.generate_revision_history(stop_revision,
1547
last_rev=last_rev, other_branch=other)
1551
def basis_tree(self):
1552
"""See Branch.basis_tree."""
1553
return self.repository.revision_tree(self.last_revision())
1556
def pull(self, source, overwrite=False, stop_revision=None,
1557
_hook_master=None, run_hooks=True, possible_transports=None):
1560
:param _hook_master: Private parameter - set the branch to
1561
be supplied as the master to push hooks.
1562
:param run_hooks: Private parameter - if false, this branch
1563
is being called because it's the master of the primary branch,
1564
so it should not run its hooks.
1566
result = PullResult()
1567
result.source_branch = source
1568
result.target_branch = self
1571
result.old_revno, result.old_revid = self.last_revision_info()
1572
self.update_revisions(source, stop_revision, overwrite=overwrite)
1573
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
1574
result.new_revno, result.new_revid = self.last_revision_info()
1576
result.master_branch = _hook_master
1577
result.local_branch = self
1579
result.master_branch = self
1580
result.local_branch = None
1582
for hook in Branch.hooks['post_pull']:
1588
def _get_parent_location(self):
1589
_locs = ['parent', 'pull', 'x-pull']
1592
return self.control_files.get(l).read().strip('\n')
1593
except errors.NoSuchFile:
1598
def push(self, target, overwrite=False, stop_revision=None,
1599
_override_hook_source_branch=None):
1602
This is the basic concrete implementation of push()
1604
:param _override_hook_source_branch: If specified, run
1605
the hooks passing this Branch as the source, rather than self.
1606
This is for use of RemoteBranch, where push is delegated to the
1607
underlying vfs-based Branch.
1609
# TODO: Public option to disable running hooks - should be trivial but
1613
result = self._push_with_bound_branches(target, overwrite,
1615
_override_hook_source_branch=_override_hook_source_branch)
1620
def _push_with_bound_branches(self, target, overwrite,
1622
_override_hook_source_branch=None):
1623
"""Push from self into target, and into target's master if any.
1625
This is on the base BzrBranch class even though it doesn't support
1626
bound branches because the *target* might be bound.
1629
if _override_hook_source_branch:
1630
result.source_branch = _override_hook_source_branch
1631
for hook in Branch.hooks['post_push']:
1634
bound_location = target.get_bound_location()
1635
if bound_location and target.base != bound_location:
1636
# there is a master branch.
1638
# XXX: Why the second check? Is it even supported for a branch to
1639
# be bound to itself? -- mbp 20070507
1640
master_branch = target.get_master_branch()
1641
master_branch.lock_write()
1643
# push into the master from this branch.
1644
self._basic_push(master_branch, overwrite, stop_revision)
1645
# and push into the target branch from this. Note that we push from
1646
# this branch again, because its considered the highest bandwidth
1648
result = self._basic_push(target, overwrite, stop_revision)
1649
result.master_branch = master_branch
1650
result.local_branch = target
1654
master_branch.unlock()
1657
result = self._basic_push(target, overwrite, stop_revision)
1658
# TODO: Why set master_branch and local_branch if there's no
1659
# binding? Maybe cleaner to just leave them unset? -- mbp
1661
result.master_branch = target
1662
result.local_branch = None
1666
def _basic_push(self, target, overwrite, stop_revision):
1667
"""Basic implementation of push without bound branches or hooks.
1669
Must be called with self read locked and target write locked.
1671
result = PushResult()
1672
result.source_branch = self
1673
result.target_branch = target
1674
result.old_revno, result.old_revid = target.last_revision_info()
1676
target.update_revisions(self, stop_revision)
1677
except errors.DivergedBranches:
1681
target.set_revision_history(self.revision_history())
1682
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
1683
result.new_revno, result.new_revid = target.last_revision_info()
1686
def get_parent(self):
1687
"""See Branch.get_parent."""
1688
parent = self._get_parent_location()
1691
# This is an old-format absolute path to a local branch
1692
# turn it into a url
1693
if parent.startswith('/'):
1694
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1696
return urlutils.join(self.base[:-1], parent)
1697
except errors.InvalidURLJoin, e:
1698
raise errors.InaccessibleParent(parent, self.base)
1700
def set_push_location(self, location):
1701
"""See Branch.set_push_location."""
1702
self.get_config().set_user_option(
1703
'push_location', location,
1704
store=_mod_config.STORE_LOCATION_NORECURSE)
1707
def set_parent(self, url):
1708
"""See Branch.set_parent."""
1709
# TODO: Maybe delete old location files?
1710
# URLs should never be unicode, even on the local fs,
1711
# FIXUP this and get_parent in a future branch format bump:
1712
# read and rewrite the file, and have the new format code read
1713
# using .get not .get_utf8. RBC 20060125
1715
if isinstance(url, unicode):
1717
url = url.encode('ascii')
1718
except UnicodeEncodeError:
1719
raise errors.InvalidURL(url,
1720
"Urls must be 7-bit ascii, "
1721
"use bzrlib.urlutils.escape")
1722
url = urlutils.relative_url(self.base, url)
1723
self._set_parent_location(url)
1725
def _set_parent_location(self, url):
1727
self.control_files._transport.delete('parent')
1729
self.control_files.put_bytes('parent', url + '\n')
1732
class BzrBranch5(BzrBranch):
1733
"""A format 5 branch. This supports new features over plain branches.
1735
It has support for a master_branch which is the data for bound branches.
1743
super(BzrBranch5, self).__init__(_format=_format,
1744
_control_files=_control_files,
1746
_repository=_repository)
1749
def pull(self, source, overwrite=False, stop_revision=None,
1750
run_hooks=True, possible_transports=None):
1751
"""Pull from source into self, updating my master if any.
1753
:param run_hooks: Private parameter - if false, this branch
1754
is being called because it's the master of the primary branch,
1755
so it should not run its hooks.
1757
bound_location = self.get_bound_location()
1758
master_branch = None
1759
if bound_location and source.base != bound_location:
1760
# not pulling from master, so we need to update master.
1761
master_branch = self.get_master_branch(possible_transports)
1762
master_branch.lock_write()
1765
# pull from source into master.
1766
master_branch.pull(source, overwrite, stop_revision,
1768
return super(BzrBranch5, self).pull(source, overwrite,
1769
stop_revision, _hook_master=master_branch,
1770
run_hooks=run_hooks)
1773
master_branch.unlock()
1775
def get_bound_location(self):
1777
return self.control_files.get_utf8('bound').read()[:-1]
1778
except errors.NoSuchFile:
1782
def get_master_branch(self, possible_transports=None):
1783
"""Return the branch we are bound to.
1785
:return: Either a Branch, or None
1787
This could memoise the branch, but if thats done
1788
it must be revalidated on each new lock.
1789
So for now we just don't memoise it.
1790
# RBC 20060304 review this decision.
1792
bound_loc = self.get_bound_location()
1796
return Branch.open(bound_loc,
1797
possible_transports=possible_transports)
1798
except (errors.NotBranchError, errors.ConnectionError), e:
1799
raise errors.BoundBranchConnectionFailure(
1803
def set_bound_location(self, location):
1804
"""Set the target where this branch is bound to.
1806
:param location: URL to the target branch
1809
self.control_files.put_utf8('bound', location+'\n')
1812
self.control_files._transport.delete('bound')
1813
except errors.NoSuchFile:
1818
def bind(self, other):
1819
"""Bind this branch to the branch other.
1821
This does not push or pull data between the branches, though it does
1822
check for divergence to raise an error when the branches are not
1823
either the same, or one a prefix of the other. That behaviour may not
1824
be useful, so that check may be removed in future.
1826
:param other: The branch to bind to
1829
# TODO: jam 20051230 Consider checking if the target is bound
1830
# It is debatable whether you should be able to bind to
1831
# a branch which is itself bound.
1832
# Committing is obviously forbidden,
1833
# but binding itself may not be.
1834
# Since we *have* to check at commit time, we don't
1835
# *need* to check here
1837
# we want to raise diverged if:
1838
# last_rev is not in the other_last_rev history, AND
1839
# other_last_rev is not in our history, and do it without pulling
1841
self.set_bound_location(other.base)
1845
"""If bound, unbind"""
1846
return self.set_bound_location(None)
1849
def update(self, possible_transports=None):
1850
"""Synchronise this branch with the master branch if any.
1852
:return: None or the last_revision that was pivoted out during the
1855
master = self.get_master_branch(possible_transports)
1856
if master is not None:
1857
old_tip = _mod_revision.ensure_null(self.last_revision())
1858
self.pull(master, overwrite=True)
1859
if self.repository.get_graph().is_ancestor(old_tip,
1860
_mod_revision.ensure_null(self.last_revision())):
1866
class BzrBranch6(BzrBranch5):
1868
def __init__(self, *args, **kwargs):
1869
super(BzrBranch6, self).__init__(*args, **kwargs)
1870
self._last_revision_info_cache = None
1871
self._partial_revision_history_cache = []
1873
def _clear_cached_state(self):
1874
super(BzrBranch6, self)._clear_cached_state()
1875
self._last_revision_info_cache = None
1876
self._partial_revision_history_cache = []
1879
def last_revision_info(self):
1880
"""Return information about the last revision.
1882
:return: A tuple (revno, revision_id).
1884
if self._last_revision_info_cache is None:
1885
self._last_revision_info_cache = self._last_revision_info()
1886
return self._last_revision_info_cache
1888
def _last_revision_info(self):
1889
revision_string = self.control_files.get('last-revision').read()
1890
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1891
revision_id = cache_utf8.get_cached_utf8(revision_id)
1893
return revno, revision_id
1895
def _write_last_revision_info(self, revno, revision_id):
1896
"""Simply write out the revision id, with no checks.
1898
Use set_last_revision_info to perform this safely.
1900
Does not update the revision_history cache.
1901
Intended to be called by set_last_revision_info and
1902
_write_revision_history.
1904
revision_id = _mod_revision.ensure_null(revision_id)
1905
out_string = '%d %s\n' % (revno, revision_id)
1906
self.control_files.put_bytes('last-revision', out_string)
1909
def set_last_revision_info(self, revno, revision_id):
1910
revision_id = _mod_revision.ensure_null(revision_id)
1911
old_revno, old_revid = self.last_revision_info()
1912
if self._get_append_revisions_only():
1913
self._check_history_violation(revision_id)
1914
self._write_last_revision_info(revno, revision_id)
1915
self._clear_cached_state()
1916
self._last_revision_info_cache = revno, revision_id
1917
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1919
def _check_history_violation(self, revision_id):
1920
last_revision = _mod_revision.ensure_null(self.last_revision())
1921
if _mod_revision.is_null(last_revision):
1923
if last_revision not in self._lefthand_history(revision_id):
1924
raise errors.AppendRevisionsOnlyViolation(self.base)
1926
def _gen_revision_history(self):
1927
"""Generate the revision history from last revision
1929
self._extend_partial_history()
1930
return list(reversed(self._partial_revision_history_cache))
1932
def _extend_partial_history(self, stop_index=None, stop_revision=None):
1933
"""Extend the partial history to include a given index
1935
If a stop_index is supplied, stop when that index has been reached.
1936
If a stop_revision is supplied, stop when that revision is
1937
encountered. Otherwise, stop when the beginning of history is
1940
:param stop_index: The index which should be present. When it is
1941
present, history extension will stop.
1942
:param revision_id: The revision id which should be present. When
1943
it is encountered, history extension will stop.
1945
repo = self.repository
1946
if len(self._partial_revision_history_cache) == 0:
1947
iterator = repo.iter_reverse_revision_history(self.last_revision())
1949
start_revision = self._partial_revision_history_cache[-1]
1950
iterator = repo.iter_reverse_revision_history(start_revision)
1951
#skip the last revision in the list
1952
next_revision = iterator.next()
1953
for revision_id in iterator:
1954
self._partial_revision_history_cache.append(revision_id)
1955
if (stop_index is not None and
1956
len(self._partial_revision_history_cache) > stop_index):
1958
if revision_id == stop_revision:
1961
def _write_revision_history(self, history):
1962
"""Factored out of set_revision_history.
1964
This performs the actual writing to disk, with format-specific checks.
1965
It is intended to be called by BzrBranch5.set_revision_history.
1967
if len(history) == 0:
1968
last_revision = 'null:'
1970
if history != self._lefthand_history(history[-1]):
1971
raise errors.NotLefthandHistory(history)
1972
last_revision = history[-1]
1973
if self._get_append_revisions_only():
1974
self._check_history_violation(last_revision)
1975
self._write_last_revision_info(len(history), last_revision)
1978
def _set_parent_location(self, url):
1979
"""Set the parent branch"""
1980
self._set_config_location('parent_location', url, make_relative=True)
1983
def _get_parent_location(self):
1984
"""Set the parent branch"""
1985
return self._get_config_location('parent_location')
1987
def set_push_location(self, location):
1988
"""See Branch.set_push_location."""
1989
self._set_config_location('push_location', location)
1991
def set_bound_location(self, location):
1992
"""See Branch.set_push_location."""
1994
config = self.get_config()
1995
if location is None:
1996
if config.get_user_option('bound') != 'True':
1999
config.set_user_option('bound', 'False', warn_masked=True)
2002
self._set_config_location('bound_location', location,
2004
config.set_user_option('bound', 'True', warn_masked=True)
2007
def _get_bound_location(self, bound):
2008
"""Return the bound location in the config file.
2010
Return None if the bound parameter does not match"""
2011
config = self.get_config()
2012
config_bound = (config.get_user_option('bound') == 'True')
2013
if config_bound != bound:
2015
return self._get_config_location('bound_location', config=config)
2017
def get_bound_location(self):
2018
"""See Branch.set_push_location."""
2019
return self._get_bound_location(True)
2021
def get_old_bound_location(self):
2022
"""See Branch.get_old_bound_location"""
2023
return self._get_bound_location(False)
2025
def set_append_revisions_only(self, enabled):
2030
self.get_config().set_user_option('append_revisions_only', value,
2033
def _get_append_revisions_only(self):
2034
value = self.get_config().get_user_option('append_revisions_only')
2035
return value == 'True'
2037
def _synchronize_history(self, destination, revision_id):
2038
"""Synchronize last revision and revision history between branches.
2040
This version is most efficient when the destination is also a
2041
BzrBranch6, but works for BzrBranch5, as long as the destination's
2042
repository contains all the lefthand ancestors of the intended
2043
last_revision. If not, set_last_revision_info will fail.
2045
:param destination: The branch to copy the history into
2046
:param revision_id: The revision-id to truncate history at. May
2047
be None to copy complete history.
2049
source_revno, source_revision_id = self.last_revision_info()
2050
if revision_id is None:
2051
revno, revision_id = source_revno, source_revision_id
2052
elif source_revision_id == revision_id:
2053
# we know the revno without needing to walk all of history
2054
revno = source_revno
2056
# To figure out the revno for a random revision, we need to build
2057
# the revision history, and count its length.
2058
# We don't care about the order, just how long it is.
2059
# Alternatively, we could start at the current location, and count
2060
# backwards. But there is no guarantee that we will find it since
2061
# it may be a merged revision.
2062
revno = len(list(self.repository.iter_reverse_revision_history(
2064
destination.set_last_revision_info(revno, revision_id)
2066
def _make_tags(self):
2067
return BasicTags(self)
2070
def generate_revision_history(self, revision_id, last_rev=None,
2072
"""See BzrBranch5.generate_revision_history"""
2073
history = self._lefthand_history(revision_id, last_rev, other_branch)
2074
revno = len(history)
2075
self.set_last_revision_info(revno, revision_id)
2078
def get_rev_id(self, revno, history=None):
2079
"""Find the revision id of the specified revno."""
2081
return _mod_revision.NULL_REVISION
2083
last_revno, last_revision_id = self.last_revision_info()
2084
if revno <= 0 or revno > last_revno:
2085
raise errors.NoSuchRevision(self, revno)
2087
if history is not None:
2088
return history[revno - 1]
2090
index = last_revno - revno
2091
if len(self._partial_revision_history_cache) <= index:
2092
self._extend_partial_history(stop_index=index)
2093
if len(self._partial_revision_history_cache) > index:
2094
return self._partial_revision_history_cache[index]
2096
raise errors.NoSuchRevision(self, revno)
2099
def revision_id_to_revno(self, revision_id):
2100
"""Given a revision id, return its revno"""
2101
if _mod_revision.is_null(revision_id):
2104
index = self._partial_revision_history_cache.index(revision_id)
2106
self._extend_partial_history(stop_revision=revision_id)
2107
index = len(self._partial_revision_history_cache) - 1
2108
if self._partial_revision_history_cache[index] != revision_id:
2109
raise errors.NoSuchRevision(self, revision_id)
2110
return self.revno() - index
2113
######################################################################
2114
# results of operations
2117
class _Result(object):
2119
def _show_tag_conficts(self, to_file):
2120
if not getattr(self, 'tag_conflicts', None):
2122
to_file.write('Conflicting tags:\n')
2123
for name, value1, value2 in self.tag_conflicts:
2124
to_file.write(' %s\n' % (name, ))
2127
class PullResult(_Result):
2128
"""Result of a Branch.pull operation.
2130
:ivar old_revno: Revision number before pull.
2131
:ivar new_revno: Revision number after pull.
2132
:ivar old_revid: Tip revision id before pull.
2133
:ivar new_revid: Tip revision id after pull.
2134
:ivar source_branch: Source (local) branch object.
2135
:ivar master_branch: Master branch of the target, or None.
2136
:ivar target_branch: Target/destination branch object.
2140
# DEPRECATED: pull used to return the change in revno
2141
return self.new_revno - self.old_revno
2143
def report(self, to_file):
2145
if self.old_revid == self.new_revid:
2146
to_file.write('No revisions to pull.\n')
2148
to_file.write('Now on revision %d.\n' % self.new_revno)
2149
self._show_tag_conficts(to_file)
2152
class PushResult(_Result):
2153
"""Result of a Branch.push operation.
2155
:ivar old_revno: Revision number before push.
2156
:ivar new_revno: Revision number after push.
2157
:ivar old_revid: Tip revision id before push.
2158
:ivar new_revid: Tip revision id after push.
2159
:ivar source_branch: Source branch object.
2160
:ivar master_branch: Master branch of the target, or None.
2161
:ivar target_branch: Target/destination branch object.
2165
# DEPRECATED: push used to return the change in revno
2166
return self.new_revno - self.old_revno
2168
def report(self, to_file):
2169
"""Write a human-readable description of the result."""
2170
if self.old_revid == self.new_revid:
2171
to_file.write('No new revisions to push.\n')
2173
to_file.write('Pushed up to revision %d.\n' % self.new_revno)
2174
self._show_tag_conficts(to_file)
2177
class BranchCheckResult(object):
2178
"""Results of checking branch consistency.
2183
def __init__(self, branch):
2184
self.branch = branch
2186
def report_results(self, verbose):
2187
"""Report the check results via trace.note.
2189
:param verbose: Requests more detailed display of what was checked,
2192
note('checked branch %s format %s',
2194
self.branch._format)
2197
class Converter5to6(object):
2198
"""Perform an in-place upgrade of format 5 to format 6"""
2200
def convert(self, branch):
2201
# Data for 5 and 6 can peacefully coexist.
2202
format = BzrBranchFormat6()
2203
new_branch = format.open(branch.bzrdir, _found=True)
2205
# Copy source data into target
2206
new_branch._write_last_revision_info(*branch.last_revision_info())
2207
new_branch.set_parent(branch.get_parent())
2208
new_branch.set_bound_location(branch.get_bound_location())
2209
new_branch.set_push_location(branch.get_push_location())
2211
# New branch has no tags by default
2212
new_branch.tags._set_tag_dict({})
2214
# Copying done; now update target format
2215
new_branch.control_files.put_utf8('format',
2216
format.get_format_string())
2218
# Clean up old files
2219
new_branch.control_files._transport.delete('revision-history')
2221
branch.set_parent(None)
2222
except errors.NoSuchFile:
2224
branch.set_bound_location(None)