##// END OF EJS Templates
filemerge: run symlink check for :merge3...
Siddharth Agarwal -
r26519:48476c61 default
parent child Browse files
Show More
@@ -1,566 +1,567 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 short
17 17
18 18 from . import (
19 19 error,
20 20 match,
21 21 simplemerge,
22 22 tagmerge,
23 23 templatekw,
24 24 templater,
25 25 util,
26 26 )
27 27
28 28 def _toolstr(ui, tool, part, default=""):
29 29 return ui.config("merge-tools", tool + "." + part, default)
30 30
31 31 def _toolbool(ui, tool, part, default=False):
32 32 return ui.configbool("merge-tools", tool + "." + part, default)
33 33
34 34 def _toollist(ui, tool, part, default=[]):
35 35 return ui.configlist("merge-tools", tool + "." + part, default)
36 36
37 37 internals = {}
38 38 # Merge tools to document.
39 39 internalsdoc = {}
40 40
41 41 def internaltool(name, trymerge, onfailure=None, precheck=None):
42 42 '''return a decorator for populating internal merge tool table'''
43 43 def decorator(func):
44 44 fullname = ':' + name
45 45 func.__doc__ = "``%s``\n" % fullname + func.__doc__.strip()
46 46 internals[fullname] = func
47 47 internals['internal:' + name] = func
48 48 internalsdoc[fullname] = func
49 49 func.trymerge = trymerge
50 50 func.onfailure = onfailure
51 51 func.precheck = precheck
52 52 return func
53 53 return decorator
54 54
55 55 def _findtool(ui, tool):
56 56 if tool in internals:
57 57 return tool
58 58 return findexternaltool(ui, tool)
59 59
60 60 def findexternaltool(ui, tool):
61 61 for kn in ("regkey", "regkeyalt"):
62 62 k = _toolstr(ui, tool, kn)
63 63 if not k:
64 64 continue
65 65 p = util.lookupreg(k, _toolstr(ui, tool, "regname"))
66 66 if p:
67 67 p = util.findexe(p + _toolstr(ui, tool, "regappend"))
68 68 if p:
69 69 return p
70 70 exe = _toolstr(ui, tool, "executable", tool)
71 71 return util.findexe(util.expandpath(exe))
72 72
73 73 def _picktool(repo, ui, path, binary, symlink):
74 74 def check(tool, pat, symlink, binary):
75 75 tmsg = tool
76 76 if pat:
77 77 tmsg += " specified for " + pat
78 78 if not _findtool(ui, tool):
79 79 if pat: # explicitly requested tool deserves a warning
80 80 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
81 81 else: # configured but non-existing tools are more silent
82 82 ui.note(_("couldn't find merge tool %s\n") % tmsg)
83 83 elif symlink and not _toolbool(ui, tool, "symlink"):
84 84 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
85 85 elif binary and not _toolbool(ui, tool, "binary"):
86 86 ui.warn(_("tool %s can't handle binary\n") % tmsg)
87 87 elif not util.gui() and _toolbool(ui, tool, "gui"):
88 88 ui.warn(_("tool %s requires a GUI\n") % tmsg)
89 89 else:
90 90 return True
91 91 return False
92 92
93 93 # internal config: ui.forcemerge
94 94 # forcemerge comes from command line arguments, highest priority
95 95 force = ui.config('ui', 'forcemerge')
96 96 if force:
97 97 toolpath = _findtool(ui, force)
98 98 if toolpath:
99 99 return (force, util.shellquote(toolpath))
100 100 else:
101 101 # mimic HGMERGE if given tool not found
102 102 return (force, force)
103 103
104 104 # HGMERGE takes next precedence
105 105 hgmerge = os.environ.get("HGMERGE")
106 106 if hgmerge:
107 107 return (hgmerge, hgmerge)
108 108
109 109 # then patterns
110 110 for pat, tool in ui.configitems("merge-patterns"):
111 111 mf = match.match(repo.root, '', [pat])
112 112 if mf(path) and check(tool, pat, symlink, False):
113 113 toolpath = _findtool(ui, tool)
114 114 return (tool, util.shellquote(toolpath))
115 115
116 116 # then merge tools
117 117 tools = {}
118 118 for k, v in ui.configitems("merge-tools"):
119 119 t = k.split('.')[0]
120 120 if t not in tools:
121 121 tools[t] = int(_toolstr(ui, t, "priority", "0"))
122 122 names = tools.keys()
123 123 tools = sorted([(-p, t) for t, p in tools.items()])
124 124 uimerge = ui.config("ui", "merge")
125 125 if uimerge:
126 126 if uimerge not in names:
127 127 return (uimerge, uimerge)
128 128 tools.insert(0, (None, uimerge)) # highest priority
129 129 tools.append((None, "hgmerge")) # the old default, if found
130 130 for p, t in tools:
131 131 if check(t, None, symlink, binary):
132 132 toolpath = _findtool(ui, t)
133 133 return (t, util.shellquote(toolpath))
134 134
135 135 # internal merge or prompt as last resort
136 136 if symlink or binary:
137 137 return ":prompt", None
138 138 return ":merge", None
139 139
140 140 def _eoltype(data):
141 141 "Guess the EOL type of a file"
142 142 if '\0' in data: # binary
143 143 return None
144 144 if '\r\n' in data: # Windows
145 145 return '\r\n'
146 146 if '\r' in data: # Old Mac
147 147 return '\r'
148 148 if '\n' in data: # UNIX
149 149 return '\n'
150 150 return None # unknown
151 151
152 152 def _matcheol(file, origfile):
153 153 "Convert EOL markers in a file to match origfile"
154 154 tostyle = _eoltype(util.readfile(origfile))
155 155 if tostyle:
156 156 data = util.readfile(file)
157 157 style = _eoltype(data)
158 158 if style:
159 159 newdata = data.replace(style, tostyle)
160 160 if newdata != data:
161 161 util.writefile(file, newdata)
162 162
163 163 @internaltool('prompt', False)
164 164 def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf):
165 165 """Asks the user which of the local or the other version to keep as
166 166 the merged version."""
167 167 ui = repo.ui
168 168 fd = fcd.path()
169 169
170 170 if ui.promptchoice(_(" no tool found to merge %s\n"
171 171 "keep (l)ocal or take (o)ther?"
172 172 "$$ &Local $$ &Other") % fd, 0):
173 173 return _iother(repo, mynode, orig, fcd, fco, fca, toolconf)
174 174 else:
175 175 return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf)
176 176
177 177 @internaltool('local', False)
178 178 def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf):
179 179 """Uses the local version of files as the merged version."""
180 180 return 0
181 181
182 182 @internaltool('other', False)
183 183 def _iother(repo, mynode, orig, fcd, fco, fca, toolconf):
184 184 """Uses the other version of files as the merged version."""
185 185 repo.wwrite(fcd.path(), fco.data(), fco.flags())
186 186 return 0
187 187
188 188 @internaltool('fail', False)
189 189 def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf):
190 190 """
191 191 Rather than attempting to merge files that were modified on both
192 192 branches, it marks them as unresolved. The resolve command must be
193 193 used to resolve these conflicts."""
194 194 return 1
195 195
196 196 def _premerge(repo, toolconf, files, labels=None):
197 197 tool, toolpath, binary, symlink = toolconf
198 198 if symlink:
199 199 return 1
200 200 a, b, c, back = files
201 201
202 202 ui = repo.ui
203 203
204 204 validkeep = ['keep', 'keep-merge3']
205 205
206 206 # do we attempt to simplemerge first?
207 207 try:
208 208 premerge = _toolbool(ui, tool, "premerge", not binary)
209 209 except error.ConfigError:
210 210 premerge = _toolstr(ui, tool, "premerge").lower()
211 211 if premerge not in validkeep:
212 212 _valid = ', '.join(["'" + v + "'" for v in validkeep])
213 213 raise error.ConfigError(_("%s.premerge not valid "
214 214 "('%s' is neither boolean nor %s)") %
215 215 (tool, premerge, _valid))
216 216
217 217 if premerge:
218 218 if premerge == 'keep-merge3':
219 219 if not labels:
220 220 labels = _defaultconflictlabels
221 221 if len(labels) < 3:
222 222 labels.append('base')
223 223 r = simplemerge.simplemerge(ui, a, b, c, quiet=True, label=labels)
224 224 if not r:
225 225 ui.debug(" premerge successful\n")
226 226 return 0
227 227 if premerge not in validkeep:
228 228 util.copyfile(back, a) # restore from backup and try again
229 229 return 1 # continue merging
230 230
231 231 def _symlinkcheck(repo, mynode, orig, fcd, fco, fca, toolconf):
232 232 tool, toolpath, binary, symlink = toolconf
233 233 if symlink:
234 234 repo.ui.warn(_('warning: internal %s cannot merge symlinks '
235 235 'for %s\n') % (tool, fcd.path()))
236 236 return False
237 237 return True
238 238
239 239 def _merge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, mode):
240 240 """
241 241 Uses the internal non-interactive simple merge algorithm for merging
242 242 files. It will fail if there are any conflicts and leave markers in
243 243 the partially merged file. Markers will have two sections, one for each side
244 244 of merge, unless mode equals 'union' which suppresses the markers."""
245 245 r = _premerge(repo, toolconf, files, labels=labels)
246 246 if r:
247 247 a, b, c, back = files
248 248
249 249 ui = repo.ui
250 250
251 251 r = simplemerge.simplemerge(ui, a, b, c, label=labels, mode=mode)
252 252 return True, r
253 253 return False, 0
254 254
255 255 @internaltool('union', True,
256 256 _("merging %s incomplete! "
257 257 "(edit conflicts, then use 'hg resolve --mark')\n"),
258 258 precheck=_symlinkcheck)
259 259 def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
260 260 """
261 261 Uses the internal non-interactive simple merge algorithm for merging
262 262 files. It will use both left and right sides for conflict regions.
263 263 No markers are inserted."""
264 264 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
265 265 files, labels, 'union')
266 266
267 267 @internaltool('merge', True,
268 268 _("merging %s incomplete! "
269 269 "(edit conflicts, then use 'hg resolve --mark')\n"),
270 270 precheck=_symlinkcheck)
271 271 def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
272 272 """
273 273 Uses the internal non-interactive simple merge algorithm for merging
274 274 files. It will fail if there are any conflicts and leave markers in
275 275 the partially merged file. Markers will have two sections, one for each side
276 276 of merge."""
277 277 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
278 278 files, labels, 'merge')
279 279
280 280 @internaltool('merge3', True,
281 281 _("merging %s incomplete! "
282 "(edit conflicts, then use 'hg resolve --mark')\n"))
282 "(edit conflicts, then use 'hg resolve --mark')\n"),
283 precheck=_symlinkcheck)
283 284 def _imerge3(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
284 285 """
285 286 Uses the internal non-interactive simple merge algorithm for merging
286 287 files. It will fail if there are any conflicts and leave markers in
287 288 the partially merged file. Marker will have three sections, one from each
288 289 side of the merge and one for the base content."""
289 290 if not labels:
290 291 labels = _defaultconflictlabels
291 292 if len(labels) < 3:
292 293 labels.append('base')
293 294 return _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels)
294 295
295 296 def _imergeauto(repo, mynode, orig, fcd, fco, fca, toolconf, files,
296 297 labels=None, localorother=None):
297 298 """
298 299 Generic driver for _imergelocal and _imergeother
299 300 """
300 301 assert localorother is not None
301 302 tool, toolpath, binary, symlink = toolconf
302 303 if symlink:
303 304 repo.ui.warn(_('warning: :merge-%s cannot merge symlinks '
304 305 'for %s\n') % (localorother, fcd.path()))
305 306 return False, 1
306 307 a, b, c, back = files
307 308 r = simplemerge.simplemerge(repo.ui, a, b, c, label=labels,
308 309 localorother=localorother)
309 310 return True, r
310 311
311 312 @internaltool('merge-local', True)
312 313 def _imergelocal(*args, **kwargs):
313 314 """
314 315 Like :merge, but resolve all conflicts non-interactively in favor
315 316 of the local changes."""
316 317 success, status = _imergeauto(localorother='local', *args, **kwargs)
317 318 return success, status
318 319
319 320 @internaltool('merge-other', True)
320 321 def _imergeother(*args, **kwargs):
321 322 """
322 323 Like :merge, but resolve all conflicts non-interactively in favor
323 324 of the other changes."""
324 325 success, status = _imergeauto(localorother='other', *args, **kwargs)
325 326 return success, status
326 327
327 328 @internaltool('tagmerge', True,
328 329 _("automatic tag merging of %s failed! "
329 330 "(use 'hg resolve --tool :merge' or another merge "
330 331 "tool of your choice)\n"))
331 332 def _itagmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
332 333 """
333 334 Uses the internal tag merge algorithm (experimental).
334 335 """
335 336 return tagmerge.merge(repo, fcd, fco, fca)
336 337
337 338 @internaltool('dump', True)
338 339 def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
339 340 """
340 341 Creates three versions of the files to merge, containing the
341 342 contents of local, other and base. These files can then be used to
342 343 perform a merge manually. If the file to be merged is named
343 344 ``a.txt``, these files will accordingly be named ``a.txt.local``,
344 345 ``a.txt.other`` and ``a.txt.base`` and they will be placed in the
345 346 same directory as ``a.txt``."""
346 347 r = _premerge(repo, toolconf, files, labels=labels)
347 348 if r:
348 349 a, b, c, back = files
349 350
350 351 fd = fcd.path()
351 352
352 353 util.copyfile(a, a + ".local")
353 354 repo.wwrite(fd + ".other", fco.data(), fco.flags())
354 355 repo.wwrite(fd + ".base", fca.data(), fca.flags())
355 356 return False, r
356 357
357 358 def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
358 359 r = _premerge(repo, toolconf, files, labels=labels)
359 360 if r:
360 361 tool, toolpath, binary, symlink = toolconf
361 362 a, b, c, back = files
362 363 out = ""
363 364 env = {'HG_FILE': fcd.path(),
364 365 'HG_MY_NODE': short(mynode),
365 366 'HG_OTHER_NODE': str(fco.changectx()),
366 367 'HG_BASE_NODE': str(fca.changectx()),
367 368 'HG_MY_ISLINK': 'l' in fcd.flags(),
368 369 'HG_OTHER_ISLINK': 'l' in fco.flags(),
369 370 'HG_BASE_ISLINK': 'l' in fca.flags(),
370 371 }
371 372
372 373 ui = repo.ui
373 374
374 375 args = _toolstr(ui, tool, "args", '$local $base $other')
375 376 if "$output" in args:
376 377 out, a = a, back # read input from backup, write to original
377 378 replace = {'local': a, 'base': b, 'other': c, 'output': out}
378 379 args = util.interpolate(r'\$', replace, args,
379 380 lambda s: util.shellquote(util.localpath(s)))
380 381 cmd = toolpath + ' ' + args
381 382 repo.ui.debug('launching merge tool: %s\n' % cmd)
382 383 r = ui.system(cmd, cwd=repo.root, environ=env)
383 384 repo.ui.debug('merge tool returned: %s\n' % r)
384 385 return True, r
385 386 return False, 0
386 387
387 388 def _formatconflictmarker(repo, ctx, template, label, pad):
388 389 """Applies the given template to the ctx, prefixed by the label.
389 390
390 391 Pad is the minimum width of the label prefix, so that multiple markers
391 392 can have aligned templated parts.
392 393 """
393 394 if ctx.node() is None:
394 395 ctx = ctx.p1()
395 396
396 397 props = templatekw.keywords.copy()
397 398 props['templ'] = template
398 399 props['ctx'] = ctx
399 400 props['repo'] = repo
400 401 templateresult = template('conflictmarker', **props)
401 402
402 403 label = ('%s:' % label).ljust(pad + 1)
403 404 mark = '%s %s' % (label, templater.stringify(templateresult))
404 405
405 406 if mark:
406 407 mark = mark.splitlines()[0] # split for safety
407 408
408 409 # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
409 410 return util.ellipsis(mark, 80 - 8)
410 411
411 412 _defaultconflictmarker = ('{node|short} ' +
412 413 '{ifeq(tags, "tip", "", "{tags} ")}' +
413 414 '{if(bookmarks, "{bookmarks} ")}' +
414 415 '{ifeq(branch, "default", "", "{branch} ")}' +
415 416 '- {author|user}: {desc|firstline}')
416 417
417 418 _defaultconflictlabels = ['local', 'other']
418 419
419 420 def _formatlabels(repo, fcd, fco, fca, labels):
420 421 """Formats the given labels using the conflict marker template.
421 422
422 423 Returns a list of formatted labels.
423 424 """
424 425 cd = fcd.changectx()
425 426 co = fco.changectx()
426 427 ca = fca.changectx()
427 428
428 429 ui = repo.ui
429 430 template = ui.config('ui', 'mergemarkertemplate', _defaultconflictmarker)
430 431 tmpl = templater.templater(None, cache={'conflictmarker': template})
431 432
432 433 pad = max(len(l) for l in labels)
433 434
434 435 newlabels = [_formatconflictmarker(repo, cd, tmpl, labels[0], pad),
435 436 _formatconflictmarker(repo, co, tmpl, labels[1], pad)]
436 437 if len(labels) > 2:
437 438 newlabels.append(_formatconflictmarker(repo, ca, tmpl, labels[2], pad))
438 439 return newlabels
439 440
440 441 def filemerge(repo, mynode, orig, fcd, fco, fca, labels=None):
441 442 """perform a 3-way merge in the working directory
442 443
443 444 mynode = parent node before merge
444 445 orig = original local filename before merge
445 446 fco = other file context
446 447 fca = ancestor file context
447 448 fcd = local file context for current/destination file
448 449 """
449 450
450 451 if True:
451 452 def temp(prefix, ctx):
452 453 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
453 454 (fd, name) = tempfile.mkstemp(prefix=pre)
454 455 data = repo.wwritedata(ctx.path(), ctx.data())
455 456 f = os.fdopen(fd, "wb")
456 457 f.write(data)
457 458 f.close()
458 459 return name
459 460
460 461 if not fco.cmp(fcd): # files identical?
461 462 return None
462 463
463 464 ui = repo.ui
464 465 fd = fcd.path()
465 466 binary = fcd.isbinary() or fco.isbinary() or fca.isbinary()
466 467 symlink = 'l' in fcd.flags() + fco.flags()
467 468 tool, toolpath = _picktool(repo, ui, fd, binary, symlink)
468 469 if tool in internals and tool.startswith('internal:'):
469 470 # normalize to new-style names (':merge' etc)
470 471 tool = tool[len('internal'):]
471 472 ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" %
472 473 (tool, fd, binary, symlink))
473 474
474 475 if tool in internals:
475 476 func = internals[tool]
476 477 trymerge = func.trymerge
477 478 onfailure = func.onfailure
478 479 precheck = func.precheck
479 480 else:
480 481 func = _xmerge
481 482 trymerge = True
482 483 onfailure = _("merging %s failed!\n")
483 484 precheck = None
484 485
485 486 toolconf = tool, toolpath, binary, symlink
486 487
487 488 if not trymerge:
488 489 return func(repo, mynode, orig, fcd, fco, fca, toolconf)
489 490
490 491 a = repo.wjoin(fd)
491 492 b = temp("base", fca)
492 493 c = temp("other", fco)
493 494 back = a + ".orig"
494 495 util.copyfile(a, back)
495 496
496 497 if orig != fco.path():
497 498 ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
498 499 else:
499 500 ui.status(_("merging %s\n") % fd)
500 501
501 502 ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca))
502 503
503 504 r = 0
504 505 if precheck and not precheck(repo, mynode, orig, fcd, fco, fca,
505 506 toolconf):
506 507 r = 1
507 508 needcheck = False
508 509
509 510 if not r: # precheck passed
510 511 markerstyle = ui.config('ui', 'mergemarkers', 'basic')
511 512 if not labels:
512 513 labels = _defaultconflictlabels
513 514 if markerstyle != 'basic':
514 515 labels = _formatlabels(repo, fcd, fco, fca, labels)
515 516
516 517 needcheck, r = func(repo, mynode, orig, fcd, fco, fca, toolconf,
517 518 (a, b, c, back), labels=labels)
518 519
519 520 if not needcheck:
520 521 if r:
521 522 if onfailure:
522 523 ui.warn(onfailure % fd)
523 524 else:
524 525 util.unlink(back)
525 526
526 527 util.unlink(b)
527 528 util.unlink(c)
528 529 return r
529 530
530 531 if not r and (_toolbool(ui, tool, "checkconflicts") or
531 532 'conflicts' in _toollist(ui, tool, "check")):
532 533 if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(),
533 534 re.MULTILINE):
534 535 r = 1
535 536
536 537 checked = False
537 538 if 'prompt' in _toollist(ui, tool, "check"):
538 539 checked = True
539 540 if ui.promptchoice(_("was merge of '%s' successful (yn)?"
540 541 "$$ &Yes $$ &No") % fd, 1):
541 542 r = 1
542 543
543 544 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
544 545 'changed' in
545 546 _toollist(ui, tool, "check")):
546 547 if filecmp.cmp(a, back):
547 548 if ui.promptchoice(_(" output file %s appears unchanged\n"
548 549 "was merge successful (yn)?"
549 550 "$$ &Yes $$ &No") % fd, 1):
550 551 r = 1
551 552
552 553 if _toolbool(ui, tool, "fixeol"):
553 554 _matcheol(a, back)
554 555
555 556 if r:
556 557 if onfailure:
557 558 ui.warn(onfailure % fd)
558 559 else:
559 560 util.unlink(back)
560 561
561 562 util.unlink(b)
562 563 util.unlink(c)
563 564 return r
564 565
565 566 # tell hggettext to extract docstrings from these functions:
566 567 i18nfunctions = internals.values()
@@ -1,376 +1,399 b''
1 1 #require symlink execbit
2 2
3 3 $ tellmeabout() {
4 4 > if [ -h $1 ]; then
5 5 > echo $1 is a symlink:
6 6 > $TESTDIR/readlink.py $1
7 7 > elif [ -x $1 ]; then
8 8 > echo $1 is an executable file with content:
9 9 > cat $1
10 10 > else
11 11 > echo $1 is a plain file with content:
12 12 > cat $1
13 13 > fi
14 14 > }
15 15
16 16 $ hg init test1
17 17 $ cd test1
18 18
19 19 $ echo a > a
20 20 $ hg ci -Aqmadd
21 21 $ chmod +x a
22 22 $ hg ci -mexecutable
23 23
24 24 $ hg up -q 0
25 25 $ rm a
26 26 $ ln -s symlink a
27 27 $ hg ci -msymlink
28 28 created new head
29 29
30 30 Symlink is local parent, executable is other:
31 31
32 32 $ hg merge --debug
33 33 searching for copies back to rev 1
34 34 resolving manifests
35 35 branchmerge: True, force: False, partial: False
36 36 ancestor: c334dc3be0da, local: 521a1e40188f+, remote: 3574f3e69b1c
37 37 preserving a for resolve of a
38 38 a: versions differ -> m
39 39 picked tool ':merge' for a (binary False symlink True)
40 40 merging a
41 41 my a@521a1e40188f+ other a@3574f3e69b1c ancestor a@c334dc3be0da
42 42 warning: internal :merge cannot merge symlinks for a
43 43 merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
44 44 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
45 45 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
46 46 [1]
47 47
48 48 $ tellmeabout a
49 49 a is a symlink:
50 50 a -> symlink
51 51 $ hg resolve a --tool internal:other
52 52 (no more unresolved files)
53 53 $ tellmeabout a
54 54 a is an executable file with content:
55 55 a
56 56 $ hg st
57 57 M a
58 58 ? a.orig
59 59
60 60 Symlink is other parent, executable is local:
61 61
62 62 $ hg update -C 1
63 63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 64
65 65 $ hg merge --debug --tool :union
66 66 searching for copies back to rev 1
67 67 resolving manifests
68 68 branchmerge: True, force: False, partial: False
69 69 ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
70 70 preserving a for resolve of a
71 71 a: versions differ -> m
72 72 picked tool ':union' for a (binary False symlink True)
73 73 merging a
74 74 my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
75 75 warning: internal :union cannot merge symlinks for a
76 76 merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
77 77 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
78 78 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
79 79 [1]
80 80
81 81 $ tellmeabout a
82 82 a is an executable file with content:
83 83 a
84 84
85 $ hg update -C 1
86 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
87
88 $ hg merge --debug --tool :merge3
89 searching for copies back to rev 1
90 resolving manifests
91 branchmerge: True, force: False, partial: False
92 ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
93 preserving a for resolve of a
94 a: versions differ -> m
95 picked tool ':merge3' for a (binary False symlink True)
96 merging a
97 my a@3574f3e69b1c+ other a@521a1e40188f ancestor a@c334dc3be0da
98 warning: internal :merge3 cannot merge symlinks for a
99 merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
100 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
101 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
102 [1]
103
104 $ tellmeabout a
105 a is an executable file with content:
106 a
107
85 108 Update to link without local change should get us a symlink (issue3316):
86 109
87 110 $ hg up -C 0
88 111 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 112 $ hg up
90 113 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 114 $ hg st
92 115 ? a.orig
93 116
94 117 Update to link with local change should cause a merge prompt (issue3200):
95 118
96 119 $ hg up -Cq 0
97 120 $ echo data > a
98 121 $ HGMERGE= hg up -y --debug
99 122 searching for copies back to rev 2
100 123 resolving manifests
101 124 branchmerge: False, force: False, partial: False
102 125 ancestor: c334dc3be0da, local: c334dc3be0da+, remote: 521a1e40188f
103 126 preserving a for resolve of a
104 127 a: versions differ -> m
105 128 (couldn't find merge tool hgmerge|tool hgmerge can't handle symlinks) (re)
106 129 picked tool ':prompt' for a (binary False symlink True)
107 130 no tool found to merge a
108 131 keep (l)ocal or take (o)ther? l
109 132 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
110 133 $ hg diff --git
111 134 diff --git a/a b/a
112 135 old mode 120000
113 136 new mode 100644
114 137 --- a/a
115 138 +++ b/a
116 139 @@ -1,1 +1,1 @@
117 140 -symlink
118 141 \ No newline at end of file
119 142 +data
120 143
121 144
122 145 Test only 'l' change - happens rarely, except when recovering from situations
123 146 where that was what happened.
124 147
125 148 $ hg init test2
126 149 $ cd test2
127 150 $ printf base > f
128 151 $ hg ci -Aqm0
129 152 $ echo file > f
130 153 $ echo content >> f
131 154 $ hg ci -qm1
132 155 $ hg up -qr0
133 156 $ rm f
134 157 $ ln -s base f
135 158 $ hg ci -qm2
136 159 $ hg merge
137 160 merging f
138 161 warning: internal :merge cannot merge symlinks for f
139 162 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
140 163 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
141 164 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
142 165 [1]
143 166 $ tellmeabout f
144 167 f is a symlink:
145 168 f -> base
146 169
147 170 $ hg up -Cqr1
148 171 $ hg merge
149 172 merging f
150 173 warning: internal :merge cannot merge symlinks for f
151 174 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
152 175 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
153 176 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
154 177 [1]
155 178 $ tellmeabout f
156 179 f is a plain file with content:
157 180 file
158 181 content
159 182
160 183 $ cd ..
161 184
162 185 Test removed 'x' flag merged with change to symlink
163 186
164 187 $ hg init test3
165 188 $ cd test3
166 189 $ echo f > f
167 190 $ chmod +x f
168 191 $ hg ci -Aqm0
169 192 $ chmod -x f
170 193 $ hg ci -qm1
171 194 $ hg up -qr0
172 195 $ rm f
173 196 $ ln -s dangling f
174 197 $ hg ci -qm2
175 198 $ hg merge
176 199 merging f
177 200 warning: internal :merge cannot merge symlinks for f
178 201 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
179 202 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
180 203 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
181 204 [1]
182 205 $ tellmeabout f
183 206 f is a symlink:
184 207 f -> dangling
185 208
186 209 $ hg up -Cqr1
187 210 $ hg merge
188 211 merging f
189 212 warning: internal :merge cannot merge symlinks for f
190 213 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
191 214 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
192 215 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
193 216 [1]
194 217 $ tellmeabout f
195 218 f is a plain file with content:
196 219 f
197 220
198 221 Test removed 'x' flag merged with content change - both ways
199 222
200 223 $ hg up -Cqr0
201 224 $ echo change > f
202 225 $ hg ci -qm3
203 226 $ hg merge -r1
204 227 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
205 228 (branch merge, don't forget to commit)
206 229 $ tellmeabout f
207 230 f is a plain file with content:
208 231 change
209 232
210 233 $ hg up -qCr1
211 234 $ hg merge -r3
212 235 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
213 236 (branch merge, don't forget to commit)
214 237 $ tellmeabout f
215 238 f is a plain file with content:
216 239 change
217 240
218 241 $ cd ..
219 242
220 243 Test merge with no common ancestor:
221 244 a: just different
222 245 b: x vs -, different (cannot calculate x, cannot ask merge tool)
223 246 c: x vs -, same (cannot calculate x, merge tool is no good)
224 247 d: x vs l, different
225 248 e: x vs l, same
226 249 f: - vs l, different
227 250 g: - vs l, same
228 251 h: l vs l, different
229 252 (where same means the filelog entry is shared and there thus is an ancestor!)
230 253
231 254 $ hg init test4
232 255 $ cd test4
233 256 $ echo 0 > 0
234 257 $ hg ci -Aqm0
235 258
236 259 $ echo 1 > a
237 260 $ echo 1 > b
238 261 $ chmod +x b
239 262 $ echo x > c
240 263 $ chmod +x c
241 264 $ echo 1 > d
242 265 $ chmod +x d
243 266 $ printf x > e
244 267 $ chmod +x e
245 268 $ echo 1 > f
246 269 $ printf x > g
247 270 $ ln -s 1 h
248 271 $ hg ci -qAm1
249 272
250 273 $ hg up -qr0
251 274 $ echo 2 > a
252 275 $ echo 2 > b
253 276 $ echo x > c
254 277 $ ln -s 2 d
255 278 $ ln -s x e
256 279 $ ln -s 2 f
257 280 $ ln -s x g
258 281 $ ln -s 2 h
259 282 $ hg ci -Aqm2
260 283
261 284 $ hg merge
262 285 merging a
263 286 warning: conflicts during merge.
264 287 merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
265 288 warning: cannot merge flags for b
266 289 merging b
267 290 warning: conflicts during merge.
268 291 merging b incomplete! (edit conflicts, then use 'hg resolve --mark')
269 292 warning: cannot merge flags for c
270 293 merging d
271 294 warning: internal :merge cannot merge symlinks for d
272 295 merging d incomplete! (edit conflicts, then use 'hg resolve --mark')
273 296 merging f
274 297 warning: internal :merge cannot merge symlinks for f
275 298 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
276 299 merging h
277 300 warning: internal :merge cannot merge symlinks for h
278 301 merging h incomplete! (edit conflicts, then use 'hg resolve --mark')
279 302 3 files updated, 0 files merged, 0 files removed, 5 files unresolved
280 303 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
281 304 [1]
282 305 $ hg resolve -l
283 306 U a
284 307 U b
285 308 U d
286 309 U f
287 310 U h
288 311 $ tellmeabout a
289 312 a is a plain file with content:
290 313 <<<<<<< local: 0139c5610547 - test: 2
291 314 2
292 315 =======
293 316 1
294 317 >>>>>>> other: 97e29675e796 - test: 1
295 318 $ tellmeabout b
296 319 b is a plain file with content:
297 320 <<<<<<< local: 0139c5610547 - test: 2
298 321 2
299 322 =======
300 323 1
301 324 >>>>>>> other: 97e29675e796 - test: 1
302 325 $ tellmeabout c
303 326 c is a plain file with content:
304 327 x
305 328 $ tellmeabout d
306 329 d is a symlink:
307 330 d -> 2
308 331 $ tellmeabout e
309 332 e is a symlink:
310 333 e -> x
311 334 $ tellmeabout f
312 335 f is a symlink:
313 336 f -> 2
314 337 $ tellmeabout g
315 338 g is a symlink:
316 339 g -> x
317 340 $ tellmeabout h
318 341 h is a symlink:
319 342 h -> 2
320 343
321 344 $ hg up -Cqr1
322 345 $ hg merge
323 346 merging a
324 347 warning: conflicts during merge.
325 348 merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
326 349 warning: cannot merge flags for b
327 350 merging b
328 351 warning: conflicts during merge.
329 352 merging b incomplete! (edit conflicts, then use 'hg resolve --mark')
330 353 warning: cannot merge flags for c
331 354 merging d
332 355 warning: internal :merge cannot merge symlinks for d
333 356 merging d incomplete! (edit conflicts, then use 'hg resolve --mark')
334 357 merging f
335 358 warning: internal :merge cannot merge symlinks for f
336 359 merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
337 360 merging h
338 361 warning: internal :merge cannot merge symlinks for h
339 362 merging h incomplete! (edit conflicts, then use 'hg resolve --mark')
340 363 3 files updated, 0 files merged, 0 files removed, 5 files unresolved
341 364 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
342 365 [1]
343 366 $ tellmeabout a
344 367 a is a plain file with content:
345 368 <<<<<<< local: 97e29675e796 - test: 1
346 369 1
347 370 =======
348 371 2
349 372 >>>>>>> other: 0139c5610547 - test: 2
350 373 $ tellmeabout b
351 374 b is an executable file with content:
352 375 <<<<<<< local: 97e29675e796 - test: 1
353 376 1
354 377 =======
355 378 2
356 379 >>>>>>> other: 0139c5610547 - test: 2
357 380 $ tellmeabout c
358 381 c is an executable file with content:
359 382 x
360 383 $ tellmeabout d
361 384 d is an executable file with content:
362 385 1
363 386 $ tellmeabout e
364 387 e is an executable file with content:
365 388 x (no-eol)
366 389 $ tellmeabout f
367 390 f is a plain file with content:
368 391 1
369 392 $ tellmeabout g
370 393 g is a plain file with content:
371 394 x (no-eol)
372 395 $ tellmeabout h
373 396 h is a symlink:
374 397 h -> 1
375 398
376 399 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now