Mercurial > public > mercurial-scm > hg
diff mercurial/debugcommands.py @ 44396:acbfa31cfaf2
debugmergestate: make templated
Our IntelliJ team wants to be able to read the merge state in order to
help the user resolve merge conflicts. They had so far been reading
file contents from p1() and p2() and their merge base. That is not
ideal for several reasons (merge base is not necessarily the "graft
base", renames are not handled, commands like `hg update -m` is not
handled). It will get especially bad as of my D7827. This patch makes
the output s a templated. I haven't bothered to make it complete
(e.g. merge driver states are not handled), but it's probably good
enough as a start.
I've done a web search for "debugmergestate" and I can't find any
indication that any tools currently rely on its output. If it turns
out that we get bug reports for it once this is released, I won't
object to backing this patch out on the stable branch (and then
perhaps replace it by a separate command, or put it behind a new
flag).
The changes in test-backout.t are interesting, in particular this:
```
- other path: foo (node not stored in v1 format)
+ other path: (node foo)
```
I wonder if that means that we actually read v1 format
incorrectly. That seems to be an old format that was switched away
from in 2014, so it doesn't matter now anyway.
Differential Revision: https://phab.mercurial-scm.org/D8120
author | Martin von Zweigbergk <martinvonz@google.com> |
---|---|
date | Thu, 13 Feb 2020 21:14:20 -0800 |
parents | f7459da77f23 |
children | f82d2d4e71db |
line wrap: on
line diff
--- a/mercurial/debugcommands.py Thu Feb 13 21:55:38 2020 -0800 +++ b/mercurial/debugcommands.py Thu Feb 13 21:14:20 2020 -0800 @@ -28,7 +28,6 @@ from .node import ( bin, hex, - nullhex, nullid, nullrev, short, @@ -1944,120 +1943,100 @@ ) -@command(b'debugmergestate', [], b'') -def debugmergestate(ui, repo, *args): +@command(b'debugmergestate', [] + cmdutil.templateopts, b'') +def debugmergestate(ui, repo, *args, **opts): """print merge state Use --verbose to print out information about whether v1 or v2 merge state was chosen.""" - def _hashornull(h): - if h == nullhex: - return b'null' - else: - return h - - def printrecords(version): - ui.writenoi18n(b'* version %d records\n' % version) - if version == 1: - records = v1records + if ui.verbose: + ms = mergemod.mergestate(repo) + + # sort so that reasonable information is on top + v1records = ms._readrecordsv1() + v2records = ms._readrecordsv2() + + if not v1records and not v2records: + pass + elif not v2records: + ui.writenoi18n(b'no version 2 merge state\n') + elif ms._v1v2match(v1records, v2records): + ui.writenoi18n(b'v1 and v2 states match: using v2\n') else: - records = v2records - - for rtype, record in records: - # pretty print some record types - if rtype == b'L': - ui.writenoi18n(b'local: %s\n' % record) - elif rtype == b'O': - ui.writenoi18n(b'other: %s\n' % record) - elif rtype == b'm': - driver, mdstate = record.split(b'\0', 1) - ui.writenoi18n( - b'merge driver: %s (state "%s")\n' % (driver, mdstate) - ) - elif rtype in b'FDC': - r = record.split(b'\0') - f, state, hash, lfile, afile, anode, ofile = r[0:7] - if version == 1: - onode = b'not stored in v1 format' - flags = r[7] - else: - onode, flags = r[7:9] - ui.writenoi18n( - b'file: %s (record type "%s", state "%s", hash %s)\n' - % (f, rtype, state, _hashornull(hash)) - ) - ui.writenoi18n( - b' local path: %s (flags "%s")\n' % (lfile, flags) - ) - ui.writenoi18n( - b' ancestor path: %s (node %s)\n' - % (afile, _hashornull(anode)) - ) - ui.writenoi18n( - b' other path: %s (node %s)\n' - % (ofile, _hashornull(onode)) - ) - elif rtype == b'f': - filename, rawextras = record.split(b'\0', 1) - extras = rawextras.split(b'\0') - i = 0 - extrastrings = [] - while i < len(extras): - extrastrings.append(b'%s = %s' % (extras[i], extras[i + 1])) - i += 2 - - ui.writenoi18n( - b'file extras: %s (%s)\n' - % (filename, b', '.join(extrastrings)) - ) - elif rtype == b'l': - labels = record.split(b'\0', 2) - labels = [l for l in labels if len(l) > 0] - ui.writenoi18n(b'labels:\n') - ui.write((b' local: %s\n' % labels[0])) - ui.write((b' other: %s\n' % labels[1])) - if len(labels) > 2: - ui.write((b' base: %s\n' % labels[2])) - else: - ui.writenoi18n( - b'unrecognized entry: %s\t%s\n' - % (rtype, record.replace(b'\0', b'\t')) - ) - - # Avoid mergestate.read() since it may raise an exception for unsupported - # merge state records. We shouldn't be doing this, but this is OK since this - # command is pretty low-level. - ms = mergemod.mergestate(repo) - - # sort so that reasonable information is on top - v1records = ms._readrecordsv1() - v2records = ms._readrecordsv2() - order = b'LOml' - - def key(r): - idx = order.find(r[0]) - if idx == -1: - return (1, r[1]) - else: - return (0, idx) - - v1records.sort(key=key) - v2records.sort(key=key) - - if not v1records and not v2records: - ui.writenoi18n(b'no merge state found\n') - elif not v2records: - ui.notenoi18n(b'no version 2 merge state\n') - printrecords(1) - elif ms._v1v2match(v1records, v2records): - ui.notenoi18n(b'v1 and v2 states match: using v2\n') - printrecords(2) - else: - ui.notenoi18n(b'v1 and v2 states mismatch: using v1\n') - printrecords(1) - if ui.verbose: - printrecords(2) + ui.writenoi18n(b'v1 and v2 states mismatch: using v1\n') + + opts = pycompat.byteskwargs(opts) + if not opts[b'template']: + opts[b'template'] = ( + b'{if(commits, "", "no merge state found\n")}' + b'{commits % "{name}{if(label, " ({label})")}: {node}\n"}' + b'{files % "file: {path} (state \\"{state}\\")\n' + b'{if(local_path, "' + b' local path: {local_path} (hash {local_key}, flags \\"{local_flags}\\")\n' + b' ancestor path: {ancestor_path} (node {ancestor_node})\n' + b' other path: {other_path} (node {other_node})\n' + b'")}' + b'{if(rename_side, "' + b' rename side: {rename_side}\n' + b' renamed path: {renamed_path}\n' + b'")}' + b'{extras % " extra: {key} = {value}\n"}' + b'"}' + ) + + ms = mergemod.mergestate.read(repo) + + fm = ui.formatter(b'debugmergestate', opts) + fm.startitem() + + fm_commits = fm.nested(b'commits') + if ms.active(): + for name, node, label_index in ( + (b'local', ms.local, 0), + (b'other', ms.other, 1), + ): + fm_commits.startitem() + fm_commits.data(name=name) + fm_commits.data(node=hex(node)) + if ms._labels and len(ms._labels) > label_index: + fm_commits.data(label=ms._labels[label_index]) + fm_commits.end() + + fm_files = fm.nested(b'files') + if ms.active(): + for f in ms: + fm_files.startitem() + fm_files.data(path=f) + state = ms._state[f] + fm_files.data(state=state[0]) + if state[0] in ( + mergemod.MERGE_RECORD_UNRESOLVED, + mergemod.MERGE_RECORD_RESOLVED, + ): + fm_files.data(local_key=state[1]) + fm_files.data(local_path=state[2]) + fm_files.data(ancestor_path=state[3]) + fm_files.data(ancestor_node=state[4]) + fm_files.data(other_path=state[5]) + fm_files.data(other_node=state[6]) + fm_files.data(local_flags=state[7]) + elif state[0] in ( + mergemod.MERGE_RECORD_UNRESOLVED_PATH, + mergemod.MERGE_RECORD_RESOLVED_PATH, + ): + fm_files.data(renamed_path=state[1]) + fm_files.data(rename_side=state[2]) + fm_extras = fm_files.nested(b'extras') + for k, v in ms.extras(f).items(): + fm_extras.startitem() + fm_extras.data(key=k) + fm_extras.data(value=v) + fm_extras.end() + + fm_files.end() + + fm.end() @command(b'debugnamecomplete', [], _(b'NAME...'))