Over the past year we went through a series of different development phases and got to learn a lot of different process techniques – more importantly, how things can spiral out of control and how complicated source code control can get!
We have a repository for a stable product, lets call it repoA. Using the core of repoA we have built a new product whose repository is repoB (forked from repoA). The twist in the tail was that because of the new product constraints, we were required to strip down some code from repoB.
So far so good and everything works well on both different product lines UNTILL we counter critical issues whose fixes are made in repoA, which are required in repoB. Teams working on both products are different and now we had to merge changes from repoA to repoB.
How do you merge 2 different repositories (one forked from the other)?
$ cd <path-to-repoB> $ git remote add repoA <git-URL-for-repoA> $ git pull repoA
Twist 1: Both repositories have progressed ahead with their development, so we required to get some point-fixes or specific commits from repoA to repoB.
Can I pull certain commits from repoA and merge into repoB?
git fetch remote git merge $commit_id
But life is not always so simple 😉 repoA and repoB started become a little diverse and eventually repoB was made a sub-directory in another repoC. This was a mistake: I should have made it a git sub-module but for *some* reason I decided to keep it clean and made is a sub-directory.
So, the repoA eventually because an ‘admin’ directory in repoC and repoB was killed. But along with this any ‘git magic’ was also destroyed. And as luck would have it — we were using rest-client and sinatra and a few relatively new libraries and again there were some major core changes made in repoA which (obviously) management requested in repoC. WTF!
How do I merge repoA into a subdirectory ‘admin’ in repoC?
Both have the same directory structure, so I was given the task of patching things together. I had no choice but to get my hands dirty with diff and patch and other tools. Here is what I did.
git checkout master # for repoC git clone <repoA> # in some other directory diff -ru -x.git <path-for-repoC-master>/admin . > diff_file # <pwd: In the repoA directory> patch -p0 < diff_file # pwd: In the repoC directory
Sounds simple enough. Here are the gotchas I found while doing this 🙂
Gotcha 1: diff format using -u
diff -u ensures ‘unified context’ which is important when using patch command for ‘easy’ merging.
$ diff -r -x.git /Users/gautam/work/repoC/admin . > diff_file # without the -u option diff -r -x.git /Users/gautam/work/repoA/config.ru ./config.ru 11c11 < Admin.new File.dirname( __FILE__ ) + "/config/config.yml" --- > Admin.new( File.dirname( __FILE__ ) + "/config/config.yml" )
With the -u option:
$ diff -ru -x.git /Users/gautam/work/repoC/admin . > diff_file # with the -u option diff -ru -x.git /Users/gautam/work/repoC/admin/config.ru ./config.ru --- /Users/gautam/work/repoC/admin/config.ru 2010-04-30 14:01:09.000000000 +0530 +++ ./config.ru 2010-04-29 17:11:54.000000000 +0530 @@ -8,6 +8,6 @@ set :root, File.dirname(__FILE__) set :environment, ENV[ 'RACK_ENV' ] -Admin.new File.dirname( __FILE__ ) + "/config/config.yml" +Admin.new( File.dirname( __FILE__ ) + "/config/config.yml" )
Notice the -x.git ! Its important to ensure that we exclude all files from the .git directory otherwise you have just messed it up further!
Gotcha 2: Create your diff file in the right repository. patch -R option (reverse option)
Notice that I was in repoA when I created a diff file. This means that I am diff’ing files in my CURRENT directory with those in another directory. This helps we by-pass the issue of the admin directory in repoC. Patch uses the second file name (in the case above it would be ./config.ru) to patch files.
Now, move the diff file to repoC/admin and issue:
$ patch -p0 < diff_file
In the unfortunate event that you land up with a different order use -R option to take the first filename during patching 😉 How did I figure this out you ask? I had to get through hell to figure out how to resolve this error:
gautam:admin gautam$ patch -p0 < 2.diff can't find file to patch at input line 7 Perhaps you used the wrong -p or --strip option? The text leading up to this was: -------------------------- |diff -r -x.git /Users/gautam/work/repoC/admin/config.ru ./config.ru -------------------------- File to patch: