Mercurial > public > mercurial-scm > hg
comparison 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 |
comparison
equal
deleted
inserted
replaced
44395:382f4f09f0bd | 44396:acbfa31cfaf2 |
---|---|
26 | 26 |
27 from .i18n import _ | 27 from .i18n import _ |
28 from .node import ( | 28 from .node import ( |
29 bin, | 29 bin, |
30 hex, | 30 hex, |
31 nullhex, | |
32 nullid, | 31 nullid, |
33 nullrev, | 32 nullrev, |
34 short, | 33 short, |
35 ) | 34 ) |
36 from .pycompat import ( | 35 from .pycompat import ( |
1942 _(b'total cache data size %s, on-disk %s\n') | 1941 _(b'total cache data size %s, on-disk %s\n') |
1943 % (util.bytecount(totalsize), util.bytecount(ondisk)) | 1942 % (util.bytecount(totalsize), util.bytecount(ondisk)) |
1944 ) | 1943 ) |
1945 | 1944 |
1946 | 1945 |
1947 @command(b'debugmergestate', [], b'') | 1946 @command(b'debugmergestate', [] + cmdutil.templateopts, b'') |
1948 def debugmergestate(ui, repo, *args): | 1947 def debugmergestate(ui, repo, *args, **opts): |
1949 """print merge state | 1948 """print merge state |
1950 | 1949 |
1951 Use --verbose to print out information about whether v1 or v2 merge state | 1950 Use --verbose to print out information about whether v1 or v2 merge state |
1952 was chosen.""" | 1951 was chosen.""" |
1953 | 1952 |
1954 def _hashornull(h): | 1953 if ui.verbose: |
1955 if h == nullhex: | 1954 ms = mergemod.mergestate(repo) |
1956 return b'null' | 1955 |
1956 # sort so that reasonable information is on top | |
1957 v1records = ms._readrecordsv1() | |
1958 v2records = ms._readrecordsv2() | |
1959 | |
1960 if not v1records and not v2records: | |
1961 pass | |
1962 elif not v2records: | |
1963 ui.writenoi18n(b'no version 2 merge state\n') | |
1964 elif ms._v1v2match(v1records, v2records): | |
1965 ui.writenoi18n(b'v1 and v2 states match: using v2\n') | |
1957 else: | 1966 else: |
1958 return h | 1967 ui.writenoi18n(b'v1 and v2 states mismatch: using v1\n') |
1959 | 1968 |
1960 def printrecords(version): | 1969 opts = pycompat.byteskwargs(opts) |
1961 ui.writenoi18n(b'* version %d records\n' % version) | 1970 if not opts[b'template']: |
1962 if version == 1: | 1971 opts[b'template'] = ( |
1963 records = v1records | 1972 b'{if(commits, "", "no merge state found\n")}' |
1964 else: | 1973 b'{commits % "{name}{if(label, " ({label})")}: {node}\n"}' |
1965 records = v2records | 1974 b'{files % "file: {path} (state \\"{state}\\")\n' |
1966 | 1975 b'{if(local_path, "' |
1967 for rtype, record in records: | 1976 b' local path: {local_path} (hash {local_key}, flags \\"{local_flags}\\")\n' |
1968 # pretty print some record types | 1977 b' ancestor path: {ancestor_path} (node {ancestor_node})\n' |
1969 if rtype == b'L': | 1978 b' other path: {other_path} (node {other_node})\n' |
1970 ui.writenoi18n(b'local: %s\n' % record) | 1979 b'")}' |
1971 elif rtype == b'O': | 1980 b'{if(rename_side, "' |
1972 ui.writenoi18n(b'other: %s\n' % record) | 1981 b' rename side: {rename_side}\n' |
1973 elif rtype == b'm': | 1982 b' renamed path: {renamed_path}\n' |
1974 driver, mdstate = record.split(b'\0', 1) | 1983 b'")}' |
1975 ui.writenoi18n( | 1984 b'{extras % " extra: {key} = {value}\n"}' |
1976 b'merge driver: %s (state "%s")\n' % (driver, mdstate) | 1985 b'"}' |
1977 ) | 1986 ) |
1978 elif rtype in b'FDC': | 1987 |
1979 r = record.split(b'\0') | 1988 ms = mergemod.mergestate.read(repo) |
1980 f, state, hash, lfile, afile, anode, ofile = r[0:7] | 1989 |
1981 if version == 1: | 1990 fm = ui.formatter(b'debugmergestate', opts) |
1982 onode = b'not stored in v1 format' | 1991 fm.startitem() |
1983 flags = r[7] | 1992 |
1984 else: | 1993 fm_commits = fm.nested(b'commits') |
1985 onode, flags = r[7:9] | 1994 if ms.active(): |
1986 ui.writenoi18n( | 1995 for name, node, label_index in ( |
1987 b'file: %s (record type "%s", state "%s", hash %s)\n' | 1996 (b'local', ms.local, 0), |
1988 % (f, rtype, state, _hashornull(hash)) | 1997 (b'other', ms.other, 1), |
1989 ) | 1998 ): |
1990 ui.writenoi18n( | 1999 fm_commits.startitem() |
1991 b' local path: %s (flags "%s")\n' % (lfile, flags) | 2000 fm_commits.data(name=name) |
1992 ) | 2001 fm_commits.data(node=hex(node)) |
1993 ui.writenoi18n( | 2002 if ms._labels and len(ms._labels) > label_index: |
1994 b' ancestor path: %s (node %s)\n' | 2003 fm_commits.data(label=ms._labels[label_index]) |
1995 % (afile, _hashornull(anode)) | 2004 fm_commits.end() |
1996 ) | 2005 |
1997 ui.writenoi18n( | 2006 fm_files = fm.nested(b'files') |
1998 b' other path: %s (node %s)\n' | 2007 if ms.active(): |
1999 % (ofile, _hashornull(onode)) | 2008 for f in ms: |
2000 ) | 2009 fm_files.startitem() |
2001 elif rtype == b'f': | 2010 fm_files.data(path=f) |
2002 filename, rawextras = record.split(b'\0', 1) | 2011 state = ms._state[f] |
2003 extras = rawextras.split(b'\0') | 2012 fm_files.data(state=state[0]) |
2004 i = 0 | 2013 if state[0] in ( |
2005 extrastrings = [] | 2014 mergemod.MERGE_RECORD_UNRESOLVED, |
2006 while i < len(extras): | 2015 mergemod.MERGE_RECORD_RESOLVED, |
2007 extrastrings.append(b'%s = %s' % (extras[i], extras[i + 1])) | 2016 ): |
2008 i += 2 | 2017 fm_files.data(local_key=state[1]) |
2009 | 2018 fm_files.data(local_path=state[2]) |
2010 ui.writenoi18n( | 2019 fm_files.data(ancestor_path=state[3]) |
2011 b'file extras: %s (%s)\n' | 2020 fm_files.data(ancestor_node=state[4]) |
2012 % (filename, b', '.join(extrastrings)) | 2021 fm_files.data(other_path=state[5]) |
2013 ) | 2022 fm_files.data(other_node=state[6]) |
2014 elif rtype == b'l': | 2023 fm_files.data(local_flags=state[7]) |
2015 labels = record.split(b'\0', 2) | 2024 elif state[0] in ( |
2016 labels = [l for l in labels if len(l) > 0] | 2025 mergemod.MERGE_RECORD_UNRESOLVED_PATH, |
2017 ui.writenoi18n(b'labels:\n') | 2026 mergemod.MERGE_RECORD_RESOLVED_PATH, |
2018 ui.write((b' local: %s\n' % labels[0])) | 2027 ): |
2019 ui.write((b' other: %s\n' % labels[1])) | 2028 fm_files.data(renamed_path=state[1]) |
2020 if len(labels) > 2: | 2029 fm_files.data(rename_side=state[2]) |
2021 ui.write((b' base: %s\n' % labels[2])) | 2030 fm_extras = fm_files.nested(b'extras') |
2022 else: | 2031 for k, v in ms.extras(f).items(): |
2023 ui.writenoi18n( | 2032 fm_extras.startitem() |
2024 b'unrecognized entry: %s\t%s\n' | 2033 fm_extras.data(key=k) |
2025 % (rtype, record.replace(b'\0', b'\t')) | 2034 fm_extras.data(value=v) |
2026 ) | 2035 fm_extras.end() |
2027 | 2036 |
2028 # Avoid mergestate.read() since it may raise an exception for unsupported | 2037 fm_files.end() |
2029 # merge state records. We shouldn't be doing this, but this is OK since this | 2038 |
2030 # command is pretty low-level. | 2039 fm.end() |
2031 ms = mergemod.mergestate(repo) | |
2032 | |
2033 # sort so that reasonable information is on top | |
2034 v1records = ms._readrecordsv1() | |
2035 v2records = ms._readrecordsv2() | |
2036 order = b'LOml' | |
2037 | |
2038 def key(r): | |
2039 idx = order.find(r[0]) | |
2040 if idx == -1: | |
2041 return (1, r[1]) | |
2042 else: | |
2043 return (0, idx) | |
2044 | |
2045 v1records.sort(key=key) | |
2046 v2records.sort(key=key) | |
2047 | |
2048 if not v1records and not v2records: | |
2049 ui.writenoi18n(b'no merge state found\n') | |
2050 elif not v2records: | |
2051 ui.notenoi18n(b'no version 2 merge state\n') | |
2052 printrecords(1) | |
2053 elif ms._v1v2match(v1records, v2records): | |
2054 ui.notenoi18n(b'v1 and v2 states match: using v2\n') | |
2055 printrecords(2) | |
2056 else: | |
2057 ui.notenoi18n(b'v1 and v2 states mismatch: using v1\n') | |
2058 printrecords(1) | |
2059 if ui.verbose: | |
2060 printrecords(2) | |
2061 | 2040 |
2062 | 2041 |
2063 @command(b'debugnamecomplete', [], _(b'NAME...')) | 2042 @command(b'debugnamecomplete', [], _(b'NAME...')) |
2064 def debugnamecomplete(ui, repo, *args): | 2043 def debugnamecomplete(ui, repo, *args): |
2065 '''complete "names" - tags, open branch names, bookmark names''' | 2044 '''complete "names" - tags, open branch names, bookmark names''' |