I'm using git on a new project that has two parallel -- but currently experimental -- development branches:
master
: import of existing codebase plus a few mods that I'm generally sure ofexp1
: experimental branch #1exp2
: experimental branch #2
exp1
and exp2
represent two very different architectural approaches. Until I get further along I have no way of knowing which one (if either) will work. As I make progress in one branch I sometimes have edits that would be useful in the other branch and would like to merge just those.
What is the best way to merge selective changes from one development branch to another while leaving behind everything else?
Approaches I've considered:
git merge --no-commit
followed by manual unstaging of a large number of edits that I don't want to make common between the branches.Manual copying of common files into a temp directory followed by
git checkout
to move to the other branch and then more manual copying out of the temp directory into the working tree.A variation on the above. Abandon the
exp
branches for now and use two additional local repositories for experimentation. This makes the manual copying of files much more straightforward.
All three of these approaches seem tedious and error-prone. I'm hoping there is a better approach; something akin to a filter path parameter that would make git-merge
more selective.
The simple way, to actually merge specific files from two branches, not just replace specific files with ones from another branch.
Step one: Diff the branches
git diff branch_b > my_patch_file.patch
Creates a patch file of the difference between the current branch and branch_b
Step two: Apply the patch on files matching a pattern
git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch
useful notes on the options
You can use
*
as a wildcard in the include pattern.Slashes don't need to be escaped.
Also, you could use --exclude instead and apply it to everything except the files matching the pattern, or reverse the patch with -R
The -p1 option is a holdover from the *unix patch command and the fact that the patch file's contents prepend each file name with
a/
orb/
( or more depending on how the patch file was generated) which you need to strip so that it can figure out the real file to the path to the file the patch needs to be applied to.Check out the man page for git-apply for more options.
Step three: there is no step three
Obviously you'd want to commit your changes, but who's to say you don't have some other related tweaks you want to do before making your commit.
You can use
read-tree
to read or merge given remote tree into the current index, for example:To perform merge, use
-m
instead.See also: How do I merge a sub directory in git?
I don't like the above approaches. Using cherry-pick is great for picking a single change, but it is a pain if you want to bring in all the changes except for some bad ones. Here is my approach.
There is no
--interactive
argument you can pass to git merge.Here is the alternative:
You have some changes in branch 'feature' and you want to bring some but not all of them over to 'master' in a not sloppy way (i.e. you don't want to cherry pick and commit each one)
So just wrap that in a shell script, change master into $to and change feature into $from and you are good to go:
When only a few files have changed between the current commits of the two branches, I manually merge the changes by going through the different files.
git difftoll <branch-1>..<branch-2>
It's strange that git still does not have such a convenient tool "out of the box". I use it heavily when update some old version branch (which still has a lot of software users) by just some bugfixes from the current version branch. In this case it is often needed to quickly get just some lines of code from the file in trunk, ignoring a lot of other changes (that are not supposed to go into the old version)... And of course interactive three-way merge is needed in this case,
git checkout --patch <branch> <file path>
is not usable for this selective merge purpose.You can do it easily:
Just add this line to
[alias]
section in your global.gitconfig
or local.git/config
file:It implies you use Beyond Compare. Just change to software of your choice if needed. Or you can change it to three-way auto-merge if you don't need the interactive selective merging:
Then use like this:
This will give you the true selective tree-way merge opportunity of just any file in other branch.
While some of these answers are pretty good, I feel like none actually answered OP's original constraint: selecting particular files from particular branches. This solution does that, but may be tedious if there are many files.
Lets say you have the
master
,exp1
, andexp2
branches. You want to merge one file from each of the experimental branches into master. I would do something like this:This will give you in-file diffs for each of the files you want. Nothing more. Nothing less. It's useful you have radically different file changes between versions--in my case, changing an app from Rails 2 to Rails 3.
EDIT: this will merge files, but does a smart merge. I wasn't able to figure out how to use this method to get in-file diff information (maybe it still will for extreme differences. Annoying small things like whitespace get merged back in unless you use the
-s recursive -X ignore-all-space
option)