[PATCH V3] status: add a flag to terse the output (issue4119)
Denis Laxalde
denis at laxalde.org
Wed Jun 28 03:24:01 EDT 2017
Pulkit Goyal a écrit :
> # HG changeset patch
> # User Pulkit Goyal <7895pulkit at gmail.com>
> # Date 1497710422 -19800
> # Sat Jun 17 20:10:22 2017 +0530
> # Node ID 79bed8c52dfc6dcb0c3098a7117ae4df4c009f8c
> # Parent 48ffac16c59bf9e2b2c5acab728eb398b25249eb
> status: add a flag to terse the output (issue4119)
>
> This adds an experimental flag -t/--terse which will terse the output. The terse flag
> will respect other flags which filters the output. The flag takes a string
> whose value can be a subsequence of "marduic" (the order does not matter here.)
>
> Ignored files are not considered while tersing unless -i flag is passed or 'i'
> is there is the terse flag value.
On the other hand (having played a bit locally with this), it's
surprising that `hg status --terse i` does show ignored files even
without a `--ignored` flag. In fact the same occurs with `hg status
--terse c` (shows "clean" files, even without `--clean`) so this is not
specific to "ignored" files. Is this intended?
> The flag is experimental for testing as there may be cases which will produce
> strange results with the flag. We can set the terse on by default by simply
> passing 'u' to the cmdutil.tersestatus().
>
> This patch also adds a test file with tests covering the new feature.
>
> diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
> --- a/mercurial/cmdutil.py
> +++ b/mercurial/cmdutil.py
> @@ -401,6 +401,117 @@
>
> return commit(ui, repo, recordinwlock, pats, opts)
>
> +def tersestatus(root, statlist, status, ignorefn, ignore):
> + """
> + Returns a list of statuses with directory collapsed if all the files in the
> + directory has the same status.
> + """
Considering how the function is use in commands.py (stat =
cmdutil.tersestatus(repo.root, stat, ...)), I think it should return a
mercurial.scmutil.status instance instead of a plain list for consistency.
Looking at function arguments:
* "statlist" is not a proper name since this is not a list but a
scmutil.status object
* "status" is misleading as it is actually the value of --terse option.
> +
> + def numfiles(dirname):
> + """
> + Calculates the number of tracked files in a given directory which also
> + includes files which were removed or deleted. Considers ignored files
> + if ignore argument is True or 'i' is present in status argument.
> + """
> + if 'i' in status or ignore:
> + def match(fname):
> + return False
> + else:
> + match = ignorefn
> + lendir = 0
> + abspath = os.path.join(root, dirname)
> + # There might be cases when a directory does not exists as the whole
> + # directory can be removed and/or deleted.
> + try:
> + for f in os.listdir(abspath):
> + if not match(f):
> + lendir += 1
> + except OSError:
> + pass
> + lendir += absentdir.get(dirname, 0)
> + return lendir
> +
> + def absentones(removedfiles, missingfiles):
> + """
> + Returns a dictionary of directories and number of files which are either
> + removed or missing (deleted) in them.
> + """
> + absentdir = {}
> + absentfiles = removedfiles + missingfiles
> + while absentfiles:
> + f = absentfiles.pop()
> + par = os.path.dirname(f)
> + if par == '':
> + continue
> + try:
> + absentdir[par] += 1
> + except KeyError:
> + absentdir[par] = 1
> + if par not in removedfiles:
> + absentfiles.append(par)
> + return absentdir
> +
> + indexes = {'m': 0, 'a': 1, 'r': 2, 'd': 3, 'u': 4, 'i': 5, 'c': 6}
> + absentdir = absentones(statlist[2], statlist[3])
> + finalrs = [[]] * len(indexes)
> + didsomethingchanged = False
> +
> + for st in pycompat.bytestr(status):
> +
> + try:
> + ind = indexes[st]
> + except KeyError:
> + # TODO: Need a better error message here
> + raise error.Abort("'%s' not recognized" % st)
> +
> + sfiles = statlist[ind]
The code might be easier to follow if "statlist" was queried by
attribute name instead of relying on tuple indexing.
For instance:
statusnames = {'m': 'modified', 'a': 'added', ...}
finalrs = scmutil.status([], [], [], [], [], [], [])
for st in pycompat.bytestr(status):
try:
statusname = statusnames[st]
except KeyError:
raise error.Abort(...)
sfiles = getattr(statlist, statusname)
rs = getattr(finalrs, statusname)
# rs.append()/rs.extend()
(Not a big deal, might be improved later.)
> + if not sfiles:
> + continue
> + pardict = {}
> + for a in sfiles:
> + par = os.path.dirname(a)
> + pardict.setdefault(par, []).append(a)
> +
> + rs = []
> + newls = []
> + for par, files in pardict.iteritems():
> + lenpar = numfiles(par)
> + if lenpar == len(files):
> + newls.append(par)
> +
> + if not newls:
> + continue
> +
> + while newls:
> + newel = newls.pop()
> + if newel == '':
> + continue
> + parn = os.path.dirname(newel)
> + pardict[newel] = []
> + # Adding pycompat.ossep as newel is a directory.
> + pardict.setdefault(parn, []).append(newel + pycompat.ossep)
> + lenpar = numfiles(parn)
> + if lenpar == len(pardict[parn]):
> + newls.append(parn)
> +
> + # dict.values() for Py3 compatibility
> + for files in pardict.values():
> + rs.extend(files)
> +
> + rs.sort()
> + finalrs[ind] = rs
> + didsomethingchanged = True
> +
> + # If nothing is changed, make sure the order of files is preserved.
> + if not didsomethingchanged:
> + return statlist
> +
> + for x in xrange(len(indexes)):
> + if not finalrs[x]:
> + finalrs[x] = statlist[x]
> +
> + return finalrs
> +
> def findpossible(cmd, table, strict=False):
> """
> Return cmd -> (aliases, command table entry)
> diff --git a/mercurial/commands.py b/mercurial/commands.py
> --- a/mercurial/commands.py
> +++ b/mercurial/commands.py
> @@ -4709,6 +4709,7 @@
> ('u', 'unknown', None, _('show only unknown (not tracked) files')),
> ('i', 'ignored', None, _('show only ignored files')),
> ('n', 'no-status', None, _('hide status prefix')),
> + ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')),
> ('C', 'copies', None, _('show source of copied files')),
> ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
> ('', 'rev', [], _('show difference from revision'), _('REV')),
> @@ -4754,6 +4755,16 @@
>
> .. container:: verbose
>
> + The -t/--terse option abbreviates the output by showing directory name
> + if all the files in it share the same status. The option expects a value
> + which can be a string formed by using 'm', 'a', 'r', 'd', 'u', 'i', 'c'
> + where, 'm' stands for 'modified', 'a' for 'added', 'r' for 'removed',
> + 'd' for 'deleted', 'u' for 'unknown', 'i' for 'ignored' and 'c' for clean.
Might be worth also having a "A" for "all" that'd be symmetrical with
-A/--all flag to avoid typing `--terse marduic`.
> +
> + It terses the output of only those status which are passed. The ignored
> + files are not considered while tersing until 'i' is there in --terse value
> + or the --ignored option is used.
> +
> Examples:
>
> - show changes in the working directory relative to a
> @@ -4780,6 +4791,7 @@
> opts = pycompat.byteskwargs(opts)
> revs = opts.get('rev')
> change = opts.get('change')
> + terse = opts.get('terse')
>
> if revs and change:
> msg = _('cannot specify --rev and --change at the same time')
> @@ -4804,16 +4816,28 @@
> show = [k for k in states if opts.get(k)]
> if opts.get('all'):
> show += ui.quiet and (states[:4] + ['clean']) or states
> + if ui.quiet and terse:
> + for st in ('ignored', 'unknown'):
> + if st[0] in terse:
> + show.append(st)
> +
> if not show:
> if ui.quiet:
> show = states[:4]
> else:
> show = states[:5]
> + if terse:
> + for st in ('ignored', 'unknown', 'clean'):
> + if st[0] in terse:
> + show.append(st)
>
> m = scmutil.match(repo[node2], pats, opts)
> stat = repo.status(node1, node2, m,
> 'ignored' in show, 'clean' in show, 'unknown' in show,
> opts.get('subrepos'))
> + if terse:
> + stat = cmdutil.tersestatus(repo.root, stat, terse,
> + repo.dirstate._ignore, opts.get('ignore'))
Isn't it opts.get('ignored')?
Also, the last (wrapped) line has an extra leading space.
> changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
>
> if (opts.get('all') or opts.get('copies')
More information about the Mercurial-devel
mailing list