For about a year, Logilab has been involved in Mercurial development in the framework of a Mozilla Open Source Support (MOSS) program. Mercurial is a foundational technology for Mozilla, as the VCS used for the development of Firefox. As the main protagonist of the program, I'd first like to mention this has been a very nice experience for me, both from the social and technical perspectives. I've learned a lot and hope to continue working on this nice piece of software.
The general objective of the program was to improve "code archaeology" workflows, especially when using hgweb (the web UI for Mercurial). Outcomes of program spread from versions 4.1 to 4.4 of Mercurial and I'm going to present the main (new) features in this post.
While this wasn't exactly in the initial scope of the program, the most interesting result of my work is arguably the introduction of the "followlines" feature set. The idea is to build upon the log command instead of annotate to make it easier to follow changes across revisions and the new thing is to make this possible by filtering changes only affecting a block of lines in a particular file.
The first component introduced a revset, named followlines, which accepts at least two arguments a file path and a line range written as fromline:toline (following Python slice syntax). For instance, say we are interested the history of LookupError class in mercurial/error.py module which, at tag 4.3, lives between line 43 and 59, we'll use the revset as follows:
$ hg log -r 'followlines(mercurial/error.py, 43:59)' changeset: 7633:08cabecfa8a8 user: Matt Mackall <email@example.com> date: Sun Jan 11 22:48:28 2009 -0600 summary: errors: move revlog errors changeset: 24038:10d02cd18604 user: Martin von Zweigbergk <firstname.lastname@example.org> date: Wed Feb 04 13:57:35 2015 -0800 summary: error: store filename and message on LookupError for later changeset: 24137:dcfdfd63bde4 user: Siddharth Agarwal <email@example.com> date: Wed Feb 18 16:45:16 2015 -0800 summary: error.LookupError: rename 'message' property to something else changeset: 25945:147bd9e238a1 user: Gregory Szorc <firstname.lastname@example.org> date: Sat Aug 08 19:09:09 2015 -0700 summary: error: use absolute_import changeset: 34016:6df193b5c437 user: Yuya Nishihara <email@example.com> date: Thu Jun 01 22:43:24 2017 +0900 summary: py3: implement __bytes__() on most of our exception classes
This only yielded changesets touching this class.
This is not an exact science (because the algorithm works on diff hunks), but in many situations it gives interesting results way faster that with an iterative "annotate" process (in which one has to step from revision to revision and run annotate every times).
The followlines() predicate accepts other arguments, in particular, descend which, in combination with the startrev, lets you walk the history of a block of lines in the descending direction of history. Below the detailed help (hg help revset.followlines) of this revset:
$ hg help revsets.followlines "followlines(file, fromline:toline[, startrev=., descend=False])" Changesets modifying 'file' in line range ('fromline', 'toline'). Line range corresponds to 'file' content at 'startrev' and should hence be consistent with file size. If startrev is not specified, working directory's parent is used. By default, ancestors of 'startrev' are returned. If 'descend' is True, descendants of 'startrev' are returned though renames are (currently) not followed in this direction.
The second milestone of the program was to port this followlines filtering feature into hgweb. This has been implemented as a line selection mechanism (using mouse) reachable from both the file and annotate views. There, you'll see a small green ± icon close to the line number. By clicking on this icon, you start the line selection process which can be completed by clicking on a similar icon on another line of the file.
After that, you'll see a box inviting you to follow the history of lines <selected range> , either in the ascending (older) or descending (newer) direction.
Here clicking on the "newer" link, we get:
As you can notice, this gives a similar result as with the command line but it also displays the patch of each changeset. In these patch blocks, only the diff hunks affecting selected line range are displayed (sometimes with some extra context).
At this point, the "followlines" feature is probably complete as far as hgweb is concerned. In the remainder of the MOSS program, I'd like to focus on a command-line interface producing a similar output as the one above in hgweb, i.e. filtering patches to only show followed lines. That would take the form of a --followlines/-L option to hg log command, e.g.:
$ hg log -L mercurial/error.py,43:59 -p
That's something I'd like to tackle at the upcoming Mercurial 4.4 sprint in Dublin!