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