##// END OF EJS Templates
filemerge: add support for change/delete conflicts to the ':other' merge tool...
Siddharth Agarwal -
r27037:a8908c13 default
parent child Browse files
Show More
@@ -1,633 +1,639
1 1 # filemerge.py - file-level merge handling for Mercurial
2 2 #
3 3 # Copyright 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import filecmp
11 11 import os
12 12 import re
13 13 import tempfile
14 14
15 15 from .i18n import _
16 16 from .node import nullid, short
17 17
18 18 from . import (
19 19 cmdutil,
20 20 error,
21 21 match,
22 22 simplemerge,
23 23 tagmerge,
24 24 templatekw,
25 25 templater,
26 26 util,
27 27 )
28 28
29 29 def _toolstr(ui, tool, part, default=""):
30 30 return ui.config("merge-tools", tool + "." + part, default)
31 31
32 32 def _toolbool(ui, tool, part, default=False):
33 33 return ui.configbool("merge-tools", tool + "." + part, default)
34 34
35 35 def _toollist(ui, tool, part, default=[]):
36 36 return ui.configlist("merge-tools", tool + "." + part, default)
37 37
38 38 internals = {}
39 39 # Merge tools to document.
40 40 internalsdoc = {}
41 41
42 42 # internal tool merge types
43 43 nomerge = None
44 44 mergeonly = 'mergeonly' # just the full merge, no premerge
45 45 fullmerge = 'fullmerge' # both premerge and merge
46 46
47 47 class absentfilectx(object):
48 48 """Represents a file that's ostensibly in a context but is actually not
49 49 present in it.
50 50
51 51 This is here because it's very specific to the filemerge code for now --
52 52 other code is likely going to break with the values this returns."""
53 53 def __init__(self, ctx, f):
54 54 self._ctx = ctx
55 55 self._f = f
56 56
57 57 def path(self):
58 58 return self._f
59 59
60 60 def size(self):
61 61 return None
62 62
63 63 def data(self):
64 64 return None
65 65
66 66 def filenode(self):
67 67 return nullid
68 68
69 69 _customcmp = True
70 70 def cmp(self, fctx):
71 71 """compare with other file context
72 72
73 73 returns True if different from fctx.
74 74 """
75 75 return not (fctx.isabsent() and
76 76 fctx.ctx() == self.ctx() and
77 77 fctx.path() == self.path())
78 78
79 79 def flags(self):
80 80 return ''
81 81
82 82 def changectx(self):
83 83 return self._ctx
84 84
85 85 def isbinary(self):
86 86 return False
87 87
88 88 def isabsent(self):
89 89 return True
90 90
91 91 def internaltool(name, mergetype, onfailure=None, precheck=None):
92 92 '''return a decorator for populating internal merge tool table'''
93 93 def decorator(func):
94 94 fullname = ':' + name
95 95 func.__doc__ = "``%s``\n" % fullname + func.__doc__.strip()
96 96 internals[fullname] = func
97 97 internals['internal:' + name] = func
98 98 internalsdoc[fullname] = func
99 99 func.mergetype = mergetype
100 100 func.onfailure = onfailure
101 101 func.precheck = precheck
102 102 return func
103 103 return decorator
104 104
105 105 def _findtool(ui, tool):
106 106 if tool in internals:
107 107 return tool
108 108 return findexternaltool(ui, tool)
109 109
110 110 def findexternaltool(ui, tool):
111 111 for kn in ("regkey", "regkeyalt"):
112 112 k = _toolstr(ui, tool, kn)
113 113 if not k:
114 114 continue
115 115 p = util.lookupreg(k, _toolstr(ui, tool, "regname"))
116 116 if p:
117 117 p = util.findexe(p + _toolstr(ui, tool, "regappend"))
118 118 if p:
119 119 return p
120 120 exe = _toolstr(ui, tool, "executable", tool)
121 121 return util.findexe(util.expandpath(exe))
122 122
123 123 def _picktool(repo, ui, path, binary, symlink):
124 124 def check(tool, pat, symlink, binary):
125 125 tmsg = tool
126 126 if pat:
127 127 tmsg += " specified for " + pat
128 128 if not _findtool(ui, tool):
129 129 if pat: # explicitly requested tool deserves a warning
130 130 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
131 131 else: # configured but non-existing tools are more silent
132 132 ui.note(_("couldn't find merge tool %s\n") % tmsg)
133 133 elif symlink and not _toolbool(ui, tool, "symlink"):
134 134 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
135 135 elif binary and not _toolbool(ui, tool, "binary"):
136 136 ui.warn(_("tool %s can't handle binary\n") % tmsg)
137 137 elif not util.gui() and _toolbool(ui, tool, "gui"):
138 138 ui.warn(_("tool %s requires a GUI\n") % tmsg)
139 139 else:
140 140 return True
141 141 return False
142 142
143 143 # internal config: ui.forcemerge
144 144 # forcemerge comes from command line arguments, highest priority
145 145 force = ui.config('ui', 'forcemerge')
146 146 if force:
147 147 toolpath = _findtool(ui, force)
148 148 if toolpath:
149 149 return (force, util.shellquote(toolpath))
150 150 else:
151 151 # mimic HGMERGE if given tool not found
152 152 return (force, force)
153 153
154 154 # HGMERGE takes next precedence
155 155 hgmerge = os.environ.get("HGMERGE")
156 156 if hgmerge:
157 157 return (hgmerge, hgmerge)
158 158
159 159 # then patterns
160 160 for pat, tool in ui.configitems("merge-patterns"):
161 161 mf = match.match(repo.root, '', [pat])
162 162 if mf(path) and check(tool, pat, symlink, False):
163 163 toolpath = _findtool(ui, tool)
164 164 return (tool, util.shellquote(toolpath))
165 165
166 166 # then merge tools
167 167 tools = {}
168 168 disabled = set()
169 169 for k, v in ui.configitems("merge-tools"):
170 170 t = k.split('.')[0]
171 171 if t not in tools:
172 172 tools[t] = int(_toolstr(ui, t, "priority", "0"))
173 173 if _toolbool(ui, t, "disabled", False):
174 174 disabled.add(t)
175 175 names = tools.keys()
176 176 tools = sorted([(-p, t) for t, p in tools.items() if t not in disabled])
177 177 uimerge = ui.config("ui", "merge")
178 178 if uimerge:
179 179 if uimerge not in names:
180 180 return (uimerge, uimerge)
181 181 tools.insert(0, (None, uimerge)) # highest priority
182 182 tools.append((None, "hgmerge")) # the old default, if found
183 183 for p, t in tools:
184 184 if check(t, None, symlink, binary):
185 185 toolpath = _findtool(ui, t)
186 186 return (t, util.shellquote(toolpath))
187 187
188 188 # internal merge or prompt as last resort
189 189 if symlink or binary:
190 190 return ":prompt", None
191 191 return ":merge", None
192 192
193 193 def _eoltype(data):
194 194 "Guess the EOL type of a file"
195 195 if '\0' in data: # binary
196 196 return None
197 197 if '\r\n' in data: # Windows
198 198 return '\r\n'
199 199 if '\r' in data: # Old Mac
200 200 return '\r'
201 201 if '\n' in data: # UNIX
202 202 return '\n'
203 203 return None # unknown
204 204
205 205 def _matcheol(file, origfile):
206 206 "Convert EOL markers in a file to match origfile"
207 207 tostyle = _eoltype(util.readfile(origfile))
208 208 if tostyle:
209 209 data = util.readfile(file)
210 210 style = _eoltype(data)
211 211 if style:
212 212 newdata = data.replace(style, tostyle)
213 213 if newdata != data:
214 214 util.writefile(file, newdata)
215 215
216 216 @internaltool('prompt', nomerge)
217 217 def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf):
218 218 """Asks the user which of the local or the other version to keep as
219 219 the merged version."""
220 220 ui = repo.ui
221 221 fd = fcd.path()
222 222
223 223 try:
224 224 index = ui.promptchoice(_("no tool found to merge %s\n"
225 225 "keep (l)ocal or take (o)ther?"
226 226 "$$ &Local $$ &Other") % fd, 0)
227 227 choice = ['local', 'other'][index]
228 228
229 229 if choice == 'other':
230 230 return _iother(repo, mynode, orig, fcd, fco, fca, toolconf)
231 231 else:
232 232 return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf)
233 233 except error.ResponseExpected:
234 234 ui.write("\n")
235 235 return 1, False
236 236
237 237 @internaltool('local', nomerge)
238 238 def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf):
239 239 """Uses the local version of files as the merged version."""
240 240 return 0, fcd.isabsent()
241 241
242 242 @internaltool('other', nomerge)
243 243 def _iother(repo, mynode, orig, fcd, fco, fca, toolconf):
244 244 """Uses the other version of files as the merged version."""
245 repo.wwrite(fcd.path(), fco.data(), fco.flags())
246 return 0, False
245 if fco.isabsent():
246 # local changed, remote deleted -- 'deleted' picked
247 repo.wvfs.unlinkpath(fcd.path())
248 deleted = True
249 else:
250 repo.wwrite(fcd.path(), fco.data(), fco.flags())
251 deleted = False
252 return 0, deleted
247 253
248 254 @internaltool('fail', nomerge)
249 255 def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf):
250 256 """
251 257 Rather than attempting to merge files that were modified on both
252 258 branches, it marks them as unresolved. The resolve command must be
253 259 used to resolve these conflicts."""
254 260 return 1, False
255 261
256 262 def _premerge(repo, toolconf, files, labels=None):
257 263 tool, toolpath, binary, symlink = toolconf
258 264 if symlink:
259 265 return 1
260 266 a, b, c, back = files
261 267
262 268 ui = repo.ui
263 269
264 270 validkeep = ['keep', 'keep-merge3']
265 271
266 272 # do we attempt to simplemerge first?
267 273 try:
268 274 premerge = _toolbool(ui, tool, "premerge", not binary)
269 275 except error.ConfigError:
270 276 premerge = _toolstr(ui, tool, "premerge").lower()
271 277 if premerge not in validkeep:
272 278 _valid = ', '.join(["'" + v + "'" for v in validkeep])
273 279 raise error.ConfigError(_("%s.premerge not valid "
274 280 "('%s' is neither boolean nor %s)") %
275 281 (tool, premerge, _valid))
276 282
277 283 if premerge:
278 284 if premerge == 'keep-merge3':
279 285 if not labels:
280 286 labels = _defaultconflictlabels
281 287 if len(labels) < 3:
282 288 labels.append('base')
283 289 r = simplemerge.simplemerge(ui, a, b, c, quiet=True, label=labels)
284 290 if not r:
285 291 ui.debug(" premerge successful\n")
286 292 return 0
287 293 if premerge not in validkeep:
288 294 util.copyfile(back, a) # restore from backup and try again
289 295 return 1 # continue merging
290 296
291 297 def _mergecheck(repo, mynode, orig, fcd, fco, fca, toolconf):
292 298 tool, toolpath, binary, symlink = toolconf
293 299 if symlink:
294 300 repo.ui.warn(_('warning: internal %s cannot merge symlinks '
295 301 'for %s\n') % (tool, fcd.path()))
296 302 return False
297 303 return True
298 304
299 305 def _merge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, mode):
300 306 """
301 307 Uses the internal non-interactive simple merge algorithm for merging
302 308 files. It will fail if there are any conflicts and leave markers in
303 309 the partially merged file. Markers will have two sections, one for each side
304 310 of merge, unless mode equals 'union' which suppresses the markers."""
305 311 a, b, c, back = files
306 312
307 313 ui = repo.ui
308 314
309 315 r = simplemerge.simplemerge(ui, a, b, c, label=labels, mode=mode)
310 316 return True, r, False
311 317
312 318 @internaltool('union', fullmerge,
313 319 _("warning: conflicts while merging %s! "
314 320 "(edit, then use 'hg resolve --mark')\n"),
315 321 precheck=_mergecheck)
316 322 def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
317 323 """
318 324 Uses the internal non-interactive simple merge algorithm for merging
319 325 files. It will use both left and right sides for conflict regions.
320 326 No markers are inserted."""
321 327 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
322 328 files, labels, 'union')
323 329
324 330 @internaltool('merge', fullmerge,
325 331 _("warning: conflicts while merging %s! "
326 332 "(edit, then use 'hg resolve --mark')\n"),
327 333 precheck=_mergecheck)
328 334 def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
329 335 """
330 336 Uses the internal non-interactive simple merge algorithm for merging
331 337 files. It will fail if there are any conflicts and leave markers in
332 338 the partially merged file. Markers will have two sections, one for each side
333 339 of merge."""
334 340 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
335 341 files, labels, 'merge')
336 342
337 343 @internaltool('merge3', fullmerge,
338 344 _("warning: conflicts while merging %s! "
339 345 "(edit, then use 'hg resolve --mark')\n"),
340 346 precheck=_mergecheck)
341 347 def _imerge3(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
342 348 """
343 349 Uses the internal non-interactive simple merge algorithm for merging
344 350 files. It will fail if there are any conflicts and leave markers in
345 351 the partially merged file. Marker will have three sections, one from each
346 352 side of the merge and one for the base content."""
347 353 if not labels:
348 354 labels = _defaultconflictlabels
349 355 if len(labels) < 3:
350 356 labels.append('base')
351 357 return _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels)
352 358
353 359 def _imergeauto(repo, mynode, orig, fcd, fco, fca, toolconf, files,
354 360 labels=None, localorother=None):
355 361 """
356 362 Generic driver for _imergelocal and _imergeother
357 363 """
358 364 assert localorother is not None
359 365 tool, toolpath, binary, symlink = toolconf
360 366 a, b, c, back = files
361 367 r = simplemerge.simplemerge(repo.ui, a, b, c, label=labels,
362 368 localorother=localorother)
363 369 return True, r
364 370
365 371 @internaltool('merge-local', mergeonly, precheck=_mergecheck)
366 372 def _imergelocal(*args, **kwargs):
367 373 """
368 374 Like :merge, but resolve all conflicts non-interactively in favor
369 375 of the local changes."""
370 376 success, status = _imergeauto(localorother='local', *args, **kwargs)
371 377 return success, status, False
372 378
373 379 @internaltool('merge-other', mergeonly, precheck=_mergecheck)
374 380 def _imergeother(*args, **kwargs):
375 381 """
376 382 Like :merge, but resolve all conflicts non-interactively in favor
377 383 of the other changes."""
378 384 success, status = _imergeauto(localorother='other', *args, **kwargs)
379 385 return success, status, False
380 386
381 387 @internaltool('tagmerge', mergeonly,
382 388 _("automatic tag merging of %s failed! "
383 389 "(use 'hg resolve --tool :merge' or another merge "
384 390 "tool of your choice)\n"))
385 391 def _itagmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
386 392 """
387 393 Uses the internal tag merge algorithm (experimental).
388 394 """
389 395 success, status = tagmerge.merge(repo, fcd, fco, fca)
390 396 return success, status, False
391 397
392 398 @internaltool('dump', fullmerge)
393 399 def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
394 400 """
395 401 Creates three versions of the files to merge, containing the
396 402 contents of local, other and base. These files can then be used to
397 403 perform a merge manually. If the file to be merged is named
398 404 ``a.txt``, these files will accordingly be named ``a.txt.local``,
399 405 ``a.txt.other`` and ``a.txt.base`` and they will be placed in the
400 406 same directory as ``a.txt``."""
401 407 a, b, c, back = files
402 408
403 409 fd = fcd.path()
404 410
405 411 util.copyfile(a, a + ".local")
406 412 repo.wwrite(fd + ".other", fco.data(), fco.flags())
407 413 repo.wwrite(fd + ".base", fca.data(), fca.flags())
408 414 return False, 1, False
409 415
410 416 def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
411 417 tool, toolpath, binary, symlink = toolconf
412 418 a, b, c, back = files
413 419 out = ""
414 420 env = {'HG_FILE': fcd.path(),
415 421 'HG_MY_NODE': short(mynode),
416 422 'HG_OTHER_NODE': str(fco.changectx()),
417 423 'HG_BASE_NODE': str(fca.changectx()),
418 424 'HG_MY_ISLINK': 'l' in fcd.flags(),
419 425 'HG_OTHER_ISLINK': 'l' in fco.flags(),
420 426 'HG_BASE_ISLINK': 'l' in fca.flags(),
421 427 }
422 428
423 429 ui = repo.ui
424 430
425 431 args = _toolstr(ui, tool, "args", '$local $base $other')
426 432 if "$output" in args:
427 433 out, a = a, back # read input from backup, write to original
428 434 replace = {'local': a, 'base': b, 'other': c, 'output': out}
429 435 args = util.interpolate(r'\$', replace, args,
430 436 lambda s: util.shellquote(util.localpath(s)))
431 437 cmd = toolpath + ' ' + args
432 438 repo.ui.debug('launching merge tool: %s\n' % cmd)
433 439 r = ui.system(cmd, cwd=repo.root, environ=env)
434 440 repo.ui.debug('merge tool returned: %s\n' % r)
435 441 return True, r, False
436 442
437 443 def _formatconflictmarker(repo, ctx, template, label, pad):
438 444 """Applies the given template to the ctx, prefixed by the label.
439 445
440 446 Pad is the minimum width of the label prefix, so that multiple markers
441 447 can have aligned templated parts.
442 448 """
443 449 if ctx.node() is None:
444 450 ctx = ctx.p1()
445 451
446 452 props = templatekw.keywords.copy()
447 453 props['templ'] = template
448 454 props['ctx'] = ctx
449 455 props['repo'] = repo
450 456 templateresult = template('conflictmarker', **props)
451 457
452 458 label = ('%s:' % label).ljust(pad + 1)
453 459 mark = '%s %s' % (label, templater.stringify(templateresult))
454 460
455 461 if mark:
456 462 mark = mark.splitlines()[0] # split for safety
457 463
458 464 # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
459 465 return util.ellipsis(mark, 80 - 8)
460 466
461 467 _defaultconflictmarker = ('{node|short} ' +
462 468 '{ifeq(tags, "tip", "", "{tags} ")}' +
463 469 '{if(bookmarks, "{bookmarks} ")}' +
464 470 '{ifeq(branch, "default", "", "{branch} ")}' +
465 471 '- {author|user}: {desc|firstline}')
466 472
467 473 _defaultconflictlabels = ['local', 'other']
468 474
469 475 def _formatlabels(repo, fcd, fco, fca, labels):
470 476 """Formats the given labels using the conflict marker template.
471 477
472 478 Returns a list of formatted labels.
473 479 """
474 480 cd = fcd.changectx()
475 481 co = fco.changectx()
476 482 ca = fca.changectx()
477 483
478 484 ui = repo.ui
479 485 template = ui.config('ui', 'mergemarkertemplate', _defaultconflictmarker)
480 486 tmpl = templater.templater(None, cache={'conflictmarker': template})
481 487
482 488 pad = max(len(l) for l in labels)
483 489
484 490 newlabels = [_formatconflictmarker(repo, cd, tmpl, labels[0], pad),
485 491 _formatconflictmarker(repo, co, tmpl, labels[1], pad)]
486 492 if len(labels) > 2:
487 493 newlabels.append(_formatconflictmarker(repo, ca, tmpl, labels[2], pad))
488 494 return newlabels
489 495
490 496 def _filemerge(premerge, repo, mynode, orig, fcd, fco, fca, labels=None):
491 497 """perform a 3-way merge in the working directory
492 498
493 499 premerge = whether this is a premerge
494 500 mynode = parent node before merge
495 501 orig = original local filename before merge
496 502 fco = other file context
497 503 fca = ancestor file context
498 504 fcd = local file context for current/destination file
499 505
500 506 Returns whether the merge is complete, the return value of the merge, and
501 507 a boolean indicating whether the file was deleted from disk."""
502 508
503 509 def temp(prefix, ctx):
504 510 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
505 511 (fd, name) = tempfile.mkstemp(prefix=pre)
506 512 data = repo.wwritedata(ctx.path(), ctx.data())
507 513 f = os.fdopen(fd, "wb")
508 514 f.write(data)
509 515 f.close()
510 516 return name
511 517
512 518 if not fco.cmp(fcd): # files identical?
513 519 return True, None, False
514 520
515 521 ui = repo.ui
516 522 fd = fcd.path()
517 523 binary = fcd.isbinary() or fco.isbinary() or fca.isbinary()
518 524 symlink = 'l' in fcd.flags() + fco.flags()
519 525 tool, toolpath = _picktool(repo, ui, fd, binary, symlink)
520 526 if tool in internals and tool.startswith('internal:'):
521 527 # normalize to new-style names (':merge' etc)
522 528 tool = tool[len('internal'):]
523 529 ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" %
524 530 (tool, fd, binary, symlink))
525 531
526 532 if tool in internals:
527 533 func = internals[tool]
528 534 mergetype = func.mergetype
529 535 onfailure = func.onfailure
530 536 precheck = func.precheck
531 537 else:
532 538 func = _xmerge
533 539 mergetype = fullmerge
534 540 onfailure = _("merging %s failed!\n")
535 541 precheck = None
536 542
537 543 toolconf = tool, toolpath, binary, symlink
538 544
539 545 if mergetype == nomerge:
540 546 r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf)
541 547 return True, r, deleted
542 548
543 549 if premerge:
544 550 if orig != fco.path():
545 551 ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
546 552 else:
547 553 ui.status(_("merging %s\n") % fd)
548 554
549 555 ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca))
550 556
551 557 if precheck and not precheck(repo, mynode, orig, fcd, fco, fca,
552 558 toolconf):
553 559 if onfailure:
554 560 ui.warn(onfailure % fd)
555 561 return True, 1, False
556 562
557 563 a = repo.wjoin(fd)
558 564 b = temp("base", fca)
559 565 c = temp("other", fco)
560 566 back = cmdutil.origpath(ui, repo, a)
561 567 if premerge:
562 568 util.copyfile(a, back)
563 569 files = (a, b, c, back)
564 570
565 571 r = 1
566 572 try:
567 573 markerstyle = ui.config('ui', 'mergemarkers', 'basic')
568 574 if not labels:
569 575 labels = _defaultconflictlabels
570 576 if markerstyle != 'basic':
571 577 labels = _formatlabels(repo, fcd, fco, fca, labels)
572 578
573 579 if premerge and mergetype == fullmerge:
574 580 r = _premerge(repo, toolconf, files, labels=labels)
575 581 # complete if premerge successful (r is 0)
576 582 return not r, r, False
577 583
578 584 needcheck, r, deleted = func(repo, mynode, orig, fcd, fco, fca,
579 585 toolconf, files, labels=labels)
580 586
581 587 if needcheck:
582 588 r = _check(r, ui, tool, fcd, files)
583 589
584 590 if r:
585 591 if onfailure:
586 592 ui.warn(onfailure % fd)
587 593
588 594 return True, r, deleted
589 595 finally:
590 596 if not r:
591 597 util.unlink(back)
592 598 util.unlink(b)
593 599 util.unlink(c)
594 600
595 601 def _check(r, ui, tool, fcd, files):
596 602 fd = fcd.path()
597 603 a, b, c, back = files
598 604
599 605 if not r and (_toolbool(ui, tool, "checkconflicts") or
600 606 'conflicts' in _toollist(ui, tool, "check")):
601 607 if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(),
602 608 re.MULTILINE):
603 609 r = 1
604 610
605 611 checked = False
606 612 if 'prompt' in _toollist(ui, tool, "check"):
607 613 checked = True
608 614 if ui.promptchoice(_("was merge of '%s' successful (yn)?"
609 615 "$$ &Yes $$ &No") % fd, 1):
610 616 r = 1
611 617
612 618 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
613 619 'changed' in
614 620 _toollist(ui, tool, "check")):
615 621 if filecmp.cmp(a, back):
616 622 if ui.promptchoice(_(" output file %s appears unchanged\n"
617 623 "was merge successful (yn)?"
618 624 "$$ &Yes $$ &No") % fd, 1):
619 625 r = 1
620 626
621 627 if _toolbool(ui, tool, "fixeol"):
622 628 _matcheol(a, back)
623 629
624 630 return r
625 631
626 632 def premerge(repo, mynode, orig, fcd, fco, fca, labels=None):
627 633 return _filemerge(True, repo, mynode, orig, fcd, fco, fca, labels=labels)
628 634
629 635 def filemerge(repo, mynode, orig, fcd, fco, fca, labels=None):
630 636 return _filemerge(False, repo, mynode, orig, fcd, fco, fca, labels=labels)
631 637
632 638 # tell hggettext to extract docstrings from these functions:
633 639 i18nfunctions = internals.values()
General Comments 0
You need to be logged in to leave comments. Login now