Feb 19

Mercurial timestamp on commit, how to record datecode as commit date

Category: Linux   — Published by tengo on February 19, 2009 at 8:55 am

Using mercurial (hg) recently, I came across an interesting problem. I had a number of files, properly dated back to their modification time by the filesystem (file_v1.ext was from 2008-June, file_v2.ext was from 2008-Aug etc...). In order to get versioning on these files finally right, I thought about adding them to a mercurial repository, thus vanishing the need to keep all these back-versioned files within the working directory.

When you do a hg commit --help, mercurial will give you these options on commits:

-A --addremove mark new/missing files as added/removed before committing
-I --include include names matching the given patterns
-X --exclude exclude names matching the given patterns
-m --message use as commit message
-l --logfile read commit message from
-d --date record datecode as commit date
-u --user record user as committer

So there is this interesting option of -d --date record datecode as commit date which, as I understand it, will artificially set the date of the commit in the repository dated-back to whatever date you set it, in contrary to the normal behaviour of using the <today> date. Quite handy for my task, as I wanted to reconstruct the versions and dates of my file iterations in the hg repository - and normally hg will save a file's permissions but will forget about the modification date of the files in the repository (right? Please correct me if I am wrong here!). A  hg revert -r <revision-version> --all would bring back the original files of that revision changeset, but with a current date timestamp, no way to reconstruct the mtime epoch, right?

Anyway, to preserve the date of my file-revisions more or less in the repository, I decided to try the -d option, but first I couldn't find out how. No matter how hard you search on the web, there is no proper documentation about the date-format the commit -d switch expects. As all human readable date versions I tried failed, it must expect UNIX epoch format, and yes, more or less, it does.

Update: after a bit more of search, I found help about the expected format in an off-topic section of the inofficial hg manual: "date Date information. The date when the changeset was committed. This is not human-readable; you must pass it through a filter that will render it appropriately. See section 11.6 for more information on filters. The date is expressed as a pair of numbers. The first number is a Unix UTC timestamp (seconds since January 1, 1970); the second is the offset of the committer’s timezone from UTC, in seconds."

Update: hg help dates gives a list of valid date formats.

So, a commit with these options:

hg commit -d '1135425600 0' -m "commit on 2005-12-24T12:00:00"

will effectively add this changeset to the repository, as if you've had commited on christmas 2005, timezoneoffset 0. There are many "human-date to epoch" converters out there, online and offline. Format for hg is -d '<epochdate> <timezoneoffset>'.

Actually, I regret that it isn't possible to define a file that mercurial will use as date reference, something like hg commit -d date filexy.ext -m "commit with autodate from file xy" would be handy. This way, getting the filesystem metadata into a repository representation would be easier, but this might be a too specific request for a standard versioning system...

UPDATE:

Today, I was confronted with this again, and this time I took the time to figure out how to auto-feed the mtime timestamp of a single file into the commit as the commit-date:

hg commit -d "$(stat <filename> --format=%Y) 0" -m "Commit with mtime of <filename>"

You can check what stat <file> --format=%Y does by prepending an echo.
After I solved this here, I found this blog post that achieves the same by using the date command.