##// END OF EJS Templates
filemerge: make warning message more i18n friendly...
FUJIWARA Katsunori -
r32254:17774266 default
parent child Browse files
Show More
@@ -1,724 +1,724 b''
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 encoding,
20 20 error,
21 21 formatter,
22 22 match,
23 23 pycompat,
24 24 scmutil,
25 25 simplemerge,
26 26 tagmerge,
27 27 templatekw,
28 28 templater,
29 29 util,
30 30 )
31 31
32 32 def _toolstr(ui, tool, part, default=""):
33 33 return ui.config("merge-tools", tool + "." + part, default)
34 34
35 35 def _toolbool(ui, tool, part, default=False):
36 36 return ui.configbool("merge-tools", tool + "." + part, default)
37 37
38 38 def _toollist(ui, tool, part, default=None):
39 39 if default is None:
40 40 default = []
41 41 return ui.configlist("merge-tools", tool + "." + part, default)
42 42
43 43 internals = {}
44 44 # Merge tools to document.
45 45 internalsdoc = {}
46 46
47 47 # internal tool merge types
48 48 nomerge = None
49 49 mergeonly = 'mergeonly' # just the full merge, no premerge
50 50 fullmerge = 'fullmerge' # both premerge and merge
51 51
52 52 class absentfilectx(object):
53 53 """Represents a file that's ostensibly in a context but is actually not
54 54 present in it.
55 55
56 56 This is here because it's very specific to the filemerge code for now --
57 57 other code is likely going to break with the values this returns."""
58 58 def __init__(self, ctx, f):
59 59 self._ctx = ctx
60 60 self._f = f
61 61
62 62 def path(self):
63 63 return self._f
64 64
65 65 def size(self):
66 66 return None
67 67
68 68 def data(self):
69 69 return None
70 70
71 71 def filenode(self):
72 72 return nullid
73 73
74 74 _customcmp = True
75 75 def cmp(self, fctx):
76 76 """compare with other file context
77 77
78 78 returns True if different from fctx.
79 79 """
80 80 return not (fctx.isabsent() and
81 81 fctx.ctx() == self.ctx() and
82 82 fctx.path() == self.path())
83 83
84 84 def flags(self):
85 85 return ''
86 86
87 87 def changectx(self):
88 88 return self._ctx
89 89
90 90 def isbinary(self):
91 91 return False
92 92
93 93 def isabsent(self):
94 94 return True
95 95
96 96 def internaltool(name, mergetype, onfailure=None, precheck=None):
97 97 '''return a decorator for populating internal merge tool table'''
98 98 def decorator(func):
99 99 fullname = ':' + name
100 100 func.__doc__ = (pycompat.sysstr("``%s``\n" % fullname)
101 101 + func.__doc__.strip())
102 102 internals[fullname] = func
103 103 internals['internal:' + name] = func
104 104 internalsdoc[fullname] = func
105 105 func.mergetype = mergetype
106 106 func.onfailure = onfailure
107 107 func.precheck = precheck
108 108 return func
109 109 return decorator
110 110
111 111 def _findtool(ui, tool):
112 112 if tool in internals:
113 113 return tool
114 114 return findexternaltool(ui, tool)
115 115
116 116 def findexternaltool(ui, tool):
117 117 for kn in ("regkey", "regkeyalt"):
118 118 k = _toolstr(ui, tool, kn)
119 119 if not k:
120 120 continue
121 121 p = util.lookupreg(k, _toolstr(ui, tool, "regname"))
122 122 if p:
123 123 p = util.findexe(p + _toolstr(ui, tool, "regappend"))
124 124 if p:
125 125 return p
126 126 exe = _toolstr(ui, tool, "executable", tool)
127 127 return util.findexe(util.expandpath(exe))
128 128
129 129 def _picktool(repo, ui, path, binary, symlink, changedelete):
130 130 def supportscd(tool):
131 131 return tool in internals and internals[tool].mergetype == nomerge
132 132
133 133 def check(tool, pat, symlink, binary, changedelete):
134 134 tmsg = tool
135 135 if pat:
136 tmsg += " specified for " + pat
136 tmsg = _("%s (for pattern %s)") % (tool, pat)
137 137 if not _findtool(ui, tool):
138 138 if pat: # explicitly requested tool deserves a warning
139 139 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
140 140 else: # configured but non-existing tools are more silent
141 141 ui.note(_("couldn't find merge tool %s\n") % tmsg)
142 142 elif symlink and not _toolbool(ui, tool, "symlink"):
143 143 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
144 144 elif binary and not _toolbool(ui, tool, "binary"):
145 145 ui.warn(_("tool %s can't handle binary\n") % tmsg)
146 146 elif changedelete and not supportscd(tool):
147 147 # the nomerge tools are the only tools that support change/delete
148 148 # conflicts
149 149 pass
150 150 elif not util.gui() and _toolbool(ui, tool, "gui"):
151 151 ui.warn(_("tool %s requires a GUI\n") % tmsg)
152 152 else:
153 153 return True
154 154 return False
155 155
156 156 # internal config: ui.forcemerge
157 157 # forcemerge comes from command line arguments, highest priority
158 158 force = ui.config('ui', 'forcemerge')
159 159 if force:
160 160 toolpath = _findtool(ui, force)
161 161 if changedelete and not supportscd(toolpath):
162 162 return ":prompt", None
163 163 else:
164 164 if toolpath:
165 165 return (force, util.shellquote(toolpath))
166 166 else:
167 167 # mimic HGMERGE if given tool not found
168 168 return (force, force)
169 169
170 170 # HGMERGE takes next precedence
171 171 hgmerge = encoding.environ.get("HGMERGE")
172 172 if hgmerge:
173 173 if changedelete and not supportscd(hgmerge):
174 174 return ":prompt", None
175 175 else:
176 176 return (hgmerge, hgmerge)
177 177
178 178 # then patterns
179 179 for pat, tool in ui.configitems("merge-patterns"):
180 180 mf = match.match(repo.root, '', [pat])
181 181 if mf(path) and check(tool, pat, symlink, False, changedelete):
182 182 toolpath = _findtool(ui, tool)
183 183 return (tool, util.shellquote(toolpath))
184 184
185 185 # then merge tools
186 186 tools = {}
187 187 disabled = set()
188 188 for k, v in ui.configitems("merge-tools"):
189 189 t = k.split('.')[0]
190 190 if t not in tools:
191 191 tools[t] = int(_toolstr(ui, t, "priority", "0"))
192 192 if _toolbool(ui, t, "disabled", False):
193 193 disabled.add(t)
194 194 names = tools.keys()
195 195 tools = sorted([(-p, tool) for tool, p in tools.items()
196 196 if tool not in disabled])
197 197 uimerge = ui.config("ui", "merge")
198 198 if uimerge:
199 199 # external tools defined in uimerge won't be able to handle
200 200 # change/delete conflicts
201 201 if uimerge not in names and not changedelete:
202 202 return (uimerge, uimerge)
203 203 tools.insert(0, (None, uimerge)) # highest priority
204 204 tools.append((None, "hgmerge")) # the old default, if found
205 205 for p, t in tools:
206 206 if check(t, None, symlink, binary, changedelete):
207 207 toolpath = _findtool(ui, t)
208 208 return (t, util.shellquote(toolpath))
209 209
210 210 # internal merge or prompt as last resort
211 211 if symlink or binary or changedelete:
212 212 if not changedelete and len(tools):
213 213 # any tool is rejected by capability for symlink or binary
214 214 ui.warn(_("no tool found to merge %s\n") % path)
215 215 return ":prompt", None
216 216 return ":merge", None
217 217
218 218 def _eoltype(data):
219 219 "Guess the EOL type of a file"
220 220 if '\0' in data: # binary
221 221 return None
222 222 if '\r\n' in data: # Windows
223 223 return '\r\n'
224 224 if '\r' in data: # Old Mac
225 225 return '\r'
226 226 if '\n' in data: # UNIX
227 227 return '\n'
228 228 return None # unknown
229 229
230 230 def _matcheol(file, origfile):
231 231 "Convert EOL markers in a file to match origfile"
232 232 tostyle = _eoltype(util.readfile(origfile))
233 233 if tostyle:
234 234 data = util.readfile(file)
235 235 style = _eoltype(data)
236 236 if style:
237 237 newdata = data.replace(style, tostyle)
238 238 if newdata != data:
239 239 util.writefile(file, newdata)
240 240
241 241 @internaltool('prompt', nomerge)
242 242 def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
243 243 """Asks the user which of the local `p1()` or the other `p2()` version to
244 244 keep as the merged version."""
245 245 ui = repo.ui
246 246 fd = fcd.path()
247 247
248 248 prompts = partextras(labels)
249 249 prompts['fd'] = fd
250 250 try:
251 251 if fco.isabsent():
252 252 index = ui.promptchoice(
253 253 _("local%(l)s changed %(fd)s which other%(o)s deleted\n"
254 254 "use (c)hanged version, (d)elete, or leave (u)nresolved?"
255 255 "$$ &Changed $$ &Delete $$ &Unresolved") % prompts, 2)
256 256 choice = ['local', 'other', 'unresolved'][index]
257 257 elif fcd.isabsent():
258 258 index = ui.promptchoice(
259 259 _("other%(o)s changed %(fd)s which local%(l)s deleted\n"
260 260 "use (c)hanged version, leave (d)eleted, or "
261 261 "leave (u)nresolved?"
262 262 "$$ &Changed $$ &Deleted $$ &Unresolved") % prompts, 2)
263 263 choice = ['other', 'local', 'unresolved'][index]
264 264 else:
265 265 index = ui.promptchoice(
266 266 _("keep (l)ocal%(l)s, take (o)ther%(o)s, or leave (u)nresolved"
267 267 " for %(fd)s?"
268 268 "$$ &Local $$ &Other $$ &Unresolved") % prompts, 2)
269 269 choice = ['local', 'other', 'unresolved'][index]
270 270
271 271 if choice == 'other':
272 272 return _iother(repo, mynode, orig, fcd, fco, fca, toolconf,
273 273 labels)
274 274 elif choice == 'local':
275 275 return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf,
276 276 labels)
277 277 elif choice == 'unresolved':
278 278 return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf,
279 279 labels)
280 280 except error.ResponseExpected:
281 281 ui.write("\n")
282 282 return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf,
283 283 labels)
284 284
285 285 @internaltool('local', nomerge)
286 286 def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
287 287 """Uses the local `p1()` version of files as the merged version."""
288 288 return 0, fcd.isabsent()
289 289
290 290 @internaltool('other', nomerge)
291 291 def _iother(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
292 292 """Uses the other `p2()` version of files as the merged version."""
293 293 if fco.isabsent():
294 294 # local changed, remote deleted -- 'deleted' picked
295 295 repo.wvfs.unlinkpath(fcd.path())
296 296 deleted = True
297 297 else:
298 298 repo.wwrite(fcd.path(), fco.data(), fco.flags())
299 299 deleted = False
300 300 return 0, deleted
301 301
302 302 @internaltool('fail', nomerge)
303 303 def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
304 304 """
305 305 Rather than attempting to merge files that were modified on both
306 306 branches, it marks them as unresolved. The resolve command must be
307 307 used to resolve these conflicts."""
308 308 # for change/delete conflicts write out the changed version, then fail
309 309 if fcd.isabsent():
310 310 repo.wwrite(fcd.path(), fco.data(), fco.flags())
311 311 return 1, False
312 312
313 313 def _premerge(repo, fcd, fco, fca, toolconf, files, labels=None):
314 314 tool, toolpath, binary, symlink = toolconf
315 315 if symlink or fcd.isabsent() or fco.isabsent():
316 316 return 1
317 317 a, b, c, back = files
318 318
319 319 ui = repo.ui
320 320
321 321 validkeep = ['keep', 'keep-merge3']
322 322
323 323 # do we attempt to simplemerge first?
324 324 try:
325 325 premerge = _toolbool(ui, tool, "premerge", not binary)
326 326 except error.ConfigError:
327 327 premerge = _toolstr(ui, tool, "premerge").lower()
328 328 if premerge not in validkeep:
329 329 _valid = ', '.join(["'" + v + "'" for v in validkeep])
330 330 raise error.ConfigError(_("%s.premerge not valid "
331 331 "('%s' is neither boolean nor %s)") %
332 332 (tool, premerge, _valid))
333 333
334 334 if premerge:
335 335 if premerge == 'keep-merge3':
336 336 if not labels:
337 337 labels = _defaultconflictlabels
338 338 if len(labels) < 3:
339 339 labels.append('base')
340 340 r = simplemerge.simplemerge(ui, a, b, c, quiet=True, label=labels)
341 341 if not r:
342 342 ui.debug(" premerge successful\n")
343 343 return 0
344 344 if premerge not in validkeep:
345 345 util.copyfile(back, a) # restore from backup and try again
346 346 return 1 # continue merging
347 347
348 348 def _mergecheck(repo, mynode, orig, fcd, fco, fca, toolconf):
349 349 tool, toolpath, binary, symlink = toolconf
350 350 if symlink:
351 351 repo.ui.warn(_('warning: internal %s cannot merge symlinks '
352 352 'for %s\n') % (tool, fcd.path()))
353 353 return False
354 354 if fcd.isabsent() or fco.isabsent():
355 355 repo.ui.warn(_('warning: internal %s cannot merge change/delete '
356 356 'conflict for %s\n') % (tool, fcd.path()))
357 357 return False
358 358 return True
359 359
360 360 def _merge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, mode):
361 361 """
362 362 Uses the internal non-interactive simple merge algorithm for merging
363 363 files. It will fail if there are any conflicts and leave markers in
364 364 the partially merged file. Markers will have two sections, one for each side
365 365 of merge, unless mode equals 'union' which suppresses the markers."""
366 366 a, b, c, back = files
367 367
368 368 ui = repo.ui
369 369
370 370 r = simplemerge.simplemerge(ui, a, b, c, label=labels, mode=mode)
371 371 return True, r, False
372 372
373 373 @internaltool('union', fullmerge,
374 374 _("warning: conflicts while merging %s! "
375 375 "(edit, then use 'hg resolve --mark')\n"),
376 376 precheck=_mergecheck)
377 377 def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
378 378 """
379 379 Uses the internal non-interactive simple merge algorithm for merging
380 380 files. It will use both left and right sides for conflict regions.
381 381 No markers are inserted."""
382 382 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
383 383 files, labels, 'union')
384 384
385 385 @internaltool('merge', fullmerge,
386 386 _("warning: conflicts while merging %s! "
387 387 "(edit, then use 'hg resolve --mark')\n"),
388 388 precheck=_mergecheck)
389 389 def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
390 390 """
391 391 Uses the internal non-interactive simple merge algorithm for merging
392 392 files. It will fail if there are any conflicts and leave markers in
393 393 the partially merged file. Markers will have two sections, one for each side
394 394 of merge."""
395 395 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
396 396 files, labels, 'merge')
397 397
398 398 @internaltool('merge3', fullmerge,
399 399 _("warning: conflicts while merging %s! "
400 400 "(edit, then use 'hg resolve --mark')\n"),
401 401 precheck=_mergecheck)
402 402 def _imerge3(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
403 403 """
404 404 Uses the internal non-interactive simple merge algorithm for merging
405 405 files. It will fail if there are any conflicts and leave markers in
406 406 the partially merged file. Marker will have three sections, one from each
407 407 side of the merge and one for the base content."""
408 408 if not labels:
409 409 labels = _defaultconflictlabels
410 410 if len(labels) < 3:
411 411 labels.append('base')
412 412 return _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels)
413 413
414 414 def _imergeauto(repo, mynode, orig, fcd, fco, fca, toolconf, files,
415 415 labels=None, localorother=None):
416 416 """
417 417 Generic driver for _imergelocal and _imergeother
418 418 """
419 419 assert localorother is not None
420 420 tool, toolpath, binary, symlink = toolconf
421 421 a, b, c, back = files
422 422 r = simplemerge.simplemerge(repo.ui, a, b, c, label=labels,
423 423 localorother=localorother)
424 424 return True, r
425 425
426 426 @internaltool('merge-local', mergeonly, precheck=_mergecheck)
427 427 def _imergelocal(*args, **kwargs):
428 428 """
429 429 Like :merge, but resolve all conflicts non-interactively in favor
430 430 of the local `p1()` changes."""
431 431 success, status = _imergeauto(localorother='local', *args, **kwargs)
432 432 return success, status, False
433 433
434 434 @internaltool('merge-other', mergeonly, precheck=_mergecheck)
435 435 def _imergeother(*args, **kwargs):
436 436 """
437 437 Like :merge, but resolve all conflicts non-interactively in favor
438 438 of the other `p2()` changes."""
439 439 success, status = _imergeauto(localorother='other', *args, **kwargs)
440 440 return success, status, False
441 441
442 442 @internaltool('tagmerge', mergeonly,
443 443 _("automatic tag merging of %s failed! "
444 444 "(use 'hg resolve --tool :merge' or another merge "
445 445 "tool of your choice)\n"))
446 446 def _itagmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
447 447 """
448 448 Uses the internal tag merge algorithm (experimental).
449 449 """
450 450 success, status = tagmerge.merge(repo, fcd, fco, fca)
451 451 return success, status, False
452 452
453 453 @internaltool('dump', fullmerge)
454 454 def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
455 455 """
456 456 Creates three versions of the files to merge, containing the
457 457 contents of local, other and base. These files can then be used to
458 458 perform a merge manually. If the file to be merged is named
459 459 ``a.txt``, these files will accordingly be named ``a.txt.local``,
460 460 ``a.txt.other`` and ``a.txt.base`` and they will be placed in the
461 461 same directory as ``a.txt``."""
462 462 a, b, c, back = files
463 463
464 464 fd = fcd.path()
465 465
466 466 util.copyfile(a, a + ".local")
467 467 repo.wwrite(fd + ".other", fco.data(), fco.flags())
468 468 repo.wwrite(fd + ".base", fca.data(), fca.flags())
469 469 return False, 1, False
470 470
471 471 def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
472 472 tool, toolpath, binary, symlink = toolconf
473 473 if fcd.isabsent() or fco.isabsent():
474 474 repo.ui.warn(_('warning: %s cannot merge change/delete conflict '
475 475 'for %s\n') % (tool, fcd.path()))
476 476 return False, 1, None
477 477 a, b, c, back = files
478 478 out = ""
479 479 env = {'HG_FILE': fcd.path(),
480 480 'HG_MY_NODE': short(mynode),
481 481 'HG_OTHER_NODE': str(fco.changectx()),
482 482 'HG_BASE_NODE': str(fca.changectx()),
483 483 'HG_MY_ISLINK': 'l' in fcd.flags(),
484 484 'HG_OTHER_ISLINK': 'l' in fco.flags(),
485 485 'HG_BASE_ISLINK': 'l' in fca.flags(),
486 486 }
487 487
488 488 ui = repo.ui
489 489
490 490 args = _toolstr(ui, tool, "args", '$local $base $other')
491 491 if "$output" in args:
492 492 out, a = a, back # read input from backup, write to original
493 493 replace = {'local': a, 'base': b, 'other': c, 'output': out}
494 494 args = util.interpolate(r'\$', replace, args,
495 495 lambda s: util.shellquote(util.localpath(s)))
496 496 cmd = toolpath + ' ' + args
497 497 if _toolbool(ui, tool, "gui"):
498 498 repo.ui.status(_('running merge tool %s for file %s\n') %
499 499 (tool, fcd.path()))
500 500 repo.ui.debug('launching merge tool: %s\n' % cmd)
501 501 r = ui.system(cmd, cwd=repo.root, environ=env, blockedtag='mergetool')
502 502 repo.ui.debug('merge tool returned: %s\n' % r)
503 503 return True, r, False
504 504
505 505 def _formatconflictmarker(repo, ctx, template, label, pad):
506 506 """Applies the given template to the ctx, prefixed by the label.
507 507
508 508 Pad is the minimum width of the label prefix, so that multiple markers
509 509 can have aligned templated parts.
510 510 """
511 511 if ctx.node() is None:
512 512 ctx = ctx.p1()
513 513
514 514 props = templatekw.keywords.copy()
515 515 props['templ'] = template
516 516 props['ctx'] = ctx
517 517 props['repo'] = repo
518 518 templateresult = template('conflictmarker', **props)
519 519
520 520 label = ('%s:' % label).ljust(pad + 1)
521 521 mark = '%s %s' % (label, templater.stringify(templateresult))
522 522
523 523 if mark:
524 524 mark = mark.splitlines()[0] # split for safety
525 525
526 526 # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
527 527 return util.ellipsis(mark, 80 - 8)
528 528
529 529 _defaultconflictmarker = ('{node|short} '
530 530 '{ifeq(tags, "tip", "", '
531 531 'ifeq(tags, "", "", "{tags} "))}'
532 532 '{if(bookmarks, "{bookmarks} ")}'
533 533 '{ifeq(branch, "default", "", "{branch} ")}'
534 534 '- {author|user}: {desc|firstline}')
535 535
536 536 _defaultconflictlabels = ['local', 'other']
537 537
538 538 def _formatlabels(repo, fcd, fco, fca, labels):
539 539 """Formats the given labels using the conflict marker template.
540 540
541 541 Returns a list of formatted labels.
542 542 """
543 543 cd = fcd.changectx()
544 544 co = fco.changectx()
545 545 ca = fca.changectx()
546 546
547 547 ui = repo.ui
548 548 template = ui.config('ui', 'mergemarkertemplate', _defaultconflictmarker)
549 549 template = templater.unquotestring(template)
550 550 tmpl = formatter.maketemplater(ui, 'conflictmarker', template)
551 551
552 552 pad = max(len(l) for l in labels)
553 553
554 554 newlabels = [_formatconflictmarker(repo, cd, tmpl, labels[0], pad),
555 555 _formatconflictmarker(repo, co, tmpl, labels[1], pad)]
556 556 if len(labels) > 2:
557 557 newlabels.append(_formatconflictmarker(repo, ca, tmpl, labels[2], pad))
558 558 return newlabels
559 559
560 560 def partextras(labels):
561 561 """Return a dictionary of extra labels for use in prompts to the user
562 562
563 563 Intended use is in strings of the form "(l)ocal%(l)s".
564 564 """
565 565 if labels is None:
566 566 return {
567 567 "l": "",
568 568 "o": "",
569 569 }
570 570
571 571 return {
572 572 "l": " [%s]" % labels[0],
573 573 "o": " [%s]" % labels[1],
574 574 }
575 575
576 576 def _filemerge(premerge, repo, mynode, orig, fcd, fco, fca, labels=None):
577 577 """perform a 3-way merge in the working directory
578 578
579 579 premerge = whether this is a premerge
580 580 mynode = parent node before merge
581 581 orig = original local filename before merge
582 582 fco = other file context
583 583 fca = ancestor file context
584 584 fcd = local file context for current/destination file
585 585
586 586 Returns whether the merge is complete, the return value of the merge, and
587 587 a boolean indicating whether the file was deleted from disk."""
588 588
589 589 def temp(prefix, ctx):
590 590 fullbase, ext = os.path.splitext(ctx.path())
591 591 pre = "%s~%s." % (os.path.basename(fullbase), prefix)
592 592 (fd, name) = tempfile.mkstemp(prefix=pre, suffix=ext)
593 593 data = repo.wwritedata(ctx.path(), ctx.data())
594 594 f = os.fdopen(fd, pycompat.sysstr("wb"))
595 595 f.write(data)
596 596 f.close()
597 597 return name
598 598
599 599 if not fco.cmp(fcd): # files identical?
600 600 return True, None, False
601 601
602 602 ui = repo.ui
603 603 fd = fcd.path()
604 604 binary = fcd.isbinary() or fco.isbinary() or fca.isbinary()
605 605 symlink = 'l' in fcd.flags() + fco.flags()
606 606 changedelete = fcd.isabsent() or fco.isabsent()
607 607 tool, toolpath = _picktool(repo, ui, fd, binary, symlink, changedelete)
608 608 if tool in internals and tool.startswith('internal:'):
609 609 # normalize to new-style names (':merge' etc)
610 610 tool = tool[len('internal'):]
611 611 ui.debug("picked tool '%s' for %s (binary %s symlink %s changedelete %s)\n"
612 612 % (tool, fd, binary, symlink, changedelete))
613 613
614 614 if tool in internals:
615 615 func = internals[tool]
616 616 mergetype = func.mergetype
617 617 onfailure = func.onfailure
618 618 precheck = func.precheck
619 619 else:
620 620 func = _xmerge
621 621 mergetype = fullmerge
622 622 onfailure = _("merging %s failed!\n")
623 623 precheck = None
624 624
625 625 toolconf = tool, toolpath, binary, symlink
626 626
627 627 if mergetype == nomerge:
628 628 r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf, labels)
629 629 return True, r, deleted
630 630
631 631 if premerge:
632 632 if orig != fco.path():
633 633 ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
634 634 else:
635 635 ui.status(_("merging %s\n") % fd)
636 636
637 637 ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca))
638 638
639 639 if precheck and not precheck(repo, mynode, orig, fcd, fco, fca,
640 640 toolconf):
641 641 if onfailure:
642 642 ui.warn(onfailure % fd)
643 643 return True, 1, False
644 644
645 645 a = repo.wjoin(fd)
646 646 b = temp("base", fca)
647 647 c = temp("other", fco)
648 648 if not fcd.isabsent():
649 649 back = scmutil.origpath(ui, repo, a)
650 650 if premerge:
651 651 util.copyfile(a, back)
652 652 else:
653 653 back = None
654 654 files = (a, b, c, back)
655 655
656 656 r = 1
657 657 try:
658 658 markerstyle = ui.config('ui', 'mergemarkers', 'basic')
659 659 if not labels:
660 660 labels = _defaultconflictlabels
661 661 if markerstyle != 'basic':
662 662 labels = _formatlabels(repo, fcd, fco, fca, labels)
663 663
664 664 if premerge and mergetype == fullmerge:
665 665 r = _premerge(repo, fcd, fco, fca, toolconf, files, labels=labels)
666 666 # complete if premerge successful (r is 0)
667 667 return not r, r, False
668 668
669 669 needcheck, r, deleted = func(repo, mynode, orig, fcd, fco, fca,
670 670 toolconf, files, labels=labels)
671 671
672 672 if needcheck:
673 673 r = _check(r, ui, tool, fcd, files)
674 674
675 675 if r:
676 676 if onfailure:
677 677 ui.warn(onfailure % fd)
678 678
679 679 return True, r, deleted
680 680 finally:
681 681 if not r and back is not None:
682 682 util.unlink(back)
683 683 util.unlink(b)
684 684 util.unlink(c)
685 685
686 686 def _check(r, ui, tool, fcd, files):
687 687 fd = fcd.path()
688 688 a, b, c, back = files
689 689
690 690 if not r and (_toolbool(ui, tool, "checkconflicts") or
691 691 'conflicts' in _toollist(ui, tool, "check")):
692 692 if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(),
693 693 re.MULTILINE):
694 694 r = 1
695 695
696 696 checked = False
697 697 if 'prompt' in _toollist(ui, tool, "check"):
698 698 checked = True
699 699 if ui.promptchoice(_("was merge of '%s' successful (yn)?"
700 700 "$$ &Yes $$ &No") % fd, 1):
701 701 r = 1
702 702
703 703 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
704 704 'changed' in
705 705 _toollist(ui, tool, "check")):
706 706 if back is not None and filecmp.cmp(a, back):
707 707 if ui.promptchoice(_(" output file %s appears unchanged\n"
708 708 "was merge successful (yn)?"
709 709 "$$ &Yes $$ &No") % fd, 1):
710 710 r = 1
711 711
712 712 if back is not None and _toolbool(ui, tool, "fixeol"):
713 713 _matcheol(a, back)
714 714
715 715 return r
716 716
717 717 def premerge(repo, mynode, orig, fcd, fco, fca, labels=None):
718 718 return _filemerge(True, repo, mynode, orig, fcd, fco, fca, labels=labels)
719 719
720 720 def filemerge(repo, mynode, orig, fcd, fco, fca, labels=None):
721 721 return _filemerge(False, repo, mynode, orig, fcd, fco, fca, labels=labels)
722 722
723 723 # tell hggettext to extract docstrings from these functions:
724 724 i18nfunctions = internals.values()
@@ -1,1217 +1,1217 b''
1 1 test merge-tools configuration - mostly exercising filemerge.py
2 2
3 3 $ unset HGMERGE # make sure HGMERGE doesn't interfere with the test
4 4 $ hg init
5 5
6 6 revision 0
7 7
8 8 $ echo "revision 0" > f
9 9 $ echo "space" >> f
10 10 $ hg commit -Am "revision 0"
11 11 adding f
12 12
13 13 revision 1
14 14
15 15 $ echo "revision 1" > f
16 16 $ echo "space" >> f
17 17 $ hg commit -Am "revision 1"
18 18 $ hg update 0 > /dev/null
19 19
20 20 revision 2
21 21
22 22 $ echo "revision 2" > f
23 23 $ echo "space" >> f
24 24 $ hg commit -Am "revision 2"
25 25 created new head
26 26 $ hg update 0 > /dev/null
27 27
28 28 revision 3 - simple to merge
29 29
30 30 $ echo "revision 3" >> f
31 31 $ hg commit -Am "revision 3"
32 32 created new head
33 33
34 34 revision 4 - hard to merge
35 35
36 36 $ hg update 0 > /dev/null
37 37 $ echo "revision 4" > f
38 38 $ hg commit -Am "revision 4"
39 39 created new head
40 40
41 41 $ echo "[merge-tools]" > .hg/hgrc
42 42
43 43 $ beforemerge() {
44 44 > cat .hg/hgrc
45 45 > echo "# hg update -C 1"
46 46 > hg update -C 1 > /dev/null
47 47 > }
48 48 $ aftermerge() {
49 49 > echo "# cat f"
50 50 > cat f
51 51 > echo "# hg stat"
52 52 > hg stat
53 53 > echo "# hg resolve --list"
54 54 > hg resolve --list
55 55 > rm -f f.orig
56 56 > }
57 57
58 58 Tool selection
59 59
60 60 default is internal merge:
61 61
62 62 $ beforemerge
63 63 [merge-tools]
64 64 # hg update -C 1
65 65
66 66 hg merge -r 2
67 67 override $PATH to ensure hgmerge not visible; use $PYTHON in case we're
68 68 running from a devel copy, not a temp installation
69 69
70 70 $ PATH="$BINDIR:/usr/sbin" $PYTHON "$BINDIR"/hg merge -r 2
71 71 merging f
72 72 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
73 73 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
74 74 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
75 75 [1]
76 76 $ aftermerge
77 77 # cat f
78 78 <<<<<<< working copy: ef83787e2614 - test: revision 1
79 79 revision 1
80 80 =======
81 81 revision 2
82 82 >>>>>>> merge rev: 0185f4e0cf02 - test: revision 2
83 83 space
84 84 # hg stat
85 85 M f
86 86 ? f.orig
87 87 # hg resolve --list
88 88 U f
89 89
90 90 simplest hgrc using false for merge:
91 91
92 92 $ echo "false.whatever=" >> .hg/hgrc
93 93 $ beforemerge
94 94 [merge-tools]
95 95 false.whatever=
96 96 # hg update -C 1
97 97 $ hg merge -r 2
98 98 merging f
99 99 merging f failed!
100 100 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
101 101 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
102 102 [1]
103 103 $ aftermerge
104 104 # cat f
105 105 revision 1
106 106 space
107 107 # hg stat
108 108 M f
109 109 ? f.orig
110 110 # hg resolve --list
111 111 U f
112 112
113 113 #if unix-permissions
114 114
115 115 unexecutable file in $PATH shouldn't be found:
116 116
117 117 $ echo "echo fail" > false
118 118 $ hg up -qC 1
119 119 $ PATH="`pwd`:$BINDIR:/usr/sbin" $PYTHON "$BINDIR"/hg merge -r 2
120 120 merging f
121 121 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
122 122 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
123 123 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
124 124 [1]
125 125 $ rm false
126 126
127 127 #endif
128 128
129 129 executable directory in $PATH shouldn't be found:
130 130
131 131 $ mkdir false
132 132 $ hg up -qC 1
133 133 $ PATH="`pwd`:$BINDIR:/usr/sbin" $PYTHON "$BINDIR"/hg merge -r 2
134 134 merging f
135 135 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
136 136 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
137 137 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
138 138 [1]
139 139 $ rmdir false
140 140
141 141 true with higher .priority gets precedence:
142 142
143 143 $ echo "true.priority=1" >> .hg/hgrc
144 144 $ beforemerge
145 145 [merge-tools]
146 146 false.whatever=
147 147 true.priority=1
148 148 # hg update -C 1
149 149 $ hg merge -r 2
150 150 merging f
151 151 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
152 152 (branch merge, don't forget to commit)
153 153 $ aftermerge
154 154 # cat f
155 155 revision 1
156 156 space
157 157 # hg stat
158 158 M f
159 159 # hg resolve --list
160 160 R f
161 161
162 162 unless lowered on command line:
163 163
164 164 $ beforemerge
165 165 [merge-tools]
166 166 false.whatever=
167 167 true.priority=1
168 168 # hg update -C 1
169 169 $ hg merge -r 2 --config merge-tools.true.priority=-7
170 170 merging f
171 171 merging f failed!
172 172 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
173 173 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
174 174 [1]
175 175 $ aftermerge
176 176 # cat f
177 177 revision 1
178 178 space
179 179 # hg stat
180 180 M f
181 181 ? f.orig
182 182 # hg resolve --list
183 183 U f
184 184
185 185 or false set higher on command line:
186 186
187 187 $ beforemerge
188 188 [merge-tools]
189 189 false.whatever=
190 190 true.priority=1
191 191 # hg update -C 1
192 192 $ hg merge -r 2 --config merge-tools.false.priority=117
193 193 merging f
194 194 merging f failed!
195 195 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
196 196 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
197 197 [1]
198 198 $ aftermerge
199 199 # cat f
200 200 revision 1
201 201 space
202 202 # hg stat
203 203 M f
204 204 ? f.orig
205 205 # hg resolve --list
206 206 U f
207 207
208 208 or true set to disabled:
209 209 $ beforemerge
210 210 [merge-tools]
211 211 false.whatever=
212 212 true.priority=1
213 213 # hg update -C 1
214 214 $ hg merge -r 2 --config merge-tools.true.disabled=yes
215 215 merging f
216 216 merging f failed!
217 217 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
218 218 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
219 219 [1]
220 220 $ aftermerge
221 221 # cat f
222 222 revision 1
223 223 space
224 224 # hg stat
225 225 M f
226 226 ? f.orig
227 227 # hg resolve --list
228 228 U f
229 229
230 230 or true.executable not found in PATH:
231 231
232 232 $ beforemerge
233 233 [merge-tools]
234 234 false.whatever=
235 235 true.priority=1
236 236 # hg update -C 1
237 237 $ hg merge -r 2 --config merge-tools.true.executable=nonexistentmergetool
238 238 merging f
239 239 merging f failed!
240 240 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
241 241 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
242 242 [1]
243 243 $ aftermerge
244 244 # cat f
245 245 revision 1
246 246 space
247 247 # hg stat
248 248 M f
249 249 ? f.orig
250 250 # hg resolve --list
251 251 U f
252 252
253 253 or true.executable with bogus path:
254 254
255 255 $ beforemerge
256 256 [merge-tools]
257 257 false.whatever=
258 258 true.priority=1
259 259 # hg update -C 1
260 260 $ hg merge -r 2 --config merge-tools.true.executable=/nonexistent/mergetool
261 261 merging f
262 262 merging f failed!
263 263 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
264 264 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
265 265 [1]
266 266 $ aftermerge
267 267 # cat f
268 268 revision 1
269 269 space
270 270 # hg stat
271 271 M f
272 272 ? f.orig
273 273 # hg resolve --list
274 274 U f
275 275
276 276 but true.executable set to cat found in PATH works:
277 277
278 278 $ echo "true.executable=cat" >> .hg/hgrc
279 279 $ beforemerge
280 280 [merge-tools]
281 281 false.whatever=
282 282 true.priority=1
283 283 true.executable=cat
284 284 # hg update -C 1
285 285 $ hg merge -r 2
286 286 merging f
287 287 revision 1
288 288 space
289 289 revision 0
290 290 space
291 291 revision 2
292 292 space
293 293 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
294 294 (branch merge, don't forget to commit)
295 295 $ aftermerge
296 296 # cat f
297 297 revision 1
298 298 space
299 299 # hg stat
300 300 M f
301 301 # hg resolve --list
302 302 R f
303 303
304 304 and true.executable set to cat with path works:
305 305
306 306 $ beforemerge
307 307 [merge-tools]
308 308 false.whatever=
309 309 true.priority=1
310 310 true.executable=cat
311 311 # hg update -C 1
312 312 $ hg merge -r 2 --config merge-tools.true.executable=cat
313 313 merging f
314 314 revision 1
315 315 space
316 316 revision 0
317 317 space
318 318 revision 2
319 319 space
320 320 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
321 321 (branch merge, don't forget to commit)
322 322 $ aftermerge
323 323 # cat f
324 324 revision 1
325 325 space
326 326 # hg stat
327 327 M f
328 328 # hg resolve --list
329 329 R f
330 330
331 331 #if unix-permissions
332 332
333 333 environment variables in true.executable are handled:
334 334
335 335 $ echo 'echo "custom merge tool"' > .hg/merge.sh
336 336 $ beforemerge
337 337 [merge-tools]
338 338 false.whatever=
339 339 true.priority=1
340 340 true.executable=cat
341 341 # hg update -C 1
342 342 $ hg --config merge-tools.true.executable='sh' \
343 343 > --config merge-tools.true.args=.hg/merge.sh \
344 344 > merge -r 2
345 345 merging f
346 346 custom merge tool
347 347 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
348 348 (branch merge, don't forget to commit)
349 349 $ aftermerge
350 350 # cat f
351 351 revision 1
352 352 space
353 353 # hg stat
354 354 M f
355 355 # hg resolve --list
356 356 R f
357 357
358 358 #endif
359 359
360 360 Tool selection and merge-patterns
361 361
362 362 merge-patterns specifies new tool false:
363 363
364 364 $ beforemerge
365 365 [merge-tools]
366 366 false.whatever=
367 367 true.priority=1
368 368 true.executable=cat
369 369 # hg update -C 1
370 370 $ hg merge -r 2 --config merge-patterns.f=false
371 371 merging f
372 372 merging f failed!
373 373 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
374 374 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
375 375 [1]
376 376 $ aftermerge
377 377 # cat f
378 378 revision 1
379 379 space
380 380 # hg stat
381 381 M f
382 382 ? f.orig
383 383 # hg resolve --list
384 384 U f
385 385
386 386 merge-patterns specifies executable not found in PATH and gets warning:
387 387
388 388 $ beforemerge
389 389 [merge-tools]
390 390 false.whatever=
391 391 true.priority=1
392 392 true.executable=cat
393 393 # hg update -C 1
394 394 $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=nonexistentmergetool
395 couldn't find merge tool true specified for f
395 couldn't find merge tool true (for pattern f)
396 396 merging f
397 couldn't find merge tool true specified for f
397 couldn't find merge tool true (for pattern f)
398 398 merging f failed!
399 399 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
400 400 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
401 401 [1]
402 402 $ aftermerge
403 403 # cat f
404 404 revision 1
405 405 space
406 406 # hg stat
407 407 M f
408 408 ? f.orig
409 409 # hg resolve --list
410 410 U f
411 411
412 412 merge-patterns specifies executable with bogus path and gets warning:
413 413
414 414 $ beforemerge
415 415 [merge-tools]
416 416 false.whatever=
417 417 true.priority=1
418 418 true.executable=cat
419 419 # hg update -C 1
420 420 $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=/nonexistent/mergetool
421 couldn't find merge tool true specified for f
421 couldn't find merge tool true (for pattern f)
422 422 merging f
423 couldn't find merge tool true specified for f
423 couldn't find merge tool true (for pattern f)
424 424 merging f failed!
425 425 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
426 426 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
427 427 [1]
428 428 $ aftermerge
429 429 # cat f
430 430 revision 1
431 431 space
432 432 # hg stat
433 433 M f
434 434 ? f.orig
435 435 # hg resolve --list
436 436 U f
437 437
438 438 ui.merge overrules priority
439 439
440 440 ui.merge specifies false:
441 441
442 442 $ beforemerge
443 443 [merge-tools]
444 444 false.whatever=
445 445 true.priority=1
446 446 true.executable=cat
447 447 # hg update -C 1
448 448 $ hg merge -r 2 --config ui.merge=false
449 449 merging f
450 450 merging f failed!
451 451 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
452 452 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
453 453 [1]
454 454 $ aftermerge
455 455 # cat f
456 456 revision 1
457 457 space
458 458 # hg stat
459 459 M f
460 460 ? f.orig
461 461 # hg resolve --list
462 462 U f
463 463
464 464 ui.merge specifies internal:fail:
465 465
466 466 $ beforemerge
467 467 [merge-tools]
468 468 false.whatever=
469 469 true.priority=1
470 470 true.executable=cat
471 471 # hg update -C 1
472 472 $ hg merge -r 2 --config ui.merge=internal:fail
473 473 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
474 474 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
475 475 [1]
476 476 $ aftermerge
477 477 # cat f
478 478 revision 1
479 479 space
480 480 # hg stat
481 481 M f
482 482 # hg resolve --list
483 483 U f
484 484
485 485 ui.merge specifies :local (without internal prefix):
486 486
487 487 $ beforemerge
488 488 [merge-tools]
489 489 false.whatever=
490 490 true.priority=1
491 491 true.executable=cat
492 492 # hg update -C 1
493 493 $ hg merge -r 2 --config ui.merge=:local
494 494 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
495 495 (branch merge, don't forget to commit)
496 496 $ aftermerge
497 497 # cat f
498 498 revision 1
499 499 space
500 500 # hg stat
501 501 M f
502 502 # hg resolve --list
503 503 R f
504 504
505 505 ui.merge specifies internal:other:
506 506
507 507 $ beforemerge
508 508 [merge-tools]
509 509 false.whatever=
510 510 true.priority=1
511 511 true.executable=cat
512 512 # hg update -C 1
513 513 $ hg merge -r 2 --config ui.merge=internal:other
514 514 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
515 515 (branch merge, don't forget to commit)
516 516 $ aftermerge
517 517 # cat f
518 518 revision 2
519 519 space
520 520 # hg stat
521 521 M f
522 522 # hg resolve --list
523 523 R f
524 524
525 525 ui.merge specifies internal:prompt:
526 526
527 527 $ beforemerge
528 528 [merge-tools]
529 529 false.whatever=
530 530 true.priority=1
531 531 true.executable=cat
532 532 # hg update -C 1
533 533 $ hg merge -r 2 --config ui.merge=internal:prompt
534 534 keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved for f? u
535 535 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
536 536 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
537 537 [1]
538 538 $ aftermerge
539 539 # cat f
540 540 revision 1
541 541 space
542 542 # hg stat
543 543 M f
544 544 # hg resolve --list
545 545 U f
546 546
547 547 ui.merge specifies :prompt, with 'leave unresolved' chosen
548 548
549 549 $ beforemerge
550 550 [merge-tools]
551 551 false.whatever=
552 552 true.priority=1
553 553 true.executable=cat
554 554 # hg update -C 1
555 555 $ hg merge -r 2 --config ui.merge=:prompt --config ui.interactive=True << EOF
556 556 > u
557 557 > EOF
558 558 keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved for f? u
559 559 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
560 560 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
561 561 [1]
562 562 $ aftermerge
563 563 # cat f
564 564 revision 1
565 565 space
566 566 # hg stat
567 567 M f
568 568 # hg resolve --list
569 569 U f
570 570
571 571 prompt with EOF
572 572
573 573 $ beforemerge
574 574 [merge-tools]
575 575 false.whatever=
576 576 true.priority=1
577 577 true.executable=cat
578 578 # hg update -C 1
579 579 $ hg merge -r 2 --config ui.merge=internal:prompt --config ui.interactive=true
580 580 keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved for f?
581 581 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
582 582 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
583 583 [1]
584 584 $ aftermerge
585 585 # cat f
586 586 revision 1
587 587 space
588 588 # hg stat
589 589 M f
590 590 # hg resolve --list
591 591 U f
592 592 $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true
593 593 keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved for f?
594 594 [1]
595 595 $ aftermerge
596 596 # cat f
597 597 revision 1
598 598 space
599 599 # hg stat
600 600 M f
601 601 ? f.orig
602 602 # hg resolve --list
603 603 U f
604 604 $ rm f
605 605 $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true
606 606 keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved for f?
607 607 [1]
608 608 $ aftermerge
609 609 # cat f
610 610 revision 1
611 611 space
612 612 # hg stat
613 613 M f
614 614 # hg resolve --list
615 615 U f
616 616 $ hg resolve --all --config ui.merge=internal:prompt
617 617 keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved for f? u
618 618 [1]
619 619 $ aftermerge
620 620 # cat f
621 621 revision 1
622 622 space
623 623 # hg stat
624 624 M f
625 625 ? f.orig
626 626 # hg resolve --list
627 627 U f
628 628
629 629 ui.merge specifies internal:dump:
630 630
631 631 $ beforemerge
632 632 [merge-tools]
633 633 false.whatever=
634 634 true.priority=1
635 635 true.executable=cat
636 636 # hg update -C 1
637 637 $ hg merge -r 2 --config ui.merge=internal:dump
638 638 merging f
639 639 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
640 640 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
641 641 [1]
642 642 $ aftermerge
643 643 # cat f
644 644 revision 1
645 645 space
646 646 # hg stat
647 647 M f
648 648 ? f.base
649 649 ? f.local
650 650 ? f.orig
651 651 ? f.other
652 652 # hg resolve --list
653 653 U f
654 654
655 655 f.base:
656 656
657 657 $ cat f.base
658 658 revision 0
659 659 space
660 660
661 661 f.local:
662 662
663 663 $ cat f.local
664 664 revision 1
665 665 space
666 666
667 667 f.other:
668 668
669 669 $ cat f.other
670 670 revision 2
671 671 space
672 672 $ rm f.base f.local f.other
673 673
674 674 ui.merge specifies internal:other but is overruled by pattern for false:
675 675
676 676 $ beforemerge
677 677 [merge-tools]
678 678 false.whatever=
679 679 true.priority=1
680 680 true.executable=cat
681 681 # hg update -C 1
682 682 $ hg merge -r 2 --config ui.merge=internal:other --config merge-patterns.f=false
683 683 merging f
684 684 merging f failed!
685 685 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
686 686 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
687 687 [1]
688 688 $ aftermerge
689 689 # cat f
690 690 revision 1
691 691 space
692 692 # hg stat
693 693 M f
694 694 ? f.orig
695 695 # hg resolve --list
696 696 U f
697 697
698 698 Premerge
699 699
700 700 ui.merge specifies internal:other but is overruled by --tool=false
701 701
702 702 $ beforemerge
703 703 [merge-tools]
704 704 false.whatever=
705 705 true.priority=1
706 706 true.executable=cat
707 707 # hg update -C 1
708 708 $ hg merge -r 2 --config ui.merge=internal:other --tool=false
709 709 merging f
710 710 merging f failed!
711 711 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
712 712 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
713 713 [1]
714 714 $ aftermerge
715 715 # cat f
716 716 revision 1
717 717 space
718 718 # hg stat
719 719 M f
720 720 ? f.orig
721 721 # hg resolve --list
722 722 U f
723 723
724 724 HGMERGE specifies internal:other but is overruled by --tool=false
725 725
726 726 $ HGMERGE=internal:other ; export HGMERGE
727 727 $ beforemerge
728 728 [merge-tools]
729 729 false.whatever=
730 730 true.priority=1
731 731 true.executable=cat
732 732 # hg update -C 1
733 733 $ hg merge -r 2 --tool=false
734 734 merging f
735 735 merging f failed!
736 736 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
737 737 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
738 738 [1]
739 739 $ aftermerge
740 740 # cat f
741 741 revision 1
742 742 space
743 743 # hg stat
744 744 M f
745 745 ? f.orig
746 746 # hg resolve --list
747 747 U f
748 748
749 749 $ unset HGMERGE # make sure HGMERGE doesn't interfere with remaining tests
750 750
751 751 update is a merge ...
752 752
753 753 (this also tests that files reverted with '--rev REV' are treated as
754 754 "modified", even if none of mode, size and timestamp of them isn't
755 755 changed on the filesystem (see also issue4583))
756 756
757 757 $ cat >> $HGRCPATH <<EOF
758 758 > [fakedirstatewritetime]
759 759 > # emulate invoking dirstate.write() via repo.status()
760 760 > # at 2000-01-01 00:00
761 761 > fakenow = 200001010000
762 762 > EOF
763 763
764 764 $ beforemerge
765 765 [merge-tools]
766 766 false.whatever=
767 767 true.priority=1
768 768 true.executable=cat
769 769 # hg update -C 1
770 770 $ hg update -q 0
771 771 $ f -s f
772 772 f: size=17
773 773 $ touch -t 200001010000 f
774 774 $ hg debugrebuildstate
775 775 $ cat >> $HGRCPATH <<EOF
776 776 > [extensions]
777 777 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
778 778 > EOF
779 779 $ hg revert -q -r 1 .
780 780 $ cat >> $HGRCPATH <<EOF
781 781 > [extensions]
782 782 > fakedirstatewritetime = !
783 783 > EOF
784 784 $ f -s f
785 785 f: size=17
786 786 $ touch -t 200001010000 f
787 787 $ hg status f
788 788 M f
789 789 $ hg update -r 2
790 790 merging f
791 791 revision 1
792 792 space
793 793 revision 0
794 794 space
795 795 revision 2
796 796 space
797 797 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
798 798 $ aftermerge
799 799 # cat f
800 800 revision 1
801 801 space
802 802 # hg stat
803 803 M f
804 804 # hg resolve --list
805 805 R f
806 806
807 807 update should also have --tool
808 808
809 809 $ beforemerge
810 810 [merge-tools]
811 811 false.whatever=
812 812 true.priority=1
813 813 true.executable=cat
814 814 # hg update -C 1
815 815 $ hg update -q 0
816 816 $ f -s f
817 817 f: size=17
818 818 $ touch -t 200001010000 f
819 819 $ hg debugrebuildstate
820 820 $ cat >> $HGRCPATH <<EOF
821 821 > [extensions]
822 822 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
823 823 > EOF
824 824 $ hg revert -q -r 1 .
825 825 $ cat >> $HGRCPATH <<EOF
826 826 > [extensions]
827 827 > fakedirstatewritetime = !
828 828 > EOF
829 829 $ f -s f
830 830 f: size=17
831 831 $ touch -t 200001010000 f
832 832 $ hg status f
833 833 M f
834 834 $ hg update -r 2 --tool false
835 835 merging f
836 836 merging f failed!
837 837 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
838 838 use 'hg resolve' to retry unresolved file merges
839 839 [1]
840 840 $ aftermerge
841 841 # cat f
842 842 revision 1
843 843 space
844 844 # hg stat
845 845 M f
846 846 ? f.orig
847 847 # hg resolve --list
848 848 U f
849 849
850 850 Default is silent simplemerge:
851 851
852 852 $ beforemerge
853 853 [merge-tools]
854 854 false.whatever=
855 855 true.priority=1
856 856 true.executable=cat
857 857 # hg update -C 1
858 858 $ hg merge -r 3
859 859 merging f
860 860 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
861 861 (branch merge, don't forget to commit)
862 862 $ aftermerge
863 863 # cat f
864 864 revision 1
865 865 space
866 866 revision 3
867 867 # hg stat
868 868 M f
869 869 # hg resolve --list
870 870 R f
871 871
872 872 .premerge=True is same:
873 873
874 874 $ beforemerge
875 875 [merge-tools]
876 876 false.whatever=
877 877 true.priority=1
878 878 true.executable=cat
879 879 # hg update -C 1
880 880 $ hg merge -r 3 --config merge-tools.true.premerge=True
881 881 merging f
882 882 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
883 883 (branch merge, don't forget to commit)
884 884 $ aftermerge
885 885 # cat f
886 886 revision 1
887 887 space
888 888 revision 3
889 889 # hg stat
890 890 M f
891 891 # hg resolve --list
892 892 R f
893 893
894 894 .premerge=False executes merge-tool:
895 895
896 896 $ beforemerge
897 897 [merge-tools]
898 898 false.whatever=
899 899 true.priority=1
900 900 true.executable=cat
901 901 # hg update -C 1
902 902 $ hg merge -r 3 --config merge-tools.true.premerge=False
903 903 merging f
904 904 revision 1
905 905 space
906 906 revision 0
907 907 space
908 908 revision 0
909 909 space
910 910 revision 3
911 911 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
912 912 (branch merge, don't forget to commit)
913 913 $ aftermerge
914 914 # cat f
915 915 revision 1
916 916 space
917 917 # hg stat
918 918 M f
919 919 # hg resolve --list
920 920 R f
921 921
922 922 premerge=keep keeps conflict markers in:
923 923
924 924 $ beforemerge
925 925 [merge-tools]
926 926 false.whatever=
927 927 true.priority=1
928 928 true.executable=cat
929 929 # hg update -C 1
930 930 $ hg merge -r 4 --config merge-tools.true.premerge=keep
931 931 merging f
932 932 <<<<<<< working copy: ef83787e2614 - test: revision 1
933 933 revision 1
934 934 space
935 935 =======
936 936 revision 4
937 937 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
938 938 revision 0
939 939 space
940 940 revision 4
941 941 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
942 942 (branch merge, don't forget to commit)
943 943 $ aftermerge
944 944 # cat f
945 945 <<<<<<< working copy: ef83787e2614 - test: revision 1
946 946 revision 1
947 947 space
948 948 =======
949 949 revision 4
950 950 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
951 951 # hg stat
952 952 M f
953 953 # hg resolve --list
954 954 R f
955 955
956 956 premerge=keep-merge3 keeps conflict markers with base content:
957 957
958 958 $ beforemerge
959 959 [merge-tools]
960 960 false.whatever=
961 961 true.priority=1
962 962 true.executable=cat
963 963 # hg update -C 1
964 964 $ hg merge -r 4 --config merge-tools.true.premerge=keep-merge3
965 965 merging f
966 966 <<<<<<< working copy: ef83787e2614 - test: revision 1
967 967 revision 1
968 968 space
969 969 ||||||| base
970 970 revision 0
971 971 space
972 972 =======
973 973 revision 4
974 974 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
975 975 revision 0
976 976 space
977 977 revision 4
978 978 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
979 979 (branch merge, don't forget to commit)
980 980 $ aftermerge
981 981 # cat f
982 982 <<<<<<< working copy: ef83787e2614 - test: revision 1
983 983 revision 1
984 984 space
985 985 ||||||| base
986 986 revision 0
987 987 space
988 988 =======
989 989 revision 4
990 990 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
991 991 # hg stat
992 992 M f
993 993 # hg resolve --list
994 994 R f
995 995
996 996
997 997 Tool execution
998 998
999 999 set tools.args explicit to include $base $local $other $output:
1000 1000
1001 1001 $ beforemerge
1002 1002 [merge-tools]
1003 1003 false.whatever=
1004 1004 true.priority=1
1005 1005 true.executable=cat
1006 1006 # hg update -C 1
1007 1007 $ hg merge -r 2 --config merge-tools.true.executable=head --config merge-tools.true.args='$base $local $other $output' \
1008 1008 > | sed 's,==> .* <==,==> ... <==,g'
1009 1009 merging f
1010 1010 ==> ... <==
1011 1011 revision 0
1012 1012 space
1013 1013
1014 1014 ==> ... <==
1015 1015 revision 1
1016 1016 space
1017 1017
1018 1018 ==> ... <==
1019 1019 revision 2
1020 1020 space
1021 1021
1022 1022 ==> ... <==
1023 1023 revision 1
1024 1024 space
1025 1025 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1026 1026 (branch merge, don't forget to commit)
1027 1027 $ aftermerge
1028 1028 # cat f
1029 1029 revision 1
1030 1030 space
1031 1031 # hg stat
1032 1032 M f
1033 1033 # hg resolve --list
1034 1034 R f
1035 1035
1036 1036 Merge with "echo mergeresult > $local":
1037 1037
1038 1038 $ beforemerge
1039 1039 [merge-tools]
1040 1040 false.whatever=
1041 1041 true.priority=1
1042 1042 true.executable=cat
1043 1043 # hg update -C 1
1044 1044 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $local'
1045 1045 merging f
1046 1046 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1047 1047 (branch merge, don't forget to commit)
1048 1048 $ aftermerge
1049 1049 # cat f
1050 1050 mergeresult
1051 1051 # hg stat
1052 1052 M f
1053 1053 # hg resolve --list
1054 1054 R f
1055 1055
1056 1056 - and $local is the file f:
1057 1057
1058 1058 $ beforemerge
1059 1059 [merge-tools]
1060 1060 false.whatever=
1061 1061 true.priority=1
1062 1062 true.executable=cat
1063 1063 # hg update -C 1
1064 1064 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > f'
1065 1065 merging f
1066 1066 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1067 1067 (branch merge, don't forget to commit)
1068 1068 $ aftermerge
1069 1069 # cat f
1070 1070 mergeresult
1071 1071 # hg stat
1072 1072 M f
1073 1073 # hg resolve --list
1074 1074 R f
1075 1075
1076 1076 Merge with "echo mergeresult > $output" - the variable is a bit magic:
1077 1077
1078 1078 $ beforemerge
1079 1079 [merge-tools]
1080 1080 false.whatever=
1081 1081 true.priority=1
1082 1082 true.executable=cat
1083 1083 # hg update -C 1
1084 1084 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $output'
1085 1085 merging f
1086 1086 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1087 1087 (branch merge, don't forget to commit)
1088 1088 $ aftermerge
1089 1089 # cat f
1090 1090 mergeresult
1091 1091 # hg stat
1092 1092 M f
1093 1093 # hg resolve --list
1094 1094 R f
1095 1095
1096 1096 Merge using tool with a path that must be quoted:
1097 1097
1098 1098 $ beforemerge
1099 1099 [merge-tools]
1100 1100 false.whatever=
1101 1101 true.priority=1
1102 1102 true.executable=cat
1103 1103 # hg update -C 1
1104 1104 $ cat <<EOF > 'my merge tool'
1105 1105 > cat "\$1" "\$2" "\$3" > "\$4"
1106 1106 > EOF
1107 1107 $ hg --config merge-tools.true.executable='sh' \
1108 1108 > --config merge-tools.true.args='"./my merge tool" $base $local $other $output' \
1109 1109 > merge -r 2
1110 1110 merging f
1111 1111 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1112 1112 (branch merge, don't forget to commit)
1113 1113 $ rm -f 'my merge tool'
1114 1114 $ aftermerge
1115 1115 # cat f
1116 1116 revision 0
1117 1117 space
1118 1118 revision 1
1119 1119 space
1120 1120 revision 2
1121 1121 space
1122 1122 # hg stat
1123 1123 M f
1124 1124 # hg resolve --list
1125 1125 R f
1126 1126
1127 1127 Issue3581: Merging a filename that needs to be quoted
1128 1128 (This test doesn't work on Windows filesystems even on Linux, so check
1129 1129 for Unix-like permission)
1130 1130
1131 1131 #if unix-permissions
1132 1132 $ beforemerge
1133 1133 [merge-tools]
1134 1134 false.whatever=
1135 1135 true.priority=1
1136 1136 true.executable=cat
1137 1137 # hg update -C 1
1138 1138 $ echo "revision 5" > '"; exit 1; echo "'
1139 1139 $ hg commit -Am "revision 5"
1140 1140 adding "; exit 1; echo "
1141 1141 warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
1142 1142 $ hg update -C 1 > /dev/null
1143 1143 $ echo "revision 6" > '"; exit 1; echo "'
1144 1144 $ hg commit -Am "revision 6"
1145 1145 adding "; exit 1; echo "
1146 1146 warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
1147 1147 created new head
1148 1148 $ hg merge --config merge-tools.true.executable="true" -r 5
1149 1149 merging "; exit 1; echo "
1150 1150 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1151 1151 (branch merge, don't forget to commit)
1152 1152 $ hg update -C 1 > /dev/null
1153 1153 #endif
1154 1154
1155 1155 Merge post-processing
1156 1156
1157 1157 cat is a bad merge-tool and doesn't change:
1158 1158
1159 1159 $ beforemerge
1160 1160 [merge-tools]
1161 1161 false.whatever=
1162 1162 true.priority=1
1163 1163 true.executable=cat
1164 1164 # hg update -C 1
1165 1165 $ hg merge -y -r 2 --config merge-tools.true.checkchanged=1
1166 1166 merging f
1167 1167 revision 1
1168 1168 space
1169 1169 revision 0
1170 1170 space
1171 1171 revision 2
1172 1172 space
1173 1173 output file f appears unchanged
1174 1174 was merge successful (yn)? n
1175 1175 merging f failed!
1176 1176 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1177 1177 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1178 1178 [1]
1179 1179 $ aftermerge
1180 1180 # cat f
1181 1181 revision 1
1182 1182 space
1183 1183 # hg stat
1184 1184 M f
1185 1185 ? f.orig
1186 1186 # hg resolve --list
1187 1187 U f
1188 1188
1189 1189 #if symlink
1190 1190
1191 1191 internal merge cannot handle symlinks and shouldn't try:
1192 1192
1193 1193 $ hg update -q -C 1
1194 1194 $ rm f
1195 1195 $ ln -s symlink f
1196 1196 $ hg commit -qm 'f is symlink'
1197 1197 $ hg merge -r 2 --tool internal:merge
1198 1198 merging f
1199 1199 warning: internal :merge cannot merge symlinks for f
1200 1200 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
1201 1201 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1202 1202 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1203 1203 [1]
1204 1204
1205 1205 #endif
1206 1206
1207 1207 Verify naming of temporary files and that extension is preserved:
1208 1208
1209 1209 $ hg update -q -C 1
1210 1210 $ hg mv f f.txt
1211 1211 $ hg ci -qm "f.txt"
1212 1212 $ hg update -q -C 2
1213 1213 $ hg merge -y -r tip --tool echo --config merge-tools.echo.args='$base $local $other $output'
1214 1214 merging f and f.txt to f.txt
1215 1215 */f~base.?????? $TESTTMP/f.txt.orig */f~other.??????.txt $TESTTMP/f.txt (glob)
1216 1216 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1217 1217 (branch merge, don't forget to commit)
General Comments 0
You need to be logged in to leave comments. Login now