14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
19
17
# Original author: David Allouche
27
from .branch import Branch
28
from .i18n import gettext
29
from .trace import note
19
from bzrlib import errors, merge, revision
20
from bzrlib.branch import Branch
21
from bzrlib.trace import note
32
24
def _run_post_switch_hooks(control_dir, to_branch, force, revision_id):
33
from .branch import SwitchHookParams
25
from bzrlib.branch import SwitchHookParams
34
26
hooks = Branch.hooks['post_switch']
42
def switch(control_dir, to_branch, force=False, quiet=False, revision_id=None,
43
store_uncommitted=False, possible_transports=None):
33
def switch(control_dir, to_branch, force=False, quiet=False, revision_id=None):
44
34
"""Switch the branch associated with a checkout.
46
:param control_dir: ControlDir of the checkout to change
36
:param control_dir: BzrDir of the checkout to change
47
37
:param to_branch: branch that the checkout is to reference
48
38
:param force: skip the check for local commits in a heavy checkout
49
39
:param revision_id: revision ID to switch to.
50
:param store_uncommitted: If True, store uncommitted changes in the
54
tree = control_dir.open_workingtree()
55
except errors.NotBranchError as ex:
41
_check_pending_merges(control_dir, force)
43
source_repository = control_dir.open_branch().repository
44
except errors.NotBranchError:
45
source_repository = to_branch.repository
46
_set_branch_location(control_dir, to_branch, force)
47
tree = control_dir.open_workingtree()
48
_update(tree, source_repository, quiet, revision_id)
49
_run_post_switch_hooks(control_dir, to_branch, force, revision_id)
51
def _check_pending_merges(control, force=False):
52
"""Check that there are no outstanding pending merges before switching.
54
:param control: BzrDir of the branch to check
57
tree = control.open_workingtree()
58
except errors.NotBranchError, ex:
56
59
# Lightweight checkout and branch is no longer there
57
if not force or store_uncommitted:
62
if store_uncommitted or tree.branch.get_bound_location():
65
tree.lock_tree_write()
68
parent_ids = tree.get_parent_ids()
69
if len(parent_ids) > 1:
70
raise errors.BzrCommandError(
71
gettext('Pending merges must be '
72
'committed or reverted before using switch.'))
75
tree.store_uncommitted()
77
source_repository = to_branch.repository
78
base_revision_id = None
80
source_repository = tree.branch.repository
81
# Attempt to retrieve the base_revision_id now, since some
82
# working tree formats (i.e. git) don't have their own
83
# last_revision but just use that of the currently active branch.
84
base_revision_id = tree.last_revision()
88
with to_branch.lock_read():
89
_set_branch_location(control_dir, to_branch, tree.branch if tree else None, force)
90
tree = control_dir.open_workingtree()
94
tree.lock_tree_write()
96
if base_revision_id is None:
97
# If we couldn't get to the tree's last_revision earlier, perhaps
99
base_revision_id = tree.last_revision()
100
if revision_id is None:
101
revision_id = to_branch.last_revision()
102
if base_revision_id == revision_id:
104
note(gettext("Tree is up to date at revision %d."),
107
base_tree = source_repository.revision_tree(base_revision_id)
108
target_tree = to_branch.repository.revision_tree(revision_id)
109
merge.Merge3Merger(tree, tree, base_tree, target_tree)
110
tree.set_last_revision(revision_id)
112
note(gettext('Updated to revision %d.') % to_branch.revno())
113
if store_uncommitted:
114
tree.restore_uncommitted()
115
_run_post_switch_hooks(control_dir, to_branch, force, revision_id)
120
def _set_branch_location(control, to_branch, current_branch, force=False):
64
# XXX: Should the tree be locked for get_parent_ids?
65
existing_pending_merges = tree.get_parent_ids()[1:]
66
if len(existing_pending_merges) > 0:
67
raise errors.BzrCommandError('Pending merges must be '
68
'committed or reverted before using switch.')
71
def _set_branch_location(control, to_branch, force=False):
121
72
"""Set location value of a branch reference.
123
:param control: ControlDir of the checkout to change
74
:param control: BzrDir of the checkout to change
124
75
:param to_branch: branch that the checkout is to reference
125
76
:param force: skip the check for local commits in a heavy checkout
139
90
possible_transports = []
141
92
if not force and _any_local_commits(b, possible_transports):
142
raise errors.BzrCommandError(gettext(
93
raise errors.BzrCommandError(
143
94
'Cannot switch as local commits found in the checkout. '
144
95
'Commit these to the bound branch or use --force to '
146
except errors.BoundBranchConnectionFailure as e:
147
raise errors.BzrCommandError(gettext(
148
'Unable to connect to current master branch %(target)s: '
149
'%(error)s To switch anyway, use --force.') %
152
b.set_bound_location(None)
153
b.pull(to_branch, overwrite=True,
154
possible_transports=possible_transports)
155
b.set_bound_location(to_branch.base)
156
b.set_parent(b.get_master_branch().get_parent())
97
except errors.BoundBranchConnectionFailure, e:
98
raise errors.BzrCommandError(
99
'Unable to connect to current master branch %(target)s: '
100
'%(error)s To switch anyway, use --force.' %
102
b.set_bound_location(None)
103
b.pull(to_branch, overwrite=True,
104
possible_transports=possible_transports)
105
b.set_bound_location(to_branch.base)
158
# If this is a standalone tree and the new branch
159
# is derived from this one, create a lightweight checkout.
161
graph = b.repository.get_graph(to_branch.repository)
162
if (b.controldir._format.colocated_branches and
163
(force or graph.is_ancestor(
164
b.last_revision(), to_branch.last_revision()))):
165
b.controldir.destroy_branch()
166
b.controldir.set_branch_reference(to_branch, name="")
168
raise errors.BzrCommandError(
169
gettext('Cannot switch a branch, only a checkout.'))
107
raise errors.BzrCommandError('Cannot switch a branch, '
172
111
def _any_local_commits(this_branch, possible_transports):
174
113
last_rev = revision.ensure_null(this_branch.last_revision())
175
114
if last_rev != revision.NULL_REVISION:
176
115
other_branch = this_branch.get_master_branch(possible_transports)
177
with this_branch.lock_read(), other_branch.lock_read():
116
this_branch.lock_read()
117
other_branch.lock_read()
178
119
other_last_rev = other_branch.last_revision()
179
120
graph = this_branch.repository.get_graph(
180
121
other_branch.repository)
181
122
if not graph.is_ancestor(last_rev, other_last_rev):
125
other_branch.unlock()
130
def _update(tree, source_repository, quiet=False, revision_id=None):
131
"""Update a working tree to the latest revision of its branch.
133
:param tree: the working tree
134
:param source_repository: repository holding the revisions
136
tree.lock_tree_write()
138
to_branch = tree.branch
139
if revision_id is None:
140
revision_id = to_branch.last_revision()
141
if tree.last_revision() == revision_id:
143
note("Tree is up to date at revision %d.", to_branch.revno())
145
base_tree = source_repository.revision_tree(tree.last_revision())
146
merge.Merge3Merger(tree, tree, base_tree, to_branch.repository.revision_tree(revision_id))
147
tree.set_last_revision(to_branch.last_revision())
149
note('Updated to revision %d.' % to_branch.revno())