mirror of
https://github.com/facebook/sapling.git
synced 2024-10-09 16:31:02 +03:00
fastannotate: implement the command
Summary: This diff adds the `fastannotate` command, which puts all components (formatter, annotatecontext - linelog, revmap) together. Note that `fastannotate.mainbranch` needs to be carefully chosen to avoid rebuilds. If `mainbranch` moves backwards, the `fastannotate` command can fail because it could not move linelog backwards and thus cannot update linelog incrementally. Test Plan: Code Review. The `.t` file is coming in the next diff. Reviewers: #sourcecontrol, stash Reviewed By: stash Subscribers: stash, mjpieters Differential Revision: https://phabricator.intern.facebook.com/D3836670 Signature: t1:3836670:1474016576:b323b62db3bfd69bff88f6911abbe26075734737
This commit is contained in:
parent
316cae170e
commit
96e56210ff
43
fastannotate/__init__.py
Normal file
43
fastannotate/__init__.py
Normal file
@ -0,0 +1,43 @@
|
||||
# Copyright 2016-present Facebook. All Rights Reserved.
|
||||
#
|
||||
# fastannotate: faster annotate implementation using linelog
|
||||
#
|
||||
# This software may be used and distributed according to the terms of the
|
||||
# GNU General Public License version 2 or any later version.
|
||||
|
||||
|
||||
"""yet another annotate implementation that might be faster
|
||||
|
||||
The fastannotate extension provides a 'fastannotate' command that makes
|
||||
use of the linelog data structure as a cache layer and is expected to
|
||||
be faster than the vanilla 'annotate' if the cache is present.
|
||||
|
||||
::
|
||||
|
||||
[fastannotate]
|
||||
# specify the main branch head. the internal linelog will only contain
|
||||
# the linear (ignoring p2) "mainbranch". since linelog cannot move
|
||||
# backwards without a rebuild, this should be something that always moves
|
||||
# forward, usually it is "master" or "@".
|
||||
mainbranch = master
|
||||
# use unfiltered repo for better performance
|
||||
unfilteredrepo = True
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fastannotate import commands
|
||||
|
||||
from mercurial import (
|
||||
cmdutil,
|
||||
)
|
||||
|
||||
testedwith = 'internal'
|
||||
|
||||
cmdtable = {}
|
||||
command = cmdutil.command(cmdtable)
|
||||
|
||||
command('^fastannotate|fastblame|fa',
|
||||
**commands.fastannotatecommandargs
|
||||
)(commands.fastannotate)
|
||||
|
118
fastannotate/commands.py
Normal file
118
fastannotate/commands.py
Normal file
@ -0,0 +1,118 @@
|
||||
# Copyright 2016-present Facebook. All Rights Reserved.
|
||||
#
|
||||
# commands: fastannotate commands
|
||||
#
|
||||
# This software may be used and distributed according to the terms of the
|
||||
# GNU General Public License version 2 or any later version.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fastannotate import (
|
||||
context as facontext,
|
||||
error as faerror,
|
||||
formatter as faformatter,
|
||||
)
|
||||
|
||||
from mercurial import (
|
||||
commands,
|
||||
error,
|
||||
scmutil,
|
||||
)
|
||||
|
||||
from mercurial.i18n import _
|
||||
|
||||
fastannotatecommandargs = {
|
||||
'options': [
|
||||
('r', 'rev', '.', _('annotate the specified revision'), _('REV')),
|
||||
('u', 'user', None, _('list the author (long with -v)')),
|
||||
('f', 'file', None, _('list the filename')),
|
||||
('d', 'date', None, _('list the date (short with -q)')),
|
||||
('n', 'number', None, _('list the revision number (default)')),
|
||||
('c', 'changeset', None, _('list the changeset')),
|
||||
('l', 'line-number', None, _('show line number at the first '
|
||||
'appearance')),
|
||||
('h', 'no-content', None, _('do not show file content')),
|
||||
('', 'no-follow', None, _("don't follow copies and renames")),
|
||||
('', 'linear', None, _('enforce linear history, ignore second parent '
|
||||
'of merges (faster)')),
|
||||
('', 'long-hash', None, _('show long changeset hash (EXPERIMENTAL)')),
|
||||
('', 'rebuild', None, _('rebuild cache even if it exists '
|
||||
'(EXPERIMENTAL)')),
|
||||
] + commands.walkopts,
|
||||
'synopsis': _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
|
||||
'inferrepo': True,
|
||||
}
|
||||
|
||||
def fastannotate(ui, repo, *pats, **opts):
|
||||
"""show changeset information by line for each file
|
||||
|
||||
List changes in files, showing the revision id responsible for each line.
|
||||
|
||||
This command is useful for discovering when a change was made and by whom.
|
||||
|
||||
If you include --file, --user, or --date, the revision number is suppressed
|
||||
unless you also include --number.
|
||||
|
||||
This command uses an implementation different from the vanilla annotate
|
||||
command, which may produce slightly different (while still reasonable)
|
||||
output for some cases.
|
||||
|
||||
For the best performance, use -c, -l, avoid -u, -d, -n. Use --linear
|
||||
and --no-content to make it even faster.
|
||||
|
||||
Returns 0 on success.
|
||||
"""
|
||||
if not pats:
|
||||
raise error.Abort(_('at least one filename or pattern is required'))
|
||||
|
||||
# performance hack: filtered repo can be slow. unfilter by default.
|
||||
if ui.configbool('fastannotate', 'unfilteredrepo', True):
|
||||
repo = repo.unfiltered()
|
||||
|
||||
rev = opts.get('rev', '.')
|
||||
rebuild = opts.get('rebuild', False)
|
||||
|
||||
ctx = scmutil.revsingle(repo, rev)
|
||||
m = scmutil.match(ctx, pats, opts)
|
||||
|
||||
aopts = facontext.annotateopts(
|
||||
followmerge=not opts.get('linear', False),
|
||||
followrename=not opts.get('no_follow', False))
|
||||
|
||||
if not any(opts.get(s)
|
||||
for s in ['user', 'date', 'file', 'number', 'changeset']):
|
||||
# default 'number' for compatibility. but fastannotate is more
|
||||
# efficient with "changeset", "line-number" and "no-content".
|
||||
for name in ui.configlist('fastannotate', 'defaultformat', ['number']):
|
||||
opts[name] = True
|
||||
|
||||
formatter = faformatter.defaultformatter(ui, repo, opts)
|
||||
showlines = not bool(opts.get('no_content'))
|
||||
showpath = opts.get('file', False)
|
||||
|
||||
# find the head of the main (master) branch
|
||||
masterrev = ui.config('fastannotate', 'mainbranch')
|
||||
if masterrev:
|
||||
master = lambda: scmutil.revsingle(repo, masterrev).rev()
|
||||
else:
|
||||
master = rev
|
||||
|
||||
for path in ctx.walk(m):
|
||||
result = lines = None
|
||||
while True:
|
||||
try:
|
||||
with facontext.annotatecontext(repo, path, aopts, rebuild) as a:
|
||||
result = a.annotate(rev, master=master, showpath=showpath,
|
||||
showlines=showlines)
|
||||
break
|
||||
except faerror.CannotReuseError: # happens if master moves backwards
|
||||
if rebuild: # give up since we have tried rebuild alreadyraise
|
||||
raise
|
||||
else: # try a second time rebuilding the cache (slow)
|
||||
rebuild = True
|
||||
continue
|
||||
|
||||
if showlines:
|
||||
result, lines = result
|
||||
|
||||
formatter.write(result, lines)
|
Loading…
Reference in New Issue
Block a user