12836
Comment: link to ConvertExtension
|
9823
Remove example HgSubversion usage because it was redundant considering HgSubversion extension has its own page.
|
Deletions are marked like this. | Additions are marked like this. |
Line 1: | Line 1: |
While pushing changes back to Subversion is not officially supported yet, interoperating with it is possible with third-party tools or existing extensions. This page will detail the options and discuss their pros and cons. [[TableOfContents(2)]] = Working with Subversion Repositories = For the following recipes, let's have a repository at ''svn://repo.org/subversion'' with a wanted module ''trunk''. |
<<TableOfContents(2)>> = Working with Subversion (SVN) = Interoperating with SVN is possible with third-party tools or existing extensions. This page will detail the options and discuss their pros and cons. == With hgsubversion == ''HgSubversion'' is a third-party tool dedicated to convenient SVN interoperability, using standard `hg` commands. See the HgSubversion extension page for installation instructions and example usage. === Pros === * Simple and fast to setup. * Can pull new revisions, using svn replay protocol if available which is the fastest way to grab svn revisions. * Can directly push back changes, so it's a full subversion bridge. * Uses standard hg commands, so learning curve is relatively short. === Cons === * For huge svn repositories the first clone is slow. * Depends on svn python bindings. There are plans to use ctypes instead, nothing done yet. * May fail to import repositories with strange history elements * If you encounter this on a public OSS repository, please file a bug so the developers can look into the problem |
Line 10: | Line 24: |
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. |
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. For this example, assume we have a repository at ''svn://repo.org/subversion'' with a wanted module ''trunk''. |
Line 28: | Line 41: |
Line 37: | Line 49: |
Line 39: | Line 50: |
Line 42: | Line 54: |
Line 60: | Line 71: |
Line 73: | Line 83: |
Line 113: | Line 122: |
Line 132: | Line 140: |
Line 144: | Line 151: |
Line 146: | Line 152: |
First use [:ConvertExtension:convert extension] to create a local reference mirror: {{{ $ hg convert --config convert.hg.tagsbranch=0 svn://repo.org/subversion/trunk trunk-mirror |
First use [[ConvertExtension|convert extension]] to create a local reference mirror. For this example, assume the SVN repository is at ''svn://repo.org/subversion'' with a wanted module ''trunk''. {{{ $ hg --config convert.hg.tagsbranch=0 convert svn://repo.org/subversion/trunk trunk-mirror |
Line 162: | Line 168: |
Recent Mercurial versions (as of 963000ed8cac, > 0.9.5), support shallow conversions like: {{{ $ hg convert --config convert.svn.startrev=3 --config convert.hg.tagsbranch=0 svn://repo.org/subversion/trunk trunk-mirror |
Mercurial versions from 1.0 support shallow conversions like: {{{ $ hg --config convert.svn.startrev=3 --config convert.hg.tagsbranch=0 convert svn://repo.org/subversion/trunk trunk-mirror |
Line 174: | Line 180: |
Line 188: | Line 193: |
$ hg pull ../trunk-mirror | $ hg pull ../trunk-mirror |
Line 203: | Line 208: |
Line 209: | Line 213: |
$ hg convert --config convert.hg.tagsbranch=0 svn://repo.org/subversion/trunk trunk-mirror | $ hg --config convert.hg.tagsbranch=0 convert svn://repo.org/subversion/trunk trunk-mirror |
Line 227: | Line 231: |
Line 246: | Line 249: |
Line 251: | Line 253: |
$ hg convert --config convert.hg.tagsbranch=0 svn://repo.org/subversion/trunk trunk-mirror | $ hg --config convert.hg.tagsbranch=0 convert svn://repo.org/subversion/trunk trunk-mirror |
Line 274: | Line 276: |
Line 283: | Line 284: |
* Requires a recent Mercurial version, Subversion python bindings may be tricky to setup, even impossible to setup with Windows binary packages. == 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 == With hgsubversion == ''hgsubversion'' is a third-party tool dedicated to convenient Subversion interoperability. Sources can be found at http://www.bitbucket.org/durin42/hgsubversion/. To install it, you currently need to follow the steps layed out in this blog post by Ben Collins-Sussman: http://blog.red-bean.com/sussman/?p=116 Let's say you want to contribute to CPython. You first svnclone the repository. {{{ $ hg svnclone http://svn.python.org/projects/python python-hg }}} This way you get a folder named "python-hg" containing both a hg repo with each changeset mirroring an SVN revision. When you want to produce a patch to the maintainers, it's simple: {{{ $ hg di -b -r last_svn_revision:your_tip > mybugfix.patch }}} When you want to push the changes directly into svn, you first pull the latest changes from svn, then rebase your changes to the svn HEAD and push them back. {{{ $ hg svn pull # pull the changes from svn $ hg up your_head # update the repo to the head of the changes you want to push to svn $ hg svn rebase # rebase your_head onto svn $ hg svn push }}} You can also first pull from svn and then generate a patch. This works just like pushing back, with the exception that you create the patch instead of pushing. {{{ $ hg svn pull $ hg up your_head $ hg svn rebase $ hg di -b -r last_svn_revision:tip > mybugfix.patch }}} Naturally you can use any of the various possible workflows to get the changes into the Mercurial repository you created with hgsubversion, including seperate feature or bugfix clones, but remember that you rebase the hgsubversion repository. === Pros === * Simple and fast to setup. * Can pull new revisions, using svn replay protocol if available which is the fastest way to grab svn revisions. * Can directly push back changes, so it's a full subversion bridge. === Cons === * Still in alpha quality as of 2008-12-29. * Currently it assumes and requires a standard svn layout with trunk/ branches/ and tags/ as of 2008-12-29. * For huge svn repositories the first svnclone is slow. * Depends on svn python bindings. There are plans to use ctypes instead, nothing done yet. |
* Requires a recent Mercurial version, Subversion python bindings may be tricky to setup. Hint for Windows users: TortoiseHg includes them. |
Line 396: | Line 287: |
Line 400: | Line 290: |
Line 401: | Line 292: |
* (./) This is a native feature of hgsubversion. |
|
Line 402: | Line 295: |
* (./) This is provided in hgsubversion as ''hg rebase --svn''. |
Working with Subversion (SVN)
Interoperating with SVN is possible with third-party tools or existing extensions. This page will detail the options and discuss their pros and cons.
1. With hgsubversion
HgSubversion is a third-party tool dedicated to convenient SVN interoperability, using standard hg commands.
See the HgSubversion extension page for installation instructions and example usage.
1.1. Pros
- Simple and fast to setup.
- Can pull new revisions, using svn replay protocol if available which is the fastest way to grab svn revisions.
- Can directly push back changes, so it's a full subversion bridge.
- Uses standard hg commands, so learning curve is relatively short.
1.2. Cons
- For huge svn repositories the first clone is slow.
- Depends on svn python bindings. There are plans to use ctypes instead, nothing done yet.
- May fail to import repositories with strange history elements
- If you encounter this on a public OSS repository, please file a bug so the developers can look into the problem
2. 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. For this example, assume we have a repository at svn://repo.org/subversion with a wanted module trunk.
$ 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
If you want to have versioning on your patches, do:
$ hg qinit -c
Use MQ in the normal way, doing qrefresh (and qcommit if using versioning).
Once we are ready to push the patches, first resync with svn:
$ hg qpop -a $ 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 -a 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.
2.1. Pros
- Simple, fast and cheap to setup
- No heavy dependencies, only requires an SVN client and MQ extension.
2.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
3. With Convert extension
First use convert extension to create a local reference mirror. For this example, assume the SVN repository is at svn://repo.org/subversion with a wanted module trunk.
$ hg --config convert.hg.tagsbranch=0 convert 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
Mercurial versions from 1.0 support shallow conversions like:
$ hg --config convert.svn.startrev=3 --config convert.hg.tagsbranch=0 convert svn://repo.org/subversion/trunk trunk-mirror initializing destination trunk-mirror repository scanning source... sorting... converting... 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 we do not 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 --config convert.hg.tagsbranch=0 convert 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 --config convert.hg.tagsbranch=0 convert 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.
3.1. Pros
- Full SVN history. It means "hg grep", "hg annotate", "hg bisect" and good merges.
3.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.
- Pushing changes may still be tedious.
Requires a recent Mercurial version, Subversion python bindings may be tricky to setup. Hint for Windows users: TortoiseHg includes them.
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.
This is a native feature of hgsubversion.
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. What about RebaseExtension?
This is provided in hgsubversion as hg rebase --svn.