1# Copyright (C) 2005-2012 Canonical Ltd 2# 3# This program is free software; you can redistribute it and/or modify 4# it under the terms of the GNU General Public License as published by 5# the Free Software Foundation; either version 2 of the License, or 6# (at your option) any later version. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program; if not, write to the Free Software 15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 16 17from .lazy_import import lazy_import 18lazy_import(globals(), """ 19import contextlib 20import itertools 21from breezy import ( 22 config as _mod_config, 23 debug, 24 memorytree, 25 repository, 26 revision as _mod_revision, 27 tag as _mod_tag, 28 transport, 29 ui, 30 urlutils, 31 ) 32from breezy.bzr import ( 33 fetch, 34 remote, 35 vf_search, 36 ) 37from breezy.i18n import gettext, ngettext 38""") 39 40from . import ( 41 controldir, 42 errors, 43 registry, 44 ) 45from .hooks import Hooks 46from .inter import InterObject 47from .lock import LogicalLockResult 48from .trace import mutter, mutter_callsite, note, is_quiet, warning 49 50 51class UnstackableBranchFormat(errors.BzrError): 52 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.") 55 56 def __init__(self, format, url): 57 errors.BzrError.__init__(self) 58 self.format = format 59 self.url = url 60 61 62class Branch(controldir.ControlComponent): 63 """Branch holding a history of revisions. 64 65 :ivar base: 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 70 _clear_cached_state. 71 """ 72 # this is really an instance variable - FIXME move it there 73 # - RBC 20060112 74 base = None 75 76 @property 77 def control_transport(self): 78 return self._transport 79 80 @property 81 def user_transport(self): 82 return self.controldir.user_transport 83 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'] 95 for hook in hooks: 96 hook(self) 97 98 def _open_hook(self, possible_transports): 99 """Called by init to allow simpler extension of the base class.""" 100 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) 109 return 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) 114 115 def break_lock(self): 116 """Break a lock if one is present from another instance. 117 118 Uses the ui factory to ask for confirmation if the lock may be from 119 an active process. 120 121 This will probe the repository for its lock as well. 122 """ 123 self.control_files.break_lock() 124 self.repository.break_lock() 125 master = self.get_master_branch() 126 if master is not None: 127 master.break_lock() 128 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) 133 134 def _extend_partial_history(self, stop_index=None, stop_revision=None): 135 """Extend the partial history to include a given index 136 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 140 reached. 141 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. 146 """ 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() 155 156 def _get_check_refs(self): 157 """Get the references needed for check(). 158 159 See breezy.check. 160 """ 161 revid = self.last_revision() 162 return [('revision-existence', revid), ('lefthand-distance', revid)] 163 164 @staticmethod 165 def open(base, _unsupported=False, possible_transports=None): 166 """Open the branch rooted at base. 167 168 For instance, if the branch is at URL/.bzr/branch, 169 Branch.open(URL) -> a Branch instance. 170 """ 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) 177 178 @staticmethod 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) 187 188 @staticmethod 189 def open_containing(url, possible_transports=None): 190 """Open an existing branch which contains url. 191 192 This probes for a branch at url, and searches upwards from there. 193 194 Basically we keep looking up until we find the control directory or 195 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 199 of url. 200 """ 201 control, relpath = controldir.ControlDir.open_containing( 202 url, possible_transports) 203 branch = control.open_branch(possible_transports=possible_transports) 204 return (branch, relpath) 205 206 def _push_should_merge_tags(self): 207 """Should _basic_push merge this branch's tags into the target? 208 209 The default implementation returns False if this branch has no tags, 210 and True the rest of the time. Subclasses may override this. 211 """ 212 return self.supports_tags() and self.tags.get_tag_dict() 213 214 def get_config(self): 215 """Get a breezy.config.BranchConfig for this Branch. 216 217 This can then be used to get and set configuration options for the 218 branch. 219 220 :return: A breezy.config.BranchConfig. 221 """ 222 return _mod_config.BranchConfig(self) 223 224 def get_config_stack(self): 225 """Get a breezy.config.BranchStack for this Branch. 226 227 This can then be used to get and set configuration options for the 228 branch. 229 230 :return: A breezy.config.BranchStack. 231 """ 232 return _mod_config.BranchStack(self) 233 234 def store_uncommitted(self, creator): 235 """Store uncommitted changes from a ShelfCreator. 236 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. 240 """ 241 raise NotImplementedError(self.store_uncommitted) 242 243 def get_unshelver(self, tree): 244 """Return a shelf.Unshelver for this branch and tree. 245 246 :param tree: The tree to use to construct the Unshelver. 247 :return: an Unshelver or None if no changes are stored. 248 """ 249 raise NotImplementedError(self.get_unshelver) 250 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 256 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(): 261 try: 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 267 return master.nick 268 except errors.RecursiveBind as e: 269 raise e 270 except errors.BzrError as e: 271 # Silently fall back to local implicit nick if the master is 272 # unavailable 273 mutter("Could not connect to bound branch, " 274 "falling back to local nick.\n " + str(e)) 275 return config.get_nickname() 276 277 def _set_nick(self, nick): 278 self.get_config().set_user_option('nickname', nick, warn_masked=True) 279 280 nick = property(_get_nick, _set_nick) 281 282 def is_locked(self): 283 raise NotImplementedError(self.is_locked) 284 285 def _lefthand_history(self, revision_id, last_rev=None, 286 other_branch=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 300 new_history = [] 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() 310 return new_history 311 312 def lock_write(self, token=None): 313 """Lock the branch for write operations. 314 315 :param token: A token to permit reacquiring a previously held and 316 preserved lock. 317 :return: A BranchWriteLockResult. 318 """ 319 raise NotImplementedError(self.lock_write) 320 321 def lock_read(self): 322 """Lock the branch for read operations. 323 324 :return: A breezy.lock.LogicalLockResult. 325 """ 326 raise NotImplementedError(self.lock_read) 327 328 def unlock(self): 329 raise NotImplementedError(self.unlock) 330 331 def peek_lock_mode(self): 332 """Return lock mode for the Branch: 'r', 'w' or None""" 333 raise NotImplementedError(self.peek_lock_mode) 334 335 def get_physical_lock_status(self): 336 raise NotImplementedError(self.get_physical_lock_status) 337 338 def dotted_revno_to_revision_id(self, revno, _cache_reverse=False): 339 """Return the revision_id for a dotted revno. 340 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 348 """ 349 with self.lock_read(): 350 rev_id = self._do_dotted_revno_to_revision_id(revno) 351 if _cache_reverse: 352 self._partial_revision_id_to_revno_cache[rev_id] = revno 353 return rev_id 354 355 def _do_dotted_revno_to_revision_id(self, revno): 356 """Worker function for dotted_revno_to_revision_id. 357 358 Subclasses should override this if they wish to 359 provide a more efficient implementation. 360 """ 361 if len(revno) == 1: 362 try: 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] 372 else: 373 revno_str = '.'.join(map(str, revno)) 374 raise errors.NoSuchRevision(self, revno_str) 375 376 def revision_id_to_dotted_revno(self, revision_id): 377 """Given a revision id, return its dotted revno. 378 379 :return: a tuple like (1,) or (400,1,3). 380 """ 381 with self.lock_read(): 382 return self._do_revision_id_to_dotted_revno(revision_id) 383 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: 389 return result 390 if self._revision_id_to_revno_cache: 391 result = self._revision_id_to_revno_cache.get(revision_id) 392 if result is None: 393 raise errors.NoSuchRevision(self, revision_id) 394 # Try the mainline as it's optimised 395 try: 396 revno = self.revision_id_to_revno(revision_id) 397 return (revno,) 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) 401 if result is None: 402 raise errors.NoSuchRevision(self, revision_id) 403 return result 404 405 def get_revision_id_to_revno_map(self): 406 """Return the revision_id => dotted revno map. 407 408 This will be regenerated on demand, but will be cached. 409 410 :return: A dictionary mapping revision_id => dotted revno. 411 This dictionary should not be modified by the caller. 412 """ 413 if 'evil' in debug.debug_flags: 414 mutter_callsite( 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 419 else: 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 423 # returning a copy? 424 # I would rather not, and instead just declare that users should 425 # not modify the return value. 426 return mapping 427 428 def _gen_revno_map(self): 429 """Create a new mapping from revision ids to dotted revnos. 430 431 Dotted revnos are generated based on the current tip in the revision 432 history. 433 This is the worker function for get_revision_id_to_revno_map, which 434 just caches the return value. 435 436 :return: A dictionary mapping revision_id => dotted revno. 437 """ 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 442 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. 447 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. 451 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: 458 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': 466 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.) 473 474 :return: an iterator over (revision_id, depth, revno, end_of_merge) 475 tuples where: 476 477 * revision_id: the unique id of the revision 478 * depth: How many levels of merging deep this node has been 479 found. 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. 487 """ 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 491 # start_revision_id. 492 if self._merge_sorted_revisions_cache is None: 493 last_revision = self.last_revision() 494 known_graph = self.repository.get_known_graph_ancestry( 495 [last_revision]) 496 self._merge_sorted_revisions_cache = known_graph.merge_sort( 497 last_revision) 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': 505 return filtered 506 if direction == 'forward': 507 return reversed(list(filtered)) 508 else: 509 raise ValueError('invalid direction %r' % direction) 510 511 def _filter_merge_sorted_revisions(self, merge_sorted_revisions, 512 start_revision_id, stop_revision_id, 513 stop_rule): 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: 518 rev_id = node.key 519 if rev_id != start_revision_id: 520 continue 521 else: 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) 526 break 527 if stop_revision_id is None: 528 # Yield everything 529 for node in rev_iter: 530 rev_id = node.key 531 yield (rev_id, node.merge_depth, node.revno, 532 node.end_of_merge) 533 elif stop_rule == 'exclude': 534 for node in rev_iter: 535 rev_id = node.key 536 if rev_id == stop_revision_id: 537 return 538 yield (rev_id, node.merge_depth, node.revno, 539 node.end_of_merge) 540 elif stop_rule == 'include': 541 for node in rev_iter: 542 rev_id = node.key 543 yield (rev_id, node.merge_depth, node.revno, 544 node.end_of_merge) 545 if rev_id == stop_revision_id: 546 return 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, 552 [stop_revision_id]) 553 for node in rev_iter: 554 rev_id = node.key 555 if rev_id not in ancestors: 556 continue 557 yield (rev_id, node.merge_depth, node.revno, 558 node.end_of_merge) 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] 563 else: 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: 570 rev_id = node.key 571 if rev_id == left_parent: 572 # reached the left parent after the stop_revision 573 return 574 if (not reached_stop_revision_id 575 or rev_id in revision_id_whitelist): 576 yield (rev_id, node.merge_depth, node.revno, 577 node.end_of_merge) 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) 581 if rev.parent_ids: 582 reached_stop_revision_id = True 583 revision_id_whitelist.extend(rev.parent_ids) 584 else: 585 raise ValueError('invalid stop_rule %r' % stop_rule) 586 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 592 # tip itself. 593 try: 594 first = next(rev_iter) 595 except StopIteration: 596 return 597 (rev_id, merge_depth, revno, end_of_merge) = first 598 yield first 599 if not merge_depth: 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: 603 yield node 604 605 clean = False 606 whitelist = set() 607 pmap = self.repository.get_parent_map([rev_id]) 608 parents = pmap.get(rev_id, []) 609 if parents: 610 whitelist.update(parents) 611 else: 612 # If there is no parents, there is nothing of interest left 613 614 # FIXME: It's hard to test this scenario here as this code is never 615 # called in that case. -- vila 20100322 616 return 617 618 for (rev_id, merge_depth, revno, end_of_merge) in rev_iter: 619 if not clean: 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) 625 if merge_depth == 0: 626 # We've reached the mainline, there is nothing left to 627 # filter 628 clean = True 629 else: 630 # A revision that is not part of the ancestry of our 631 # starting revision. 632 continue 633 yield (rev_id, merge_depth, revno, end_of_merge) 634 635 def leave_lock_in_place(self): 636 """Tell this branch object not to release the physical lock when this 637 object is unlocked. 638 639 If lock_write doesn't return a token, then this method is not 640 supported. 641 """ 642 self.control_files.leave_in_place() 643 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. 647 648 If lock_write doesn't return a token, then this method is not 649 supported. 650 """ 651 self.control_files.dont_leave_in_place() 652 653 def bind(self, other): 654 """Bind the local branch the other branch. 655 656 :param other: The branch to bind to 657 :type other: Branch 658 """ 659 raise errors.UpgradeRequired(self.user_url) 660 661 def get_append_revisions_only(self): 662 """Whether it is only possible to append revisions to the history. 663 """ 664 if not self._format.supports_set_append_revisions_only(): 665 return False 666 return self.get_config_stack().get('append_revisions_only') 667 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) 672 673 def fetch(self, from_branch, stop_revision=None, limit=None, lossy=False): 674 """Copy revisions from from_branch into this branch. 675 676 :param from_branch: Where to copy from. 677 :param stop_revision: What revision to stop at (None for at the end 678 of the branch. 679 :param limit: Optional rough limit of revisions to fetch 680 :return: None 681 """ 682 with self.lock_write(): 683 return InterBranch.get(from_branch, self).fetch( 684 stop_revision, limit=limit, lossy=lossy) 685 686 def get_bound_location(self): 687 """Return the URL of the branch we are bound to. 688 689 Older format branches cannot bind, please be sure to use a metadir 690 branch. 691 """ 692 return None 693 694 def get_old_bound_location(self): 695 """Return the URL of the branch we used to be bound to 696 """ 697 raise errors.UpgradeRequired(self.user_url) 698 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. 703 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 713 """ 714 715 if config_stack is None: 716 config_stack = self.get_config_stack() 717 718 return self.repository.get_commit_builder( 719 self, parents, config_stack, timestamp, timezone, committer, 720 revprops, revision_id, lossy) 721 722 def get_master_branch(self, possible_transports=None): 723 """Return the branch we are bound to. 724 725 :return: Either a Branch, or None 726 """ 727 return None 728 729 def get_stacked_on_url(self): 730 """Get the URL this branch is stacked against. 731 732 :raises NotStacked: If the branch is not stacked. 733 :raises UnstackableBranchFormat: If the branch does not support 734 stacking. 735 """ 736 raise NotImplementedError(self.get_stacked_on_url) 737 738 def set_last_revision_info(self, revno, revision_id): 739 """Set the last revision of this branch. 740 741 The caller is responsible for checking that the revno is correct 742 for this revision id. 743 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 747 be permitted. 748 """ 749 raise NotImplementedError(self.set_last_revision_info) 750 751 def generate_revision_history(self, revision_id, last_rev=None, 752 other_branch=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), 760 ] 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) 768 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 775 if url is not None: 776 if isinstance(url, str): 777 try: 778 url.encode('ascii') 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) 786 787 def set_stacked_on_url(self, url): 788 """Set the URL this branch is stacked against. 789 790 :raises UnstackableBranchFormat: If the branch does not support 791 stacking. 792 :raises UnstackableRepositoryFormat: If the repository does not support 793 stacking. 794 """ 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() 802 if not url: 803 try: 804 self.get_stacked_on_url() 805 except (errors.NotStacked, UnstackableBranchFormat, 806 errors.UnstackableRepositoryFormat): 807 return 808 self._unstack() 809 else: 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) 815 816 def _unstack(self): 817 """Change a branch to be unstacked, copying data as needed. 818 819 Don't call this directly, use set_stacked_on_url(None). 820 """ 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. 826 # 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() 871 old_lock_count = 0 872 while True: 873 try: 874 old_repository.unlock() 875 except errors.LockNotHeld: 876 break 877 old_lock_count += 1 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. 888 try: 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) 897 898 def _cache_revision_history(self, rev_history): 899 """Set the cached revision history to rev_history. 900 901 The revision_history method will use this cache to avoid regenerating 902 the revision history. 903 904 This API is semi-public; it only for use by subclasses, all other code 905 should consider it to be private. 906 """ 907 self._revision_history_cache = rev_history 908 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. 911 912 This API is semi-public; it only for use by subclasses, all other code 913 should consider it to be private. 914 """ 915 self._revision_id_to_revno_cache = revision_id_to_revno 916 917 def _clear_cached_state(self): 918 """Clear any cached data on this branch, e.g. cached revision history. 919 920 This means the next call to revision_history will need to call 921 _gen_revision_history. 922 923 This API is semi-public; it is only for use by subclasses, all other 924 code should consider it to be private. 925 """ 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 = {} 933 934 def _gen_revision_history(self): 935 """Return sequence of revision hashes on to this branch. 936 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 939 may be expensive. 940 941 Concrete subclasses should override this instead of revision_history so 942 that subclasses do not need to deal with caching logic. 943 944 This API is semi-public; it only for use by subclasses, all other code 945 should consider it to be private. 946 """ 947 raise NotImplementedError(self._gen_revision_history) 948 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 954 else: 955 history = self._gen_revision_history() 956 self._cache_revision_history(history) 957 return list(history) 958 959 def revno(self): 960 """Return current revision number for this branch. 961 962 That is equivalent to the number of revisions committed to 963 this branch. 964 """ 965 return self.last_revision_info()[0] 966 967 def unbind(self): 968 """Older format branches cannot bind or unbind.""" 969 raise errors.UpgradeRequired(self.user_url) 970 971 def last_revision(self): 972 """Return last revision id, or NULL_REVISION.""" 973 return self.last_revision_info()[1] 974 975 def last_revision_info(self): 976 """Return information about the last revision. 977 978 :return: A tuple (revno, revision_id). 979 """ 980 with self.lock_read(): 981 if self._last_revision_info_cache is None: 982 self._last_revision_info_cache = ( 983 self._read_last_revision_info()) 984 return self._last_revision_info_cache 985 986 def _read_last_revision_info(self): 987 raise NotImplementedError(self._read_last_revision_info) 988 989 def import_last_revision_info_and_tags(self, source, revno, revid, 990 lossy=False): 991 """Set the last revision info, importing from another repo if necessary. 992 993 This is used by the bound branch code to upload a revision to 994 the master branch first before updating the tip of the local branch. 995 Revisions referenced by source's tags are also transferred. 996 997 :param source: Source branch to optionally fetch from 998 :param revno: Revision number of the new tip 999 :param revid: Revision id of the new tip 1000 :param lossy: Whether to discard metadata that can not be 1001 natively represented 1002 :return: Tuple with the new revision number and revision id 1003 (should only be different from the arguments when lossy=True) 1004 """ 1005 if not self.repository.has_same_location(source.repository): 1006 self.fetch(source, revid) 1007 self.set_last_revision_info(revno, revid) 1008 return (revno, revid) 1009 1010 def revision_id_to_revno(self, revision_id): 1011 """Given a revision id, return its revno""" 1012 if _mod_revision.is_null(revision_id): 1013 return 0 1014 history = self._revision_history() 1015 try: 1016 return history.index(revision_id) + 1 1017 except ValueError: 1018 raise errors.NoSuchRevision(self, revision_id) 1019 1020 def get_rev_id(self, revno, history=None): 1021 """Find the revision id of the specified revno.""" 1022 with self.lock_read(): 1023 if revno == 0: 1024 return _mod_revision.NULL_REVISION 1025 last_revno, last_revid = self.last_revision_info() 1026 if revno == last_revno: 1027 return last_revid 1028 if revno <= 0 or revno > last_revno: 1029 raise errors.NoSuchRevision(self, revno) 1030 distance_from_last = last_revno - revno 1031 if len(self._partial_revision_history_cache) <= distance_from_last: 1032 self._extend_partial_history(distance_from_last) 1033 return self._partial_revision_history_cache[distance_from_last] 1034 1035 def pull(self, source, overwrite=False, stop_revision=None, 1036 possible_transports=None, *args, **kwargs): 1037 """Mirror source into this branch. 1038 1039 This branch is considered to be 'local', having low latency. 1040 1041 :returns: PullResult instance 1042 """ 1043 return InterBranch.get(source, self).pull( 1044 overwrite=overwrite, stop_revision=stop_revision, 1045 possible_transports=possible_transports, *args, **kwargs) 1046 1047 def push(self, target, overwrite=False, stop_revision=None, lossy=False, 1048 *args, **kwargs): 1049 """Mirror this branch into target. 1050 1051 This branch is considered to be 'local', having low latency. 1052 """ 1053 return InterBranch.get(self, target).push( 1054 overwrite, stop_revision, lossy, *args, **kwargs) 1055 1056 def basis_tree(self): 1057 """Return `Tree` object for last revision.""" 1058 return self.repository.revision_tree(self.last_revision()) 1059 1060 def get_parent(self): 1061 """Return the parent location of the branch. 1062 1063 This is the default location for pull/missing. The usual 1064 pattern is that the user can override it by specifying a 1065 location. 1066 """ 1067 parent = self._get_parent_location() 1068 if parent is None: 1069 return parent 1070 # This is an old-format absolute path to a local branch 1071 # turn it into a url 1072 if parent.startswith('/'): 1073 parent = urlutils.local_path_to_url(parent) 1074 try: 1075 return urlutils.join(self.base[:-1], parent) 1076 except urlutils.InvalidURLJoin: 1077 raise errors.InaccessibleParent(parent, self.user_url) 1078 1079 def _get_parent_location(self): 1080 raise NotImplementedError(self._get_parent_location) 1081 1082 def _set_config_location(self, name, url, config=None, 1083 make_relative=False): 1084 if config is None: 1085 config = self.get_config_stack() 1086 if url is None: 1087 url = '' 1088 elif make_relative: 1089 url = urlutils.relative_url(self.base, url) 1090 config.set(name, url) 1091 1092 def _get_config_location(self, name, config=None): 1093 if config is None: 1094 config = self.get_config_stack() 1095 location = config.get(name) 1096 if location == '': 1097 location = None 1098 return location 1099 1100 def get_child_submit_format(self): 1101 """Return the preferred format of submissions to this branch.""" 1102 return self.get_config_stack().get('child_submit_format') 1103 1104 def get_submit_branch(self): 1105 """Return the submit location of the branch. 1106 1107 This is the default location for bundle. The usual 1108 pattern is that the user can override it by specifying a 1109 location. 1110 """ 1111 return self.get_config_stack().get('submit_branch') 1112 1113 def set_submit_branch(self, location): 1114 """Return the submit location of the branch. 1115 1116 This is the default location for bundle. The usual 1117 pattern is that the user can override it by specifying a 1118 location. 1119 """ 1120 self.get_config_stack().set('submit_branch', location) 1121 1122 def get_public_branch(self): 1123 """Return the public location of the branch. 1124 1125 This is used by merge directives. 1126 """ 1127 return self._get_config_location('public_branch') 1128 1129 def set_public_branch(self, location): 1130 """Return the submit location of the branch. 1131 1132 This is the default location for bundle. The usual 1133 pattern is that the user can override it by specifying a 1134 location. 1135 """ 1136 self._set_config_location('public_branch', location) 1137 1138 def get_push_location(self): 1139 """Return None or the location to push this branch to.""" 1140 return self.get_config_stack().get('push_location') 1141 1142 def set_push_location(self, location): 1143 """Set a new push location for this branch.""" 1144 raise NotImplementedError(self.set_push_location) 1145 1146 def _run_post_change_branch_tip_hooks(self, old_revno, old_revid): 1147 """Run the post_change_branch_tip hooks.""" 1148 hooks = Branch.hooks['post_change_branch_tip'] 1149 if not hooks: 1150 return 1151 new_revno, new_revid = self.last_revision_info() 1152 params = ChangeBranchTipParams( 1153 self, old_revno, new_revno, old_revid, new_revid) 1154 for hook in hooks: 1155 hook(params) 1156 1157 def _run_pre_change_branch_tip_hooks(self, new_revno, new_revid): 1158 """Run the pre_change_branch_tip hooks.""" 1159 hooks = Branch.hooks['pre_change_branch_tip'] 1160 if not hooks: 1161 return 1162 old_revno, old_revid = self.last_revision_info() 1163 params = ChangeBranchTipParams( 1164 self, old_revno, new_revno, old_revid, new_revid) 1165 for hook in hooks: 1166 hook(params) 1167 1168 def update(self): 1169 """Synchronise this branch with the master branch if any. 1170 1171 :return: None or the last_revision pivoted out during the update. 1172 """ 1173 return None 1174 1175 def check_revno(self, revno): 1176 """\ 1177 Check whether a revno corresponds to any revision. 1178 Zero (the NULL revision) is considered valid. 1179 """ 1180 if revno != 0: 1181 self.check_real_revno(revno) 1182 1183 def check_real_revno(self, revno): 1184 """\ 1185 Check whether a revno corresponds to a real revision. 1186 Zero (the NULL revision) is considered invalid 1187 """ 1188 if revno < 1 or revno > self.revno(): 1189 raise errors.InvalidRevisionNumber(revno) 1190 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. 1194 1195 Most API users will want 'create_clone_on_transport', which creates a 1196 new bzrdir and branch on the fly. 1197 1198 revision_id: if not None, the revision history in the new branch will 1199 be truncated to end with revision_id. 1200 """ 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) 1207 return result 1208 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. 1212 1213 to_controldir controls the branch format. 1214 1215 revision_id: if not None, the revision history in the new branch will 1216 be truncated to end with revision_id. 1217 """ 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) 1222 if lossy: 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) 1232 else: 1233 result.set_parent(master_url) 1234 return result 1235 1236 def _synchronize_history(self, destination, revision_id): 1237 """Synchronize last revision and revision history between branches. 1238 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. 1243 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. 1247 """ 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 1251 else: 1252 graph = self.repository.get_graph() 1253 try: 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 1258 revno = 1 1259 destination.set_last_revision_info(revno, revision_id) 1260 1261 def copy_content_into(self, destination, revision_id=None, tag_selector=None): 1262 """Copy the content of self into destination. 1263 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 1268 """ 1269 return InterBranch.get(self, destination).copy_content_into( 1270 revision_id=revision_id, tag_selector=tag_selector) 1271 1272 def update_references(self, target): 1273 if not self._format.supports_reference_locations: 1274 return 1275 return InterBranch.get(self, target).update_references() 1276 1277 def check(self, refs): 1278 """Check consistency of the branch. 1279 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. 1283 1284 Callers will typically also want to check the repository. 1285 1286 :param refs: Calculated refs for this branch as specified by 1287 branch._get_check_refs() 1288 :return: A BranchCheckResult. 1289 """ 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. 1303 return result 1304 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. 1308 """ 1309 format = self.repository.controldir.checkout_metadir() 1310 format.set_branch_format(self._format) 1311 return format 1312 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, 1316 tag_selector=None): 1317 """Create a clone of this branch and its bzrdir. 1318 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 1324 to_transport. 1325 :param use_existing_dir: Use an existing directory if one exists. 1326 """ 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() 1338 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. 1343 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, 1353 where possible. 1354 :param recurse_nested: Whether to recurse into nested trees 1355 :return: The tree of the created checkout 1356 """ 1357 t = transport.get_transport(to_location) 1358 t.ensure_base() 1359 format = self._get_checkout_format(lightweight=lightweight) 1360 try: 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) 1366 try: 1367 checkout.open_branch() 1368 except errors.NotBranchError: 1369 pass 1370 else: 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 1376 lightweight = True 1377 1378 if lightweight: 1379 from_branch = checkout.set_branch_reference(target_branch=self) 1380 else: 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) 1388 from_branch = None 1389 tree = checkout.create_workingtree(revision_id, 1390 from_branch=from_branch, 1391 accelerator_tree=accelerator_tree, 1392 hardlink=hardlink) 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) 1399 continue 1400 reference_parent.create_checkout( 1401 tree.abspath(path), 1402 basis_tree.get_reference_revision(path), lightweight) 1403 return tree 1404 1405 def reconcile(self, thorough=True): 1406 """Make sure the data stored in this branch is consistent. 1407 1408 :return: A `ReconcileResult` object. 1409 """ 1410 raise NotImplementedError(self.reconcile) 1411 1412 def supports_tags(self): 1413 return self._format.supports_tags() 1414 1415 def automatic_tag_name(self, revision_id): 1416 """Try to automatically find the tag name for a revision. 1417 1418 :param revision_id: Revision id of the revision. 1419 :return: A tag name or None if no tag name could be determined. 1420 """ 1421 for hook in Branch.hooks['automatic_tag_name']: 1422 ret = hook(self, revision_id) 1423 if ret is not None: 1424 return ret 1425 return None 1426 1427 def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph, 1428 other_branch): 1429 """Ensure that revision_b is a descendant of revision_a. 1430 1431 This is a helper function for update_revisions. 1432 1433 :raises: DivergedBranches if revision_b has diverged from revision_a. 1434 :returns: True if revision_b is a descendant of revision_a. 1435 """ 1436 relation = self._revision_relations(revision_a, revision_b, graph) 1437 if relation == 'b_descends_from_a': 1438 return True 1439 elif relation == 'diverged': 1440 raise errors.DivergedBranches(self, other_branch) 1441 elif relation == 'a_descends_from_b': 1442 return False 1443 else: 1444 raise AssertionError("invalid relation: %r" % (relation,)) 1445 1446 def _revision_relations(self, revision_a, revision_b, graph): 1447 """Determine the relationship between two revisions. 1448 1449 :returns: One of: 'a_descends_from_b', 'b_descends_from_a', 'diverged' 1450 """ 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 1456 return 'diverged' 1457 elif heads == {revision_a}: 1458 return 'a_descends_from_b' 1459 else: 1460 raise AssertionError("invalid heads: %r" % (heads,)) 1461 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. 1465 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. 1470 """ 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'): 1476 try: 1477 if_present_fetch = set(self.tags.get_reverse_tag_dict()) 1478 except errors.TagsNotSupported: 1479 pass 1480 must_fetch.discard(_mod_revision.NULL_REVISION) 1481 if_present_fetch.discard(_mod_revision.NULL_REVISION) 1482 return must_fetch, if_present_fetch 1483 1484 def create_memorytree(self): 1485 """Create a memory tree for this branch. 1486 1487 :return: An in-memory MutableTree instance 1488 """ 1489 return memorytree.MemoryTree.create_on_branch(self) 1490 1491 1492class BranchFormat(controldir.ControlComponentFormat): 1493 """An encapsulation of the initialization and open routines for a format. 1494 1495 Formats provide three things: 1496 * An initialization routine, 1497 * a format description 1498 * an open routine. 1499 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. 1504 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. 1508 """ 1509 1510 def __eq__(self, other): 1511 return self.__class__ is other.__class__ 1512 1513 def __ne__(self, other): 1514 return not (self == other) 1515 1516 def get_reference(self, controldir, name=None): 1517 """Get the target reference of the branch in controldir. 1518 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. 1522 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. 1526 """ 1527 return None 1528 1529 @classmethod 1530 def set_reference(self, controldir, name, to_branch): 1531 """Set the target reference of the branch in controldir. 1532 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. 1536 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 1540 """ 1541 raise NotImplementedError(self.set_reference) 1542 1543 def get_format_description(self): 1544 """Return the short format description for this format.""" 1545 raise NotImplementedError(self.get_format_description) 1546 1547 def _run_post_branch_init_hooks(self, controldir, name, branch): 1548 hooks = Branch.hooks['post_branch_init'] 1549 if not hooks: 1550 return 1551 params = BranchInitHookParams(self, controldir, name, branch) 1552 for hook in hooks: 1553 hook(params) 1554 1555 def initialize(self, controldir, name=None, repository=None, 1556 append_revisions_only=None): 1557 """Create a branch of this format in controldir. 1558 1559 :param name: Name of the colocated branch to create. 1560 """ 1561 raise NotImplementedError(self.initialize) 1562 1563 def is_supported(self): 1564 """Is this format supported? 1565 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. 1569 """ 1570 return True 1571 1572 def make_tags(self, branch): 1573 """Create a tags object for branch. 1574 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. 1578 1579 The default implementation returns a disabled-tags instance. 1580 1581 Note that it is normal for branch to be a RemoteBranch when using tags 1582 on a RemoteBranch. 1583 """ 1584 return _mod_tag.DisabledTags(branch) 1585 1586 def network_name(self): 1587 """A simple byte string uniquely identifying this format for RPC calls. 1588 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. 1593 """ 1594 raise NotImplementedError(self.network_name) 1595 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. 1599 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. 1606 """ 1607 raise NotImplementedError(self.open) 1608 1609 def supports_set_append_revisions_only(self): 1610 """True if this format supports set_append_revisions_only.""" 1611 return False 1612 1613 def supports_stacking(self): 1614 """True if this format records a stacked-on branch.""" 1615 return False 1616 1617 def supports_leaving_lock(self): 1618 """True if this format supports leaving locks in place.""" 1619 return False # by default 1620 1621 def __str__(self): 1622 return self.get_format_description().rstrip() 1623 1624 def supports_tags(self): 1625 """True if this format supports tags stored in the branch""" 1626 return False # by default 1627 1628 def tags_are_versioned(self): 1629 """Whether the tag container for this branch versions tags.""" 1630 return False 1631 1632 def supports_tags_referencing_ghosts(self): 1633 """True if tags can reference ghost revisions.""" 1634 return True 1635 1636 def supports_store_uncommitted(self): 1637 """True if uncommitted changes can be stored in this branch.""" 1638 return True 1639 1640 def stores_revno(self): 1641 """True if this branch format store revision numbers.""" 1642 return True 1643 1644 1645class BranchHooks(Hooks): 1646 """A dictionary mapping hook name to a list of callables for branch hooks. 1647 1648 e.g. ['post_push'] Is the list of items to be called when the 1649 push function is invoked. 1650 """ 1651 1652 def __init__(self): 1653 """Create the default hooks. 1654 1655 These are all empty initially, because by default nothing should get 1656 notified. 1657 """ 1658 Hooks.__init__(self, "breezy.branch", "Branch.hooks") 1659 self.add_hook( 1660 'open', 1661 "Called with the Branch object that has been opened after a " 1662 "branch is opened.", (1, 8)) 1663 self.add_hook( 1664 'post_push', 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)) 1668 self.add_hook( 1669 'post_pull', 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)) 1673 self.add_hook( 1674 'pre_commit', 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 " 1683 "tree.", (0, 91)) 1684 self.add_hook( 1685 'post_commit', 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)) 1690 self.add_hook( 1691 'post_uncommit', 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)) 1697 self.add_hook( 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)) 1703 self.add_hook( 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)) 1709 self.add_hook( 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)) 1722 self.add_hook( 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.", 1728 (2, 2)) 1729 self.add_hook( 1730 'post_branch_init', 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)) 1736 self.add_hook( 1737 'post_switch', 1738 "Called after a checkout switches branch. " 1739 "post_switch is called with a " 1740 "breezy.branch.SwitchHookParams.", (2, 2)) 1741 1742 1743# install the default hooks into the Branch class. 1744Branch.hooks = BranchHooks() 1745 1746 1747class ChangeBranchTipParams(object): 1748 """Object holding parameters passed to `*_change_branch_tip` hooks. 1749 1750 There are 5 fields that hooks may wish to access: 1751 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 1757 1758 The revid fields are strings. The revno fields are integers. 1759 """ 1760 1761 def __init__(self, branch, old_revno, new_revno, old_revid, new_revid): 1762 """Create a group of ChangeBranchTip parameters. 1763 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. 1769 """ 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 1775 1776 def __eq__(self, other): 1777 return self.__dict__ == other.__dict__ 1778 1779 def __repr__(self): 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) 1783 1784 1785class BranchInitHookParams(object): 1786 """Object holding parameters passed to `*_branch_init` hooks. 1787 1788 There are 4 fields that hooks may wish to access: 1789 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 1794 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. 1798 """ 1799 1800 def __init__(self, format, controldir, name, branch): 1801 """Create a group of BranchInitHook parameters. 1802 1803 :param format: the branch format 1804 :param controldir: the ControlDir where the branch will be/has been 1805 initialized 1806 :param name: name of colocated branch, if any (or None) 1807 :param branch: the branch created 1808 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. 1812 """ 1813 self.format = format 1814 self.controldir = controldir 1815 self.name = name 1816 self.branch = branch 1817 1818 def __eq__(self, other): 1819 return self.__dict__ == other.__dict__ 1820 1821 def __repr__(self): 1822 return "<%s of %s>" % (self.__class__.__name__, self.branch) 1823 1824 1825class SwitchHookParams(object): 1826 """Object holding parameters passed to `*_switch` hooks. 1827 1828 There are 4 fields that hooks may wish to access: 1829 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) 1834 """ 1835 1836 def __init__(self, control_dir, to_branch, force, revision_id): 1837 """Create a group of SwitchHook parameters. 1838 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) 1843 """ 1844 self.control_dir = control_dir 1845 self.to_branch = to_branch 1846 self.force = force 1847 self.revision_id = revision_id 1848 1849 def __eq__(self, other): 1850 return self.__dict__ == other.__dict__ 1851 1852 def __repr__(self): 1853 return "<%s for %s to (%s, %s)>" % ( 1854 self.__class__.__name__, self.control_dir, self.to_branch, 1855 self.revision_id) 1856 1857 1858class BranchFormatRegistry(controldir.ControlComponentFormatRegistry): 1859 """Branch format registry.""" 1860 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 1865 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 1872 1873 def set_default(self, format): 1874 """Set the default format.""" 1875 self._default_format = format 1876 self._default_format_key = None 1877 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 1882 1883 1884network_format_registry = registry.FormatRegistry() 1885"""Registry of formats indexed by their network name. 1886 1887The network name for a branch format is an identifier that can be used when 1888referring to formats with smart server operations. See 1889BranchFormat.network_name() for more detail. 1890""" 1891 1892format_registry = BranchFormatRegistry(network_format_registry) 1893 1894 1895# formats which have no format string are not discoverable 1896# and not independently creatable, so are not registered. 1897format_registry.register_lazy( 1898 b"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory", 1899 "BzrBranchFormat5") 1900format_registry.register_lazy( 1901 b"Bazaar Branch Format 6 (bzr 0.15)\n", 1902 "breezy.bzr.branch", "BzrBranchFormat6") 1903format_registry.register_lazy( 1904 b"Bazaar Branch Format 7 (needs bzr 1.6)\n", 1905 "breezy.bzr.branch", "BzrBranchFormat7") 1906format_registry.register_lazy( 1907 b"Bazaar Branch Format 8 (needs bzr 1.15)\n", 1908 "breezy.bzr.branch", "BzrBranchFormat8") 1909format_registry.register_lazy( 1910 b"Bazaar-NG Branch Reference Format 1\n", 1911 "breezy.bzr.branch", "BranchReferenceFormat") 1912 1913format_registry.set_default_key(b"Bazaar Branch Format 7 (needs bzr 1.6)\n") 1914 1915 1916class BranchWriteLockResult(LogicalLockResult): 1917 """The result of write locking a branch. 1918 1919 :ivar token: The token obtained from the underlying branch lock, or 1920 None. 1921 :ivar unlock: A callable which will unlock the lock. 1922 """ 1923 1924 def __repr__(self): 1925 return "BranchWriteLockResult(%r, %r)" % (self.unlock, self.token) 1926 1927 1928###################################################################### 1929# results of operations 1930 1931 1932class _Result(object): 1933 1934 def _show_tag_conficts(self, to_file): 1935 if not getattr(self, 'tag_conflicts', None): 1936 return 1937 to_file.write('Conflicting tags:\n') 1938 for name, value1, value2 in self.tag_conflicts: 1939 to_file.write(' %s\n' % (name, )) 1940 1941 1942class PullResult(_Result): 1943 """Result of a Branch.pull operation. 1944 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 1951 Master 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 1956 """ 1957 1958 def report(self, to_file): 1959 tag_conflicts = getattr(self, "tag_conflicts", None) 1960 tag_updates = getattr(self, "tag_updates", None) 1961 if not is_quiet(): 1962 if self.old_revid != self.new_revid: 1963 to_file.write('Now on revision %d.\n' % self.new_revno) 1964 if tag_updates: 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') 1969 else: 1970 to_file.write('No revisions to pull.\n') 1971 self._show_tag_conficts(to_file) 1972 1973 1974class BranchPushResult(_Result): 1975 """Result of a Branch.push operation. 1976 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 1980 before the push. 1981 :ivar new_revid: Tip revision id (eg joe@foo.com-5676566-boa234a) of target 1982 after the push. 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 1988 locked). 1989 :ivar local_branch: If the target is a bound branch this will be the 1990 target, otherwise it will be None. 1991 """ 1992 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) 2000 if not is_quiet(): 2001 if self.old_revid != self.new_revid: 2002 if self.new_revno is not None: 2003 note(gettext('Pushed up to revision %d.'), 2004 self.new_revno) 2005 else: 2006 note(gettext('Pushed up to revision id %s.'), 2007 self.new_revid.decode('utf-8')) 2008 if tag_updates: 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.')) 2014 else: 2015 note(gettext('No new revisions to push.')) 2016 self._show_tag_conficts(to_file) 2017 2018 2019class BranchCheckResult(object): 2020 """Results of checking branch consistency. 2021 2022 :see: Branch.check 2023 """ 2024 2025 def __init__(self, branch): 2026 self.branch = branch 2027 self.errors = [] 2028 2029 def report_results(self, verbose): 2030 """Report the check results via trace.note. 2031 2032 :param verbose: Requests more detailed display of what was checked, 2033 if any. 2034 """ 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) 2039 2040 2041class InterBranch(InterObject): 2042 """This class represents operations taking place between two branches. 2043 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. 2047 """ 2048 2049 _optimisers = [] 2050 """The available optimised InterBranch types.""" 2051 2052 @classmethod 2053 def _get_branch_formats_to_test(klass): 2054 """Return an iterable of format tuples for testing. 2055 2056 :return: An iterable of (from_format, to_format) to use when testing 2057 this InterBranch class. Each InterBranch class should define this 2058 method itself. 2059 """ 2060 raise NotImplementedError(klass._get_branch_formats_to_test) 2061 2062 def pull(self, overwrite=False, stop_revision=None, 2063 possible_transports=None, local=False, tag_selector=None): 2064 """Mirror source into target branch. 2065 2066 The target branch is considered to be 'local', having low latency. 2067 2068 :returns: PullResult instance 2069 """ 2070 raise NotImplementedError(self.pull) 2071 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. 2075 2076 The source branch is considered to be 'local', having low latency. 2077 """ 2078 raise NotImplementedError(self.push) 2079 2080 def copy_content_into(self, revision_id=None, tag_selector=None): 2081 """Copy the content of source into target 2082 2083 :param revision_id: 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. 2088 """ 2089 raise NotImplementedError(self.copy_content_into) 2090 2091 def fetch(self, stop_revision=None, limit=None, lossy=False): 2092 """Fetch revisions. 2093 2094 :param stop_revision: Last revision to fetch 2095 :param limit: Optional rough limit of revisions to fetch 2096 :return: FetchResult object 2097 """ 2098 raise NotImplementedError(self.fetch) 2099 2100 def update_references(self): 2101 """Import reference information from source to target. 2102 """ 2103 raise NotImplementedError(self.update_references) 2104 2105 2106def _fix_overwrite_type(overwrite): 2107 if isinstance(overwrite, bool): 2108 if overwrite: 2109 return ["history", "tags"] 2110 else: 2111 return [] 2112 return overwrite 2113 2114 2115class GenericInterBranch(InterBranch): 2116 """InterBranch implementation that uses public Branch functions.""" 2117 2118 @classmethod 2119 def is_compatible(klass, source, target): 2120 # GenericBranch uses the public API, so always compatible 2121 return True 2122 2123 @classmethod 2124 def _get_branch_formats_to_test(klass): 2125 return [(format_registry.get_default(), format_registry.get_default())] 2126 2127 @classmethod 2128 def unwrap_format(klass, format): 2129 if isinstance(format, remote.RemoteBranchFormat): 2130 format._ensure_real() 2131 return format._custom_format 2132 return format 2133 2134 def copy_content_into(self, revision_id=None, tag_selector=None): 2135 """Copy the content of source into target 2136 2137 revision_id: if not None, the revision history in the new branch will 2138 be truncated to end with revision_id. 2139 """ 2140 with self.source.lock_read(), self.target.lock_write(): 2141 self.source._synchronize_history(self.target, revision_id) 2142 self.update_references() 2143 try: 2144 parent = self.source.get_parent() 2145 except errors.InaccessibleParent as e: 2146 mutter('parent was not accessible to copy: %s', str(e)) 2147 else: 2148 if parent: 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) 2152 2153 def fetch(self, stop_revision=None, limit=None, lossy=False): 2154 if self.target.base == self.source.base: 2155 return (0, []) 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, 2168 lossy=lossy, 2169 fetch_spec=fetch_spec) 2170 2171 def _update_revisions(self, stop_revision=None, overwrite=False, 2172 graph=None): 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. 2180 return 2181 stop_revno = other_revno 2182 2183 # what's the current last revision, before we fetch [and change it 2184 # possibly] 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 2192 if not overwrite: 2193 if graph is None: 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. 2199 return 2200 if stop_revno is None: 2201 if graph 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) 2209 2210 def pull(self, overwrite=False, stop_revision=None, 2211 possible_transports=None, run_hooks=True, 2212 _override_hook_target=None, local=False, 2213 tag_selector=None): 2214 """Pull from source into self, updating my master if any. 2215 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. 2219 """ 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 2227 if bound_location: 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) 2231 try: 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()) 2241 if master_branch: 2242 # pull from source into master. 2243 master_branch.pull( 2244 self.source, overwrite, stop_revision, run_hooks=False, 2245 tag_selector=tag_selector) 2246 return self._pull( 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) 2252 2253 def push(self, overwrite=False, stop_revision=None, lossy=False, 2254 _override_hook_source_branch=None, tag_selector=None): 2255 """See InterBranch.push. 2256 2257 This is the basic concrete implementation of push() 2258 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 2262 vfs-based Branch. 2263 """ 2264 if lossy: 2265 raise errors.LossyPushToSameVCS(self.source, self.target) 2266 # TODO: Public option to disable running hooks - should be trivial but 2267 # needs tests. 2268 2269 def _run_hooks(): 2270 if _override_hook_source_branch: 2271 result.source_branch = _override_hook_source_branch 2272 for hook in Branch.hooks['post_push']: 2273 hook(result) 2274 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. 2279 # 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 2295 _run_hooks() 2296 else: 2297 master_branch = None 2298 # no master branch 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 2303 # 20070504 2304 result.master_branch = self.target 2305 result.local_branch = None 2306 _run_hooks() 2307 return result 2308 2309 def _basic_push(self, overwrite, stop_revision, tag_selector=None): 2310 """Basic implementation of push without bound branches or hooks. 2311 2312 Must be called with source read locked and target write locked. 2313 """ 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 2321 # the target. 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() 2331 return result 2332 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): 2337 """See Branch.pull. 2338 2339 This function is the core worker, used by GenericInterBranch.pull to 2340 avoid duplication when pulling source->master and source->local. 2341 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. 2352 """ 2353 # This type of branch can't be bound. 2354 if local: 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 2360 else: 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 2364 # the source one. 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. 2368 # -- JRV20090506 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()) 2385 if _hook_master: 2386 result.master_branch = _hook_master 2387 result.local_branch = result.target_branch 2388 else: 2389 result.master_branch = result.target_branch 2390 result.local_branch = None 2391 if run_hooks: 2392 for hook in Branch.hooks['post_pull']: 2393 hook(result) 2394 return result 2395 2396 def update_references(self): 2397 if not getattr(self.source._format, 'supports_reference_locations', False): 2398 return 2399 reference_dict = self.source._get_all_reference_info() 2400 if len(reference_dict) == 0: 2401 return 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(): 2406 try: 2407 branch_location = urlutils.rebase_url(branch_location, 2408 old_base, new_base) 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) 2415 2416 2417InterBranch.register_optimiser(GenericInterBranch) 2418