in eden/scm/edenscm/mercurial/commands/__init__.py [0:0]
def annotate(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.
Without the -a/--text option, annotate will avoid processing files
it detects as binary. With -a, annotate will annotate the file
anyway, although the results will probably be neither useful
nor desirable.
Returns 0 on success.
"""
if not pats:
raise error.Abort(_("at least one filename or pattern is required"))
ctx = scmutil.revsingle(repo, opts.get("rev"))
rootfm = ui.formatter("annotate", opts)
if ui.quiet:
datefunc = util.shortdate
else:
datefunc = util.datestr
if ctx.rev() is None:
def hexfn(node):
if node is None:
return None
else:
return rootfm.hexfunc(node)
if opts.get("changeset"):
# omit "+" suffix which is appended to node hex
def formatrev(rev):
if rev is None:
return "%d" % ctx.p1().rev()
else:
return "%d" % rev
else:
def formatrev(rev):
if rev is None:
return "%d+" % ctx.p1().rev()
else:
return "%d " % rev
def formathex(hex):
if hex is None:
return "%s+" % rootfm.hexfunc(ctx.p1().node())
else:
return "%s " % hex
else:
hexfn = rootfm.hexfunc
formatrev = formathex = pycompat.bytestr
now = time.time()
def agebucket(d):
t, tz = d
if t > now - 3600:
return "1hour"
day = 86400
if t > now - day:
return "1day"
if t > now - 7 * day:
return "7day"
if t > now - 30 * day:
return "30day"
if t > now - 60 * day:
return "60day"
if t > now - 180 * day:
return "180day"
if t > now - 360 * day:
return "360day"
return "old"
opmap = [
("user", " ", lambda x: x.fctx.user(), ui.shortuser),
("number", " ", lambda x: x.fctx.rev(), formatrev),
("changeset", " ", lambda x: hexfn(x.fctx.node()), formathex),
("date", " ", lambda x: x.fctx.date(), util.cachefunc(datefunc)),
("file", " ", lambda x: x.fctx.path(), str),
("line_number", ":", lambda x: x.lineno, str),
("age_bucket", "", lambda x: agebucket(x.fctx.date()), lambda x: ""),
]
fieldnamemap = {"number": "rev", "changeset": "node"}
if (
not opts.get("user")
and not opts.get("changeset")
and not opts.get("date")
and not opts.get("file")
):
opts["number"] = True
opts["age_bucket"] = True
linenumber = opts.get("line_number") is not None
if linenumber and (not opts.get("changeset")) and (not opts.get("number")):
raise error.Abort(_("at least one of -n/-c is required for -l"))
ui.pager("annotate")
if rootfm.isplain():
def makefunc(get, fmt):
return lambda x: fmt(get(x))
else:
def makefunc(get, fmt):
return get
funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap if opts.get(op)]
funcmap[0] = (funcmap[0][0], "") # no separator in front of first column
fields = " ".join(
fieldnamemap.get(op, op) for op, sep, get, fmt in opmap if opts.get(op)
)
def bad(x, y):
raise error.Abort("%s: %s" % (x, y))
m = scmutil.match(ctx, pats, opts, badfn=bad)
follow = not opts.get("no_follow")
diffopts = patch.difffeatureopts(ui, opts, section="annotate", whitespace=True)
skiprevs = opts.get("skip")
if skiprevs:
skiprevs = scmutil.revrange(repo, skiprevs)
for abs in ctx.walk(m):
fctx = ctx[abs]
rootfm.startitem()
rootfm.data(abspath=abs, path=m.rel(abs))
if not opts.get("text") and fctx.isbinary():
rootfm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
continue
fm = rootfm.nested("lines")
lines = list(
fctx.annotate(
follow=follow,
linenumber=linenumber,
skiprevs=skiprevs,
diffopts=diffopts,
)
)
if not lines:
fm.end()
continue
formats = []
pieces = []
for f, sep in funcmap:
l = [f(n) for n, dummy in lines]
if fm.isplain():
sizes = [encoding.colwidth(x) for x in l]
ml = max(sizes)
formats.append([sep + " " * (ml - w) + "%s" for w in sizes])
else:
formats.append(["%s" for x in l])
pieces.append(l)
agebuckets = [agebucket(x.fctx.date()) for x, dummy in lines]
for f, p, l, a in zip(zip(*formats), zip(*pieces), lines, agebuckets):
fm.startitem()
sep = "* " if l[0].skip else ": "
fm.write(fields, "".join(f) + sep, *p, label="blame.age." + a)
fm.write("line", "%s", pycompat.decodeutf8(l[1], errors="replace"))
if not lines[-1][1].endswith(b"\n"):
fm.plain("\n")
fm.end()
rootfm.end()