Is it possible to exclude specific commits when doing a git merge?

Git

Git Problem Overview


Let's say that I want to merge from a release branch to the master branch and there are some commits in the release branch that I don't want to include in the master branch. Is there a way to do the merge so that one or more of those commits will not be merged?

My strategy so far is to do the following (in master):

git merge --no-commit release-branch
# Resolve conflicts and apply reverse patch of the commits that I don't want included
git commit # Edit commit message so that it lists the commits that have been reverse-patched

Is there a better way to do this?

Git Solutions


Solution 1 - Git

I've found a solution that works for me in the Pro Git book.

Let's say you want to exclude the file config.php.

On branch A:

  1. Create a file named .gitattributes in the same dir, with this line: config.php merge=ours. This tells git what strategy to use when merging the file. In this case it always keep your version, ie. the version on the branch you are merging into.

  2. Add the .gitattributes file and commit

On branch B: repeat steps 1-2

Try merging now. Your file should be left untouched.

Solution 2 - Git

Create a new branch, rebase the branch interactively and drop commits you don't want, and then merge that.

You can't take changes out of the middle of a branch without rehashing, but the right thing will happen when it sees the same changes in a later merge (e.g. from cherry-picking and what-not).

Solution 3 - Git

If you have a support branch where you fix bugs and build new versions. On master you have the next version where you also build new versions frequently.

Every time you build a new version you change the version in some file, commit that new file, create a tag and push. Now merges from support to master will always have conflicts in the file containing the version info.

If the file containing the version information only contains the version information, you can go with the answer of fcurella. But if it does indeed may also contain mergeable information (pom.xml, gradle.properties, MANIFEST.MF, ...), you must perform some extra action.

Lets use the following example

      C---D*---E---F* support
     /
A---B---G---H*---I master

where commits with stars contain only changes due to version changes that should be ignored during merge.

To merge support into master without merge-conflicts due to the version builds, you can do either of the following:

Multiple merge commits

git checkout master
git merge C
git merge D -s ours
git merge E
git merge F -s ours

With the -s ours argument we are telling git to only record a merge without altering the workspace. This is comparable to the --record-only option of svn.

The above will result in the following layout

      -------------C---D*---E---F* support
     /              \   \    \   \
A---B---G---H*---I---J---K----L---M master

One merge commit using cherry-pick

git checkout master
git merge support -s ours --no-commit
git cherry-pick C E --no-commit
git commit -m 'merged support into master'

first we are starting a merge but only record that we are merging, without altering the workspace and without doing the merge commit. Then we are cherry-picking the commits to merge, again without commit. Finally we are committing the merge.

The above will result in the following layout

      C---D*---E---F* support
     /              \
A---B---G---H*---I---J master

One could even automate the cherry-picking.

git checkout master
git merge support -s ours --no-commit
for id in `git log support --reverse --not HEAD --format="%H [%an] %s" |
  grep -v "bump version" |
  sed "s/\(\w*\)\s.*/\1/g"`
do
  git cherry-pick --no-commit $id
done
git commit -m 'merged support into master'

Solution 4 - Git

The reason why this can't be done directly is that every commit contains links to the parent commits (typically just one but several for merges). That way if you have one commit (by its SHA1 sum) the whole history is also fixed as the parents also contain links to their parents and so on. So the only way to leave out patches in the history is to write a new one. git rebase -i on a newly created branch is probably the easiest way of achieving that.

Solution 5 - Git

The main question is: how do you want to represent the commits you want to skip?

  1. sneakily hide them (not my favorite)
  2. explicitly skip them
  3. explicitly undo them

Unfortunately no. 2 is impossible to express in the history graph.

No. 1 is possible, but I would never do that: a merge commit can contain changes. – Normally a merge commit points to the result of the merge of two more more branches: all things developed in those branches should be in the code after the merge (i.e. in the code pointed to by the merge commit). And nothing else should be in this commit.

But surprise, surprise, you can change the entire code base and represent it as a merge. The effect of a merge is two-fold: it merges the history tree and it should merge two code bases. The former it does for sure (otherwise no-one calls it a merge), for the latter it might fail, e.g. when a merge conflict arose and it was resolved wrongly (then the code bases were not merged properly).

Some of the other answers suggest this hiding. I recommend the explicit way: merge plus revert commits.

Solution 6 - Git

It's also possible to modify .git/info/attributes file and keep it inside .git folder instead of adding .gitattribute files all over that will require adding them to source control eventually.

Solution 7 - Git

If you only want to exclude some commits that are at the end, you can just commit to a specific commit number:

git checkout partlyMergedFrom
git whatchanged
--> find the commit hash up to where you want to merge
git checkout partlyMergedInto
git merge e40a0e384f58409fe3c864c655a8d252b6422bfc
git whatchanged
--> check that you really got all the changes you want to have

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionreadonlyView Question on Stackoverflow
Solution 1 - GitfcurellaView Answer on Stackoverflow
Solution 2 - GitDustinView Answer on Stackoverflow
Solution 3 - GitTobias SchulteView Answer on Stackoverflow
Solution 4 - Gituser43563View Answer on Stackoverflow
Solution 5 - GitRobert SiemerView Answer on Stackoverflow
Solution 6 - GitKuzneroView Answer on Stackoverflow
Solution 7 - GitMazeView Answer on Stackoverflow