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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 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 break_lock(self):
103
"""Break a lock if one is present from another instance.
105
Uses the ui factory to ask for confirmation if the lock may be from
108
This will probe the repository for its lock as well.
110
self.control_files.break_lock()
111
self.repository.break_lock()
112
master = self.get_master_branch()
113
if master is not None:
117
def open(base, _unsupported=False, possible_transports=None):
118
"""Open the branch rooted at base.
120
For instance, if the branch is at URL/.bzr/branch,
121
Branch.open(URL) -> a Branch instance.
123
control = bzrdir.BzrDir.open(base, _unsupported,
124
possible_transports=possible_transports)
125
return control.open_branch(_unsupported)
128
def open_from_transport(transport, _unsupported=False):
129
"""Open the branch rooted at transport"""
130
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
131
return control.open_branch(_unsupported)
134
def open_containing(url, possible_transports=None):
135
"""Open an existing branch which contains url.
137
This probes for a branch at url, and searches upwards from there.
139
Basically we keep looking up until we find the control directory or
140
run into the root. If there isn't one, raises NotBranchError.
141
If there is one and it is either an unrecognised format or an unsupported
142
format, UnknownFormatError or UnsupportedFormatError are raised.
143
If there is one, it is returned, along with the unused portion of url.
145
control, relpath = bzrdir.BzrDir.open_containing(url,
147
return control.open_branch(), relpath
149
def _push_should_merge_tags(self):
150
"""Should _basic_push merge this branch's tags into the target?
152
The default implementation returns False if this branch has no tags,
153
and True the rest of the time. Subclasses may override this.
155
return self.supports_tags() and self.tags.get_tag_dict()
157
def get_config(self):
158
return BranchConfig(self)
160
def _get_tags_bytes(self):
161
"""Get the bytes of a serialised tags dict.
163
Note that not all branches support tags, nor do all use the same tags
164
logic: this method is specific to BasicTags. Other tag implementations
165
may use the same method name and behave differently, safely, because
166
of the double-dispatch via
167
format.make_tags->tags_instance->get_tags_dict.
169
:return: The bytes of the tags file.
170
:seealso: Branch._set_tags_bytes.
172
return self._transport.get_bytes('tags')
174
def _get_nick(self, local=False, possible_transports=None):
175
config = self.get_config()
176
# explicit overrides master, but don't look for master if local is True
177
if not local and not config.has_explicit_nickname():
179
master = self.get_master_branch(possible_transports)
180
if master is not None:
181
# return the master branch value
183
except errors.BzrError, e:
184
# Silently fall back to local implicit nick if the master is
186
mutter("Could not connect to bound branch, "
187
"falling back to local nick.\n " + str(e))
188
return config.get_nickname()
190
def _set_nick(self, nick):
191
self.get_config().set_user_option('nickname', nick, warn_masked=True)
193
nick = property(_get_nick, _set_nick)
196
raise NotImplementedError(self.is_locked)
198
def _lefthand_history(self, revision_id, last_rev=None,
200
if 'evil' in debug.debug_flags:
201
mutter_callsite(4, "_lefthand_history scales with history.")
202
# stop_revision must be a descendant of last_revision
203
graph = self.repository.get_graph()
204
if last_rev is not None:
205
if not graph.is_ancestor(last_rev, revision_id):
206
# our previous tip is not merged into stop_revision
207
raise errors.DivergedBranches(self, other_branch)
208
# make a new revision history from the graph
209
parents_map = graph.get_parent_map([revision_id])
210
if revision_id not in parents_map:
211
raise errors.NoSuchRevision(self, revision_id)
212
current_rev_id = revision_id
214
check_not_reserved_id = _mod_revision.check_not_reserved_id
215
# Do not include ghosts or graph origin in revision_history
216
while (current_rev_id in parents_map and
217
len(parents_map[current_rev_id]) > 0):
218
check_not_reserved_id(current_rev_id)
219
new_history.append(current_rev_id)
220
current_rev_id = parents_map[current_rev_id][0]
221
parents_map = graph.get_parent_map([current_rev_id])
222
new_history.reverse()
225
def lock_write(self):
226
raise NotImplementedError(self.lock_write)
229
raise NotImplementedError(self.lock_read)
232
raise NotImplementedError(self.unlock)
234
def peek_lock_mode(self):
235
"""Return lock mode for the Branch: 'r', 'w' or None"""
236
raise NotImplementedError(self.peek_lock_mode)
238
def get_physical_lock_status(self):
239
raise NotImplementedError(self.get_physical_lock_status)
242
def dotted_revno_to_revision_id(self, revno, _cache_reverse=False):
243
"""Return the revision_id for a dotted revno.
245
:param revno: a tuple like (1,) or (1,1,2)
246
:param _cache_reverse: a private parameter enabling storage
247
of the reverse mapping in a top level cache. (This should
248
only be done in selective circumstances as we want to
249
avoid having the mapping cached multiple times.)
250
:return: the revision_id
251
:raises errors.NoSuchRevision: if the revno doesn't exist
253
rev_id = self._do_dotted_revno_to_revision_id(revno)
255
self._partial_revision_id_to_revno_cache[rev_id] = revno
258
def _do_dotted_revno_to_revision_id(self, revno):
259
"""Worker function for dotted_revno_to_revision_id.
261
Subclasses should override this if they wish to
262
provide a more efficient implementation.
265
return self.get_rev_id(revno[0])
266
revision_id_to_revno = self.get_revision_id_to_revno_map()
267
revision_ids = [revision_id for revision_id, this_revno
268
in revision_id_to_revno.iteritems()
269
if revno == this_revno]
270
if len(revision_ids) == 1:
271
return revision_ids[0]
273
revno_str = '.'.join(map(str, revno))
274
raise errors.NoSuchRevision(self, revno_str)
277
def revision_id_to_dotted_revno(self, revision_id):
278
"""Given a revision id, return its dotted revno.
280
:return: a tuple like (1,) or (400,1,3).
282
return self._do_revision_id_to_dotted_revno(revision_id)
284
def _do_revision_id_to_dotted_revno(self, revision_id):
285
"""Worker function for revision_id_to_revno."""
286
# Try the caches if they are loaded
287
result = self._partial_revision_id_to_revno_cache.get(revision_id)
288
if result is not None:
290
if self._revision_id_to_revno_cache:
291
result = self._revision_id_to_revno_cache.get(revision_id)
293
raise errors.NoSuchRevision(self, revision_id)
294
# Try the mainline as it's optimised
296
revno = self.revision_id_to_revno(revision_id)
298
except errors.NoSuchRevision:
299
# We need to load and use the full revno map after all
300
result = self.get_revision_id_to_revno_map().get(revision_id)
302
raise errors.NoSuchRevision(self, revision_id)
306
def get_revision_id_to_revno_map(self):
307
"""Return the revision_id => dotted revno map.
309
This will be regenerated on demand, but will be cached.
311
:return: A dictionary mapping revision_id => dotted revno.
312
This dictionary should not be modified by the caller.
314
if self._revision_id_to_revno_cache is not None:
315
mapping = self._revision_id_to_revno_cache
317
mapping = self._gen_revno_map()
318
self._cache_revision_id_to_revno(mapping)
319
# TODO: jam 20070417 Since this is being cached, should we be returning
321
# I would rather not, and instead just declare that users should not
322
# modify the return value.
325
def _gen_revno_map(self):
326
"""Create a new mapping from revision ids to dotted revnos.
328
Dotted revnos are generated based on the current tip in the revision
330
This is the worker function for get_revision_id_to_revno_map, which
331
just caches the return value.
333
:return: A dictionary mapping revision_id => dotted revno.
335
revision_id_to_revno = dict((rev_id, revno)
336
for rev_id, depth, revno, end_of_merge
337
in self.iter_merge_sorted_revisions())
338
return revision_id_to_revno
341
def iter_merge_sorted_revisions(self, start_revision_id=None,
342
stop_revision_id=None, stop_rule='exclude', direction='reverse'):
343
"""Walk the revisions for a branch in merge sorted order.
345
Merge sorted order is the output from a merge-aware,
346
topological sort, i.e. all parents come before their
347
children going forward; the opposite for reverse.
349
:param start_revision_id: the revision_id to begin walking from.
350
If None, the branch tip is used.
351
:param stop_revision_id: the revision_id to terminate the walk
352
after. If None, the rest of history is included.
353
:param stop_rule: if stop_revision_id is not None, the precise rule
354
to use for termination:
355
* 'exclude' - leave the stop revision out of the result (default)
356
* 'include' - the stop revision is the last item in the result
357
* 'with-merges' - include the stop revision and all of its
358
merged revisions in the result
359
:param direction: either 'reverse' or 'forward':
360
* reverse means return the start_revision_id first, i.e.
361
start at the most recent revision and go backwards in history
362
* forward returns tuples in the opposite order to reverse.
363
Note in particular that forward does *not* do any intelligent
364
ordering w.r.t. depth as some clients of this API may like.
365
(If required, that ought to be done at higher layers.)
367
:return: an iterator over (revision_id, depth, revno, end_of_merge)
370
* revision_id: the unique id of the revision
371
* depth: How many levels of merging deep this node has been
373
* revno_sequence: This field provides a sequence of
374
revision numbers for all revisions. The format is:
375
(REVNO, BRANCHNUM, BRANCHREVNO). BRANCHNUM is the number of the
376
branch that the revno is on. From left to right the REVNO numbers
377
are the sequence numbers within that branch of the revision.
378
* end_of_merge: When True the next node (earlier in history) is
379
part of a different merge.
381
# Note: depth and revno values are in the context of the branch so
382
# we need the full graph to get stable numbers, regardless of the
384
if self._merge_sorted_revisions_cache is None:
385
last_revision = self.last_revision()
386
graph = self.repository.get_graph()
387
parent_map = dict(((key, value) for key, value in
388
graph.iter_ancestry([last_revision]) if value is not None))
389
revision_graph = repository._strip_NULL_ghosts(parent_map)
390
revs = tsort.merge_sort(revision_graph, last_revision, None,
392
# Drop the sequence # before caching
393
self._merge_sorted_revisions_cache = [r[1:] for r in revs]
395
filtered = self._filter_merge_sorted_revisions(
396
self._merge_sorted_revisions_cache, start_revision_id,
397
stop_revision_id, stop_rule)
398
if direction == 'reverse':
400
if direction == 'forward':
401
return reversed(list(filtered))
403
raise ValueError('invalid direction %r' % direction)
405
def _filter_merge_sorted_revisions(self, merge_sorted_revisions,
406
start_revision_id, stop_revision_id, stop_rule):
407
"""Iterate over an inclusive range of sorted revisions."""
408
rev_iter = iter(merge_sorted_revisions)
409
if start_revision_id is not None:
410
for rev_id, depth, revno, end_of_merge in rev_iter:
411
if rev_id != start_revision_id:
414
# The decision to include the start or not
415
# depends on the stop_rule if a stop is provided
417
iter([(rev_id, depth, revno, end_of_merge)]),
420
if stop_revision_id is None:
421
for rev_id, depth, revno, end_of_merge in rev_iter:
422
yield rev_id, depth, revno, end_of_merge
423
elif stop_rule == 'exclude':
424
for rev_id, depth, revno, end_of_merge in rev_iter:
425
if rev_id == stop_revision_id:
427
yield rev_id, depth, revno, end_of_merge
428
elif stop_rule == 'include':
429
for rev_id, depth, revno, end_of_merge in rev_iter:
430
yield rev_id, depth, revno, end_of_merge
431
if rev_id == stop_revision_id:
433
elif stop_rule == 'with-merges':
434
stop_rev = self.repository.get_revision(stop_revision_id)
435
if stop_rev.parent_ids:
436
left_parent = stop_rev.parent_ids[0]
438
left_parent = _mod_revision.NULL_REVISION
439
for rev_id, depth, revno, end_of_merge in rev_iter:
440
if rev_id == left_parent:
442
yield rev_id, depth, revno, end_of_merge
444
raise ValueError('invalid stop_rule %r' % stop_rule)
446
def leave_lock_in_place(self):
447
"""Tell this branch object not to release the physical lock when this
450
If lock_write doesn't return a token, then this method is not supported.
452
self.control_files.leave_in_place()
454
def dont_leave_lock_in_place(self):
455
"""Tell this branch object to release the physical lock when this
456
object is unlocked, even if it didn't originally acquire it.
458
If lock_write doesn't return a token, then this method is not supported.
460
self.control_files.dont_leave_in_place()
462
def bind(self, other):
463
"""Bind the local branch the other branch.
465
:param other: The branch to bind to
468
raise errors.UpgradeRequired(self.base)
471
def fetch(self, from_branch, last_revision=None, pb=None):
472
"""Copy revisions from from_branch into this branch.
474
:param from_branch: Where to copy from.
475
:param last_revision: What revision to stop at (None for at the end
477
:param pb: An optional progress bar to use.
480
if self.base == from_branch.base:
483
symbol_versioning.warn(
484
symbol_versioning.deprecated_in((1, 14, 0))
485
% "pb parameter to fetch()")
486
from_branch.lock_read()
488
if last_revision is None:
489
last_revision = from_branch.last_revision()
490
last_revision = _mod_revision.ensure_null(last_revision)
491
return self.repository.fetch(from_branch.repository,
492
revision_id=last_revision,
497
def get_bound_location(self):
498
"""Return the URL of the branch we are bound to.
500
Older format branches cannot bind, please be sure to use a metadir
505
def get_old_bound_location(self):
506
"""Return the URL of the branch we used to be bound to
508
raise errors.UpgradeRequired(self.base)
510
def get_commit_builder(self, parents, config=None, timestamp=None,
511
timezone=None, committer=None, revprops=None,
513
"""Obtain a CommitBuilder for this branch.
515
:param parents: Revision ids of the parents of the new revision.
516
:param config: Optional configuration to use.
517
:param timestamp: Optional timestamp recorded for commit.
518
:param timezone: Optional timezone for timestamp.
519
:param committer: Optional committer to set for commit.
520
:param revprops: Optional dictionary of revision properties.
521
:param revision_id: Optional revision id.
525
config = self.get_config()
527
return self.repository.get_commit_builder(self, parents, config,
528
timestamp, timezone, committer, revprops, revision_id)
530
def get_master_branch(self, possible_transports=None):
531
"""Return the branch we are bound to.
533
:return: Either a Branch, or None
537
def get_revision_delta(self, revno):
538
"""Return the delta for one revision.
540
The delta is relative to its mainline predecessor, or the
541
empty tree for revision 1.
543
rh = self.revision_history()
544
if not (1 <= revno <= len(rh)):
545
raise errors.InvalidRevisionNumber(revno)
546
return self.repository.get_revision_delta(rh[revno-1])
548
def get_stacked_on_url(self):
549
"""Get the URL this branch is stacked against.
551
:raises NotStacked: If the branch is not stacked.
552
:raises UnstackableBranchFormat: If the branch does not support
555
raise NotImplementedError(self.get_stacked_on_url)
557
def print_file(self, file, revision_id):
558
"""Print `file` to stdout."""
559
raise NotImplementedError(self.print_file)
561
def set_revision_history(self, rev_history):
562
raise NotImplementedError(self.set_revision_history)
564
def set_stacked_on_url(self, url):
565
"""Set the URL this branch is stacked against.
567
:raises UnstackableBranchFormat: If the branch does not support
569
:raises UnstackableRepositoryFormat: If the repository does not support
572
raise NotImplementedError(self.set_stacked_on_url)
574
def _set_tags_bytes(self, bytes):
575
"""Mirror method for _get_tags_bytes.
577
:seealso: Branch._get_tags_bytes.
579
return _run_with_write_locked_target(self, self._transport.put_bytes,
582
def _cache_revision_history(self, rev_history):
583
"""Set the cached revision history to rev_history.
585
The revision_history method will use this cache to avoid regenerating
586
the revision history.
588
This API is semi-public; it only for use by subclasses, all other code
589
should consider it to be private.
591
self._revision_history_cache = rev_history
593
def _cache_revision_id_to_revno(self, revision_id_to_revno):
594
"""Set the cached revision_id => revno map to revision_id_to_revno.
596
This API is semi-public; it only for use by subclasses, all other code
597
should consider it to be private.
599
self._revision_id_to_revno_cache = revision_id_to_revno
601
def _clear_cached_state(self):
602
"""Clear any cached data on this branch, e.g. cached revision history.
604
This means the next call to revision_history will need to call
605
_gen_revision_history.
607
This API is semi-public; it only for use by subclasses, all other code
608
should consider it to be private.
610
self._revision_history_cache = None
611
self._revision_id_to_revno_cache = None
612
self._last_revision_info_cache = None
613
self._merge_sorted_revisions_cache = None
615
def _gen_revision_history(self):
616
"""Return sequence of revision hashes on to this branch.
618
Unlike revision_history, this method always regenerates or rereads the
619
revision history, i.e. it does not cache the result, so repeated calls
622
Concrete subclasses should override this instead of revision_history so
623
that subclasses do not need to deal with caching logic.
625
This API is semi-public; it only for use by subclasses, all other code
626
should consider it to be private.
628
raise NotImplementedError(self._gen_revision_history)
631
def revision_history(self):
632
"""Return sequence of revision ids on this branch.
634
This method will cache the revision history for as long as it is safe to
637
if 'evil' in debug.debug_flags:
638
mutter_callsite(3, "revision_history scales with history.")
639
if self._revision_history_cache is not None:
640
history = self._revision_history_cache
642
history = self._gen_revision_history()
643
self._cache_revision_history(history)
647
"""Return current revision number for this branch.
649
That is equivalent to the number of revisions committed to
652
return self.last_revision_info()[0]
655
"""Older format branches cannot bind or unbind."""
656
raise errors.UpgradeRequired(self.base)
658
def set_append_revisions_only(self, enabled):
659
"""Older format branches are never restricted to append-only"""
660
raise errors.UpgradeRequired(self.base)
662
def last_revision(self):
663
"""Return last revision id, or NULL_REVISION."""
664
return self.last_revision_info()[1]
667
def last_revision_info(self):
668
"""Return information about the last revision.
670
:return: A tuple (revno, revision_id).
672
if self._last_revision_info_cache is None:
673
self._last_revision_info_cache = self._last_revision_info()
674
return self._last_revision_info_cache
676
def _last_revision_info(self):
677
rh = self.revision_history()
680
return (revno, rh[-1])
682
return (0, _mod_revision.NULL_REVISION)
684
@deprecated_method(deprecated_in((1, 6, 0)))
685
def missing_revisions(self, other, stop_revision=None):
686
"""Return a list of new revisions that would perfectly fit.
688
If self and other have not diverged, return a list of the revisions
689
present in other, but missing from self.
691
self_history = self.revision_history()
692
self_len = len(self_history)
693
other_history = other.revision_history()
694
other_len = len(other_history)
695
common_index = min(self_len, other_len) -1
696
if common_index >= 0 and \
697
self_history[common_index] != other_history[common_index]:
698
raise errors.DivergedBranches(self, other)
700
if stop_revision is None:
701
stop_revision = other_len
703
if stop_revision > other_len:
704
raise errors.NoSuchRevision(self, stop_revision)
705
return other_history[self_len:stop_revision]
708
def update_revisions(self, other, stop_revision=None, overwrite=False,
710
"""Pull in new perfect-fit revisions.
712
:param other: Another Branch to pull from
713
:param stop_revision: Updated until the given revision
714
:param overwrite: Always set the branch pointer, rather than checking
715
to see if it is a proper descendant.
716
:param graph: A Graph object that can be used to query history
717
information. This can be None.
720
return InterBranch.get(other, self).update_revisions(stop_revision,
723
def import_last_revision_info(self, source_repo, revno, revid):
724
"""Set the last revision info, importing from another repo if necessary.
726
This is used by the bound branch code to upload a revision to
727
the master branch first before updating the tip of the local branch.
729
:param source_repo: Source repository to optionally fetch from
730
:param revno: Revision number of the new tip
731
:param revid: Revision id of the new tip
733
if not self.repository.has_same_location(source_repo):
734
self.repository.fetch(source_repo, revision_id=revid)
735
self.set_last_revision_info(revno, revid)
737
def revision_id_to_revno(self, revision_id):
738
"""Given a revision id, return its revno"""
739
if _mod_revision.is_null(revision_id):
741
history = self.revision_history()
743
return history.index(revision_id) + 1
745
raise errors.NoSuchRevision(self, revision_id)
747
def get_rev_id(self, revno, history=None):
748
"""Find the revision id of the specified revno."""
750
return _mod_revision.NULL_REVISION
752
history = self.revision_history()
753
if revno <= 0 or revno > len(history):
754
raise errors.NoSuchRevision(self, revno)
755
return history[revno - 1]
757
def pull(self, source, overwrite=False, stop_revision=None,
758
possible_transports=None, _override_hook_target=None):
759
"""Mirror source into this branch.
761
This branch is considered to be 'local', having low latency.
763
:returns: PullResult instance
765
raise NotImplementedError(self.pull)
767
def push(self, target, overwrite=False, stop_revision=None):
768
"""Mirror this branch into target.
770
This branch is considered to be 'local', having low latency.
772
raise NotImplementedError(self.push)
774
def basis_tree(self):
775
"""Return `Tree` object for last revision."""
776
return self.repository.revision_tree(self.last_revision())
778
def get_parent(self):
779
"""Return the parent location of the branch.
781
This is the default location for pull/missing. The usual
782
pattern is that the user can override it by specifying a
785
parent = self._get_parent_location()
788
# This is an old-format absolute path to a local branch
790
if parent.startswith('/'):
791
parent = urlutils.local_path_to_url(parent.decode('utf8'))
793
return urlutils.join(self.base[:-1], parent)
794
except errors.InvalidURLJoin, e:
795
raise errors.InaccessibleParent(parent, self.base)
797
def _get_parent_location(self):
798
raise NotImplementedError(self._get_parent_location)
800
def _set_config_location(self, name, url, config=None,
801
make_relative=False):
803
config = self.get_config()
807
url = urlutils.relative_url(self.base, url)
808
config.set_user_option(name, url, warn_masked=True)
810
def _get_config_location(self, name, config=None):
812
config = self.get_config()
813
location = config.get_user_option(name)
818
def get_submit_branch(self):
819
"""Return the submit location of the branch.
821
This is the default location for bundle. The usual
822
pattern is that the user can override it by specifying a
825
return self.get_config().get_user_option('submit_branch')
827
def set_submit_branch(self, location):
828
"""Return the submit location of the branch.
830
This is the default location for bundle. The usual
831
pattern is that the user can override it by specifying a
834
self.get_config().set_user_option('submit_branch', location,
837
def get_public_branch(self):
838
"""Return the public location of the branch.
840
This is is used by merge directives.
842
return self._get_config_location('public_branch')
844
def set_public_branch(self, location):
845
"""Return the submit location of the branch.
847
This is the default location for bundle. The usual
848
pattern is that the user can override it by specifying a
851
self._set_config_location('public_branch', location)
853
def get_push_location(self):
854
"""Return the None or the location to push this branch to."""
855
push_loc = self.get_config().get_user_option('push_location')
858
def set_push_location(self, location):
859
"""Set a new push location for this branch."""
860
raise NotImplementedError(self.set_push_location)
862
def _run_post_change_branch_tip_hooks(self, old_revno, old_revid):
863
"""Run the post_change_branch_tip hooks."""
864
hooks = Branch.hooks['post_change_branch_tip']
867
new_revno, new_revid = self.last_revision_info()
868
params = ChangeBranchTipParams(
869
self, old_revno, new_revno, old_revid, new_revid)
873
def _run_pre_change_branch_tip_hooks(self, new_revno, new_revid):
874
"""Run the pre_change_branch_tip hooks."""
875
hooks = Branch.hooks['pre_change_branch_tip']
878
old_revno, old_revid = self.last_revision_info()
879
params = ChangeBranchTipParams(
880
self, old_revno, new_revno, old_revid, new_revid)
884
except errors.TipChangeRejected:
887
exc_info = sys.exc_info()
888
hook_name = Branch.hooks.get_hook_name(hook)
889
raise errors.HookFailed(
890
'pre_change_branch_tip', hook_name, exc_info)
892
def set_parent(self, url):
893
raise NotImplementedError(self.set_parent)
897
"""Synchronise this branch with the master branch if any.
899
:return: None or the last_revision pivoted out during the update.
903
def check_revno(self, revno):
905
Check whether a revno corresponds to any revision.
906
Zero (the NULL revision) is considered valid.
909
self.check_real_revno(revno)
911
def check_real_revno(self, revno):
913
Check whether a revno corresponds to a real revision.
914
Zero (the NULL revision) is considered invalid
916
if revno < 1 or revno > self.revno():
917
raise errors.InvalidRevisionNumber(revno)
920
def clone(self, to_bzrdir, revision_id=None, repository_policy=None):
921
"""Clone this branch into to_bzrdir preserving all semantic values.
923
Most API users will want 'create_clone_on_transport', which creates a
924
new bzrdir and branch on the fly.
926
revision_id: if not None, the revision history in the new branch will
927
be truncated to end with revision_id.
929
result = to_bzrdir.create_branch()
930
if repository_policy is not None:
931
repository_policy.configure_branch(result)
932
self.copy_content_into(result, revision_id=revision_id)
936
def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
937
"""Create a new line of development from the branch, into to_bzrdir.
939
to_bzrdir controls the branch format.
941
revision_id: if not None, the revision history in the new branch will
942
be truncated to end with revision_id.
944
result = to_bzrdir.create_branch()
945
if repository_policy is not None:
946
repository_policy.configure_branch(result)
947
self.copy_content_into(result, revision_id=revision_id)
948
result.set_parent(self.bzrdir.root_transport.base)
951
def _synchronize_history(self, destination, revision_id):
952
"""Synchronize last revision and revision history between branches.
954
This version is most efficient when the destination is also a
955
BzrBranch6, but works for BzrBranch5, as long as the destination's
956
repository contains all the lefthand ancestors of the intended
957
last_revision. If not, set_last_revision_info will fail.
959
:param destination: The branch to copy the history into
960
:param revision_id: The revision-id to truncate history at. May
961
be None to copy complete history.
963
source_revno, source_revision_id = self.last_revision_info()
964
if revision_id is None:
965
revno, revision_id = source_revno, source_revision_id
966
elif source_revision_id == revision_id:
967
# we know the revno without needing to walk all of history
970
# To figure out the revno for a random revision, we need to build
971
# the revision history, and count its length.
972
# We don't care about the order, just how long it is.
973
# Alternatively, we could start at the current location, and count
974
# backwards. But there is no guarantee that we will find it since
975
# it may be a merged revision.
976
revno = len(list(self.repository.iter_reverse_revision_history(
978
destination.set_last_revision_info(revno, revision_id)
981
def copy_content_into(self, destination, revision_id=None):
982
"""Copy the content of self into destination.
984
revision_id: if not None, the revision history in the new branch will
985
be truncated to end with revision_id.
987
self._synchronize_history(destination, revision_id)
989
parent = self.get_parent()
990
except errors.InaccessibleParent, e:
991
mutter('parent was not accessible to copy: %s', e)
994
destination.set_parent(parent)
995
if self._push_should_merge_tags():
996
self.tags.merge_to(destination.tags)
1000
"""Check consistency of the branch.
1002
In particular this checks that revisions given in the revision-history
1003
do actually match up in the revision graph, and that they're all
1004
present in the repository.
1006
Callers will typically also want to check the repository.
1008
:return: A BranchCheckResult.
1010
mainline_parent_id = None
1011
last_revno, last_revision_id = self.last_revision_info()
1012
real_rev_history = list(self.repository.iter_reverse_revision_history(
1014
real_rev_history.reverse()
1015
if len(real_rev_history) != last_revno:
1016
raise errors.BzrCheckError('revno does not match len(mainline)'
1017
' %s != %s' % (last_revno, len(real_rev_history)))
1018
# TODO: We should probably also check that real_rev_history actually
1019
# matches self.revision_history()
1020
for revision_id in real_rev_history:
1022
revision = self.repository.get_revision(revision_id)
1023
except errors.NoSuchRevision, e:
1024
raise errors.BzrCheckError("mainline revision {%s} not in repository"
1026
# In general the first entry on the revision history has no parents.
1027
# But it's not illegal for it to have parents listed; this can happen
1028
# in imports from Arch when the parents weren't reachable.
1029
if mainline_parent_id is not None:
1030
if mainline_parent_id not in revision.parent_ids:
1031
raise errors.BzrCheckError("previous revision {%s} not listed among "
1033
% (mainline_parent_id, revision_id))
1034
mainline_parent_id = revision_id
1035
return BranchCheckResult(self)
1037
def _get_checkout_format(self):
1038
"""Return the most suitable metadir for a checkout of this branch.
1039
Weaves are used if this branch's repository uses weaves.
1041
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
1042
from bzrlib.repofmt import weaverepo
1043
format = bzrdir.BzrDirMetaFormat1()
1044
format.repository_format = weaverepo.RepositoryFormat7()
1046
format = self.repository.bzrdir.checkout_metadir()
1047
format.set_branch_format(self._format)
1050
def create_clone_on_transport(self, to_transport, revision_id=None,
1052
"""Create a clone of this branch and its bzrdir.
1054
:param to_transport: The transport to clone onto.
1055
:param revision_id: The revision id to use as tip in the new branch.
1056
If None the tip is obtained from this branch.
1057
:param stacked_on: An optional URL to stack the clone on.
1059
# XXX: Fix the bzrdir API to allow getting the branch back from the
1060
# clone call. Or something. 20090224 RBC/spiv.
1061
dir_to = self.bzrdir.clone_on_transport(to_transport,
1062
revision_id=revision_id, stacked_on=stacked_on)
1063
return dir_to.open_branch()
1065
def create_checkout(self, to_location, revision_id=None,
1066
lightweight=False, accelerator_tree=None,
1068
"""Create a checkout of a branch.
1070
:param to_location: The url to produce the checkout at
1071
:param revision_id: The revision to check out
1072
:param lightweight: If True, produce a lightweight checkout, otherwise,
1073
produce a bound branch (heavyweight checkout)
1074
:param accelerator_tree: A tree which can be used for retrieving file
1075
contents more quickly than the revision tree, i.e. a workingtree.
1076
The revision tree will be used for cases where accelerator_tree's
1077
content is different.
1078
:param hardlink: If true, hard-link files from accelerator_tree,
1080
:return: The tree of the created checkout
1082
t = transport.get_transport(to_location)
1085
format = self._get_checkout_format()
1086
checkout = format.initialize_on_transport(t)
1087
from_branch = BranchReferenceFormat().initialize(checkout, self)
1089
format = self._get_checkout_format()
1090
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1091
to_location, force_new_tree=False, format=format)
1092
checkout = checkout_branch.bzrdir
1093
checkout_branch.bind(self)
1094
# pull up to the specified revision_id to set the initial
1095
# branch tip correctly, and seed it with history.
1096
checkout_branch.pull(self, stop_revision=revision_id)
1098
tree = checkout.create_workingtree(revision_id,
1099
from_branch=from_branch,
1100
accelerator_tree=accelerator_tree,
1102
basis_tree = tree.basis_tree()
1103
basis_tree.lock_read()
1105
for path, file_id in basis_tree.iter_references():
1106
reference_parent = self.reference_parent(file_id, path)
1107
reference_parent.create_checkout(tree.abspath(path),
1108
basis_tree.get_reference_revision(file_id, path),
1115
def reconcile(self, thorough=True):
1116
"""Make sure the data stored in this branch is consistent."""
1117
from bzrlib.reconcile import BranchReconciler
1118
reconciler = BranchReconciler(self, thorough=thorough)
1119
reconciler.reconcile()
1122
def reference_parent(self, file_id, path):
1123
"""Return the parent branch for a tree-reference file_id
1124
:param file_id: The file_id of the tree reference
1125
:param path: The path of the file_id in the tree
1126
:return: A branch associated with the file_id
1128
# FIXME should provide multiple branches, based on config
1129
return Branch.open(self.bzrdir.root_transport.clone(path).base)
1131
def supports_tags(self):
1132
return self._format.supports_tags()
1134
def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1136
"""Ensure that revision_b is a descendant of revision_a.
1138
This is a helper function for update_revisions.
1140
:raises: DivergedBranches if revision_b has diverged from revision_a.
1141
:returns: True if revision_b is a descendant of revision_a.
1143
relation = self._revision_relations(revision_a, revision_b, graph)
1144
if relation == 'b_descends_from_a':
1146
elif relation == 'diverged':
1147
raise errors.DivergedBranches(self, other_branch)
1148
elif relation == 'a_descends_from_b':
1151
raise AssertionError("invalid relation: %r" % (relation,))
1153
def _revision_relations(self, revision_a, revision_b, graph):
1154
"""Determine the relationship between two revisions.
1156
:returns: One of: 'a_descends_from_b', 'b_descends_from_a', 'diverged'
1158
heads = graph.heads([revision_a, revision_b])
1159
if heads == set([revision_b]):
1160
return 'b_descends_from_a'
1161
elif heads == set([revision_a, revision_b]):
1162
# These branches have diverged
1164
elif heads == set([revision_a]):
1165
return 'a_descends_from_b'
1167
raise AssertionError("invalid heads: %r" % (heads,))
1170
class BranchFormat(object):
1171
"""An encapsulation of the initialization and open routines for a format.
1173
Formats provide three things:
1174
* An initialization routine,
1178
Formats are placed in an dict by their format string for reference
1179
during branch opening. Its not required that these be instances, they
1180
can be classes themselves with class methods - it simply depends on
1181
whether state is needed for a given format or not.
1183
Once a format is deprecated, just deprecate the initialize and open
1184
methods on the format class. Do not deprecate the object, as the
1185
object will be created every time regardless.
1188
_default_format = None
1189
"""The default format used for new branches."""
1192
"""The known formats."""
1194
def __eq__(self, other):
1195
return self.__class__ is other.__class__
1197
def __ne__(self, other):
1198
return not (self == other)
1201
def find_format(klass, a_bzrdir):
1202
"""Return the format for the branch object in a_bzrdir."""
1204
transport = a_bzrdir.get_branch_transport(None)
1205
format_string = transport.get("format").read()
1206
return klass._formats[format_string]
1207
except errors.NoSuchFile:
1208
raise errors.NotBranchError(path=transport.base)
1210
raise errors.UnknownFormatError(format=format_string, kind='branch')
1213
def get_default_format(klass):
1214
"""Return the current default format."""
1215
return klass._default_format
1217
def get_reference(self, a_bzrdir):
1218
"""Get the target reference of the branch in a_bzrdir.
1220
format probing must have been completed before calling
1221
this method - it is assumed that the format of the branch
1222
in a_bzrdir is correct.
1224
:param a_bzrdir: The bzrdir to get the branch data from.
1225
:return: None if the branch is not a reference branch.
1230
def set_reference(self, a_bzrdir, to_branch):
1231
"""Set the target reference of the branch in a_bzrdir.
1233
format probing must have been completed before calling
1234
this method - it is assumed that the format of the branch
1235
in a_bzrdir is correct.
1237
:param a_bzrdir: The bzrdir to set the branch reference for.
1238
:param to_branch: branch that the checkout is to reference
1240
raise NotImplementedError(self.set_reference)
1242
def get_format_string(self):
1243
"""Return the ASCII format string that identifies this format."""
1244
raise NotImplementedError(self.get_format_string)
1246
def get_format_description(self):
1247
"""Return the short format description for this format."""
1248
raise NotImplementedError(self.get_format_description)
1250
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
1252
"""Initialize a branch in a bzrdir, with specified files
1254
:param a_bzrdir: The bzrdir to initialize the branch in
1255
:param utf8_files: The files to create as a list of
1256
(filename, content) tuples
1257
:param set_format: If True, set the format with
1258
self.get_format_string. (BzrBranch4 has its format set
1260
:return: a branch in this format
1262
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1263
branch_transport = a_bzrdir.get_branch_transport(self)
1265
'metadir': ('lock', lockdir.LockDir),
1266
'branch4': ('branch-lock', lockable_files.TransportLock),
1268
lock_name, lock_class = lock_map[lock_type]
1269
control_files = lockable_files.LockableFiles(branch_transport,
1270
lock_name, lock_class)
1271
control_files.create_lock()
1272
control_files.lock_write()
1274
utf8_files += [('format', self.get_format_string())]
1276
for (filename, content) in utf8_files:
1277
branch_transport.put_bytes(
1279
mode=a_bzrdir._get_file_mode())
1281
control_files.unlock()
1282
return self.open(a_bzrdir, _found=True)
1284
def initialize(self, a_bzrdir):
1285
"""Create a branch of this format in a_bzrdir."""
1286
raise NotImplementedError(self.initialize)
1288
def is_supported(self):
1289
"""Is this format supported?
1291
Supported formats can be initialized and opened.
1292
Unsupported formats may not support initialization or committing or
1293
some other features depending on the reason for not being supported.
1297
def make_tags(self, branch):
1298
"""Create a tags object for branch.
1300
This method is on BranchFormat, because BranchFormats are reflected
1301
over the wire via network_name(), whereas full Branch instances require
1302
multiple VFS method calls to operate at all.
1304
The default implementation returns a disabled-tags instance.
1306
Note that it is normal for branch to be a RemoteBranch when using tags
1309
return DisabledTags(branch)
1311
def network_name(self):
1312
"""A simple byte string uniquely identifying this format for RPC calls.
1314
MetaDir branch formats use their disk format string to identify the
1315
repository over the wire. All in one formats such as bzr < 0.8, and
1316
foreign formats like svn/git and hg should use some marker which is
1317
unique and immutable.
1319
raise NotImplementedError(self.network_name)
1321
def open(self, a_bzrdir, _found=False):
1322
"""Return the branch object for a_bzrdir
1324
_found is a private parameter, do not use it. It is used to indicate
1325
if format probing has already be done.
1327
raise NotImplementedError(self.open)
1330
def register_format(klass, format):
1331
"""Register a metadir format."""
1332
klass._formats[format.get_format_string()] = format
1333
# Metadir formats have a network name of their format string, and get
1334
# registered as class factories.
1335
network_format_registry.register(format.get_format_string(), format.__class__)
1338
def set_default_format(klass, format):
1339
klass._default_format = format
1341
def supports_stacking(self):
1342
"""True if this format records a stacked-on branch."""
1346
def unregister_format(klass, format):
1347
del klass._formats[format.get_format_string()]
1350
return self.get_format_description().rstrip()
1352
def supports_tags(self):
1353
"""True if this format supports tags stored in the branch"""
1354
return False # by default
1357
class BranchHooks(Hooks):
1358
"""A dictionary mapping hook name to a list of callables for branch hooks.
1360
e.g. ['set_rh'] Is the list of items to be called when the
1361
set_revision_history function is invoked.
1365
"""Create the default hooks.
1367
These are all empty initially, because by default nothing should get
1370
Hooks.__init__(self)
1371
# Introduced in 0.15:
1372
# invoked whenever the revision history has been set
1373
# with set_revision_history. The api signature is
1374
# (branch, revision_history), and the branch will
1377
# Invoked after a branch is opened. The api signature is (branch).
1379
# invoked after a push operation completes.
1380
# the api signature is
1382
# containing the members
1383
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1384
# where local is the local target branch or None, master is the target
1385
# master branch, and the rest should be self explanatory. The source
1386
# is read locked and the target branches write locked. Source will
1387
# be the local low-latency branch.
1388
self['post_push'] = []
1389
# invoked after a pull operation completes.
1390
# the api signature is
1392
# containing the members
1393
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1394
# where local is the local branch or None, master is the target
1395
# master branch, and the rest should be self explanatory. The source
1396
# is read locked and the target branches write locked. The local
1397
# branch is the low-latency branch.
1398
self['post_pull'] = []
1399
# invoked before a commit operation takes place.
1400
# the api signature is
1401
# (local, master, old_revno, old_revid, future_revno, future_revid,
1402
# tree_delta, future_tree).
1403
# old_revid is NULL_REVISION for the first commit to a branch
1404
# tree_delta is a TreeDelta object describing changes from the basis
1405
# revision, hooks MUST NOT modify this delta
1406
# future_tree is an in-memory tree obtained from
1407
# CommitBuilder.revision_tree() and hooks MUST NOT modify this tree
1408
self['pre_commit'] = []
1409
# invoked after a commit operation completes.
1410
# the api signature is
1411
# (local, master, old_revno, old_revid, new_revno, new_revid)
1412
# old_revid is NULL_REVISION for the first commit to a branch.
1413
self['post_commit'] = []
1414
# invoked after a uncommit operation completes.
1415
# the api signature is
1416
# (local, master, old_revno, old_revid, new_revno, new_revid) where
1417
# local is the local branch or None, master is the target branch,
1418
# and an empty branch recieves new_revno of 0, new_revid of None.
1419
self['post_uncommit'] = []
1421
# Invoked before the tip of a branch changes.
1422
# the api signature is
1423
# (params) where params is a ChangeBranchTipParams with the members
1424
# (branch, old_revno, new_revno, old_revid, new_revid)
1425
self['pre_change_branch_tip'] = []
1427
# Invoked after the tip of a branch changes.
1428
# the api signature is
1429
# (params) where params is a ChangeBranchTipParams with the members
1430
# (branch, old_revno, new_revno, old_revid, new_revid)
1431
self['post_change_branch_tip'] = []
1433
# Invoked when a stacked branch activates its fallback locations and
1434
# allows the transformation of the url of said location.
1435
# the api signature is
1436
# (branch, url) where branch is the branch having its fallback
1437
# location activated and url is the url for the fallback location.
1438
# The hook should return a url.
1439
self['transform_fallback_location'] = []
1442
# install the default hooks into the Branch class.
1443
Branch.hooks = BranchHooks()
1446
class ChangeBranchTipParams(object):
1447
"""Object holding parameters passed to *_change_branch_tip hooks.
1449
There are 5 fields that hooks may wish to access:
1451
:ivar branch: the branch being changed
1452
:ivar old_revno: revision number before the change
1453
:ivar new_revno: revision number after the change
1454
:ivar old_revid: revision id before the change
1455
:ivar new_revid: revision id after the change
1457
The revid fields are strings. The revno fields are integers.
1460
def __init__(self, branch, old_revno, new_revno, old_revid, new_revid):
1461
"""Create a group of ChangeBranchTip parameters.
1463
:param branch: The branch being changed.
1464
:param old_revno: Revision number before the change.
1465
:param new_revno: Revision number after the change.
1466
:param old_revid: Tip revision id before the change.
1467
:param new_revid: Tip revision id after the change.
1469
self.branch = branch
1470
self.old_revno = old_revno
1471
self.new_revno = new_revno
1472
self.old_revid = old_revid
1473
self.new_revid = new_revid
1475
def __eq__(self, other):
1476
return self.__dict__ == other.__dict__
1479
return "<%s of %s from (%s, %s) to (%s, %s)>" % (
1480
self.__class__.__name__, self.branch,
1481
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1484
class BzrBranchFormat4(BranchFormat):
1485
"""Bzr branch format 4.
1488
- a revision-history file.
1489
- a branch-lock lock file [ to be shared with the bzrdir ]
1492
def get_format_description(self):
1493
"""See BranchFormat.get_format_description()."""
1494
return "Branch format 4"
1496
def initialize(self, a_bzrdir):
1497
"""Create a branch of this format in a_bzrdir."""
1498
utf8_files = [('revision-history', ''),
1499
('branch-name', ''),
1501
return self._initialize_helper(a_bzrdir, utf8_files,
1502
lock_type='branch4', set_format=False)
1505
super(BzrBranchFormat4, self).__init__()
1506
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1508
def network_name(self):
1509
"""The network name for this format is the control dirs disk label."""
1510
return self._matchingbzrdir.get_format_string()
1512
def open(self, a_bzrdir, _found=False):
1513
"""Return the branch object for a_bzrdir
1515
_found is a private parameter, do not use it. It is used to indicate
1516
if format probing has already be done.
1519
# we are being called directly and must probe.
1520
raise NotImplementedError
1521
return BzrBranch(_format=self,
1522
_control_files=a_bzrdir._control_files,
1524
_repository=a_bzrdir.open_repository())
1527
return "Bazaar-NG branch format 4"
1530
class BranchFormatMetadir(BranchFormat):
1531
"""Common logic for meta-dir based branch formats."""
1533
def _branch_class(self):
1534
"""What class to instantiate on open calls."""
1535
raise NotImplementedError(self._branch_class)
1537
def network_name(self):
1538
"""A simple byte string uniquely identifying this format for RPC calls.
1540
Metadir branch formats use their format string.
1542
return self.get_format_string()
1544
def open(self, a_bzrdir, _found=False):
1545
"""Return the branch object for a_bzrdir.
1547
_found is a private parameter, do not use it. It is used to indicate
1548
if format probing has already be done.
1551
format = BranchFormat.find_format(a_bzrdir)
1552
if format.__class__ != self.__class__:
1553
raise AssertionError("wrong format %r found for %r" %
1556
transport = a_bzrdir.get_branch_transport(None)
1557
control_files = lockable_files.LockableFiles(transport, 'lock',
1559
return self._branch_class()(_format=self,
1560
_control_files=control_files,
1562
_repository=a_bzrdir.find_repository())
1563
except errors.NoSuchFile:
1564
raise errors.NotBranchError(path=transport.base)
1567
super(BranchFormatMetadir, self).__init__()
1568
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1569
self._matchingbzrdir.set_branch_format(self)
1571
def supports_tags(self):
1575
class BzrBranchFormat5(BranchFormatMetadir):
1576
"""Bzr branch format 5.
1579
- a revision-history file.
1581
- a lock dir guarding the branch itself
1582
- all of this stored in a branch/ subdirectory
1583
- works with shared repositories.
1585
This format is new in bzr 0.8.
1588
def _branch_class(self):
1591
def get_format_string(self):
1592
"""See BranchFormat.get_format_string()."""
1593
return "Bazaar-NG branch format 5\n"
1595
def get_format_description(self):
1596
"""See BranchFormat.get_format_description()."""
1597
return "Branch format 5"
1599
def initialize(self, a_bzrdir):
1600
"""Create a branch of this format in a_bzrdir."""
1601
utf8_files = [('revision-history', ''),
1602
('branch-name', ''),
1604
return self._initialize_helper(a_bzrdir, utf8_files)
1606
def supports_tags(self):
1610
class BzrBranchFormat6(BranchFormatMetadir):
1611
"""Branch format with last-revision and tags.
1613
Unlike previous formats, this has no explicit revision history. Instead,
1614
this just stores the last-revision, and the left-hand history leading
1615
up to there is the history.
1617
This format was introduced in bzr 0.15
1618
and became the default in 0.91.
1621
def _branch_class(self):
1624
def get_format_string(self):
1625
"""See BranchFormat.get_format_string()."""
1626
return "Bazaar Branch Format 6 (bzr 0.15)\n"
1628
def get_format_description(self):
1629
"""See BranchFormat.get_format_description()."""
1630
return "Branch format 6"
1632
def initialize(self, a_bzrdir):
1633
"""Create a branch of this format in a_bzrdir."""
1634
utf8_files = [('last-revision', '0 null:\n'),
1635
('branch.conf', ''),
1638
return self._initialize_helper(a_bzrdir, utf8_files)
1640
def make_tags(self, branch):
1641
"""See bzrlib.branch.BranchFormat.make_tags()."""
1642
return BasicTags(branch)
1646
class BzrBranchFormat7(BranchFormatMetadir):
1647
"""Branch format with last-revision, tags, and a stacked location pointer.
1649
The stacked location pointer is passed down to the repository and requires
1650
a repository format with supports_external_lookups = True.
1652
This format was introduced in bzr 1.6.
1655
def _branch_class(self):
1658
def get_format_string(self):
1659
"""See BranchFormat.get_format_string()."""
1660
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
1662
def get_format_description(self):
1663
"""See BranchFormat.get_format_description()."""
1664
return "Branch format 7"
1666
def initialize(self, a_bzrdir):
1667
"""Create a branch of this format in a_bzrdir."""
1668
utf8_files = [('last-revision', '0 null:\n'),
1669
('branch.conf', ''),
1672
return self._initialize_helper(a_bzrdir, utf8_files)
1675
super(BzrBranchFormat7, self).__init__()
1676
self._matchingbzrdir.repository_format = \
1677
RepositoryFormatKnitPack5RichRoot()
1679
def make_tags(self, branch):
1680
"""See bzrlib.branch.BranchFormat.make_tags()."""
1681
return BasicTags(branch)
1683
def supports_stacking(self):
1687
class BranchReferenceFormat(BranchFormat):
1688
"""Bzr branch reference format.
1690
Branch references are used in implementing checkouts, they
1691
act as an alias to the real branch which is at some other url.
1698
def get_format_string(self):
1699
"""See BranchFormat.get_format_string()."""
1700
return "Bazaar-NG Branch Reference Format 1\n"
1702
def get_format_description(self):
1703
"""See BranchFormat.get_format_description()."""
1704
return "Checkout reference format 1"
1706
def get_reference(self, a_bzrdir):
1707
"""See BranchFormat.get_reference()."""
1708
transport = a_bzrdir.get_branch_transport(None)
1709
return transport.get('location').read()
1711
def set_reference(self, a_bzrdir, to_branch):
1712
"""See BranchFormat.set_reference()."""
1713
transport = a_bzrdir.get_branch_transport(None)
1714
location = transport.put_bytes('location', to_branch.base)
1716
def initialize(self, a_bzrdir, target_branch=None):
1717
"""Create a branch of this format in a_bzrdir."""
1718
if target_branch is None:
1719
# this format does not implement branch itself, thus the implicit
1720
# creation contract must see it as uninitializable
1721
raise errors.UninitializableFormat(self)
1722
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1723
branch_transport = a_bzrdir.get_branch_transport(self)
1724
branch_transport.put_bytes('location',
1725
target_branch.bzrdir.root_transport.base)
1726
branch_transport.put_bytes('format', self.get_format_string())
1728
a_bzrdir, _found=True,
1729
possible_transports=[target_branch.bzrdir.root_transport])
1732
super(BranchReferenceFormat, self).__init__()
1733
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1734
self._matchingbzrdir.set_branch_format(self)
1736
def _make_reference_clone_function(format, a_branch):
1737
"""Create a clone() routine for a branch dynamically."""
1738
def clone(to_bzrdir, revision_id=None,
1739
repository_policy=None):
1740
"""See Branch.clone()."""
1741
return format.initialize(to_bzrdir, a_branch)
1742
# cannot obey revision_id limits when cloning a reference ...
1743
# FIXME RBC 20060210 either nuke revision_id for clone, or
1744
# emit some sort of warning/error to the caller ?!
1747
def open(self, a_bzrdir, _found=False, location=None,
1748
possible_transports=None):
1749
"""Return the branch that the branch reference in a_bzrdir points at.
1751
_found is a private parameter, do not use it. It is used to indicate
1752
if format probing has already be done.
1755
format = BranchFormat.find_format(a_bzrdir)
1756
if format.__class__ != self.__class__:
1757
raise AssertionError("wrong format %r found for %r" %
1759
if location is None:
1760
location = self.get_reference(a_bzrdir)
1761
real_bzrdir = bzrdir.BzrDir.open(
1762
location, possible_transports=possible_transports)
1763
result = real_bzrdir.open_branch()
1764
# this changes the behaviour of result.clone to create a new reference
1765
# rather than a copy of the content of the branch.
1766
# I did not use a proxy object because that needs much more extensive
1767
# testing, and we are only changing one behaviour at the moment.
1768
# If we decide to alter more behaviours - i.e. the implicit nickname
1769
# then this should be refactored to introduce a tested proxy branch
1770
# and a subclass of that for use in overriding clone() and ....
1772
result.clone = self._make_reference_clone_function(result)
1776
network_format_registry = registry.FormatRegistry()
1777
"""Registry of formats indexed by their network name.
1779
The network name for a branch format is an identifier that can be used when
1780
referring to formats with smart server operations. See
1781
BranchFormat.network_name() for more detail.
1785
# formats which have no format string are not discoverable
1786
# and not independently creatable, so are not registered.
1787
__format5 = BzrBranchFormat5()
1788
__format6 = BzrBranchFormat6()
1789
__format7 = BzrBranchFormat7()
1790
BranchFormat.register_format(__format5)
1791
BranchFormat.register_format(BranchReferenceFormat())
1792
BranchFormat.register_format(__format6)
1793
BranchFormat.register_format(__format7)
1794
BranchFormat.set_default_format(__format6)
1795
_legacy_formats = [BzrBranchFormat4(),
1797
network_format_registry.register(
1798
_legacy_formats[0].network_name(), _legacy_formats[0].__class__)
1801
class BzrBranch(Branch):
1802
"""A branch stored in the actual filesystem.
1804
Note that it's "local" in the context of the filesystem; it doesn't
1805
really matter if it's on an nfs/smb/afs/coda/... share, as long as
1806
it's writable, and can be accessed via the normal filesystem API.
1808
:ivar _transport: Transport for file operations on this branch's
1809
control files, typically pointing to the .bzr/branch directory.
1810
:ivar repository: Repository for this branch.
1811
:ivar base: The url of the base directory for this branch; the one
1812
containing the .bzr directory.
1815
def __init__(self, _format=None,
1816
_control_files=None, a_bzrdir=None, _repository=None):
1817
"""Create new branch object at a particular location."""
1818
if a_bzrdir is None:
1819
raise ValueError('a_bzrdir must be supplied')
1821
self.bzrdir = a_bzrdir
1822
self._base = self.bzrdir.transport.clone('..').base
1823
# XXX: We should be able to just do
1824
# self.base = self.bzrdir.root_transport.base
1825
# but this does not quite work yet -- mbp 20080522
1826
self._format = _format
1827
if _control_files is None:
1828
raise ValueError('BzrBranch _control_files is None')
1829
self.control_files = _control_files
1830
self._transport = _control_files._transport
1831
self.repository = _repository
1832
Branch.__init__(self)
1835
return '%s(%r)' % (self.__class__.__name__, self.base)
1839
def _get_base(self):
1840
"""Returns the directory containing the control directory."""
1843
base = property(_get_base, doc="The URL for the root of this branch.")
1845
def is_locked(self):
1846
return self.control_files.is_locked()
1848
def lock_write(self, token=None):
1849
repo_token = self.repository.lock_write()
1851
token = self.control_files.lock_write(token=token)
1853
self.repository.unlock()
1857
def lock_read(self):
1858
self.repository.lock_read()
1860
self.control_files.lock_read()
1862
self.repository.unlock()
1866
# TODO: test for failed two phase locks. This is known broken.
1868
self.control_files.unlock()
1870
self.repository.unlock()
1871
if not self.control_files.is_locked():
1872
# we just released the lock
1873
self._clear_cached_state()
1875
def peek_lock_mode(self):
1876
if self.control_files._lock_count == 0:
1879
return self.control_files._lock_mode
1881
def get_physical_lock_status(self):
1882
return self.control_files.get_physical_lock_status()
1885
def print_file(self, file, revision_id):
1886
"""See Branch.print_file."""
1887
return self.repository.print_file(file, revision_id)
1889
def _write_revision_history(self, history):
1890
"""Factored out of set_revision_history.
1892
This performs the actual writing to disk.
1893
It is intended to be called by BzrBranch5.set_revision_history."""
1894
self._transport.put_bytes(
1895
'revision-history', '\n'.join(history),
1896
mode=self.bzrdir._get_file_mode())
1899
def set_revision_history(self, rev_history):
1900
"""See Branch.set_revision_history."""
1901
if 'evil' in debug.debug_flags:
1902
mutter_callsite(3, "set_revision_history scales with history.")
1903
check_not_reserved_id = _mod_revision.check_not_reserved_id
1904
for rev_id in rev_history:
1905
check_not_reserved_id(rev_id)
1906
if Branch.hooks['post_change_branch_tip']:
1907
# Don't calculate the last_revision_info() if there are no hooks
1909
old_revno, old_revid = self.last_revision_info()
1910
if len(rev_history) == 0:
1911
revid = _mod_revision.NULL_REVISION
1913
revid = rev_history[-1]
1914
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
1915
self._write_revision_history(rev_history)
1916
self._clear_cached_state()
1917
self._cache_revision_history(rev_history)
1918
for hook in Branch.hooks['set_rh']:
1919
hook(self, rev_history)
1920
if Branch.hooks['post_change_branch_tip']:
1921
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1923
def _synchronize_history(self, destination, revision_id):
1924
"""Synchronize last revision and revision history between branches.
1926
This version is most efficient when the destination is also a
1927
BzrBranch5, but works for BzrBranch6 as long as the revision
1928
history is the true lefthand parent history, and all of the revisions
1929
are in the destination's repository. If not, set_revision_history
1932
:param destination: The branch to copy the history into
1933
:param revision_id: The revision-id to truncate history at. May
1934
be None to copy complete history.
1936
if not isinstance(destination._format, BzrBranchFormat5):
1937
super(BzrBranch, self)._synchronize_history(
1938
destination, revision_id)
1940
if revision_id == _mod_revision.NULL_REVISION:
1943
new_history = self.revision_history()
1944
if revision_id is not None and new_history != []:
1946
new_history = new_history[:new_history.index(revision_id) + 1]
1948
rev = self.repository.get_revision(revision_id)
1949
new_history = rev.get_history(self.repository)[1:]
1950
destination.set_revision_history(new_history)
1953
def set_last_revision_info(self, revno, revision_id):
1954
"""Set the last revision of this branch.
1956
The caller is responsible for checking that the revno is correct
1957
for this revision id.
1959
It may be possible to set the branch last revision to an id not
1960
present in the repository. However, branches can also be
1961
configured to check constraints on history, in which case this may not
1964
revision_id = _mod_revision.ensure_null(revision_id)
1965
# this old format stores the full history, but this api doesn't
1966
# provide it, so we must generate, and might as well check it's
1968
history = self._lefthand_history(revision_id)
1969
if len(history) != revno:
1970
raise AssertionError('%d != %d' % (len(history), revno))
1971
self.set_revision_history(history)
1973
def _gen_revision_history(self):
1974
history = self._transport.get_bytes('revision-history').split('\n')
1975
if history[-1:] == ['']:
1976
# There shouldn't be a trailing newline, but just in case.
1981
def generate_revision_history(self, revision_id, last_rev=None,
1983
"""Create a new revision history that will finish with revision_id.
1985
:param revision_id: the new tip to use.
1986
:param last_rev: The previous last_revision. If not None, then this
1987
must be a ancestory of revision_id, or DivergedBranches is raised.
1988
:param other_branch: The other branch that DivergedBranches should
1989
raise with respect to.
1991
self.set_revision_history(self._lefthand_history(revision_id,
1992
last_rev, other_branch))
1994
def basis_tree(self):
1995
"""See Branch.basis_tree."""
1996
return self.repository.revision_tree(self.last_revision())
1999
def pull(self, source, overwrite=False, stop_revision=None,
2000
_hook_master=None, run_hooks=True, possible_transports=None,
2001
_override_hook_target=None):
2004
:param _hook_master: Private parameter - set the branch to
2005
be supplied as the master to pull hooks.
2006
:param run_hooks: Private parameter - if false, this branch
2007
is being called because it's the master of the primary branch,
2008
so it should not run its hooks.
2009
:param _override_hook_target: Private parameter - set the branch to be
2010
supplied as the target_branch to pull hooks.
2012
result = PullResult()
2013
result.source_branch = source
2014
if _override_hook_target is None:
2015
result.target_branch = self
2017
result.target_branch = _override_hook_target
2020
# We assume that during 'pull' the local repository is closer than
2022
graph = self.repository.get_graph(source.repository)
2023
result.old_revno, result.old_revid = self.last_revision_info()
2024
self.update_revisions(source, stop_revision, overwrite=overwrite,
2026
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
2027
result.new_revno, result.new_revid = self.last_revision_info()
2029
result.master_branch = _hook_master
2030
result.local_branch = result.target_branch
2032
result.master_branch = result.target_branch
2033
result.local_branch = None
2035
for hook in Branch.hooks['post_pull']:
2041
def _get_parent_location(self):
2042
_locs = ['parent', 'pull', 'x-pull']
2045
return self._transport.get_bytes(l).strip('\n')
2046
except errors.NoSuchFile:
2051
def push(self, target, overwrite=False, stop_revision=None,
2052
_override_hook_source_branch=None):
2055
This is the basic concrete implementation of push()
2057
:param _override_hook_source_branch: If specified, run
2058
the hooks passing this Branch as the source, rather than self.
2059
This is for use of RemoteBranch, where push is delegated to the
2060
underlying vfs-based Branch.
2062
# TODO: Public option to disable running hooks - should be trivial but
2064
return _run_with_write_locked_target(
2065
target, self._push_with_bound_branches, target, overwrite,
2067
_override_hook_source_branch=_override_hook_source_branch)
2069
def _push_with_bound_branches(self, target, overwrite,
2071
_override_hook_source_branch=None):
2072
"""Push from self into target, and into target's master if any.
2074
This is on the base BzrBranch class even though it doesn't support
2075
bound branches because the *target* might be bound.
2078
if _override_hook_source_branch:
2079
result.source_branch = _override_hook_source_branch
2080
for hook in Branch.hooks['post_push']:
2083
bound_location = target.get_bound_location()
2084
if bound_location and target.base != bound_location:
2085
# there is a master branch.
2087
# XXX: Why the second check? Is it even supported for a branch to
2088
# be bound to itself? -- mbp 20070507
2089
master_branch = target.get_master_branch()
2090
master_branch.lock_write()
2092
# push into the master from this branch.
2093
self._basic_push(master_branch, overwrite, stop_revision)
2094
# and push into the target branch from this. Note that we push from
2095
# this branch again, because its considered the highest bandwidth
2097
result = self._basic_push(target, overwrite, stop_revision)
2098
result.master_branch = master_branch
2099
result.local_branch = target
2103
master_branch.unlock()
2106
result = self._basic_push(target, overwrite, stop_revision)
2107
# TODO: Why set master_branch and local_branch if there's no
2108
# binding? Maybe cleaner to just leave them unset? -- mbp
2110
result.master_branch = target
2111
result.local_branch = None
2115
def _basic_push(self, target, overwrite, stop_revision):
2116
"""Basic implementation of push without bound branches or hooks.
2118
Must be called with self read locked and target write locked.
2120
result = PushResult()
2121
result.source_branch = self
2122
result.target_branch = target
2123
result.old_revno, result.old_revid = target.last_revision_info()
2124
if result.old_revid != self.last_revision():
2125
# We assume that during 'push' this repository is closer than
2127
graph = self.repository.get_graph(target.repository)
2128
target.update_revisions(self, stop_revision, overwrite=overwrite,
2130
if self._push_should_merge_tags():
2131
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
2132
result.new_revno, result.new_revid = target.last_revision_info()
2135
def get_stacked_on_url(self):
2136
raise errors.UnstackableBranchFormat(self._format, self.base)
2138
def set_push_location(self, location):
2139
"""See Branch.set_push_location."""
2140
self.get_config().set_user_option(
2141
'push_location', location,
2142
store=_mod_config.STORE_LOCATION_NORECURSE)
2145
def set_parent(self, url):
2146
"""See Branch.set_parent."""
2147
# TODO: Maybe delete old location files?
2148
# URLs should never be unicode, even on the local fs,
2149
# FIXUP this and get_parent in a future branch format bump:
2150
# read and rewrite the file. RBC 20060125
2152
if isinstance(url, unicode):
2154
url = url.encode('ascii')
2155
except UnicodeEncodeError:
2156
raise errors.InvalidURL(url,
2157
"Urls must be 7-bit ascii, "
2158
"use bzrlib.urlutils.escape")
2159
url = urlutils.relative_url(self.base, url)
2160
self._set_parent_location(url)
2162
def _set_parent_location(self, url):
2164
self._transport.delete('parent')
2166
self._transport.put_bytes('parent', url + '\n',
2167
mode=self.bzrdir._get_file_mode())
2169
def set_stacked_on_url(self, url):
2170
raise errors.UnstackableBranchFormat(self._format, self.base)
2173
class BzrBranch5(BzrBranch):
2174
"""A format 5 branch. This supports new features over plain branches.
2176
It has support for a master_branch which is the data for bound branches.
2180
def pull(self, source, overwrite=False, stop_revision=None,
2181
run_hooks=True, possible_transports=None,
2182
_override_hook_target=None):
2183
"""Pull from source into self, updating my master if any.
2185
:param run_hooks: Private parameter - if false, this branch
2186
is being called because it's the master of the primary branch,
2187
so it should not run its hooks.
2189
bound_location = self.get_bound_location()
2190
master_branch = None
2191
if bound_location and source.base != bound_location:
2192
# not pulling from master, so we need to update master.
2193
master_branch = self.get_master_branch(possible_transports)
2194
master_branch.lock_write()
2197
# pull from source into master.
2198
master_branch.pull(source, overwrite, stop_revision,
2200
return super(BzrBranch5, self).pull(source, overwrite,
2201
stop_revision, _hook_master=master_branch,
2202
run_hooks=run_hooks,
2203
_override_hook_target=_override_hook_target)
2206
master_branch.unlock()
2208
def get_bound_location(self):
2210
return self._transport.get_bytes('bound')[:-1]
2211
except errors.NoSuchFile:
2215
def get_master_branch(self, possible_transports=None):
2216
"""Return the branch we are bound to.
2218
:return: Either a Branch, or None
2220
This could memoise the branch, but if thats done
2221
it must be revalidated on each new lock.
2222
So for now we just don't memoise it.
2223
# RBC 20060304 review this decision.
2225
bound_loc = self.get_bound_location()
2229
return Branch.open(bound_loc,
2230
possible_transports=possible_transports)
2231
except (errors.NotBranchError, errors.ConnectionError), e:
2232
raise errors.BoundBranchConnectionFailure(
2236
def set_bound_location(self, location):
2237
"""Set the target where this branch is bound to.
2239
:param location: URL to the target branch
2242
self._transport.put_bytes('bound', location+'\n',
2243
mode=self.bzrdir._get_file_mode())
2246
self._transport.delete('bound')
2247
except errors.NoSuchFile:
2252
def bind(self, other):
2253
"""Bind this branch to the branch other.
2255
This does not push or pull data between the branches, though it does
2256
check for divergence to raise an error when the branches are not
2257
either the same, or one a prefix of the other. That behaviour may not
2258
be useful, so that check may be removed in future.
2260
:param other: The branch to bind to
2263
# TODO: jam 20051230 Consider checking if the target is bound
2264
# It is debatable whether you should be able to bind to
2265
# a branch which is itself bound.
2266
# Committing is obviously forbidden,
2267
# but binding itself may not be.
2268
# Since we *have* to check at commit time, we don't
2269
# *need* to check here
2271
# we want to raise diverged if:
2272
# last_rev is not in the other_last_rev history, AND
2273
# other_last_rev is not in our history, and do it without pulling
2275
self.set_bound_location(other.base)
2279
"""If bound, unbind"""
2280
return self.set_bound_location(None)
2283
def update(self, possible_transports=None):
2284
"""Synchronise this branch with the master branch if any.
2286
:return: None or the last_revision that was pivoted out during the
2289
master = self.get_master_branch(possible_transports)
2290
if master is not None:
2291
old_tip = _mod_revision.ensure_null(self.last_revision())
2292
self.pull(master, overwrite=True)
2293
if self.repository.get_graph().is_ancestor(old_tip,
2294
_mod_revision.ensure_null(self.last_revision())):
2300
class BzrBranch7(BzrBranch5):
2301
"""A branch with support for a fallback repository."""
2303
def _get_fallback_repository(self, url):
2304
"""Get the repository we fallback to at url."""
2305
url = urlutils.join(self.base, url)
2306
a_bzrdir = bzrdir.BzrDir.open(url,
2307
possible_transports=[self._transport])
2308
return a_bzrdir.open_branch().repository
2310
def _activate_fallback_location(self, url):
2311
"""Activate the branch/repository from url as a fallback repository."""
2312
self.repository.add_fallback_repository(
2313
self._get_fallback_repository(url))
2315
def _open_hook(self):
2317
url = self.get_stacked_on_url()
2318
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2319
errors.UnstackableBranchFormat):
2322
for hook in Branch.hooks['transform_fallback_location']:
2323
url = hook(self, url)
2325
hook_name = Branch.hooks.get_hook_name(hook)
2326
raise AssertionError(
2327
"'transform_fallback_location' hook %s returned "
2328
"None, not a URL." % hook_name)
2329
self._activate_fallback_location(url)
2331
def _check_stackable_repo(self):
2332
if not self.repository._format.supports_external_lookups:
2333
raise errors.UnstackableRepositoryFormat(self.repository._format,
2334
self.repository.base)
2336
def __init__(self, *args, **kwargs):
2337
super(BzrBranch7, self).__init__(*args, **kwargs)
2338
self._last_revision_info_cache = None
2339
self._partial_revision_history_cache = []
2341
def _clear_cached_state(self):
2342
super(BzrBranch7, self)._clear_cached_state()
2343
self._last_revision_info_cache = None
2344
self._partial_revision_history_cache = []
2346
def _last_revision_info(self):
2347
revision_string = self._transport.get_bytes('last-revision')
2348
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2349
revision_id = cache_utf8.get_cached_utf8(revision_id)
2351
return revno, revision_id
2353
def _write_last_revision_info(self, revno, revision_id):
2354
"""Simply write out the revision id, with no checks.
2356
Use set_last_revision_info to perform this safely.
2358
Does not update the revision_history cache.
2359
Intended to be called by set_last_revision_info and
2360
_write_revision_history.
2362
revision_id = _mod_revision.ensure_null(revision_id)
2363
out_string = '%d %s\n' % (revno, revision_id)
2364
self._transport.put_bytes('last-revision', out_string,
2365
mode=self.bzrdir._get_file_mode())
2368
def set_last_revision_info(self, revno, revision_id):
2369
revision_id = _mod_revision.ensure_null(revision_id)
2370
old_revno, old_revid = self.last_revision_info()
2371
if self._get_append_revisions_only():
2372
self._check_history_violation(revision_id)
2373
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2374
self._write_last_revision_info(revno, revision_id)
2375
self._clear_cached_state()
2376
self._last_revision_info_cache = revno, revision_id
2377
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2379
def _synchronize_history(self, destination, revision_id):
2380
"""Synchronize last revision and revision history between branches.
2382
:see: Branch._synchronize_history
2384
# XXX: The base Branch has a fast implementation of this method based
2385
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2386
# that uses set_revision_history. This class inherits from BzrBranch5,
2387
# but wants the fast implementation, so it calls
2388
# Branch._synchronize_history directly.
2389
Branch._synchronize_history(self, destination, revision_id)
2391
def _check_history_violation(self, revision_id):
2392
last_revision = _mod_revision.ensure_null(self.last_revision())
2393
if _mod_revision.is_null(last_revision):
2395
if last_revision not in self._lefthand_history(revision_id):
2396
raise errors.AppendRevisionsOnlyViolation(self.base)
2398
def _gen_revision_history(self):
2399
"""Generate the revision history from last revision
2401
last_revno, last_revision = self.last_revision_info()
2402
self._extend_partial_history(stop_index=last_revno-1)
2403
return list(reversed(self._partial_revision_history_cache))
2405
def _extend_partial_history(self, stop_index=None, stop_revision=None):
2406
"""Extend the partial history to include a given index
2408
If a stop_index is supplied, stop when that index has been reached.
2409
If a stop_revision is supplied, stop when that revision is
2410
encountered. Otherwise, stop when the beginning of history is
2413
:param stop_index: The index which should be present. When it is
2414
present, history extension will stop.
2415
:param revision_id: The revision id which should be present. When
2416
it is encountered, history extension will stop.
2418
repo = self.repository
2419
if len(self._partial_revision_history_cache) == 0:
2420
iterator = repo.iter_reverse_revision_history(self.last_revision())
2422
start_revision = self._partial_revision_history_cache[-1]
2423
iterator = repo.iter_reverse_revision_history(start_revision)
2424
#skip the last revision in the list
2425
next_revision = iterator.next()
2426
for revision_id in iterator:
2427
self._partial_revision_history_cache.append(revision_id)
2428
if (stop_index is not None and
2429
len(self._partial_revision_history_cache) > stop_index):
2431
if revision_id == stop_revision:
2434
def _write_revision_history(self, history):
2435
"""Factored out of set_revision_history.
2437
This performs the actual writing to disk, with format-specific checks.
2438
It is intended to be called by BzrBranch5.set_revision_history.
2440
if len(history) == 0:
2441
last_revision = 'null:'
2443
if history != self._lefthand_history(history[-1]):
2444
raise errors.NotLefthandHistory(history)
2445
last_revision = history[-1]
2446
if self._get_append_revisions_only():
2447
self._check_history_violation(last_revision)
2448
self._write_last_revision_info(len(history), last_revision)
2451
def _set_parent_location(self, url):
2452
"""Set the parent branch"""
2453
self._set_config_location('parent_location', url, make_relative=True)
2456
def _get_parent_location(self):
2457
"""Set the parent branch"""
2458
return self._get_config_location('parent_location')
2460
def set_push_location(self, location):
2461
"""See Branch.set_push_location."""
2462
self._set_config_location('push_location', location)
2464
def set_bound_location(self, location):
2465
"""See Branch.set_push_location."""
2467
config = self.get_config()
2468
if location is None:
2469
if config.get_user_option('bound') != 'True':
2472
config.set_user_option('bound', 'False', warn_masked=True)
2475
self._set_config_location('bound_location', location,
2477
config.set_user_option('bound', 'True', warn_masked=True)
2480
def _get_bound_location(self, bound):
2481
"""Return the bound location in the config file.
2483
Return None if the bound parameter does not match"""
2484
config = self.get_config()
2485
config_bound = (config.get_user_option('bound') == 'True')
2486
if config_bound != bound:
2488
return self._get_config_location('bound_location', config=config)
2490
def get_bound_location(self):
2491
"""See Branch.set_push_location."""
2492
return self._get_bound_location(True)
2494
def get_old_bound_location(self):
2495
"""See Branch.get_old_bound_location"""
2496
return self._get_bound_location(False)
2498
def get_stacked_on_url(self):
2499
# you can always ask for the URL; but you might not be able to use it
2500
# if the repo can't support stacking.
2501
## self._check_stackable_repo()
2502
stacked_url = self._get_config_location('stacked_on_location')
2503
if stacked_url is None:
2504
raise errors.NotStacked(self)
2507
def set_append_revisions_only(self, enabled):
2512
self.get_config().set_user_option('append_revisions_only', value,
2515
def set_stacked_on_url(self, url):
2516
self._check_stackable_repo()
2519
old_url = self.get_stacked_on_url()
2520
except (errors.NotStacked, errors.UnstackableBranchFormat,
2521
errors.UnstackableRepositoryFormat):
2524
# repositories don't offer an interface to remove fallback
2525
# repositories today; take the conceptually simpler option and just
2527
self.repository = self.bzrdir.find_repository()
2528
# for every revision reference the branch has, ensure it is pulled
2530
source_repository = self._get_fallback_repository(old_url)
2531
for revision_id in chain([self.last_revision()],
2532
self.tags.get_reverse_tag_dict()):
2533
self.repository.fetch(source_repository, revision_id,
2536
self._activate_fallback_location(url)
2537
# write this out after the repository is stacked to avoid setting a
2538
# stacked config that doesn't work.
2539
self._set_config_location('stacked_on_location', url)
2541
def _get_append_revisions_only(self):
2542
value = self.get_config().get_user_option('append_revisions_only')
2543
return value == 'True'
2546
def generate_revision_history(self, revision_id, last_rev=None,
2548
"""See BzrBranch5.generate_revision_history"""
2549
history = self._lefthand_history(revision_id, last_rev, other_branch)
2550
revno = len(history)
2551
self.set_last_revision_info(revno, revision_id)
2554
def get_rev_id(self, revno, history=None):
2555
"""Find the revision id of the specified revno."""
2557
return _mod_revision.NULL_REVISION
2559
last_revno, last_revision_id = self.last_revision_info()
2560
if revno <= 0 or revno > last_revno:
2561
raise errors.NoSuchRevision(self, revno)
2563
if history is not None:
2564
return history[revno - 1]
2566
index = last_revno - revno
2567
if len(self._partial_revision_history_cache) <= index:
2568
self._extend_partial_history(stop_index=index)
2569
if len(self._partial_revision_history_cache) > index:
2570
return self._partial_revision_history_cache[index]
2572
raise errors.NoSuchRevision(self, revno)
2575
def revision_id_to_revno(self, revision_id):
2576
"""Given a revision id, return its revno"""
2577
if _mod_revision.is_null(revision_id):
2580
index = self._partial_revision_history_cache.index(revision_id)
2582
self._extend_partial_history(stop_revision=revision_id)
2583
index = len(self._partial_revision_history_cache) - 1
2584
if self._partial_revision_history_cache[index] != revision_id:
2585
raise errors.NoSuchRevision(self, revision_id)
2586
return self.revno() - index
2589
class BzrBranch6(BzrBranch7):
2590
"""See BzrBranchFormat6 for the capabilities of this branch.
2592
This subclass of BzrBranch7 disables the new features BzrBranch7 added,
2596
def get_stacked_on_url(self):
2597
raise errors.UnstackableBranchFormat(self._format, self.base)
2599
def set_stacked_on_url(self, url):
2600
raise errors.UnstackableBranchFormat(self._format, self.base)
2603
######################################################################
2604
# results of operations
2607
class _Result(object):
2609
def _show_tag_conficts(self, to_file):
2610
if not getattr(self, 'tag_conflicts', None):
2612
to_file.write('Conflicting tags:\n')
2613
for name, value1, value2 in self.tag_conflicts:
2614
to_file.write(' %s\n' % (name, ))
2617
class PullResult(_Result):
2618
"""Result of a Branch.pull operation.
2620
:ivar old_revno: Revision number before pull.
2621
:ivar new_revno: Revision number after pull.
2622
:ivar old_revid: Tip revision id before pull.
2623
:ivar new_revid: Tip revision id after pull.
2624
:ivar source_branch: Source (local) branch object.
2625
:ivar master_branch: Master branch of the target, or the target if no
2627
:ivar local_branch: target branch if there is a Master, else None
2628
:ivar target_branch: Target/destination branch object.
2629
:ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
2633
# DEPRECATED: pull used to return the change in revno
2634
return self.new_revno - self.old_revno
2636
def report(self, to_file):
2638
if self.old_revid == self.new_revid:
2639
to_file.write('No revisions to pull.\n')
2641
to_file.write('Now on revision %d.\n' % self.new_revno)
2642
self._show_tag_conficts(to_file)
2645
class PushResult(_Result):
2646
"""Result of a Branch.push operation.
2648
:ivar old_revno: Revision number before push.
2649
:ivar new_revno: Revision number after push.
2650
:ivar old_revid: Tip revision id before push.
2651
:ivar new_revid: Tip revision id after push.
2652
:ivar source_branch: Source branch object.
2653
:ivar master_branch: Master branch of the target, or None.
2654
:ivar target_branch: Target/destination branch object.
2658
# DEPRECATED: push used to return the change in revno
2659
return self.new_revno - self.old_revno
2661
def report(self, to_file):
2662
"""Write a human-readable description of the result."""
2663
if self.old_revid == self.new_revid:
2664
note('No new revisions to push.')
2666
note('Pushed up to revision %d.' % self.new_revno)
2667
self._show_tag_conficts(to_file)
2670
class BranchCheckResult(object):
2671
"""Results of checking branch consistency.
2676
def __init__(self, branch):
2677
self.branch = branch
2679
def report_results(self, verbose):
2680
"""Report the check results via trace.note.
2682
:param verbose: Requests more detailed display of what was checked,
2685
note('checked branch %s format %s',
2687
self.branch._format)
2690
class Converter5to6(object):
2691
"""Perform an in-place upgrade of format 5 to format 6"""
2693
def convert(self, branch):
2694
# Data for 5 and 6 can peacefully coexist.
2695
format = BzrBranchFormat6()
2696
new_branch = format.open(branch.bzrdir, _found=True)
2698
# Copy source data into target
2699
new_branch._write_last_revision_info(*branch.last_revision_info())
2700
new_branch.set_parent(branch.get_parent())
2701
new_branch.set_bound_location(branch.get_bound_location())
2702
new_branch.set_push_location(branch.get_push_location())
2704
# New branch has no tags by default
2705
new_branch.tags._set_tag_dict({})
2707
# Copying done; now update target format
2708
new_branch._transport.put_bytes('format',
2709
format.get_format_string(),
2710
mode=new_branch.bzrdir._get_file_mode())
2712
# Clean up old files
2713
new_branch._transport.delete('revision-history')
2715
branch.set_parent(None)
2716
except errors.NoSuchFile:
2718
branch.set_bound_location(None)
2721
class Converter6to7(object):
2722
"""Perform an in-place upgrade of format 6 to format 7"""
2724
def convert(self, branch):
2725
format = BzrBranchFormat7()
2726
branch._set_config_location('stacked_on_location', '')
2727
# update target format
2728
branch._transport.put_bytes('format', format.get_format_string())
2732
def _run_with_write_locked_target(target, callable, *args, **kwargs):
2733
"""Run ``callable(*args, **kwargs)``, write-locking target for the
2736
_run_with_write_locked_target will attempt to release the lock it acquires.
2738
If an exception is raised by callable, then that exception *will* be
2739
propagated, even if the unlock attempt raises its own error. Thus
2740
_run_with_write_locked_target should be preferred to simply doing::
2744
return callable(*args, **kwargs)
2749
# This is very similar to bzrlib.decorators.needs_write_lock. Perhaps they
2750
# should share code?
2753
result = callable(*args, **kwargs)
2755
exc_info = sys.exc_info()
2759
raise exc_info[0], exc_info[1], exc_info[2]
2765
class InterBranch(InterObject):
2766
"""This class represents operations taking place between two branches.
2768
Its instances have methods like pull() and push() and contain
2769
references to the source and target repositories these operations
2770
can be carried out on.
2774
"""The available optimised InterBranch types."""
2777
def _get_branch_formats_to_test():
2778
"""Return a tuple with the Branch formats to use when testing."""
2779
raise NotImplementedError(self._get_branch_formats_to_test)
2781
def update_revisions(self, stop_revision=None, overwrite=False,
2783
"""Pull in new perfect-fit revisions.
2785
:param stop_revision: Updated until the given revision
2786
:param overwrite: Always set the branch pointer, rather than checking
2787
to see if it is a proper descendant.
2788
:param graph: A Graph object that can be used to query history
2789
information. This can be None.
2792
raise NotImplementedError(self.update_revisions)
2795
class GenericInterBranch(InterBranch):
2796
"""InterBranch implementation that uses public Branch functions.
2800
def _get_branch_formats_to_test():
2801
return BranchFormat._default_format, BranchFormat._default_format
2803
def update_revisions(self, stop_revision=None, overwrite=False,
2805
"""See InterBranch.update_revisions()."""
2806
self.source.lock_read()
2808
other_revno, other_last_revision = self.source.last_revision_info()
2809
stop_revno = None # unknown
2810
if stop_revision is None:
2811
stop_revision = other_last_revision
2812
if _mod_revision.is_null(stop_revision):
2813
# if there are no commits, we're done.
2815
stop_revno = other_revno
2817
# what's the current last revision, before we fetch [and change it
2819
last_rev = _mod_revision.ensure_null(self.target.last_revision())
2820
# we fetch here so that we don't process data twice in the common
2821
# case of having something to pull, and so that the check for
2822
# already merged can operate on the just fetched graph, which will
2823
# be cached in memory.
2824
self.target.fetch(self.source, stop_revision)
2825
# Check to see if one is an ancestor of the other
2828
graph = self.target.repository.get_graph()
2829
if self.target._check_if_descendant_or_diverged(
2830
stop_revision, last_rev, graph, self.source):
2831
# stop_revision is a descendant of last_rev, but we aren't
2832
# overwriting, so we're done.
2834
if stop_revno is None:
2836
graph = self.target.repository.get_graph()
2837
this_revno, this_last_revision = \
2838
self.target.last_revision_info()
2839
stop_revno = graph.find_distance_to_null(stop_revision,
2840
[(other_last_revision, other_revno),
2841
(this_last_revision, this_revno)])
2842
self.target.set_last_revision_info(stop_revno, stop_revision)
2844
self.source.unlock()
2847
def is_compatible(self, source, target):
2848
# GenericBranch uses the public API, so always compatible
2852
InterBranch.register_optimiser(GenericInterBranch)