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