When working on an OpenERP project for a customer, we (sadly too frequently) have to keep in its private SVN repository a local copy of a module. I feel quite dirty when doing so but I have to admit it’s a pragmatic approach. Especially when you have to quickly add loads of customizations on a tight schedule.
Months later, the tainted copy residing in SVN starts rotting, stucked with its customizations in an old version of the module. Meanwhile, the original module was updated at a fast pace and kept all its freshness and purity. It’s time to reconcile the two versions and backport commits from Git to Subversion. The graph below sums up the situation:
The arrow at the bottom between Git and Subversion is what we have done when we decided to copy the module in the customer’s project repository. The top arrow is what we want to do.
The module we forked is the matrix widget for OpenERP. The copy happened at commit
8f189e44a3 and landed in SVN in revision
301. That’s our bottom arrow from the illustration.
Since then, the matrix module evolved a lot. It is now in Git at commit
b2810f0024. We want to merge back these changes to Subversion (currently at revision
Let’s start by downloading a copy of the original module:
$ git clone firstname.lastname@example.org:Smile-SA/smile_openerp_matrix_widget.git $ cd smile_openerp_matrix_widget
Now we’ll import in a Git branch all customizations made in the copy living in SVN:
$ git svn clone --no-metadata -r301:HEAD --username kevin svn://svn.company.com:3690/customer-project/trunk . $ git branch svn-trunk-copy git-svn $ git checkout svn-trunk-copy
At that point we don’t need the remote
$ git branch -r -D git-svn
As usual, the SVN repository is a mess and contain numerous stuff unrelated to our original matrix module. The only folders I want to keep, corresponding to the original Git repository, are located in:
Let’s remove all other content:
$ git filter-branch --force --prune-empty --tree-filter 'find ./ -not -ipath "*_matrix_*" -and -not -path "./addons-web" -and -not -path "./addons-server" -and -not -path "./.git*" -and -not -path "./" | xargs rm -rf' --
I’ll then move back these folders at the root of the SVN branch, to replicate the layout of the original Git repository:
$ git filter-branch --force --prune-empty --tree-filter 'test -d ./addons-web && cp -axv ./addons-web/* ./ && rm -rf ./addons-web || echo "No ./addons-web folder found"' -- $ git filter-branch --force --prune-empty --tree-filter 'test -d ./addons-server && cp -axv ./addons-server/* ./ && rm -rf ./addons-server || echo "No ./addons-server folder found"' --
Finally we remove unwanted Git metadata:
$ rm -rf ./.git/svn/ $ rm -rf ./.git/refs/original/ $ git reflog expire --all $ git gc --aggressive --prune
If you’re lost in this cleaning step, please have look at a previous article in which I explain how I re-arranged a messy Subversion repository into a clean Git project.
Now that we have a good looking SVN branch similar to our Git’s, we can proceed to the merging itself:
The merged result is sitting on the
svn-trunk-copy branch. Git made all the hard work. All you have to do is resolve tiny conflicts by hand.
Then commit back the result to your Subversion repository, in the right location:
$ cd .. $ svn co svn://svn.company.com:3690/customer-project/trunk $ cp -axv ../smile_openerp_matrix_widget/smile_matrix_widget ./trunk/addons-web/ $ cp -axv ../smile_openerp_matrix_widget/smile_matrix_field ./trunk/addons-server/ $ svn commit -m "Merge back all changes from commit 8f189e44a3:b2810f0024 of the original smile_openerp_matrix_widget Git repository." ./trunk