Merge lp:~jelmer/brz/switch-git-bug into lp:brz/3.0

Proposed by Jelmer Vernooij on 2019-04-13
Status: Merged
Approved by: Jelmer Vernooij on 2019-06-16
Approved revision: 7312
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/switch-git-bug
Merge into: lp:brz/3.0
Diff against target: 341 lines (+127/-32)
9 files modified
breezy/builtins.py (+11/-8)
breezy/bzr/bzrdir.py (+10/-2)
breezy/controldir.py (+4/-4)
breezy/git/dir.py (+13/-0)
breezy/git/tests/test_blackbox.py (+25/-0)
breezy/git/workingtree.py (+19/-12)
breezy/switch.py (+2/-5)
breezy/tests/per_controldir_colo/test_supported.py (+38/-1)
doc/en/release-notes/brz-3.0.txt (+5/-0)
To merge this branch: bzr merge lp:~jelmer/brz/switch-git-bug
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve on 2019-05-28
Review via email: mp+365990@code.launchpad.net

This proposal supersedes a proposal from 2019-04-13.

Commit message

Fix switching between branches while preserving uncommitted changes in git.

Description of the change

Fix switching between branches while preserving uncommitted changes in git.

To post a comment you must log in.
Jelmer Vernooij (jelmer) :
review: Approve
lp:~jelmer/brz/switch-git-bug updated on 2019-06-16
7312. By Jelmer Vernooij on 2019-06-16

Fine, we'll not reuse the transport - it runs into concurrency issues.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/builtins.py'
2--- breezy/builtins.py 2019-03-03 23:05:11 +0000
3+++ breezy/builtins.py 2019-06-16 20:07:09 +0000
4@@ -1210,8 +1210,8 @@
5 possible_transports = []
6 if location is not None:
7 try:
8- mergeable = bundle.read_mergeable_from_url(location,
9- possible_transports=possible_transports)
10+ mergeable = bundle.read_mergeable_from_url(
11+ location, possible_transports=possible_transports)
12 except errors.NotABundle:
13 mergeable = None
14
15@@ -6277,9 +6277,8 @@
16 from . import switch
17 tree_location = directory
18 revision = _get_one_revision('switch', revision)
19- possible_transports = []
20- control_dir = controldir.ControlDir.open_containing(tree_location,
21- possible_transports=possible_transports)[0]
22+ control_dir = controldir.ControlDir.open_containing(tree_location)[0]
23+ possible_transports = [control_dir.root_transport]
24 if to_location is None:
25 if revision is None:
26 raise errors.BzrCommandError(gettext('You must supply either a'
27@@ -6292,6 +6291,8 @@
28 except errors.NotBranchError:
29 branch = None
30 had_explicit_nick = False
31+ else:
32+ possible_transports.append(branch.user_transport)
33 if create_branch:
34 if branch is None:
35 raise errors.BzrCommandError(
36@@ -6308,17 +6309,19 @@
37 source_branch=branch).open_branch()
38 else:
39 try:
40- to_branch = Branch.open(to_location,
41- possible_transports=possible_transports)
42+ to_branch = Branch.open(
43+ to_location, possible_transports=possible_transports)
44 except errors.NotBranchError:
45 to_branch = open_sibling_branch(
46 control_dir, to_location,
47 possible_transports=possible_transports)
48 if revision is not None:
49 revision = revision.as_revision_id(to_branch)
50+ possible_transports.append(to_branch.user_transport)
51 try:
52 switch.switch(control_dir, to_branch, force, revision_id=revision,
53- store_uncommitted=store)
54+ store_uncommitted=store,
55+ possible_transports=possible_transports)
56 except controldir.BranchReferenceLoop:
57 raise errors.BzrCommandError(
58 gettext('switching would create a branch reference loop. '
59
60=== modified file 'breezy/bzr/bzrdir.py'
61--- breezy/bzr/bzrdir.py 2018-11-18 19:48:57 +0000
62+++ breezy/bzr/bzrdir.py 2019-06-16 20:07:09 +0000
63@@ -448,8 +448,11 @@
64
65 # Create/update the result working tree
66 if (create_tree_if_local and not result.has_workingtree()
67- and isinstance(target_transport, local.LocalTransport)
68- and (result_repo is None or result_repo.make_working_trees())):
69+ and isinstance(target_transport, local.LocalTransport)
70+ and (result_repo is None or result_repo.make_working_trees())
71+ and result.open_branch(
72+ name="",
73+ possible_transports=possible_transports).name == result_branch.name):
74 wt = result.create_workingtree(
75 accelerator_tree=accelerator_tree, hardlink=hardlink,
76 from_branch=result_branch)
77@@ -1092,6 +1095,11 @@
78 name = self._get_selected_branch()
79 format = self.find_branch_format(name=name)
80 format.check_support_status(unsupported)
81+ if possible_transports is None:
82+ possible_transports = []
83+ else:
84+ possible_transports = list(possible_transports)
85+ possible_transports.append(self.root_transport)
86 return format.open(self, name=name,
87 _found=True, ignore_fallbacks=ignore_fallbacks,
88 possible_transports=possible_transports)
89
90=== modified file 'breezy/controldir.py'
91--- breezy/controldir.py 2018-11-12 01:41:38 +0000
92+++ breezy/controldir.py 2019-06-16 20:07:09 +0000
93@@ -443,12 +443,12 @@
94 try:
95 tree_to = self.open_workingtree()
96 except errors.NotLocalUrl:
97- push_result.branch_push_result = source.push(br_to,
98- overwrite, stop_revision=revision_id, lossy=lossy)
99+ push_result.branch_push_result = source.push(
100+ br_to, overwrite, stop_revision=revision_id, lossy=lossy)
101 push_result.workingtree_updated = False
102 except errors.NoWorkingTree:
103- push_result.branch_push_result = source.push(br_to,
104- overwrite, stop_revision=revision_id, lossy=lossy)
105+ push_result.branch_push_result = source.push(
106+ br_to, overwrite, stop_revision=revision_id, lossy=lossy)
107 push_result.workingtree_updated = None # Not applicable
108 else:
109 with tree_to.lock_write():
110
111=== modified file 'breezy/git/dir.py'
112--- breezy/git/dir.py 2018-11-16 11:37:47 +0000
113+++ breezy/git/dir.py 2019-06-16 20:07:09 +0000
114@@ -182,6 +182,7 @@
115 result_branch = source_branch.sprout(
116 result, revision_id=revision_id, repository=result_repo)
117 if (create_tree_if_local and
118+ result.open_branch(name="").name == result_branch.name and
119 isinstance(target_transport, LocalTransport) and
120 (result_repo is None or result_repo.make_working_trees())):
121 result.create_workingtree(
122@@ -302,6 +303,18 @@
123 lossy=lossy)
124 push_result.new_revid = push_result.branch_push_result.new_revid
125 push_result.old_revid = push_result.branch_push_result.old_revid
126+ try:
127+ wt = self.open_workingtree()
128+ except brz_errors.NoWorkingTree:
129+ push_result.workingtree_updated = None
130+ else:
131+ if self.open_branch(name="").name == target.name:
132+ wt._update_git_tree(
133+ old_revision=push_result.old_revid,
134+ new_revision=push_result.new_revid)
135+ push_result.workingtree_updated = True
136+ else:
137+ push_result.workingtree_updated = False
138 push_result.target_branch = target
139 if source.get_push_location() is None or remember:
140 source.set_push_location(push_result.target_branch.base)
141
142=== modified file 'breezy/git/tests/test_blackbox.py'
143--- breezy/git/tests/test_blackbox.py 2019-05-28 21:46:53 +0000
144+++ breezy/git/tests/test_blackbox.py 2019-06-16 20:07:09 +0000
145@@ -35,6 +35,7 @@
146 from .. import (
147 tests,
148 )
149+from ...tests.script import TestCaseWithTransportAndScript
150 from ...tests.features import PluginLoadedFeature
151
152
153@@ -393,6 +394,30 @@
154 self.assertEqual([], list(tree.iter_changes(basis_tree)))
155
156
157+class SwitchScriptTests(TestCaseWithTransportAndScript):
158+
159+ def test_switch_preserves(self):
160+ # See https://bugs.launchpad.net/brz/+bug/1820606
161+ self.run_script("""
162+$ brz init --git r
163+Created a standalone tree (format: git)
164+$ cd r
165+$ echo original > file.txt
166+$ brz add
167+adding file.txt
168+$ brz ci -q -m "Initial"
169+$ echo "entered on master branch" > file.txt
170+$ brz stat
171+modified:
172+ file.txt
173+$ brz switch -b other
174+2>Tree is up to date at revision 1.
175+2>Switched to branch other
176+$ cat file.txt
177+entered on master branch
178+""")
179+
180+
181 class GrepTests(ExternalBase):
182
183 def test_simple_grep(self):
184
185=== modified file 'breezy/git/workingtree.py'
186--- breezy/git/workingtree.py 2019-02-05 04:00:02 +0000
187+++ breezy/git/workingtree.py 2019-06-16 20:07:09 +0000
188@@ -1202,26 +1202,33 @@
189 (index, subpath) = self._lookup_index(entry.path)
190 index[subpath] = index_entry_from_stat(st, entry.sha, 0)
191
192+ def _update_git_tree(self, old_revision, new_revision, change_reporter=None,
193+ show_base=False):
194+ basis_tree = self.revision_tree(old_revision)
195+ if new_revision != old_revision:
196+ with basis_tree.lock_read():
197+ new_basis_tree = self.branch.basis_tree()
198+ merge.merge_inner(
199+ self.branch,
200+ new_basis_tree,
201+ basis_tree,
202+ this_tree=self,
203+ change_reporter=change_reporter,
204+ show_base=show_base)
205+
206 def pull(self, source, overwrite=False, stop_revision=None,
207 change_reporter=None, possible_transports=None, local=False,
208 show_base=False):
209 with self.lock_write(), source.lock_read():
210 old_revision = self.branch.last_revision()
211- basis_tree = self.basis_tree()
212 count = self.branch.pull(source, overwrite, stop_revision,
213 possible_transports=possible_transports,
214 local=local)
215- new_revision = self.branch.last_revision()
216- if new_revision != old_revision:
217- with basis_tree.lock_read():
218- new_basis_tree = self.branch.basis_tree()
219- merge.merge_inner(
220- self.branch,
221- new_basis_tree,
222- basis_tree,
223- this_tree=self,
224- change_reporter=change_reporter,
225- show_base=show_base)
226+ self._update_git_tree(
227+ old_revision=old_revision,
228+ new_revision=self.branch.last_revision(),
229+ change_reporter=change_reporter,
230+ show_base=show_base)
231 return count
232
233 def add_reference(self, sub_tree):
234
235=== modified file 'breezy/switch.py'
236--- breezy/switch.py 2018-11-16 23:28:26 +0000
237+++ breezy/switch.py 2019-06-16 20:07:09 +0000
238@@ -40,7 +40,7 @@
239
240
241 def switch(control_dir, to_branch, force=False, quiet=False, revision_id=None,
242- store_uncommitted=False):
243+ store_uncommitted=False, possible_transports=None):
244 """Switch the branch associated with a checkout.
245
246 :param control_dir: ControlDir of the checkout to change
247@@ -148,15 +148,12 @@
248 'Unable to connect to current master branch %(target)s: '
249 '%(error)s To switch anyway, use --force.') %
250 e.__dict__)
251- b.lock_write()
252- try:
253+ with b.lock_write():
254 b.set_bound_location(None)
255 b.pull(to_branch, overwrite=True,
256 possible_transports=possible_transports)
257 b.set_bound_location(to_branch.base)
258 b.set_parent(b.get_master_branch().get_parent())
259- finally:
260- b.unlock()
261 else:
262 # If this is a standalone tree and the new branch
263 # is derived from this one, create a lightweight checkout.
264
265=== modified file 'breezy/tests/per_controldir_colo/test_supported.py'
266--- breezy/tests/per_controldir_colo/test_supported.py 2018-11-11 04:08:32 +0000
267+++ breezy/tests/per_controldir_colo/test_supported.py 2019-06-16 20:07:09 +0000
268@@ -114,10 +114,47 @@
269 'Control dir does not support creating new branches.')
270 to_dir = from_tree.controldir.sprout(
271 urlutils.join_segment_parameters(
272- other_branch.controldir.user_url, {"branch": "target"}))
273+ other_branch.user_url, {"branch": "target"}))
274 to_branch = to_dir.open_branch(name="target")
275 self.assertEqual(revid, to_branch.last_revision())
276
277+ def test_sprout_into_colocated_leaves_workingtree(self):
278+ # a bzrdir can construct a branch and repository for itself.
279+ if not self.bzrdir_format.is_supported():
280+ # unsupported formats are not loopback testable
281+ # because the default open will not open them and
282+ # they may not be initializable.
283+ raise tests.TestNotApplicable('Control dir format not supported')
284+ if not self.bzrdir_format.supports_workingtrees:
285+ raise tests.TestNotApplicable(
286+ 'Control dir format does not support working trees')
287+ from_tree = self.make_branch_and_tree('from')
288+ self.build_tree_contents([('from/foo', 'contents')])
289+ from_tree.add(['foo'])
290+ revid1 = from_tree.commit("rev1")
291+ self.build_tree_contents([('from/foo', 'new contents')])
292+ revid2 = from_tree.commit("rev2")
293+ try:
294+ other_branch = self.make_branch_and_tree("to")
295+ except errors.UninitializableFormat:
296+ raise tests.TestNotApplicable(
297+ 'Control dir does not support creating new branches.')
298+
299+ result = other_branch.controldir.push_branch(
300+ from_tree.branch, revision_id=revid1)
301+ self.assertTrue(result.workingtree_updated)
302+ self.assertFileEqual('contents', 'to/foo')
303+
304+ from_tree.controldir.sprout(
305+ urlutils.join_segment_parameters(
306+ other_branch.user_url, {"branch": "target"}),
307+ revision_id=revid2)
308+ active_branch = other_branch.controldir.open_branch(name="")
309+ self.assertEqual(revid1, active_branch.last_revision())
310+ to_branch = other_branch.controldir.open_branch(name="target")
311+ self.assertEqual(revid2, to_branch.last_revision())
312+ self.assertFileEqual('contents', 'to/foo')
313+
314 def test_unicode(self):
315 self.requireFeature(UnicodeFilenameFeature)
316 if not self.bzrdir_format.is_supported():
317
318=== modified file 'doc/en/release-notes/brz-3.0.txt'
319--- doc/en/release-notes/brz-3.0.txt 2019-06-16 00:04:17 +0000
320+++ doc/en/release-notes/brz-3.0.txt 2019-06-16 20:07:09 +0000
321@@ -35,9 +35,13 @@
322
323 * Fix a nasty corner case merging changes into a tree with changed
324 symlinks when pushing from bzr into git.
325+ (Jelmer Vernooij)
326
327 * Fix installation on Windows. (Raoul Snyman, #1818947)
328
329+* Fix switching between branches while preserving uncommitted changes in git.
330+ (Jelmer Vernooij, #1820606)
331+
332 * Return consist errors from ``Branch.get_revid`` and
333 ``Repository.get_revid_for_revno`` when the revision
334 number is invalid. (Jelmer Vernooij, #701953)
335@@ -73,6 +77,7 @@
336
337 None.
338
339+
340 brz 3.0.0
341 #########
342

Subscribers

People subscribed via source and target branches