Mercurial > hg-stable
changeset 27951:6bce6d925e45 stable
merge: don't try to merge subrepos twice (issue4988)
In my patch series ending with rev 25e4b2f000c5 I switched most change/delete
conflicts to be handled at the resolve layer. .hgsubstate was the one file that
we weren't able to handle, so we kept the old code path around for it.
The old code path added .hgsubstate to one of the other lists as the user
specifies, including possibly the 'g' list.
Now since we did this check after converting the actions from being keyed by
file to being keyed by action type, there was nothing that actually removed
.hgsubstate from the 'cd' or 'dc' lists. This meant that the file would
eventually make its way into the 'mergeactions' list, now freshly augmented
with 'cd' and 'dc' actions.
We call subrepo.submerge for both 'g' actions and merge actions.
This means that if the resolution to an .hgsubstate change/delete conflict was
to add it to the 'g' list, subrepo.submerge would be called twice. It turns out
that this doesn't cause any adverse effects on Linux due to caching, but
apparently breaks on other operating systems including Windows.
The fix here moves this to before we convert the actions over. This ensures
that it .hgsubstate doesn't make its way into multiple lists.
The real fix here is going to be:
(1) move .hgsubstate conflict resolution into the resolve layer, and
(2) use a real data structure for the actions rather than shuffling data around
between lists and dictionaries: we need a hash (or prefix-based) index by
file and a list index by action type.
There's a very tiny behavior change here: collision detection on
case-insensitive systems will happen after this is resolved, not before. I think
this is the right change -- .hgsubstate could theoretically collide with other
files -- but in any case it makes no practical difference.
Thanks to Yuya Nishihara for investigating this.
author | Siddharth Agarwal <sid0@fb.com> |
---|---|
date | Fri, 29 Jan 2016 14:19:29 -0800 |
parents | f0d3c5794380 |
children | c5ffbd4c033b |
files | mercurial/merge.py |
diffstat | 1 files changed, 28 insertions(+), 27 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/merge.py Wed Jan 27 16:16:38 2016 -0600 +++ b/mercurial/merge.py Fri Jan 29 14:19:29 2016 -0800 @@ -1464,6 +1464,34 @@ actionbyfile, diverge, renamedelete = calculateupdates( repo, wc, p2, pas, branchmerge, force, mergeancestor, followcopies, matcher=matcher) + + # Prompt and create actions. Most of this is in the resolve phase + # already, but we can't handle .hgsubstate in filemerge or + # subrepo.submerge yet so we have to keep prompting for it. + if '.hgsubstate' in actionbyfile: + f = '.hgsubstate' + m, args, msg = actionbyfile[f] + if m == 'cd': + if repo.ui.promptchoice( + _("local changed %s which remote deleted\n" + "use (c)hanged version or (d)elete?" + "$$ &Changed $$ &Delete") % f, 0): + actionbyfile[f] = ('r', None, "prompt delete") + elif f in p1: + actionbyfile[f] = ('am', None, "prompt keep") + else: + actionbyfile[f] = ('a', None, "prompt keep") + elif m == 'dc': + f1, f2, fa, move, anc = args + flags = p2[f2].flags() + if repo.ui.promptchoice( + _("remote changed %s which local deleted\n" + "use (c)hanged version or leave (d)eleted?" + "$$ &Changed $$ &Deleted") % f, 0) == 0: + actionbyfile[f] = ('g', (flags, False), "prompt recreating") + else: + del actionbyfile[f] + # Convert to dictionary-of-lists format actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split()) for f, (m, args, msg) in actionbyfile.iteritems(): @@ -1479,33 +1507,6 @@ else: _checkcollision(repo, wc.manifest(), actions) - # Prompt and create actions. Most of this is in the resolve phase - # already, but we can't handle .hgsubstate in filemerge or - # subrepo.submerge yet so we have to keep prompting for it. - for f, args, msg in sorted(actions['cd']): - if f != '.hgsubstate': - continue - if repo.ui.promptchoice( - _("local changed %s which remote deleted\n" - "use (c)hanged version or (d)elete?" - "$$ &Changed $$ &Delete") % f, 0): - actions['r'].append((f, None, "prompt delete")) - elif f in p1: - actions['am'].append((f, None, "prompt keep")) - else: - actions['a'].append((f, None, "prompt keep")) - - for f, args, msg in sorted(actions['dc']): - if f != '.hgsubstate': - continue - f1, f2, fa, move, anc = args - flags = p2[f2].flags() - if repo.ui.promptchoice( - _("remote changed %s which local deleted\n" - "use (c)hanged version or leave (d)eleted?" - "$$ &Changed $$ &Deleted") % f, 0) == 0: - actions['g'].append((f, (flags, False), "prompt recreating")) - # divergent renames for f, fl in sorted(diverge.iteritems()): repo.ui.warn(_("note: possible conflict - %s was renamed "