1
# Copyright (C) 2005-2012 Canonical Ltd
1
# Copyright (C) 2005 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from .lazy_import import lazy_import
18
lazy_import(globals(), """
22
config as _mod_config,
26
revision as _mod_revision,
32
from breezy.bzr import (
37
from breezy.i18n import gettext, ngettext
45
from .hooks import Hooks
46
from .inter import InterObject
47
from .lock import LogicalLockResult
48
from .trace import mutter, mutter_callsite, note, is_quiet, warning
51
class UnstackableBranchFormat(errors.BzrError):
53
_fmt = ("The branch '%(url)s'(%(format)s) is not a stackable format. "
54
"You will need to upgrade the branch to permit branch stacking.")
56
def __init__(self, format, url):
57
errors.BzrError.__init__(self)
62
class Branch(controldir.ControlComponent):
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
from warnings import warn
23
from cStringIO import StringIO
27
import bzrlib.inventory as inventory
28
from bzrlib.trace import mutter, note
29
from bzrlib.osutils import (isdir, quotefn,
30
rename, splitpath, sha_file, appendpath,
32
import bzrlib.errors as errors
33
from bzrlib.errors import (BzrError, InvalidRevisionNumber, InvalidRevisionId,
34
NoSuchRevision, HistoryMissing, NotBranchError,
35
DivergedBranches, LockError, UnlistableStore,
36
UnlistableBranch, NoSuchFile, NotVersionedError,
38
from bzrlib.textui import show_status
39
from bzrlib.revision import (Revision, is_ancestor, get_intervening_revisions,
42
from bzrlib.delta import compare_trees
43
from bzrlib.tree import EmptyTree, RevisionTree
44
from bzrlib.inventory import Inventory
45
from bzrlib.store import copy_all
46
from bzrlib.store.text import TextStore
47
from bzrlib.store.weave import WeaveStore
48
from bzrlib.testament import Testament
49
import bzrlib.transactions as transactions
50
from bzrlib.transport import Transport, get_transport
53
from config import TreeConfig
56
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
57
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
58
BZR_BRANCH_FORMAT_6 = "Bazaar-NG branch, format 6\n"
59
## TODO: Maybe include checks for common corruption of newlines, etc?
62
# TODO: Some operations like log might retrieve the same revisions
63
# repeatedly to calculate deltas. We could perhaps have a weakref
64
# cache in memory to make this faster. In general anything can be
65
# cached in memory between lock and unlock operations.
67
def find_branch(*ignored, **ignored_too):
68
# XXX: leave this here for about one release, then remove it
69
raise NotImplementedError('find_branch() is not supported anymore, '
70
'please use one of the new branch constructors')
73
def needs_read_lock(unbound):
74
"""Decorate unbound to take out and release a read lock."""
75
def decorated(self, *args, **kwargs):
78
return unbound(self, *args, **kwargs)
84
def needs_write_lock(unbound):
85
"""Decorate unbound to take out and release a write lock."""
86
def decorated(self, *args, **kwargs):
89
return unbound(self, *args, **kwargs)
94
######################################################################
63
98
"""Branch holding a history of revisions.
66
Base directory/url of the branch; using control_url and
67
control_transport is more standardized.
68
:ivar hooks: An instance of BranchHooks.
69
:ivar _master_branch_cache: cached result of get_master_branch, see
101
Base directory/url of the branch.
72
# this is really an instance variable - FIXME move it there
77
def control_transport(self):
78
return self._transport
81
def user_transport(self):
82
return self.controldir.user_transport
84
def __init__(self, possible_transports=None):
85
self.tags = self._format.make_tags(self)
86
self._revision_history_cache = None
87
self._revision_id_to_revno_cache = None
88
self._partial_revision_id_to_revno_cache = {}
89
self._partial_revision_history_cache = []
90
self._last_revision_info_cache = None
91
self._master_branch_cache = None
92
self._merge_sorted_revisions_cache = None
93
self._open_hook(possible_transports)
94
hooks = Branch.hooks['open']
98
def _open_hook(self, possible_transports):
99
"""Called by init to allow simpler extension of the base class."""
101
def _activate_fallback_location(self, url, possible_transports):
102
"""Activate the branch/repository from url as a fallback repository."""
103
for existing_fallback_repo in self.repository._fallback_repositories:
104
if existing_fallback_repo.user_url == url:
105
# This fallback is already configured. This probably only
106
# happens because ControlDir.sprout is a horrible mess. To
107
# avoid confusing _unstack we don't add this a second time.
108
mutter('duplicate activation of fallback %r on %r', url, self)
110
repo = self._get_fallback_repository(url, possible_transports)
111
if repo.has_same_location(self.repository):
112
raise errors.UnstackableLocationError(self.user_url, url)
113
self.repository.add_fallback_repository(repo)
115
def break_lock(self):
116
"""Break a lock if one is present from another instance.
118
Uses the ui factory to ask for confirmation if the lock may be from
121
This will probe the repository for its lock as well.
123
self.control_files.break_lock()
124
self.repository.break_lock()
125
master = self.get_master_branch()
126
if master is not None:
129
def _check_stackable_repo(self):
130
if not self.repository._format.supports_external_lookups:
131
raise errors.UnstackableRepositoryFormat(
132
self.repository._format, self.repository.base)
134
def _extend_partial_history(self, stop_index=None, stop_revision=None):
135
"""Extend the partial history to include a given index
137
If a stop_index is supplied, stop when that index has been reached.
138
If a stop_revision is supplied, stop when that revision is
139
encountered. Otherwise, stop when the beginning of history is
142
:param stop_index: The index which should be present. When it is
143
present, history extension will stop.
144
:param stop_revision: The revision id which should be present. When
145
it is encountered, history extension will stop.
147
if len(self._partial_revision_history_cache) == 0:
148
self._partial_revision_history_cache = [self.last_revision()]
149
repository._iter_for_revno(
150
self.repository, self._partial_revision_history_cache,
151
stop_index=stop_index, stop_revision=stop_revision)
152
if self._partial_revision_history_cache[-1] == \
153
_mod_revision.NULL_REVISION:
154
self._partial_revision_history_cache.pop()
156
def _get_check_refs(self):
157
"""Get the references needed for check().
161
revid = self.last_revision()
162
return [('revision-existence', revid), ('lefthand-distance', revid)]
165
def open(base, _unsupported=False, possible_transports=None):
166
"""Open the branch rooted at base.
168
For instance, if the branch is at URL/.bzr/branch,
169
Branch.open(URL) -> a Branch instance.
171
control = controldir.ControlDir.open(
172
base, possible_transports=possible_transports,
173
_unsupported=_unsupported)
174
return control.open_branch(
175
unsupported=_unsupported,
176
possible_transports=possible_transports)
179
def open_from_transport(transport, name=None, _unsupported=False,
180
possible_transports=None):
181
"""Open the branch rooted at transport"""
182
control = controldir.ControlDir.open_from_transport(
183
transport, _unsupported)
184
return control.open_branch(
185
name=name, unsupported=_unsupported,
186
possible_transports=possible_transports)
189
def open_containing(url, possible_transports=None):
105
def __init__(self, *ignored, **ignored_too):
106
raise NotImplementedError('The Branch class is abstract')
109
def open_downlevel(base):
110
"""Open a branch which may be of an old format.
112
Only local branches are supported."""
113
return BzrBranch(get_transport(base), relax_version_check=True)
117
"""Open an existing branch, rooted at 'base' (url)"""
118
t = get_transport(base)
119
mutter("trying to open %r with transport %r", base, t)
123
def open_containing(url):
190
124
"""Open an existing branch which contains url.
192
126
This probes for a branch at url, and searches upwards from there.
194
128
Basically we keep looking up until we find the control directory or
195
129
run into the root. If there isn't one, raises NotBranchError.
196
If there is one and it is either an unrecognised format or an
197
unsupported format, UnknownFormatError or UnsupportedFormatError are
198
raised. If there is one, it is returned, along with the unused portion
201
control, relpath = controldir.ControlDir.open_containing(
202
url, possible_transports)
203
branch = control.open_branch(possible_transports=possible_transports)
204
return (branch, relpath)
206
def _push_should_merge_tags(self):
207
"""Should _basic_push merge this branch's tags into the target?
209
The default implementation returns False if this branch has no tags,
210
and True the rest of the time. Subclasses may override this.
212
return self.supports_tags() and self.tags.get_tag_dict()
214
def get_config(self):
215
"""Get a breezy.config.BranchConfig for this Branch.
217
This can then be used to get and set configuration options for the
220
:return: A breezy.config.BranchConfig.
222
return _mod_config.BranchConfig(self)
224
def get_config_stack(self):
225
"""Get a breezy.config.BranchStack for this Branch.
227
This can then be used to get and set configuration options for the
230
:return: A breezy.config.BranchStack.
232
return _mod_config.BranchStack(self)
234
def store_uncommitted(self, creator):
235
"""Store uncommitted changes from a ShelfCreator.
237
:param creator: The ShelfCreator containing uncommitted changes, or
238
None to delete any stored changes.
239
:raises: ChangesAlreadyStored if the branch already has changes.
241
raise NotImplementedError(self.store_uncommitted)
243
def get_unshelver(self, tree):
244
"""Return a shelf.Unshelver for this branch and tree.
246
:param tree: The tree to use to construct the Unshelver.
247
:return: an Unshelver or None if no changes are stored.
249
raise NotImplementedError(self.get_unshelver)
251
def _get_fallback_repository(self, url, possible_transports):
252
"""Get the repository we fallback to at url."""
253
url = urlutils.join(self.base, url)
254
a_branch = Branch.open(url, possible_transports=possible_transports)
255
return a_branch.repository
257
def _get_nick(self, local=False, possible_transports=None):
258
config = self.get_config()
259
# explicit overrides master, but don't look for master if local is True
260
if not local and not config.has_explicit_nickname():
130
If there is one, it is returned, along with the unused portion of url.
132
t = get_transport(url)
262
master = self.get_master_branch(possible_transports)
263
if master and self.user_url == master.user_url:
264
raise errors.RecursiveBind(self.user_url)
265
if master is not None:
266
# return the master branch value
268
except errors.RecursiveBind as e:
270
except errors.BzrError as e:
271
# Silently fall back to local implicit nick if the master is
273
mutter("Could not connect to bound branch, "
274
"falling back to local nick.\n " + str(e))
275
return config.get_nickname()
135
return BzrBranch(t), t.relpath(url)
136
except NotBranchError:
138
new_t = t.clone('..')
139
if new_t.base == t.base:
140
# reached the root, whatever that may be
141
raise NotBranchError(path=url)
145
def initialize(base):
146
"""Create a new branch, rooted at 'base' (url)"""
147
t = get_transport(base)
148
return BzrBranch(t, init=True)
150
def setup_caching(self, cache_root):
151
"""Subclasses that care about caching should override this, and set
152
up cached stores located under cache_root.
154
self.cache_root = cache_root
157
cfg = self.tree_config()
158
return cfg.get_option(u"nickname", default=self.base.split('/')[-1])
277
160
def _set_nick(self, nick):
278
self.get_config().set_user_option('nickname', nick, warn_masked=True)
161
cfg = self.tree_config()
162
cfg.set_option(nick, "nickname")
163
assert cfg.get_option("nickname") == nick
280
165
nick = property(_get_nick, _set_nick)
283
raise NotImplementedError(self.is_locked)
285
def _lefthand_history(self, revision_id, last_rev=None,
287
if 'evil' in debug.debug_flags:
288
mutter_callsite(4, "_lefthand_history scales with history.")
289
# stop_revision must be a descendant of last_revision
290
graph = self.repository.get_graph()
291
if last_rev is not None:
292
if not graph.is_ancestor(last_rev, revision_id):
293
# our previous tip is not merged into stop_revision
294
raise errors.DivergedBranches(self, other_branch)
295
# make a new revision history from the graph
296
parents_map = graph.get_parent_map([revision_id])
297
if revision_id not in parents_map:
298
raise errors.NoSuchRevision(self, revision_id)
299
current_rev_id = revision_id
301
check_not_reserved_id = _mod_revision.check_not_reserved_id
302
# Do not include ghosts or graph origin in revision_history
303
while (current_rev_id in parents_map
304
and len(parents_map[current_rev_id]) > 0):
305
check_not_reserved_id(current_rev_id)
306
new_history.append(current_rev_id)
307
current_rev_id = parents_map[current_rev_id][0]
308
parents_map = graph.get_parent_map([current_rev_id])
309
new_history.reverse()
312
def lock_write(self, token=None):
313
"""Lock the branch for write operations.
315
:param token: A token to permit reacquiring a previously held and
317
:return: A BranchWriteLockResult.
167
def push_stores(self, branch_to):
168
"""Copy the content of this branches store to branch_to."""
169
raise NotImplementedError('push_stores is abstract')
171
def get_transaction(self):
172
"""Return the current active transaction.
174
If no transaction is active, this returns a passthrough object
175
for which all data is immediately flushed and no caching happens.
319
raise NotImplementedError(self.lock_write)
177
raise NotImplementedError('get_transaction is abstract')
179
def lock_write(self):
180
raise NotImplementedError('lock_write is abstract')
321
182
def lock_read(self):
322
"""Lock the branch for read operations.
324
:return: A breezy.lock.LogicalLockResult.
326
raise NotImplementedError(self.lock_read)
183
raise NotImplementedError('lock_read is abstract')
328
185
def unlock(self):
329
raise NotImplementedError(self.unlock)
331
def peek_lock_mode(self):
332
"""Return lock mode for the Branch: 'r', 'w' or None"""
333
raise NotImplementedError(self.peek_lock_mode)
335
def get_physical_lock_status(self):
336
raise NotImplementedError(self.get_physical_lock_status)
338
def dotted_revno_to_revision_id(self, revno, _cache_reverse=False):
339
"""Return the revision_id for a dotted revno.
341
:param revno: a tuple like (1,) or (1,1,2)
342
:param _cache_reverse: a private parameter enabling storage
343
of the reverse mapping in a top level cache. (This should
344
only be done in selective circumstances as we want to
345
avoid having the mapping cached multiple times.)
346
:return: the revision_id
347
:raises errors.NoSuchRevision: if the revno doesn't exist
349
with self.lock_read():
350
rev_id = self._do_dotted_revno_to_revision_id(revno)
352
self._partial_revision_id_to_revno_cache[rev_id] = revno
355
def _do_dotted_revno_to_revision_id(self, revno):
356
"""Worker function for dotted_revno_to_revision_id.
358
Subclasses should override this if they wish to
359
provide a more efficient implementation.
363
return self.get_rev_id(revno[0])
364
except errors.RevisionNotPresent as e:
365
raise errors.GhostRevisionsHaveNoRevno(revno[0], e.revision_id)
366
revision_id_to_revno = self.get_revision_id_to_revno_map()
367
revision_ids = [revision_id for revision_id, this_revno
368
in revision_id_to_revno.items()
369
if revno == this_revno]
370
if len(revision_ids) == 1:
371
return revision_ids[0]
373
revno_str = '.'.join(map(str, revno))
374
raise errors.NoSuchRevision(self, revno_str)
376
def revision_id_to_dotted_revno(self, revision_id):
377
"""Given a revision id, return its dotted revno.
379
:return: a tuple like (1,) or (400,1,3).
381
with self.lock_read():
382
return self._do_revision_id_to_dotted_revno(revision_id)
384
def _do_revision_id_to_dotted_revno(self, revision_id):
385
"""Worker function for revision_id_to_revno."""
386
# Try the caches if they are loaded
387
result = self._partial_revision_id_to_revno_cache.get(revision_id)
388
if result is not None:
390
if self._revision_id_to_revno_cache:
391
result = self._revision_id_to_revno_cache.get(revision_id)
393
raise errors.NoSuchRevision(self, revision_id)
394
# Try the mainline as it's optimised
396
revno = self.revision_id_to_revno(revision_id)
398
except errors.NoSuchRevision:
399
# We need to load and use the full revno map after all
400
result = self.get_revision_id_to_revno_map().get(revision_id)
402
raise errors.NoSuchRevision(self, revision_id)
405
def get_revision_id_to_revno_map(self):
406
"""Return the revision_id => dotted revno map.
408
This will be regenerated on demand, but will be cached.
410
:return: A dictionary mapping revision_id => dotted revno.
411
This dictionary should not be modified by the caller.
413
if 'evil' in debug.debug_flags:
415
3, "get_revision_id_to_revno_map scales with ancestry.")
416
with self.lock_read():
417
if self._revision_id_to_revno_cache is not None:
418
mapping = self._revision_id_to_revno_cache
420
mapping = self._gen_revno_map()
421
self._cache_revision_id_to_revno(mapping)
422
# TODO: jam 20070417 Since this is being cached, should we be
424
# I would rather not, and instead just declare that users should
425
# not modify the return value.
428
def _gen_revno_map(self):
429
"""Create a new mapping from revision ids to dotted revnos.
431
Dotted revnos are generated based on the current tip in the revision
433
This is the worker function for get_revision_id_to_revno_map, which
434
just caches the return value.
436
:return: A dictionary mapping revision_id => dotted revno.
438
revision_id_to_revno = {
439
rev_id: revno for rev_id, depth, revno, end_of_merge
440
in self.iter_merge_sorted_revisions()}
441
return revision_id_to_revno
443
def iter_merge_sorted_revisions(self, start_revision_id=None,
444
stop_revision_id=None,
445
stop_rule='exclude', direction='reverse'):
446
"""Walk the revisions for a branch in merge sorted order.
448
Merge sorted order is the output from a merge-aware,
449
topological sort, i.e. all parents come before their
450
children going forward; the opposite for reverse.
452
:param start_revision_id: the revision_id to begin walking from.
453
If None, the branch tip is used.
454
:param stop_revision_id: the revision_id to terminate the walk
455
after. If None, the rest of history is included.
456
:param stop_rule: if stop_revision_id is not None, the precise rule
457
to use for termination:
459
* 'exclude' - leave the stop revision out of the result (default)
460
* 'include' - the stop revision is the last item in the result
461
* 'with-merges' - include the stop revision and all of its
462
merged revisions in the result
463
* 'with-merges-without-common-ancestry' - filter out revisions
464
that are in both ancestries
465
:param direction: either 'reverse' or 'forward':
467
* reverse means return the start_revision_id first, i.e.
468
start at the most recent revision and go backwards in history
469
* forward returns tuples in the opposite order to reverse.
470
Note in particular that forward does *not* do any intelligent
471
ordering w.r.t. depth as some clients of this API may like.
472
(If required, that ought to be done at higher layers.)
474
:return: an iterator over (revision_id, depth, revno, end_of_merge)
477
* revision_id: the unique id of the revision
478
* depth: How many levels of merging deep this node has been
480
* revno_sequence: This field provides a sequence of
481
revision numbers for all revisions. The format is:
482
(REVNO, BRANCHNUM, BRANCHREVNO). BRANCHNUM is the number of the
483
branch that the revno is on. From left to right the REVNO numbers
484
are the sequence numbers within that branch of the revision.
485
* end_of_merge: When True the next node (earlier in history) is
486
part of a different merge.
488
with self.lock_read():
489
# Note: depth and revno values are in the context of the branch so
490
# we need the full graph to get stable numbers, regardless of the
492
if self._merge_sorted_revisions_cache is None:
493
last_revision = self.last_revision()
494
known_graph = self.repository.get_known_graph_ancestry(
496
self._merge_sorted_revisions_cache = known_graph.merge_sort(
498
filtered = self._filter_merge_sorted_revisions(
499
self._merge_sorted_revisions_cache, start_revision_id,
500
stop_revision_id, stop_rule)
501
# Make sure we don't return revisions that are not part of the
502
# start_revision_id ancestry.
503
filtered = self._filter_start_non_ancestors(filtered)
504
if direction == 'reverse':
506
if direction == 'forward':
507
return reversed(list(filtered))
509
raise ValueError('invalid direction %r' % direction)
511
def _filter_merge_sorted_revisions(self, merge_sorted_revisions,
512
start_revision_id, stop_revision_id,
514
"""Iterate over an inclusive range of sorted revisions."""
515
rev_iter = iter(merge_sorted_revisions)
516
if start_revision_id is not None:
517
for node in rev_iter:
519
if rev_id != start_revision_id:
522
# The decision to include the start or not
523
# depends on the stop_rule if a stop is provided
524
# so pop this node back into the iterator
525
rev_iter = itertools.chain(iter([node]), rev_iter)
527
if stop_revision_id is None:
529
for node in rev_iter:
531
yield (rev_id, node.merge_depth, node.revno,
533
elif stop_rule == 'exclude':
534
for node in rev_iter:
536
if rev_id == stop_revision_id:
538
yield (rev_id, node.merge_depth, node.revno,
540
elif stop_rule == 'include':
541
for node in rev_iter:
543
yield (rev_id, node.merge_depth, node.revno,
545
if rev_id == stop_revision_id:
547
elif stop_rule == 'with-merges-without-common-ancestry':
548
# We want to exclude all revisions that are already part of the
549
# stop_revision_id ancestry.
550
graph = self.repository.get_graph()
551
ancestors = graph.find_unique_ancestors(start_revision_id,
553
for node in rev_iter:
555
if rev_id not in ancestors:
557
yield (rev_id, node.merge_depth, node.revno,
559
elif stop_rule == 'with-merges':
560
stop_rev = self.repository.get_revision(stop_revision_id)
561
if stop_rev.parent_ids:
562
left_parent = stop_rev.parent_ids[0]
564
left_parent = _mod_revision.NULL_REVISION
565
# left_parent is the actual revision we want to stop logging at,
566
# since we want to show the merged revisions after the stop_rev too
567
reached_stop_revision_id = False
568
revision_id_whitelist = []
569
for node in rev_iter:
571
if rev_id == left_parent:
572
# reached the left parent after the stop_revision
574
if (not reached_stop_revision_id
575
or rev_id in revision_id_whitelist):
576
yield (rev_id, node.merge_depth, node.revno,
578
if reached_stop_revision_id or rev_id == stop_revision_id:
579
# only do the merged revs of rev_id from now on
580
rev = self.repository.get_revision(rev_id)
582
reached_stop_revision_id = True
583
revision_id_whitelist.extend(rev.parent_ids)
585
raise ValueError('invalid stop_rule %r' % stop_rule)
587
def _filter_start_non_ancestors(self, rev_iter):
588
# If we started from a dotted revno, we want to consider it as a tip
589
# and don't want to yield revisions that are not part of its
590
# ancestry. Given the order guaranteed by the merge sort, we will see
591
# uninteresting descendants of the first parent of our tip before the
594
first = next(rev_iter)
595
except StopIteration:
597
(rev_id, merge_depth, revno, end_of_merge) = first
600
# We start at a mainline revision so by definition, all others
601
# revisions in rev_iter are ancestors
602
for node in rev_iter:
607
pmap = self.repository.get_parent_map([rev_id])
608
parents = pmap.get(rev_id, [])
610
whitelist.update(parents)
612
# If there is no parents, there is nothing of interest left
614
# FIXME: It's hard to test this scenario here as this code is never
615
# called in that case. -- vila 20100322
618
for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
620
if rev_id in whitelist:
621
pmap = self.repository.get_parent_map([rev_id])
622
parents = pmap.get(rev_id, [])
623
whitelist.remove(rev_id)
624
whitelist.update(parents)
626
# We've reached the mainline, there is nothing left to
630
# A revision that is not part of the ancestry of our
633
yield (rev_id, merge_depth, revno, end_of_merge)
635
def leave_lock_in_place(self):
636
"""Tell this branch object not to release the physical lock when this
639
If lock_write doesn't return a token, then this method is not
642
self.control_files.leave_in_place()
644
def dont_leave_lock_in_place(self):
645
"""Tell this branch object to release the physical lock when this
646
object is unlocked, even if it didn't originally acquire it.
648
If lock_write doesn't return a token, then this method is not
651
self.control_files.dont_leave_in_place()
653
def bind(self, other):
654
"""Bind the local branch the other branch.
656
:param other: The branch to bind to
659
raise errors.UpgradeRequired(self.user_url)
661
def get_append_revisions_only(self):
662
"""Whether it is only possible to append revisions to the history.
664
if not self._format.supports_set_append_revisions_only():
666
return self.get_config_stack().get('append_revisions_only')
668
def set_append_revisions_only(self, enabled):
669
if not self._format.supports_set_append_revisions_only():
670
raise errors.UpgradeRequired(self.user_url)
671
self.get_config_stack().set('append_revisions_only', enabled)
673
def fetch(self, from_branch, stop_revision=None, limit=None, lossy=False):
674
"""Copy revisions from from_branch into this branch.
676
:param from_branch: Where to copy from.
677
:param stop_revision: What revision to stop at (None for at the end
679
:param limit: Optional rough limit of revisions to fetch
682
with self.lock_write():
683
return InterBranch.get(from_branch, self).fetch(
684
stop_revision, limit=limit, lossy=lossy)
686
def get_bound_location(self):
687
"""Return the URL of the branch we are bound to.
689
Older format branches cannot bind, please be sure to use a metadir
694
def get_old_bound_location(self):
695
"""Return the URL of the branch we used to be bound to
697
raise errors.UpgradeRequired(self.user_url)
699
def get_commit_builder(self, parents, config_stack=None, timestamp=None,
700
timezone=None, committer=None, revprops=None,
701
revision_id=None, lossy=False):
702
"""Obtain a CommitBuilder for this branch.
704
:param parents: Revision ids of the parents of the new revision.
705
:param config: Optional configuration to use.
706
:param timestamp: Optional timestamp recorded for commit.
707
:param timezone: Optional timezone for timestamp.
708
:param committer: Optional committer to set for commit.
709
:param revprops: Optional dictionary of revision properties.
710
:param revision_id: Optional revision id.
711
:param lossy: Whether to discard data that can not be natively
712
represented, when pushing to a foreign VCS
715
if config_stack is None:
716
config_stack = self.get_config_stack()
718
return self.repository.get_commit_builder(
719
self, parents, config_stack, timestamp, timezone, committer,
720
revprops, revision_id, lossy)
722
def get_master_branch(self, possible_transports=None):
723
"""Return the branch we are bound to.
725
:return: Either a Branch, or None
729
def get_stacked_on_url(self):
730
"""Get the URL this branch is stacked against.
732
:raises NotStacked: If the branch is not stacked.
733
:raises UnstackableBranchFormat: If the branch does not support
736
raise NotImplementedError(self.get_stacked_on_url)
738
def set_last_revision_info(self, revno, revision_id):
739
"""Set the last revision of this branch.
741
The caller is responsible for checking that the revno is correct
742
for this revision id.
744
It may be possible to set the branch last revision to an id not
745
present in the repository. However, branches can also be
746
configured to check constraints on history, in which case this may not
749
raise NotImplementedError(self.set_last_revision_info)
751
def generate_revision_history(self, revision_id, last_rev=None,
753
"""See Branch.generate_revision_history"""
754
with self.lock_write():
755
graph = self.repository.get_graph()
756
(last_revno, last_revid) = self.last_revision_info()
757
known_revision_ids = [
758
(last_revid, last_revno),
759
(_mod_revision.NULL_REVISION, 0),
761
if last_rev is not None:
762
if not graph.is_ancestor(last_rev, revision_id):
763
# our previous tip is not merged into stop_revision
764
raise errors.DivergedBranches(self, other_branch)
765
revno = graph.find_distance_to_null(
766
revision_id, known_revision_ids)
767
self.set_last_revision_info(revno, revision_id)
769
def set_parent(self, url):
770
"""See Branch.set_parent."""
771
# TODO: Maybe delete old location files?
772
# URLs should never be unicode, even on the local fs,
773
# FIXUP this and get_parent in a future branch format bump:
774
# read and rewrite the file. RBC 20060125
776
if isinstance(url, str):
779
except UnicodeEncodeError:
780
raise urlutils.InvalidURL(
781
url, "Urls must be 7-bit ascii, "
782
"use breezy.urlutils.escape")
783
url = urlutils.relative_url(self.base, url)
784
with self.lock_write():
785
self._set_parent_location(url)
787
def set_stacked_on_url(self, url):
788
"""Set the URL this branch is stacked against.
790
:raises UnstackableBranchFormat: If the branch does not support
792
:raises UnstackableRepositoryFormat: If the repository does not support
795
if not self._format.supports_stacking():
796
raise UnstackableBranchFormat(self._format, self.user_url)
797
with self.lock_write():
798
# XXX: Changing from one fallback repository to another does not
799
# check that all the data you need is present in the new fallback.
800
# Possibly it should.
801
self._check_stackable_repo()
804
self.get_stacked_on_url()
805
except (errors.NotStacked, UnstackableBranchFormat,
806
errors.UnstackableRepositoryFormat):
810
self._activate_fallback_location(
811
url, possible_transports=[self.controldir.root_transport])
812
# write this out after the repository is stacked to avoid setting a
813
# stacked config that doesn't work.
814
self._set_config_location('stacked_on_location', url)
817
"""Change a branch to be unstacked, copying data as needed.
819
Don't call this directly, use set_stacked_on_url(None).
821
with ui.ui_factory.nested_progress_bar() as pb:
822
pb.update(gettext("Unstacking"))
823
# The basic approach here is to fetch the tip of the branch,
824
# including all available ghosts, from the existing stacked
825
# repository into a new repository object without the fallbacks.
827
# XXX: See <https://launchpad.net/bugs/397286> - this may not be
828
# correct for CHKMap repostiories
829
old_repository = self.repository
830
if len(old_repository._fallback_repositories) != 1:
831
raise AssertionError(
832
"can't cope with fallback repositories "
833
"of %r (fallbacks: %r)" % (
834
old_repository, old_repository._fallback_repositories))
835
# Open the new repository object.
836
# Repositories don't offer an interface to remove fallback
837
# repositories today; take the conceptually simpler option and just
838
# reopen it. We reopen it starting from the URL so that we
839
# get a separate connection for RemoteRepositories and can
840
# stream from one of them to the other. This does mean doing
841
# separate SSH connection setup, but unstacking is not a
842
# common operation so it's tolerable.
843
new_bzrdir = controldir.ControlDir.open(
844
self.controldir.root_transport.base)
845
new_repository = new_bzrdir.find_repository()
846
if new_repository._fallback_repositories:
847
raise AssertionError(
848
"didn't expect %r to have fallback_repositories"
849
% (self.repository,))
850
# Replace self.repository with the new repository.
851
# Do our best to transfer the lock state (i.e. lock-tokens and
852
# lock count) of self.repository to the new repository.
853
lock_token = old_repository.lock_write().repository_token
854
self.repository = new_repository
855
if isinstance(self, remote.RemoteBranch):
856
# Remote branches can have a second reference to the old
857
# repository that need to be replaced.
858
if self._real_branch is not None:
859
self._real_branch.repository = new_repository
860
self.repository.lock_write(token=lock_token)
861
if lock_token is not None:
862
old_repository.leave_lock_in_place()
863
old_repository.unlock()
864
if lock_token is not None:
865
# XXX: self.repository.leave_lock_in_place() before this
866
# function will not be preserved. Fortunately that doesn't
867
# affect the current default format (2a), and would be a
868
# corner-case anyway.
869
# - Andrew Bennetts, 2010/06/30
870
self.repository.dont_leave_lock_in_place()
874
old_repository.unlock()
875
except errors.LockNotHeld:
878
if old_lock_count == 0:
879
raise AssertionError(
880
'old_repository should have been locked at least once.')
881
for i in range(old_lock_count - 1):
882
self.repository.lock_write()
883
# Fetch from the old repository into the new.
884
with old_repository.lock_read():
885
# XXX: If you unstack a branch while it has a working tree
886
# with a pending merge, the pending-merged revisions will no
887
# longer be present. You can (probably) revert and remerge.
889
tags_to_fetch = set(self.tags.get_reverse_tag_dict())
890
except errors.TagsNotSupported:
891
tags_to_fetch = set()
892
fetch_spec = vf_search.NotInOtherForRevs(
893
self.repository, old_repository,
894
required_ids=[self.last_revision()],
895
if_present_ids=tags_to_fetch, find_ghosts=True).execute()
896
self.repository.fetch(old_repository, fetch_spec=fetch_spec)
898
def _cache_revision_history(self, rev_history):
899
"""Set the cached revision history to rev_history.
901
The revision_history method will use this cache to avoid regenerating
902
the revision history.
904
This API is semi-public; it only for use by subclasses, all other code
905
should consider it to be private.
907
self._revision_history_cache = rev_history
909
def _cache_revision_id_to_revno(self, revision_id_to_revno):
910
"""Set the cached revision_id => revno map to revision_id_to_revno.
912
This API is semi-public; it only for use by subclasses, all other code
913
should consider it to be private.
915
self._revision_id_to_revno_cache = revision_id_to_revno
917
def _clear_cached_state(self):
918
"""Clear any cached data on this branch, e.g. cached revision history.
920
This means the next call to revision_history will need to call
921
_gen_revision_history.
923
This API is semi-public; it is only for use by subclasses, all other
924
code should consider it to be private.
926
self._revision_history_cache = None
927
self._revision_id_to_revno_cache = None
928
self._last_revision_info_cache = None
929
self._master_branch_cache = None
930
self._merge_sorted_revisions_cache = None
931
self._partial_revision_history_cache = []
932
self._partial_revision_id_to_revno_cache = {}
934
def _gen_revision_history(self):
935
"""Return sequence of revision hashes on to this branch.
937
Unlike revision_history, this method always regenerates or rereads the
938
revision history, i.e. it does not cache the result, so repeated calls
941
Concrete subclasses should override this instead of revision_history so
942
that subclasses do not need to deal with caching logic.
944
This API is semi-public; it only for use by subclasses, all other code
945
should consider it to be private.
947
raise NotImplementedError(self._gen_revision_history)
949
def _revision_history(self):
950
if 'evil' in debug.debug_flags:
951
mutter_callsite(3, "revision_history scales with history.")
952
if self._revision_history_cache is not None:
953
history = self._revision_history_cache
955
history = self._gen_revision_history()
956
self._cache_revision_history(history)
186
raise NotImplementedError('unlock is abstract')
188
def abspath(self, name):
189
"""Return absolute filename for something in the branch
191
XXX: Robert Collins 20051017 what is this used for? why is it a branch
192
method and not a tree method.
194
raise NotImplementedError('abspath is abstract')
196
def controlfilename(self, file_or_path):
197
"""Return location relative to branch."""
198
raise NotImplementedError('controlfilename is abstract')
200
def controlfile(self, file_or_path, mode='r'):
201
"""Open a control file for this branch.
203
There are two classes of file in the control directory: text
204
and binary. binary files are untranslated byte streams. Text
205
control files are stored with Unix newlines and in UTF-8, even
206
if the platform or locale defaults are different.
208
Controlfiles should almost never be opened in write mode but
209
rather should be atomically copied and replaced using atomicfile.
211
raise NotImplementedError('controlfile is abstract')
213
def put_controlfile(self, path, f, encode=True):
214
"""Write an entry as a controlfile.
216
:param path: The path to put the file, relative to the .bzr control
218
:param f: A file-like or string object whose contents should be copied.
219
:param encode: If true, encode the contents as utf-8
221
raise NotImplementedError('put_controlfile is abstract')
223
def put_controlfiles(self, files, encode=True):
224
"""Write several entries as controlfiles.
226
:param files: A list of [(path, file)] pairs, where the path is the directory
227
underneath the bzr control directory
228
:param encode: If true, encode the contents as utf-8
230
raise NotImplementedError('put_controlfiles is abstract')
232
def get_root_id(self):
233
"""Return the id of this branches root"""
234
raise NotImplementedError('get_root_id is abstract')
236
def set_root_id(self, file_id):
237
raise NotImplementedError('set_root_id is abstract')
239
def print_file(self, file, revision_id):
240
"""Print `file` to stdout."""
241
raise NotImplementedError('print_file is abstract')
243
def append_revision(self, *revision_ids):
244
raise NotImplementedError('append_revision is abstract')
246
def set_revision_history(self, rev_history):
247
raise NotImplementedError('set_revision_history is abstract')
249
def has_revision(self, revision_id):
250
"""True if this branch has a copy of the revision.
252
This does not necessarily imply the revision is merge
253
or on the mainline."""
254
raise NotImplementedError('has_revision is abstract')
256
def get_revision_xml(self, revision_id):
257
raise NotImplementedError('get_revision_xml is abstract')
259
def get_revision(self, revision_id):
260
"""Return the Revision object for a named revision"""
261
raise NotImplementedError('get_revision is abstract')
263
def get_revision_delta(self, revno):
264
"""Return the delta for one revision.
266
The delta is relative to its mainline predecessor, or the
267
empty tree for revision 1.
269
assert isinstance(revno, int)
270
rh = self.revision_history()
271
if not (1 <= revno <= len(rh)):
272
raise InvalidRevisionNumber(revno)
274
# revno is 1-based; list is 0-based
276
new_tree = self.revision_tree(rh[revno-1])
278
old_tree = EmptyTree()
280
old_tree = self.revision_tree(rh[revno-2])
282
return compare_trees(old_tree, new_tree)
284
def get_revision_sha1(self, revision_id):
285
"""Hash the stored value of a revision, and return it."""
286
raise NotImplementedError('get_revision_sha1 is abstract')
288
def get_ancestry(self, revision_id):
289
"""Return a list of revision-ids integrated by a revision.
291
This currently returns a list, but the ordering is not guaranteed:
294
raise NotImplementedError('get_ancestry is abstract')
296
def get_inventory(self, revision_id):
297
"""Get Inventory object by hash."""
298
raise NotImplementedError('get_inventory is abstract')
300
def get_inventory_xml(self, revision_id):
301
"""Get inventory XML as a file object."""
302
raise NotImplementedError('get_inventory_xml is abstract')
304
def get_inventory_sha1(self, revision_id):
305
"""Return the sha1 hash of the inventory entry."""
306
raise NotImplementedError('get_inventory_sha1 is abstract')
308
def get_revision_inventory(self, revision_id):
309
"""Return inventory of a past revision."""
310
raise NotImplementedError('get_revision_inventory is abstract')
312
def revision_history(self):
313
"""Return sequence of revision hashes on to this branch."""
314
raise NotImplementedError('revision_history is abstract')
960
317
"""Return current revision number for this branch.
1181
474
self.check_real_revno(revno)
1183
476
def check_real_revno(self, revno):
1185
478
Check whether a revno corresponds to a real revision.
1186
479
Zero (the NULL revision) is considered invalid
1188
481
if revno < 1 or revno > self.revno():
1189
raise errors.InvalidRevisionNumber(revno)
1191
def clone(self, to_controldir, revision_id=None, name=None,
1192
repository_policy=None, tag_selector=None):
1193
"""Clone this branch into to_controldir preserving all semantic values.
1195
Most API users will want 'create_clone_on_transport', which creates a
1196
new bzrdir and branch on the fly.
1198
revision_id: if not None, the revision history in the new branch will
1199
be truncated to end with revision_id.
1201
result = to_controldir.create_branch(name=name)
1202
with self.lock_read(), result.lock_write():
1203
if repository_policy is not None:
1204
repository_policy.configure_branch(result)
1205
self.copy_content_into(
1206
result, revision_id=revision_id, tag_selector=tag_selector)
1209
def sprout(self, to_controldir, revision_id=None, repository_policy=None,
1210
repository=None, lossy=False, tag_selector=None):
1211
"""Create a new line of development from the branch, into to_controldir.
1213
to_controldir controls the branch format.
1215
revision_id: if not None, the revision history in the new branch will
1216
be truncated to end with revision_id.
1218
if (repository_policy is not None
1219
and repository_policy.requires_stacking()):
1220
to_controldir._format.require_stacking(_skip_repo=True)
1221
result = to_controldir.create_branch(repository=repository)
1223
raise errors.LossyPushToSameVCS(self, result)
1224
with self.lock_read(), result.lock_write():
1225
if repository_policy is not None:
1226
repository_policy.configure_branch(result)
1227
self.copy_content_into(
1228
result, revision_id=revision_id, tag_selector=tag_selector)
1229
master_url = self.get_bound_location()
1230
if master_url is None:
1231
result.set_parent(self.user_url)
1233
result.set_parent(master_url)
1236
def _synchronize_history(self, destination, revision_id):
1237
"""Synchronize last revision and revision history between branches.
1239
This version is most efficient when the destination is also a
1240
BzrBranch6, but works for BzrBranch5, as long as the destination's
1241
repository contains all the lefthand ancestors of the intended
1242
last_revision. If not, set_last_revision_info will fail.
1244
:param destination: The branch to copy the history into
1245
:param revision_id: The revision-id to truncate history at. May
1246
be None to copy complete history.
1248
source_revno, source_revision_id = self.last_revision_info()
1249
if revision_id is None:
1250
revno, revision_id = source_revno, source_revision_id
1252
graph = self.repository.get_graph()
1254
revno = graph.find_distance_to_null(
1255
revision_id, [(source_revision_id, source_revno)])
1256
except errors.GhostRevisionsHaveNoRevno:
1257
# Default to 1, if we can't find anything else
1259
destination.set_last_revision_info(revno, revision_id)
1261
def copy_content_into(self, destination, revision_id=None, tag_selector=None):
1262
"""Copy the content of self into destination.
1264
revision_id: if not None, the revision history in the new branch will
1265
be truncated to end with revision_id.
1266
tag_selector: Optional callback that receives a tag name
1267
and should return a boolean to indicate whether a tag should be copied
1269
return InterBranch.get(self, destination).copy_content_into(
1270
revision_id=revision_id, tag_selector=tag_selector)
1272
def update_references(self, target):
1273
if not self._format.supports_reference_locations:
1275
return InterBranch.get(self, target).update_references()
1277
def check(self, refs):
1278
"""Check consistency of the branch.
1280
In particular this checks that revisions given in the revision-history
1281
do actually match up in the revision graph, and that they're all
1282
present in the repository.
1284
Callers will typically also want to check the repository.
1286
:param refs: Calculated refs for this branch as specified by
1287
branch._get_check_refs()
1288
:return: A BranchCheckResult.
1290
with self.lock_read():
1291
result = BranchCheckResult(self)
1292
last_revno, last_revision_id = self.last_revision_info()
1293
actual_revno = refs[('lefthand-distance', last_revision_id)]
1294
if actual_revno != last_revno:
1295
result.errors.append(errors.BzrCheckError(
1296
'revno does not match len(mainline) %s != %s' % (
1297
last_revno, actual_revno)))
1298
# TODO: We should probably also check that self.revision_history
1299
# matches the repository for older branch formats.
1300
# If looking for the code that cross-checks repository parents
1301
# against the Graph.iter_lefthand_ancestry output, that is now a
1302
# repository specific check.
1305
def _get_checkout_format(self, lightweight=False):
1306
"""Return the most suitable metadir for a checkout of this branch.
1307
Weaves are used if this branch's repository uses weaves.
1309
format = self.repository.controldir.checkout_metadir()
1310
format.set_branch_format(self._format)
1313
def create_clone_on_transport(self, to_transport, revision_id=None,
1314
stacked_on=None, create_prefix=False,
1315
use_existing_dir=False, no_tree=None,
1317
"""Create a clone of this branch and its bzrdir.
1319
:param to_transport: The transport to clone onto.
1320
:param revision_id: The revision id to use as tip in the new branch.
1321
If None the tip is obtained from this branch.
1322
:param stacked_on: An optional URL to stack the clone on.
1323
:param create_prefix: Create any missing directories leading up to
1325
:param use_existing_dir: Use an existing directory if one exists.
1327
# XXX: Fix the bzrdir API to allow getting the branch back from the
1328
# clone call. Or something. 20090224 RBC/spiv.
1329
# XXX: Should this perhaps clone colocated branches as well,
1330
# rather than just the default branch? 20100319 JRV
1331
if revision_id is None:
1332
revision_id = self.last_revision()
1333
dir_to = self.controldir.clone_on_transport(
1334
to_transport, revision_id=revision_id, stacked_on=stacked_on,
1335
create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1336
no_tree=no_tree, tag_selector=tag_selector)
1337
return dir_to.open_branch()
1339
def create_checkout(self, to_location, revision_id=None,
1340
lightweight=False, accelerator_tree=None,
1341
hardlink=False, recurse_nested=True):
1342
"""Create a checkout of a branch.
1344
:param to_location: The url to produce the checkout at
1345
:param revision_id: The revision to check out
1346
:param lightweight: If True, produce a lightweight checkout, otherwise,
1347
produce a bound branch (heavyweight checkout)
1348
:param accelerator_tree: A tree which can be used for retrieving file
1349
contents more quickly than the revision tree, i.e. a workingtree.
1350
The revision tree will be used for cases where accelerator_tree's
1351
content is different.
1352
:param hardlink: If true, hard-link files from accelerator_tree,
1354
:param recurse_nested: Whether to recurse into nested trees
1355
:return: The tree of the created checkout
1357
t = transport.get_transport(to_location)
1359
format = self._get_checkout_format(lightweight=lightweight)
1361
checkout = format.initialize_on_transport(t)
1362
except errors.AlreadyControlDirError:
1363
# It's fine if the control directory already exists,
1364
# as long as there is no existing branch and working tree.
1365
checkout = controldir.ControlDir.open_from_transport(t)
1367
checkout.open_branch()
1368
except errors.NotBranchError:
1371
raise errors.AlreadyControlDirError(t.base)
1372
if (checkout.control_transport.base
1373
== self.controldir.control_transport.base):
1374
# When checking out to the same control directory,
1375
# always create a lightweight checkout
1379
from_branch = checkout.set_branch_reference(target_branch=self)
1381
policy = checkout.determine_repository_policy()
1382
policy.acquire_repository()
1383
checkout_branch = checkout.create_branch()
1384
checkout_branch.bind(self)
1385
# pull up to the specified revision_id to set the initial
1386
# branch tip correctly, and seed it with history.
1387
checkout_branch.pull(self, stop_revision=revision_id)
1389
tree = checkout.create_workingtree(revision_id,
1390
from_branch=from_branch,
1391
accelerator_tree=accelerator_tree,
1393
basis_tree = tree.basis_tree()
1394
with basis_tree.lock_read():
1395
for path in basis_tree.iter_references():
1396
reference_parent = tree.reference_parent(path)
1397
if reference_parent is None:
1398
warning('Branch location for %s unknown.', path)
1400
reference_parent.create_checkout(
1402
basis_tree.get_reference_revision(path), lightweight)
1405
def reconcile(self, thorough=True):
1406
"""Make sure the data stored in this branch is consistent.
1408
:return: A `ReconcileResult` object.
1410
raise NotImplementedError(self.reconcile)
1412
def supports_tags(self):
1413
return self._format.supports_tags()
1415
def automatic_tag_name(self, revision_id):
1416
"""Try to automatically find the tag name for a revision.
1418
:param revision_id: Revision id of the revision.
1419
:return: A tag name or None if no tag name could be determined.
1421
for hook in Branch.hooks['automatic_tag_name']:
1422
ret = hook(self, revision_id)
1427
def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1429
"""Ensure that revision_b is a descendant of revision_a.
1431
This is a helper function for update_revisions.
1433
:raises: DivergedBranches if revision_b has diverged from revision_a.
1434
:returns: True if revision_b is a descendant of revision_a.
1436
relation = self._revision_relations(revision_a, revision_b, graph)
1437
if relation == 'b_descends_from_a':
482
raise InvalidRevisionNumber(revno)
484
def sign_revision(self, revision_id, gpg_strategy):
485
raise NotImplementedError('sign_revision is abstract')
487
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
488
raise NotImplementedError('store_revision_signature is abstract')
490
class BzrBranch(Branch):
491
"""A branch stored in the actual filesystem.
493
Note that it's "local" in the context of the filesystem; it doesn't
494
really matter if it's on an nfs/smb/afs/coda/... share, as long as
495
it's writable, and can be accessed via the normal filesystem API.
501
If _lock_mode is true, a positive count of the number of times the
505
Lock object from bzrlib.lock.
507
# We actually expect this class to be somewhat short-lived; part of its
508
# purpose is to try to isolate what bits of the branch logic are tied to
509
# filesystem access, so that in a later step, we can extricate them to
510
# a separarte ("storage") class.
514
_inventory_weave = None
516
# Map some sort of prefix into a namespace
517
# stuff like "revno:10", "revid:", etc.
518
# This should match a prefix with a function which accepts
519
REVISION_NAMESPACES = {}
521
def push_stores(self, branch_to):
522
"""See Branch.push_stores."""
523
if (self._branch_format != branch_to._branch_format
524
or self._branch_format != 4):
525
from bzrlib.fetch import greedy_fetch
526
mutter("falling back to fetch logic to push between %s(%s) and %s(%s)",
527
self, self._branch_format, branch_to, branch_to._branch_format)
528
greedy_fetch(to_branch=branch_to, from_branch=self,
529
revision=self.last_revision())
532
store_pairs = ((self.text_store, branch_to.text_store),
533
(self.inventory_store, branch_to.inventory_store),
534
(self.revision_store, branch_to.revision_store))
536
for from_store, to_store in store_pairs:
537
copy_all(from_store, to_store)
538
except UnlistableStore:
539
raise UnlistableBranch(from_store)
541
def __init__(self, transport, init=False,
542
relax_version_check=False):
543
"""Create new branch object at a particular location.
545
transport -- A Transport object, defining how to access files.
547
init -- If True, create new control files in a previously
548
unversioned directory. If False, the branch must already
551
relax_version_check -- If true, the usual check for the branch
552
version is not applied. This is intended only for
553
upgrade/recovery type use; it's not guaranteed that
554
all operations will work on old format branches.
556
In the test suite, creation of new trees is tested using the
557
`ScratchBranch` class.
559
assert isinstance(transport, Transport), \
560
"%r is not a Transport" % transport
561
self._transport = transport
564
self._check_format(relax_version_check)
566
def get_store(name, compressed=True, prefixed=False):
567
# FIXME: This approach of assuming stores are all entirely compressed
568
# or entirely uncompressed is tidy, but breaks upgrade from
569
# some existing branches where there's a mixture; we probably
570
# still want the option to look for both.
571
relpath = self._rel_controlfilename(unicode(name))
572
store = TextStore(self._transport.clone(relpath),
574
compressed=compressed)
575
#if self._transport.should_cache():
576
# cache_path = os.path.join(self.cache_root, name)
577
# os.mkdir(cache_path)
578
# store = bzrlib.store.CachedStore(store, cache_path)
581
def get_weave(name, prefixed=False):
582
relpath = self._rel_controlfilename(unicode(name))
583
ws = WeaveStore(self._transport.clone(relpath), prefixed=prefixed)
584
if self._transport.should_cache():
585
ws.enable_cache = True
588
if self._branch_format == 4:
589
self.inventory_store = get_store('inventory-store')
590
self.text_store = get_store('text-store')
591
self.revision_store = get_store('revision-store')
592
elif self._branch_format == 5:
593
self.control_weaves = get_weave(u'')
594
self.weave_store = get_weave(u'weaves')
595
self.revision_store = get_store(u'revision-store', compressed=False)
596
elif self._branch_format == 6:
597
self.control_weaves = get_weave(u'')
598
self.weave_store = get_weave(u'weaves', prefixed=True)
599
self.revision_store = get_store(u'revision-store', compressed=False,
601
self.revision_store.register_suffix('sig')
602
self._transaction = None
605
return '%s(%r)' % (self.__class__.__name__, self._transport.base)
610
if self._lock_mode or self._lock:
611
# XXX: This should show something every time, and be suitable for
612
# headless operation and embedding
613
warn("branch %r was not explicitly unlocked" % self)
616
# TODO: It might be best to do this somewhere else,
617
# but it is nice for a Branch object to automatically
618
# cache it's information.
619
# Alternatively, we could have the Transport objects cache requests
620
# See the earlier discussion about how major objects (like Branch)
621
# should never expect their __del__ function to run.
622
if hasattr(self, 'cache_root') and self.cache_root is not None:
624
shutil.rmtree(self.cache_root)
627
self.cache_root = None
631
return self._transport.base
634
base = property(_get_base, doc="The URL for the root of this branch.")
636
def _finish_transaction(self):
637
"""Exit the current transaction."""
638
if self._transaction is None:
639
raise errors.LockError('Branch %s is not in a transaction' %
641
transaction = self._transaction
642
self._transaction = None
645
def get_transaction(self):
646
"""See Branch.get_transaction."""
647
if self._transaction is None:
648
return transactions.PassThroughTransaction()
650
return self._transaction
652
def _set_transaction(self, new_transaction):
653
"""Set a new active transaction."""
654
if self._transaction is not None:
655
raise errors.LockError('Branch %s is in a transaction already.' %
657
self._transaction = new_transaction
659
def lock_write(self):
660
#mutter("lock write: %s (%s)", self, self._lock_count)
661
# TODO: Upgrade locking to support using a Transport,
662
# and potentially a remote locking protocol
664
if self._lock_mode != 'w':
665
raise LockError("can't upgrade to a write lock from %r" %
667
self._lock_count += 1
669
self._lock = self._transport.lock_write(
670
self._rel_controlfilename('branch-lock'))
671
self._lock_mode = 'w'
673
self._set_transaction(transactions.PassThroughTransaction())
676
#mutter("lock read: %s (%s)", self, self._lock_count)
678
assert self._lock_mode in ('r', 'w'), \
679
"invalid lock mode %r" % self._lock_mode
680
self._lock_count += 1
682
self._lock = self._transport.lock_read(
683
self._rel_controlfilename('branch-lock'))
684
self._lock_mode = 'r'
686
self._set_transaction(transactions.ReadOnlyTransaction())
687
# 5K may be excessive, but hey, its a knob.
688
self.get_transaction().set_cache_size(5000)
691
#mutter("unlock: %s (%s)", self, self._lock_count)
692
if not self._lock_mode:
693
raise LockError('branch %r is not locked' % (self))
695
if self._lock_count > 1:
696
self._lock_count -= 1
698
self._finish_transaction()
701
self._lock_mode = self._lock_count = None
703
def abspath(self, name):
704
"""See Branch.abspath."""
705
return self._transport.abspath(name)
707
def _rel_controlfilename(self, file_or_path):
708
if not isinstance(file_or_path, basestring):
709
file_or_path = u'/'.join(file_or_path)
710
if file_or_path == '':
712
return bzrlib.transport.urlescape(bzrlib.BZRDIR + u'/' + file_or_path)
714
def controlfilename(self, file_or_path):
715
"""See Branch.controlfilename."""
716
return self._transport.abspath(self._rel_controlfilename(file_or_path))
718
def controlfile(self, file_or_path, mode='r'):
719
"""See Branch.controlfile."""
722
relpath = self._rel_controlfilename(file_or_path)
723
#TODO: codecs.open() buffers linewise, so it was overloaded with
724
# a much larger buffer, do we need to do the same for getreader/getwriter?
726
return self._transport.get(relpath)
728
raise BzrError("Branch.controlfile(mode='wb') is not supported, use put_controlfiles")
730
# XXX: Do we really want errors='replace'? Perhaps it should be
731
# an error, or at least reported, if there's incorrectly-encoded
732
# data inside a file.
733
# <https://launchpad.net/products/bzr/+bug/3823>
734
return codecs.getreader('utf-8')(self._transport.get(relpath), errors='replace')
736
raise BzrError("Branch.controlfile(mode='w') is not supported, use put_controlfiles")
738
raise BzrError("invalid controlfile mode %r" % mode)
740
def put_controlfile(self, path, f, encode=True):
741
"""See Branch.put_controlfile."""
742
self.put_controlfiles([(path, f)], encode=encode)
744
def put_controlfiles(self, files, encode=True):
745
"""See Branch.put_controlfiles."""
748
for path, f in files:
750
if isinstance(f, basestring):
751
f = f.encode('utf-8', 'replace')
753
f = codecs.getwriter('utf-8')(f, errors='replace')
754
path = self._rel_controlfilename(path)
755
ctrl_files.append((path, f))
756
self._transport.put_multi(ctrl_files)
758
def _make_control(self):
759
from bzrlib.inventory import Inventory
760
from bzrlib.weavefile import write_weave_v5
761
from bzrlib.weave import Weave
763
# Create an empty inventory
765
# if we want per-tree root ids then this is the place to set
766
# them; they're not needed for now and so ommitted for
768
bzrlib.xml5.serializer_v5.write_inventory(Inventory(), sio)
769
empty_inv = sio.getvalue()
771
bzrlib.weavefile.write_weave_v5(Weave(), sio)
772
empty_weave = sio.getvalue()
774
dirs = [[], 'revision-store', 'weaves']
776
"This is a Bazaar-NG control directory.\n"
777
"Do not change any files in this directory.\n"),
778
('branch-format', BZR_BRANCH_FORMAT_6),
779
('revision-history', ''),
782
('pending-merges', ''),
783
('inventory', empty_inv),
784
('inventory.weave', empty_weave),
785
('ancestry.weave', empty_weave)
787
cfn = self._rel_controlfilename
788
self._transport.mkdir_multi([cfn(d) for d in dirs])
789
self.put_controlfiles(files)
790
mutter('created control directory in ' + self._transport.base)
792
def _check_format(self, relax_version_check):
793
"""Check this branch format is supported.
795
The format level is stored, as an integer, in
796
self._branch_format for code that needs to check it later.
798
In the future, we might need different in-memory Branch
799
classes to support downlevel branches. But not yet.
802
fmt = self.controlfile('branch-format', 'r').read()
804
raise NotBranchError(path=self.base)
805
mutter("got branch format %r", fmt)
806
if fmt == BZR_BRANCH_FORMAT_6:
807
self._branch_format = 6
808
elif fmt == BZR_BRANCH_FORMAT_5:
809
self._branch_format = 5
810
elif fmt == BZR_BRANCH_FORMAT_4:
811
self._branch_format = 4
813
if (not relax_version_check
814
and self._branch_format not in (5, 6)):
815
raise errors.UnsupportedFormatError(
816
'sorry, branch format %r not supported' % fmt,
817
['use a different bzr version',
818
'or remove the .bzr directory'
819
' and "bzr init" again'])
822
def get_root_id(self):
823
"""See Branch.get_root_id."""
824
inv = self.get_inventory(self.last_revision())
825
return inv.root.file_id
828
def print_file(self, file, revision_id):
829
"""See Branch.print_file."""
830
tree = self.revision_tree(revision_id)
831
# use inventory as it was in that revision
832
file_id = tree.inventory.path2id(file)
835
revno = self.revision_id_to_revno(revision_id)
836
except errors.NoSuchRevision:
837
# TODO: This should not be BzrError,
838
# but NoSuchFile doesn't fit either
839
raise BzrError('%r is not present in revision %s'
840
% (file, revision_id))
842
raise BzrError('%r is not present in revision %s'
844
tree.print_file(file_id)
847
def append_revision(self, *revision_ids):
848
"""See Branch.append_revision."""
849
for revision_id in revision_ids:
850
mutter("add {%s} to revision-history" % revision_id)
851
rev_history = self.revision_history()
852
rev_history.extend(revision_ids)
853
self.set_revision_history(rev_history)
856
def set_revision_history(self, rev_history):
857
"""See Branch.set_revision_history."""
858
old_revision = self.last_revision()
859
new_revision = rev_history[-1]
860
self.put_controlfile('revision-history', '\n'.join(rev_history))
862
self.working_tree().set_last_revision(new_revision, old_revision)
863
except NoWorkingTree:
864
mutter('Unable to set_last_revision without a working tree.')
866
def has_revision(self, revision_id):
867
"""See Branch.has_revision."""
868
return (revision_id is None
869
or self.revision_store.has_id(revision_id))
872
def _get_revision_xml_file(self, revision_id):
873
if not revision_id or not isinstance(revision_id, basestring):
874
raise InvalidRevisionId(revision_id=revision_id, branch=self)
876
return self.revision_store.get(revision_id)
877
except (IndexError, KeyError):
878
raise bzrlib.errors.NoSuchRevision(self, revision_id)
880
def get_revision_xml(self, revision_id):
881
"""See Branch.get_revision_xml."""
882
return self._get_revision_xml_file(revision_id).read()
884
def get_revision(self, revision_id):
885
"""See Branch.get_revision."""
886
xml_file = self._get_revision_xml_file(revision_id)
889
r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
890
except SyntaxError, e:
891
raise bzrlib.errors.BzrError('failed to unpack revision_xml',
895
assert r.revision_id == revision_id
898
def get_revision_sha1(self, revision_id):
899
"""See Branch.get_revision_sha1."""
900
# In the future, revision entries will be signed. At that
901
# point, it is probably best *not* to include the signature
902
# in the revision hash. Because that lets you re-sign
903
# the revision, (add signatures/remove signatures) and still
904
# have all hash pointers stay consistent.
905
# But for now, just hash the contents.
906
return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
908
def get_ancestry(self, revision_id):
909
"""See Branch.get_ancestry."""
910
if revision_id is None:
912
w = self._get_inventory_weave()
913
return [None] + map(w.idx_to_name,
914
w.inclusions([w.lookup(revision_id)]))
916
def _get_inventory_weave(self):
917
return self.control_weaves.get_weave('inventory',
918
self.get_transaction())
920
def get_inventory(self, revision_id):
921
"""See Branch.get_inventory."""
922
xml = self.get_inventory_xml(revision_id)
923
return bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
925
def get_inventory_xml(self, revision_id):
926
"""See Branch.get_inventory_xml."""
928
assert isinstance(revision_id, basestring), type(revision_id)
929
iw = self._get_inventory_weave()
930
return iw.get_text(iw.lookup(revision_id))
932
raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
934
def get_inventory_sha1(self, revision_id):
935
"""See Branch.get_inventory_sha1."""
936
return self.get_revision(revision_id).inventory_sha1
938
def get_revision_inventory(self, revision_id):
939
"""See Branch.get_revision_inventory."""
940
# TODO: Unify this with get_inventory()
941
# bzr 0.0.6 and later imposes the constraint that the inventory_id
942
# must be the same as its revision, so this is trivial.
943
if revision_id == None:
944
# This does not make sense: if there is no revision,
945
# then it is the current tree inventory surely ?!
946
# and thus get_root_id() is something that looks at the last
947
# commit on the branch, and the get_root_id is an inventory check.
948
raise NotImplementedError
949
# return Inventory(self.get_root_id())
951
return self.get_inventory(revision_id)
954
def revision_history(self):
955
"""See Branch.revision_history."""
956
transaction = self.get_transaction()
957
history = transaction.map.find_revision_history()
958
if history is not None:
959
mutter("cache hit for revision-history in %s", self)
961
history = [l.rstrip('\r\n') for l in
962
self.controlfile('revision-history', 'r').readlines()]
963
transaction.map.add_revision_history(history)
964
# this call is disabled because revision_history is
965
# not really an object yet, and the transaction is for objects.
966
# transaction.register_clean(history, precious=True)
969
def update_revisions(self, other, stop_revision=None):
970
"""See Branch.update_revisions."""
971
from bzrlib.fetch import greedy_fetch
972
if stop_revision is None:
973
stop_revision = other.last_revision()
974
### Should this be checking is_ancestor instead of revision_history?
975
if (stop_revision is not None and
976
stop_revision in self.revision_history()):
978
greedy_fetch(to_branch=self, from_branch=other,
979
revision=stop_revision)
980
pullable_revs = self.pullable_revisions(other, stop_revision)
981
if len(pullable_revs) > 0:
982
self.append_revision(*pullable_revs)
984
def pullable_revisions(self, other, stop_revision):
985
"""See Branch.pullable_revisions."""
986
other_revno = other.revision_id_to_revno(stop_revision)
988
return self.missing_revisions(other, other_revno)
989
except DivergedBranches, e:
991
pullable_revs = get_intervening_revisions(self.last_revision(),
993
assert self.last_revision() not in pullable_revs
995
except bzrlib.errors.NotAncestor:
996
if is_ancestor(self.last_revision(), stop_revision, self):
1001
def revision_tree(self, revision_id):
1002
"""See Branch.revision_tree."""
1003
# TODO: refactor this to use an existing revision object
1004
# so we don't need to read it in twice.
1005
if revision_id == None or revision_id == NULL_REVISION:
1008
inv = self.get_revision_inventory(revision_id)
1009
return RevisionTree(self.weave_store, inv, revision_id)
1011
def basis_tree(self):
1012
"""See Branch.basis_tree."""
1014
revision_id = self.revision_history()[-1]
1015
xml = self.working_tree().read_basis_inventory(revision_id)
1016
inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
1017
return RevisionTree(self.weave_store, inv, revision_id)
1018
except (IndexError, NoSuchFile, NoWorkingTree), e:
1019
return self.revision_tree(self.last_revision())
1021
def working_tree(self):
1022
"""See Branch.working_tree."""
1023
from bzrlib.workingtree import WorkingTree
1024
if self._transport.base.find('://') != -1:
1025
raise NoWorkingTree(self.base)
1026
return WorkingTree(self.base, branch=self)
1029
def pull(self, source, overwrite=False):
1030
"""See Branch.pull."""
1033
old_count = len(self.revision_history())
1035
self.update_revisions(source)
1036
except DivergedBranches:
1040
self.set_revision_history(source.revision_history())
1041
new_count = len(self.revision_history())
1042
return new_count - old_count
1046
def get_parent(self):
1047
"""See Branch.get_parent."""
1049
_locs = ['parent', 'pull', 'x-pull']
1052
return self.controlfile(l, 'r').read().strip('\n')
1057
def get_push_location(self):
1058
"""See Branch.get_push_location."""
1059
config = bzrlib.config.BranchConfig(self)
1060
push_loc = config.get_user_option('push_location')
1063
def set_push_location(self, location):
1064
"""See Branch.set_push_location."""
1065
config = bzrlib.config.LocationConfig(self.base)
1066
config.set_user_option('push_location', location)
1069
def set_parent(self, url):
1070
"""See Branch.set_parent."""
1071
# TODO: Maybe delete old location files?
1072
from bzrlib.atomicfile import AtomicFile
1073
f = AtomicFile(self.controlfilename('parent'))
1080
def tree_config(self):
1081
return TreeConfig(self)
1083
def sign_revision(self, revision_id, gpg_strategy):
1084
"""See Branch.sign_revision."""
1085
plaintext = Testament.from_revision(self, revision_id).as_short_text()
1086
self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1089
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1090
"""See Branch.store_revision_signature."""
1091
self.revision_store.add(StringIO(gpg_strategy.sign(plaintext)),
1095
class ScratchBranch(BzrBranch):
1096
"""Special test class: a branch that cleans up after itself.
1098
>>> b = ScratchBranch()
1102
>>> b._transport.__del__()
1107
def __init__(self, files=[], dirs=[], transport=None):
1108
"""Make a test branch.
1110
This creates a temporary directory and runs init-tree in it.
1112
If any files are listed, they are created in the working copy.
1114
if transport is None:
1115
transport = bzrlib.transport.local.ScratchTransport()
1116
super(ScratchBranch, self).__init__(transport, init=True)
1118
super(ScratchBranch, self).__init__(transport)
1121
self._transport.mkdir(d)
1124
self._transport.put(f, 'content of %s' % f)
1129
>>> orig = ScratchBranch(files=["file1", "file2"])
1130
>>> clone = orig.clone()
1131
>>> if os.name != 'nt':
1132
... os.path.samefile(orig.base, clone.base)
1134
... orig.base == clone.base
1137
>>> os.path.isfile(os.path.join(clone.base, "file1"))
1140
from shutil import copytree
1141
from tempfile import mkdtemp
1144
copytree(self.base, base, symlinks=True)
1145
return ScratchBranch(
1146
transport=bzrlib.transport.local.ScratchTransport(base))
1149
######################################################################
1153
def is_control_file(filename):
1154
## FIXME: better check
1155
filename = os.path.normpath(filename)
1156
while filename != '':
1157
head, tail = os.path.split(filename)
1158
## mutter('check %r for control file' % ((head, tail), ))
1159
if tail == bzrlib.BZRDIR:
1439
elif relation == 'diverged':
1440
raise errors.DivergedBranches(self, other_branch)
1441
elif relation == 'a_descends_from_b':
1444
raise AssertionError("invalid relation: %r" % (relation,))
1446
def _revision_relations(self, revision_a, revision_b, graph):
1447
"""Determine the relationship between two revisions.
1449
:returns: One of: 'a_descends_from_b', 'b_descends_from_a', 'diverged'
1451
heads = graph.heads([revision_a, revision_b])
1452
if heads == {revision_b}:
1453
return 'b_descends_from_a'
1454
elif heads == {revision_a, revision_b}:
1455
# These branches have diverged
1457
elif heads == {revision_a}:
1458
return 'a_descends_from_b'
1460
raise AssertionError("invalid heads: %r" % (heads,))
1462
def heads_to_fetch(self):
1463
"""Return the heads that must and that should be fetched to copy this
1464
branch into another repo.
1466
:returns: a 2-tuple of (must_fetch, if_present_fetch). must_fetch is a
1467
set of heads that must be fetched. if_present_fetch is a set of
1468
heads that must be fetched if present, but no error is necessary if
1469
they are not present.
1471
# For bzr native formats must_fetch is just the tip, and
1472
# if_present_fetch are the tags.
1473
must_fetch = {self.last_revision()}
1474
if_present_fetch = set()
1475
if self.get_config_stack().get('branch.fetch_tags'):
1477
if_present_fetch = set(self.tags.get_reverse_tag_dict())
1478
except errors.TagsNotSupported:
1480
must_fetch.discard(_mod_revision.NULL_REVISION)
1481
if_present_fetch.discard(_mod_revision.NULL_REVISION)
1482
return must_fetch, if_present_fetch
1484
def create_memorytree(self):
1485
"""Create a memory tree for this branch.
1487
:return: An in-memory MutableTree instance
1489
return memorytree.MemoryTree.create_on_branch(self)
1492
class BranchFormat(controldir.ControlComponentFormat):
1493
"""An encapsulation of the initialization and open routines for a format.
1495
Formats provide three things:
1496
* An initialization routine,
1497
* a format description
1500
Formats are placed in an dict by their format string for reference
1501
during branch opening. It's not required that these be instances, they
1502
can be classes themselves with class methods - it simply depends on
1503
whether state is needed for a given format or not.
1505
Once a format is deprecated, just deprecate the initialize and open
1506
methods on the format class. Do not deprecate the object, as the
1507
object will be created every time regardless.
1510
def __eq__(self, other):
1511
return self.__class__ is other.__class__
1513
def __ne__(self, other):
1514
return not (self == other)
1516
def get_reference(self, controldir, name=None):
1517
"""Get the target reference of the branch in controldir.
1519
format probing must have been completed before calling
1520
this method - it is assumed that the format of the branch
1521
in controldir is correct.
1523
:param controldir: The controldir to get the branch data from.
1524
:param name: Name of the colocated branch to fetch
1525
:return: None if the branch is not a reference branch.
1530
def set_reference(self, controldir, name, to_branch):
1531
"""Set the target reference of the branch in controldir.
1533
format probing must have been completed before calling
1534
this method - it is assumed that the format of the branch
1535
in controldir is correct.
1537
:param controldir: The controldir to set the branch reference for.
1538
:param name: Name of colocated branch to set, None for default
1539
:param to_branch: branch that the checkout is to reference
1541
raise NotImplementedError(self.set_reference)
1543
def get_format_description(self):
1544
"""Return the short format description for this format."""
1545
raise NotImplementedError(self.get_format_description)
1547
def _run_post_branch_init_hooks(self, controldir, name, branch):
1548
hooks = Branch.hooks['post_branch_init']
1551
params = BranchInitHookParams(self, controldir, name, branch)
1555
def initialize(self, controldir, name=None, repository=None,
1556
append_revisions_only=None):
1557
"""Create a branch of this format in controldir.
1559
:param name: Name of the colocated branch to create.
1561
raise NotImplementedError(self.initialize)
1563
def is_supported(self):
1564
"""Is this format supported?
1566
Supported formats can be initialized and opened.
1567
Unsupported formats may not support initialization or committing or
1568
some other features depending on the reason for not being supported.
1572
def make_tags(self, branch):
1573
"""Create a tags object for branch.
1575
This method is on BranchFormat, because BranchFormats are reflected
1576
over the wire via network_name(), whereas full Branch instances require
1577
multiple VFS method calls to operate at all.
1579
The default implementation returns a disabled-tags instance.
1581
Note that it is normal for branch to be a RemoteBranch when using tags
1584
return _mod_tag.DisabledTags(branch)
1586
def network_name(self):
1587
"""A simple byte string uniquely identifying this format for RPC calls.
1589
MetaDir branch formats use their disk format string to identify the
1590
repository over the wire. All in one formats such as bzr < 0.8, and
1591
foreign formats like svn/git and hg should use some marker which is
1592
unique and immutable.
1594
raise NotImplementedError(self.network_name)
1596
def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
1597
found_repository=None, possible_transports=None):
1598
"""Return the branch object for controldir.
1600
:param controldir: A ControlDir that contains a branch.
1601
:param name: Name of colocated branch to open
1602
:param _found: a private parameter, do not use it. It is used to
1603
indicate if format probing has already be done.
1604
:param ignore_fallbacks: when set, no fallback branches will be opened
1605
(if there are any). Default is to open fallbacks.
1607
raise NotImplementedError(self.open)
1609
def supports_set_append_revisions_only(self):
1610
"""True if this format supports set_append_revisions_only."""
1613
def supports_stacking(self):
1614
"""True if this format records a stacked-on branch."""
1617
def supports_leaving_lock(self):
1618
"""True if this format supports leaving locks in place."""
1619
return False # by default
1622
return self.get_format_description().rstrip()
1624
def supports_tags(self):
1625
"""True if this format supports tags stored in the branch"""
1626
return False # by default
1628
def tags_are_versioned(self):
1629
"""Whether the tag container for this branch versions tags."""
1632
def supports_tags_referencing_ghosts(self):
1633
"""True if tags can reference ghost revisions."""
1636
def supports_store_uncommitted(self):
1637
"""True if uncommitted changes can be stored in this branch."""
1640
def stores_revno(self):
1641
"""True if this branch format store revision numbers."""
1645
class BranchHooks(Hooks):
1646
"""A dictionary mapping hook name to a list of callables for branch hooks.
1648
e.g. ['post_push'] Is the list of items to be called when the
1649
push function is invoked.
1653
"""Create the default hooks.
1655
These are all empty initially, because by default nothing should get
1658
Hooks.__init__(self, "breezy.branch", "Branch.hooks")
1661
"Called with the Branch object that has been opened after a "
1662
"branch is opened.", (1, 8))
1665
"Called after a push operation completes. post_push is called "
1666
"with a breezy.branch.BranchPushResult object and only runs in "
1667
"the bzr client.", (0, 15))
1670
"Called after a pull operation completes. post_pull is called "
1671
"with a breezy.branch.PullResult object and only runs in the "
1672
"bzr client.", (0, 15))
1675
"Called after a commit is calculated but before it is "
1676
"completed. pre_commit is called with (local, master, old_revno, "
1677
"old_revid, future_revno, future_revid, tree_delta, future_tree"
1678
"). old_revid is NULL_REVISION for the first commit to a branch, "
1679
"tree_delta is a TreeDelta object describing changes from the "
1680
"basis revision. hooks MUST NOT modify this delta. "
1681
" future_tree is an in-memory tree obtained from "
1682
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1686
"Called in the bzr client after a commit has completed. "
1687
"post_commit is called with (local, master, old_revno, old_revid, "
1688
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1689
"commit to a branch.", (0, 15))
1692
"Called in the bzr client after an uncommit completes. "
1693
"post_uncommit is called with (local, master, old_revno, "
1694
"old_revid, new_revno, new_revid) where local is the local branch "
1695
"or None, master is the target branch, and an empty branch "
1696
"receives new_revno of 0, new_revid of None.", (0, 15))
1698
'pre_change_branch_tip',
1699
"Called in bzr client and server before a change to the tip of a "
1700
"branch is made. pre_change_branch_tip is called with a "
1701
"breezy.branch.ChangeBranchTipParams. Note that push, pull, "
1702
"commit, uncommit will all trigger this hook.", (1, 6))
1704
'post_change_branch_tip',
1705
"Called in bzr client and server after a change to the tip of a "
1706
"branch is made. post_change_branch_tip is called with a "
1707
"breezy.branch.ChangeBranchTipParams. Note that push, pull, "
1708
"commit, uncommit will all trigger this hook.", (1, 4))
1710
'transform_fallback_location',
1711
"Called when a stacked branch is activating its fallback "
1712
"locations. transform_fallback_location is called with (branch, "
1713
"url), and should return a new url. Returning the same url "
1714
"allows it to be used as-is, returning a different one can be "
1715
"used to cause the branch to stack on a closer copy of that "
1716
"fallback_location. Note that the branch cannot have history "
1717
"accessing methods called on it during this hook because the "
1718
"fallback locations have not been activated. When there are "
1719
"multiple hooks installed for transform_fallback_location, "
1720
"all are called with the url returned from the previous hook."
1721
"The order is however undefined.", (1, 9))
1723
'automatic_tag_name',
1724
"Called to determine an automatic tag name for a revision. "
1725
"automatic_tag_name is called with (branch, revision_id) and "
1726
"should return a tag name or None if no tag name could be "
1727
"determined. The first non-None tag name returned will be used.",
1731
"Called after new branch initialization completes. "
1732
"post_branch_init is called with a "
1733
"breezy.branch.BranchInitHookParams. "
1734
"Note that init, branch and checkout (both heavyweight and "
1735
"lightweight) will all trigger this hook.", (2, 2))
1738
"Called after a checkout switches branch. "
1739
"post_switch is called with a "
1740
"breezy.branch.SwitchHookParams.", (2, 2))
1743
# install the default hooks into the Branch class.
1744
Branch.hooks = BranchHooks()
1747
class ChangeBranchTipParams(object):
1748
"""Object holding parameters passed to `*_change_branch_tip` hooks.
1750
There are 5 fields that hooks may wish to access:
1752
:ivar branch: the branch being changed
1753
:ivar old_revno: revision number before the change
1754
:ivar new_revno: revision number after the change
1755
:ivar old_revid: revision id before the change
1756
:ivar new_revid: revision id after the change
1758
The revid fields are strings. The revno fields are integers.
1761
def __init__(self, branch, old_revno, new_revno, old_revid, new_revid):
1762
"""Create a group of ChangeBranchTip parameters.
1764
:param branch: The branch being changed.
1765
:param old_revno: Revision number before the change.
1766
:param new_revno: Revision number after the change.
1767
:param old_revid: Tip revision id before the change.
1768
:param new_revid: Tip revision id after the change.
1770
self.branch = branch
1771
self.old_revno = old_revno
1772
self.new_revno = new_revno
1773
self.old_revid = old_revid
1774
self.new_revid = new_revid
1776
def __eq__(self, other):
1777
return self.__dict__ == other.__dict__
1780
return "<%s of %s from (%s, %s) to (%s, %s)>" % (
1781
self.__class__.__name__, self.branch,
1782
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1785
class BranchInitHookParams(object):
1786
"""Object holding parameters passed to `*_branch_init` hooks.
1788
There are 4 fields that hooks may wish to access:
1790
:ivar format: the branch format
1791
:ivar bzrdir: the ControlDir where the branch will be/has been initialized
1792
:ivar name: name of colocated branch, if any (or None)
1793
:ivar branch: the branch created
1795
Note that for lightweight checkouts, the bzrdir and format fields refer to
1796
the checkout, hence they are different from the corresponding fields in
1797
branch, which refer to the original branch.
1800
def __init__(self, format, controldir, name, branch):
1801
"""Create a group of BranchInitHook parameters.
1803
:param format: the branch format
1804
:param controldir: the ControlDir where the branch will be/has been
1806
:param name: name of colocated branch, if any (or None)
1807
:param branch: the branch created
1809
Note that for lightweight checkouts, the bzrdir and format fields refer
1810
to the checkout, hence they are different from the corresponding fields
1811
in branch, which refer to the original branch.
1813
self.format = format
1814
self.controldir = controldir
1816
self.branch = branch
1818
def __eq__(self, other):
1819
return self.__dict__ == other.__dict__
1822
return "<%s of %s>" % (self.__class__.__name__, self.branch)
1825
class SwitchHookParams(object):
1826
"""Object holding parameters passed to `*_switch` hooks.
1828
There are 4 fields that hooks may wish to access:
1830
:ivar control_dir: ControlDir of the checkout to change
1831
:ivar to_branch: branch that the checkout is to reference
1832
:ivar force: skip the check for local commits in a heavy checkout
1833
:ivar revision_id: revision ID to switch to (or None)
1836
def __init__(self, control_dir, to_branch, force, revision_id):
1837
"""Create a group of SwitchHook parameters.
1839
:param control_dir: ControlDir of the checkout to change
1840
:param to_branch: branch that the checkout is to reference
1841
:param force: skip the check for local commits in a heavy checkout
1842
:param revision_id: revision ID to switch to (or None)
1844
self.control_dir = control_dir
1845
self.to_branch = to_branch
1847
self.revision_id = revision_id
1849
def __eq__(self, other):
1850
return self.__dict__ == other.__dict__
1853
return "<%s for %s to (%s, %s)>" % (
1854
self.__class__.__name__, self.control_dir, self.to_branch,
1858
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
1859
"""Branch format registry."""
1861
def __init__(self, other_registry=None):
1862
super(BranchFormatRegistry, self).__init__(other_registry)
1863
self._default_format = None
1864
self._default_format_key = None
1866
def get_default(self):
1867
"""Return the current default format."""
1868
if (self._default_format_key is not None
1869
and self._default_format is None):
1870
self._default_format = self.get(self._default_format_key)
1871
return self._default_format
1873
def set_default(self, format):
1874
"""Set the default format."""
1875
self._default_format = format
1876
self._default_format_key = None
1878
def set_default_key(self, format_string):
1879
"""Set the default format by its format string."""
1880
self._default_format_key = format_string
1881
self._default_format = None
1884
network_format_registry = registry.FormatRegistry()
1885
"""Registry of formats indexed by their network name.
1887
The network name for a branch format is an identifier that can be used when
1888
referring to formats with smart server operations. See
1889
BranchFormat.network_name() for more detail.
1892
format_registry = BranchFormatRegistry(network_format_registry)
1895
# formats which have no format string are not discoverable
1896
# and not independently creatable, so are not registered.
1897
format_registry.register_lazy(
1898
b"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory",
1900
format_registry.register_lazy(
1901
b"Bazaar Branch Format 6 (bzr 0.15)\n",
1902
"breezy.bzr.branch", "BzrBranchFormat6")
1903
format_registry.register_lazy(
1904
b"Bazaar Branch Format 7 (needs bzr 1.6)\n",
1905
"breezy.bzr.branch", "BzrBranchFormat7")
1906
format_registry.register_lazy(
1907
b"Bazaar Branch Format 8 (needs bzr 1.15)\n",
1908
"breezy.bzr.branch", "BzrBranchFormat8")
1909
format_registry.register_lazy(
1910
b"Bazaar-NG Branch Reference Format 1\n",
1911
"breezy.bzr.branch", "BranchReferenceFormat")
1913
format_registry.set_default_key(b"Bazaar Branch Format 7 (needs bzr 1.6)\n")
1916
class BranchWriteLockResult(LogicalLockResult):
1917
"""The result of write locking a branch.
1919
:ivar token: The token obtained from the underlying branch lock, or
1921
:ivar unlock: A callable which will unlock the lock.
1925
return "BranchWriteLockResult(%r, %r)" % (self.unlock, self.token)
1928
######################################################################
1929
# results of operations
1932
class _Result(object):
1934
def _show_tag_conficts(self, to_file):
1935
if not getattr(self, 'tag_conflicts', None):
1937
to_file.write('Conflicting tags:\n')
1938
for name, value1, value2 in self.tag_conflicts:
1939
to_file.write(' %s\n' % (name, ))
1942
class PullResult(_Result):
1943
"""Result of a Branch.pull operation.
1945
:ivar old_revno: Revision number before pull.
1946
:ivar new_revno: Revision number after pull.
1947
:ivar old_revid: Tip revision id before pull.
1948
:ivar new_revid: Tip revision id after pull.
1949
:ivar source_branch: Source (local) branch object. (read locked)
1950
:ivar master_branch: Master branch of the target, or the target if no
1952
:ivar local_branch: target branch if there is a Master, else None
1953
:ivar target_branch: Target/destination branch object. (write locked)
1954
:ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
1955
:ivar tag_updates: A dict with new tags, see BasicTags.merge_to
1958
def report(self, to_file):
1959
tag_conflicts = getattr(self, "tag_conflicts", None)
1960
tag_updates = getattr(self, "tag_updates", None)
1962
if self.old_revid != self.new_revid:
1963
to_file.write('Now on revision %d.\n' % self.new_revno)
1965
to_file.write('%d tag(s) updated.\n' % len(tag_updates))
1966
if self.old_revid == self.new_revid and not tag_updates:
1967
if not tag_conflicts:
1968
to_file.write('No revisions or tags to pull.\n')
1970
to_file.write('No revisions to pull.\n')
1971
self._show_tag_conficts(to_file)
1974
class BranchPushResult(_Result):
1975
"""Result of a Branch.push operation.
1977
:ivar old_revno: Revision number (eg 10) of the target before push.
1978
:ivar new_revno: Revision number (eg 12) of the target after push.
1979
:ivar old_revid: Tip revision id (eg joe@foo.com-1234234-aoeua34) of target
1981
:ivar new_revid: Tip revision id (eg joe@foo.com-5676566-boa234a) of target
1983
:ivar source_branch: Source branch object that the push was from. This is
1984
read locked, and generally is a local (and thus low latency) branch.
1985
:ivar master_branch: If target is a bound branch, the master branch of
1986
target, or target itself. Always write locked.
1987
:ivar target_branch: The direct Branch where data is being sent (write
1989
:ivar local_branch: If the target is a bound branch this will be the
1990
target, otherwise it will be None.
1993
def report(self, to_file):
1994
# TODO: This function gets passed a to_file, but then
1995
# ignores it and calls note() instead. This is also
1996
# inconsistent with PullResult(), which writes to stdout.
1997
# -- JRV20110901, bug #838853
1998
tag_conflicts = getattr(self, "tag_conflicts", None)
1999
tag_updates = getattr(self, "tag_updates", None)
2001
if self.old_revid != self.new_revid:
2002
if self.new_revno is not None:
2003
note(gettext('Pushed up to revision %d.'),
2006
note(gettext('Pushed up to revision id %s.'),
2007
self.new_revid.decode('utf-8'))
2009
note(ngettext('%d tag updated.', '%d tags updated.',
2010
len(tag_updates)) % len(tag_updates))
2011
if self.old_revid == self.new_revid and not tag_updates:
2012
if not tag_conflicts:
2013
note(gettext('No new revisions or tags to push.'))
2015
note(gettext('No new revisions to push.'))
2016
self._show_tag_conficts(to_file)
2019
class BranchCheckResult(object):
2020
"""Results of checking branch consistency.
2025
def __init__(self, branch):
2026
self.branch = branch
2029
def report_results(self, verbose):
2030
"""Report the check results via trace.note.
2032
:param verbose: Requests more detailed display of what was checked,
2035
note(gettext('checked branch {0} format {1}').format(
2036
self.branch.user_url, self.branch._format))
2037
for error in self.errors:
2038
note(gettext('found error:%s'), error)
2041
class InterBranch(InterObject):
2042
"""This class represents operations taking place between two branches.
2044
Its instances have methods like pull() and push() and contain
2045
references to the source and target repositories these operations
2046
can be carried out on.
2050
"""The available optimised InterBranch types."""
2053
def _get_branch_formats_to_test(klass):
2054
"""Return an iterable of format tuples for testing.
2056
:return: An iterable of (from_format, to_format) to use when testing
2057
this InterBranch class. Each InterBranch class should define this
2060
raise NotImplementedError(klass._get_branch_formats_to_test)
2062
def pull(self, overwrite=False, stop_revision=None,
2063
possible_transports=None, local=False, tag_selector=None):
2064
"""Mirror source into target branch.
2066
The target branch is considered to be 'local', having low latency.
2068
:returns: PullResult instance
2070
raise NotImplementedError(self.pull)
2072
def push(self, overwrite=False, stop_revision=None, lossy=False,
2073
_override_hook_source_branch=None, tag_selector=None):
2074
"""Mirror the source branch into the target branch.
2076
The source branch is considered to be 'local', having low latency.
2078
raise NotImplementedError(self.push)
2080
def copy_content_into(self, revision_id=None, tag_selector=None):
2081
"""Copy the content of source into target
2084
if not None, the revision history in the new branch will
2085
be truncated to end with revision_id.
2086
:param tag_selector: Optional callback that can decide
2087
to copy or not copy tags.
2089
raise NotImplementedError(self.copy_content_into)
2091
def fetch(self, stop_revision=None, limit=None, lossy=False):
2094
:param stop_revision: Last revision to fetch
2095
:param limit: Optional rough limit of revisions to fetch
2096
:return: FetchResult object
2098
raise NotImplementedError(self.fetch)
2100
def update_references(self):
2101
"""Import reference information from source to target.
2103
raise NotImplementedError(self.update_references)
2106
def _fix_overwrite_type(overwrite):
2107
if isinstance(overwrite, bool):
2109
return ["history", "tags"]
2115
class GenericInterBranch(InterBranch):
2116
"""InterBranch implementation that uses public Branch functions."""
2119
def is_compatible(klass, source, target):
2120
# GenericBranch uses the public API, so always compatible
2124
def _get_branch_formats_to_test(klass):
2125
return [(format_registry.get_default(), format_registry.get_default())]
2128
def unwrap_format(klass, format):
2129
if isinstance(format, remote.RemoteBranchFormat):
2130
format._ensure_real()
2131
return format._custom_format
2134
def copy_content_into(self, revision_id=None, tag_selector=None):
2135
"""Copy the content of source into target
2137
revision_id: if not None, the revision history in the new branch will
2138
be truncated to end with revision_id.
2140
with self.source.lock_read(), self.target.lock_write():
2141
self.source._synchronize_history(self.target, revision_id)
2142
self.update_references()
2144
parent = self.source.get_parent()
2145
except errors.InaccessibleParent as e:
2146
mutter('parent was not accessible to copy: %s', str(e))
2149
self.target.set_parent(parent)
2150
if self.source._push_should_merge_tags():
2151
self.source.tags.merge_to(self.target.tags, selector=tag_selector)
2153
def fetch(self, stop_revision=None, limit=None, lossy=False):
2154
if self.target.base == self.source.base:
2156
with self.source.lock_read(), self.target.lock_write():
2157
fetch_spec_factory = fetch.FetchSpecFactory()
2158
fetch_spec_factory.source_branch = self.source
2159
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
2160
fetch_spec_factory.source_repo = self.source.repository
2161
fetch_spec_factory.target_repo = self.target.repository
2162
fetch_spec_factory.target_repo_kind = (
2163
fetch.TargetRepoKinds.PREEXISTING)
2164
fetch_spec_factory.limit = limit
2165
fetch_spec = fetch_spec_factory.make_fetch_spec()
2166
return self.target.repository.fetch(
2167
self.source.repository,
2169
fetch_spec=fetch_spec)
2171
def _update_revisions(self, stop_revision=None, overwrite=False,
2173
with self.source.lock_read(), self.target.lock_write():
2174
other_revno, other_last_revision = self.source.last_revision_info()
2175
stop_revno = None # unknown
2176
if stop_revision is None:
2177
stop_revision = other_last_revision
2178
if _mod_revision.is_null(stop_revision):
2179
# if there are no commits, we're done.
2181
stop_revno = other_revno
2183
# what's the current last revision, before we fetch [and change it
2185
last_rev = _mod_revision.ensure_null(self.target.last_revision())
2186
# we fetch here so that we don't process data twice in the common
2187
# case of having something to pull, and so that the check for
2188
# already merged can operate on the just fetched graph, which will
2189
# be cached in memory.
2190
self.fetch(stop_revision=stop_revision)
2191
# Check to see if one is an ancestor of the other
2194
graph = self.target.repository.get_graph()
2195
if self.target._check_if_descendant_or_diverged(
2196
stop_revision, last_rev, graph, self.source):
2197
# stop_revision is a descendant of last_rev, but we aren't
2198
# overwriting, so we're done.
2200
if stop_revno is None:
2202
graph = self.target.repository.get_graph()
2203
this_revno, this_last_revision = \
2204
self.target.last_revision_info()
2205
stop_revno = graph.find_distance_to_null(
2206
stop_revision, [(other_last_revision, other_revno),
2207
(this_last_revision, this_revno)])
2208
self.target.set_last_revision_info(stop_revno, stop_revision)
2210
def pull(self, overwrite=False, stop_revision=None,
2211
possible_transports=None, run_hooks=True,
2212
_override_hook_target=None, local=False,
2214
"""Pull from source into self, updating my master if any.
2216
:param run_hooks: Private parameter - if false, this branch
2217
is being called because it's the master of the primary branch,
2218
so it should not run its hooks.
2220
with contextlib.ExitStack() as exit_stack:
2221
exit_stack.enter_context(self.target.lock_write())
2222
bound_location = self.target.get_bound_location()
2223
if local and not bound_location:
2224
raise errors.LocalRequiresBoundBranch()
2225
master_branch = None
2226
source_is_master = False
2228
# bound_location comes from a config file, some care has to be
2229
# taken to relate it to source.user_url
2230
normalized = urlutils.normalize_url(bound_location)
2232
relpath = self.source.user_transport.relpath(normalized)
2233
source_is_master = (relpath == '')
2234
except (errors.PathNotChild, urlutils.InvalidURL):
2235
source_is_master = False
2236
if not local and bound_location and not source_is_master:
2237
# not pulling from master, so we need to update master.
2238
master_branch = self.target.get_master_branch(
2239
possible_transports)
2240
exit_stack.enter_context(master_branch.lock_write())
2242
# pull from source into master.
2244
self.source, overwrite, stop_revision, run_hooks=False,
2245
tag_selector=tag_selector)
2247
overwrite, stop_revision, _hook_master=master_branch,
2248
run_hooks=run_hooks,
2249
_override_hook_target=_override_hook_target,
2250
merge_tags_to_master=not source_is_master,
2251
tag_selector=tag_selector)
2253
def push(self, overwrite=False, stop_revision=None, lossy=False,
2254
_override_hook_source_branch=None, tag_selector=None):
2255
"""See InterBranch.push.
2257
This is the basic concrete implementation of push()
2259
:param _override_hook_source_branch: If specified, run the hooks
2260
passing this Branch as the source, rather than self. This is for
2261
use of RemoteBranch, where push is delegated to the underlying
2265
raise errors.LossyPushToSameVCS(self.source, self.target)
2266
# TODO: Public option to disable running hooks - should be trivial but
2270
if _override_hook_source_branch:
2271
result.source_branch = _override_hook_source_branch
2272
for hook in Branch.hooks['post_push']:
2275
with self.source.lock_read(), self.target.lock_write():
2276
bound_location = self.target.get_bound_location()
2277
if bound_location and self.target.base != bound_location:
2278
# there is a master branch.
2280
# XXX: Why the second check? Is it even supported for a branch
2281
# to be bound to itself? -- mbp 20070507
2282
master_branch = self.target.get_master_branch()
2283
with master_branch.lock_write():
2284
# push into the master from the source branch.
2285
master_inter = InterBranch.get(self.source, master_branch)
2286
master_inter._basic_push(
2287
overwrite, stop_revision, tag_selector=tag_selector)
2288
# and push into the target branch from the source. Note
2289
# that we push from the source branch again, because it's
2290
# considered the highest bandwidth repository.
2291
result = self._basic_push(
2292
overwrite, stop_revision, tag_selector=tag_selector)
2293
result.master_branch = master_branch
2294
result.local_branch = self.target
2297
master_branch = None
2299
result = self._basic_push(
2300
overwrite, stop_revision, tag_selector=tag_selector)
2301
# TODO: Why set master_branch and local_branch if there's no
2302
# binding? Maybe cleaner to just leave them unset? -- mbp
2304
result.master_branch = self.target
2305
result.local_branch = None
2309
def _basic_push(self, overwrite, stop_revision, tag_selector=None):
2310
"""Basic implementation of push without bound branches or hooks.
2312
Must be called with source read locked and target write locked.
2314
result = BranchPushResult()
2315
result.source_branch = self.source
2316
result.target_branch = self.target
2317
result.old_revno, result.old_revid = self.target.last_revision_info()
2318
overwrite = _fix_overwrite_type(overwrite)
2319
if result.old_revid != stop_revision:
2320
# We assume that during 'push' this repository is closer than
2322
graph = self.source.repository.get_graph(self.target.repository)
2323
self._update_revisions(
2324
stop_revision, overwrite=("history" in overwrite), graph=graph)
2325
if self.source._push_should_merge_tags():
2326
result.tag_updates, result.tag_conflicts = (
2327
self.source.tags.merge_to(
2328
self.target.tags, "tags" in overwrite, selector=tag_selector))
2329
self.update_references()
2330
result.new_revno, result.new_revid = self.target.last_revision_info()
2333
def _pull(self, overwrite=False, stop_revision=None,
2334
possible_transports=None, _hook_master=None, run_hooks=True,
2335
_override_hook_target=None, local=False,
2336
merge_tags_to_master=True, tag_selector=None):
2339
This function is the core worker, used by GenericInterBranch.pull to
2340
avoid duplication when pulling source->master and source->local.
2342
:param _hook_master: Private parameter - set the branch to
2343
be supplied as the master to pull hooks.
2344
:param run_hooks: Private parameter - if false, this branch
2345
is being called because it's the master of the primary branch,
2346
so it should not run its hooks.
2347
is being called because it's the master of the primary branch,
2348
so it should not run its hooks.
2349
:param _override_hook_target: Private parameter - set the branch to be
2350
supplied as the target_branch to pull hooks.
2351
:param local: Only update the local branch, and not the bound branch.
2353
# This type of branch can't be bound.
2355
raise errors.LocalRequiresBoundBranch()
2356
result = PullResult()
2357
result.source_branch = self.source
2358
if _override_hook_target is None:
2359
result.target_branch = self.target
2361
result.target_branch = _override_hook_target
2362
with self.source.lock_read():
2363
# We assume that during 'pull' the target repository is closer than
2365
graph = self.target.repository.get_graph(self.source.repository)
2366
# TODO: Branch formats should have a flag that indicates
2367
# that revno's are expensive, and pull() should honor that flag.
2369
result.old_revno, result.old_revid = \
2370
self.target.last_revision_info()
2371
overwrite = _fix_overwrite_type(overwrite)
2372
self._update_revisions(
2373
stop_revision, overwrite=("history" in overwrite), graph=graph)
2374
# TODO: The old revid should be specified when merging tags,
2375
# so a tags implementation that versions tags can only
2376
# pull in the most recent changes. -- JRV20090506
2377
result.tag_updates, result.tag_conflicts = (
2378
self.source.tags.merge_to(
2379
self.target.tags, "tags" in overwrite,
2380
ignore_master=not merge_tags_to_master,
2381
selector=tag_selector))
2382
self.update_references()
2383
result.new_revno, result.new_revid = (
2384
self.target.last_revision_info())
2386
result.master_branch = _hook_master
2387
result.local_branch = result.target_branch
2389
result.master_branch = result.target_branch
2390
result.local_branch = None
2392
for hook in Branch.hooks['post_pull']:
2396
def update_references(self):
2397
if not getattr(self.source._format, 'supports_reference_locations', False):
2399
reference_dict = self.source._get_all_reference_info()
2400
if len(reference_dict) == 0:
2402
old_base = self.source.base
2403
new_base = self.target.base
2404
target_reference_dict = self.target._get_all_reference_info()
2405
for tree_path, (branch_location, file_id) in reference_dict.items():
2407
branch_location = urlutils.rebase_url(branch_location,
2409
except urlutils.InvalidRebaseURLs:
2410
# Fall back to absolute URL
2411
branch_location = urlutils.join(old_base, branch_location)
2412
target_reference_dict.setdefault(
2413
tree_path, (branch_location, file_id))
2414
self.target._set_all_reference_info(target_reference_dict)
2417
InterBranch.register_optimiser(GenericInterBranch)
1161
if filename == head: