Rev 4534: (abentley) Implement merge --interactive in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Tue Jul 14 16:05:11 BST 2009
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 4534 [merge]
revision-id: pqm at pqm.ubuntu.com-20090714150506-zspaa7037mm7x9hi
parent: pqm at pqm.ubuntu.com-20090714111244-zzen702pevyjugjr
parent: aaron at aaronbentley.com-20090714135621-7lrhvrwqkwa4xi5y
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2009-07-14 16:05:06 +0100
message:
(abentley) Implement merge --interactive
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/shelf.py prepare_shelf.py-20081005181341-n74qe6gu1e65ad4v-1
bzrlib/shelf_ui.py shelver.py-20081005210102-33worgzwrtdw0yrm-1
bzrlib/tests/test_shelf.py test_prepare_shelf.p-20081005181341-n74qe6gu1e65ad4v-2
bzrlib/tests/test_shelf_ui.py test_shelf_ui.py-20081027155203-wtcuazg85wp9u4fv-1
=== modified file 'NEWS'
--- a/NEWS 2009-07-13 13:25:18 +0000
+++ b/NEWS 2009-07-14 13:19:52 +0000
@@ -16,6 +16,9 @@
New Features
************
+* ``merge --interactive`` applies a user-selected portion of the merge. The UI
+ is similar to ``shelve``. (Aaron Bentley)
+
Bug Fixes
*********
=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py 2009-07-10 08:33:11 +0000
+++ b/bzrlib/builtins.py 2009-07-14 13:56:21 +0000
@@ -3573,6 +3573,9 @@
merge refuses to run if there are any uncommitted changes, unless
--force is given.
+ To select only some changes to merge, use "merge -i", which will prompt
+ you to apply each diff hunk and file change, similar to "shelve".
+
:Examples:
To merge the latest revision from bzr.dev::
@@ -3616,7 +3619,10 @@
short_name='d',
type=unicode,
),
- Option('preview', help='Instead of merging, show a diff of the merge.')
+ Option('preview', help='Instead of merging, show a diff of the'
+ ' merge.'),
+ Option('interactive', help='Select changes interactively.',
+ short_name='i')
]
def run(self, location=None, revision=None, force=False,
@@ -3624,6 +3630,7 @@
uncommitted=False, pull=False,
directory=None,
preview=False,
+ interactive=False,
):
if merge_type is None:
merge_type = _mod_merge.Merge3Merger
@@ -3700,7 +3707,9 @@
return 0
merger.check_basis(False)
if preview:
- return self._do_preview(merger)
+ return self._do_preview(merger, cleanups)
+ elif interactive:
+ return self._do_interactive(merger, cleanups)
else:
return self._do_merge(merger, change_reporter, allow_pending,
verified)
@@ -3708,16 +3717,18 @@
for cleanup in reversed(cleanups):
cleanup()
- def _do_preview(self, merger):
- from bzrlib.diff import show_diff_trees
+ def _get_preview(self, merger, cleanups):
tree_merger = merger.make_merger()
tt = tree_merger.make_preview_transform()
- try:
- result_tree = tt.get_preview_tree()
- show_diff_trees(merger.this_tree, result_tree, self.outf,
- old_label='', new_label='')
- finally:
- tt.finalize()
+ cleanups.append(tt.finalize)
+ result_tree = tt.get_preview_tree()
+ return result_tree
+
+ def _do_preview(self, merger, cleanups):
+ from bzrlib.diff import show_diff_trees
+ result_tree = self._get_preview(merger, cleanups)
+ show_diff_trees(merger.this_tree, result_tree, self.outf,
+ old_label='', new_label='')
def _do_merge(self, merger, change_reporter, allow_pending, verified):
merger.change_reporter = change_reporter
@@ -3731,6 +3742,21 @@
else:
return 0
+ def _do_interactive(self, merger, cleanups):
+ """Perform an interactive merge.
+
+ This works by generating a preview tree of the merge, then using
+ Shelver to selectively remove the differences between the working tree
+ and the preview tree.
+ """
+ from bzrlib import shelf_ui
+ result_tree = self._get_preview(merger, cleanups)
+ writer = bzrlib.option.diff_writer_registry.get()
+ shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
+ reporter=shelf_ui.ApplyReporter(),
+ diff_writer=writer(sys.stdout))
+ shelver.run()
+
def sanity_check_merger(self, merger):
if (merger.show_base and
not merger.merge_type is _mod_merge.Merge3Merger):
=== modified file 'bzrlib/shelf.py'
--- a/bzrlib/shelf.py 2009-06-10 03:56:49 +0000
+++ b/bzrlib/shelf.py 2009-07-13 17:35:09 +0000
@@ -96,6 +96,21 @@
elif changed:
yield ('modify text', file_id)
+ def shelve_change(self, change):
+ """Shelve a change in the iter_shelvable format."""
+ if change[0] == 'rename':
+ self.shelve_rename(change[1])
+ elif change[0] == 'delete file':
+ self.shelve_deletion(change[1])
+ elif change[0] == 'add file':
+ self.shelve_creation(change[1])
+ elif change[0] == 'change kind':
+ self.shelve_content_change(change[1])
+ elif change[0] == 'modify target':
+ self.shelve_modify_target(change[1])
+ else:
+ raise ValueError('Unknown change kind: "%s"' % change[0])
+
def shelve_rename(self, file_id):
"""Shelve a file rename.
=== modified file 'bzrlib/shelf_ui.py'
--- a/bzrlib/shelf_ui.py 2009-06-26 03:44:30 +0000
+++ b/bzrlib/shelf_ui.py 2009-07-13 13:00:27 +0000
@@ -37,20 +37,75 @@
class ShelfReporter(object):
+ vocab = {'add file': 'Shelve adding file "%(path)s"?',
+ 'binary': 'Shelve binary changes?',
+ 'change kind': 'Shelve changing "%s" from %(other)s'
+ ' to %(this)s?',
+ 'delete file': 'Shelve removing file "%(path)s"?',
+ 'final': 'Shelve %d change(s)?',
+ 'hunk': 'Shelve?',
+ 'modify target': 'Shelve changing target of'
+ ' "%(path)s" from "%(other)s" to "%(this)s"?',
+ 'rename': 'Shelve renaming "%(other)s" =>'
+ ' "%(this)s"?'
+ }
+
+ invert_diff = False
+
def __init__(self):
self.delta_reporter = delta._ChangeReporter()
def no_changes(self):
+ """Report that no changes were selected to apply."""
trace.warning('No changes to shelve.')
def shelved_id(self, shelf_id):
+ """Report the id changes were shelved to."""
trace.note('Changes shelved with id "%d".' % shelf_id)
+ def changes_destroyed(self):
+ """Report that changes were made without shelving."""
+ trace.note('Selected changes destroyed.')
+
def selected_changes(self, transform):
+ """Report the changes that were selected."""
trace.note("Selected changes:")
changes = transform.iter_changes()
delta.report_changes(changes, self.delta_reporter)
+ def prompt_change(self, change):
+ """Determine the prompt for a change to apply."""
+ if change[0] == 'rename':
+ vals = {'this': change[3], 'other': change[2]}
+ elif change[0] == 'change kind':
+ vals = {'path': change[4], 'other': change[2], 'this': change[3]}
+ elif change[0] == 'modify target':
+ vals = {'path': change[2], 'other': change[3], 'this': change[4]}
+ else:
+ vals = {'path': change[3]}
+ prompt = self.vocab[change[0]] % vals
+ return prompt
+
+
+class ApplyReporter(ShelfReporter):
+
+ vocab = {'add file': 'Delete file "%(path)s"?',
+ 'binary': 'Apply binary changes?',
+ 'change kind': 'Change "%(path)s" from %(this)s'
+ ' to %(other)s?',
+ 'delete file': 'Add file "%(path)s"?',
+ 'final': 'Apply %d change(s)?',
+ 'hunk': 'Apply change?',
+ 'modify target': 'Change target of'
+ ' "%(path)s" from "%(this)s" to "%(other)s"?',
+ 'rename': 'Rename "%(this)s" => "%(other)s"?',
+ }
+
+ invert_diff = True
+
+ def changes_destroyed(self):
+ pass
+
class Shelver(object):
"""Interactively shelve the changes in a working tree."""
@@ -70,6 +125,7 @@
:param destroy: Change the working tree without storing the shelved
changes.
:param manager: The shelf manager to use.
+ :param reporter: Object for reporting changes to user.
"""
self.work_tree = work_tree
self.target_tree = target_tree
@@ -121,41 +177,20 @@
changes_shelved += self.handle_modify_text(creator,
change[1])
except errors.BinaryFile:
- if self.prompt_bool('Shelve binary changes?'):
+ if self.prompt_bool(self.reporter.vocab['binary']):
changes_shelved += 1
creator.shelve_content_change(change[1])
- if change[0] == 'add file':
- if self.prompt_bool('Shelve adding file "%s"?'
- % change[3]):
- creator.shelve_creation(change[1])
- changes_shelved += 1
- if change[0] == 'delete file':
- if self.prompt_bool('Shelve removing file "%s"?'
- % change[3]):
- creator.shelve_deletion(change[1])
- changes_shelved += 1
- if change[0] == 'change kind':
- if self.prompt_bool('Shelve changing "%s" from %s to %s? '
- % (change[4], change[2], change[3])):
- creator.shelve_content_change(change[1])
- changes_shelved += 1
- if change[0] == 'rename':
- if self.prompt_bool('Shelve renaming "%s" => "%s"?' %
- change[2:]):
- creator.shelve_rename(change[1])
- changes_shelved += 1
- if change[0] == 'modify target':
- if self.prompt_bool('Shelve changing target of "%s" '
- 'from "%s" to "%s"?' % change[2:]):
- creator.shelve_modify_target(change[1])
+ else:
+ if self.prompt_bool(self.reporter.prompt_change(change)):
+ creator.shelve_change(change)
changes_shelved += 1
if changes_shelved > 0:
self.reporter.selected_changes(creator.work_transform)
if (self.auto_apply or self.prompt_bool(
- 'Shelve %d change(s)?' % changes_shelved)):
+ self.reporter.vocab['final'] % changes_shelved)):
if self.destroy:
creator.transform()
- trace.note('Selected changes destroyed.')
+ self.reporter.changes_destroyed()
else:
shelf_id = self.manager.shelve_changes(creator,
self.message)
@@ -166,17 +201,24 @@
shutil.rmtree(self.tempdir)
creator.finalize()
- def get_parsed_patch(self, file_id):
+ def get_parsed_patch(self, file_id, invert=False):
"""Return a parsed version of a file's patch.
:param file_id: The id of the file to generate a patch for.
+ :param invert: If True, provide an inverted patch (insertions displayed
+ as removals, removals displayed as insertions).
:return: A patches.Patch.
"""
- old_path = self.target_tree.id2path(file_id)
- new_path = self.work_tree.id2path(file_id)
diff_file = StringIO()
- text_differ = diff.DiffText(self.target_tree, self.work_tree,
- diff_file)
+ if invert:
+ old_tree = self.work_tree
+ new_tree = self.target_tree
+ else:
+ old_tree = self.target_tree
+ new_tree = self.work_tree
+ old_path = old_tree.id2path(file_id)
+ new_path = new_tree.id2path(file_id)
+ text_differ = diff.DiffText(old_tree, new_tree, diff_file)
patch = text_differ.diff(file_id, old_path, new_path, 'file', 'file')
diff_file.seek(0)
return patches.parse_patch(diff_file)
@@ -223,30 +265,44 @@
def handle_modify_text(self, creator, file_id):
"""Provide diff hunk selection for modified text.
+ If self.reporter.invert_diff is True, the diff is inverted so that
+ insertions are displayed as removals and vice versa.
+
:param creator: a ShelfCreator
:param file_id: The id of the file to shelve.
:return: number of shelved hunks.
"""
- target_lines = self.target_tree.get_file_lines(file_id)
+ if self.reporter.invert_diff:
+ target_lines = self.work_tree.get_file_lines(file_id)
+ else:
+ target_lines = self.target_tree.get_file_lines(file_id)
textfile.check_text_lines(self.work_tree.get_file_lines(file_id))
textfile.check_text_lines(target_lines)
- parsed = self.get_parsed_patch(file_id)
+ parsed = self.get_parsed_patch(file_id, self.reporter.invert_diff)
final_hunks = []
if not self.auto:
offset = 0
self.diff_writer.write(parsed.get_header())
for hunk in parsed.hunks:
self.diff_writer.write(str(hunk))
- if not self.prompt_bool('Shelve?'):
+ selected = self.prompt_bool(self.reporter.vocab['hunk'])
+ if not self.reporter.invert_diff:
+ selected = (not selected)
+ if selected:
hunk.mod_pos += offset
final_hunks.append(hunk)
else:
offset -= (hunk.mod_range - hunk.orig_range)
sys.stdout.flush()
- if len(parsed.hunks) == len(final_hunks):
+ if not self.reporter.invert_diff and (
+ len(parsed.hunks) == len(final_hunks)):
+ return 0
+ if self.reporter.invert_diff and len(final_hunks) == 0:
return 0
patched = patches.iter_patched_from_hunks(target_lines, final_hunks)
creator.shelve_lines(file_id, list(patched))
+ if self.reporter.invert_diff:
+ return len(final_hunks)
return len(parsed.hunks) - len(final_hunks)
=== modified file 'bzrlib/tests/test_shelf.py'
--- a/bzrlib/tests/test_shelf.py 2009-05-06 05:36:28 +0000
+++ b/bzrlib/tests/test_shelf.py 2009-07-13 17:35:09 +0000
@@ -40,7 +40,7 @@
class TestPrepareShelf(tests.TestCaseWithTransport):
- def test_shelve_rename(self):
+ def prepare_shelve_rename(self):
tree = self.make_branch_and_tree('.')
self.build_tree(['foo'])
tree.add(['foo'], ['foo-id'])
@@ -50,7 +50,9 @@
self.addCleanup(creator.finalize)
self.assertEqual([('rename', 'foo-id', 'foo', 'bar')],
list(creator.iter_shelvable()))
- creator.shelve_rename('foo-id')
+ return creator
+
+ def check_shelve_rename(self, creator):
work_trans_id = creator.work_transform.trans_id_file_id('foo-id')
self.assertEqual('foo', creator.work_transform.final_name(
work_trans_id))
@@ -58,7 +60,17 @@
self.assertEqual('bar', creator.shelf_transform.final_name(
shelf_trans_id))
- def test_shelve_move(self):
+ def test_shelve_rename(self):
+ creator = self.prepare_shelve_rename()
+ creator.shelve_rename('foo-id')
+ self.check_shelve_rename(creator)
+
+ def test_shelve_change_handles_rename(self):
+ creator = self.prepare_shelve_rename()
+ creator.shelve_change(('rename', 'foo-id', 'foo', 'bar'))
+ self.check_shelve_rename(creator)
+
+ def prepare_shelve_move(self):
tree = self.make_branch_and_tree('.')
self.build_tree(['foo/', 'bar/', 'foo/baz'])
tree.add(['foo', 'bar', 'foo/baz'], ['foo-id', 'bar-id', 'baz-id'])
@@ -68,7 +80,9 @@
self.addCleanup(creator.finalize)
self.assertEqual([('rename', 'baz-id', 'foo/baz', 'bar/baz')],
list(creator.iter_shelvable()))
- creator.shelve_rename('baz-id')
+ return creator, tree
+
+ def check_shelve_move(self, creator, tree):
work_trans_id = creator.work_transform.trans_id_file_id('baz-id')
work_foo = creator.work_transform.trans_id_file_id('foo-id')
self.assertEqual(work_foo, creator.work_transform.final_parent(
@@ -80,6 +94,16 @@
creator.transform()
self.assertEqual('foo/baz', tree.id2path('baz-id'))
+ def test_shelve_move(self):
+ creator, tree = self.prepare_shelve_move()
+ creator.shelve_rename('baz-id')
+ self.check_shelve_move(creator, tree)
+
+ def test_shelve_change_handles_move(self):
+ creator, tree = self.prepare_shelve_move()
+ creator.shelve_change(('rename', 'baz-id', 'foo/baz', 'bar/baz'))
+ self.check_shelve_move(creator, tree)
+
def assertShelvedFileEqual(self, expected_content, creator, file_id):
s_trans_id = creator.shelf_transform.trans_id_file_id(file_id)
shelf_file = creator.shelf_transform._limbo_name(s_trans_id)
@@ -102,7 +126,8 @@
self.assertFileEqual('a\nc\n', 'foo')
self.assertShelvedFileEqual('b\na\n', creator, 'foo-id')
- def test_shelve_creation(self):
+
+ def prepare_shelve_creation(self):
tree = self.make_branch_and_tree('.')
tree.lock_write()
self.addCleanup(tree.unlock)
@@ -114,9 +139,9 @@
self.assertEqual([('add file', 'bar-id', 'directory', 'bar'),
('add file', 'foo-id', 'file', 'foo')],
sorted(list(creator.iter_shelvable())))
- creator.shelve_creation('foo-id')
- creator.shelve_creation('bar-id')
- creator.transform()
+ return creator, tree
+
+ def check_shelve_creation(self, creator, tree):
self.assertRaises(StopIteration,
tree.iter_entries_by_dir(['foo-id']).next)
s_trans_id = creator.shelf_transform.trans_id_file_id('foo-id')
@@ -129,7 +154,22 @@
self.assertEqual('directory',
creator.shelf_transform.final_kind(s_bar_trans_id))
- def _test_shelve_symlink_creation(self, link_name, link_target):
+ def test_shelve_creation(self):
+ creator, tree = self.prepare_shelve_creation()
+ creator.shelve_creation('foo-id')
+ creator.shelve_creation('bar-id')
+ creator.transform()
+ self.check_shelve_creation(creator, tree)
+
+ def test_shelve_change_handles_creation(self):
+ creator, tree = self.prepare_shelve_creation()
+ creator.shelve_change(('add file', 'foo-id', 'file', 'foo'))
+ creator.shelve_change(('add file', 'bar-id', 'directory', 'bar'))
+ creator.transform()
+ self.check_shelve_creation(creator, tree)
+
+ def _test_shelve_symlink_creation(self, link_name, link_target,
+ shelve_change=False):
self.requireFeature(tests.SymlinkFeature)
tree = self.make_branch_and_tree('.')
tree.lock_write()
@@ -141,7 +181,10 @@
self.addCleanup(creator.finalize)
self.assertEqual([('add file', 'foo-id', 'symlink', link_name)],
list(creator.iter_shelvable()))
- creator.shelve_creation('foo-id')
+ if shelve_change:
+ creator.shelve_change(('add file', 'foo-id', 'symlink', link_name))
+ else:
+ creator.shelve_creation('foo-id')
creator.transform()
s_trans_id = creator.shelf_transform.trans_id_file_id('foo-id')
self.failIfExists(link_name)
@@ -158,8 +201,12 @@
self._test_shelve_symlink_creation(u'fo\N{Euro Sign}o',
u'b\N{Euro Sign}ar')
+ def test_shelve_change_handles_symlink_creation(self):
+ self._test_shelve_symlink_creation('foo', 'bar', shelve_change=True)
+
def _test_shelve_symlink_target_change(self, link_name,
- old_target, new_target):
+ old_target, new_target,
+ shelve_change=False):
self.requireFeature(tests.SymlinkFeature)
tree = self.make_branch_and_tree('.')
tree.lock_write()
@@ -174,7 +221,11 @@
self.assertEqual([('modify target', 'foo-id', link_name,
old_target, new_target)],
list(creator.iter_shelvable()))
- creator.shelve_modify_target('foo-id')
+ if shelve_change:
+ creator.shelve_change(('modify target', 'foo-id', link_name,
+ old_target, new_target))
+ else:
+ creator.shelve_modify_target('foo-id')
creator.transform()
self.assertEqual(old_target, osutils.readlink(link_name))
s_trans_id = creator.shelf_transform.trans_id_file_id('foo-id')
@@ -191,6 +242,10 @@
self._test_shelve_symlink_target_change(
u'fo\N{Euro Sign}o', u'b\N{Euro Sign}ar', u'b\N{Euro Sign}az')
+ def test_shelve_change_handles_symlink_target_change(self):
+ self._test_shelve_symlink_target_change('foo', 'bar', 'baz',
+ shelve_change=True)
+
def test_shelve_creation_no_contents(self):
tree = self.make_branch_and_tree('.')
tree.lock_write()
@@ -213,7 +268,7 @@
creator.shelf_transform.final_file_id(s_trans_id))
self.failIfExists('foo')
- def test_shelve_deletion(self):
+ def prepare_shelve_deletion(self):
tree = self.make_branch_and_tree('tree')
tree.lock_write()
self.addCleanup(tree.unlock)
@@ -228,13 +283,27 @@
self.assertEqual([('delete file', 'bar-id', 'file', 'foo/bar'),
('delete file', 'foo-id', 'directory', 'foo')],
sorted(list(creator.iter_shelvable())))
- creator.shelve_deletion('foo-id')
- creator.shelve_deletion('bar-id')
- creator.transform()
+ return creator, tree
+
+ def check_shelve_deletion(self, tree):
self.assertTrue('foo-id' in tree)
self.assertTrue('bar-id' in tree)
self.assertFileEqual('baz', 'tree/foo/bar')
+ def test_shelve_deletion(self):
+ creator, tree = self.prepare_shelve_deletion()
+ creator.shelve_deletion('foo-id')
+ creator.shelve_deletion('bar-id')
+ creator.transform()
+ self.check_shelve_deletion(tree)
+
+ def test_shelve_change_handles_deletion(self):
+ creator, tree = self.prepare_shelve_deletion()
+ creator.shelve_change(('delete file', 'foo-id', 'directory', 'foo'))
+ creator.shelve_change(('delete file', 'bar-id', 'file', 'foo/bar'))
+ creator.transform()
+ self.check_shelve_deletion(tree)
+
def test_shelve_delete_contents(self):
tree = self.make_branch_and_tree('tree')
self.build_tree(['tree/foo',])
@@ -249,7 +318,7 @@
creator.transform()
self.failUnlessExists('tree/foo')
- def test_shelve_change_kind(self):
+ def prepare_shelve_change_kind(self):
tree = self.make_branch_and_tree('tree')
self.build_tree_contents([('tree/foo', 'bar')])
tree.add('foo', 'foo-id')
@@ -260,12 +329,33 @@
self.addCleanup(creator.finalize)
self.assertEqual([('change kind', 'foo-id', 'file', 'directory',
'foo')], sorted(list(creator.iter_shelvable())))
+ return creator
+
+ def check_shelve_change_kind(self, creator):
+ self.assertFileEqual('bar', 'tree/foo')
+ s_trans_id = creator.shelf_transform.trans_id_file_id('foo-id')
+ self.assertEqual('directory',
+ creator.shelf_transform._new_contents[s_trans_id])
+
+ def test_shelve_change_kind(self):
+ creator = self.prepare_shelve_change_kind()
creator.shelve_content_change('foo-id')
creator.transform()
- self.assertFileEqual('bar', 'tree/foo')
- s_trans_id = creator.shelf_transform.trans_id_file_id('foo-id')
- self.assertEqual('directory',
- creator.shelf_transform._new_contents[s_trans_id])
+ self.check_shelve_change_kind(creator)
+
+ def test_shelve_change_handles_change_kind(self):
+ creator = self.prepare_shelve_change_kind()
+ creator.shelve_change(('change kind', 'foo-id', 'file', 'directory',
+ 'foo'))
+ creator.transform()
+ self.check_shelve_change_kind(creator)
+
+ def test_shelve_change_unknown_change(self):
+ tree = self.make_branch_and_tree('tree')
+ creator = shelf.ShelfCreator(tree, tree.basis_tree())
+ self.addCleanup(creator.finalize)
+ e = self.assertRaises(ValueError, creator.shelve_change, ('unknown',))
+ self.assertEqual('Unknown change kind: "unknown"', str(e))
def test_shelve_unversion(self):
tree = self.make_branch_and_tree('tree')
=== modified file 'bzrlib/tests/test_shelf_ui.py'
--- a/bzrlib/tests/test_shelf_ui.py 2009-03-23 14:59:43 +0000
+++ b/bzrlib/tests/test_shelf_ui.py 2009-07-14 13:19:52 +0000
@@ -27,10 +27,10 @@
def __init__(self, work_tree, target_tree, diff_writer=None,
auto=False, auto_apply=False, file_list=None, message=None,
- destroy=False):
+ destroy=False, reporter=None):
shelf_ui.Shelver.__init__(self, work_tree, target_tree, diff_writer,
auto, auto_apply, file_list, message,
- destroy)
+ destroy, reporter=reporter)
self.expected = []
self.diff_writer = StringIO()
@@ -165,6 +165,7 @@
shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
def test_shelve_modify_target(self):
+ self.requireFeature(tests.SymlinkFeature)
tree = self.create_shelvable_tree()
os.symlink('bar', 'tree/baz')
tree.add('baz', 'baz-id')
@@ -224,6 +225,108 @@
self.assertFileEqual(LINES_AJ, 'tree/foo')
+class TestApplyReporter(TestShelver):
+
+ def test_shelve_not_diff(self):
+ tree = self.create_shelvable_tree()
+ shelver = ExpectShelver(tree, tree.basis_tree(),
+ reporter=shelf_ui.ApplyReporter())
+ shelver.expect('Apply change? [yNfq?]', 'n')
+ shelver.expect('Apply change? [yNfq?]', 'n')
+ # No final shelving prompt because no changes were selected
+ shelver.run()
+ self.assertFileEqual(LINES_ZY, 'tree/foo')
+
+ def test_shelve_diff_no(self):
+ tree = self.create_shelvable_tree()
+ shelver = ExpectShelver(tree, tree.basis_tree(),
+ reporter=shelf_ui.ApplyReporter())
+ shelver.expect('Apply change? [yNfq?]', 'y')
+ shelver.expect('Apply change? [yNfq?]', 'y')
+ shelver.expect('Apply 2 change(s)? [yNfq?]', 'n')
+ shelver.run()
+ self.assertFileEqual(LINES_ZY, 'tree/foo')
+
+ def test_shelve_diff(self):
+ tree = self.create_shelvable_tree()
+ shelver = ExpectShelver(tree, tree.basis_tree(),
+ reporter=shelf_ui.ApplyReporter())
+ shelver.expect('Apply change? [yNfq?]', 'y')
+ shelver.expect('Apply change? [yNfq?]', 'y')
+ shelver.expect('Apply 2 change(s)? [yNfq?]', 'y')
+ shelver.run()
+ self.assertFileEqual(LINES_AJ, 'tree/foo')
+
+ def test_shelve_binary_change(self):
+ tree = self.create_shelvable_tree()
+ self.build_tree_contents([('tree/foo', '\x00')])
+ shelver = ExpectShelver(tree, tree.basis_tree(),
+ reporter=shelf_ui.ApplyReporter())
+ shelver.expect('Apply binary changes? [yNfq?]', 'y')
+ shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
+ shelver.run()
+ self.assertFileEqual(LINES_AJ, 'tree/foo')
+
+ def test_shelve_rename(self):
+ tree = self.create_shelvable_tree()
+ tree.rename_one('foo', 'bar')
+ shelver = ExpectShelver(tree, tree.basis_tree(),
+ reporter=shelf_ui.ApplyReporter())
+ shelver.expect('Rename "bar" => "foo"? [yNfq?]', 'y')
+ shelver.expect('Apply change? [yNfq?]', 'y')
+ shelver.expect('Apply change? [yNfq?]', 'y')
+ shelver.expect('Apply 3 change(s)? [yNfq?]', 'y')
+ shelver.run()
+ self.assertFileEqual(LINES_AJ, 'tree/foo')
+
+ def test_shelve_deletion(self):
+ tree = self.create_shelvable_tree()
+ os.unlink('tree/foo')
+ shelver = ExpectShelver(tree, tree.basis_tree(),
+ reporter=shelf_ui.ApplyReporter())
+ shelver.expect('Add file "foo"? [yNfq?]', 'y')
+ shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
+ shelver.run()
+ self.assertFileEqual(LINES_AJ, 'tree/foo')
+
+ def test_shelve_creation(self):
+ tree = self.make_branch_and_tree('tree')
+ tree.commit('add tree root')
+ self.build_tree(['tree/foo'])
+ tree.add('foo')
+ shelver = ExpectShelver(tree, tree.basis_tree(),
+ reporter=shelf_ui.ApplyReporter())
+ shelver.expect('Delete file "foo"? [yNfq?]', 'y')
+ shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
+ shelver.run()
+ self.failIfExists('tree/foo')
+
+ def test_shelve_kind_change(self):
+ tree = self.create_shelvable_tree()
+ os.unlink('tree/foo')
+ os.mkdir('tree/foo')
+ shelver = ExpectShelver(tree, tree.basis_tree(),
+ reporter=shelf_ui.ApplyReporter())
+ shelver.expect('Change "foo" from directory to a file? [yNfq?]', 'y')
+ shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
+
+ def test_shelve_modify_target(self):
+ self.requireFeature(tests.SymlinkFeature)
+ tree = self.create_shelvable_tree()
+ os.symlink('bar', 'tree/baz')
+ tree.add('baz', 'baz-id')
+ tree.commit("Add symlink")
+ os.unlink('tree/baz')
+ os.symlink('vax', 'tree/baz')
+ shelver = ExpectShelver(tree, tree.basis_tree(),
+ reporter=shelf_ui.ApplyReporter())
+ shelver.expect('Change target of "baz" from "vax" to "bar"? [yNfq?]',
+ 'y')
+ shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
+ shelver.run()
+ self.assertEqual('bar', os.readlink('tree/baz'))
+
+
class TestUnshelver(tests.TestCaseWithTransport):
def create_tree_with_shelf(self):
More information about the bazaar-commits
mailing list