Differences between revisions 1 and 2
Revision 1 as of 2008-02-17 15:48:02
Size: 8396
Comment:
Revision 2 as of 2008-02-20 21:25:01
Size: 10120
Comment:
Deletions are marked like this. Additions are marked like this.
Line 266: Line 266:
=== With hgpullsvn ===

* TODO *
=== With hgsvn ===

''hgsvn'' is a third-party tool dedicated Subversion interoperability. Sources can be found at http://pypi.python.org/pypi/hgsvn/.

Let's say you want to contribute to CPython. You first import a recent chunk of the CPython history (starting from rev 60800 - so that it doesn't take too much time):

{{{
$ hgimportsvn -r 60800 http://svn.python.org/projects/python/trunk
$ cd trunk
$ hgpullsvn
$ cd ..
}}}

You then have a folder named "trunk" containing both an hg repo (with each changeset mirroring an SVN revision) and an SVN checkout. It has an unique named branch "trunk".

You clone that repo in order to start hacking:

{{{
$ hg clone trunk mybugfix
$ cd mybugfix
$ hg branch mybugfix
 # Hacking session, lots of hg add/hg ci etc.
}}}

When you want to produce a patch to the maintainers, it's simple:

{{{
$ hg di -b -r trunk > mybugfix.patch
}}}

Often patches are not reviewed immediately, so you will want to regenerate them according to the latest changes in SVN trunk. You first fetch those changes:

{{{
$ cd ../trunk
$ hgpullsvn
$ cd ../mybugfix
$ hg pull
 # Merge changes from trunk into the current "mybugfix" branch
$ hg merge
$ hg ci
}}}

Then you can regenerate the patch (of course you may have to rework some code if the latest trunk changes conflict with your modifications):

{{{
$ hg di -b -r trunk > mybugfix2.patch
}}}

==== Pros ====
 * Simple and fast to setup, can synchronize at a given Subversion revision, and pull new ones.
 * No heavy dependencies, only requires an SVN and a Mercurial client.

==== Cons ====
 * Cannot push to SVN, MQ can be used for this part with the same drawbacks described above.
 * Pulling new Subversion revisions may be slow

Working with Subversion Repositories

While pushing changes back to Subversion is not officially supported yet, interoperating with it is possible with third-party tools or existing extensions.

1. Recipes

Let's have a repository at svn://repo.org/subversion with a wanted module trunk.

1.1. With MQ only

If we don't really care about existing history, and only want to hack an existing Subversion codebase with Mercurial and push changes back, we can start with an svn checkout and track it into Mercurial.

$ svn co svn://repo.org/subversion/trunk trunk
A    trunk/a
Checked out revision 1.
$ cd trunk
$ hg init
$ cat > .hgignore <<EOF
> \.svn/
> \.hgignore$
> EOF
$ hg st
? a
$ hg ci -Am "Initializing from subversion checkout"
adding a

Now we hack the sources. The trick is not to commit in Mercurial but to store all the changes as MQ patches.

$ echo b >> a
$ hg st
M a
$ hg qnew -f changea

Once we are ready to push the patches, first resync with svn:

$ svn up
U    a
A    b
Updated to revision 3.
$ hg st
M a
? b
$ hg addre -s 75
adding b
$ hg ci -m upstream

Then apply the patch queue again.

$ hg qpush
applying changea
patching file a
Hunk #1 FAILED at 0
1 out of 1 hunk FAILED -- saving rejects to file a.rej
patch failed, unable to continue (try -v)
patch failed, rejects left in working dir
Errors during apply, please fix and refresh changea

Oups, our changes do not apply any longer, we have to rebase:

$ hg up -C .^
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg qpush -a
applying changea
Now at: changea
$ hg qsave -c -e
copy .hg/patches to .hg/patches.1
$ hg heads
changeset:   4:ace2da3ad857
tag:         tip
user:        Patrick Mezard <pmezard@gmail.com>
date:        Sun Feb 17 15:39:11 2008 +0100
summary:     hg patches saved state

changeset:   2:6e35211ce252
user:        Patrick Mezard <pmezard@gmail.com>
date:        Sun Feb 17 15:38:11 2008 +0100
summary:     upstream

$ hg up -C 2
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg qpush -am
merging with queue at: .hg/patches.1
applying changea
patching file a
Hunk #1 FAILED at 0
1 out of 1 hunk FAILED -- saving rejects to file a.rej
patch failed, unable to continue (try -v)
patch failed, rejects left in working dir
patch didn't work out, merging changea
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
merging a
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
Now at: changea

In most case, you won't have to do that, the patch queue will apply cleanly. Now, we can apply one patch after another and push them into Subversion.

$ hg qpop -a
Patch queue now empty
$ hg qpush
applying changea
Now at: changea
$ svn st
?      .hgignore
?      .hg
M      a
$ svn ci -m "change a"
Sending        a
Transmitting file data .
Committed revision 5.
$ hg qdel -r qbase:qtip

At this point, our changes are in Subversion and in our local Mercurial mirror as real revisions. Large chunks of this process can be automated.

1.1.1. Pros

  • Simple, fast and cheap to setup
  • No heavy dependencies, only requires an SVN client and MQ extension.

1.1.2. Cons

  • Renames cannot be handled that way because they cannot be recorded in Subversion after they happened. You have to perform them directly in SVN, outside of this workflow.
  • No SVN history. It means no "hg grep", "hg annotate", "hg bisect" and dumb merges.
  • May be tedious

1.2. With Convert extension

First create a local reference mirror:

$ hg convert --config convert.hg.tagsbranch=0 svn://repo.org/subversion/trunk trunk-mirror
initializing destination trunk-mirror repository
scanning source...
sorting...
converting...
4 Repository initialization
3 append c to a
2 add b
1 change a
0 change a

Prepare the working copy. Instead of cloning the mirror, we checkout a working copy, make it a Mercurial repository, pull from the mirror and synchronize both. This way we end with an hybrid Subversion and Mercurial working copy.

$ svn co svn://repo.org/subversion/trunk trunk
A    trunk/a
A    trunk/b
Checked out revision 5.
$ cd trunk
$ hg init
$ cat > .hgignore <<EOF
> \.svn/
> \.hgignore$
> EOF
$ hg pull ../trunk-mirror 
pulling from ../trunk-mirror
requesting all changes
adding changesets
adding manifests
adding file changes
added 5 changesets with 5 changes to 2 files
(run 'hg update' to get a working copy)
$ hg up -C
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg st
$ svn st
?      .hgignore
?      .hg

We hack it and end with an collection of patches in MQ. The situation is very similar to the one in "With MQ only" section. The difference is do not we change the patches into Mercurial revisions, we push them and get rid of them when done. Synchronize with Subversion first:

$ hg qpop -a
$ cd ..
$ hg convert --config convert.hg.tagsbranch=0 svn://repo.org/subversion/trunk trunk-mirror
scanning source...
sorting...
converting...
0 add c
$ cd trunk
$ svn up
A    c
Updated to revision 6.
$ hg pull -u ../trunk-mirror
pulling from ../trunk-mirror
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
1 files updated, 0 files merged, 0 files removed, 0 files unresolved

Then apply the patches:

$ hg qpush
applying changea
Now at: changea
$ svn st
?      .hgignore
?      .hg
M      a
$ svn ci -m "change a"
Sending        a
Transmitting file data .
Committed revision 7.
$ hg qpop
Patch queue now empty
$ hg qdel changea

Synchronize with Subversion again:

$ cd ..
$ hg convert --config convert.hg.tagsbranch=0 svn://repo.org/subversion/trunk trunk-mirror
scanning source...
sorting...
converting...
0 change a
$ cd trunk
$ svn up
At revision 7.
$ hg pull -u ../trunk-mirror
pulling from ../trunk-mirror
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
[16:14:55][pmezard:~/dev/mercurial/svn/trunk]$ hg tip
changeset:   6:2fba38614917
tag:         tip
user:        pmezard
date:        Sun Feb 17 15:13:55 2008 +0000
summary:     change a

Again, large parts of this process can be scripted.

1.2.1. Pros

  • Full SVN history. It means "hg grep", "hg annotate", "hg bisect" and good merges.

1.2.2. Cons

  • Setup is expensive. Convert extension requires all SVN history of the selected branch(es) to be converted. This can be prohibitive with large, old and remove repositories. Implementing shallow conversions is possible.
  • Renames cannot be handled that way because they cannot be recorded in Subversion after they happened. You have to perform them directly in SVN, outside of this workflow.
  • Pushing changes may still be tedious.
  • Requires a recent Mercurial version, Subversion python bindings may be tricky to setup, even impossible to setup with Windows binary packages.

1.3. With hgsvn

hgsvn is a third-party tool dedicated Subversion interoperability. Sources can be found at http://pypi.python.org/pypi/hgsvn/.

Let's say you want to contribute to CPython. You first import a recent chunk of the CPython history (starting from rev 60800 - so that it doesn't take too much time):

$ hgimportsvn -r 60800 http://svn.python.org/projects/python/trunk
$ cd trunk
$ hgpullsvn
$ cd ..

You then have a folder named "trunk" containing both an hg repo (with each changeset mirroring an SVN revision) and an SVN checkout. It has an unique named branch "trunk".

You clone that repo in order to start hacking:

$ hg clone trunk mybugfix
$ cd mybugfix
$ hg branch mybugfix
 # Hacking session, lots of hg add/hg ci etc.

When you want to produce a patch to the maintainers, it's simple:

$ hg di -b -r trunk > mybugfix.patch

Often patches are not reviewed immediately, so you will want to regenerate them according to the latest changes in SVN trunk. You first fetch those changes:

$ cd ../trunk
$ hgpullsvn
$ cd ../mybugfix
$ hg pull
 # Merge changes from trunk into the current "mybugfix" branch
$ hg merge
$ hg ci

Then you can regenerate the patch (of course you may have to rework some code if the latest trunk changes conflict with your modifications):

$ hg di -b -r trunk > mybugfix2.patch

1.3.1. Pros

  • Simple and fast to setup, can synchronize at a given Subversion revision, and pull new ones.
  • No heavy dependencies, only requires an SVN and a Mercurial client.

1.3.2. Cons

  • Cannot push to SVN, MQ can be used for this part with the same drawbacks described above.
  • Pulling new Subversion revisions may be slow

2. Improvement Discussion

Mercurial interoperability with Subversion is clearly not as good as Git or Bazaar-NG one, and this is a real showstopper for massive adoption. The question is what can be improved and in which order so we move from "existing but tedious" to "usable".

  • Shallow conversions. Convert extension inability to convert partially makes it unusable with most of remote repositories. Imperfect suggestions were made in this direction, see "convert: add --startrev option" development mailing thread for more details (http://selenic.com/pipermail/mercurial-devel/2008-January/004004.html).

  • Pushing back to Subversion. A functional svn sink is implemented in Convert extension but it is very basic and lack documentation. There is a test-convert-hg-svn test using it though.

  • Rebasing. There is not simple native branch rebasing support in Mercurial. This feature is very useful to update the branch being worked on on top of the latest Subversion revision. In the meantime, it can be done with MQ.

WorkingWithSubversion (last edited 2013-05-17 06:26:53 by rcl)