Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/merge.py @ 5042:f191bc3916f7
merge: do early copy to deal with issue636
Without copies/renames, merges source names are 1:1 with their
targets. Copies and renames introduce the possibility that there will
be two merges with the same input but different output. By doing the
copy to the destination name before the merge, the actual merge
becomes 1:1 again, and no source is the input to two different merges.
- add a preliminary scan to applyupdates to do copies
- for the merge action, pass the old name (for finding ancestors) and
the new name (for input to the merge) to filemerge
- eliminate the old post-merge copy
- lookup file contents from new name in filemerge
- pass new name to external merge helper
- report merge failure at new name
- add a test
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Wed, 01 Aug 2007 12:33:12 -0500 |
parents | 60c54154ec4c |
children | bf444a9a9c23 8d9bdcbb2b18 |
comparison
equal
deleted
inserted
replaced
5029:ac97e065cfc7 | 5042:f191bc3916f7 |
---|---|
7 | 7 |
8 from node import * | 8 from node import * |
9 from i18n import _ | 9 from i18n import _ |
10 import errno, util, os, tempfile, context | 10 import errno, util, os, tempfile, context |
11 | 11 |
12 def filemerge(repo, fw, fo, wctx, mctx): | 12 def filemerge(repo, fw, fd, fo, wctx, mctx): |
13 """perform a 3-way merge in the working directory | 13 """perform a 3-way merge in the working directory |
14 | 14 |
15 fw = filename in the working directory | 15 fw = original filename in the working directory |
16 fd = destination filename in the working directory | |
16 fo = filename in other parent | 17 fo = filename in other parent |
17 wctx, mctx = working and merge changecontexts | 18 wctx, mctx = working and merge changecontexts |
18 """ | 19 """ |
19 | 20 |
20 def temp(prefix, ctx): | 21 def temp(prefix, ctx): |
25 f.write(data) | 26 f.write(data) |
26 f.close() | 27 f.close() |
27 return name | 28 return name |
28 | 29 |
29 fcm = wctx.filectx(fw) | 30 fcm = wctx.filectx(fw) |
31 fcmdata = wctx.filectx(fd).data() | |
30 fco = mctx.filectx(fo) | 32 fco = mctx.filectx(fo) |
31 | 33 |
32 if not fco.cmp(fcm.data()): # files identical? | 34 if not fco.cmp(fcmdata): # files identical? |
33 return None | 35 return None |
34 | 36 |
35 fca = fcm.ancestor(fco) | 37 fca = fcm.ancestor(fco) |
36 if not fca: | 38 if not fca: |
37 fca = repo.filectx(fw, fileid=nullrev) | 39 fca = repo.filectx(fw, fileid=nullrev) |
38 a = repo.wjoin(fw) | 40 a = repo.wjoin(fd) |
39 b = temp("base", fca) | 41 b = temp("base", fca) |
40 c = temp("other", fco) | 42 c = temp("other", fco) |
41 | 43 |
42 if fw != fo: | 44 if fw != fo: |
43 repo.ui.status(_("merging %s and %s\n") % (fw, fo)) | 45 repo.ui.status(_("merging %s and %s\n") % (fw, fo)) |
47 repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcm, fco, fca)) | 49 repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcm, fco, fca)) |
48 | 50 |
49 cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge") | 51 cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge") |
50 or "hgmerge") | 52 or "hgmerge") |
51 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root, | 53 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root, |
52 environ={'HG_FILE': fw, | 54 environ={'HG_FILE': fd, |
53 'HG_MY_NODE': str(wctx.parents()[0]), | 55 'HG_MY_NODE': str(wctx.parents()[0]), |
54 'HG_OTHER_NODE': str(mctx)}) | 56 'HG_OTHER_NODE': str(mctx)}) |
55 if r: | 57 if r: |
56 repo.ui.warn(_("merging %s failed!\n") % fw) | 58 repo.ui.warn(_("merging %s failed!\n") % fd) |
57 | 59 |
58 os.unlink(b) | 60 os.unlink(b) |
59 os.unlink(c) | 61 os.unlink(c) |
60 return r | 62 return r |
61 | 63 |
378 def applyupdates(repo, action, wctx, mctx): | 380 def applyupdates(repo, action, wctx, mctx): |
379 "apply the merge action list to the working directory" | 381 "apply the merge action list to the working directory" |
380 | 382 |
381 updated, merged, removed, unresolved = 0, 0, 0, 0 | 383 updated, merged, removed, unresolved = 0, 0, 0, 0 |
382 action.sort() | 384 action.sort() |
385 # prescan for copy/renames | |
386 for a in action: | |
387 f, m = a[:2] | |
388 if m == 'm': # merge | |
389 f2, fd, flags, move = a[2:] | |
390 if f != fd: | |
391 repo.ui.debug(_("copying %s to %s\n") % (f, fd)) | |
392 repo.wwrite(fd, repo.wread(f), flags) | |
393 | |
383 for a in action: | 394 for a in action: |
384 f, m = a[:2] | 395 f, m = a[:2] |
385 if f and f[0] == "/": | 396 if f and f[0] == "/": |
386 continue | 397 continue |
387 if m == "r": # remove | 398 if m == "r": # remove |
394 repo.ui.warn(_("update failed to remove %s: %s!\n") % | 405 repo.ui.warn(_("update failed to remove %s: %s!\n") % |
395 (f, inst.strerror)) | 406 (f, inst.strerror)) |
396 removed += 1 | 407 removed += 1 |
397 elif m == "m": # merge | 408 elif m == "m": # merge |
398 f2, fd, flags, move = a[2:] | 409 f2, fd, flags, move = a[2:] |
399 r = filemerge(repo, f, f2, wctx, mctx) | 410 r = filemerge(repo, f, fd, f2, wctx, mctx) |
400 if r > 0: | 411 if r > 0: |
401 unresolved += 1 | 412 unresolved += 1 |
402 else: | 413 else: |
403 if r is None: | 414 if r is None: |
404 updated += 1 | 415 updated += 1 |
405 else: | 416 else: |
406 merged += 1 | 417 merged += 1 |
407 if f != fd: | 418 if f != fd and move: |
408 repo.ui.debug(_("copying %s to %s\n") % (f, fd)) | 419 repo.ui.debug(_("removing %s\n") % f) |
409 repo.wwrite(fd, repo.wread(f), flags) | 420 os.unlink(repo.wjoin(f)) |
410 if move: | |
411 repo.ui.debug(_("removing %s\n") % f) | |
412 os.unlink(repo.wjoin(f)) | |
413 util.set_exec(repo.wjoin(fd), "x" in flags) | 421 util.set_exec(repo.wjoin(fd), "x" in flags) |
414 elif m == "g": # get | 422 elif m == "g": # get |
415 flags = a[2] | 423 flags = a[2] |
416 repo.ui.note(_("getting %s\n") % f) | 424 repo.ui.note(_("getting %s\n") % f) |
417 t = mctx.filectx(f).data() | 425 t = mctx.filectx(f).data() |