##// END OF EJS Templates
filemerge: make the 'local' path match the format that 'base' and 'other' use...
Kyle Lippincott -
r37095:1e30a26a default
parent child Browse files
Show More
@@ -1,900 +1,922 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 contextlib
11 11 import os
12 12 import re
13 13 import shutil
14 14 import tempfile
15 15
16 16 from .i18n import _
17 17 from .node import nullid, short
18 18
19 19 from . import (
20 20 encoding,
21 21 error,
22 22 formatter,
23 23 match,
24 24 pycompat,
25 25 registrar,
26 26 scmutil,
27 27 simplemerge,
28 28 tagmerge,
29 29 templatekw,
30 30 templater,
31 31 util,
32 32 )
33 33
34 34 def _toolstr(ui, tool, part, *args):
35 35 return ui.config("merge-tools", tool + "." + part, *args)
36 36
37 37 def _toolbool(ui, tool, part,*args):
38 38 return ui.configbool("merge-tools", tool + "." + part, *args)
39 39
40 40 def _toollist(ui, tool, part):
41 41 return ui.configlist("merge-tools", tool + "." + part)
42 42
43 43 internals = {}
44 44 # Merge tools to document.
45 45 internalsdoc = {}
46 46
47 47 internaltool = registrar.internalmerge()
48 48
49 49 # internal tool merge types
50 50 nomerge = internaltool.nomerge
51 51 mergeonly = internaltool.mergeonly # just the full merge, no premerge
52 52 fullmerge = internaltool.fullmerge # both premerge and merge
53 53
54 54 _localchangedotherdeletedmsg = _(
55 55 "local%(l)s changed %(fd)s which other%(o)s deleted\n"
56 56 "use (c)hanged version, (d)elete, or leave (u)nresolved?"
57 57 "$$ &Changed $$ &Delete $$ &Unresolved")
58 58
59 59 _otherchangedlocaldeletedmsg = _(
60 60 "other%(o)s changed %(fd)s which local%(l)s deleted\n"
61 61 "use (c)hanged version, leave (d)eleted, or "
62 62 "leave (u)nresolved?"
63 63 "$$ &Changed $$ &Deleted $$ &Unresolved")
64 64
65 65 class absentfilectx(object):
66 66 """Represents a file that's ostensibly in a context but is actually not
67 67 present in it.
68 68
69 69 This is here because it's very specific to the filemerge code for now --
70 70 other code is likely going to break with the values this returns."""
71 71 def __init__(self, ctx, f):
72 72 self._ctx = ctx
73 73 self._f = f
74 74
75 75 def path(self):
76 76 return self._f
77 77
78 78 def size(self):
79 79 return None
80 80
81 81 def data(self):
82 82 return None
83 83
84 84 def filenode(self):
85 85 return nullid
86 86
87 87 _customcmp = True
88 88 def cmp(self, fctx):
89 89 """compare with other file context
90 90
91 91 returns True if different from fctx.
92 92 """
93 93 return not (fctx.isabsent() and
94 94 fctx.ctx() == self.ctx() and
95 95 fctx.path() == self.path())
96 96
97 97 def flags(self):
98 98 return ''
99 99
100 100 def changectx(self):
101 101 return self._ctx
102 102
103 103 def isbinary(self):
104 104 return False
105 105
106 106 def isabsent(self):
107 107 return True
108 108
109 109 def _findtool(ui, tool):
110 110 if tool in internals:
111 111 return tool
112 112 return findexternaltool(ui, tool)
113 113
114 114 def findexternaltool(ui, tool):
115 115 for kn in ("regkey", "regkeyalt"):
116 116 k = _toolstr(ui, tool, kn)
117 117 if not k:
118 118 continue
119 119 p = util.lookupreg(k, _toolstr(ui, tool, "regname"))
120 120 if p:
121 121 p = util.findexe(p + _toolstr(ui, tool, "regappend", ""))
122 122 if p:
123 123 return p
124 124 exe = _toolstr(ui, tool, "executable", tool)
125 125 return util.findexe(util.expandpath(exe))
126 126
127 127 def _picktool(repo, ui, path, binary, symlink, changedelete):
128 128 def supportscd(tool):
129 129 return tool in internals and internals[tool].mergetype == nomerge
130 130
131 131 def check(tool, pat, symlink, binary, changedelete):
132 132 tmsg = tool
133 133 if pat:
134 134 tmsg = _("%s (for pattern %s)") % (tool, pat)
135 135 if not _findtool(ui, tool):
136 136 if pat: # explicitly requested tool deserves a warning
137 137 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
138 138 else: # configured but non-existing tools are more silent
139 139 ui.note(_("couldn't find merge tool %s\n") % tmsg)
140 140 elif symlink and not _toolbool(ui, tool, "symlink"):
141 141 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
142 142 elif binary and not _toolbool(ui, tool, "binary"):
143 143 ui.warn(_("tool %s can't handle binary\n") % tmsg)
144 144 elif changedelete and not supportscd(tool):
145 145 # the nomerge tools are the only tools that support change/delete
146 146 # conflicts
147 147 pass
148 148 elif not util.gui() and _toolbool(ui, tool, "gui"):
149 149 ui.warn(_("tool %s requires a GUI\n") % tmsg)
150 150 else:
151 151 return True
152 152 return False
153 153
154 154 # internal config: ui.forcemerge
155 155 # forcemerge comes from command line arguments, highest priority
156 156 force = ui.config('ui', 'forcemerge')
157 157 if force:
158 158 toolpath = _findtool(ui, force)
159 159 if changedelete and not supportscd(toolpath):
160 160 return ":prompt", None
161 161 else:
162 162 if toolpath:
163 163 return (force, util.shellquote(toolpath))
164 164 else:
165 165 # mimic HGMERGE if given tool not found
166 166 return (force, force)
167 167
168 168 # HGMERGE takes next precedence
169 169 hgmerge = encoding.environ.get("HGMERGE")
170 170 if hgmerge:
171 171 if changedelete and not supportscd(hgmerge):
172 172 return ":prompt", None
173 173 else:
174 174 return (hgmerge, hgmerge)
175 175
176 176 # then patterns
177 177 for pat, tool in ui.configitems("merge-patterns"):
178 178 mf = match.match(repo.root, '', [pat])
179 179 if mf(path) and check(tool, pat, symlink, False, changedelete):
180 180 toolpath = _findtool(ui, tool)
181 181 return (tool, util.shellquote(toolpath))
182 182
183 183 # then merge tools
184 184 tools = {}
185 185 disabled = set()
186 186 for k, v in ui.configitems("merge-tools"):
187 187 t = k.split('.')[0]
188 188 if t not in tools:
189 189 tools[t] = int(_toolstr(ui, t, "priority"))
190 190 if _toolbool(ui, t, "disabled"):
191 191 disabled.add(t)
192 192 names = tools.keys()
193 193 tools = sorted([(-p, tool) for tool, p in tools.items()
194 194 if tool not in disabled])
195 195 uimerge = ui.config("ui", "merge")
196 196 if uimerge:
197 197 # external tools defined in uimerge won't be able to handle
198 198 # change/delete conflicts
199 199 if uimerge not in names and not changedelete:
200 200 return (uimerge, uimerge)
201 201 tools.insert(0, (None, uimerge)) # highest priority
202 202 tools.append((None, "hgmerge")) # the old default, if found
203 203 for p, t in tools:
204 204 if check(t, None, symlink, binary, changedelete):
205 205 toolpath = _findtool(ui, t)
206 206 return (t, util.shellquote(toolpath))
207 207
208 208 # internal merge or prompt as last resort
209 209 if symlink or binary or changedelete:
210 210 if not changedelete and len(tools):
211 211 # any tool is rejected by capability for symlink or binary
212 212 ui.warn(_("no tool found to merge %s\n") % path)
213 213 return ":prompt", None
214 214 return ":merge", None
215 215
216 216 def _eoltype(data):
217 217 "Guess the EOL type of a file"
218 218 if '\0' in data: # binary
219 219 return None
220 220 if '\r\n' in data: # Windows
221 221 return '\r\n'
222 222 if '\r' in data: # Old Mac
223 223 return '\r'
224 224 if '\n' in data: # UNIX
225 225 return '\n'
226 226 return None # unknown
227 227
228 228 def _matcheol(file, back):
229 229 "Convert EOL markers in a file to match origfile"
230 230 tostyle = _eoltype(back.data()) # No repo.wread filters?
231 231 if tostyle:
232 232 data = util.readfile(file)
233 233 style = _eoltype(data)
234 234 if style:
235 235 newdata = data.replace(style, tostyle)
236 236 if newdata != data:
237 237 util.writefile(file, newdata)
238 238
239 239 @internaltool('prompt', nomerge)
240 240 def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
241 241 """Asks the user which of the local `p1()` or the other `p2()` version to
242 242 keep as the merged version."""
243 243 ui = repo.ui
244 244 fd = fcd.path()
245 245
246 246 # Avoid prompting during an in-memory merge since it doesn't support merge
247 247 # conflicts.
248 248 if fcd.changectx().isinmemory():
249 249 raise error.InMemoryMergeConflictsError('in-memory merge does not '
250 250 'support file conflicts')
251 251
252 252 prompts = partextras(labels)
253 253 prompts['fd'] = fd
254 254 try:
255 255 if fco.isabsent():
256 256 index = ui.promptchoice(
257 257 _localchangedotherdeletedmsg % prompts, 2)
258 258 choice = ['local', 'other', 'unresolved'][index]
259 259 elif fcd.isabsent():
260 260 index = ui.promptchoice(
261 261 _otherchangedlocaldeletedmsg % prompts, 2)
262 262 choice = ['other', 'local', 'unresolved'][index]
263 263 else:
264 264 index = ui.promptchoice(
265 265 _("keep (l)ocal%(l)s, take (o)ther%(o)s, or leave (u)nresolved"
266 266 " for %(fd)s?"
267 267 "$$ &Local $$ &Other $$ &Unresolved") % prompts, 2)
268 268 choice = ['local', 'other', 'unresolved'][index]
269 269
270 270 if choice == 'other':
271 271 return _iother(repo, mynode, orig, fcd, fco, fca, toolconf,
272 272 labels)
273 273 elif choice == 'local':
274 274 return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf,
275 275 labels)
276 276 elif choice == 'unresolved':
277 277 return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf,
278 278 labels)
279 279 except error.ResponseExpected:
280 280 ui.write("\n")
281 281 return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf,
282 282 labels)
283 283
284 284 @internaltool('local', nomerge)
285 285 def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
286 286 """Uses the local `p1()` version of files as the merged version."""
287 287 return 0, fcd.isabsent()
288 288
289 289 @internaltool('other', nomerge)
290 290 def _iother(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
291 291 """Uses the other `p2()` version of files as the merged version."""
292 292 if fco.isabsent():
293 293 # local changed, remote deleted -- 'deleted' picked
294 294 _underlyingfctxifabsent(fcd).remove()
295 295 deleted = True
296 296 else:
297 297 _underlyingfctxifabsent(fcd).write(fco.data(), fco.flags())
298 298 deleted = False
299 299 return 0, deleted
300 300
301 301 @internaltool('fail', nomerge)
302 302 def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
303 303 """
304 304 Rather than attempting to merge files that were modified on both
305 305 branches, it marks them as unresolved. The resolve command must be
306 306 used to resolve these conflicts."""
307 307 # for change/delete conflicts write out the changed version, then fail
308 308 if fcd.isabsent():
309 309 _underlyingfctxifabsent(fcd).write(fco.data(), fco.flags())
310 310 return 1, False
311 311
312 312 def _underlyingfctxifabsent(filectx):
313 313 """Sometimes when resolving, our fcd is actually an absentfilectx, but
314 314 we want to write to it (to do the resolve). This helper returns the
315 315 underyling workingfilectx in that case.
316 316 """
317 317 if filectx.isabsent():
318 318 return filectx.changectx()[filectx.path()]
319 319 else:
320 320 return filectx
321 321
322 322 def _premerge(repo, fcd, fco, fca, toolconf, files, labels=None):
323 323 tool, toolpath, binary, symlink = toolconf
324 324 if symlink or fcd.isabsent() or fco.isabsent():
325 325 return 1
326 326 unused, unused, unused, back = files
327 327
328 328 ui = repo.ui
329 329
330 330 validkeep = ['keep', 'keep-merge3']
331 331
332 332 # do we attempt to simplemerge first?
333 333 try:
334 334 premerge = _toolbool(ui, tool, "premerge", not binary)
335 335 except error.ConfigError:
336 336 premerge = _toolstr(ui, tool, "premerge", "").lower()
337 337 if premerge not in validkeep:
338 338 _valid = ', '.join(["'" + v + "'" for v in validkeep])
339 339 raise error.ConfigError(_("%s.premerge not valid "
340 340 "('%s' is neither boolean nor %s)") %
341 341 (tool, premerge, _valid))
342 342
343 343 if premerge:
344 344 if premerge == 'keep-merge3':
345 345 if not labels:
346 346 labels = _defaultconflictlabels
347 347 if len(labels) < 3:
348 348 labels.append('base')
349 349 r = simplemerge.simplemerge(ui, fcd, fca, fco, quiet=True, label=labels)
350 350 if not r:
351 351 ui.debug(" premerge successful\n")
352 352 return 0
353 353 if premerge not in validkeep:
354 354 # restore from backup and try again
355 355 _restorebackup(fcd, back)
356 356 return 1 # continue merging
357 357
358 358 def _mergecheck(repo, mynode, orig, fcd, fco, fca, toolconf):
359 359 tool, toolpath, binary, symlink = toolconf
360 360 if symlink:
361 361 repo.ui.warn(_('warning: internal %s cannot merge symlinks '
362 362 'for %s\n') % (tool, fcd.path()))
363 363 return False
364 364 if fcd.isabsent() or fco.isabsent():
365 365 repo.ui.warn(_('warning: internal %s cannot merge change/delete '
366 366 'conflict for %s\n') % (tool, fcd.path()))
367 367 return False
368 368 return True
369 369
370 370 def _merge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, mode):
371 371 """
372 372 Uses the internal non-interactive simple merge algorithm for merging
373 373 files. It will fail if there are any conflicts and leave markers in
374 374 the partially merged file. Markers will have two sections, one for each side
375 375 of merge, unless mode equals 'union' which suppresses the markers."""
376 376 ui = repo.ui
377 377
378 378 r = simplemerge.simplemerge(ui, fcd, fca, fco, label=labels, mode=mode)
379 379 return True, r, False
380 380
381 381 @internaltool('union', fullmerge,
382 382 _("warning: conflicts while merging %s! "
383 383 "(edit, then use 'hg resolve --mark')\n"),
384 384 precheck=_mergecheck)
385 385 def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
386 386 """
387 387 Uses the internal non-interactive simple merge algorithm for merging
388 388 files. It will use both left and right sides for conflict regions.
389 389 No markers are inserted."""
390 390 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
391 391 files, labels, 'union')
392 392
393 393 @internaltool('merge', fullmerge,
394 394 _("warning: conflicts while merging %s! "
395 395 "(edit, then use 'hg resolve --mark')\n"),
396 396 precheck=_mergecheck)
397 397 def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
398 398 """
399 399 Uses the internal non-interactive simple merge algorithm for merging
400 400 files. It will fail if there are any conflicts and leave markers in
401 401 the partially merged file. Markers will have two sections, one for each side
402 402 of merge."""
403 403 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
404 404 files, labels, 'merge')
405 405
406 406 @internaltool('merge3', fullmerge,
407 407 _("warning: conflicts while merging %s! "
408 408 "(edit, then use 'hg resolve --mark')\n"),
409 409 precheck=_mergecheck)
410 410 def _imerge3(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
411 411 """
412 412 Uses the internal non-interactive simple merge algorithm for merging
413 413 files. It will fail if there are any conflicts and leave markers in
414 414 the partially merged file. Marker will have three sections, one from each
415 415 side of the merge and one for the base content."""
416 416 if not labels:
417 417 labels = _defaultconflictlabels
418 418 if len(labels) < 3:
419 419 labels.append('base')
420 420 return _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels)
421 421
422 422 def _imergeauto(repo, mynode, orig, fcd, fco, fca, toolconf, files,
423 423 labels=None, localorother=None):
424 424 """
425 425 Generic driver for _imergelocal and _imergeother
426 426 """
427 427 assert localorother is not None
428 428 tool, toolpath, binary, symlink = toolconf
429 429 r = simplemerge.simplemerge(repo.ui, fcd, fca, fco, label=labels,
430 430 localorother=localorother)
431 431 return True, r
432 432
433 433 @internaltool('merge-local', mergeonly, precheck=_mergecheck)
434 434 def _imergelocal(*args, **kwargs):
435 435 """
436 436 Like :merge, but resolve all conflicts non-interactively in favor
437 437 of the local `p1()` changes."""
438 438 success, status = _imergeauto(localorother='local', *args, **kwargs)
439 439 return success, status, False
440 440
441 441 @internaltool('merge-other', mergeonly, precheck=_mergecheck)
442 442 def _imergeother(*args, **kwargs):
443 443 """
444 444 Like :merge, but resolve all conflicts non-interactively in favor
445 445 of the other `p2()` changes."""
446 446 success, status = _imergeauto(localorother='other', *args, **kwargs)
447 447 return success, status, False
448 448
449 449 @internaltool('tagmerge', mergeonly,
450 450 _("automatic tag merging of %s failed! "
451 451 "(use 'hg resolve --tool :merge' or another merge "
452 452 "tool of your choice)\n"))
453 453 def _itagmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
454 454 """
455 455 Uses the internal tag merge algorithm (experimental).
456 456 """
457 457 success, status = tagmerge.merge(repo, fcd, fco, fca)
458 458 return success, status, False
459 459
460 460 @internaltool('dump', fullmerge)
461 461 def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
462 462 """
463 463 Creates three versions of the files to merge, containing the
464 464 contents of local, other and base. These files can then be used to
465 465 perform a merge manually. If the file to be merged is named
466 466 ``a.txt``, these files will accordingly be named ``a.txt.local``,
467 467 ``a.txt.other`` and ``a.txt.base`` and they will be placed in the
468 468 same directory as ``a.txt``.
469 469
470 470 This implies premerge. Therefore, files aren't dumped, if premerge
471 471 runs successfully. Use :forcedump to forcibly write files out.
472 472 """
473 473 a = _workingpath(repo, fcd)
474 474 fd = fcd.path()
475 475
476 476 from . import context
477 477 if isinstance(fcd, context.overlayworkingfilectx):
478 478 raise error.InMemoryMergeConflictsError('in-memory merge does not '
479 479 'support the :dump tool.')
480 480
481 481 util.writefile(a + ".local", fcd.decodeddata())
482 482 repo.wwrite(fd + ".other", fco.data(), fco.flags())
483 483 repo.wwrite(fd + ".base", fca.data(), fca.flags())
484 484 return False, 1, False
485 485
486 486 @internaltool('forcedump', mergeonly)
487 487 def _forcedump(repo, mynode, orig, fcd, fco, fca, toolconf, files,
488 488 labels=None):
489 489 """
490 490 Creates three versions of the files as same as :dump, but omits premerge.
491 491 """
492 492 return _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files,
493 493 labels=labels)
494 494
495 495 def _xmergeimm(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
496 496 # In-memory merge simply raises an exception on all external merge tools,
497 497 # for now.
498 498 #
499 499 # It would be possible to run most tools with temporary files, but this
500 500 # raises the question of what to do if the user only partially resolves the
501 501 # file -- we can't leave a merge state. (Copy to somewhere in the .hg/
502 502 # directory and tell the user how to get it is my best idea, but it's
503 503 # clunky.)
504 504 raise error.InMemoryMergeConflictsError('in-memory merge does not support '
505 505 'external merge tools')
506 506
507 507 def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
508 508 tool, toolpath, binary, symlink = toolconf
509 509 if fcd.isabsent() or fco.isabsent():
510 510 repo.ui.warn(_('warning: %s cannot merge change/delete conflict '
511 511 'for %s\n') % (tool, fcd.path()))
512 512 return False, 1, None
513 513 unused, unused, unused, back = files
514 514 localpath = _workingpath(repo, fcd)
515 with _maketempfiles(repo, fco, fca) as temppaths:
516 basepath, otherpath = temppaths
515 args = _toolstr(repo.ui, tool, "args")
516
517 with _maketempfiles(repo, fco, fca, repo.wvfs.join(back.path()),
518 "$output" in args) as temppaths:
519 basepath, otherpath, localoutputpath = temppaths
517 520 outpath = ""
518 521 mylabel, otherlabel = labels[:2]
519 522 if len(labels) >= 3:
520 523 baselabel = labels[2]
521 524 else:
522 525 baselabel = 'base'
523 526 env = {'HG_FILE': fcd.path(),
524 527 'HG_MY_NODE': short(mynode),
525 528 'HG_OTHER_NODE': short(fco.changectx().node()),
526 529 'HG_BASE_NODE': short(fca.changectx().node()),
527 530 'HG_MY_ISLINK': 'l' in fcd.flags(),
528 531 'HG_OTHER_ISLINK': 'l' in fco.flags(),
529 532 'HG_BASE_ISLINK': 'l' in fca.flags(),
530 533 'HG_MY_LABEL': mylabel,
531 534 'HG_OTHER_LABEL': otherlabel,
532 535 'HG_BASE_LABEL': baselabel,
533 536 }
534 537 ui = repo.ui
535 538
536 args = _toolstr(ui, tool, "args")
537 539 if "$output" in args:
538 540 # read input from backup, write to original
539 541 outpath = localpath
540 localpath = repo.wvfs.join(back.path())
542 localpath = localoutputpath
541 543 replace = {'local': localpath, 'base': basepath, 'other': otherpath,
542 544 'output': outpath, 'labellocal': mylabel,
543 545 'labelother': otherlabel, 'labelbase': baselabel}
544 546 args = util.interpolate(br'\$', replace, args,
545 547 lambda s: util.shellquote(util.localpath(s)))
546 548 cmd = toolpath + ' ' + args
547 549 if _toolbool(ui, tool, "gui"):
548 550 repo.ui.status(_('running merge tool %s for file %s\n') %
549 551 (tool, fcd.path()))
550 552 repo.ui.debug('launching merge tool: %s\n' % cmd)
551 553 r = ui.system(cmd, cwd=repo.root, environ=env, blockedtag='mergetool')
552 554 repo.ui.debug('merge tool returned: %d\n' % r)
553 555 return True, r, False
554 556
555 557 def _formatconflictmarker(ctx, template, label, pad):
556 558 """Applies the given template to the ctx, prefixed by the label.
557 559
558 560 Pad is the minimum width of the label prefix, so that multiple markers
559 561 can have aligned templated parts.
560 562 """
561 563 if ctx.node() is None:
562 564 ctx = ctx.p1()
563 565
564 566 props = {'ctx': ctx}
565 567 templateresult = template.renderdefault(props)
566 568
567 569 label = ('%s:' % label).ljust(pad + 1)
568 570 mark = '%s %s' % (label, templateresult)
569 571
570 572 if mark:
571 573 mark = mark.splitlines()[0] # split for safety
572 574
573 575 # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
574 576 return util.ellipsis(mark, 80 - 8)
575 577
576 578 _defaultconflictlabels = ['local', 'other']
577 579
578 580 def _formatlabels(repo, fcd, fco, fca, labels, tool=None):
579 581 """Formats the given labels using the conflict marker template.
580 582
581 583 Returns a list of formatted labels.
582 584 """
583 585 cd = fcd.changectx()
584 586 co = fco.changectx()
585 587 ca = fca.changectx()
586 588
587 589 ui = repo.ui
588 590 template = ui.config('ui', 'mergemarkertemplate')
589 591 if tool is not None:
590 592 template = _toolstr(ui, tool, 'mergemarkertemplate', template)
591 593 template = templater.unquotestring(template)
592 594 tres = formatter.templateresources(ui, repo)
593 595 tmpl = formatter.maketemplater(ui, template, defaults=templatekw.keywords,
594 596 resources=tres)
595 597
596 598 pad = max(len(l) for l in labels)
597 599
598 600 newlabels = [_formatconflictmarker(cd, tmpl, labels[0], pad),
599 601 _formatconflictmarker(co, tmpl, labels[1], pad)]
600 602 if len(labels) > 2:
601 603 newlabels.append(_formatconflictmarker(ca, tmpl, labels[2], pad))
602 604 return newlabels
603 605
604 606 def partextras(labels):
605 607 """Return a dictionary of extra labels for use in prompts to the user
606 608
607 609 Intended use is in strings of the form "(l)ocal%(l)s".
608 610 """
609 611 if labels is None:
610 612 return {
611 613 "l": "",
612 614 "o": "",
613 615 }
614 616
615 617 return {
616 618 "l": " [%s]" % labels[0],
617 619 "o": " [%s]" % labels[1],
618 620 }
619 621
620 622 def _restorebackup(fcd, back):
621 623 # TODO: Add a workingfilectx.write(otherfilectx) path so we can use
622 624 # util.copy here instead.
623 625 fcd.write(back.data(), fcd.flags())
624 626
625 627 def _makebackup(repo, ui, wctx, fcd, premerge):
626 628 """Makes and returns a filectx-like object for ``fcd``'s backup file.
627 629
628 630 In addition to preserving the user's pre-existing modifications to `fcd`
629 631 (if any), the backup is used to undo certain premerges, confirm whether a
630 632 merge changed anything, and determine what line endings the new file should
631 633 have.
632 634
633 635 Backups only need to be written once (right before the premerge) since their
634 636 content doesn't change afterwards.
635 637 """
636 638 if fcd.isabsent():
637 639 return None
638 640 # TODO: Break this import cycle somehow. (filectx -> ctx -> fileset ->
639 641 # merge -> filemerge). (I suspect the fileset import is the weakest link)
640 642 from . import context
641 643 a = _workingpath(repo, fcd)
642 644 back = scmutil.origpath(ui, repo, a)
643 645 inworkingdir = (back.startswith(repo.wvfs.base) and not
644 646 back.startswith(repo.vfs.base))
645 647 if isinstance(fcd, context.overlayworkingfilectx) and inworkingdir:
646 648 # If the backup file is to be in the working directory, and we're
647 649 # merging in-memory, we must redirect the backup to the memory context
648 650 # so we don't disturb the working directory.
649 651 relpath = back[len(repo.wvfs.base) + 1:]
650 652 if premerge:
651 653 wctx[relpath].write(fcd.data(), fcd.flags())
652 654 return wctx[relpath]
653 655 else:
654 656 if premerge:
655 657 # Otherwise, write to wherever path the user specified the backups
656 658 # should go. We still need to switch based on whether the source is
657 659 # in-memory so we can use the fast path of ``util.copy`` if both are
658 660 # on disk.
659 661 if isinstance(fcd, context.overlayworkingfilectx):
660 662 util.writefile(back, fcd.data())
661 663 else:
662 664 util.copyfile(a, back)
663 665 # A arbitraryfilectx is returned, so we can run the same functions on
664 666 # the backup context regardless of where it lives.
665 667 return context.arbitraryfilectx(back, repo=repo)
666 668
667 669 @contextlib.contextmanager
668 def _maketempfiles(repo, fco, fca):
669 """Writes out `fco` and `fca` as temporary files, so an external merge
670 tool may use them.
670 def _maketempfiles(repo, fco, fca, localpath, uselocalpath):
671 """Writes out `fco` and `fca` as temporary files, and (if uselocalpath)
672 copies `localpath` to another temporary file, so an external merge tool may
673 use them.
671 674 """
672 675 tmproot = None
673 676 tmprootprefix = repo.ui.config('experimental', 'mergetempdirprefix')
674 677 if tmprootprefix:
675 678 tmproot = tempfile.mkdtemp(prefix=tmprootprefix)
676 679
677 def temp(prefix, ctx):
678 fullbase, ext = os.path.splitext(ctx.path())
680 def maketempfrompath(prefix, path):
681 fullbase, ext = os.path.splitext(path)
679 682 pre = "%s~%s" % (os.path.basename(fullbase), prefix)
680 683 if tmproot:
681 684 name = os.path.join(tmproot, pre)
682 685 if ext:
683 686 name += ext
684 687 f = open(name, r"wb")
685 688 else:
686 (fd, name) = tempfile.mkstemp(prefix=pre + '.', suffix=ext)
689 fd, name = tempfile.mkstemp(prefix=pre + '.', suffix=ext)
687 690 f = os.fdopen(fd, r"wb")
691 return f, name
692
693 def tempfromcontext(prefix, ctx):
694 f, name = maketempfrompath(prefix, ctx.path())
688 695 data = repo.wwritedata(ctx.path(), ctx.data())
689 696 f.write(data)
690 697 f.close()
691 698 return name
692 699
693 b = temp("base", fca)
694 c = temp("other", fco)
700 b = tempfromcontext("base", fca)
701 c = tempfromcontext("other", fco)
702 d = localpath
703 if uselocalpath:
704 # We start off with this being the backup filename, so remove the .orig
705 # to make syntax-highlighting more likely.
706 if d.endswith('.orig'):
707 d, _ = os.path.splitext(d)
708 f, d = maketempfrompath("local", d)
709 with open(localpath, 'rb') as src:
710 f.write(src.read())
711 f.close()
712
695 713 try:
696 yield b, c
714 yield b, c, d
697 715 finally:
698 716 if tmproot:
699 717 shutil.rmtree(tmproot)
700 718 else:
701 719 util.unlink(b)
702 720 util.unlink(c)
721 # if not uselocalpath, d is the 'orig'/backup file which we
722 # shouldn't delete.
723 if d and uselocalpath:
724 util.unlink(d)
703 725
704 726 def _filemerge(premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=None):
705 727 """perform a 3-way merge in the working directory
706 728
707 729 premerge = whether this is a premerge
708 730 mynode = parent node before merge
709 731 orig = original local filename before merge
710 732 fco = other file context
711 733 fca = ancestor file context
712 734 fcd = local file context for current/destination file
713 735
714 736 Returns whether the merge is complete, the return value of the merge, and
715 737 a boolean indicating whether the file was deleted from disk."""
716 738
717 739 if not fco.cmp(fcd): # files identical?
718 740 return True, None, False
719 741
720 742 ui = repo.ui
721 743 fd = fcd.path()
722 744 binary = fcd.isbinary() or fco.isbinary() or fca.isbinary()
723 745 symlink = 'l' in fcd.flags() + fco.flags()
724 746 changedelete = fcd.isabsent() or fco.isabsent()
725 747 tool, toolpath = _picktool(repo, ui, fd, binary, symlink, changedelete)
726 748 if tool in internals and tool.startswith('internal:'):
727 749 # normalize to new-style names (':merge' etc)
728 750 tool = tool[len('internal'):]
729 751 ui.debug("picked tool '%s' for %s (binary %s symlink %s changedelete %s)\n"
730 752 % (tool, fd, pycompat.bytestr(binary), pycompat.bytestr(symlink),
731 753 pycompat.bytestr(changedelete)))
732 754
733 755 if tool in internals:
734 756 func = internals[tool]
735 757 mergetype = func.mergetype
736 758 onfailure = func.onfailure
737 759 precheck = func.precheck
738 760 isexternal = False
739 761 else:
740 762 if wctx.isinmemory():
741 763 func = _xmergeimm
742 764 else:
743 765 func = _xmerge
744 766 mergetype = fullmerge
745 767 onfailure = _("merging %s failed!\n")
746 768 precheck = None
747 769 isexternal = True
748 770
749 771 toolconf = tool, toolpath, binary, symlink
750 772
751 773 if mergetype == nomerge:
752 774 r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf, labels)
753 775 return True, r, deleted
754 776
755 777 if premerge:
756 778 if orig != fco.path():
757 779 ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
758 780 else:
759 781 ui.status(_("merging %s\n") % fd)
760 782
761 783 ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca))
762 784
763 785 if precheck and not precheck(repo, mynode, orig, fcd, fco, fca,
764 786 toolconf):
765 787 if onfailure:
766 788 if wctx.isinmemory():
767 789 raise error.InMemoryMergeConflictsError('in-memory merge does '
768 790 'not support merge '
769 791 'conflicts')
770 792 ui.warn(onfailure % fd)
771 793 return True, 1, False
772 794
773 795 back = _makebackup(repo, ui, wctx, fcd, premerge)
774 796 files = (None, None, None, back)
775 797 r = 1
776 798 try:
777 799 internalmarkerstyle = ui.config('ui', 'mergemarkers')
778 800 if isexternal:
779 801 markerstyle = _toolstr(ui, tool, 'mergemarkers')
780 802 else:
781 803 markerstyle = internalmarkerstyle
782 804
783 805 if not labels:
784 806 labels = _defaultconflictlabels
785 807 formattedlabels = labels
786 808 if markerstyle != 'basic':
787 809 formattedlabels = _formatlabels(repo, fcd, fco, fca, labels,
788 810 tool=tool)
789 811
790 812 if premerge and mergetype == fullmerge:
791 813 # conflict markers generated by premerge will use 'detailed'
792 814 # settings if either ui.mergemarkers or the tool's mergemarkers
793 815 # setting is 'detailed'. This way tools can have basic labels in
794 816 # space-constrained areas of the UI, but still get full information
795 817 # in conflict markers if premerge is 'keep' or 'keep-merge3'.
796 818 premergelabels = labels
797 819 labeltool = None
798 820 if markerstyle != 'basic':
799 821 # respect 'tool's mergemarkertemplate (which defaults to
800 822 # ui.mergemarkertemplate)
801 823 labeltool = tool
802 824 if internalmarkerstyle != 'basic' or markerstyle != 'basic':
803 825 premergelabels = _formatlabels(repo, fcd, fco, fca,
804 826 premergelabels, tool=labeltool)
805 827
806 828 r = _premerge(repo, fcd, fco, fca, toolconf, files,
807 829 labels=premergelabels)
808 830 # complete if premerge successful (r is 0)
809 831 return not r, r, False
810 832
811 833 needcheck, r, deleted = func(repo, mynode, orig, fcd, fco, fca,
812 834 toolconf, files, labels=formattedlabels)
813 835
814 836 if needcheck:
815 837 r = _check(repo, r, ui, tool, fcd, files)
816 838
817 839 if r:
818 840 if onfailure:
819 841 if wctx.isinmemory():
820 842 raise error.InMemoryMergeConflictsError('in-memory merge '
821 843 'does not support '
822 844 'merge conflicts')
823 845 ui.warn(onfailure % fd)
824 846 _onfilemergefailure(ui)
825 847
826 848 return True, r, deleted
827 849 finally:
828 850 if not r and back is not None:
829 851 back.remove()
830 852
831 853 def _haltmerge():
832 854 msg = _('merge halted after failed merge (see hg resolve)')
833 855 raise error.InterventionRequired(msg)
834 856
835 857 def _onfilemergefailure(ui):
836 858 action = ui.config('merge', 'on-failure')
837 859 if action == 'prompt':
838 860 msg = _('continue merge operation (yn)?' '$$ &Yes $$ &No')
839 861 if ui.promptchoice(msg, 0) == 1:
840 862 _haltmerge()
841 863 if action == 'halt':
842 864 _haltmerge()
843 865 # default action is 'continue', in which case we neither prompt nor halt
844 866
845 867 def _check(repo, r, ui, tool, fcd, files):
846 868 fd = fcd.path()
847 869 unused, unused, unused, back = files
848 870
849 871 if not r and (_toolbool(ui, tool, "checkconflicts") or
850 872 'conflicts' in _toollist(ui, tool, "check")):
851 873 if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(),
852 874 re.MULTILINE):
853 875 r = 1
854 876
855 877 checked = False
856 878 if 'prompt' in _toollist(ui, tool, "check"):
857 879 checked = True
858 880 if ui.promptchoice(_("was merge of '%s' successful (yn)?"
859 881 "$$ &Yes $$ &No") % fd, 1):
860 882 r = 1
861 883
862 884 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
863 885 'changed' in
864 886 _toollist(ui, tool, "check")):
865 887 if back is not None and not fcd.cmp(back):
866 888 if ui.promptchoice(_(" output file %s appears unchanged\n"
867 889 "was merge successful (yn)?"
868 890 "$$ &Yes $$ &No") % fd, 1):
869 891 r = 1
870 892
871 893 if back is not None and _toolbool(ui, tool, "fixeol"):
872 894 _matcheol(_workingpath(repo, fcd), back)
873 895
874 896 return r
875 897
876 898 def _workingpath(repo, ctx):
877 899 return repo.wjoin(ctx.path())
878 900
879 901 def premerge(repo, wctx, mynode, orig, fcd, fco, fca, labels=None):
880 902 return _filemerge(True, repo, wctx, mynode, orig, fcd, fco, fca,
881 903 labels=labels)
882 904
883 905 def filemerge(repo, wctx, mynode, orig, fcd, fco, fca, labels=None):
884 906 return _filemerge(False, repo, wctx, mynode, orig, fcd, fco, fca,
885 907 labels=labels)
886 908
887 909 def loadinternalmerge(ui, extname, registrarobj):
888 910 """Load internal merge tool from specified registrarobj
889 911 """
890 912 for name, func in registrarobj._table.iteritems():
891 913 fullname = ':' + name
892 914 internals[fullname] = func
893 915 internals['internal:' + name] = func
894 916 internalsdoc[fullname] = func
895 917
896 918 # load built-in merge tools explicitly to setup internalsdoc
897 919 loadinternalmerge(None, None, internaltool)
898 920
899 921 # tell hggettext to extract docstrings from these functions:
900 922 i18nfunctions = internals.values()
@@ -1,1670 +1,1670 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 merge --abort' 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 merge --abort' 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 merge --abort' 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 merge --abort' 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 merge --abort' 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 merge --abort' 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 merge --abort' 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 merge --abort' 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 merge --abort' 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 merge --abort' 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 395 couldn't find merge tool true (for pattern f)
396 396 merging f
397 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 merge --abort' 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 421 couldn't find merge tool true (for pattern f)
422 422 merging f
423 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 merge --abort' 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 merge --abort' 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 merge --abort' 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 merge --abort' 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 merge --abort' 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 merge --abort' 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 merge --abort' 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 check that internal:dump doesn't dump files if premerge runs
675 675 successfully
676 676
677 677 $ beforemerge
678 678 [merge-tools]
679 679 false.whatever=
680 680 true.priority=1
681 681 true.executable=cat
682 682 # hg update -C 1
683 683 $ hg merge -r 3 --config ui.merge=internal:dump
684 684 merging f
685 685 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
686 686 (branch merge, don't forget to commit)
687 687
688 688 $ aftermerge
689 689 # cat f
690 690 revision 1
691 691 space
692 692 revision 3
693 693 # hg stat
694 694 M f
695 695 # hg resolve --list
696 696 R f
697 697
698 698 check that internal:forcedump dumps files, even if local and other can
699 699 be merged easily
700 700
701 701 $ beforemerge
702 702 [merge-tools]
703 703 false.whatever=
704 704 true.priority=1
705 705 true.executable=cat
706 706 # hg update -C 1
707 707 $ hg merge -r 3 --config ui.merge=internal:forcedump
708 708 merging f
709 709 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
710 710 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
711 711 [1]
712 712 $ aftermerge
713 713 # cat f
714 714 revision 1
715 715 space
716 716 # hg stat
717 717 M f
718 718 ? f.base
719 719 ? f.local
720 720 ? f.orig
721 721 ? f.other
722 722 # hg resolve --list
723 723 U f
724 724
725 725 $ cat f.base
726 726 revision 0
727 727 space
728 728
729 729 $ cat f.local
730 730 revision 1
731 731 space
732 732
733 733 $ cat f.other
734 734 revision 0
735 735 space
736 736 revision 3
737 737
738 738 $ rm -f f.base f.local f.other
739 739
740 740 ui.merge specifies internal:other but is overruled by pattern for false:
741 741
742 742 $ beforemerge
743 743 [merge-tools]
744 744 false.whatever=
745 745 true.priority=1
746 746 true.executable=cat
747 747 # hg update -C 1
748 748 $ hg merge -r 2 --config ui.merge=internal:other --config merge-patterns.f=false
749 749 merging f
750 750 merging f failed!
751 751 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
752 752 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
753 753 [1]
754 754 $ aftermerge
755 755 # cat f
756 756 revision 1
757 757 space
758 758 # hg stat
759 759 M f
760 760 ? f.orig
761 761 # hg resolve --list
762 762 U f
763 763
764 764 Premerge
765 765
766 766 ui.merge specifies internal:other but is overruled by --tool=false
767 767
768 768 $ beforemerge
769 769 [merge-tools]
770 770 false.whatever=
771 771 true.priority=1
772 772 true.executable=cat
773 773 # hg update -C 1
774 774 $ hg merge -r 2 --config ui.merge=internal:other --tool=false
775 775 merging f
776 776 merging f failed!
777 777 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
778 778 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
779 779 [1]
780 780 $ aftermerge
781 781 # cat f
782 782 revision 1
783 783 space
784 784 # hg stat
785 785 M f
786 786 ? f.orig
787 787 # hg resolve --list
788 788 U f
789 789
790 790 HGMERGE specifies internal:other but is overruled by --tool=false
791 791
792 792 $ HGMERGE=internal:other ; export HGMERGE
793 793 $ beforemerge
794 794 [merge-tools]
795 795 false.whatever=
796 796 true.priority=1
797 797 true.executable=cat
798 798 # hg update -C 1
799 799 $ hg merge -r 2 --tool=false
800 800 merging f
801 801 merging f failed!
802 802 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
803 803 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
804 804 [1]
805 805 $ aftermerge
806 806 # cat f
807 807 revision 1
808 808 space
809 809 # hg stat
810 810 M f
811 811 ? f.orig
812 812 # hg resolve --list
813 813 U f
814 814
815 815 $ unset HGMERGE # make sure HGMERGE doesn't interfere with remaining tests
816 816
817 817 update is a merge ...
818 818
819 819 (this also tests that files reverted with '--rev REV' are treated as
820 820 "modified", even if none of mode, size and timestamp of them isn't
821 821 changed on the filesystem (see also issue4583))
822 822
823 823 $ cat >> $HGRCPATH <<EOF
824 824 > [fakedirstatewritetime]
825 825 > # emulate invoking dirstate.write() via repo.status()
826 826 > # at 2000-01-01 00:00
827 827 > fakenow = 200001010000
828 828 > EOF
829 829
830 830 $ beforemerge
831 831 [merge-tools]
832 832 false.whatever=
833 833 true.priority=1
834 834 true.executable=cat
835 835 # hg update -C 1
836 836 $ hg update -q 0
837 837 $ f -s f
838 838 f: size=17
839 839 $ touch -t 200001010000 f
840 840 $ hg debugrebuildstate
841 841 $ cat >> $HGRCPATH <<EOF
842 842 > [extensions]
843 843 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
844 844 > EOF
845 845 $ hg revert -q -r 1 .
846 846 $ cat >> $HGRCPATH <<EOF
847 847 > [extensions]
848 848 > fakedirstatewritetime = !
849 849 > EOF
850 850 $ f -s f
851 851 f: size=17
852 852 $ touch -t 200001010000 f
853 853 $ hg status f
854 854 M f
855 855 $ hg update -r 2
856 856 merging f
857 857 revision 1
858 858 space
859 859 revision 0
860 860 space
861 861 revision 2
862 862 space
863 863 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
864 864 $ aftermerge
865 865 # cat f
866 866 revision 1
867 867 space
868 868 # hg stat
869 869 M f
870 870 # hg resolve --list
871 871 R f
872 872
873 873 update should also have --tool
874 874
875 875 $ beforemerge
876 876 [merge-tools]
877 877 false.whatever=
878 878 true.priority=1
879 879 true.executable=cat
880 880 # hg update -C 1
881 881 $ hg update -q 0
882 882 $ f -s f
883 883 f: size=17
884 884 $ touch -t 200001010000 f
885 885 $ hg debugrebuildstate
886 886 $ cat >> $HGRCPATH <<EOF
887 887 > [extensions]
888 888 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
889 889 > EOF
890 890 $ hg revert -q -r 1 .
891 891 $ cat >> $HGRCPATH <<EOF
892 892 > [extensions]
893 893 > fakedirstatewritetime = !
894 894 > EOF
895 895 $ f -s f
896 896 f: size=17
897 897 $ touch -t 200001010000 f
898 898 $ hg status f
899 899 M f
900 900 $ hg update -r 2 --tool false
901 901 merging f
902 902 merging f failed!
903 903 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
904 904 use 'hg resolve' to retry unresolved file merges
905 905 [1]
906 906 $ aftermerge
907 907 # cat f
908 908 revision 1
909 909 space
910 910 # hg stat
911 911 M f
912 912 ? f.orig
913 913 # hg resolve --list
914 914 U f
915 915
916 916 Default is silent simplemerge:
917 917
918 918 $ beforemerge
919 919 [merge-tools]
920 920 false.whatever=
921 921 true.priority=1
922 922 true.executable=cat
923 923 # hg update -C 1
924 924 $ hg merge -r 3
925 925 merging f
926 926 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
927 927 (branch merge, don't forget to commit)
928 928 $ aftermerge
929 929 # cat f
930 930 revision 1
931 931 space
932 932 revision 3
933 933 # hg stat
934 934 M f
935 935 # hg resolve --list
936 936 R f
937 937
938 938 .premerge=True is same:
939 939
940 940 $ beforemerge
941 941 [merge-tools]
942 942 false.whatever=
943 943 true.priority=1
944 944 true.executable=cat
945 945 # hg update -C 1
946 946 $ hg merge -r 3 --config merge-tools.true.premerge=True
947 947 merging f
948 948 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
949 949 (branch merge, don't forget to commit)
950 950 $ aftermerge
951 951 # cat f
952 952 revision 1
953 953 space
954 954 revision 3
955 955 # hg stat
956 956 M f
957 957 # hg resolve --list
958 958 R f
959 959
960 960 .premerge=False executes merge-tool:
961 961
962 962 $ beforemerge
963 963 [merge-tools]
964 964 false.whatever=
965 965 true.priority=1
966 966 true.executable=cat
967 967 # hg update -C 1
968 968 $ hg merge -r 3 --config merge-tools.true.premerge=False
969 969 merging f
970 970 revision 1
971 971 space
972 972 revision 0
973 973 space
974 974 revision 0
975 975 space
976 976 revision 3
977 977 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
978 978 (branch merge, don't forget to commit)
979 979 $ aftermerge
980 980 # cat f
981 981 revision 1
982 982 space
983 983 # hg stat
984 984 M f
985 985 # hg resolve --list
986 986 R f
987 987
988 988 premerge=keep keeps conflict markers in:
989 989
990 990 $ beforemerge
991 991 [merge-tools]
992 992 false.whatever=
993 993 true.priority=1
994 994 true.executable=cat
995 995 # hg update -C 1
996 996 $ hg merge -r 4 --config merge-tools.true.premerge=keep
997 997 merging f
998 998 <<<<<<< working copy: ef83787e2614 - test: revision 1
999 999 revision 1
1000 1000 space
1001 1001 =======
1002 1002 revision 4
1003 1003 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1004 1004 revision 0
1005 1005 space
1006 1006 revision 4
1007 1007 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1008 1008 (branch merge, don't forget to commit)
1009 1009 $ aftermerge
1010 1010 # cat f
1011 1011 <<<<<<< working copy: ef83787e2614 - test: revision 1
1012 1012 revision 1
1013 1013 space
1014 1014 =======
1015 1015 revision 4
1016 1016 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1017 1017 # hg stat
1018 1018 M f
1019 1019 # hg resolve --list
1020 1020 R f
1021 1021
1022 1022 premerge=keep-merge3 keeps conflict markers with base content:
1023 1023
1024 1024 $ beforemerge
1025 1025 [merge-tools]
1026 1026 false.whatever=
1027 1027 true.priority=1
1028 1028 true.executable=cat
1029 1029 # hg update -C 1
1030 1030 $ hg merge -r 4 --config merge-tools.true.premerge=keep-merge3
1031 1031 merging f
1032 1032 <<<<<<< working copy: ef83787e2614 - test: revision 1
1033 1033 revision 1
1034 1034 space
1035 1035 ||||||| base
1036 1036 revision 0
1037 1037 space
1038 1038 =======
1039 1039 revision 4
1040 1040 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1041 1041 revision 0
1042 1042 space
1043 1043 revision 4
1044 1044 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1045 1045 (branch merge, don't forget to commit)
1046 1046 $ aftermerge
1047 1047 # cat f
1048 1048 <<<<<<< working copy: ef83787e2614 - test: revision 1
1049 1049 revision 1
1050 1050 space
1051 1051 ||||||| base
1052 1052 revision 0
1053 1053 space
1054 1054 =======
1055 1055 revision 4
1056 1056 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1057 1057 # hg stat
1058 1058 M f
1059 1059 # hg resolve --list
1060 1060 R f
1061 1061
1062 1062 premerge=keep respects ui.mergemarkers=basic:
1063 1063
1064 1064 $ beforemerge
1065 1065 [merge-tools]
1066 1066 false.whatever=
1067 1067 true.priority=1
1068 1068 true.executable=cat
1069 1069 # hg update -C 1
1070 1070 $ hg merge -r 4 --config merge-tools.true.premerge=keep --config ui.mergemarkers=basic
1071 1071 merging f
1072 1072 <<<<<<< working copy
1073 1073 revision 1
1074 1074 space
1075 1075 =======
1076 1076 revision 4
1077 1077 >>>>>>> merge rev
1078 1078 revision 0
1079 1079 space
1080 1080 revision 4
1081 1081 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1082 1082 (branch merge, don't forget to commit)
1083 1083 $ aftermerge
1084 1084 # cat f
1085 1085 <<<<<<< working copy
1086 1086 revision 1
1087 1087 space
1088 1088 =======
1089 1089 revision 4
1090 1090 >>>>>>> merge rev
1091 1091 # hg stat
1092 1092 M f
1093 1093 # hg resolve --list
1094 1094 R f
1095 1095
1096 1096 premerge=keep ignores ui.mergemarkers=basic if true.mergemarkers=detailed:
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 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1105 1105 > --config ui.mergemarkers=basic \
1106 1106 > --config merge-tools.true.mergemarkers=detailed
1107 1107 merging f
1108 1108 <<<<<<< working copy: ef83787e2614 - test: revision 1
1109 1109 revision 1
1110 1110 space
1111 1111 =======
1112 1112 revision 4
1113 1113 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1114 1114 revision 0
1115 1115 space
1116 1116 revision 4
1117 1117 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1118 1118 (branch merge, don't forget to commit)
1119 1119 $ aftermerge
1120 1120 # cat f
1121 1121 <<<<<<< working copy: ef83787e2614 - test: revision 1
1122 1122 revision 1
1123 1123 space
1124 1124 =======
1125 1125 revision 4
1126 1126 >>>>>>> merge rev: 81448d39c9a0 - test: revision 4
1127 1127 # hg stat
1128 1128 M f
1129 1129 # hg resolve --list
1130 1130 R f
1131 1131
1132 1132 premerge=keep respects ui.mergemarkertemplate instead of
1133 1133 true.mergemarkertemplate if true.mergemarkers=basic:
1134 1134
1135 1135 $ beforemerge
1136 1136 [merge-tools]
1137 1137 false.whatever=
1138 1138 true.priority=1
1139 1139 true.executable=cat
1140 1140 # hg update -C 1
1141 1141 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1142 1142 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1143 1143 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}'
1144 1144 merging f
1145 1145 <<<<<<< working copy: uitmpl 1
1146 1146 revision 1
1147 1147 space
1148 1148 =======
1149 1149 revision 4
1150 1150 >>>>>>> merge rev: uitmpl 4
1151 1151 revision 0
1152 1152 space
1153 1153 revision 4
1154 1154 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1155 1155 (branch merge, don't forget to commit)
1156 1156 $ aftermerge
1157 1157 # cat f
1158 1158 <<<<<<< working copy: uitmpl 1
1159 1159 revision 1
1160 1160 space
1161 1161 =======
1162 1162 revision 4
1163 1163 >>>>>>> merge rev: uitmpl 4
1164 1164 # hg stat
1165 1165 M f
1166 1166 # hg resolve --list
1167 1167 R f
1168 1168
1169 1169 premerge=keep respects true.mergemarkertemplate instead of
1170 1170 true.mergemarkertemplate if true.mergemarkers=detailed:
1171 1171
1172 1172 $ beforemerge
1173 1173 [merge-tools]
1174 1174 false.whatever=
1175 1175 true.priority=1
1176 1176 true.executable=cat
1177 1177 # hg update -C 1
1178 1178 $ hg merge -r 4 --config merge-tools.true.premerge=keep \
1179 1179 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1180 1180 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1181 1181 > --config merge-tools.true.mergemarkers=detailed
1182 1182 merging f
1183 1183 <<<<<<< working copy: tooltmpl ef83787e2614
1184 1184 revision 1
1185 1185 space
1186 1186 =======
1187 1187 revision 4
1188 1188 >>>>>>> merge rev: tooltmpl 81448d39c9a0
1189 1189 revision 0
1190 1190 space
1191 1191 revision 4
1192 1192 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1193 1193 (branch merge, don't forget to commit)
1194 1194 $ aftermerge
1195 1195 # cat f
1196 1196 <<<<<<< working copy: tooltmpl ef83787e2614
1197 1197 revision 1
1198 1198 space
1199 1199 =======
1200 1200 revision 4
1201 1201 >>>>>>> merge rev: tooltmpl 81448d39c9a0
1202 1202 # hg stat
1203 1203 M f
1204 1204 # hg resolve --list
1205 1205 R f
1206 1206
1207 1207 Tool execution
1208 1208
1209 1209 set tools.args explicit to include $base $local $other $output:
1210 1210
1211 1211 $ beforemerge
1212 1212 [merge-tools]
1213 1213 false.whatever=
1214 1214 true.priority=1
1215 1215 true.executable=cat
1216 1216 # hg update -C 1
1217 1217 $ hg merge -r 2 --config merge-tools.true.executable=head --config merge-tools.true.args='$base $local $other $output' \
1218 1218 > | sed 's,==> .* <==,==> ... <==,g'
1219 1219 merging f
1220 1220 ==> ... <==
1221 1221 revision 0
1222 1222 space
1223 1223
1224 1224 ==> ... <==
1225 1225 revision 1
1226 1226 space
1227 1227
1228 1228 ==> ... <==
1229 1229 revision 2
1230 1230 space
1231 1231
1232 1232 ==> ... <==
1233 1233 revision 1
1234 1234 space
1235 1235 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1236 1236 (branch merge, don't forget to commit)
1237 1237 $ aftermerge
1238 1238 # cat f
1239 1239 revision 1
1240 1240 space
1241 1241 # hg stat
1242 1242 M f
1243 1243 # hg resolve --list
1244 1244 R f
1245 1245
1246 1246 Merge with "echo mergeresult > $local":
1247 1247
1248 1248 $ beforemerge
1249 1249 [merge-tools]
1250 1250 false.whatever=
1251 1251 true.priority=1
1252 1252 true.executable=cat
1253 1253 # hg update -C 1
1254 1254 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $local'
1255 1255 merging f
1256 1256 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1257 1257 (branch merge, don't forget to commit)
1258 1258 $ aftermerge
1259 1259 # cat f
1260 1260 mergeresult
1261 1261 # hg stat
1262 1262 M f
1263 1263 # hg resolve --list
1264 1264 R f
1265 1265
1266 1266 - and $local is the file f:
1267 1267
1268 1268 $ beforemerge
1269 1269 [merge-tools]
1270 1270 false.whatever=
1271 1271 true.priority=1
1272 1272 true.executable=cat
1273 1273 # hg update -C 1
1274 1274 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > f'
1275 1275 merging f
1276 1276 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1277 1277 (branch merge, don't forget to commit)
1278 1278 $ aftermerge
1279 1279 # cat f
1280 1280 mergeresult
1281 1281 # hg stat
1282 1282 M f
1283 1283 # hg resolve --list
1284 1284 R f
1285 1285
1286 1286 Merge with "echo mergeresult > $output" - the variable is a bit magic:
1287 1287
1288 1288 $ beforemerge
1289 1289 [merge-tools]
1290 1290 false.whatever=
1291 1291 true.priority=1
1292 1292 true.executable=cat
1293 1293 # hg update -C 1
1294 1294 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $output'
1295 1295 merging f
1296 1296 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1297 1297 (branch merge, don't forget to commit)
1298 1298 $ aftermerge
1299 1299 # cat f
1300 1300 mergeresult
1301 1301 # hg stat
1302 1302 M f
1303 1303 # hg resolve --list
1304 1304 R f
1305 1305
1306 1306 Merge using tool with a path that must be quoted:
1307 1307
1308 1308 $ beforemerge
1309 1309 [merge-tools]
1310 1310 false.whatever=
1311 1311 true.priority=1
1312 1312 true.executable=cat
1313 1313 # hg update -C 1
1314 1314 $ cat <<EOF > 'my merge tool'
1315 1315 > cat "\$1" "\$2" "\$3" > "\$4"
1316 1316 > EOF
1317 1317 $ hg --config merge-tools.true.executable='sh' \
1318 1318 > --config merge-tools.true.args='"./my merge tool" $base $local $other $output' \
1319 1319 > merge -r 2
1320 1320 merging f
1321 1321 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1322 1322 (branch merge, don't forget to commit)
1323 1323 $ rm -f 'my merge tool'
1324 1324 $ aftermerge
1325 1325 # cat f
1326 1326 revision 0
1327 1327 space
1328 1328 revision 1
1329 1329 space
1330 1330 revision 2
1331 1331 space
1332 1332 # hg stat
1333 1333 M f
1334 1334 # hg resolve --list
1335 1335 R f
1336 1336
1337 1337 Merge using a tool that supports labellocal, labelother, and labelbase, checking
1338 1338 that they're quoted properly as well. This is using the default 'basic'
1339 1339 mergemarkers even though ui.mergemarkers is 'detailed', so it's ignoring both
1340 1340 mergemarkertemplate settings:
1341 1341
1342 1342 $ beforemerge
1343 1343 [merge-tools]
1344 1344 false.whatever=
1345 1345 true.priority=1
1346 1346 true.executable=cat
1347 1347 # hg update -C 1
1348 1348 $ cat <<EOF > printargs_merge_tool
1349 1349 > while test \$# -gt 0; do echo arg: \"\$1\"; shift; done
1350 1350 > EOF
1351 1351 $ hg --config merge-tools.true.executable='sh' \
1352 1352 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1353 1353 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1354 1354 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1355 1355 > --config ui.mergemarkers=detailed \
1356 1356 > merge -r 2
1357 1357 merging f
1358 1358 arg: "ll:working copy"
1359 1359 arg: "lo:"
1360 1360 arg: "merge rev"
1361 1361 arg: "lb:base: */f~base.*" (glob)
1362 1362 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1363 1363 (branch merge, don't forget to commit)
1364 1364 $ rm -f 'printargs_merge_tool'
1365 1365
1366 1366 Same test with experimental.mergetempdirprefix set:
1367 1367
1368 1368 $ beforemerge
1369 1369 [merge-tools]
1370 1370 false.whatever=
1371 1371 true.priority=1
1372 1372 true.executable=cat
1373 1373 # hg update -C 1
1374 1374 $ cat <<EOF > printargs_merge_tool
1375 1375 > while test \$# -gt 0; do echo arg: \"\$1\"; shift; done
1376 1376 > EOF
1377 1377 $ hg --config experimental.mergetempdirprefix=$TESTTMP/hgmerge. \
1378 1378 > --config merge-tools.true.executable='sh' \
1379 1379 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1380 1380 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1381 1381 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1382 1382 > --config ui.mergemarkers=detailed \
1383 1383 > merge -r 2
1384 1384 merging f
1385 1385 arg: "ll:working copy"
1386 1386 arg: "lo:"
1387 1387 arg: "merge rev"
1388 1388 arg: "lb:base: */hgmerge.*/f~base" (glob)
1389 1389 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1390 1390 (branch merge, don't forget to commit)
1391 1391 $ rm -f 'printargs_merge_tool'
1392 1392
1393 1393 Merge using a tool that supports labellocal, labelother, and labelbase, checking
1394 1394 that they're quoted properly as well. This is using 'detailed' mergemarkers,
1395 1395 even though ui.mergemarkers is 'basic', and using the tool's
1396 1396 mergemarkertemplate:
1397 1397
1398 1398 $ beforemerge
1399 1399 [merge-tools]
1400 1400 false.whatever=
1401 1401 true.priority=1
1402 1402 true.executable=cat
1403 1403 # hg update -C 1
1404 1404 $ cat <<EOF > printargs_merge_tool
1405 1405 > while test \$# -gt 0; do echo arg: \"\$1\"; shift; done
1406 1406 > EOF
1407 1407 $ hg --config merge-tools.true.executable='sh' \
1408 1408 > --config merge-tools.true.args='./printargs_merge_tool ll:$labellocal lo: $labelother lb:$labelbase": "$base' \
1409 1409 > --config merge-tools.true.mergemarkers=detailed \
1410 1410 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1411 1411 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1412 1412 > --config ui.mergemarkers=basic \
1413 1413 > merge -r 2
1414 1414 merging f
1415 1415 arg: "ll:working copy: tooltmpl ef83787e2614"
1416 1416 arg: "lo:"
1417 1417 arg: "merge rev: tooltmpl 0185f4e0cf02"
1418 1418 arg: "lb:base: */f~base.*" (glob)
1419 1419 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1420 1420 (branch merge, don't forget to commit)
1421 1421 $ rm -f 'printargs_merge_tool'
1422 1422
1423 1423 The merge tool still gets labellocal and labelother as 'basic' even when
1424 1424 premerge=keep is used and has 'detailed' markers:
1425 1425
1426 1426 $ beforemerge
1427 1427 [merge-tools]
1428 1428 false.whatever=
1429 1429 true.priority=1
1430 1430 true.executable=cat
1431 1431 # hg update -C 1
1432 1432 $ cat <<EOF > mytool
1433 1433 > echo labellocal: \"\$1\"
1434 1434 > echo labelother: \"\$2\"
1435 1435 > echo "output (arg)": \"\$3\"
1436 1436 > echo "output (contents)":
1437 1437 > cat "\$3"
1438 1438 > EOF
1439 1439 $ hg --config merge-tools.true.executable='sh' \
1440 1440 > --config merge-tools.true.args='mytool $labellocal $labelother $output' \
1441 1441 > --config merge-tools.true.premerge=keep \
1442 1442 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1443 1443 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1444 1444 > --config ui.mergemarkers=detailed \
1445 1445 > merge -r 2
1446 1446 merging f
1447 1447 labellocal: "working copy"
1448 1448 labelother: "merge rev"
1449 1449 output (arg): "$TESTTMP/f"
1450 1450 output (contents):
1451 1451 <<<<<<< working copy: uitmpl 1
1452 1452 revision 1
1453 1453 =======
1454 1454 revision 2
1455 1455 >>>>>>> merge rev: uitmpl 2
1456 1456 space
1457 1457 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1458 1458 (branch merge, don't forget to commit)
1459 1459 $ rm -f 'mytool'
1460 1460
1461 1461 premerge=keep uses the *tool's* mergemarkertemplate if tool's
1462 1462 mergemarkers=detailed; labellocal and labelother also use the tool's template
1463 1463
1464 1464 $ beforemerge
1465 1465 [merge-tools]
1466 1466 false.whatever=
1467 1467 true.priority=1
1468 1468 true.executable=cat
1469 1469 # hg update -C 1
1470 1470 $ cat <<EOF > mytool
1471 1471 > echo labellocal: \"\$1\"
1472 1472 > echo labelother: \"\$2\"
1473 1473 > echo "output (arg)": \"\$3\"
1474 1474 > echo "output (contents)":
1475 1475 > cat "\$3"
1476 1476 > EOF
1477 1477 $ hg --config merge-tools.true.executable='sh' \
1478 1478 > --config merge-tools.true.args='mytool $labellocal $labelother $output' \
1479 1479 > --config merge-tools.true.premerge=keep \
1480 1480 > --config merge-tools.true.mergemarkers=detailed \
1481 1481 > --config merge-tools.true.mergemarkertemplate='tooltmpl {short(node)}' \
1482 1482 > --config ui.mergemarkertemplate='uitmpl {rev}' \
1483 1483 > --config ui.mergemarkers=detailed \
1484 1484 > merge -r 2
1485 1485 merging f
1486 1486 labellocal: "working copy: tooltmpl ef83787e2614"
1487 1487 labelother: "merge rev: tooltmpl 0185f4e0cf02"
1488 1488 output (arg): "$TESTTMP/f"
1489 1489 output (contents):
1490 1490 <<<<<<< working copy: tooltmpl ef83787e2614
1491 1491 revision 1
1492 1492 =======
1493 1493 revision 2
1494 1494 >>>>>>> merge rev: tooltmpl 0185f4e0cf02
1495 1495 space
1496 1496 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1497 1497 (branch merge, don't forget to commit)
1498 1498 $ rm -f 'mytool'
1499 1499
1500 1500 Issue3581: Merging a filename that needs to be quoted
1501 1501 (This test doesn't work on Windows filesystems even on Linux, so check
1502 1502 for Unix-like permission)
1503 1503
1504 1504 #if unix-permissions
1505 1505 $ beforemerge
1506 1506 [merge-tools]
1507 1507 false.whatever=
1508 1508 true.priority=1
1509 1509 true.executable=cat
1510 1510 # hg update -C 1
1511 1511 $ echo "revision 5" > '"; exit 1; echo "'
1512 1512 $ hg commit -Am "revision 5"
1513 1513 adding "; exit 1; echo "
1514 1514 warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
1515 1515 $ hg update -C 1 > /dev/null
1516 1516 $ echo "revision 6" > '"; exit 1; echo "'
1517 1517 $ hg commit -Am "revision 6"
1518 1518 adding "; exit 1; echo "
1519 1519 warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
1520 1520 created new head
1521 1521 $ hg merge --config merge-tools.true.executable="true" -r 5
1522 1522 merging "; exit 1; echo "
1523 1523 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1524 1524 (branch merge, don't forget to commit)
1525 1525 $ hg update -C 1 > /dev/null
1526 1526 #endif
1527 1527
1528 1528 Merge post-processing
1529 1529
1530 1530 cat is a bad merge-tool and doesn't change:
1531 1531
1532 1532 $ beforemerge
1533 1533 [merge-tools]
1534 1534 false.whatever=
1535 1535 true.priority=1
1536 1536 true.executable=cat
1537 1537 # hg update -C 1
1538 1538 $ hg merge -y -r 2 --config merge-tools.true.checkchanged=1
1539 1539 merging f
1540 1540 revision 1
1541 1541 space
1542 1542 revision 0
1543 1543 space
1544 1544 revision 2
1545 1545 space
1546 1546 output file f appears unchanged
1547 1547 was merge successful (yn)? n
1548 1548 merging f failed!
1549 1549 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1550 1550 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1551 1551 [1]
1552 1552 $ aftermerge
1553 1553 # cat f
1554 1554 revision 1
1555 1555 space
1556 1556 # hg stat
1557 1557 M f
1558 1558 ? f.orig
1559 1559 # hg resolve --list
1560 1560 U f
1561 1561
1562 1562 #if symlink
1563 1563
1564 1564 internal merge cannot handle symlinks and shouldn't try:
1565 1565
1566 1566 $ hg update -q -C 1
1567 1567 $ rm f
1568 1568 $ ln -s symlink f
1569 1569 $ hg commit -qm 'f is symlink'
1570 1570 $ hg merge -r 2 --tool internal:merge
1571 1571 merging f
1572 1572 warning: internal :merge cannot merge symlinks for f
1573 1573 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
1574 1574 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1575 1575 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
1576 1576 [1]
1577 1577
1578 1578 #endif
1579 1579
1580 1580 Verify naming of temporary files and that extension is preserved:
1581 1581
1582 1582 $ hg update -q -C 1
1583 1583 $ hg mv f f.txt
1584 1584 $ hg ci -qm "f.txt"
1585 1585 $ hg update -q -C 2
1586 1586 $ hg merge -y -r tip --tool echo --config merge-tools.echo.args='$base $local $other $output'
1587 1587 merging f and f.txt to f.txt
1588 */f~base.* $TESTTMP/f.txt.orig */f~other.*.txt $TESTTMP/f.txt (glob)
1588 */f~base.* */f~local.*.txt */f~other.*.txt $TESTTMP/f.txt (glob)
1589 1589 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1590 1590 (branch merge, don't forget to commit)
1591 1591
1592 1592 Verify naming of temporary files and that extension is preserved
1593 1593 (experimental.mergetempdirprefix version):
1594 1594
1595 1595 $ hg update -q -C 1
1596 1596 $ hg mv f f.txt
1597 1597 $ hg ci -qm "f.txt"
1598 1598 $ hg update -q -C 2
1599 1599 $ hg merge -y -r tip --tool echo \
1600 1600 > --config merge-tools.echo.args='$base $local $other $output' \
1601 1601 > --config experimental.mergetempdirprefix=$TESTTMP/hgmerge.
1602 1602 merging f and f.txt to f.txt
1603 $TESTTMP/hgmerge.*/f~base $TESTTMP/f.txt.orig $TESTTMP/hgmerge.*/f~other.txt $TESTTMP/f.txt (glob)
1603 $TESTTMP/hgmerge.*/f~base $TESTTMP/hgmerge.*/f~local.txt $TESTTMP/hgmerge.*/f~other.txt $TESTTMP/f.txt (glob)
1604 1604 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1605 1605 (branch merge, don't forget to commit)
1606 1606
1607 1607 Check that debugpicktool examines which merge tool is chosen for
1608 1608 specified file as expected
1609 1609
1610 1610 $ beforemerge
1611 1611 [merge-tools]
1612 1612 false.whatever=
1613 1613 true.priority=1
1614 1614 true.executable=cat
1615 1615 # hg update -C 1
1616 1616
1617 1617 (default behavior: checking files in the working parent context)
1618 1618
1619 1619 $ hg manifest
1620 1620 f
1621 1621 $ hg debugpickmergetool
1622 1622 f = true
1623 1623
1624 1624 (-X/-I and file patterns limmit examination targets)
1625 1625
1626 1626 $ hg debugpickmergetool -X f
1627 1627 $ hg debugpickmergetool unknown
1628 1628 unknown: no such file in rev ef83787e2614
1629 1629
1630 1630 (--changedelete emulates merging change and delete)
1631 1631
1632 1632 $ hg debugpickmergetool --changedelete
1633 1633 f = :prompt
1634 1634
1635 1635 (-r REV causes checking files in specified revision)
1636 1636
1637 1637 $ hg manifest -r tip
1638 1638 f.txt
1639 1639 $ hg debugpickmergetool -r tip
1640 1640 f.txt = true
1641 1641
1642 1642 #if symlink
1643 1643
1644 1644 (symlink causes chosing :prompt)
1645 1645
1646 1646 $ hg debugpickmergetool -r 6d00b3726f6e
1647 1647 f = :prompt
1648 1648
1649 1649 #endif
1650 1650
1651 1651 (--verbose shows some configurations)
1652 1652
1653 1653 $ hg debugpickmergetool --tool foobar -v
1654 1654 with --tool 'foobar'
1655 1655 f = foobar
1656 1656
1657 1657 $ HGMERGE=false hg debugpickmergetool -v
1658 1658 with HGMERGE='false'
1659 1659 f = false
1660 1660
1661 1661 $ hg debugpickmergetool --config ui.merge=false -v
1662 1662 with ui.merge='false'
1663 1663 f = false
1664 1664
1665 1665 (--debug shows errors detected intermediately)
1666 1666
1667 1667 $ hg debugpickmergetool --config merge-patterns.f=true --config merge-tools.true.executable=nonexistentmergetool --debug f
1668 1668 couldn't find merge tool true (for pattern f)
1669 1669 couldn't find merge tool true
1670 1670 f = false
General Comments 0
You need to be logged in to leave comments. Login now