Patchwork [rfc] merge: present merge part labels to user in prompts

login
register
mail settings
Submitter Simon Farnsworth
Date March 16, 2016, 11:59 p.m.
Message ID <85202d019e4f6d4093fd.1458172743@SimonFar-MacBookPro.local>
Download mbox | patch
Permalink /patch/13917/
State Changes Requested
Headers show

Comments

Simon Farnsworth - March 16, 2016, 11:59 p.m.
# HG changeset patch
# User Simon Farnsworth <simonfar@fb.com>
# Date 1458172710 0
#      Wed Mar 16 23:58:30 2016 +0000
# Node ID 85202d019e4f6d4093fdbb35b0aff105db1767b4
# Parent  70c2f8a982766b512e9d7f41f2d93fdb92f5481f
merge: present merge part labels to user in prompts

"local", "remote" and "other" are not always clear; we rename them in
conflict markers to try and clarify what's meant by each label. Present the
conflict marker names in the merge prompts, too.
Simon Farnsworth - March 17, 2016, midnight
I've sent this as an RFC because I'm not entirely convinced I've hit everywhere that should present merge labels, and I know that _getpromptsfromlabels is in the wrong place.

Simon




On 16/03/2016, 23:59, "Mercurial-devel on behalf of Simon Farnsworth" <mercurial-devel-bounces@mercurial-scm.org on behalf of simonfar@fb.com> wrote:

># HG changeset patch

># User Simon Farnsworth <simonfar@fb.com>

># Date 1458172710 0

>#      Wed Mar 16 23:58:30 2016 +0000

># Node ID 85202d019e4f6d4093fdbb35b0aff105db1767b4

># Parent  70c2f8a982766b512e9d7f41f2d93fdb92f5481f

>merge: present merge part labels to user in prompts

>

>"local", "remote" and "other" are not always clear; we rename them in

>conflict markers to try and clarify what's meant by each label. Present the

>conflict marker names in the merge prompts, too.

>

>diff --git a/mercurial/commands.py b/mercurial/commands.py

>--- a/mercurial/commands.py

>+++ b/mercurial/commands.py

>@@ -2896,6 +2896,14 @@

> 

>                 ui.write(('file extras: %s (%s)\n')

>                          % (filename, ', '.join(extrastrings)))

>+            elif rtype == 'l':

>+                labels = record.split('\0', 2)

>+                labels = [l for l in labels if len(l) > 0]

>+                ui.write(('labels:\n'))

>+                ui.write(('  local: %s\n' % labels[0]))

>+                ui.write(('  other: %s\n' % labels[1]))

>+                if len(labels) > 2:

>+                    ui.write(('  base:  %s\n' % labels[2]))

>             else:

>                 ui.write(('unrecognized entry: %s\t%s\n')

>                          % (rtype, record.replace('\0', '\t')))

>diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py

>--- a/mercurial/filemerge.py

>+++ b/mercurial/filemerge.py

>@@ -228,32 +228,51 @@

>             if newdata != data:

>                 util.writefile(file, newdata)

> 

>+def _getpromptsfromlabels(labels, fd):

>+    if labels is None:

>+        return {

>+            "local": "local",

>+            "other": "remote",

>+            "localact": "(l)ocal",

>+            "otheract": "(o)ther",

>+            "fd": fd,

>+        }

>+

>+    return {

>+        "local": "local [%s]" % labels[0],

>+        "other": "remote [%s]" % labels[1],

>+        "localact": "(l)ocal [%s]" % labels[0],

>+        "otheract": "(r)emote [%s]" % labels[1],

>+        "fd": fd,

>+    }

>+

> @internaltool('prompt', nomerge)

>-def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf):

>+def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):

>     """Asks the user which of the local or the other version to keep as

>     the merged version."""

>     ui = repo.ui

>     fd = fcd.path()

> 

>+    prompts = _getpromptsfromlabels(labels, fd)

>     try:

>         if fco.isabsent():

>             index = ui.promptchoice(

>-                _("local changed %s which remote deleted\n"

>+                _("%(local)s changed %(fd)s which %(other)s deleted\n"

>                   "use (c)hanged version, (d)elete, or leave (u)nresolved?"

>-                  "$$ &Changed $$ &Delete $$ &Unresolved") % fd, 2)

>+                  "$$ &Changed $$ &Delete $$ &Unresolved") % prompts, 2)

>             choice = ['local', 'other', 'unresolved'][index]

>         elif fcd.isabsent():

>             index = ui.promptchoice(

>-                _("remote changed %s which local deleted\n"

>+                _("%(other)s changed %(fd)s which %(local)s deleted\n"

>                   "use (c)hanged version, leave (d)eleted, or "

>                   "leave (u)nresolved?"

>-                  "$$ &Changed $$ &Deleted $$ &Unresolved") % fd, 2)

>+                  "$$ &Changed $$ &Deleted $$ &Unresolved") % prompts, 2)

>             choice = ['other', 'local', 'unresolved'][index]

>         else:

>             index = ui.promptchoice(

>-                _("no tool found to merge %s\n"

>-                  "keep (l)ocal, take (o)ther, or leave (u)nresolved?"

>-                  "$$ &Local $$ &Other $$ &Unresolved") % fd, 2)

>+                _("no tool found to merge %(fd)s\n"

>+                  "keep %(localact)s, take %(otheract)s, or leave (u)nresolved?"

>+                  "$$ &Local $$ &Other $$ &Unresolved") % prompts, 2)

>             choice = ['local', 'other', 'unresolved'][index]

> 

>         if choice == 'other':

>@@ -267,12 +286,12 @@

>         return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf)

> 

> @internaltool('local', nomerge)

>-def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf):

>+def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):

>     """Uses the local version of files as the merged version."""

>     return 0, fcd.isabsent()

> 

> @internaltool('other', nomerge)

>-def _iother(repo, mynode, orig, fcd, fco, fca, toolconf):

>+def _iother(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):

>     """Uses the other version of files as the merged version."""

>     if fco.isabsent():

>         # local changed, remote deleted -- 'deleted' picked

>@@ -284,7 +303,7 @@

>     return 0, deleted

> 

> @internaltool('fail', nomerge)

>-def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf):

>+def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):

>     """

>     Rather than attempting to merge files that were modified on both

>     branches, it marks them as unresolved. The resolve command must be

>@@ -587,7 +606,7 @@

>     toolconf = tool, toolpath, binary, symlink

> 

>     if mergetype == nomerge:

>-        r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf)

>+        r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf, labels)

>         return True, r, deleted

> 

>     if premerge:

>diff --git a/mercurial/merge.py b/mercurial/merge.py

>--- a/mercurial/merge.py

>+++ b/mercurial/merge.py

>@@ -68,6 +68,7 @@

>     f: a (filename, dictonary) tuple of optional values for a given file

>     X: unsupported mandatory record type (used in tests)

>     x: unsupported advisory record type (used in tests)

>+    l: the labels for the parts of the merge.

> 

>     Merge driver run states (experimental):

>     u: driver-resolved files unmarked -- needs to be run next time we're about

>@@ -80,11 +81,11 @@

>     statepathv2 = 'merge/state2'

> 

>     @staticmethod

>-    def clean(repo, node=None, other=None):

>+    def clean(repo, node=None, other=None, labels=None):

>         """Initialize a brand new merge state, removing any existing state on

>         disk."""

>         ms = mergestate(repo)

>-        ms.reset(node, other)

>+        ms.reset(node, other, labels)

>         return ms

> 

>     @staticmethod

>@@ -100,12 +101,14 @@

>         Do not use this directly! Instead call read() or clean()."""

>         self._repo = repo

>         self._dirty = False

>+        self._labels = None

> 

>-    def reset(self, node=None, other=None):

>+    def reset(self, node=None, other=None, labels=None):

>         self._state = {}

>         self._stateextras = {}

>         self._local = None

>         self._other = None

>+        self._labels = labels

>         for var in ('localctx', 'otherctx'):

>             if var in vars(self):

>                 delattr(self, var)

>@@ -165,6 +168,9 @@

>                     i += 2

> 

>                 self._stateextras[filename] = extras

>+            elif rtype == 'l':

>+                labels = record.split('\0', 2)

>+                self._labels = [l for l in labels if len(l) > 0]

>             elif not rtype.islower():

>                 unsupported.add(rtype)

>         self._results = {}

>@@ -353,6 +359,9 @@

>             rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in

>                                   extras.iteritems())

>             records.append(('f', '%s\0%s' % (filename, rawextras)))

>+        if self._labels is not None:

>+            labels = '\0'.join(self._labels)

>+            records.append(('l', labels))

>         return records

> 

>     def _writerecords(self, records):

>@@ -444,7 +453,7 @@

>     def extras(self, filename):

>         return self._stateextras.setdefault(filename, {})

> 

>-    def _resolve(self, preresolve, dfile, wctx, labels=None):

>+    def _resolve(self, preresolve, dfile, wctx):

>         """rerun merge process for file path `dfile`"""

>         if self[dfile] in 'rd':

>             return True, 0

>@@ -481,11 +490,11 @@

>                 self._repo.wvfs.unlinkpath(dfile, ignoremissing=True)

>             complete, r, deleted = filemerge.premerge(self._repo, self._local,

>                                                       lfile, fcd, fco, fca,

>-                                                      labels=labels)

>+                                                      labels=self._labels)

>         else:

>             complete, r, deleted = filemerge.filemerge(self._repo, self._local,

>                                                        lfile, fcd, fco, fca,

>-                                                       labels=labels)

>+                                                       labels=self._labels)

>         if r is None:

>             # no real conflict

>             del self._state[dfile]

>@@ -523,17 +532,17 @@

>         else:

>             return ctx[f]

> 

>-    def preresolve(self, dfile, wctx, labels=None):

>+    def preresolve(self, dfile, wctx):

>         """run premerge process for dfile

> 

>         Returns whether the merge is complete, and the exit code."""

>-        return self._resolve(True, dfile, wctx, labels=labels)

>+        return self._resolve(True, dfile, wctx)

> 

>-    def resolve(self, dfile, wctx, labels=None):

>+    def resolve(self, dfile, wctx):

>         """run merge process (assuming premerge was run) for dfile

> 

>         Returns the exit code of the merge."""

>-        return self._resolve(False, dfile, wctx, labels=labels)[1]

>+        return self._resolve(False, dfile, wctx)[1]

> 

>     def counts(self):

>         """return counts for updated, merged and removed files in this

>@@ -1094,7 +1103,7 @@

>     """

> 

>     updated, merged, removed = 0, 0, 0

>-    ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())

>+    ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)

>     moves = []

>     for m, l in actions.items():

>         l.sort()

>@@ -1247,7 +1256,7 @@

>                              overwrite)

>             continue

>         audit(f)

>-        complete, r = ms.preresolve(f, wctx, labels=labels)

>+        complete, r = ms.preresolve(f, wctx)

>         if not complete:

>             numupdates += 1

>             tocomplete.append((f, args, msg))

>@@ -1257,7 +1266,7 @@

>         repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))

>         z += 1

>         progress(_updating, z, item=f, total=numupdates, unit=_files)

>-        ms.resolve(f, wctx, labels=labels)

>+        ms.resolve(f, wctx)

> 

>     ms.commit()

> 

>@@ -1528,11 +1537,12 @@

>         if '.hgsubstate' in actionbyfile:

>             f = '.hgsubstate'

>             m, args, msg = actionbyfile[f]

>+            prompts = filemerge._getpromptsfromlabels(labels, f)

>             if m == 'cd':

>                 if repo.ui.promptchoice(

>-                    _("local changed %s which remote deleted\n"

>+                    _("%(local)s changed %(fd)s which %(other)s deleted\n"

>                       "use (c)hanged version or (d)elete?"

>-                      "$$ &Changed $$ &Delete") % f, 0):

>+                      "$$ &Changed $$ &Delete") % prompts, 0):

>                     actionbyfile[f] = ('r', None, "prompt delete")

>                 elif f in p1:

>                     actionbyfile[f] = ('am', None, "prompt keep")

>@@ -1542,9 +1552,9 @@

>                 f1, f2, fa, move, anc = args

>                 flags = p2[f2].flags()

>                 if repo.ui.promptchoice(

>-                    _("remote changed %s which local deleted\n"

>+                    _("%(other)s changed %(fd)s which %(local)s deleted\n"

>                       "use (c)hanged version or leave (d)eleted?"

>-                      "$$ &Changed $$ &Deleted") % f, 0) == 0:

>+                      "$$ &Changed $$ &Deleted") % prompts, 0) == 0:

>                     actionbyfile[f] = ('g', (flags, False), "prompt recreating")

>                 else:

>                     del actionbyfile[f]

>diff --git a/tests/test-copy-move-merge.t b/tests/test-copy-move-merge.t

>--- a/tests/test-copy-move-merge.t

>+++ b/tests/test-copy-move-merge.t

>@@ -85,7 +85,7 @@

>   > c

>   > EOF

>   rebasing 2:add3f11052fa "other" (tip)

>-  remote changed a which local deleted

>+  remote [source] changed a which local [dest] deleted

>   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c

> 

>   $ cat b

>diff --git a/tests/test-graft.t b/tests/test-graft.t

>--- a/tests/test-graft.t

>+++ b/tests/test-graft.t

>@@ -452,7 +452,7 @@

>   c

>   =======

>   b

>-  >>>>>>> other: 5d205f8b35b6  - bar: 1

>+  >>>>>>> graft: 5d205f8b35b6  - bar: 1

>   $ echo b > a

>   $ hg resolve -m a

>   (no more unresolved files)

>diff --git a/tests/test-histedit-non-commute-abort.t b/tests/test-histedit-non-commute-abort.t

>--- a/tests/test-histedit-non-commute-abort.t

>+++ b/tests/test-histedit-non-commute-abort.t

>@@ -86,6 +86,9 @@

>     local path: e (flags "")

>     ancestor path: e (node null)

>     other path: e (node 6b67ccefd5ce6de77e7ead4f5292843a0255329f)

>+  labels:

>+    local: local

>+    other: histedit

>   $ hg resolve -l

>   U e

> 

>@@ -100,6 +103,9 @@

>     local path: e (flags "")

>     ancestor path: e (node null)

>     other path: e (node 6b67ccefd5ce6de77e7ead4f5292843a0255329f)

>+  labels:

>+    local: local

>+    other: histedit

>   unrecognized entry: X	mandatory record

>   $ hg resolve -l

>   abort: unsupported merge state records: X

>diff --git a/tests/test-largefiles-update.t b/tests/test-largefiles-update.t

>--- a/tests/test-largefiles-update.t

>+++ b/tests/test-largefiles-update.t

>@@ -611,7 +611,7 @@

>   > EOF

>   rebasing 1:72518492caa6 "#1"

>   rebasing 4:07d6153b5c04 "#4"

>-  local changed .hglf/large1 which remote deleted

>+  local [dest] changed .hglf/large1 which remote [source] deleted

>   use (c)hanged version, (d)elete, or leave (u)nresolved? c

> 

>   $ hg diff -c "tip~1" --nodates .hglf/large1 | grep '^[+-][0-9a-z]'

>diff --git a/tests/test-merge-changedelete.t b/tests/test-merge-changedelete.t

>--- a/tests/test-merge-changedelete.t

>+++ b/tests/test-merge-changedelete.t

>@@ -717,9 +717,9 @@

>   $ echo changed >> file1

>   $ hg rm file2

>   $ hg update 1 -y

>-  local changed file1 which remote deleted

>+  local [working copy] changed file1 which remote [destination] deleted

>   use (c)hanged version, (d)elete, or leave (u)nresolved? u

>-  remote changed file2 which local deleted

>+  remote [destination] changed file2 which local [working copy] deleted

>   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u

>   1 files updated, 0 files merged, 0 files removed, 2 files unresolved

>   use 'hg resolve' to retry unresolved file merges

>@@ -746,6 +746,9 @@

>     local path: file2 (flags "")

>     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)

>     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)

>+  labels:

>+    local: working copy

>+    other: destination

>   --- file1 ---

>   1

>   changed

>@@ -786,6 +789,9 @@

>     local path: file2 (flags "")

>     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)

>     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)

>+  labels:

>+    local: working copy

>+    other: destination

>   --- file1 ---

>   1

>   changed

>@@ -824,6 +830,9 @@

>     local path: file2 (flags "")

>     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)

>     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)

>+  labels:

>+    local: working copy

>+    other: destination

>   *** file1 does not exist

>   --- file2 ---

>   2

>@@ -864,6 +873,9 @@

>     local path: file2 (flags "")

>     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)

>     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)

>+  labels:

>+    local: working copy

>+    other: destination

>   --- file1 ---

>   1

>   changed

>@@ -881,9 +893,9 @@

>   $ echo changed >> file1

>   $ hg rm file2

>   $ hg update 1 --config ui.interactive=True --tool :prompt

>-  local changed file1 which remote deleted

>+  local [working copy] changed file1 which remote [destination] deleted

>   use (c)hanged version, (d)elete, or leave (u)nresolved? 

>-  remote changed file2 which local deleted

>+  remote [destination] changed file2 which local [working copy] deleted

>   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 

>   1 files updated, 0 files merged, 0 files removed, 2 files unresolved

>   use 'hg resolve' to retry unresolved file merges

>@@ -910,6 +922,9 @@

>     local path: file2 (flags "")

>     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)

>     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)

>+  labels:

>+    local: working copy

>+    other: destination

>   --- file1 ---

>   1

>   changed

>@@ -928,9 +943,9 @@

>   $ echo changed >> file1

>   $ hg rm file2

>   $ hg update 1 --tool :merge3

>-  local changed file1 which remote deleted

>+  local [working copy] changed file1 which remote [destination] deleted

>   use (c)hanged version, (d)elete, or leave (u)nresolved? u

>-  remote changed file2 which local deleted

>+  remote [destination] changed file2 which local [working copy] deleted

>   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u

>   1 files updated, 0 files merged, 0 files removed, 2 files unresolved

>   use 'hg resolve' to retry unresolved file merges

>@@ -957,6 +972,9 @@

>     local path: file2 (flags "")

>     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)

>     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)

>+  labels:

>+    local: working copy

>+    other: destination

>   --- file1 ---

>   1

>   changed

>@@ -981,9 +999,9 @@

>   (status identical)

>   

>   === :other -> :prompt ===

>-  local changed file1 which remote deleted

>+  local [working copy] changed file1 which remote [destination] deleted

>   use (c)hanged version, (d)elete, or leave (u)nresolved? 

>-  remote changed file2 which local deleted

>+  remote [destination] changed file2 which local [working copy] deleted

>   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 

>   --- diff of status ---

>   (status identical)

>@@ -1008,9 +1026,9 @@

>   (status identical)

>   

>   === :local -> :prompt ===

>-  local changed file1 which remote deleted

>+  local [working copy] changed file1 which remote [destination] deleted

>   use (c)hanged version, (d)elete, or leave (u)nresolved? 

>-  remote changed file2 which local deleted

>+  remote [destination] changed file2 which local [working copy] deleted

>   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 

>   --- diff of status ---

>   (status identical)

>@@ -1025,9 +1043,9 @@

>   (status identical)

>   

>   === :fail -> :prompt ===

>-  local changed file1 which remote deleted

>+  local [working copy] changed file1 which remote [destination] deleted

>   use (c)hanged version, (d)elete, or leave (u)nresolved? 

>-  remote changed file2 which local deleted

>+  remote [destination] changed file2 which local [working copy] deleted

>   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 

>   --- diff of status ---

>   (status identical)

>diff --git a/tests/test-merge-types.t b/tests/test-merge-types.t

>--- a/tests/test-merge-types.t

>+++ b/tests/test-merge-types.t

>@@ -173,7 +173,7 @@

>   (couldn't find merge tool hgmerge|tool hgmerge can't handle symlinks) (re)

>   picked tool ':prompt' for a (binary False symlink True changedelete False)

>   no tool found to merge a

>-  keep (l)ocal, take (o)ther, or leave (u)nresolved? u

>+  keep (l)ocal [working copy], take (r)emote [destination], or leave (u)nresolved? u

>   0 files updated, 0 files merged, 0 files removed, 1 files unresolved

>   use 'hg resolve' to retry unresolved file merges

>   1 other heads for branch "default"

>diff --git a/tests/test-rebase-abort.t b/tests/test-rebase-abort.t

>--- a/tests/test-rebase-abort.t

>+++ b/tests/test-rebase-abort.t

>@@ -81,6 +81,9 @@

>     local path: common (flags "")

>     ancestor path: common (node de0a666fdd9c1a0b0698b90d85064d8bd34f74b6)

>     other path: common (node 2f6411de53677f6f1048fef5bf888d67a342e0a5)

>+  labels:

>+    local: dest

>+    other: source

>   $ hg resolve -l

>   U common

> 

>@@ -96,6 +99,9 @@

>     local path: common (flags "")

>     ancestor path: common (node de0a666fdd9c1a0b0698b90d85064d8bd34f74b6)

>     other path: common (node 2f6411de53677f6f1048fef5bf888d67a342e0a5)

>+  labels:

>+    local: dest

>+    other: source

>   unrecognized entry: X	mandatory record

>   $ hg resolve -l

>   abort: unsupported merge state records: X

>diff --git a/tests/test-rebase-newancestor.t b/tests/test-rebase-newancestor.t

>--- a/tests/test-rebase-newancestor.t

>+++ b/tests/test-rebase-newancestor.t

>@@ -135,7 +135,7 @@

>   note: rebase of 1:1d1a643d390e created no changes to commit

>   rebasing 2:ec2c14fb2984 "dev: f-dev stuff"

>   rebasing 4:4b019212aaf6 "dev: merge default"

>-  remote changed f-default which local deleted

>+  remote [source] changed f-default which local [dest] deleted

>   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c

>   rebasing 6:9455ee510502 "dev: merge default"

>   saved backup bundle to $TESTTMP/ancestor-merge/.hg/strip-backup/1d1a643d390e-43e9e04b-backup.hg (glob)

>@@ -164,7 +164,7 @@

>   > EOF

>   rebasing 2:ec2c14fb2984 "dev: f-dev stuff"

>   rebasing 4:4b019212aaf6 "dev: merge default"

>-  remote changed f-default which local deleted

>+  remote [source] changed f-default which local [dest] deleted

>   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c

>   rebasing 6:9455ee510502 "dev: merge default"

>   saved backup bundle to $TESTTMP/ancestor-merge-2/.hg/strip-backup/ec2c14fb2984-62d0b222-backup.hg (glob)

>diff --git a/tests/test-subrepo-missing.t b/tests/test-subrepo-missing.t

>--- a/tests/test-subrepo-missing.t

>+++ b/tests/test-subrepo-missing.t

>@@ -62,7 +62,7 @@

>   2 files updated, 0 files merged, 0 files removed, 0 files unresolved

>   $ rm .hgsubstate

>   $ hg up 0

>-  remote changed .hgsubstate which local deleted

>+  remote [destination] changed .hgsubstate which local [working copy] deleted

>   use (c)hanged version or leave (d)eleted? c

>   1 files updated, 0 files merged, 0 files removed, 0 files unresolved

>   $ hg st

>_______________________________________________

>Mercurial-devel mailing list

>Mercurial-devel@mercurial-scm.org

>https://urldefense.proofpoint.com/v2/url?u=https-3A__www.mercurial-2Dscm.org_mailman_listinfo_mercurial-2Ddevel&d=CwIGaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=mEgSWILcY4c4W3zjApBQLA&m=OubE9-bqhfPq69y0Pqzhi8jCI4TieZ8tqNftDIY2UfA&s=-_z1ru5q8NgtOJfKdPmqJeAV5lSBCkMfVOElVP9bhF4&e=
Sean Farley - March 17, 2016, 12:12 a.m.
Simon Farnsworth <simonfar@fb.com> writes:

> I've sent this as an RFC because I'm not entirely convinced I've hit everywhere that should present merge labels, and I know that _getpromptsfromlabels is in the wrong place.

I like the direction of this a lot (I get confused by rebase all the
time!). Could you show an example of this at the sprint with fancy
markers and maybe how mergetools might change?
Pierre-Yves David - March 17, 2016, 1:30 a.m.
On 03/16/2016 04:59 PM, Simon Farnsworth wrote:
> # HG changeset patch
> # User Simon Farnsworth <simonfar@fb.com>
> # Date 1458172710 0
> #      Wed Mar 16 23:58:30 2016 +0000
> # Node ID 85202d019e4f6d4093fdbb35b0aff105db1767b4
> # Parent  70c2f8a982766b512e9d7f41f2d93fdb92f5481f
> merge: present merge part labels to user in prompts
>
> "local", "remote" and "other" are not always clear; we rename them in
> conflict markers to try and clarify what's meant by each label. Present the
> conflict marker names in the merge prompts, too.

I did a very very fast pass through this here is a couple of high level 
feedback:

- It is usually good to provide example of the output change you do in 
the commit description. This help people getting and idea of what is the 
change doing without digging down to the tests,

- This changesets seems to be doingg multiple things:
   - use labels for changed/deleted prompt
   - adding the labels to the mergestate file (and support in 
debugmergestate)
   - reused the label in the merge state when using `hg resolve`

   You should probably slice this out in multiple patch. to make each 
patch simpler and clearer o review.

- As we are at changing this prompt message should we get ride of the 
"remote" terms (as we already have local/other). (there might be good 
reason not to)

- There might be a lot of file in a merge state, I would move the 
'labels:' section of debugmergestate before them

- I'm not sure about the '[...]' do we use that anywhere? why not ()?

- Do we alway have only two labels? We never allow to change the "base" 
label?

Cheers,
Simon Farnsworth - March 18, 2016, 11:46 p.m.
On 16/03/2016, 18:30, "Pierre-Yves David" <pierre-yves.david@ens-lyon.org> wrote:


>On 03/16/2016 04:59 PM, Simon Farnsworth wrote:

>> # HG changeset patch

>> # User Simon Farnsworth <simonfar@fb.com>

>> # Date 1458172710 0

>> #      Wed Mar 16 23:58:30 2016 +0000

>> # Node ID 85202d019e4f6d4093fdbb35b0aff105db1767b4

>> # Parent  70c2f8a982766b512e9d7f41f2d93fdb92f5481f

>> merge: present merge part labels to user in prompts

>>

>> "local", "remote" and "other" are not always clear; we rename them in

>> conflict markers to try and clarify what's meant by each label. Present the

>> conflict marker names in the merge prompts, too.

>

>I did a very very fast pass through this here is a couple of high level 

>feedback:

>

>- It is usually good to provide example of the output change you do in 

>the commit description. This help people getting and idea of what is the 

>change doing without digging down to the tests,


Will do.

>

>- This changesets seems to be doingg multiple things:

>   - use labels for changed/deleted prompt

>   - adding the labels to the mergestate file (and support in 

>debugmergestate)

>   - reused the label in the merge state when using `hg resolve`

>

>   You should probably slice this out in multiple patch. to make each 

>patch simpler and clearer o review.


Will do. The single patch thing is a consequence of my personal workflow (build the feature, then split it into sensible parts).

>

>- As we are at changing this prompt message should we get ride of the 

>"remote" terms (as we already have local/other). (there might be good 

>reason not to)


Digging through the history, it looks like "remote" was another attempt to be friendlier than "other". I think we should consistently use local/other, and rely on commands using friendly labels.

Will get rid of "remote".

>

>- There might be a lot of file in a merge state, I would move the 

>'labels:' section of debugmergestate before them


Done.

>

>- I'm not sure about the '[...]' do we use that anywhere? why not ()?


I'm not committed to [...]; the reason I'm using it is that we use (l)ocal to tell you that "l" is an acceptable response to a prompt; I don't want someone surprised when they see "(l)ocal (dest)", type in "dest" at the prompt, and get a bad answer.

>

>- Do we alway have only two labels? We never allow to change the "base" 

>label?


We do permit changes to the base label, AFAICT, we just don't use it outside of conflict markers. This is like the option to have different bases for each file in conflict; it's allowed by the code, we just don't use it.

As we never prompt with the "base" label, I don't use it, but I do preserve it if present, in case future changes introduce a way to use "base".

Simon
Pierre-Yves David - March 18, 2016, 11:50 p.m.
On 03/18/2016 04:46 PM, Simon Farnsworth wrote:
> On 16/03/2016, 18:30, "Pierre-Yves David" <pierre-yves.david@ens-lyon.org> wrote:
>
>
>> On 03/16/2016 04:59 PM, Simon Farnsworth wrote:
>>> # HG changeset patch
>>> # User Simon Farnsworth <simonfar@fb.com>
>>> # Date 1458172710 0
>>> #      Wed Mar 16 23:58:30 2016 +0000
>>> # Node ID 85202d019e4f6d4093fdbb35b0aff105db1767b4
>>> # Parent  70c2f8a982766b512e9d7f41f2d93fdb92f5481f
>>> merge: present merge part labels to user in prompts
>>>
>>> "local", "remote" and "other" are not always clear; we rename them in
>>> conflict markers to try and clarify what's meant by each label. Present the
>>> conflict marker names in the merge prompts, too.
>>
>> I did a very very fast pass through this here is a couple of high level
>> feedback:
>>
>> - It is usually good to provide example of the output change you do in
>> the commit description. This help people getting and idea of what is the
>> change doing without digging down to the tests,
>
> Will do.
>
>>
>> - This changesets seems to be doingg multiple things:
>>    - use labels for changed/deleted prompt
>>    - adding the labels to the mergestate file (and support in
>> debugmergestate)
>>    - reused the label in the merge state when using `hg resolve`
>>
>>    You should probably slice this out in multiple patch. to make each
>> patch simpler and clearer o review.
>
> Will do. The single patch thing is a consequence of my personal workflow (build the feature, then split it into sensible parts).
>
>>
>> - As we are at changing this prompt message should we get ride of the
>> "remote" terms (as we already have local/other). (there might be good
>> reason not to)
>
> Digging through the history, it looks like "remote" was another attempt to be friendlier than "other". I think we should consistently use local/other, and rely on commands using friendly labels.
>
> Will get rid of "remote".
>
>>
>> - There might be a lot of file in a merge state, I would move the
>> 'labels:' section of debugmergestate before them
>
> Done.
>
>>
>> - I'm not sure about the '[...]' do we use that anywhere? why not ()?
>
> I'm not committed to [...]; the reason I'm using it is that we use (l)ocal to tell you that "l" is an acceptable response to a prompt; I don't want someone surprised when they see "(l)ocal (dest)", type in "dest" at the prompt, and get a bad answer.
>
>>
>> - Do we alway have only two labels? We never allow to change the "base"
>> label?
>
> We do permit changes to the base label, AFAICT, we just don't use it outside of conflict markers. This is like the option to have different bases for each file in conflict; it's allowed by the code, we just don't use it.
>
> As we never prompt with the "base" label, I don't use it, but I do preserve it if present, in case future changes introduce a way to use "base".

But you also use this preservation to regenerate conflict marker in the 
`hg resolve` case, right? So we should store it.
Simon Farnsworth - March 19, 2016, 12:09 a.m.
On 18/03/2016, 16:50, "Pierre-Yves David" <pierre-yves.david@ens-lyon.org> wrote:


>On 03/18/2016 04:46 PM, Simon Farnsworth wrote:

>> On 16/03/2016, 18:30, "Pierre-Yves David" <pierre-yves.david@ens-lyon.org> wrote:

<snip>
>>> - Do we alway have only two labels? We never allow to change the "base"

>>> label?

>>

>> We do permit changes to the base label, AFAICT, we just don't use it outside of conflict markers. This is like the option to have different bases for each file in conflict; it's allowed by the code, we just don't use it.

>>

>> As we never prompt with the "base" label, I don't use it, but I do preserve it if present, in case future changes introduce a way to use "base".

>

>But you also use this preservation to regenerate conflict marker in the 

>`hg resolve` case, right? So we should store it.


True; I've never used a plain 'hg resolve', so I'd not noticed its ability to remerge - my instinct was just to save everything, and thus I wouldn't have broken this use case.

Simon

Patch

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2896,6 +2896,14 @@ 
 
                 ui.write(('file extras: %s (%s)\n')
                          % (filename, ', '.join(extrastrings)))
+            elif rtype == 'l':
+                labels = record.split('\0', 2)
+                labels = [l for l in labels if len(l) > 0]
+                ui.write(('labels:\n'))
+                ui.write(('  local: %s\n' % labels[0]))
+                ui.write(('  other: %s\n' % labels[1]))
+                if len(labels) > 2:
+                    ui.write(('  base:  %s\n' % labels[2]))
             else:
                 ui.write(('unrecognized entry: %s\t%s\n')
                          % (rtype, record.replace('\0', '\t')))
diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py
--- a/mercurial/filemerge.py
+++ b/mercurial/filemerge.py
@@ -228,32 +228,51 @@ 
             if newdata != data:
                 util.writefile(file, newdata)
 
+def _getpromptsfromlabels(labels, fd):
+    if labels is None:
+        return {
+            "local": "local",
+            "other": "remote",
+            "localact": "(l)ocal",
+            "otheract": "(o)ther",
+            "fd": fd,
+        }
+
+    return {
+        "local": "local [%s]" % labels[0],
+        "other": "remote [%s]" % labels[1],
+        "localact": "(l)ocal [%s]" % labels[0],
+        "otheract": "(r)emote [%s]" % labels[1],
+        "fd": fd,
+    }
+
 @internaltool('prompt', nomerge)
-def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf):
+def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
     """Asks the user which of the local or the other version to keep as
     the merged version."""
     ui = repo.ui
     fd = fcd.path()
 
+    prompts = _getpromptsfromlabels(labels, fd)
     try:
         if fco.isabsent():
             index = ui.promptchoice(
-                _("local changed %s which remote deleted\n"
+                _("%(local)s changed %(fd)s which %(other)s deleted\n"
                   "use (c)hanged version, (d)elete, or leave (u)nresolved?"
-                  "$$ &Changed $$ &Delete $$ &Unresolved") % fd, 2)
+                  "$$ &Changed $$ &Delete $$ &Unresolved") % prompts, 2)
             choice = ['local', 'other', 'unresolved'][index]
         elif fcd.isabsent():
             index = ui.promptchoice(
-                _("remote changed %s which local deleted\n"
+                _("%(other)s changed %(fd)s which %(local)s deleted\n"
                   "use (c)hanged version, leave (d)eleted, or "
                   "leave (u)nresolved?"
-                  "$$ &Changed $$ &Deleted $$ &Unresolved") % fd, 2)
+                  "$$ &Changed $$ &Deleted $$ &Unresolved") % prompts, 2)
             choice = ['other', 'local', 'unresolved'][index]
         else:
             index = ui.promptchoice(
-                _("no tool found to merge %s\n"
-                  "keep (l)ocal, take (o)ther, or leave (u)nresolved?"
-                  "$$ &Local $$ &Other $$ &Unresolved") % fd, 2)
+                _("no tool found to merge %(fd)s\n"
+                  "keep %(localact)s, take %(otheract)s, or leave (u)nresolved?"
+                  "$$ &Local $$ &Other $$ &Unresolved") % prompts, 2)
             choice = ['local', 'other', 'unresolved'][index]
 
         if choice == 'other':
@@ -267,12 +286,12 @@ 
         return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf)
 
 @internaltool('local', nomerge)
-def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf):
+def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
     """Uses the local version of files as the merged version."""
     return 0, fcd.isabsent()
 
 @internaltool('other', nomerge)
-def _iother(repo, mynode, orig, fcd, fco, fca, toolconf):
+def _iother(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
     """Uses the other version of files as the merged version."""
     if fco.isabsent():
         # local changed, remote deleted -- 'deleted' picked
@@ -284,7 +303,7 @@ 
     return 0, deleted
 
 @internaltool('fail', nomerge)
-def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf):
+def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
     """
     Rather than attempting to merge files that were modified on both
     branches, it marks them as unresolved. The resolve command must be
@@ -587,7 +606,7 @@ 
     toolconf = tool, toolpath, binary, symlink
 
     if mergetype == nomerge:
-        r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf)
+        r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf, labels)
         return True, r, deleted
 
     if premerge:
diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -68,6 +68,7 @@ 
     f: a (filename, dictonary) tuple of optional values for a given file
     X: unsupported mandatory record type (used in tests)
     x: unsupported advisory record type (used in tests)
+    l: the labels for the parts of the merge.
 
     Merge driver run states (experimental):
     u: driver-resolved files unmarked -- needs to be run next time we're about
@@ -80,11 +81,11 @@ 
     statepathv2 = 'merge/state2'
 
     @staticmethod
-    def clean(repo, node=None, other=None):
+    def clean(repo, node=None, other=None, labels=None):
         """Initialize a brand new merge state, removing any existing state on
         disk."""
         ms = mergestate(repo)
-        ms.reset(node, other)
+        ms.reset(node, other, labels)
         return ms
 
     @staticmethod
@@ -100,12 +101,14 @@ 
         Do not use this directly! Instead call read() or clean()."""
         self._repo = repo
         self._dirty = False
+        self._labels = None
 
-    def reset(self, node=None, other=None):
+    def reset(self, node=None, other=None, labels=None):
         self._state = {}
         self._stateextras = {}
         self._local = None
         self._other = None
+        self._labels = labels
         for var in ('localctx', 'otherctx'):
             if var in vars(self):
                 delattr(self, var)
@@ -165,6 +168,9 @@ 
                     i += 2
 
                 self._stateextras[filename] = extras
+            elif rtype == 'l':
+                labels = record.split('\0', 2)
+                self._labels = [l for l in labels if len(l) > 0]
             elif not rtype.islower():
                 unsupported.add(rtype)
         self._results = {}
@@ -353,6 +359,9 @@ 
             rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in
                                   extras.iteritems())
             records.append(('f', '%s\0%s' % (filename, rawextras)))
+        if self._labels is not None:
+            labels = '\0'.join(self._labels)
+            records.append(('l', labels))
         return records
 
     def _writerecords(self, records):
@@ -444,7 +453,7 @@ 
     def extras(self, filename):
         return self._stateextras.setdefault(filename, {})
 
-    def _resolve(self, preresolve, dfile, wctx, labels=None):
+    def _resolve(self, preresolve, dfile, wctx):
         """rerun merge process for file path `dfile`"""
         if self[dfile] in 'rd':
             return True, 0
@@ -481,11 +490,11 @@ 
                 self._repo.wvfs.unlinkpath(dfile, ignoremissing=True)
             complete, r, deleted = filemerge.premerge(self._repo, self._local,
                                                       lfile, fcd, fco, fca,
-                                                      labels=labels)
+                                                      labels=self._labels)
         else:
             complete, r, deleted = filemerge.filemerge(self._repo, self._local,
                                                        lfile, fcd, fco, fca,
-                                                       labels=labels)
+                                                       labels=self._labels)
         if r is None:
             # no real conflict
             del self._state[dfile]
@@ -523,17 +532,17 @@ 
         else:
             return ctx[f]
 
-    def preresolve(self, dfile, wctx, labels=None):
+    def preresolve(self, dfile, wctx):
         """run premerge process for dfile
 
         Returns whether the merge is complete, and the exit code."""
-        return self._resolve(True, dfile, wctx, labels=labels)
+        return self._resolve(True, dfile, wctx)
 
-    def resolve(self, dfile, wctx, labels=None):
+    def resolve(self, dfile, wctx):
         """run merge process (assuming premerge was run) for dfile
 
         Returns the exit code of the merge."""
-        return self._resolve(False, dfile, wctx, labels=labels)[1]
+        return self._resolve(False, dfile, wctx)[1]
 
     def counts(self):
         """return counts for updated, merged and removed files in this
@@ -1094,7 +1103,7 @@ 
     """
 
     updated, merged, removed = 0, 0, 0
-    ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
+    ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
     moves = []
     for m, l in actions.items():
         l.sort()
@@ -1247,7 +1256,7 @@ 
                              overwrite)
             continue
         audit(f)
-        complete, r = ms.preresolve(f, wctx, labels=labels)
+        complete, r = ms.preresolve(f, wctx)
         if not complete:
             numupdates += 1
             tocomplete.append((f, args, msg))
@@ -1257,7 +1266,7 @@ 
         repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
         z += 1
         progress(_updating, z, item=f, total=numupdates, unit=_files)
-        ms.resolve(f, wctx, labels=labels)
+        ms.resolve(f, wctx)
 
     ms.commit()
 
@@ -1528,11 +1537,12 @@ 
         if '.hgsubstate' in actionbyfile:
             f = '.hgsubstate'
             m, args, msg = actionbyfile[f]
+            prompts = filemerge._getpromptsfromlabels(labels, f)
             if m == 'cd':
                 if repo.ui.promptchoice(
-                    _("local changed %s which remote deleted\n"
+                    _("%(local)s changed %(fd)s which %(other)s deleted\n"
                       "use (c)hanged version or (d)elete?"
-                      "$$ &Changed $$ &Delete") % f, 0):
+                      "$$ &Changed $$ &Delete") % prompts, 0):
                     actionbyfile[f] = ('r', None, "prompt delete")
                 elif f in p1:
                     actionbyfile[f] = ('am', None, "prompt keep")
@@ -1542,9 +1552,9 @@ 
                 f1, f2, fa, move, anc = args
                 flags = p2[f2].flags()
                 if repo.ui.promptchoice(
-                    _("remote changed %s which local deleted\n"
+                    _("%(other)s changed %(fd)s which %(local)s deleted\n"
                       "use (c)hanged version or leave (d)eleted?"
-                      "$$ &Changed $$ &Deleted") % f, 0) == 0:
+                      "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
                     actionbyfile[f] = ('g', (flags, False), "prompt recreating")
                 else:
                     del actionbyfile[f]
diff --git a/tests/test-copy-move-merge.t b/tests/test-copy-move-merge.t
--- a/tests/test-copy-move-merge.t
+++ b/tests/test-copy-move-merge.t
@@ -85,7 +85,7 @@ 
   > c
   > EOF
   rebasing 2:add3f11052fa "other" (tip)
-  remote changed a which local deleted
+  remote [source] changed a which local [dest] deleted
   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
 
   $ cat b
diff --git a/tests/test-graft.t b/tests/test-graft.t
--- a/tests/test-graft.t
+++ b/tests/test-graft.t
@@ -452,7 +452,7 @@ 
   c
   =======
   b
-  >>>>>>> other: 5d205f8b35b6  - bar: 1
+  >>>>>>> graft: 5d205f8b35b6  - bar: 1
   $ echo b > a
   $ hg resolve -m a
   (no more unresolved files)
diff --git a/tests/test-histedit-non-commute-abort.t b/tests/test-histedit-non-commute-abort.t
--- a/tests/test-histedit-non-commute-abort.t
+++ b/tests/test-histedit-non-commute-abort.t
@@ -86,6 +86,9 @@ 
     local path: e (flags "")
     ancestor path: e (node null)
     other path: e (node 6b67ccefd5ce6de77e7ead4f5292843a0255329f)
+  labels:
+    local: local
+    other: histedit
   $ hg resolve -l
   U e
 
@@ -100,6 +103,9 @@ 
     local path: e (flags "")
     ancestor path: e (node null)
     other path: e (node 6b67ccefd5ce6de77e7ead4f5292843a0255329f)
+  labels:
+    local: local
+    other: histedit
   unrecognized entry: X	mandatory record
   $ hg resolve -l
   abort: unsupported merge state records: X
diff --git a/tests/test-largefiles-update.t b/tests/test-largefiles-update.t
--- a/tests/test-largefiles-update.t
+++ b/tests/test-largefiles-update.t
@@ -611,7 +611,7 @@ 
   > EOF
   rebasing 1:72518492caa6 "#1"
   rebasing 4:07d6153b5c04 "#4"
-  local changed .hglf/large1 which remote deleted
+  local [dest] changed .hglf/large1 which remote [source] deleted
   use (c)hanged version, (d)elete, or leave (u)nresolved? c
 
   $ hg diff -c "tip~1" --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
diff --git a/tests/test-merge-changedelete.t b/tests/test-merge-changedelete.t
--- a/tests/test-merge-changedelete.t
+++ b/tests/test-merge-changedelete.t
@@ -717,9 +717,9 @@ 
   $ echo changed >> file1
   $ hg rm file2
   $ hg update 1 -y
-  local changed file1 which remote deleted
+  local [working copy] changed file1 which remote [destination] deleted
   use (c)hanged version, (d)elete, or leave (u)nresolved? u
-  remote changed file2 which local deleted
+  remote [destination] changed file2 which local [working copy] deleted
   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u
   1 files updated, 0 files merged, 0 files removed, 2 files unresolved
   use 'hg resolve' to retry unresolved file merges
@@ -746,6 +746,9 @@ 
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  labels:
+    local: working copy
+    other: destination
   --- file1 ---
   1
   changed
@@ -786,6 +789,9 @@ 
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  labels:
+    local: working copy
+    other: destination
   --- file1 ---
   1
   changed
@@ -824,6 +830,9 @@ 
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  labels:
+    local: working copy
+    other: destination
   *** file1 does not exist
   --- file2 ---
   2
@@ -864,6 +873,9 @@ 
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  labels:
+    local: working copy
+    other: destination
   --- file1 ---
   1
   changed
@@ -881,9 +893,9 @@ 
   $ echo changed >> file1
   $ hg rm file2
   $ hg update 1 --config ui.interactive=True --tool :prompt
-  local changed file1 which remote deleted
+  local [working copy] changed file1 which remote [destination] deleted
   use (c)hanged version, (d)elete, or leave (u)nresolved? 
-  remote changed file2 which local deleted
+  remote [destination] changed file2 which local [working copy] deleted
   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 
   1 files updated, 0 files merged, 0 files removed, 2 files unresolved
   use 'hg resolve' to retry unresolved file merges
@@ -910,6 +922,9 @@ 
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  labels:
+    local: working copy
+    other: destination
   --- file1 ---
   1
   changed
@@ -928,9 +943,9 @@ 
   $ echo changed >> file1
   $ hg rm file2
   $ hg update 1 --tool :merge3
-  local changed file1 which remote deleted
+  local [working copy] changed file1 which remote [destination] deleted
   use (c)hanged version, (d)elete, or leave (u)nresolved? u
-  remote changed file2 which local deleted
+  remote [destination] changed file2 which local [working copy] deleted
   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u
   1 files updated, 0 files merged, 0 files removed, 2 files unresolved
   use 'hg resolve' to retry unresolved file merges
@@ -957,6 +972,9 @@ 
     local path: file2 (flags "")
     ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
     other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  labels:
+    local: working copy
+    other: destination
   --- file1 ---
   1
   changed
@@ -981,9 +999,9 @@ 
   (status identical)
   
   === :other -> :prompt ===
-  local changed file1 which remote deleted
+  local [working copy] changed file1 which remote [destination] deleted
   use (c)hanged version, (d)elete, or leave (u)nresolved? 
-  remote changed file2 which local deleted
+  remote [destination] changed file2 which local [working copy] deleted
   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 
   --- diff of status ---
   (status identical)
@@ -1008,9 +1026,9 @@ 
   (status identical)
   
   === :local -> :prompt ===
-  local changed file1 which remote deleted
+  local [working copy] changed file1 which remote [destination] deleted
   use (c)hanged version, (d)elete, or leave (u)nresolved? 
-  remote changed file2 which local deleted
+  remote [destination] changed file2 which local [working copy] deleted
   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 
   --- diff of status ---
   (status identical)
@@ -1025,9 +1043,9 @@ 
   (status identical)
   
   === :fail -> :prompt ===
-  local changed file1 which remote deleted
+  local [working copy] changed file1 which remote [destination] deleted
   use (c)hanged version, (d)elete, or leave (u)nresolved? 
-  remote changed file2 which local deleted
+  remote [destination] changed file2 which local [working copy] deleted
   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 
   --- diff of status ---
   (status identical)
diff --git a/tests/test-merge-types.t b/tests/test-merge-types.t
--- a/tests/test-merge-types.t
+++ b/tests/test-merge-types.t
@@ -173,7 +173,7 @@ 
   (couldn't find merge tool hgmerge|tool hgmerge can't handle symlinks) (re)
   picked tool ':prompt' for a (binary False symlink True changedelete False)
   no tool found to merge a
-  keep (l)ocal, take (o)ther, or leave (u)nresolved? u
+  keep (l)ocal [working copy], take (r)emote [destination], or leave (u)nresolved? u
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
   use 'hg resolve' to retry unresolved file merges
   1 other heads for branch "default"
diff --git a/tests/test-rebase-abort.t b/tests/test-rebase-abort.t
--- a/tests/test-rebase-abort.t
+++ b/tests/test-rebase-abort.t
@@ -81,6 +81,9 @@ 
     local path: common (flags "")
     ancestor path: common (node de0a666fdd9c1a0b0698b90d85064d8bd34f74b6)
     other path: common (node 2f6411de53677f6f1048fef5bf888d67a342e0a5)
+  labels:
+    local: dest
+    other: source
   $ hg resolve -l
   U common
 
@@ -96,6 +99,9 @@ 
     local path: common (flags "")
     ancestor path: common (node de0a666fdd9c1a0b0698b90d85064d8bd34f74b6)
     other path: common (node 2f6411de53677f6f1048fef5bf888d67a342e0a5)
+  labels:
+    local: dest
+    other: source
   unrecognized entry: X	mandatory record
   $ hg resolve -l
   abort: unsupported merge state records: X
diff --git a/tests/test-rebase-newancestor.t b/tests/test-rebase-newancestor.t
--- a/tests/test-rebase-newancestor.t
+++ b/tests/test-rebase-newancestor.t
@@ -135,7 +135,7 @@ 
   note: rebase of 1:1d1a643d390e created no changes to commit
   rebasing 2:ec2c14fb2984 "dev: f-dev stuff"
   rebasing 4:4b019212aaf6 "dev: merge default"
-  remote changed f-default which local deleted
+  remote [source] changed f-default which local [dest] deleted
   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   rebasing 6:9455ee510502 "dev: merge default"
   saved backup bundle to $TESTTMP/ancestor-merge/.hg/strip-backup/1d1a643d390e-43e9e04b-backup.hg (glob)
@@ -164,7 +164,7 @@ 
   > EOF
   rebasing 2:ec2c14fb2984 "dev: f-dev stuff"
   rebasing 4:4b019212aaf6 "dev: merge default"
-  remote changed f-default which local deleted
+  remote [source] changed f-default which local [dest] deleted
   use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c
   rebasing 6:9455ee510502 "dev: merge default"
   saved backup bundle to $TESTTMP/ancestor-merge-2/.hg/strip-backup/ec2c14fb2984-62d0b222-backup.hg (glob)
diff --git a/tests/test-subrepo-missing.t b/tests/test-subrepo-missing.t
--- a/tests/test-subrepo-missing.t
+++ b/tests/test-subrepo-missing.t
@@ -62,7 +62,7 @@ 
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ rm .hgsubstate
   $ hg up 0
-  remote changed .hgsubstate which local deleted
+  remote [destination] changed .hgsubstate which local [working copy] deleted
   use (c)hanged version or leave (d)eleted? c
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg st