17
17
# Original author: David Allouche
19
from bzrlib import errors, merge
19
from bzrlib import errors, merge, revision
20
20
from bzrlib.branch import Branch, BranchFormat, BranchReferenceFormat
21
21
from bzrlib.bzrdir import BzrDir
22
22
from bzrlib.trace import note
25
def switch(control_dir, to_branch):
25
def switch(control_dir, to_branch, force=False):
26
26
"""Switch the branch associated with a checkout.
28
28
:param control_dir: BzrDir of the checkout to change
29
29
:param to_branch: branch that the checkout is to reference
30
:param force: skip the check for local commits in a heavy checkout
31
_check_switch_branch_format(control_dir)
32
_check_pending_merges(control_dir)
32
_check_pending_merges(control_dir, force)
34
34
source_repository = control_dir.open_branch().repository
35
35
except errors.NotBranchError:
36
36
source_repository = to_branch.repository
37
_set_branch_location(control_dir, to_branch)
37
_set_branch_location(control_dir, to_branch, force)
38
38
tree = control_dir.open_workingtree()
39
39
_update(tree, source_repository)
42
def _check_switch_branch_format(control):
43
"""Check that the branch format supports the switch operation.
45
Note: Only lightweight checkouts are currently supported.
46
This may change in the future though.
48
:param control: BzrDir of the branch to check
50
branch_format = BranchFormat.find_format(control)
51
format_string = branch_format.get_format_string()
52
if not format_string.startswith("Bazaar-NG Branch Reference Format "):
53
raise errors.BzrCommandError(
54
'The switch command can only be used on a lightweight checkout.\n'
55
'Expected branch reference, found %s at %s' % (
56
format_string.strip(), control.root_transport.base))
57
if not format_string == BranchReferenceFormat().get_format_string():
58
raise errors.BzrCommandError(
59
'Unsupported: %r' % (format_string.strip(),))
62
def _check_pending_merges(control):
42
def _check_pending_merges(control, force=False):
63
43
"""Check that there are no outstanding pending merges before switching.
65
45
:param control: BzrDir of the branch to check
68
48
tree = control.open_workingtree()
69
except errors.NotBranchError:
49
except errors.NotBranchError, ex:
50
# Lightweight checkout and branch is no longer there
72
55
# XXX: Should the tree be locked for get_parent_ids?
73
56
existing_pending_merges = tree.get_parent_ids()[1:]
74
57
if len(existing_pending_merges) > 0:
76
59
'committed or reverted before using switch.')
79
def _set_branch_location(control, to_branch):
62
def _set_branch_location(control, to_branch, force=False):
80
63
"""Set location value of a branch reference.
82
65
:param control: BzrDir of the checkout to change
83
66
:param to_branch: branch that the checkout is to reference
67
:param force: skip the check for local commits in a heavy checkout
85
transport = control.get_branch_transport(None)
86
location = transport.put_bytes('location', to_branch.base)
69
branch_format = control.find_branch_format()
70
if branch_format.get_reference(control) is not None:
71
# Lightweight checkout: update the branch reference
72
branch_format.set_reference(control, to_branch)
74
b = control.open_branch()
75
bound_branch = b.get_bound_location()
76
if bound_branch is not None:
77
# Heavyweight checkout: check all local commits
78
# have been pushed to the current bound branch then
79
# synchronise the local branch with the new remote branch
81
possible_transports = []
82
if not force and _any_local_commits(b, possible_transports):
83
raise errors.BzrCommandError(
84
'Cannot switch as local commits found in the checkout. '
85
'Commit these to the bound branch or use --force to '
87
b.set_bound_location(None)
88
b.pull(to_branch, overwrite=True,
89
possible_transports=possible_transports)
90
b.set_bound_location(to_branch.base)
92
raise errors.BzrCommandError('Cannot switch a branch, '
96
def _any_local_commits(this_branch, possible_transports):
97
"""Does this branch have any commits not in the master branch?"""
98
last_rev = revision.ensure_null(this_branch.last_revision())
99
if last_rev != revision.NULL_REVISION:
100
other_branch = this_branch.get_master_branch(possible_transports)
101
this_branch.lock_read()
102
other_branch.lock_read()
104
other_last_rev = other_branch.last_revision()
105
graph = this_branch.repository.get_graph(
106
other_branch.repository)
107
if not graph.is_ancestor(last_rev, other_last_rev):
110
other_branch.unlock()
89
115
def _update(tree, source_repository):