1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
from bzrlib.lazy_import import lazy_import
21
lazy_import(globals(), """
22
from itertools import chain
26
config as _mod_config,
32
revision as _mod_revision,
39
from bzrlib.config import BranchConfig
40
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
41
from bzrlib.tag import (
47
from bzrlib.decorators import needs_read_lock, needs_write_lock
48
from bzrlib.hooks import HookPoint, Hooks
49
from bzrlib.inter import InterObject
50
from bzrlib import registry
51
from bzrlib.symbol_versioning import (
55
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
58
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
59
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
60
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
63
# TODO: Maybe include checks for common corruption of newlines, etc?
65
# TODO: Some operations like log might retrieve the same revisions
66
# repeatedly to calculate deltas. We could perhaps have a weakref
67
# cache in memory to make this faster. In general anything can be
68
# cached in memory between lock and unlock operations. .. nb thats
69
# what the transaction identity map provides
72
######################################################################
76
"""Branch holding a history of revisions.
79
Base directory/url of the branch.
81
hooks: An instance of BranchHooks.
83
# this is really an instance variable - FIXME move it there
87
def __init__(self, *ignored, **ignored_too):
88
self.tags = self._format.make_tags(self)
89
self._revision_history_cache = None
90
self._revision_id_to_revno_cache = None
91
self._partial_revision_id_to_revno_cache = {}
92
self._last_revision_info_cache = None
93
self._merge_sorted_revisions_cache = None
95
hooks = Branch.hooks['open']
100
"""Called by init to allow simpler extension of the base class."""
102
def _activate_fallback_location(self, url):
103
"""Activate the branch/repository from url as a fallback repository."""
104
self.repository.add_fallback_repository(
105
self._get_fallback_repository(url))
107
def break_lock(self):
108
"""Break a lock if one is present from another instance.
110
Uses the ui factory to ask for confirmation if the lock may be from
113
This will probe the repository for its lock as well.
115
self.control_files.break_lock()
116
self.repository.break_lock()
117
master = self.get_master_branch()
118
if master is not None:
121
def _check_stackable_repo(self):
122
if not self.repository._format.supports_external_lookups:
123
raise errors.UnstackableRepositoryFormat(self.repository._format,
124
self.repository.base)
127
def open(base, _unsupported=False, possible_transports=None):
128
"""Open the branch rooted at base.
130
For instance, if the branch is at URL/.bzr/branch,
131
Branch.open(URL) -> a Branch instance.
133
control = bzrdir.BzrDir.open(base, _unsupported,
134
possible_transports=possible_transports)
135
return control.open_branch(_unsupported)
138
def open_from_transport(transport, _unsupported=False):
139
"""Open the branch rooted at transport"""
140
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
141
return control.open_branch(_unsupported)
144
def open_containing(url, possible_transports=None):
145
"""Open an existing branch which contains url.
147
This probes for a branch at url, and searches upwards from there.
149
Basically we keep looking up until we find the control directory or
150
run into the root. If there isn't one, raises NotBranchError.
151
If there is one and it is either an unrecognised format or an unsupported
152
format, UnknownFormatError or UnsupportedFormatError are raised.
153
If there is one, it is returned, along with the unused portion of url.
155
control, relpath = bzrdir.BzrDir.open_containing(url,
157
return control.open_branch(), relpath
159
def _push_should_merge_tags(self):
160
"""Should _basic_push merge this branch's tags into the target?
162
The default implementation returns False if this branch has no tags,
163
and True the rest of the time. Subclasses may override this.
165
return self.supports_tags() and self.tags.get_tag_dict()
167
def get_config(self):
168
return BranchConfig(self)
170
def _get_fallback_repository(self, url):
171
"""Get the repository we fallback to at url."""
172
url = urlutils.join(self.base, url)
173
a_bzrdir = bzrdir.BzrDir.open(url,
174
possible_transports=[self.bzrdir.root_transport])
175
return a_bzrdir.open_branch().repository
177
def _get_tags_bytes(self):
178
"""Get the bytes of a serialised tags dict.
180
Note that not all branches support tags, nor do all use the same tags
181
logic: this method is specific to BasicTags. Other tag implementations
182
may use the same method name and behave differently, safely, because
183
of the double-dispatch via
184
format.make_tags->tags_instance->get_tags_dict.
186
:return: The bytes of the tags file.
187
:seealso: Branch._set_tags_bytes.
189
return self._transport.get_bytes('tags')
191
def _get_nick(self, local=False, possible_transports=None):
192
config = self.get_config()
193
# explicit overrides master, but don't look for master if local is True
194
if not local and not config.has_explicit_nickname():
196
master = self.get_master_branch(possible_transports)
197
if master is not None:
198
# return the master branch value
200
except errors.BzrError, e:
201
# Silently fall back to local implicit nick if the master is
203
mutter("Could not connect to bound branch, "
204
"falling back to local nick.\n " + str(e))
205
return config.get_nickname()
207
def _set_nick(self, nick):
208
self.get_config().set_user_option('nickname', nick, warn_masked=True)
210
nick = property(_get_nick, _set_nick)
213
raise NotImplementedError(self.is_locked)
215
def _lefthand_history(self, revision_id, last_rev=None,
217
if 'evil' in debug.debug_flags:
218
mutter_callsite(4, "_lefthand_history scales with history.")
219
# stop_revision must be a descendant of last_revision
220
graph = self.repository.get_graph()
221
if last_rev is not None:
222
if not graph.is_ancestor(last_rev, revision_id):
223
# our previous tip is not merged into stop_revision
224
raise errors.DivergedBranches(self, other_branch)
225
# make a new revision history from the graph
226
parents_map = graph.get_parent_map([revision_id])
227
if revision_id not in parents_map:
228
raise errors.NoSuchRevision(self, revision_id)
229
current_rev_id = revision_id
231
check_not_reserved_id = _mod_revision.check_not_reserved_id
232
# Do not include ghosts or graph origin in revision_history
233
while (current_rev_id in parents_map and
234
len(parents_map[current_rev_id]) > 0):
235
check_not_reserved_id(current_rev_id)
236
new_history.append(current_rev_id)
237
current_rev_id = parents_map[current_rev_id][0]
238
parents_map = graph.get_parent_map([current_rev_id])
239
new_history.reverse()
242
def lock_write(self):
243
raise NotImplementedError(self.lock_write)
246
raise NotImplementedError(self.lock_read)
249
raise NotImplementedError(self.unlock)
251
def peek_lock_mode(self):
252
"""Return lock mode for the Branch: 'r', 'w' or None"""
253
raise NotImplementedError(self.peek_lock_mode)
255
def get_physical_lock_status(self):
256
raise NotImplementedError(self.get_physical_lock_status)
259
def dotted_revno_to_revision_id(self, revno, _cache_reverse=False):
260
"""Return the revision_id for a dotted revno.
262
:param revno: a tuple like (1,) or (1,1,2)
263
:param _cache_reverse: a private parameter enabling storage
264
of the reverse mapping in a top level cache. (This should
265
only be done in selective circumstances as we want to
266
avoid having the mapping cached multiple times.)
267
:return: the revision_id
268
:raises errors.NoSuchRevision: if the revno doesn't exist
270
rev_id = self._do_dotted_revno_to_revision_id(revno)
272
self._partial_revision_id_to_revno_cache[rev_id] = revno
275
def _do_dotted_revno_to_revision_id(self, revno):
276
"""Worker function for dotted_revno_to_revision_id.
278
Subclasses should override this if they wish to
279
provide a more efficient implementation.
282
return self.get_rev_id(revno[0])
283
revision_id_to_revno = self.get_revision_id_to_revno_map()
284
revision_ids = [revision_id for revision_id, this_revno
285
in revision_id_to_revno.iteritems()
286
if revno == this_revno]
287
if len(revision_ids) == 1:
288
return revision_ids[0]
290
revno_str = '.'.join(map(str, revno))
291
raise errors.NoSuchRevision(self, revno_str)
294
def revision_id_to_dotted_revno(self, revision_id):
295
"""Given a revision id, return its dotted revno.
297
:return: a tuple like (1,) or (400,1,3).
299
return self._do_revision_id_to_dotted_revno(revision_id)
301
def _do_revision_id_to_dotted_revno(self, revision_id):
302
"""Worker function for revision_id_to_revno."""
303
# Try the caches if they are loaded
304
result = self._partial_revision_id_to_revno_cache.get(revision_id)
305
if result is not None:
307
if self._revision_id_to_revno_cache:
308
result = self._revision_id_to_revno_cache.get(revision_id)
310
raise errors.NoSuchRevision(self, revision_id)
311
# Try the mainline as it's optimised
313
revno = self.revision_id_to_revno(revision_id)
315
except errors.NoSuchRevision:
316
# We need to load and use the full revno map after all
317
result = self.get_revision_id_to_revno_map().get(revision_id)
319
raise errors.NoSuchRevision(self, revision_id)
323
def get_revision_id_to_revno_map(self):
324
"""Return the revision_id => dotted revno map.
326
This will be regenerated on demand, but will be cached.
328
:return: A dictionary mapping revision_id => dotted revno.
329
This dictionary should not be modified by the caller.
331
if self._revision_id_to_revno_cache is not None:
332
mapping = self._revision_id_to_revno_cache
334
mapping = self._gen_revno_map()
335
self._cache_revision_id_to_revno(mapping)
336
# TODO: jam 20070417 Since this is being cached, should we be returning
338
# I would rather not, and instead just declare that users should not
339
# modify the return value.
342
def _gen_revno_map(self):
343
"""Create a new mapping from revision ids to dotted revnos.
345
Dotted revnos are generated based on the current tip in the revision
347
This is the worker function for get_revision_id_to_revno_map, which
348
just caches the return value.
350
:return: A dictionary mapping revision_id => dotted revno.
352
revision_id_to_revno = dict((rev_id, revno)
353
for rev_id, depth, revno, end_of_merge
354
in self.iter_merge_sorted_revisions())
355
return revision_id_to_revno
358
def iter_merge_sorted_revisions(self, start_revision_id=None,
359
stop_revision_id=None, stop_rule='exclude', direction='reverse'):
360
"""Walk the revisions for a branch in merge sorted order.
362
Merge sorted order is the output from a merge-aware,
363
topological sort, i.e. all parents come before their
364
children going forward; the opposite for reverse.
366
:param start_revision_id: the revision_id to begin walking from.
367
If None, the branch tip is used.
368
:param stop_revision_id: the revision_id to terminate the walk
369
after. If None, the rest of history is included.
370
:param stop_rule: if stop_revision_id is not None, the precise rule
371
to use for termination:
372
* 'exclude' - leave the stop revision out of the result (default)
373
* 'include' - the stop revision is the last item in the result
374
* 'with-merges' - include the stop revision and all of its
375
merged revisions in the result
376
:param direction: either 'reverse' or 'forward':
377
* reverse means return the start_revision_id first, i.e.
378
start at the most recent revision and go backwards in history
379
* forward returns tuples in the opposite order to reverse.
380
Note in particular that forward does *not* do any intelligent
381
ordering w.r.t. depth as some clients of this API may like.
382
(If required, that ought to be done at higher layers.)
384
:return: an iterator over (revision_id, depth, revno, end_of_merge)
387
* revision_id: the unique id of the revision
388
* depth: How many levels of merging deep this node has been
390
* revno_sequence: This field provides a sequence of
391
revision numbers for all revisions. The format is:
392
(REVNO, BRANCHNUM, BRANCHREVNO). BRANCHNUM is the number of the
393
branch that the revno is on. From left to right the REVNO numbers
394
are the sequence numbers within that branch of the revision.
395
* end_of_merge: When True the next node (earlier in history) is
396
part of a different merge.
398
# Note: depth and revno values are in the context of the branch so
399
# we need the full graph to get stable numbers, regardless of the
401
if self._merge_sorted_revisions_cache is None:
402
last_revision = self.last_revision()
403
graph = self.repository.get_graph()
404
parent_map = dict(((key, value) for key, value in
405
graph.iter_ancestry([last_revision]) if value is not None))
406
revision_graph = repository._strip_NULL_ghosts(parent_map)
407
revs = tsort.merge_sort(revision_graph, last_revision, None,
409
# Drop the sequence # before caching
410
self._merge_sorted_revisions_cache = [r[1:] for r in revs]
412
filtered = self._filter_merge_sorted_revisions(
413
self._merge_sorted_revisions_cache, start_revision_id,
414
stop_revision_id, stop_rule)
415
if direction == 'reverse':
417
if direction == 'forward':
418
return reversed(list(filtered))
420
raise ValueError('invalid direction %r' % direction)
422
def _filter_merge_sorted_revisions(self, merge_sorted_revisions,
423
start_revision_id, stop_revision_id, stop_rule):
424
"""Iterate over an inclusive range of sorted revisions."""
425
rev_iter = iter(merge_sorted_revisions)
426
if start_revision_id is not None:
427
for rev_id, depth, revno, end_of_merge in rev_iter:
428
if rev_id != start_revision_id:
431
# The decision to include the start or not
432
# depends on the stop_rule if a stop is provided
434
iter([(rev_id, depth, revno, end_of_merge)]),
437
if stop_revision_id is None:
438
for rev_id, depth, revno, end_of_merge in rev_iter:
439
yield rev_id, depth, revno, end_of_merge
440
elif stop_rule == 'exclude':
441
for rev_id, depth, revno, end_of_merge in rev_iter:
442
if rev_id == stop_revision_id:
444
yield rev_id, depth, revno, end_of_merge
445
elif stop_rule == 'include':
446
for rev_id, depth, revno, end_of_merge in rev_iter:
447
yield rev_id, depth, revno, end_of_merge
448
if rev_id == stop_revision_id:
450
elif stop_rule == 'with-merges':
451
stop_rev = self.repository.get_revision(stop_revision_id)
452
if stop_rev.parent_ids:
453
left_parent = stop_rev.parent_ids[0]
455
left_parent = _mod_revision.NULL_REVISION
456
for rev_id, depth, revno, end_of_merge in rev_iter:
457
if rev_id == left_parent:
459
yield rev_id, depth, revno, end_of_merge
461
raise ValueError('invalid stop_rule %r' % stop_rule)
463
def leave_lock_in_place(self):
464
"""Tell this branch object not to release the physical lock when this
467
If lock_write doesn't return a token, then this method is not supported.
469
self.control_files.leave_in_place()
471
def dont_leave_lock_in_place(self):
472
"""Tell this branch object to release the physical lock when this
473
object is unlocked, even if it didn't originally acquire it.
475
If lock_write doesn't return a token, then this method is not supported.
477
self.control_files.dont_leave_in_place()
479
def bind(self, other):
480
"""Bind the local branch the other branch.
482
:param other: The branch to bind to
485
raise errors.UpgradeRequired(self.base)
488
def fetch(self, from_branch, last_revision=None, pb=None):
489
"""Copy revisions from from_branch into this branch.
491
:param from_branch: Where to copy from.
492
:param last_revision: What revision to stop at (None for at the end
494
:param pb: An optional progress bar to use.
497
if self.base == from_branch.base:
500
symbol_versioning.warn(
501
symbol_versioning.deprecated_in((1, 14, 0))
502
% "pb parameter to fetch()")
503
from_branch.lock_read()
505
if last_revision is None:
506
last_revision = from_branch.last_revision()
507
last_revision = _mod_revision.ensure_null(last_revision)
508
return self.repository.fetch(from_branch.repository,
509
revision_id=last_revision,
514
def get_bound_location(self):
515
"""Return the URL of the branch we are bound to.
517
Older format branches cannot bind, please be sure to use a metadir
522
def get_old_bound_location(self):
523
"""Return the URL of the branch we used to be bound to
525
raise errors.UpgradeRequired(self.base)
527
def get_commit_builder(self, parents, config=None, timestamp=None,
528
timezone=None, committer=None, revprops=None,
530
"""Obtain a CommitBuilder for this branch.
532
:param parents: Revision ids of the parents of the new revision.
533
:param config: Optional configuration to use.
534
:param timestamp: Optional timestamp recorded for commit.
535
:param timezone: Optional timezone for timestamp.
536
:param committer: Optional committer to set for commit.
537
:param revprops: Optional dictionary of revision properties.
538
:param revision_id: Optional revision id.
542
config = self.get_config()
544
return self.repository.get_commit_builder(self, parents, config,
545
timestamp, timezone, committer, revprops, revision_id)
547
def get_master_branch(self, possible_transports=None):
548
"""Return the branch we are bound to.
550
:return: Either a Branch, or None
554
def get_revision_delta(self, revno):
555
"""Return the delta for one revision.
557
The delta is relative to its mainline predecessor, or the
558
empty tree for revision 1.
560
rh = self.revision_history()
561
if not (1 <= revno <= len(rh)):
562
raise errors.InvalidRevisionNumber(revno)
563
return self.repository.get_revision_delta(rh[revno-1])
565
def get_stacked_on_url(self):
566
"""Get the URL this branch is stacked against.
568
:raises NotStacked: If the branch is not stacked.
569
:raises UnstackableBranchFormat: If the branch does not support
572
raise NotImplementedError(self.get_stacked_on_url)
574
def print_file(self, file, revision_id):
575
"""Print `file` to stdout."""
576
raise NotImplementedError(self.print_file)
578
def set_revision_history(self, rev_history):
579
raise NotImplementedError(self.set_revision_history)
581
def set_stacked_on_url(self, url):
582
"""Set the URL this branch is stacked against.
584
:raises UnstackableBranchFormat: If the branch does not support
586
:raises UnstackableRepositoryFormat: If the repository does not support
589
if not self._format.supports_stacking():
590
raise errors.UnstackableBranchFormat(self._format, self.base)
591
self._check_stackable_repo()
594
old_url = self.get_stacked_on_url()
595
except (errors.NotStacked, errors.UnstackableBranchFormat,
596
errors.UnstackableRepositoryFormat):
599
# repositories don't offer an interface to remove fallback
600
# repositories today; take the conceptually simpler option and just
602
self.repository = self.bzrdir.find_repository()
603
# for every revision reference the branch has, ensure it is pulled
605
source_repository = self._get_fallback_repository(old_url)
606
for revision_id in chain([self.last_revision()],
607
self.tags.get_reverse_tag_dict()):
608
self.repository.fetch(source_repository, revision_id,
611
self._activate_fallback_location(url)
612
# write this out after the repository is stacked to avoid setting a
613
# stacked config that doesn't work.
614
self._set_config_location('stacked_on_location', url)
617
def _set_tags_bytes(self, bytes):
618
"""Mirror method for _get_tags_bytes.
620
:seealso: Branch._get_tags_bytes.
622
return _run_with_write_locked_target(self, self._transport.put_bytes,
625
def _cache_revision_history(self, rev_history):
626
"""Set the cached revision history to rev_history.
628
The revision_history method will use this cache to avoid regenerating
629
the revision history.
631
This API is semi-public; it only for use by subclasses, all other code
632
should consider it to be private.
634
self._revision_history_cache = rev_history
636
def _cache_revision_id_to_revno(self, revision_id_to_revno):
637
"""Set the cached revision_id => revno map to revision_id_to_revno.
639
This API is semi-public; it only for use by subclasses, all other code
640
should consider it to be private.
642
self._revision_id_to_revno_cache = revision_id_to_revno
644
def _clear_cached_state(self):
645
"""Clear any cached data on this branch, e.g. cached revision history.
647
This means the next call to revision_history will need to call
648
_gen_revision_history.
650
This API is semi-public; it only for use by subclasses, all other code
651
should consider it to be private.
653
self._revision_history_cache = None
654
self._revision_id_to_revno_cache = None
655
self._last_revision_info_cache = None
656
self._merge_sorted_revisions_cache = None
658
def _gen_revision_history(self):
659
"""Return sequence of revision hashes on to this branch.
661
Unlike revision_history, this method always regenerates or rereads the
662
revision history, i.e. it does not cache the result, so repeated calls
665
Concrete subclasses should override this instead of revision_history so
666
that subclasses do not need to deal with caching logic.
668
This API is semi-public; it only for use by subclasses, all other code
669
should consider it to be private.
671
raise NotImplementedError(self._gen_revision_history)
674
def revision_history(self):
675
"""Return sequence of revision ids on this branch.
677
This method will cache the revision history for as long as it is safe to
680
if 'evil' in debug.debug_flags:
681
mutter_callsite(3, "revision_history scales with history.")
682
if self._revision_history_cache is not None:
683
history = self._revision_history_cache
685
history = self._gen_revision_history()
686
self._cache_revision_history(history)
690
"""Return current revision number for this branch.
692
That is equivalent to the number of revisions committed to
695
return self.last_revision_info()[0]
698
"""Older format branches cannot bind or unbind."""
699
raise errors.UpgradeRequired(self.base)
701
def set_append_revisions_only(self, enabled):
702
"""Older format branches are never restricted to append-only"""
703
raise errors.UpgradeRequired(self.base)
705
def last_revision(self):
706
"""Return last revision id, or NULL_REVISION."""
707
return self.last_revision_info()[1]
710
def last_revision_info(self):
711
"""Return information about the last revision.
713
:return: A tuple (revno, revision_id).
715
if self._last_revision_info_cache is None:
716
self._last_revision_info_cache = self._last_revision_info()
717
return self._last_revision_info_cache
719
def _last_revision_info(self):
720
rh = self.revision_history()
723
return (revno, rh[-1])
725
return (0, _mod_revision.NULL_REVISION)
727
@deprecated_method(deprecated_in((1, 6, 0)))
728
def missing_revisions(self, other, stop_revision=None):
729
"""Return a list of new revisions that would perfectly fit.
731
If self and other have not diverged, return a list of the revisions
732
present in other, but missing from self.
734
self_history = self.revision_history()
735
self_len = len(self_history)
736
other_history = other.revision_history()
737
other_len = len(other_history)
738
common_index = min(self_len, other_len) -1
739
if common_index >= 0 and \
740
self_history[common_index] != other_history[common_index]:
741
raise errors.DivergedBranches(self, other)
743
if stop_revision is None:
744
stop_revision = other_len
746
if stop_revision > other_len:
747
raise errors.NoSuchRevision(self, stop_revision)
748
return other_history[self_len:stop_revision]
751
def update_revisions(self, other, stop_revision=None, overwrite=False,
753
"""Pull in new perfect-fit revisions.
755
:param other: Another Branch to pull from
756
:param stop_revision: Updated until the given revision
757
:param overwrite: Always set the branch pointer, rather than checking
758
to see if it is a proper descendant.
759
:param graph: A Graph object that can be used to query history
760
information. This can be None.
763
return InterBranch.get(other, self).update_revisions(stop_revision,
766
def import_last_revision_info(self, source_repo, revno, revid):
767
"""Set the last revision info, importing from another repo if necessary.
769
This is used by the bound branch code to upload a revision to
770
the master branch first before updating the tip of the local branch.
772
:param source_repo: Source repository to optionally fetch from
773
:param revno: Revision number of the new tip
774
:param revid: Revision id of the new tip
776
if not self.repository.has_same_location(source_repo):
777
self.repository.fetch(source_repo, revision_id=revid)
778
self.set_last_revision_info(revno, revid)
780
def revision_id_to_revno(self, revision_id):
781
"""Given a revision id, return its revno"""
782
if _mod_revision.is_null(revision_id):
784
history = self.revision_history()
786
return history.index(revision_id) + 1
788
raise errors.NoSuchRevision(self, revision_id)
790
def get_rev_id(self, revno, history=None):
791
"""Find the revision id of the specified revno."""
793
return _mod_revision.NULL_REVISION
795
history = self.revision_history()
796
if revno <= 0 or revno > len(history):
797
raise errors.NoSuchRevision(self, revno)
798
return history[revno - 1]
800
def pull(self, source, overwrite=False, stop_revision=None,
801
possible_transports=None, _override_hook_target=None):
802
"""Mirror source into this branch.
804
This branch is considered to be 'local', having low latency.
806
:returns: PullResult instance
808
raise NotImplementedError(self.pull)
810
def push(self, target, overwrite=False, stop_revision=None):
811
"""Mirror this branch into target.
813
This branch is considered to be 'local', having low latency.
815
raise NotImplementedError(self.push)
817
def basis_tree(self):
818
"""Return `Tree` object for last revision."""
819
return self.repository.revision_tree(self.last_revision())
821
def get_parent(self):
822
"""Return the parent location of the branch.
824
This is the default location for pull/missing. The usual
825
pattern is that the user can override it by specifying a
828
parent = self._get_parent_location()
831
# This is an old-format absolute path to a local branch
833
if parent.startswith('/'):
834
parent = urlutils.local_path_to_url(parent.decode('utf8'))
836
return urlutils.join(self.base[:-1], parent)
837
except errors.InvalidURLJoin, e:
838
raise errors.InaccessibleParent(parent, self.base)
840
def _get_parent_location(self):
841
raise NotImplementedError(self._get_parent_location)
843
def _set_config_location(self, name, url, config=None,
844
make_relative=False):
846
config = self.get_config()
850
url = urlutils.relative_url(self.base, url)
851
config.set_user_option(name, url, warn_masked=True)
853
def _get_config_location(self, name, config=None):
855
config = self.get_config()
856
location = config.get_user_option(name)
861
def get_submit_branch(self):
862
"""Return the submit location of the branch.
864
This is the default location for bundle. The usual
865
pattern is that the user can override it by specifying a
868
return self.get_config().get_user_option('submit_branch')
870
def set_submit_branch(self, location):
871
"""Return the submit location of the branch.
873
This is the default location for bundle. The usual
874
pattern is that the user can override it by specifying a
877
self.get_config().set_user_option('submit_branch', location,
880
def get_public_branch(self):
881
"""Return the public location of the branch.
883
This is is used by merge directives.
885
return self._get_config_location('public_branch')
887
def set_public_branch(self, location):
888
"""Return the submit location of the branch.
890
This is the default location for bundle. The usual
891
pattern is that the user can override it by specifying a
894
self._set_config_location('public_branch', location)
896
def get_push_location(self):
897
"""Return the None or the location to push this branch to."""
898
push_loc = self.get_config().get_user_option('push_location')
901
def set_push_location(self, location):
902
"""Set a new push location for this branch."""
903
raise NotImplementedError(self.set_push_location)
905
def _run_post_change_branch_tip_hooks(self, old_revno, old_revid):
906
"""Run the post_change_branch_tip hooks."""
907
hooks = Branch.hooks['post_change_branch_tip']
910
new_revno, new_revid = self.last_revision_info()
911
params = ChangeBranchTipParams(
912
self, old_revno, new_revno, old_revid, new_revid)
916
def _run_pre_change_branch_tip_hooks(self, new_revno, new_revid):
917
"""Run the pre_change_branch_tip hooks."""
918
hooks = Branch.hooks['pre_change_branch_tip']
921
old_revno, old_revid = self.last_revision_info()
922
params = ChangeBranchTipParams(
923
self, old_revno, new_revno, old_revid, new_revid)
927
except errors.TipChangeRejected:
930
exc_info = sys.exc_info()
931
hook_name = Branch.hooks.get_hook_name(hook)
932
raise errors.HookFailed(
933
'pre_change_branch_tip', hook_name, exc_info)
935
def set_parent(self, url):
936
raise NotImplementedError(self.set_parent)
940
"""Synchronise this branch with the master branch if any.
942
:return: None or the last_revision pivoted out during the update.
946
def check_revno(self, revno):
948
Check whether a revno corresponds to any revision.
949
Zero (the NULL revision) is considered valid.
952
self.check_real_revno(revno)
954
def check_real_revno(self, revno):
956
Check whether a revno corresponds to a real revision.
957
Zero (the NULL revision) is considered invalid
959
if revno < 1 or revno > self.revno():
960
raise errors.InvalidRevisionNumber(revno)
963
def clone(self, to_bzrdir, revision_id=None, repository_policy=None):
964
"""Clone this branch into to_bzrdir preserving all semantic values.
966
Most API users will want 'create_clone_on_transport', which creates a
967
new bzrdir and branch on the fly.
969
revision_id: if not None, the revision history in the new branch will
970
be truncated to end with revision_id.
972
result = to_bzrdir.create_branch()
973
if repository_policy is not None:
974
repository_policy.configure_branch(result)
975
self.copy_content_into(result, revision_id=revision_id)
979
def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
980
"""Create a new line of development from the branch, into to_bzrdir.
982
to_bzrdir controls the branch format.
984
revision_id: if not None, the revision history in the new branch will
985
be truncated to end with revision_id.
987
result = to_bzrdir.create_branch()
988
if repository_policy is not None:
989
repository_policy.configure_branch(result)
990
self.copy_content_into(result, revision_id=revision_id)
991
result.set_parent(self.bzrdir.root_transport.base)
994
def _synchronize_history(self, destination, revision_id):
995
"""Synchronize last revision and revision history between branches.
997
This version is most efficient when the destination is also a
998
BzrBranch6, but works for BzrBranch5, as long as the destination's
999
repository contains all the lefthand ancestors of the intended
1000
last_revision. If not, set_last_revision_info will fail.
1002
:param destination: The branch to copy the history into
1003
:param revision_id: The revision-id to truncate history at. May
1004
be None to copy complete history.
1006
source_revno, source_revision_id = self.last_revision_info()
1007
if revision_id is None:
1008
revno, revision_id = source_revno, source_revision_id
1009
elif source_revision_id == revision_id:
1010
# we know the revno without needing to walk all of history
1011
revno = source_revno
1013
# To figure out the revno for a random revision, we need to build
1014
# the revision history, and count its length.
1015
# We don't care about the order, just how long it is.
1016
# Alternatively, we could start at the current location, and count
1017
# backwards. But there is no guarantee that we will find it since
1018
# it may be a merged revision.
1019
revno = len(list(self.repository.iter_reverse_revision_history(
1021
destination.set_last_revision_info(revno, revision_id)
1024
def copy_content_into(self, destination, revision_id=None):
1025
"""Copy the content of self into destination.
1027
revision_id: if not None, the revision history in the new branch will
1028
be truncated to end with revision_id.
1030
self._synchronize_history(destination, revision_id)
1032
parent = self.get_parent()
1033
except errors.InaccessibleParent, e:
1034
mutter('parent was not accessible to copy: %s', e)
1037
destination.set_parent(parent)
1038
if self._push_should_merge_tags():
1039
self.tags.merge_to(destination.tags)
1043
"""Check consistency of the branch.
1045
In particular this checks that revisions given in the revision-history
1046
do actually match up in the revision graph, and that they're all
1047
present in the repository.
1049
Callers will typically also want to check the repository.
1051
:return: A BranchCheckResult.
1053
mainline_parent_id = None
1054
last_revno, last_revision_id = self.last_revision_info()
1055
real_rev_history = list(self.repository.iter_reverse_revision_history(
1057
real_rev_history.reverse()
1058
if len(real_rev_history) != last_revno:
1059
raise errors.BzrCheckError('revno does not match len(mainline)'
1060
' %s != %s' % (last_revno, len(real_rev_history)))
1061
# TODO: We should probably also check that real_rev_history actually
1062
# matches self.revision_history()
1063
for revision_id in real_rev_history:
1065
revision = self.repository.get_revision(revision_id)
1066
except errors.NoSuchRevision, e:
1067
raise errors.BzrCheckError("mainline revision {%s} not in repository"
1069
# In general the first entry on the revision history has no parents.
1070
# But it's not illegal for it to have parents listed; this can happen
1071
# in imports from Arch when the parents weren't reachable.
1072
if mainline_parent_id is not None:
1073
if mainline_parent_id not in revision.parent_ids:
1074
raise errors.BzrCheckError("previous revision {%s} not listed among "
1076
% (mainline_parent_id, revision_id))
1077
mainline_parent_id = revision_id
1078
return BranchCheckResult(self)
1080
def _get_checkout_format(self):
1081
"""Return the most suitable metadir for a checkout of this branch.
1082
Weaves are used if this branch's repository uses weaves.
1084
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
1085
from bzrlib.repofmt import weaverepo
1086
format = bzrdir.BzrDirMetaFormat1()
1087
format.repository_format = weaverepo.RepositoryFormat7()
1089
format = self.repository.bzrdir.checkout_metadir()
1090
format.set_branch_format(self._format)
1093
def create_clone_on_transport(self, to_transport, revision_id=None,
1095
"""Create a clone of this branch and its bzrdir.
1097
:param to_transport: The transport to clone onto.
1098
:param revision_id: The revision id to use as tip in the new branch.
1099
If None the tip is obtained from this branch.
1100
:param stacked_on: An optional URL to stack the clone on.
1102
# XXX: Fix the bzrdir API to allow getting the branch back from the
1103
# clone call. Or something. 20090224 RBC/spiv.
1104
dir_to = self.bzrdir.clone_on_transport(to_transport,
1105
revision_id=revision_id, stacked_on=stacked_on)
1106
return dir_to.open_branch()
1108
def create_checkout(self, to_location, revision_id=None,
1109
lightweight=False, accelerator_tree=None,
1111
"""Create a checkout of a branch.
1113
:param to_location: The url to produce the checkout at
1114
:param revision_id: The revision to check out
1115
:param lightweight: If True, produce a lightweight checkout, otherwise,
1116
produce a bound branch (heavyweight checkout)
1117
:param accelerator_tree: A tree which can be used for retrieving file
1118
contents more quickly than the revision tree, i.e. a workingtree.
1119
The revision tree will be used for cases where accelerator_tree's
1120
content is different.
1121
:param hardlink: If true, hard-link files from accelerator_tree,
1123
:return: The tree of the created checkout
1125
t = transport.get_transport(to_location)
1128
format = self._get_checkout_format()
1129
checkout = format.initialize_on_transport(t)
1130
from_branch = BranchReferenceFormat().initialize(checkout, self)
1132
format = self._get_checkout_format()
1133
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1134
to_location, force_new_tree=False, format=format)
1135
checkout = checkout_branch.bzrdir
1136
checkout_branch.bind(self)
1137
# pull up to the specified revision_id to set the initial
1138
# branch tip correctly, and seed it with history.
1139
checkout_branch.pull(self, stop_revision=revision_id)
1141
tree = checkout.create_workingtree(revision_id,
1142
from_branch=from_branch,
1143
accelerator_tree=accelerator_tree,
1145
basis_tree = tree.basis_tree()
1146
basis_tree.lock_read()
1148
for path, file_id in basis_tree.iter_references():
1149
reference_parent = self.reference_parent(file_id, path)
1150
reference_parent.create_checkout(tree.abspath(path),
1151
basis_tree.get_reference_revision(file_id, path),
1158
def reconcile(self, thorough=True):
1159
"""Make sure the data stored in this branch is consistent."""
1160
from bzrlib.reconcile import BranchReconciler
1161
reconciler = BranchReconciler(self, thorough=thorough)
1162
reconciler.reconcile()
1165
def reference_parent(self, file_id, path):
1166
"""Return the parent branch for a tree-reference file_id
1167
:param file_id: The file_id of the tree reference
1168
:param path: The path of the file_id in the tree
1169
:return: A branch associated with the file_id
1171
# FIXME should provide multiple branches, based on config
1172
return Branch.open(self.bzrdir.root_transport.clone(path).base)
1174
def supports_tags(self):
1175
return self._format.supports_tags()
1177
def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1179
"""Ensure that revision_b is a descendant of revision_a.
1181
This is a helper function for update_revisions.
1183
:raises: DivergedBranches if revision_b has diverged from revision_a.
1184
:returns: True if revision_b is a descendant of revision_a.
1186
relation = self._revision_relations(revision_a, revision_b, graph)
1187
if relation == 'b_descends_from_a':
1189
elif relation == 'diverged':
1190
raise errors.DivergedBranches(self, other_branch)
1191
elif relation == 'a_descends_from_b':
1194
raise AssertionError("invalid relation: %r" % (relation,))
1196
def _revision_relations(self, revision_a, revision_b, graph):
1197
"""Determine the relationship between two revisions.
1199
:returns: One of: 'a_descends_from_b', 'b_descends_from_a', 'diverged'
1201
heads = graph.heads([revision_a, revision_b])
1202
if heads == set([revision_b]):
1203
return 'b_descends_from_a'
1204
elif heads == set([revision_a, revision_b]):
1205
# These branches have diverged
1207
elif heads == set([revision_a]):
1208
return 'a_descends_from_b'
1210
raise AssertionError("invalid heads: %r" % (heads,))
1213
class BranchFormat(object):
1214
"""An encapsulation of the initialization and open routines for a format.
1216
Formats provide three things:
1217
* An initialization routine,
1221
Formats are placed in an dict by their format string for reference
1222
during branch opening. Its not required that these be instances, they
1223
can be classes themselves with class methods - it simply depends on
1224
whether state is needed for a given format or not.
1226
Once a format is deprecated, just deprecate the initialize and open
1227
methods on the format class. Do not deprecate the object, as the
1228
object will be created every time regardless.
1231
_default_format = None
1232
"""The default format used for new branches."""
1235
"""The known formats."""
1237
def __eq__(self, other):
1238
return self.__class__ is other.__class__
1240
def __ne__(self, other):
1241
return not (self == other)
1244
def find_format(klass, a_bzrdir):
1245
"""Return the format for the branch object in a_bzrdir."""
1247
transport = a_bzrdir.get_branch_transport(None)
1248
format_string = transport.get("format").read()
1249
return klass._formats[format_string]
1250
except errors.NoSuchFile:
1251
raise errors.NotBranchError(path=transport.base)
1253
raise errors.UnknownFormatError(format=format_string, kind='branch')
1256
def get_default_format(klass):
1257
"""Return the current default format."""
1258
return klass._default_format
1260
def get_reference(self, a_bzrdir):
1261
"""Get the target reference of the branch in a_bzrdir.
1263
format probing must have been completed before calling
1264
this method - it is assumed that the format of the branch
1265
in a_bzrdir is correct.
1267
:param a_bzrdir: The bzrdir to get the branch data from.
1268
:return: None if the branch is not a reference branch.
1273
def set_reference(self, a_bzrdir, to_branch):
1274
"""Set the target reference of the branch in a_bzrdir.
1276
format probing must have been completed before calling
1277
this method - it is assumed that the format of the branch
1278
in a_bzrdir is correct.
1280
:param a_bzrdir: The bzrdir to set the branch reference for.
1281
:param to_branch: branch that the checkout is to reference
1283
raise NotImplementedError(self.set_reference)
1285
def get_format_string(self):
1286
"""Return the ASCII format string that identifies this format."""
1287
raise NotImplementedError(self.get_format_string)
1289
def get_format_description(self):
1290
"""Return the short format description for this format."""
1291
raise NotImplementedError(self.get_format_description)
1293
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
1295
"""Initialize a branch in a bzrdir, with specified files
1297
:param a_bzrdir: The bzrdir to initialize the branch in
1298
:param utf8_files: The files to create as a list of
1299
(filename, content) tuples
1300
:param set_format: If True, set the format with
1301
self.get_format_string. (BzrBranch4 has its format set
1303
:return: a branch in this format
1305
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1306
branch_transport = a_bzrdir.get_branch_transport(self)
1308
'metadir': ('lock', lockdir.LockDir),
1309
'branch4': ('branch-lock', lockable_files.TransportLock),
1311
lock_name, lock_class = lock_map[lock_type]
1312
control_files = lockable_files.LockableFiles(branch_transport,
1313
lock_name, lock_class)
1314
control_files.create_lock()
1315
control_files.lock_write()
1317
utf8_files += [('format', self.get_format_string())]
1319
for (filename, content) in utf8_files:
1320
branch_transport.put_bytes(
1322
mode=a_bzrdir._get_file_mode())
1324
control_files.unlock()
1325
return self.open(a_bzrdir, _found=True)
1327
def initialize(self, a_bzrdir):
1328
"""Create a branch of this format in a_bzrdir."""
1329
raise NotImplementedError(self.initialize)
1331
def is_supported(self):
1332
"""Is this format supported?
1334
Supported formats can be initialized and opened.
1335
Unsupported formats may not support initialization or committing or
1336
some other features depending on the reason for not being supported.
1340
def make_tags(self, branch):
1341
"""Create a tags object for branch.
1343
This method is on BranchFormat, because BranchFormats are reflected
1344
over the wire via network_name(), whereas full Branch instances require
1345
multiple VFS method calls to operate at all.
1347
The default implementation returns a disabled-tags instance.
1349
Note that it is normal for branch to be a RemoteBranch when using tags
1352
return DisabledTags(branch)
1354
def network_name(self):
1355
"""A simple byte string uniquely identifying this format for RPC calls.
1357
MetaDir branch formats use their disk format string to identify the
1358
repository over the wire. All in one formats such as bzr < 0.8, and
1359
foreign formats like svn/git and hg should use some marker which is
1360
unique and immutable.
1362
raise NotImplementedError(self.network_name)
1364
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1365
"""Return the branch object for a_bzrdir
1367
:param a_bzrdir: A BzrDir that contains a branch.
1368
:param _found: a private parameter, do not use it. It is used to
1369
indicate if format probing has already be done.
1370
:param ignore_fallbacks: when set, no fallback branches will be opened
1371
(if there are any). Default is to open fallbacks.
1373
raise NotImplementedError(self.open)
1376
def register_format(klass, format):
1377
"""Register a metadir format."""
1378
klass._formats[format.get_format_string()] = format
1379
# Metadir formats have a network name of their format string, and get
1380
# registered as class factories.
1381
network_format_registry.register(format.get_format_string(), format.__class__)
1384
def set_default_format(klass, format):
1385
klass._default_format = format
1387
def supports_stacking(self):
1388
"""True if this format records a stacked-on branch."""
1392
def unregister_format(klass, format):
1393
del klass._formats[format.get_format_string()]
1396
return self.get_format_description().rstrip()
1398
def supports_tags(self):
1399
"""True if this format supports tags stored in the branch"""
1400
return False # by default
1403
class BranchHooks(Hooks):
1404
"""A dictionary mapping hook name to a list of callables for branch hooks.
1406
e.g. ['set_rh'] Is the list of items to be called when the
1407
set_revision_history function is invoked.
1411
"""Create the default hooks.
1413
These are all empty initially, because by default nothing should get
1416
Hooks.__init__(self)
1417
self.create_hook(HookPoint('set_rh',
1418
"Invoked whenever the revision history has been set via "
1419
"set_revision_history. The api signature is (branch, "
1420
"revision_history), and the branch will be write-locked. "
1421
"The set_rh hook can be expensive for bzr to trigger, a better "
1422
"hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1423
self.create_hook(HookPoint('open',
1424
"Called with the Branch object that has been opened after a "
1425
"branch is opened.", (1, 8), None))
1426
self.create_hook(HookPoint('post_push',
1427
"Called after a push operation completes. post_push is called "
1428
"with a bzrlib.branch.BranchPushResult object and only runs in the "
1429
"bzr client.", (0, 15), None))
1430
self.create_hook(HookPoint('post_pull',
1431
"Called after a pull operation completes. post_pull is called "
1432
"with a bzrlib.branch.PullResult object and only runs in the "
1433
"bzr client.", (0, 15), None))
1434
self.create_hook(HookPoint('pre_commit',
1435
"Called after a commit is calculated but before it is is "
1436
"completed. pre_commit is called with (local, master, old_revno, "
1437
"old_revid, future_revno, future_revid, tree_delta, future_tree"
1438
"). old_revid is NULL_REVISION for the first commit to a branch, "
1439
"tree_delta is a TreeDelta object describing changes from the "
1440
"basis revision. hooks MUST NOT modify this delta. "
1441
" future_tree is an in-memory tree obtained from "
1442
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1443
"tree.", (0,91), None))
1444
self.create_hook(HookPoint('post_commit',
1445
"Called in the bzr client after a commit has completed. "
1446
"post_commit is called with (local, master, old_revno, old_revid, "
1447
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1448
"commit to a branch.", (0, 15), None))
1449
self.create_hook(HookPoint('post_uncommit',
1450
"Called in the bzr client after an uncommit completes. "
1451
"post_uncommit is called with (local, master, old_revno, "
1452
"old_revid, new_revno, new_revid) where local is the local branch "
1453
"or None, master is the target branch, and an empty branch "
1454
"recieves new_revno of 0, new_revid of None.", (0, 15), None))
1455
self.create_hook(HookPoint('pre_change_branch_tip',
1456
"Called in bzr client and server before a change to the tip of a "
1457
"branch is made. pre_change_branch_tip is called with a "
1458
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1459
"commit, uncommit will all trigger this hook.", (1, 6), None))
1460
self.create_hook(HookPoint('post_change_branch_tip',
1461
"Called in bzr client and server after a change to the tip of a "
1462
"branch is made. post_change_branch_tip is called with a "
1463
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1464
"commit, uncommit will all trigger this hook.", (1, 4), None))
1465
self.create_hook(HookPoint('transform_fallback_location',
1466
"Called when a stacked branch is activating its fallback "
1467
"locations. transform_fallback_location is called with (branch, "
1468
"url), and should return a new url. Returning the same url "
1469
"allows it to be used as-is, returning a different one can be "
1470
"used to cause the branch to stack on a closer copy of that "
1471
"fallback_location. Note that the branch cannot have history "
1472
"accessing methods called on it during this hook because the "
1473
"fallback locations have not been activated. When there are "
1474
"multiple hooks installed for transform_fallback_location, "
1475
"all are called with the url returned from the previous hook."
1476
"The order is however undefined.", (1, 9), None))
1479
# install the default hooks into the Branch class.
1480
Branch.hooks = BranchHooks()
1483
class ChangeBranchTipParams(object):
1484
"""Object holding parameters passed to *_change_branch_tip hooks.
1486
There are 5 fields that hooks may wish to access:
1488
:ivar branch: the branch being changed
1489
:ivar old_revno: revision number before the change
1490
:ivar new_revno: revision number after the change
1491
:ivar old_revid: revision id before the change
1492
:ivar new_revid: revision id after the change
1494
The revid fields are strings. The revno fields are integers.
1497
def __init__(self, branch, old_revno, new_revno, old_revid, new_revid):
1498
"""Create a group of ChangeBranchTip parameters.
1500
:param branch: The branch being changed.
1501
:param old_revno: Revision number before the change.
1502
:param new_revno: Revision number after the change.
1503
:param old_revid: Tip revision id before the change.
1504
:param new_revid: Tip revision id after the change.
1506
self.branch = branch
1507
self.old_revno = old_revno
1508
self.new_revno = new_revno
1509
self.old_revid = old_revid
1510
self.new_revid = new_revid
1512
def __eq__(self, other):
1513
return self.__dict__ == other.__dict__
1516
return "<%s of %s from (%s, %s) to (%s, %s)>" % (
1517
self.__class__.__name__, self.branch,
1518
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1521
class BzrBranchFormat4(BranchFormat):
1522
"""Bzr branch format 4.
1525
- a revision-history file.
1526
- a branch-lock lock file [ to be shared with the bzrdir ]
1529
def get_format_description(self):
1530
"""See BranchFormat.get_format_description()."""
1531
return "Branch format 4"
1533
def initialize(self, a_bzrdir):
1534
"""Create a branch of this format in a_bzrdir."""
1535
utf8_files = [('revision-history', ''),
1536
('branch-name', ''),
1538
return self._initialize_helper(a_bzrdir, utf8_files,
1539
lock_type='branch4', set_format=False)
1542
super(BzrBranchFormat4, self).__init__()
1543
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1545
def network_name(self):
1546
"""The network name for this format is the control dirs disk label."""
1547
return self._matchingbzrdir.get_format_string()
1549
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1550
"""See BranchFormat.open()."""
1552
# we are being called directly and must probe.
1553
raise NotImplementedError
1554
return BzrBranch(_format=self,
1555
_control_files=a_bzrdir._control_files,
1557
_repository=a_bzrdir.open_repository())
1560
return "Bazaar-NG branch format 4"
1563
class BranchFormatMetadir(BranchFormat):
1564
"""Common logic for meta-dir based branch formats."""
1566
def _branch_class(self):
1567
"""What class to instantiate on open calls."""
1568
raise NotImplementedError(self._branch_class)
1570
def network_name(self):
1571
"""A simple byte string uniquely identifying this format for RPC calls.
1573
Metadir branch formats use their format string.
1575
return self.get_format_string()
1577
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1578
"""See BranchFormat.open()."""
1580
format = BranchFormat.find_format(a_bzrdir)
1581
if format.__class__ != self.__class__:
1582
raise AssertionError("wrong format %r found for %r" %
1585
transport = a_bzrdir.get_branch_transport(None)
1586
control_files = lockable_files.LockableFiles(transport, 'lock',
1588
return self._branch_class()(_format=self,
1589
_control_files=control_files,
1591
_repository=a_bzrdir.find_repository(),
1592
ignore_fallbacks=ignore_fallbacks)
1593
except errors.NoSuchFile:
1594
raise errors.NotBranchError(path=transport.base)
1597
super(BranchFormatMetadir, self).__init__()
1598
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1599
self._matchingbzrdir.set_branch_format(self)
1601
def supports_tags(self):
1605
class BzrBranchFormat5(BranchFormatMetadir):
1606
"""Bzr branch format 5.
1609
- a revision-history file.
1611
- a lock dir guarding the branch itself
1612
- all of this stored in a branch/ subdirectory
1613
- works with shared repositories.
1615
This format is new in bzr 0.8.
1618
def _branch_class(self):
1621
def get_format_string(self):
1622
"""See BranchFormat.get_format_string()."""
1623
return "Bazaar-NG branch format 5\n"
1625
def get_format_description(self):
1626
"""See BranchFormat.get_format_description()."""
1627
return "Branch format 5"
1629
def initialize(self, a_bzrdir):
1630
"""Create a branch of this format in a_bzrdir."""
1631
utf8_files = [('revision-history', ''),
1632
('branch-name', ''),
1634
return self._initialize_helper(a_bzrdir, utf8_files)
1636
def supports_tags(self):
1640
class BzrBranchFormat6(BranchFormatMetadir):
1641
"""Branch format with last-revision and tags.
1643
Unlike previous formats, this has no explicit revision history. Instead,
1644
this just stores the last-revision, and the left-hand history leading
1645
up to there is the history.
1647
This format was introduced in bzr 0.15
1648
and became the default in 0.91.
1651
def _branch_class(self):
1654
def get_format_string(self):
1655
"""See BranchFormat.get_format_string()."""
1656
return "Bazaar Branch Format 6 (bzr 0.15)\n"
1658
def get_format_description(self):
1659
"""See BranchFormat.get_format_description()."""
1660
return "Branch format 6"
1662
def initialize(self, a_bzrdir):
1663
"""Create a branch of this format in a_bzrdir."""
1664
utf8_files = [('last-revision', '0 null:\n'),
1665
('branch.conf', ''),
1668
return self._initialize_helper(a_bzrdir, utf8_files)
1670
def make_tags(self, branch):
1671
"""See bzrlib.branch.BranchFormat.make_tags()."""
1672
return BasicTags(branch)
1676
class BzrBranchFormat7(BranchFormatMetadir):
1677
"""Branch format with last-revision, tags, and a stacked location pointer.
1679
The stacked location pointer is passed down to the repository and requires
1680
a repository format with supports_external_lookups = True.
1682
This format was introduced in bzr 1.6.
1685
def _branch_class(self):
1688
def get_format_string(self):
1689
"""See BranchFormat.get_format_string()."""
1690
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
1692
def get_format_description(self):
1693
"""See BranchFormat.get_format_description()."""
1694
return "Branch format 7"
1696
def initialize(self, a_bzrdir):
1697
"""Create a branch of this format in a_bzrdir."""
1698
utf8_files = [('last-revision', '0 null:\n'),
1699
('branch.conf', ''),
1702
return self._initialize_helper(a_bzrdir, utf8_files)
1705
super(BzrBranchFormat7, self).__init__()
1706
self._matchingbzrdir.repository_format = \
1707
RepositoryFormatKnitPack5RichRoot()
1709
def make_tags(self, branch):
1710
"""See bzrlib.branch.BranchFormat.make_tags()."""
1711
return BasicTags(branch)
1713
def supports_stacking(self):
1717
class BranchReferenceFormat(BranchFormat):
1718
"""Bzr branch reference format.
1720
Branch references are used in implementing checkouts, they
1721
act as an alias to the real branch which is at some other url.
1728
def get_format_string(self):
1729
"""See BranchFormat.get_format_string()."""
1730
return "Bazaar-NG Branch Reference Format 1\n"
1732
def get_format_description(self):
1733
"""See BranchFormat.get_format_description()."""
1734
return "Checkout reference format 1"
1736
def get_reference(self, a_bzrdir):
1737
"""See BranchFormat.get_reference()."""
1738
transport = a_bzrdir.get_branch_transport(None)
1739
return transport.get('location').read()
1741
def set_reference(self, a_bzrdir, to_branch):
1742
"""See BranchFormat.set_reference()."""
1743
transport = a_bzrdir.get_branch_transport(None)
1744
location = transport.put_bytes('location', to_branch.base)
1746
def initialize(self, a_bzrdir, target_branch=None):
1747
"""Create a branch of this format in a_bzrdir."""
1748
if target_branch is None:
1749
# this format does not implement branch itself, thus the implicit
1750
# creation contract must see it as uninitializable
1751
raise errors.UninitializableFormat(self)
1752
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1753
branch_transport = a_bzrdir.get_branch_transport(self)
1754
branch_transport.put_bytes('location',
1755
target_branch.bzrdir.root_transport.base)
1756
branch_transport.put_bytes('format', self.get_format_string())
1758
a_bzrdir, _found=True,
1759
possible_transports=[target_branch.bzrdir.root_transport])
1762
super(BranchReferenceFormat, self).__init__()
1763
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1764
self._matchingbzrdir.set_branch_format(self)
1766
def _make_reference_clone_function(format, a_branch):
1767
"""Create a clone() routine for a branch dynamically."""
1768
def clone(to_bzrdir, revision_id=None,
1769
repository_policy=None):
1770
"""See Branch.clone()."""
1771
return format.initialize(to_bzrdir, a_branch)
1772
# cannot obey revision_id limits when cloning a reference ...
1773
# FIXME RBC 20060210 either nuke revision_id for clone, or
1774
# emit some sort of warning/error to the caller ?!
1777
def open(self, a_bzrdir, _found=False, location=None,
1778
possible_transports=None, ignore_fallbacks=False):
1779
"""Return the branch that the branch reference in a_bzrdir points at.
1781
:param a_bzrdir: A BzrDir that contains a branch.
1782
:param _found: a private parameter, do not use it. It is used to
1783
indicate if format probing has already be done.
1784
:param ignore_fallbacks: when set, no fallback branches will be opened
1785
(if there are any). Default is to open fallbacks.
1786
:param location: The location of the referenced branch. If
1787
unspecified, this will be determined from the branch reference in
1789
:param possible_transports: An optional reusable transports list.
1792
format = BranchFormat.find_format(a_bzrdir)
1793
if format.__class__ != self.__class__:
1794
raise AssertionError("wrong format %r found for %r" %
1796
if location is None:
1797
location = self.get_reference(a_bzrdir)
1798
real_bzrdir = bzrdir.BzrDir.open(
1799
location, possible_transports=possible_transports)
1800
result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
1801
# this changes the behaviour of result.clone to create a new reference
1802
# rather than a copy of the content of the branch.
1803
# I did not use a proxy object because that needs much more extensive
1804
# testing, and we are only changing one behaviour at the moment.
1805
# If we decide to alter more behaviours - i.e. the implicit nickname
1806
# then this should be refactored to introduce a tested proxy branch
1807
# and a subclass of that for use in overriding clone() and ....
1809
result.clone = self._make_reference_clone_function(result)
1813
network_format_registry = registry.FormatRegistry()
1814
"""Registry of formats indexed by their network name.
1816
The network name for a branch format is an identifier that can be used when
1817
referring to formats with smart server operations. See
1818
BranchFormat.network_name() for more detail.
1822
# formats which have no format string are not discoverable
1823
# and not independently creatable, so are not registered.
1824
__format5 = BzrBranchFormat5()
1825
__format6 = BzrBranchFormat6()
1826
__format7 = BzrBranchFormat7()
1827
BranchFormat.register_format(__format5)
1828
BranchFormat.register_format(BranchReferenceFormat())
1829
BranchFormat.register_format(__format6)
1830
BranchFormat.register_format(__format7)
1831
BranchFormat.set_default_format(__format6)
1832
_legacy_formats = [BzrBranchFormat4(),
1834
network_format_registry.register(
1835
_legacy_formats[0].network_name(), _legacy_formats[0].__class__)
1838
class BzrBranch(Branch):
1839
"""A branch stored in the actual filesystem.
1841
Note that it's "local" in the context of the filesystem; it doesn't
1842
really matter if it's on an nfs/smb/afs/coda/... share, as long as
1843
it's writable, and can be accessed via the normal filesystem API.
1845
:ivar _transport: Transport for file operations on this branch's
1846
control files, typically pointing to the .bzr/branch directory.
1847
:ivar repository: Repository for this branch.
1848
:ivar base: The url of the base directory for this branch; the one
1849
containing the .bzr directory.
1852
def __init__(self, _format=None,
1853
_control_files=None, a_bzrdir=None, _repository=None,
1854
ignore_fallbacks=False):
1855
"""Create new branch object at a particular location."""
1856
if a_bzrdir is None:
1857
raise ValueError('a_bzrdir must be supplied')
1859
self.bzrdir = a_bzrdir
1860
self._base = self.bzrdir.transport.clone('..').base
1861
# XXX: We should be able to just do
1862
# self.base = self.bzrdir.root_transport.base
1863
# but this does not quite work yet -- mbp 20080522
1864
self._format = _format
1865
if _control_files is None:
1866
raise ValueError('BzrBranch _control_files is None')
1867
self.control_files = _control_files
1868
self._transport = _control_files._transport
1869
self.repository = _repository
1870
Branch.__init__(self)
1873
return '%s(%r)' % (self.__class__.__name__, self.base)
1877
def _get_base(self):
1878
"""Returns the directory containing the control directory."""
1881
base = property(_get_base, doc="The URL for the root of this branch.")
1883
def is_locked(self):
1884
return self.control_files.is_locked()
1886
def lock_write(self, token=None):
1887
repo_token = self.repository.lock_write()
1889
token = self.control_files.lock_write(token=token)
1891
self.repository.unlock()
1895
def lock_read(self):
1896
self.repository.lock_read()
1898
self.control_files.lock_read()
1900
self.repository.unlock()
1904
# TODO: test for failed two phase locks. This is known broken.
1906
self.control_files.unlock()
1908
self.repository.unlock()
1909
if not self.control_files.is_locked():
1910
# we just released the lock
1911
self._clear_cached_state()
1913
def peek_lock_mode(self):
1914
if self.control_files._lock_count == 0:
1917
return self.control_files._lock_mode
1919
def get_physical_lock_status(self):
1920
return self.control_files.get_physical_lock_status()
1923
def print_file(self, file, revision_id):
1924
"""See Branch.print_file."""
1925
return self.repository.print_file(file, revision_id)
1927
def _write_revision_history(self, history):
1928
"""Factored out of set_revision_history.
1930
This performs the actual writing to disk.
1931
It is intended to be called by BzrBranch5.set_revision_history."""
1932
self._transport.put_bytes(
1933
'revision-history', '\n'.join(history),
1934
mode=self.bzrdir._get_file_mode())
1937
def set_revision_history(self, rev_history):
1938
"""See Branch.set_revision_history."""
1939
if 'evil' in debug.debug_flags:
1940
mutter_callsite(3, "set_revision_history scales with history.")
1941
check_not_reserved_id = _mod_revision.check_not_reserved_id
1942
for rev_id in rev_history:
1943
check_not_reserved_id(rev_id)
1944
if Branch.hooks['post_change_branch_tip']:
1945
# Don't calculate the last_revision_info() if there are no hooks
1947
old_revno, old_revid = self.last_revision_info()
1948
if len(rev_history) == 0:
1949
revid = _mod_revision.NULL_REVISION
1951
revid = rev_history[-1]
1952
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
1953
self._write_revision_history(rev_history)
1954
self._clear_cached_state()
1955
self._cache_revision_history(rev_history)
1956
for hook in Branch.hooks['set_rh']:
1957
hook(self, rev_history)
1958
if Branch.hooks['post_change_branch_tip']:
1959
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1961
def _synchronize_history(self, destination, revision_id):
1962
"""Synchronize last revision and revision history between branches.
1964
This version is most efficient when the destination is also a
1965
BzrBranch5, but works for BzrBranch6 as long as the revision
1966
history is the true lefthand parent history, and all of the revisions
1967
are in the destination's repository. If not, set_revision_history
1970
:param destination: The branch to copy the history into
1971
:param revision_id: The revision-id to truncate history at. May
1972
be None to copy complete history.
1974
if not isinstance(destination._format, BzrBranchFormat5):
1975
super(BzrBranch, self)._synchronize_history(
1976
destination, revision_id)
1978
if revision_id == _mod_revision.NULL_REVISION:
1981
new_history = self.revision_history()
1982
if revision_id is not None and new_history != []:
1984
new_history = new_history[:new_history.index(revision_id) + 1]
1986
rev = self.repository.get_revision(revision_id)
1987
new_history = rev.get_history(self.repository)[1:]
1988
destination.set_revision_history(new_history)
1991
def set_last_revision_info(self, revno, revision_id):
1992
"""Set the last revision of this branch.
1994
The caller is responsible for checking that the revno is correct
1995
for this revision id.
1997
It may be possible to set the branch last revision to an id not
1998
present in the repository. However, branches can also be
1999
configured to check constraints on history, in which case this may not
2002
revision_id = _mod_revision.ensure_null(revision_id)
2003
# this old format stores the full history, but this api doesn't
2004
# provide it, so we must generate, and might as well check it's
2006
history = self._lefthand_history(revision_id)
2007
if len(history) != revno:
2008
raise AssertionError('%d != %d' % (len(history), revno))
2009
self.set_revision_history(history)
2011
def _gen_revision_history(self):
2012
history = self._transport.get_bytes('revision-history').split('\n')
2013
if history[-1:] == ['']:
2014
# There shouldn't be a trailing newline, but just in case.
2019
def generate_revision_history(self, revision_id, last_rev=None,
2021
"""Create a new revision history that will finish with revision_id.
2023
:param revision_id: the new tip to use.
2024
:param last_rev: The previous last_revision. If not None, then this
2025
must be a ancestory of revision_id, or DivergedBranches is raised.
2026
:param other_branch: The other branch that DivergedBranches should
2027
raise with respect to.
2029
self.set_revision_history(self._lefthand_history(revision_id,
2030
last_rev, other_branch))
2032
def basis_tree(self):
2033
"""See Branch.basis_tree."""
2034
return self.repository.revision_tree(self.last_revision())
2037
def pull(self, source, overwrite=False, stop_revision=None,
2038
_hook_master=None, run_hooks=True, possible_transports=None,
2039
_override_hook_target=None):
2042
:param _hook_master: Private parameter - set the branch to
2043
be supplied as the master to pull hooks.
2044
:param run_hooks: Private parameter - if false, this branch
2045
is being called because it's the master of the primary branch,
2046
so it should not run its hooks.
2047
:param _override_hook_target: Private parameter - set the branch to be
2048
supplied as the target_branch to pull hooks.
2050
result = PullResult()
2051
result.source_branch = source
2052
if _override_hook_target is None:
2053
result.target_branch = self
2055
result.target_branch = _override_hook_target
2058
# We assume that during 'pull' the local repository is closer than
2060
graph = self.repository.get_graph(source.repository)
2061
result.old_revno, result.old_revid = self.last_revision_info()
2062
self.update_revisions(source, stop_revision, overwrite=overwrite,
2064
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
2065
result.new_revno, result.new_revid = self.last_revision_info()
2067
result.master_branch = _hook_master
2068
result.local_branch = result.target_branch
2070
result.master_branch = result.target_branch
2071
result.local_branch = None
2073
for hook in Branch.hooks['post_pull']:
2079
def _get_parent_location(self):
2080
_locs = ['parent', 'pull', 'x-pull']
2083
return self._transport.get_bytes(l).strip('\n')
2084
except errors.NoSuchFile:
2089
def push(self, target, overwrite=False, stop_revision=None,
2090
_override_hook_source_branch=None):
2093
This is the basic concrete implementation of push()
2095
:param _override_hook_source_branch: If specified, run
2096
the hooks passing this Branch as the source, rather than self.
2097
This is for use of RemoteBranch, where push is delegated to the
2098
underlying vfs-based Branch.
2100
# TODO: Public option to disable running hooks - should be trivial but
2102
return _run_with_write_locked_target(
2103
target, self._push_with_bound_branches, target, overwrite,
2105
_override_hook_source_branch=_override_hook_source_branch)
2107
def _push_with_bound_branches(self, target, overwrite,
2109
_override_hook_source_branch=None):
2110
"""Push from self into target, and into target's master if any.
2112
This is on the base BzrBranch class even though it doesn't support
2113
bound branches because the *target* might be bound.
2116
if _override_hook_source_branch:
2117
result.source_branch = _override_hook_source_branch
2118
for hook in Branch.hooks['post_push']:
2121
bound_location = target.get_bound_location()
2122
if bound_location and target.base != bound_location:
2123
# there is a master branch.
2125
# XXX: Why the second check? Is it even supported for a branch to
2126
# be bound to itself? -- mbp 20070507
2127
master_branch = target.get_master_branch()
2128
master_branch.lock_write()
2130
# push into the master from this branch.
2131
self._basic_push(master_branch, overwrite, stop_revision)
2132
# and push into the target branch from this. Note that we push from
2133
# this branch again, because its considered the highest bandwidth
2135
result = self._basic_push(target, overwrite, stop_revision)
2136
result.master_branch = master_branch
2137
result.local_branch = target
2141
master_branch.unlock()
2144
result = self._basic_push(target, overwrite, stop_revision)
2145
# TODO: Why set master_branch and local_branch if there's no
2146
# binding? Maybe cleaner to just leave them unset? -- mbp
2148
result.master_branch = target
2149
result.local_branch = None
2153
def _basic_push(self, target, overwrite, stop_revision):
2154
"""Basic implementation of push without bound branches or hooks.
2156
Must be called with self read locked and target write locked.
2158
result = BranchPushResult()
2159
result.source_branch = self
2160
result.target_branch = target
2161
result.old_revno, result.old_revid = target.last_revision_info()
2162
if result.old_revid != self.last_revision():
2163
# We assume that during 'push' this repository is closer than
2165
graph = self.repository.get_graph(target.repository)
2166
target.update_revisions(self, stop_revision, overwrite=overwrite,
2168
if self._push_should_merge_tags():
2169
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
2170
result.new_revno, result.new_revid = target.last_revision_info()
2173
def get_stacked_on_url(self):
2174
raise errors.UnstackableBranchFormat(self._format, self.base)
2176
def set_push_location(self, location):
2177
"""See Branch.set_push_location."""
2178
self.get_config().set_user_option(
2179
'push_location', location,
2180
store=_mod_config.STORE_LOCATION_NORECURSE)
2183
def set_parent(self, url):
2184
"""See Branch.set_parent."""
2185
# TODO: Maybe delete old location files?
2186
# URLs should never be unicode, even on the local fs,
2187
# FIXUP this and get_parent in a future branch format bump:
2188
# read and rewrite the file. RBC 20060125
2190
if isinstance(url, unicode):
2192
url = url.encode('ascii')
2193
except UnicodeEncodeError:
2194
raise errors.InvalidURL(url,
2195
"Urls must be 7-bit ascii, "
2196
"use bzrlib.urlutils.escape")
2197
url = urlutils.relative_url(self.base, url)
2198
self._set_parent_location(url)
2200
def _set_parent_location(self, url):
2202
self._transport.delete('parent')
2204
self._transport.put_bytes('parent', url + '\n',
2205
mode=self.bzrdir._get_file_mode())
2208
class BzrBranch5(BzrBranch):
2209
"""A format 5 branch. This supports new features over plain branches.
2211
It has support for a master_branch which is the data for bound branches.
2215
def pull(self, source, overwrite=False, stop_revision=None,
2216
run_hooks=True, possible_transports=None,
2217
_override_hook_target=None):
2218
"""Pull from source into self, updating my master if any.
2220
:param run_hooks: Private parameter - if false, this branch
2221
is being called because it's the master of the primary branch,
2222
so it should not run its hooks.
2224
bound_location = self.get_bound_location()
2225
master_branch = None
2226
if bound_location and source.base != bound_location:
2227
# not pulling from master, so we need to update master.
2228
master_branch = self.get_master_branch(possible_transports)
2229
master_branch.lock_write()
2232
# pull from source into master.
2233
master_branch.pull(source, overwrite, stop_revision,
2235
return super(BzrBranch5, self).pull(source, overwrite,
2236
stop_revision, _hook_master=master_branch,
2237
run_hooks=run_hooks,
2238
_override_hook_target=_override_hook_target)
2241
master_branch.unlock()
2243
def get_bound_location(self):
2245
return self._transport.get_bytes('bound')[:-1]
2246
except errors.NoSuchFile:
2250
def get_master_branch(self, possible_transports=None):
2251
"""Return the branch we are bound to.
2253
:return: Either a Branch, or None
2255
This could memoise the branch, but if thats done
2256
it must be revalidated on each new lock.
2257
So for now we just don't memoise it.
2258
# RBC 20060304 review this decision.
2260
bound_loc = self.get_bound_location()
2264
return Branch.open(bound_loc,
2265
possible_transports=possible_transports)
2266
except (errors.NotBranchError, errors.ConnectionError), e:
2267
raise errors.BoundBranchConnectionFailure(
2271
def set_bound_location(self, location):
2272
"""Set the target where this branch is bound to.
2274
:param location: URL to the target branch
2277
self._transport.put_bytes('bound', location+'\n',
2278
mode=self.bzrdir._get_file_mode())
2281
self._transport.delete('bound')
2282
except errors.NoSuchFile:
2287
def bind(self, other):
2288
"""Bind this branch to the branch other.
2290
This does not push or pull data between the branches, though it does
2291
check for divergence to raise an error when the branches are not
2292
either the same, or one a prefix of the other. That behaviour may not
2293
be useful, so that check may be removed in future.
2295
:param other: The branch to bind to
2298
# TODO: jam 20051230 Consider checking if the target is bound
2299
# It is debatable whether you should be able to bind to
2300
# a branch which is itself bound.
2301
# Committing is obviously forbidden,
2302
# but binding itself may not be.
2303
# Since we *have* to check at commit time, we don't
2304
# *need* to check here
2306
# we want to raise diverged if:
2307
# last_rev is not in the other_last_rev history, AND
2308
# other_last_rev is not in our history, and do it without pulling
2310
self.set_bound_location(other.base)
2314
"""If bound, unbind"""
2315
return self.set_bound_location(None)
2318
def update(self, possible_transports=None):
2319
"""Synchronise this branch with the master branch if any.
2321
:return: None or the last_revision that was pivoted out during the
2324
master = self.get_master_branch(possible_transports)
2325
if master is not None:
2326
old_tip = _mod_revision.ensure_null(self.last_revision())
2327
self.pull(master, overwrite=True)
2328
if self.repository.get_graph().is_ancestor(old_tip,
2329
_mod_revision.ensure_null(self.last_revision())):
2335
class BzrBranch7(BzrBranch5):
2336
"""A branch with support for a fallback repository."""
2338
def _open_hook(self):
2339
if self._ignore_fallbacks:
2342
url = self.get_stacked_on_url()
2343
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2344
errors.UnstackableBranchFormat):
2347
for hook in Branch.hooks['transform_fallback_location']:
2348
url = hook(self, url)
2350
hook_name = Branch.hooks.get_hook_name(hook)
2351
raise AssertionError(
2352
"'transform_fallback_location' hook %s returned "
2353
"None, not a URL." % hook_name)
2354
self._activate_fallback_location(url)
2356
def __init__(self, *args, **kwargs):
2357
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2358
super(BzrBranch7, self).__init__(*args, **kwargs)
2359
self._last_revision_info_cache = None
2360
self._partial_revision_history_cache = []
2362
def _clear_cached_state(self):
2363
super(BzrBranch7, self)._clear_cached_state()
2364
self._last_revision_info_cache = None
2365
self._partial_revision_history_cache = []
2367
def _last_revision_info(self):
2368
revision_string = self._transport.get_bytes('last-revision')
2369
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2370
revision_id = cache_utf8.get_cached_utf8(revision_id)
2372
return revno, revision_id
2374
def _write_last_revision_info(self, revno, revision_id):
2375
"""Simply write out the revision id, with no checks.
2377
Use set_last_revision_info to perform this safely.
2379
Does not update the revision_history cache.
2380
Intended to be called by set_last_revision_info and
2381
_write_revision_history.
2383
revision_id = _mod_revision.ensure_null(revision_id)
2384
out_string = '%d %s\n' % (revno, revision_id)
2385
self._transport.put_bytes('last-revision', out_string,
2386
mode=self.bzrdir._get_file_mode())
2389
def set_last_revision_info(self, revno, revision_id):
2390
revision_id = _mod_revision.ensure_null(revision_id)
2391
old_revno, old_revid = self.last_revision_info()
2392
if self._get_append_revisions_only():
2393
self._check_history_violation(revision_id)
2394
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2395
self._write_last_revision_info(revno, revision_id)
2396
self._clear_cached_state()
2397
self._last_revision_info_cache = revno, revision_id
2398
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2400
def _synchronize_history(self, destination, revision_id):
2401
"""Synchronize last revision and revision history between branches.
2403
:see: Branch._synchronize_history
2405
# XXX: The base Branch has a fast implementation of this method based
2406
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2407
# that uses set_revision_history. This class inherits from BzrBranch5,
2408
# but wants the fast implementation, so it calls
2409
# Branch._synchronize_history directly.
2410
Branch._synchronize_history(self, destination, revision_id)
2412
def _check_history_violation(self, revision_id):
2413
last_revision = _mod_revision.ensure_null(self.last_revision())
2414
if _mod_revision.is_null(last_revision):
2416
if last_revision not in self._lefthand_history(revision_id):
2417
raise errors.AppendRevisionsOnlyViolation(self.base)
2419
def _gen_revision_history(self):
2420
"""Generate the revision history from last revision
2422
last_revno, last_revision = self.last_revision_info()
2423
self._extend_partial_history(stop_index=last_revno-1)
2424
return list(reversed(self._partial_revision_history_cache))
2426
def _extend_partial_history(self, stop_index=None, stop_revision=None):
2427
"""Extend the partial history to include a given index
2429
If a stop_index is supplied, stop when that index has been reached.
2430
If a stop_revision is supplied, stop when that revision is
2431
encountered. Otherwise, stop when the beginning of history is
2434
:param stop_index: The index which should be present. When it is
2435
present, history extension will stop.
2436
:param revision_id: The revision id which should be present. When
2437
it is encountered, history extension will stop.
2439
repo = self.repository
2440
if len(self._partial_revision_history_cache) == 0:
2441
iterator = repo.iter_reverse_revision_history(self.last_revision())
2443
start_revision = self._partial_revision_history_cache[-1]
2444
iterator = repo.iter_reverse_revision_history(start_revision)
2445
#skip the last revision in the list
2446
next_revision = iterator.next()
2447
for revision_id in iterator:
2448
self._partial_revision_history_cache.append(revision_id)
2449
if (stop_index is not None and
2450
len(self._partial_revision_history_cache) > stop_index):
2452
if revision_id == stop_revision:
2455
def _write_revision_history(self, history):
2456
"""Factored out of set_revision_history.
2458
This performs the actual writing to disk, with format-specific checks.
2459
It is intended to be called by BzrBranch5.set_revision_history.
2461
if len(history) == 0:
2462
last_revision = 'null:'
2464
if history != self._lefthand_history(history[-1]):
2465
raise errors.NotLefthandHistory(history)
2466
last_revision = history[-1]
2467
if self._get_append_revisions_only():
2468
self._check_history_violation(last_revision)
2469
self._write_last_revision_info(len(history), last_revision)
2472
def _set_parent_location(self, url):
2473
"""Set the parent branch"""
2474
self._set_config_location('parent_location', url, make_relative=True)
2477
def _get_parent_location(self):
2478
"""Set the parent branch"""
2479
return self._get_config_location('parent_location')
2481
def set_push_location(self, location):
2482
"""See Branch.set_push_location."""
2483
self._set_config_location('push_location', location)
2485
def set_bound_location(self, location):
2486
"""See Branch.set_push_location."""
2488
config = self.get_config()
2489
if location is None:
2490
if config.get_user_option('bound') != 'True':
2493
config.set_user_option('bound', 'False', warn_masked=True)
2496
self._set_config_location('bound_location', location,
2498
config.set_user_option('bound', 'True', warn_masked=True)
2501
def _get_bound_location(self, bound):
2502
"""Return the bound location in the config file.
2504
Return None if the bound parameter does not match"""
2505
config = self.get_config()
2506
config_bound = (config.get_user_option('bound') == 'True')
2507
if config_bound != bound:
2509
return self._get_config_location('bound_location', config=config)
2511
def get_bound_location(self):
2512
"""See Branch.set_push_location."""
2513
return self._get_bound_location(True)
2515
def get_old_bound_location(self):
2516
"""See Branch.get_old_bound_location"""
2517
return self._get_bound_location(False)
2519
def get_stacked_on_url(self):
2520
# you can always ask for the URL; but you might not be able to use it
2521
# if the repo can't support stacking.
2522
## self._check_stackable_repo()
2523
stacked_url = self._get_config_location('stacked_on_location')
2524
if stacked_url is None:
2525
raise errors.NotStacked(self)
2528
def set_append_revisions_only(self, enabled):
2533
self.get_config().set_user_option('append_revisions_only', value,
2536
def _get_append_revisions_only(self):
2537
value = self.get_config().get_user_option('append_revisions_only')
2538
return value == 'True'
2541
def generate_revision_history(self, revision_id, last_rev=None,
2543
"""See BzrBranch5.generate_revision_history"""
2544
history = self._lefthand_history(revision_id, last_rev, other_branch)
2545
revno = len(history)
2546
self.set_last_revision_info(revno, revision_id)
2549
def get_rev_id(self, revno, history=None):
2550
"""Find the revision id of the specified revno."""
2552
return _mod_revision.NULL_REVISION
2554
last_revno, last_revision_id = self.last_revision_info()
2555
if revno <= 0 or revno > last_revno:
2556
raise errors.NoSuchRevision(self, revno)
2558
if history is not None:
2559
return history[revno - 1]
2561
index = last_revno - revno
2562
if len(self._partial_revision_history_cache) <= index:
2563
self._extend_partial_history(stop_index=index)
2564
if len(self._partial_revision_history_cache) > index:
2565
return self._partial_revision_history_cache[index]
2567
raise errors.NoSuchRevision(self, revno)
2570
def revision_id_to_revno(self, revision_id):
2571
"""Given a revision id, return its revno"""
2572
if _mod_revision.is_null(revision_id):
2575
index = self._partial_revision_history_cache.index(revision_id)
2577
self._extend_partial_history(stop_revision=revision_id)
2578
index = len(self._partial_revision_history_cache) - 1
2579
if self._partial_revision_history_cache[index] != revision_id:
2580
raise errors.NoSuchRevision(self, revision_id)
2581
return self.revno() - index
2584
class BzrBranch6(BzrBranch7):
2585
"""See BzrBranchFormat6 for the capabilities of this branch.
2587
This subclass of BzrBranch7 disables the new features BzrBranch7 added,
2591
def get_stacked_on_url(self):
2592
raise errors.UnstackableBranchFormat(self._format, self.base)
2595
######################################################################
2596
# results of operations
2599
class _Result(object):
2601
def _show_tag_conficts(self, to_file):
2602
if not getattr(self, 'tag_conflicts', None):
2604
to_file.write('Conflicting tags:\n')
2605
for name, value1, value2 in self.tag_conflicts:
2606
to_file.write(' %s\n' % (name, ))
2609
class PullResult(_Result):
2610
"""Result of a Branch.pull operation.
2612
:ivar old_revno: Revision number before pull.
2613
:ivar new_revno: Revision number after pull.
2614
:ivar old_revid: Tip revision id before pull.
2615
:ivar new_revid: Tip revision id after pull.
2616
:ivar source_branch: Source (local) branch object. (read locked)
2617
:ivar master_branch: Master branch of the target, or the target if no
2619
:ivar local_branch: target branch if there is a Master, else None
2620
:ivar target_branch: Target/destination branch object. (write locked)
2621
:ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
2625
# DEPRECATED: pull used to return the change in revno
2626
return self.new_revno - self.old_revno
2628
def report(self, to_file):
2630
if self.old_revid == self.new_revid:
2631
to_file.write('No revisions to pull.\n')
2633
to_file.write('Now on revision %d.\n' % self.new_revno)
2634
self._show_tag_conficts(to_file)
2637
class BranchPushResult(_Result):
2638
"""Result of a Branch.push operation.
2640
:ivar old_revno: Revision number (eg 10) of the target before push.
2641
:ivar new_revno: Revision number (eg 12) of the target after push.
2642
:ivar old_revid: Tip revision id (eg joe@foo.com-1234234-aoeua34) of target
2644
:ivar new_revid: Tip revision id (eg joe@foo.com-5676566-boa234a) of target
2646
:ivar source_branch: Source branch object that the push was from. This is
2647
read locked, and generally is a local (and thus low latency) branch.
2648
:ivar master_branch: If target is a bound branch, the master branch of
2649
target, or target itself. Always write locked.
2650
:ivar target_branch: The direct Branch where data is being sent (write
2652
:ivar local_branch: If the target is a bound branch this will be the
2653
target, otherwise it will be None.
2657
# DEPRECATED: push used to return the change in revno
2658
return self.new_revno - self.old_revno
2660
def report(self, to_file):
2661
"""Write a human-readable description of the result."""
2662
if self.old_revid == self.new_revid:
2663
note('No new revisions to push.')
2665
note('Pushed up to revision %d.' % self.new_revno)
2666
self._show_tag_conficts(to_file)
2669
class BranchCheckResult(object):
2670
"""Results of checking branch consistency.
2675
def __init__(self, branch):
2676
self.branch = branch
2678
def report_results(self, verbose):
2679
"""Report the check results via trace.note.
2681
:param verbose: Requests more detailed display of what was checked,
2684
note('checked branch %s format %s',
2686
self.branch._format)
2689
class Converter5to6(object):
2690
"""Perform an in-place upgrade of format 5 to format 6"""
2692
def convert(self, branch):
2693
# Data for 5 and 6 can peacefully coexist.
2694
format = BzrBranchFormat6()
2695
new_branch = format.open(branch.bzrdir, _found=True)
2697
# Copy source data into target
2698
new_branch._write_last_revision_info(*branch.last_revision_info())
2699
new_branch.set_parent(branch.get_parent())
2700
new_branch.set_bound_location(branch.get_bound_location())
2701
new_branch.set_push_location(branch.get_push_location())
2703
# New branch has no tags by default
2704
new_branch.tags._set_tag_dict({})
2706
# Copying done; now update target format
2707
new_branch._transport.put_bytes('format',
2708
format.get_format_string(),
2709
mode=new_branch.bzrdir._get_file_mode())
2711
# Clean up old files
2712
new_branch._transport.delete('revision-history')
2714
branch.set_parent(None)
2715
except errors.NoSuchFile:
2717
branch.set_bound_location(None)
2720
class Converter6to7(object):
2721
"""Perform an in-place upgrade of format 6 to format 7"""
2723
def convert(self, branch):
2724
format = BzrBranchFormat7()
2725
branch._set_config_location('stacked_on_location', '')
2726
# update target format
2727
branch._transport.put_bytes('format', format.get_format_string())
2731
def _run_with_write_locked_target(target, callable, *args, **kwargs):
2732
"""Run ``callable(*args, **kwargs)``, write-locking target for the
2735
_run_with_write_locked_target will attempt to release the lock it acquires.
2737
If an exception is raised by callable, then that exception *will* be
2738
propagated, even if the unlock attempt raises its own error. Thus
2739
_run_with_write_locked_target should be preferred to simply doing::
2743
return callable(*args, **kwargs)
2748
# This is very similar to bzrlib.decorators.needs_write_lock. Perhaps they
2749
# should share code?
2752
result = callable(*args, **kwargs)
2754
exc_info = sys.exc_info()
2758
raise exc_info[0], exc_info[1], exc_info[2]
2764
class InterBranch(InterObject):
2765
"""This class represents operations taking place between two branches.
2767
Its instances have methods like pull() and push() and contain
2768
references to the source and target repositories these operations
2769
can be carried out on.
2773
"""The available optimised InterBranch types."""
2776
def _get_branch_formats_to_test():
2777
"""Return a tuple with the Branch formats to use when testing."""
2778
raise NotImplementedError(self._get_branch_formats_to_test)
2780
def update_revisions(self, stop_revision=None, overwrite=False,
2782
"""Pull in new perfect-fit revisions.
2784
:param stop_revision: Updated until the given revision
2785
:param overwrite: Always set the branch pointer, rather than checking
2786
to see if it is a proper descendant.
2787
:param graph: A Graph object that can be used to query history
2788
information. This can be None.
2791
raise NotImplementedError(self.update_revisions)
2794
class GenericInterBranch(InterBranch):
2795
"""InterBranch implementation that uses public Branch functions.
2799
def _get_branch_formats_to_test():
2800
return BranchFormat._default_format, BranchFormat._default_format
2802
def update_revisions(self, stop_revision=None, overwrite=False,
2804
"""See InterBranch.update_revisions()."""
2805
self.source.lock_read()
2807
other_revno, other_last_revision = self.source.last_revision_info()
2808
stop_revno = None # unknown
2809
if stop_revision is None:
2810
stop_revision = other_last_revision
2811
if _mod_revision.is_null(stop_revision):
2812
# if there are no commits, we're done.
2814
stop_revno = other_revno
2816
# what's the current last revision, before we fetch [and change it
2818
last_rev = _mod_revision.ensure_null(self.target.last_revision())
2819
# we fetch here so that we don't process data twice in the common
2820
# case of having something to pull, and so that the check for
2821
# already merged can operate on the just fetched graph, which will
2822
# be cached in memory.
2823
self.target.fetch(self.source, stop_revision)
2824
# Check to see if one is an ancestor of the other
2827
graph = self.target.repository.get_graph()
2828
if self.target._check_if_descendant_or_diverged(
2829
stop_revision, last_rev, graph, self.source):
2830
# stop_revision is a descendant of last_rev, but we aren't
2831
# overwriting, so we're done.
2833
if stop_revno is None:
2835
graph = self.target.repository.get_graph()
2836
this_revno, this_last_revision = \
2837
self.target.last_revision_info()
2838
stop_revno = graph.find_distance_to_null(stop_revision,
2839
[(other_last_revision, other_revno),
2840
(this_last_revision, this_revno)])
2841
self.target.set_last_revision_info(stop_revno, stop_revision)
2843
self.source.unlock()
2846
def is_compatible(self, source, target):
2847
# GenericBranch uses the public API, so always compatible
2851
InterBranch.register_optimiser(GenericInterBranch)