Merge lp:~ryorke/bzr/202374-pull-update-showbase into lp:bzr

Proposed by Rory Yorke
Status: Merged
Approved by: Martin Pool
Approved revision: no longer in the source branch.
Merged at revision: 5455
Proposed branch: lp:~ryorke/bzr/202374-pull-update-showbase
Merge into: lp:bzr
Diff against target: 263 lines (+118/-12)
5 files modified
NEWS (+9/-0)
bzrlib/builtins.py (+16/-5)
bzrlib/tests/blackbox/test_pull.py (+43/-0)
bzrlib/tests/blackbox/test_update.py (+38/-0)
bzrlib/workingtree.py (+12/-7)
To merge this branch: bzr merge lp:~ryorke/bzr/202374-pull-update-showbase
Reviewer Review Type Date Requested Status
Martin Packman (community) Approve
bzr-core Pending
Review via email: mp+36571@code.launchpad.net

Commit message

Enable use of 3-way conflict markers in pull and update by adding --show-base option

Description of the change

Fixes bug 202374, --show-base for pull and update.

Code more-or-less copied from builtins.cmd_merge. In both cases show_base gets passed down to merge.merge_inner in the appropriate WorkingTree method.

Disallow --show-base for pull if not invoked on a working tree.

Tests added in tests/blackbox/test_pull.py and test_update.py.

Default unchange (i.e., by default don't show-base). The bug reporter asked for --show-base to be the default, which may be sensible, but that is a design decision that I won't take.

I've run './bzr --no-plugins selftest --parallel=fork' on my system which selftest reports as

  bzr-2.3.0dev1 python-2.6.4 Linux-2.6.31-22-generic-i686-with-Ubuntu-9.10-karmic

I get some odd results, including maximum recursion depth exceeded, and a few errors in test_test_server. I hope these are unrelated to my changes.

Running

'./bzr --no-plugins selftest --parallel=fork blackbox'

gives

OK (known_failures=2)
22 tests skipped

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Thanks for working on this Rory, the change looks pretty good to me.

You'll want to add a NEWS entry for this, see the 'Documenting Changes' section of doc/developers/HACKING.txt for details.

> I get some odd results, including maximum recursion depth exceeded, and a few errors in > test_test_server. I hope these are unrelated to my changes.

Almost certainly unrelated, but it might be worth checking if there are open bugs on any of these failures and filing if not.

Some nitpicks on the diff:

+ def run(self, dir='.', revision=None,show_base=None):

PEP 8, you want a space before show_base there.

+ open(pathjoin('a', 'hello'),'wt').write('fee')

The test lines like this are relying on refcounting to do the right thing, which is fine, and as you care about the content for these tests I see why you didn't use the normal make_branch_and_tree options, but a different spelling would be preferable.

+ #tree.update() gives no such revision, so ...
+ self.run_bzr(['update','-r1'])

As your comment notes, this isn't ideal, we only really want to be using run_bzr for commands we're actually testing. Perhaps another reviewer can suggest a better option.

Tests pass here, just need that news added and another reviewer's opinion.

review: Needs Fixing
Revision history for this message
Martin Packman (gz) wrote :

Great, thanks.

review: Approve
Revision history for this message
Martin Pool (mbp) wrote :

It would be nice to put that into per-process-context configuration,
which would avoid passing so many parameters around, but the
infrastructure for that isn't here yet.

(more review later)

--
Martin

Revision history for this message
Martin Packman (gz) wrote :

sent to pqm by email

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'NEWS'
--- NEWS 2010-09-27 19:31:45 +0000
+++ NEWS 2010-09-27 20:26:42 +0000
@@ -240,6 +240,10 @@
240* New development format ``development8-subtree`` which is similar to the 240* New development format ``development8-subtree`` which is similar to the
241 ``2a`` format and adds subtree support. (Jelmer Vernooij)241 ``2a`` format and adds subtree support. (Jelmer Vernooij)
242242
243* The ``pull`` and ``update`` commands now take a ``-show-base``
244 option that, in the case of conflicts, shows the base revision text.
245 (Rory Yorke, #202374)
246
243Bug Fixes247Bug Fixes
244*********248*********
245249
@@ -442,6 +446,11 @@
442 constructor has been deprecated, use the ``file_name`` parameter instead.446 constructor has been deprecated, use the ``file_name`` parameter instead.
443 (Vincent Ladeuil)447 (Vincent Ladeuil)
444448
449* ``WorkingTree`` methods ``pull``, ``update``, and ``_update_tree``
450 now have an optional argument, ``show_base``, which is by default
451 False. This is flag is ultimately passed to ``merge.merge_inner``
452 in each case. (Rory Yorke, #202374)
453
445Internals454Internals
446*********455*********
447456
448457
=== modified file 'bzrlib/builtins.py'
--- bzrlib/builtins.py 2010-09-24 12:53:00 +0000
+++ bzrlib/builtins.py 2010-09-27 20:26:42 +0000
@@ -923,13 +923,16 @@
923 "branch. Local pulls are not applied to "923 "branch. Local pulls are not applied to "
924 "the master branch."924 "the master branch."
925 ),925 ),
926 Option('show-base',
927 help="Show base revision text in conflicts.")
926 ]928 ]
927 takes_args = ['location?']929 takes_args = ['location?']
928 encoding_type = 'replace'930 encoding_type = 'replace'
929931
930 def run(self, location=None, remember=False, overwrite=False,932 def run(self, location=None, remember=False, overwrite=False,
931 revision=None, verbose=False,933 revision=None, verbose=False,
932 directory=None, local=False):934 directory=None, local=False,
935 show_base=False):
933 # FIXME: too much stuff is in the command class936 # FIXME: too much stuff is in the command class
934 revision_id = None937 revision_id = None
935 mergeable = None938 mergeable = None
@@ -944,6 +947,9 @@
944 branch_to = Branch.open_containing(directory)[0]947 branch_to = Branch.open_containing(directory)[0]
945 self.add_cleanup(branch_to.lock_write().unlock)948 self.add_cleanup(branch_to.lock_write().unlock)
946949
950 if tree_to is None and show_base:
951 raise errors.BzrCommandError("Need working tree for --show-base.")
952
947 if local and not branch_to.get_bound_location():953 if local and not branch_to.get_bound_location():
948 raise errors.LocalRequiresBoundBranch()954 raise errors.LocalRequiresBoundBranch()
949955
@@ -994,7 +1000,8 @@
994 view_info=view_info)1000 view_info=view_info)
995 result = tree_to.pull(1001 result = tree_to.pull(
996 branch_from, overwrite, revision_id, change_reporter,1002 branch_from, overwrite, revision_id, change_reporter,
997 possible_transports=possible_transports, local=local)1003 possible_transports=possible_transports, local=local,
1004 show_base=show_base)
998 else:1005 else:
999 result = branch_to.pull(1006 result = branch_to.pull(
1000 branch_from, overwrite, revision_id, local=local)1007 branch_from, overwrite, revision_id, local=local)
@@ -1363,10 +1370,13 @@
13631370
1364 _see_also = ['pull', 'working-trees', 'status-flags']1371 _see_also = ['pull', 'working-trees', 'status-flags']
1365 takes_args = ['dir?']1372 takes_args = ['dir?']
1366 takes_options = ['revision']1373 takes_options = ['revision',
1374 Option('show-base',
1375 help="Show base revision text in conflicts."),
1376 ]
1367 aliases = ['up']1377 aliases = ['up']
13681378
1369 def run(self, dir='.', revision=None):1379 def run(self, dir='.', revision=None, show_base=None):
1370 if revision is not None and len(revision) != 1:1380 if revision is not None and len(revision) != 1:
1371 raise errors.BzrCommandError(1381 raise errors.BzrCommandError(
1372 "bzr update --revision takes exactly one revision")1382 "bzr update --revision takes exactly one revision")
@@ -1412,7 +1422,8 @@
1412 change_reporter,1422 change_reporter,
1413 possible_transports=possible_transports,1423 possible_transports=possible_transports,
1414 revision=revision_id,1424 revision=revision_id,
1415 old_tip=old_tip)1425 old_tip=old_tip,
1426 show_base=show_base)
1416 except errors.NoSuchRevision, e:1427 except errors.NoSuchRevision, e:
1417 raise errors.BzrCommandError(1428 raise errors.BzrCommandError(
1418 "branch has no revision %s\n"1429 "branch has no revision %s\n"
14191430
=== modified file 'bzrlib/tests/blackbox/test_pull.py'
--- bzrlib/tests/blackbox/test_pull.py 2010-06-11 07:32:12 +0000
+++ bzrlib/tests/blackbox/test_pull.py 2010-09-27 20:26:42 +0000
@@ -453,3 +453,46 @@
453 out, err = self.run_bzr(['pull', '-d', 'to', 'from'])453 out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
454 self.assertContainsRe(err,454 self.assertContainsRe(err,
455 "(?m)Fetching into experimental format")455 "(?m)Fetching into experimental format")
456
457 def test_pull_show_base(self):
458 """bzr pull supports --show-base
459
460 see https://bugs.launchpad.net/bzr/+bug/202374"""
461 # create two trees with conflicts, setup conflict, check that
462 # conflicted file looks correct
463 a_tree = self.example_branch('a')
464 b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
465
466 f = open(pathjoin('a', 'hello'),'wt')
467 f.write('fee')
468 f.close()
469 a_tree.commit('fee')
470
471 f = open(pathjoin('b', 'hello'),'wt')
472 f.write('fie')
473 f.close()
474
475 out,err=self.run_bzr(['pull','-d','b','a','--show-base'])
476
477 # check for message here
478 self.assertEqual(err,
479 ' M hello\nText conflict in hello\n1 conflicts encountered.\n')
480
481 self.assertEqualDiff('<<<<<<< TREE\n'
482 'fie||||||| BASE-REVISION\n'
483 'foo=======\n'
484 'fee>>>>>>> MERGE-SOURCE\n',
485 open(pathjoin('b', 'hello')).read())
486
487 def test_pull_show_base_working_tree_only(self):
488 """--show-base only allowed if there's a working tree
489
490 see https://bugs.launchpad.net/bzr/+bug/202374"""
491 # create a branch, see that --show-base fails
492 self.make_branch('from')
493 self.make_branch('to')
494 out=self.run_bzr(['pull','-d','to','from','--show-base'],retcode=3)
495 self.assertEqual(out,
496 ('','bzr: ERROR: Need working tree for --show-base.\n'))
497
498
456499
=== modified file 'bzrlib/tests/blackbox/test_update.py'
--- bzrlib/tests/blackbox/test_update.py 2010-04-14 04:48:00 +0000
+++ bzrlib/tests/blackbox/test_update.py 2010-09-27 20:26:42 +0000
@@ -359,6 +359,44 @@
3592>Updated to revision 2 of branch .../master3592>Updated to revision 2 of branch .../master
360''')360''')
361361
362 def test_update_show_base(self):
363 """bzr update support --show-base
364
365 see https://bugs.launchpad.net/bzr/+bug/202374"""
366
367 tree=self.make_branch_and_tree('.')
368
369 f = open('hello','wt')
370 f.write('foo')
371 f.close()
372 tree.add('hello')
373 tree.commit('fie')
374
375 f = open('hello','wt')
376 f.write('fee')
377 f.close()
378 tree.commit('fee')
379
380 #tree.update() gives no such revision, so ...
381 self.run_bzr(['update','-r1'])
382
383 #create conflict
384 f = open('hello','wt')
385 f.write('fie')
386 f.close()
387
388 out, err = self.run_bzr(['update','--show-base'],retcode=1)
389
390 # check for conflict notification
391 self.assertContainsString(err,
392 ' M hello\nText conflict in hello\n1 conflicts encountered.\n')
393
394 self.assertEqualDiff('<<<<<<< TREE\n'
395 'fie||||||| BASE-REVISION\n'
396 'foo=======\n'
397 'fee>>>>>>> MERGE-SOURCE\n',
398 open('hello').read())
399
362 def test_update_checkout_prevent_double_merge(self):400 def test_update_checkout_prevent_double_merge(self):
363 """"Launchpad bug 113809 in bzr "update performs two merges"401 """"Launchpad bug 113809 in bzr "update performs two merges"
364 https://launchpad.net/bugs/113809"""402 https://launchpad.net/bugs/113809"""
365403
=== modified file 'bzrlib/workingtree.py'
--- bzrlib/workingtree.py 2010-08-20 19:07:17 +0000
+++ bzrlib/workingtree.py 2010-09-27 20:26:42 +0000
@@ -1663,7 +1663,8 @@
16631663
1664 @needs_write_lock1664 @needs_write_lock
1665 def pull(self, source, overwrite=False, stop_revision=None,1665 def pull(self, source, overwrite=False, stop_revision=None,
1666 change_reporter=None, possible_transports=None, local=False):1666 change_reporter=None, possible_transports=None, local=False,
1667 show_base=False):
1667 source.lock_read()1668 source.lock_read()
1668 try:1669 try:
1669 old_revision_info = self.branch.last_revision_info()1670 old_revision_info = self.branch.last_revision_info()
@@ -1683,7 +1684,8 @@
1683 basis_tree,1684 basis_tree,
1684 this_tree=self,1685 this_tree=self,
1685 pb=None,1686 pb=None,
1686 change_reporter=change_reporter)1687 change_reporter=change_reporter,
1688 show_base=show_base)
1687 basis_root_id = basis_tree.get_root_id()1689 basis_root_id = basis_tree.get_root_id()
1688 new_root_id = new_basis_tree.get_root_id()1690 new_root_id = new_basis_tree.get_root_id()
1689 if basis_root_id != new_root_id:1691 if basis_root_id != new_root_id:
@@ -2261,7 +2263,7 @@
2261 _marker = object()2263 _marker = object()
22622264
2263 def update(self, change_reporter=None, possible_transports=None,2265 def update(self, change_reporter=None, possible_transports=None,
2264 revision=None, old_tip=_marker):2266 revision=None, old_tip=_marker, show_base=False):
2265 """Update a working tree along its branch.2267 """Update a working tree along its branch.
22662268
2267 This will update the branch if its bound too, which means we have2269 This will update the branch if its bound too, which means we have
@@ -2304,12 +2306,13 @@
2304 else:2306 else:
2305 if old_tip is self._marker:2307 if old_tip is self._marker:
2306 old_tip = None2308 old_tip = None
2307 return self._update_tree(old_tip, change_reporter, revision)2309 return self._update_tree(old_tip, change_reporter, revision, show_base)
2308 finally:2310 finally:
2309 self.unlock()2311 self.unlock()
23102312
2311 @needs_tree_write_lock2313 @needs_tree_write_lock
2312 def _update_tree(self, old_tip=None, change_reporter=None, revision=None):2314 def _update_tree(self, old_tip=None, change_reporter=None, revision=None,
2315 show_base=False):
2313 """Update a tree to the master branch.2316 """Update a tree to the master branch.
23142317
2315 :param old_tip: if supplied, the previous tip revision the branch,2318 :param old_tip: if supplied, the previous tip revision the branch,
@@ -2342,7 +2345,8 @@
2342 other_tree = self.branch.repository.revision_tree(old_tip)2345 other_tree = self.branch.repository.revision_tree(old_tip)
2343 nb_conflicts = merge.merge_inner(self.branch, other_tree,2346 nb_conflicts = merge.merge_inner(self.branch, other_tree,
2344 base_tree, this_tree=self,2347 base_tree, this_tree=self,
2345 change_reporter=change_reporter)2348 change_reporter=change_reporter,
2349 show_base=show_base)
2346 if nb_conflicts:2350 if nb_conflicts:
2347 self.add_parent_tree((old_tip, other_tree))2351 self.add_parent_tree((old_tip, other_tree))
2348 trace.note('Rerun update after fixing the conflicts.')2352 trace.note('Rerun update after fixing the conflicts.')
@@ -2372,7 +2376,8 @@
23722376
2373 nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,2377 nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
2374 this_tree=self,2378 this_tree=self,
2375 change_reporter=change_reporter)2379 change_reporter=change_reporter,
2380 show_base=show_base)
2376 self.set_last_revision(revision)2381 self.set_last_revision(revision)
2377 # TODO - dedup parents list with things merged by pull ?2382 # TODO - dedup parents list with things merged by pull ?
2378 # reuse the tree we've updated to to set the basis:2383 # reuse the tree we've updated to to set the basis: