13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
17
# Original author: David Allouche
27
from bzrlib.branch import Branch
28
from bzrlib.i18n import gettext
19
from bzrlib import errors, merge
20
from bzrlib.branch import Branch, BranchFormat, BranchReferenceFormat
21
from bzrlib.bzrdir import BzrDir
29
22
from bzrlib.trace import note
31
def _run_post_switch_hooks(control_dir, to_branch, force, revision_id):
32
from bzrlib.branch import SwitchHookParams
33
hooks = Branch.hooks['post_switch']
36
params = SwitchHookParams(control_dir, to_branch, force, revision_id)
40
def switch(control_dir, to_branch, force=False, quiet=False, revision_id=None,
41
store_uncommitted=False):
25
def switch(control_dir, to_branch):
42
26
"""Switch the branch associated with a checkout.
44
:param control_dir: ControlDir of the checkout to change
28
:param control_dir: BzrDir of the checkout to change
45
29
:param to_branch: branch that the checkout is to reference
46
:param force: skip the check for local commits in a heavy checkout
47
:param revision_id: revision ID to switch to.
48
:param store_uncommitted: If True, store uncommitted changes in the
51
_check_pending_merges(control_dir, force)
31
_check_switch_branch_format(control_dir)
32
_check_pending_merges(control_dir)
53
34
source_repository = control_dir.open_branch().repository
54
35
except errors.NotBranchError:
55
36
source_repository = to_branch.repository
57
with lock.write_locked(control_dir.open_workingtree()) as tree:
58
tree.store_uncommitted()
61
_set_branch_location(control_dir, to_branch, force)
37
_set_branch_location(control_dir, to_branch)
64
38
tree = control_dir.open_workingtree()
65
_update(tree, source_repository, quiet, revision_id, store_uncommitted)
66
_run_post_switch_hooks(control_dir, to_branch, force, revision_id)
68
def _check_pending_merges(control, force=False):
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):
69
63
"""Check that there are no outstanding pending merges before switching.
71
:param control: ControlDir of the branch to check
65
:param control: BzrDir of the branch to check
74
68
tree = control.open_workingtree()
75
except errors.NotBranchError, ex:
76
# Lightweight checkout and branch is no longer there
69
except errors.NotBranchError:
81
72
# XXX: Should the tree be locked for get_parent_ids?
82
73
existing_pending_merges = tree.get_parent_ids()[1:]
83
74
if len(existing_pending_merges) > 0:
84
raise errors.BzrCommandError(gettext('Pending merges must be '
85
'committed or reverted before using switch.'))
88
def _set_branch_location(control, to_branch, force=False):
75
raise errors.BzrCommandError('Pending merges must be '
76
'committed or reverted before using switch.')
79
def _set_branch_location(control, to_branch):
89
80
"""Set location value of a branch reference.
91
:param control: ControlDir of the checkout to change
82
:param control: BzrDir of the checkout to change
92
83
:param to_branch: branch that the checkout is to reference
93
:param force: skip the check for local commits in a heavy checkout
95
branch_format = control.find_branch_format()
96
if branch_format.get_reference(control) is not None:
97
# Lightweight checkout: update the branch reference
98
branch_format.set_reference(control, None, to_branch)
100
b = control.open_branch()
101
bound_branch = b.get_bound_location()
102
if bound_branch is not None:
103
# Heavyweight checkout: check all local commits
104
# have been pushed to the current bound branch then
105
# synchronise the local branch with the new remote branch
107
possible_transports = []
109
if not force and _any_local_commits(b, possible_transports):
110
raise errors.BzrCommandError(gettext(
111
'Cannot switch as local commits found in the checkout. '
112
'Commit these to the bound branch or use --force to '
114
except errors.BoundBranchConnectionFailure, e:
115
raise errors.BzrCommandError(gettext(
116
'Unable to connect to current master branch %(target)s: '
117
'%(error)s To switch anyway, use --force.') %
121
b.set_bound_location(None)
122
b.pull(to_branch, overwrite=True,
123
possible_transports=possible_transports)
124
b.set_bound_location(to_branch.base)
125
b.set_parent(b.get_master_branch().get_parent())
129
# If this is a standalone tree and the new branch
130
# is derived from this one, create a lightweight checkout.
133
graph = b.repository.get_graph(to_branch.repository)
134
if (b.bzrdir._format.colocated_branches and
135
(force or graph.is_ancestor(b.last_revision(),
136
to_branch.last_revision()))):
137
b.bzrdir.destroy_branch()
138
b.bzrdir.set_branch_reference(to_branch, name="")
140
raise errors.BzrCommandError(gettext('Cannot switch a branch, '
146
def _any_local_commits(this_branch, possible_transports):
147
"""Does this branch have any commits not in the master branch?"""
148
last_rev = revision.ensure_null(this_branch.last_revision())
149
if last_rev != revision.NULL_REVISION:
150
other_branch = this_branch.get_master_branch(possible_transports)
151
this_branch.lock_read()
152
other_branch.lock_read()
154
other_last_rev = other_branch.last_revision()
155
graph = this_branch.repository.get_graph(
156
other_branch.repository)
157
if not graph.is_ancestor(last_rev, other_last_rev):
160
other_branch.unlock()
165
def _update(tree, source_repository, quiet=False, revision_id=None,
166
restore_uncommitted=False):
85
transport = control.get_branch_transport(None)
86
location = transport.put_bytes('location', to_branch.base)
89
def _update(tree, source_repository):
167
90
"""Update a working tree to the latest revision of its branch.
169
92
:param tree: the working tree
170
93
:param source_repository: repository holding the revisions
171
:param restore_uncommitted: restore any uncommitted changes in the branch.
173
if restore_uncommitted:
176
tree.lock_tree_write()
95
tree.lock_tree_write()
178
97
to_branch = tree.branch
179
if revision_id is None:
180
revision_id = to_branch.last_revision()
181
if tree.last_revision() == revision_id:
183
note(gettext("Tree is up to date at revision %d."), to_branch.revno())
185
base_tree = source_repository.revision_tree(tree.last_revision())
186
merge.Merge3Merger(tree, tree, base_tree,
187
to_branch.repository.revision_tree(revision_id))
188
tree.set_last_revision(to_branch.last_revision())
190
note(gettext('Updated to revision %d.') % to_branch.revno())
191
if restore_uncommitted:
192
tree.restore_uncommitted()
98
if tree.last_revision() == to_branch.last_revision():
99
note("Tree is up to date at revision %d.", to_branch.revno())
101
base_tree = source_repository.revision_tree(tree.last_revision())
102
merge.Merge3Merger(tree, tree, base_tree, to_branch.basis_tree())
103
tree.set_last_revision(to_branch.last_revision())
104
note('Updated to revision %d.' % to_branch.revno())