Programming notes and tools...

Mercurial CSV Log

Mon, 13 May 2019 Mercurial

At work recently I needed to produce a list of all commits to a certain folder in our source control system for a given time period for audit purposes. We use Mercurial for source control, develop on Windows and wanted the results in a spreadsheet readable format. This post is to remind myself how to do this next time I need to do it!

To get a list of changes you need to use the hg log feature which you can filter using revsets. For my use case I wanted to filter by a given folder and date range and exclude merges (since merges generally do not include new code and add a lot of noise to the search). To achieve this I used the following command:

hg log -r"file('path:Required/Folder') and not merge() and date('>2019-02-01') and date('<2019-02-28')"

This will output all non-merge commits that affected Required/Folder in February (as far as I can tell the date filters are inclusive). The next step was to turn the output into csv format. HG log allows you to specify a --template parameter for formatting the output. Most of this is pretty simple, curly braces surround variables that can be piped to formatters (e.g. "{branch}" or "{date|shortdate}", in the second example shortdate is a formatter for the date variable). The fun comes when you want to include the commit description which could include commas, double quotes and new lines.

The following template will output csv style details, one per line, with the last element being the description, quoted with any double quotes replaced by single quotes and any new lines being replaced by ' :: '. The most confusing thing here is the escaping of characters, any pair of double quotes is one escaped double quote for the windows command interpreter (so Mercurial will see individual double quotes), any backslash starts an escaped character in what Mercurial will see.

"{node|short},{branch},{author},{date|shortdate},""{join(splitlines(sub(r'""','\'',desc)), ' :: ')}""\r\n"

Breaking down the description segment we first use the sub method to substitute a double quote with a single quote in desc. We then wrap that in a call to splitlines which gives us a list of single line strings which we put back together using join and ' :: '.

Putting this all together we can run the whole thing and pipe the output to a file as in the following example which will produce a file called commits.csv:

hg log -r"file('path:Required/Folder') and not merge() and date('>2019-02-01') and date('<2019-02-28')" --template "{node|short},{branch},{author},{date|shortdate},""{join(splitlines(sub(r'""','\'',desc)), ' :: ')}""\r\n" > commits.csv

Resource Links

I found the following resources very useful in putting this together: