Merge lp:~jelmer/brz/resolve-auto-refactor into lp:brz/3.0

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/resolve-auto-refactor
Merge into: lp:brz/3.0
Diff against target: 278 lines (+90/-59)
4 files modified
breezy/conflicts.py (+41/-28)
breezy/tests/test_workingtree.py (+38/-17)
breezy/workingtree.py (+8/-14)
doc/en/release-notes/brz-3.0.txt (+3/-0)
To merge this branch: bzr merge lp:~jelmer/brz/resolve-auto-refactor
Reviewer Review Type Date Requested Status
Martin Packman Approve
Review via email: mp+368872@code.launchpad.net

Commit message

Create a new kind of conflict resolution action named 'auto'.

Description of the change

Create a new kind of conflict resolution action named 'auto', refactoring code
from `WorkingTree.auto_resolve` and deprecating that method. There should be no
behaviour change.

(from lp:~gz/bzr/resolve_auto_refactor)

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

Thanks, some wally should probably have landed this long ago.

review: Approve
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/conflicts.py'
2--- breezy/conflicts.py 2018-11-11 04:08:32 +0000
3+++ breezy/conflicts.py 2019-06-16 16:47:08 +0000
4@@ -20,6 +20,7 @@
5 from __future__ import absolute_import
6
7 import os
8+import re
9
10 from .lazy_import import lazy_import
11 lazy_import(globals(), """
12@@ -84,6 +85,8 @@
13
14
15 resolve_action_registry.register(
16+ 'auto', 'auto', 'Detect whether conflict has been resolved by user.')
17+resolve_action_registry.register(
18 'done', 'done', 'Marks the conflict as resolved.')
19 resolve_action_registry.register(
20 'take-this', 'take_this',
21@@ -137,38 +140,27 @@
22 else:
23 tree, file_list = workingtree.WorkingTree.open_containing_paths(
24 file_list, directory)
25- if file_list is None:
26- if action is None:
27- # FIXME: There is a special case here related to the option
28- # handling that could be clearer and easier to discover by
29- # providing an --auto action (bug #344013 and #383396) and
30- # make it mandatory instead of implicit and active only
31- # when no file_list is provided -- vila 091229
32+ if action is None:
33+ if file_list is None:
34 action = 'auto'
35- else:
36- if action is None:
37+ else:
38 action = 'done'
39- if action == 'auto':
40- if file_list is None:
41- un_resolved, resolved = tree.auto_resolve()
42- if len(un_resolved) > 0:
43- trace.note(ngettext('%d conflict auto-resolved.',
44- '%d conflicts auto-resolved.', len(resolved)),
45- len(resolved))
46- trace.note(gettext('Remaining conflicts:'))
47- for conflict in un_resolved:
48- trace.note(text_type(conflict))
49- return 1
50- else:
51- trace.note(gettext('All conflicts resolved.'))
52- return 0
53+ before, after = resolve(tree, file_list, action=action)
54+ # GZ 2012-07-27: Should unify UI below now that auto is less magical.
55+ if action == 'auto' and file_list is None:
56+ if after > 0:
57+ trace.note(
58+ ngettext('%d conflict auto-resolved.',
59+ '%d conflicts auto-resolved.', before - after),
60+ before - after)
61+ trace.note(gettext('Remaining conflicts:'))
62+ for conflict in tree.conflicts():
63+ trace.note(text_type(conflict))
64+ return 1
65 else:
66- # FIXME: This can never occur but the block above needs some
67- # refactoring to transfer tree.auto_resolve() to
68- # conflict.auto(tree) --vila 091242
69- pass
70+ trace.note(gettext('All conflicts resolved.'))
71+ return 0
72 else:
73- before, after = resolve(tree, file_list, action=action)
74 trace.note(ngettext('{0} conflict resolved, {1} remaining',
75 '{0} conflicts resolved, {1} remaining',
76 before - after).format(before - after, after))
77@@ -453,6 +445,9 @@
78 if e.errno != errno.ENOENT:
79 raise
80
81+ def action_auto(self, tree):
82+ raise NotImplementedError(self.action_auto)
83+
84 def action_done(self, tree):
85 """Mark the conflict as solved once it has been handled."""
86 # This method does nothing but simplifies the design of upper levels.
87@@ -651,6 +646,8 @@
88
89 rformat = '%(class)s(%(path)r, %(file_id)r)'
90
91+ _conflict_re = re.compile(b'^(<{7}|={7}|>{7})')
92+
93 def associated_filenames(self):
94 return [self.path + suffix for suffix in CONFLICT_SUFFIXES]
95
96@@ -681,6 +678,22 @@
97 tt.version_file(self.file_id, winner_tid)
98 tt.apply()
99
100+ def action_auto(self, tree):
101+ # GZ 2012-07-27: Using NotImplementedError to signal that a conflict
102+ # can't be auto resolved does not seem ideal.
103+ try:
104+ kind = tree.kind(self.path)
105+ except errors.NoSuchFile:
106+ return
107+ if kind != 'file':
108+ raise NotImplementedError("Conflict is not a file")
109+ conflict_markers_in_line = self._conflict_re.search
110+ # GZ 2012-07-27: What if not tree.has_id(self.file_id) due to removal?
111+ with tree.get_file(self.path) as f:
112+ for line in f:
113+ if conflict_markers_in_line(line):
114+ raise NotImplementedError("Conflict markers present")
115+
116 def action_take_this(self, tree):
117 self._resolve_with_cleanups(tree, 'THIS')
118
119
120=== modified file 'breezy/tests/test_workingtree.py'
121--- breezy/tests/test_workingtree.py 2018-11-12 01:41:38 +0000
122+++ breezy/tests/test_workingtree.py 2019-06-16 16:47:08 +0000
123@@ -18,6 +18,7 @@
124 from .. import (
125 conflicts,
126 errors,
127+ symbol_versioning,
128 transport,
129 workingtree,
130 )
131@@ -405,6 +406,12 @@
132
133 class TestAutoResolve(TestCaseWithTransport):
134
135+ def _auto_resolve(self, tree):
136+ """Call auto_resolve on tree expecting deprecation"""
137+ return self.applyDeprecated(
138+ symbol_versioning.deprecated_in((3, 0, 1)),
139+ tree.auto_resolve,)
140+
141 def test_auto_resolve(self):
142 base = self.make_branch_and_tree('base')
143 self.build_tree_contents([('base/hello', b'Hello')])
144@@ -420,24 +427,24 @@
145 this.merge_from_branch(other.branch)
146 self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
147 this.conflicts())
148- this.auto_resolve()
149- self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
150- this.conflicts())
151- self.build_tree_contents([('this/hello', b'<<<<<<<')])
152- this.auto_resolve()
153- self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
154- this.conflicts())
155- self.build_tree_contents([('this/hello', b'=======')])
156- this.auto_resolve()
157- self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
158- this.conflicts())
159- self.build_tree_contents([('this/hello', b'\n>>>>>>>')])
160- remaining, resolved = this.auto_resolve()
161+ self._auto_resolve(this)
162+ self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
163+ this.conflicts())
164+ self.build_tree_contents([('this/hello', '<<<<<<<')])
165+ self._auto_resolve(this)
166+ self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
167+ this.conflicts())
168+ self.build_tree_contents([('this/hello', '=======')])
169+ self._auto_resolve(this)
170+ self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
171+ this.conflicts())
172+ self.build_tree_contents([('this/hello', '\n>>>>>>>')])
173+ remaining, resolved = self._auto_resolve(this)
174 self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
175 this.conflicts())
176 self.assertEqual([], resolved)
177 self.build_tree_contents([('this/hello', b'hELLO wORLD')])
178- remaining, resolved = this.auto_resolve()
179+ remaining, resolved = self._auto_resolve(this)
180 self.assertEqual([], this.conflicts())
181 self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
182 resolved)
183@@ -447,9 +454,23 @@
184 tree = self.make_branch_and_tree('tree')
185 self.build_tree(['tree/hello/'])
186 tree.add('hello', b'hello-id')
187- file_conflict = conflicts.TextConflict('file', b'hello-id')
188- tree.set_conflicts(conflicts.ConflictList([file_conflict]))
189- tree.auto_resolve()
190+ file_conflict = conflicts.TextConflict('hello', b'hello-id')
191+ tree.set_conflicts(conflicts.ConflictList([file_conflict]))
192+ remaining, resolved = self._auto_resolve(tree)
193+ self.assertEqual(
194+ remaining,
195+ conflicts.ConflictList([conflicts.TextConflict(u'hello', 'hello-id')]))
196+ self.assertEqual(resolved, [])
197+
198+ def test_auto_resolve_missing(self):
199+ tree = self.make_branch_and_tree('tree')
200+ file_conflict = conflicts.TextConflict('hello', b'hello-id')
201+ tree.set_conflicts(conflicts.ConflictList([file_conflict]))
202+ remaining, resolved = self._auto_resolve(tree)
203+ self.assertEqual(remaining, [])
204+ self.assertEqual(
205+ resolved,
206+ conflicts.ConflictList([conflicts.TextConflict(u'hello', 'hello-id')]))
207
208
209 class TestFindTrees(TestCaseWithTransport):
210
211=== modified file 'breezy/workingtree.py'
212--- breezy/workingtree.py 2019-02-14 22:18:59 +0000
213+++ breezy/workingtree.py 2019-06-16 16:47:08 +0000
214@@ -31,7 +31,6 @@
215
216 import errno
217 import os
218-import re
219 import sys
220
221 import breezy
222@@ -62,6 +61,7 @@
223 )
224 from .i18n import gettext
225 from . import mutabletree
226+from .symbol_versioning import deprecated_method, deprecated_in
227 from .trace import mutter, note
228
229
230@@ -1271,6 +1271,7 @@
231 """
232 raise NotImplementedError(self.walkdirs)
233
234+ @deprecated_method(deprecated_in((3, 0, 1)))
235 def auto_resolve(self):
236 """Automatically resolve text conflicts according to contents.
237
238@@ -1284,21 +1285,14 @@
239 with self.lock_tree_write():
240 un_resolved = _mod_conflicts.ConflictList()
241 resolved = _mod_conflicts.ConflictList()
242- conflict_re = re.compile(b'^(<{7}|={7}|>{7})')
243 for conflict in self.conflicts():
244- path = self.id2path(conflict.file_id)
245- if (conflict.typestring != 'text conflict' or
246- self.kind(path) != 'file'):
247+ try:
248+ conflict.action_auto(self)
249+ except NotImplementedError:
250 un_resolved.append(conflict)
251- continue
252- with open(self.abspath(path), 'rb') as my_file:
253- for line in my_file:
254- if conflict_re.search(line):
255- un_resolved.append(conflict)
256- break
257- else:
258- resolved.append(conflict)
259- resolved.remove_files(self)
260+ else:
261+ conflict.cleanup(self)
262+ resolved.append(conflict)
263 self.set_conflicts(un_resolved)
264 return un_resolved, resolved
265
266
267=== modified file 'doc/en/release-notes/brz-3.0.txt'
268--- doc/en/release-notes/brz-3.0.txt 2019-06-15 18:57:07 +0000
269+++ doc/en/release-notes/brz-3.0.txt 2019-06-16 16:47:08 +0000
270@@ -45,6 +45,9 @@
271 * Print full upgrade command to run when complaining about lack of
272 support for tags. (Jelmer Vernooij, #163908)
273
274+* Refactor auto conflict handling and add a ``--auto`` flag
275+ to ``bzr resolve``. (Martin Packman, #688506)
276+
277 Documentation
278 *************
279

Subscribers

People subscribed via source and target branches