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'''