16
votes

Because of work policies, I'm forced to use Subversion. Currently I've developed some code using Mercurial, and I have to convert this repository to a new Subversion repository. The current Mercurial repository also contains legacy code parts that I copied from another SVN repository (a HW driver), and this code contains keywords (like $Id: ...$) that were expanded while I copied the code.

My current approach to do the conversion is:

hg convert --dest-type svn repository.hg repository.svn

This works, unfortunately it does not preserve the timestamps of Mercurial revisions. Additionally, it expands tags in some legacy code parts where someone wrote $Id: ...$ stuff.

The questions:

  1. How can I export to Subversion, keeping the timestamps of revisions.
  2. How can I convert without expanding $Id:$ style keywords, or, even better, delete the offending lines while converting. Is this possible, or do I have to modify the history of the Mercurial dump before converting to make sure no keywords are present?

Sorry if this question has been answered already, I've searched intensely with Google and on here, but I cannot find an answer for this problem. Especially the timestamp problem must be something that others have already run into.

Cheers, H.

EDIT: I've since created a script that extracts timestamps from a Mercurial export file and updates a Subversion dumpfile, which is nothing more but a kludge, but it works. The $Id: problem is still unsolved, unfortunately.

4

4 Answers

3
votes

It is a tricky issue. Why aproaching the solution differently:

https://bitbucket.org/durin42/hgsubversion/wiki/Home

This is an extension to Mercurial which allows you to access it as a SVN Client, could it work for you?

2
votes

I want to re-suggest hgsubversion as a solution. In the normal case, it is used to access a Subversion repository using hg, but you can use it to convert a Mercurial repository to Subversion as well if you are not afraid to work on your history of changesets. (If you know how to work with changesets, you have quite a bit of control over the process.)

Essentially, you have to do the following steps:

  1. Make your Mercurial history linear (this is required for Subversion); you can use the Collapse extension to collapse any non-linear area of the history into a single changeset
  2. Create a Subversion repository or directory and hg pull this SVN path (using hgsubversion) into your Mercurial repository. The Subversion path may have to be non-empty for this, but as long as there is no overlap with the files in the Mercurial repository, this should be fine.
  3. Rebase all of the changesets from the Mercurial history on top of the head of the Subversion history. This is done using the Rebase extension. If there is no overlap, this should be an automatic operation.
  4. Use hg push to send the result back to Subversion. The linear set of changes that descend from the former head of the Subversion repository will be added to the Subversion path.

Check this blog article for some concrete command-line commands that you can use.

2
votes

I tried the keyword substitution once on SVN and I had to add a svn:keywords property to the working folder indicating which keyword(s) to be substituted. So, deleting this property should do the job.

This article on Stack Overflow is possibly related to this question.

1
votes

As far as I understand your problem now: You have a Hg repository which was once converted from SVN, where keyword expansion had been used. Now the expanded keywords remain unchanged in the Hg repo. You now want to strip those keywords (or just their expanded values) from the Hg history, preserving author, date/time, commit message and of course changed files.

This is just an idea, I have never done this in practical, but ... Speaking just of mercurial, I think it's possible to do this by a combination of usual mercurial behaviour, just for one branch and without tags and other branches:

  1. Clone the intended source repository
  2. Prepare an empty Hg repository
  3. Perform hg log > logfile.txt over the source repo on that branch you want to change
  4. Parse the file getting a structured list of all necessary information like changeset number, user, date and summary, store this into a data structure that allows easy iteration
  5. Iterate through the list (or directly use the log file) and ...
    • delete all working files from the destination repo
    • export a changeset from the source repo to the working copy of the destination repo
    • change anything in the working files you want to do
    • hg addremove
    • hg commit -m ... -d ... -u ...
  6. Repeat step 5 as often as necessary

The drawback is that the changesets will get new hash values, so tags and bookmarks need to be updated.

If there are other branches that have to be migrated:

  1. Update the destination repo to that branch's starting point
  2. and the source repo, too
  3. insert steps 3 to 6 from the above list here

Perhaps there is some more work to be done, but I think this could work. As told before, I've never done this.