Merge lp:~vila/bzr/638451-malformed into lp:bzr

Proposed by Vincent Ladeuil on 2010-11-10
Status: Superseded
Proposed branch: lp:~vila/bzr/638451-malformed
Merge into: lp:bzr
Prerequisite: lp:~vila/bzr/reports-conflict-resolved
Diff against target: 244 lines (+102/-56)
7 files modified
bzrlib/conflicts.py (+31/-0)
bzrlib/help_topics/en/conflict-types.txt (+15/-5)
bzrlib/tests/blackbox/test_resolve.py (+0/-45)
bzrlib/tests/test_conflicts.py (+44/-5)
bzrlib/transform.py (+1/-1)
doc/en/release-notes/bzr-2.3.txt (+5/-0)
doc/en/whats-new/whats-new-in-2.3.txt (+6/-0)
To merge this branch: bzr merge lp:~vila/bzr/638451-malformed
Reviewer Review Type Date Requested Status
bzr-core 2010-11-10 Pending
Review via email: mp+40567@code.launchpad.net

This proposal supersedes a proposal from 2010-11-10.

This proposal has been superseded by a proposal from 2010-11-24.

Commit Message

Implement resolve --take-this and --take-other for text conflicts.

Description of the Change

This fixes bug #638451 by implementing resolve --take-this and
--take-other for text conflicts.

While this is exactly the use case reported in the bug (ignore
the merge result, give me the content of the file as it was in
the OTHER branch), I'm a bit hesitant: is it really the thing to
do ?

The alternative would be to define:

- take-this choose THIS in conflicted regions
- take-other choose OTHER in conflicted regions
- take-both take THIS and OTHER in conflicted regions
- restore-this take THIS overriding all cleanly merged modifications
- restore-other take OTHER overriding all cleanly merged modifications

Thoughts ?

Implementing the other actions will be significantly more work
than this patch so I'd like to land it as a first step but with
an agreement on the action names.

To post a comment you must log in.
Martin von Gagern (gagern) wrote :

I'd like "resolve --take-this name" with its old semantics. To me it means "take name.THIS, discarding the files name.OTHER, name.BASE and name". It's a shorthand for a manual copy followed by a resolve.

I think the alternative you suggest makes some sense as well, in particular for file formats I prefer to edit with an application different from a text editor. But you'll have to examine the file for reproducable results: a more intelligent merger might in the future merge things successfully that fail to merge today, leading to different behaviour in this case.

I think the current --take-this should keep its name for the sake of backwards compatibility as well. Perhaps --prefer-this would be an option for adding the other approach one day.

Given that such an option would have to parse the current file in order to identify which changes have been merged and which haven't, I'm not sure I'd always trust its output. Perhaps for this reason you'd rather want this an option to remerge than to resolve. The workflow I've got in mind would be:
merge (reports difference)
optionally examine file (see where the changes are, what to look for later on)
remerge --{prefer|take}-this (decides on conflicting regions)
examine result (e.g. open in app, check XML wellformedness, or something like this)
resolve (takes the file off my TODO)
commit.

Martin von Gagern (gagern) wrote :

Gave the branch a quick test and verified that both my shell script and the blackbox test from my lp:~gagern/bzr/bug638451-resolve-take-other branch work as expected. I've also updated my branch to merge that unscripted blackbox test into the test suite. As scripted tests are far more readable, I wonder whether I should turn it into such a scripted test, or whether you will do so. Currently your branch doesn't seem to actually have a blackbox test for --tahe-this or --take-other, does it?

Vincent Ladeuil (vila) wrote :

>>>>> Martin von Gagern <email address hidden> writes:

    > Gave the branch a quick test and verified that both my shell
    > script and the blackbox test from my
    > lp:~gagern/bzr/bug638451-resolve-take-other branch work as
    > expected.

Great, thanks !

    > I've also updated my branch to merge that unscripted blackbox test
    > into the test suite. As scripted tests are far more readable, I
    > wonder whether I should turn it into such a scripted test, or
    > whether you will do so.

    > Currently your branch doesn't seem to actually have a blackbox
    > test for --tahe-this or --take-other, does it?

No it doesn't, it has whitebox tests for it in
bt.test_conflicts.TestResolveTextConflicts.

These tests ensures that both --take-this and --take-other are covered as
well as merging both branches in both directions.

I didn't try to write blackbox tests ensuring the same coverage so far
and I'm not sure it would be the right approach. I agree they are more
readable and easier to write for people that doesn't have the needed
knowledge of the bzr internals but in the long run we don't want to
write blackbox tests if more precise tests can be written as they are
harder to debug when they fail (if only because stdin/stdout/stderr are
redirected which forbids using pdb...).

There are still a lot more tests to be written though, for example I
suspect that if the user delete the .OTHER file before running bzr
resolve --take-other bzr will issue a traceback instead of a nicer error
message but there is a related FIXME in bt.test_conflicts about
robustness.

Neil Martinsen-Burrell (nmb) wrote :

As someone who experienced this bug (or its more recent kin) I can say that what I expected was that "resolve --take-other" would be a direct synonym for "cp file.OTHER file; bzr resolved file". I think that giving a simple meaning to --take-this and --take-other for text conflicts is more valuable than discussing the possibility of a richer set of actions. In the interest of backwards compatibility and simplicity, I think that --take-this and --take-other should retain the simple meanings and other, more complex actions should get new names.

John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 11/19/2010 9:27 AM, Neil Martinsen-Burrell wrote:
> As someone who experienced this bug (or its more recent kin) I can say that what I expected was that "resolve --take-other" would be a direct synonym for "cp file.OTHER file; bzr resolved file". I think that giving a simple meaning to --take-this and --take-other for text conflicts is more valuable than discussing the possibility of a richer set of actions. In the interest of backwards compatibility and simplicity, I think that --take-this and --take-other should retain the simple meanings and other, more complex actions should get new names.

Just to mention, that *my* belief would be to use the content from the
repository, rather than whatever was on disk. (In case someone modified
the files).

I don't know if that is how other people would see it, though.

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkzmp4kACgkQJdeBCYSNAAOHZACgtflEFqnEF/PVaZrPCoIF23jo
sFcAn32GnNw91vhtAukmymAnx1oCXwHd
=LWhY
-----END PGP SIGNATURE-----

Vincent Ladeuil (vila) wrote :

>>>>> John A Meinel <email address hidden> writes:

    > On 11/19/2010 9:27 AM, Neil Martinsen-Burrell wrote:
    >> As someone who experienced this bug (or its more recent kin) I can say that what I expected was that "resolve --take-other" would be a direct synonym for "cp file.OTHER file; bzr resolved file". I think that giving a simple meaning to --take-this and --take-other for text conflicts is more valuable than discussing the possibility of a richer set of actions. In the interest of backwards compatibility and simplicity, I think that --take-this and --take-other should retain the simple meanings and other, more complex actions should get new names.

    > Just to mention, that *my* belief would be to use the content from the
    > repository, rather than whatever was on disk. (In case someone modified
    > the files).

Yeah, there is a lot to discuss here.

If the user hasn't modified the .OTHER file, then the end result is the
same (the .OTHER file was created from the repo).

If the user has modified the .OTHER file, then restoring from the repo
will get rid of the user modifications which will be surprising (which
of the two scenarios is the most surprising is open to debate IMHO).

In the end, after conflicts have been resolved, and before committing,
you're supposed to review the result so whatever we implement, the
outcome is still visible to the user.

I'm still slightly prefering taking the .OTHER file over restoring from
the repo as it's more in line with the resolve 'spirit': We don't try to
guess, we just take what the user gives us (the plan is still to
document what --take-other does (whatever we chose)).

Also keep in mind that when 'bzr resolve --take-other' is used, without
any file (and all the related bug reports were cases were people using
that form) essentially means:

- ok, bzr has merged everything it can and left conflicts,

- for those conflicts, the other side is right, take it.

So we are not doing a 'pull --overwrite OTHER', we have merged cleanly
as best as we could. The user may have already resolved some other
conflicts manually (or not) and want to complete his resolution process.

So there is a grey area: the user did "stuff" and we should respect
that.

In my mind taking the file is closer to what GUI conflict handling
allows *inside* a text file with conflicts: you can chose the THIS part
or the OTHER part or both *including* any editing you may have done to
resolve the conflict in a finer way.

All in all, both features make sense and both can implemented as actions
(and both can also be achieved by other means), so we have to decide
which one we implement.

Thanks to all for the feedback anyway and don't forget to vote ;)

Martin Pool (mbp) wrote :

My expectation is that it would probably come from the repo, even if
you have modified the OTHER file. At any rate that is a reasonable
question so it should be documented.

I think you should mention this in the user guide, under 'resolving
conflicts', even adding a stub of this section if there is not one
already. So 'needsfixing' for docs, at least.

I agree with MvG, we could use --prefer-this/other.

This increases the tension about the conflation of:

 * update the working copy for a conflicted file
 * mark this conflict as having been resolved
 * forget this conflict ever occurred

However we can get there later.

--
Martin

John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 11/23/2010 1:34 AM, Martin Pool wrote:
> My expectation is that it would probably come from the repo, even if
> you have modified the OTHER file. At any rate that is a reasonable
> question so it should be documented.
>
> I think you should mention this in the user guide, under 'resolving
> conflicts', even adding a stub of this section if there is not one
> already. So 'needsfixing' for docs, at least.
>
> I agree with MvG, we could use --prefer-this/other.
>
> This increases the tension about the conflation of:
>
> * update the working copy for a conflicted file
> * mark this conflict as having been resolved
> * forget this conflict ever occurred
>
> However we can get there later.
>

I think the best answer to that I've seen so far is to add a new command.

"bzr resolved" to do what 'bzr resolve' does today. (Mark something as
manually resolved.)

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkzr0ygACgkQJdeBCYSNAAMzZgCgpgYKKgqdwuGXB9DbAFZ/4zP9
HfwAoLHqHcLarXoZ418UI774cb0L6XqI
=2VoH
-----END PGP SIGNATURE-----

Martin Pool (mbp) wrote :

On 24 November 2010 01:43, John Arbash Meinel <email address hidden> wrote:
>> This increases the tension about the conflation of:
>>
>>  * update the working copy for a conflicted file
>>  * mark this conflict as having been resolved
>>  * forget this conflict ever occurred
>>
>> However we can get there later.
>>
>
> I think the best answer to that I've seen so far is to add a new command.
>
> "bzr resolved" to do what 'bzr resolve' does today. (Mark something as
> manually resolved.)

I think we do need one command for the first action, and one for the
other two. I don't think we should have those commands differ only in
grammatical tense or only by one letter.

--
Martin

lp:~vila/bzr/638451-malformed updated on 2010-12-03
4654. By Vincent Ladeuil on 2010-11-12

Merge reports-conflict-resolved into 638451-malformed

4655. By Vincent Ladeuil on 2010-11-12

Merge reports-conflict-resolved into 638451-malformed

4656. By Vincent Ladeuil on 2010-11-13

Merge cleanup into 638451-malformed

4657. By Vincent Ladeuil on 2010-11-24

Merge cleanup into 638451-malformed

4658. By Vincent Ladeuil on 2010-11-24

Describe --take-this and --take-other actions for text conflicts.

4659. By Vincent Ladeuil on 2010-11-24

Fix typo.

4660. By Vincent Ladeuil on 2010-12-03

Merge cleanup into 638451-malformed

4661. By Vincent Ladeuil on 2010-12-03

Fix the news entry.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/conflicts.py'
2--- bzrlib/conflicts.py 2010-11-07 16:09:43 +0000
3+++ bzrlib/conflicts.py 2010-11-24 16:33:38 +0000
4@@ -626,6 +626,37 @@
5 def associated_filenames(self):
6 return [self.path + suffix for suffix in CONFLICT_SUFFIXES]
7
8+ def _resolve(self, tt, winner_suffix):
9+ """Resolve the conflict by copying one of .THIS or .OTHER into file.
10+
11+ :param tt: The TreeTransform where the conflict is resolved.
12+ :param winner_suffix: Either 'THIS' or 'OTHER'
13+
14+ The resolution is symmetric, when taking THIS, item.THIS is renamed
15+ into item and vice-versa. This takes one of the files as a whole
16+ ignoring every difference that could have been merged cleanly.
17+ """
18+ # To avoid useless copies, we switch item and item.winner_suffix, only
19+ # item will exist after the conflict has been resolved anyway.
20+ item_tid = tt.trans_id_file_id(self.file_id)
21+ item_parent_tid = tt.get_tree_parent(item_tid)
22+ winner_path = self.path + '.' + winner_suffix
23+ winner_tid = tt.trans_id_tree_path(winner_path)
24+ winner_parent_tid = tt.get_tree_parent(winner_tid)
25+ # Switch the paths to preserve the content
26+ tt.adjust_path(self.path, winner_parent_tid, winner_tid)
27+ tt.adjust_path(winner_path, item_parent_tid, item_tid)
28+ # Associate the file_id to the right content
29+ tt.unversion_file(item_tid)
30+ tt.version_file(self.file_id, winner_tid)
31+ tt.apply()
32+
33+ def action_take_this(self, tree):
34+ self._resolve_with_cleanups(tree, 'THIS')
35+
36+ def action_take_other(self, tree):
37+ self._resolve_with_cleanups(tree, 'OTHER')
38+
39
40 class HandledConflict(Conflict):
41 """A path problem that has been provisionally resolved.
42
43=== modified file 'bzrlib/help_topics/en/conflict-types.txt'
44--- bzrlib/help_topics/en/conflict-types.txt 2010-10-15 14:57:42 +0000
45+++ bzrlib/help_topics/en/conflict-types.txt 2010-11-24 16:33:38 +0000
46@@ -87,11 +87,11 @@
47
48 The correct resolution would be "Martin Pool released Bazaar."
49
50-You can handle text conflicts either by editing the main copy of the file, or
51-by invoking external tools on the THIS, OTHER and BASE versions. It's worth
52-mentioning that resolving text conflicts rarely involves picking one set of
53-changes over the other. More often, the two sets of changes must be
54-intelligently combined.
55+You can handle text conflicts either by editing the main copy of the file,
56+or by invoking external tools on the THIS, OTHER and BASE versions. It's
57+worth mentioning that resolving text conflicts rarely involves picking one
58+set of changes over the other (but see below when you encounter these
59+cases). More often, the two sets of changes must be intelligently combined.
60
61 If you edit the main copy, be sure to remove the herringbone markers. When
62 you are done editing, the file should look like it never had a conflict, and be
63@@ -104,6 +104,16 @@
64 ``.BASE``, ``.THIS`` and ``.OTHER`` files if they are still present in the
65 working tree.
66
67+
68+When you want to pick one set of changes over the other, you can use ``bzr
69+resolve`` with one of the following actions:
70+
71+* ``--action=take-this`` will issue ``mv FILE.THIS FILE``,
72+* ``--action=take-other`` will issue ``mv FILE.OTHER FILE``.
73+
74+Note that if you have modified ``FILE.THIS`` or ``FILE.OTHER``, these
75+modifications will be taken into account.
76+
77 Content conflicts
78 -----------------
79
80
81=== modified file 'bzrlib/tests/blackbox/test_resolve.py'
82--- bzrlib/tests/blackbox/test_resolve.py 2010-11-07 16:09:43 +0000
83+++ bzrlib/tests/blackbox/test_resolve.py 2010-11-24 16:33:38 +0000
84@@ -91,48 +91,3 @@
85 self.build_tree_contents([('tree/file', 'a\n')])
86 note = self.run_bzr('resolve', working_dir='tree')[1]
87 self.assertContainsRe(note, 'All conflicts resolved.')
88-
89-
90-class TestResolveSilentlyIgnore(script.TestCaseWithTransportAndScript):
91-
92- def test_bug_646961(self):
93- self.run_script("""\
94- $ bzr init base
95- Created a standalone tree (format: 2a)
96- $ cd base
97- $ echo >file1
98- $ bzr add
99- adding file1
100- $ bzr ci -m "stuff"
101- 2>Committing to: .../base/
102- 2>added file1
103- 2>Committed revision 1.
104- $ cd ..
105- $ bzr branch base branch
106- 2>Branched 1 revision(s).
107- $ cd base
108- $ echo "1" >> file1
109- $ bzr ci -m "branch 1"
110- 2>Committing to: .../base/
111- 2>modified file1
112- 2>Committed revision 2.
113- $ cd ../branch
114- $ echo "2" >> file1
115- $ bzr ci -m "branch 2"
116- 2>Committing to: .../branch/
117- 2>modified file1
118- 2>Committed revision 2.
119- $ cd ../base
120- $ bzr merge ../branch
121- 2> M file1
122- 2>Text conflict in file1
123- 2>1 conflicts encountered.
124- # The following succeeds silently without resolving the conflict
125- $ bzr resolve file1 --take-other
126- 2>0 conflict(s) resolved, 1 remaining
127- # The following wil fail when --take-other is implemented
128- # for text conflicts
129- $ bzr conflicts
130- Text conflict in file1
131- """)
132-
133
134=== modified file 'bzrlib/tests/test_conflicts.py'
135--- bzrlib/tests/test_conflicts.py 2010-11-10 11:15:50 +0000
136+++ bzrlib/tests/test_conflicts.py 2010-11-24 16:33:38 +0000
137@@ -197,11 +197,6 @@
138 self.run_script(self.preamble)
139
140
141-class TestResolveTextConflicts(TestResolveConflicts):
142- # TBC
143- pass
144-
145-
146 def mirror_scenarios(base_scenarios):
147 """Return a list of mirrored scenarios.
148
149@@ -372,6 +367,50 @@
150 check_other()
151
152
153+class TestResolveTextConflicts(TestParametrizedResolveConflicts):
154+
155+ _conflict_type = conflicts.TextConflict
156+
157+ # Set by the scenarios
158+ # path and file-id for the file involved in the conflict
159+ _path = None
160+ _file_id = None
161+
162+ scenarios = mirror_scenarios(
163+ [
164+ # File modified/deleted
165+ (dict(_base_actions='create_file',
166+ _path='file', _file_id='file-id'),
167+ ('filed_modified_A',
168+ dict(actions='modify_file_A', check='file_has_content_A')),
169+ ('file_modified_B',
170+ dict(actions='modify_file_B', check='file_has_content_B')),),
171+ ])
172+
173+ def do_create_file(self):
174+ return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
175+
176+ def do_modify_file_A(self):
177+ return [('modify', ('file-id', 'trunk content\nfeature A\n'))]
178+
179+ def do_modify_file_B(self):
180+ return [('modify', ('file-id', 'trunk content\nfeature B\n'))]
181+
182+ def check_file_has_content_A(self):
183+ self.assertFileEqual('trunk content\nfeature A\n', 'branch/file')
184+
185+ def check_file_has_content_B(self):
186+ self.assertFileEqual('trunk content\nfeature B\n', 'branch/file')
187+
188+ def _get_resolve_path_arg(self, wt, action):
189+ return self._path
190+
191+ def assertTextConflict(self, wt, c):
192+ self.assertEqual(self._file_id, c.file_id)
193+ self.assertEqual(self._path, c.path)
194+ _assert_conflict = assertTextConflict
195+
196+
197 class TestResolveContentsConflict(TestParametrizedResolveConflicts):
198
199 _conflict_type = conflicts.ContentsConflict
200
201=== modified file 'bzrlib/transform.py'
202--- bzrlib/transform.py 2010-11-19 16:28:25 +0000
203+++ bzrlib/transform.py 2010-11-24 16:33:38 +0000
204@@ -130,7 +130,7 @@
205 self._new_root = self.trans_id_tree_file_id(root_id)
206 else:
207 self._new_root = None
208- # Indictor of whether the transform has been applied
209+ # Indicator of whether the transform has been applied
210 self._done = False
211 # A progress bar
212 self._pb = pb
213
214=== modified file 'doc/en/release-notes/bzr-2.3.txt'
215--- doc/en/release-notes/bzr-2.3.txt 2010-11-22 03:35:24 +0000
216+++ doc/en/release-notes/bzr-2.3.txt 2010-11-24 16:33:38 +0000
217@@ -35,6 +35,11 @@
218 options matching a given regular expression is now controlled via the
219 ``--all`` option. (Vincent Ladeuil, bug #670251)
220
221+* ``bzr resolve`` now accepts ``--take-this`` and ``--take-other`` actions
222+ for text conflicts. This *replace* the whole file with the content
223+ designated by the action. This will *ignore* all differences that would
224+ have been merge cleanly otherwise. (Vincent Ladeuil, #638451)
225+
226 * ``bzr resolve`` now reports the number of conflicts resolved and the
227 number of remaining conflicts. This provides a better feedback about the
228 whole resolution process. (Vincent Ladeuil)
229
230=== modified file 'doc/en/whats-new/whats-new-in-2.3.txt'
231--- doc/en/whats-new/whats-new-in-2.3.txt 2010-11-22 03:35:24 +0000
232+++ doc/en/whats-new/whats-new-in-2.3.txt 2010-11-24 16:33:38 +0000
233@@ -118,6 +118,12 @@
234 default behaviour is specified (if needed) by setting the variable to
235 ``conflict``. (Vincent Ladeuil, #323111)
236
237+* ``bzr resolve --take-this`` and ``bzr resolve --take-other`` can now be
238+ used for text conflicts. This will ignore the differences that were merged
239+ cleanly and replace the file with its content in the current branch
240+ (``--take-this``) or with its content in the merged branch
241+ (``--take-other``). (Vincent Ladeuil, #638451)
242+
243 * ``bzr resolve`` now provides more feedback about the conflicts just
244 resolved and the remaining ones. (Vincent Ladeuil)
245