git - skipping specific commits when merging

GitVersion Control

Git Problem Overview


I've been using Git for about a year now and think it's fantastic, but I've just started on a second version of the project and started a new branch for it. I'm struggling a little with the best way to handle things going forward.

I have two branches called say master10 (for v1) and master20 (for v2). I've been making bug fixes in v1 on branch master10, and developing new stuff of master20. Whenever I make a bug fix I merge it into v2 by checking out master20 and doing git merge master10. So far so good.

Now however, I've made a change in v1 that I don't want in v2, but I want to continue merging other bug fixes. How do I tell Git to skip that particular commit (or a range of commits), but that going forward I still want to merge other bug fixes.

I thought git rebase might be what I need but read the doc and my head nearly exploded.

I think what I want is something like a "git sync" command that tells git that two branches are now in-sync and in future only merge the commits from this sync-point on.

Any help appreciated.

Git Solutions


Solution 1 - Git

If you want to merge most but not all of the commits on branch "maint" to "master", for instance, you can do this. It requires some work---- as mentioned above, the usual use case is to merge everything from a branch--- but sometimes it happens that you made a change to a release version that shouldn't be integrated back (maybe that code's been superceded in master already), so how do you represent that? Here goes...

So let's suppose maint has had 5 changes applied, and one of those (maint3) is not to be merged back into master, although all the others should be. You do this in three stages: actually merge everything before that one, tell git to mark maint3 as merged even when it isn't, and then merge the rest. The magic is:

bash <master>$ git merge maint~4
bash <master>$ git merge -s ours maint~3
bash <master>$ git merge maint

The first command merges everything before your troublesome maint commit onto master. The default merge log message will explain you're merging "branch 'maint' (early part)".

The second command merges the troublesome maint3 commit, but the "-s ours" option tells git to use a special "merge strategy" which, in fact, works by simply keeping the tree you are merging into and ignoring the commit(s) you are merging completely. But it does still make a new merge commit with HEAD and maint3 as the parents, so the revision graph now says that maint3 is merged. So in fact you probably want to use the -m option to git merge as well, to explain that that maint3 commit is actually being ignored!

The final command simply merges the rest of maint (maint~2..maint) into master so that you're all synced up again.

Solution 2 - Git

IMHO, the most logical thing to do, is to merge everything, and then use git revert (commit_you_dont_want) to remove it.

Example:

git merge master
git revert 12345678

If you have multiple "to-ignore" commits, or would like to edit revert message:

git merge master
git revert -n 123456
git revert -n abcdef
git commit -m "... Except commits 123456 and abcdef"

Then your history may look like:

| ... Except 123456 and abcdef
|\ Merge branch 'master' into 'your_branch'

If you have conflicts involving ONLY these "to-ignore" commits, you may use:

git merge master -X ours

So your version will persist over the other one. Even without error messages, you may still "revert" those unwanted commits, because they may have other changes that did not conflict, and you still don't want them.

If you have conflicts envolving NOT ONLY the "to-ignore" commits, you should resolve them manually, and you'll probably have to resolve them again during reverting.

Solution 3 - Git

Commits include ancestry. You can't merge a commit without merging prior commits.

You can cherry-pick them, of course. That's a fine flow when you have a branch that's in maintenance mode.

Solution 4 - Git

Sounds like a classic case for 'git cherry-pick' https://git-scm.com/docs/git-cherry-pick it does exactly what it sounds like

Solution 5 - Git

A kind of an advertisement to my project which basically wraps the process described by @araqnid.

It's kind of helper that introduces following GIT flow:

  • there's a daily/weekly notification on pending merges from maintenance branches into dev/master branch
  • branch maintainer checks for the status and decides him/herself whether all commits are required and either block some them or asks developers to block themselves. In the end maintenance branch is merged into the upsteam.

A quote from the project page:

> Depending on the workflow it's possible to have maintenance or > customer-specific branches along with the master branch. These > branches are also called LTS branches. > > Oftentimes the hot fixes go into the branches where the bug was > reported and then the commit is merged back into the master branch. > > General practice is to have all branches perfectly synchronized with > the master, i.e. you want to see a clear delta between a particular > branch and master to understand whether the master contains all > features and bugfixes. > > However sometimes you don't want particular commits because they are > customer-specific and shall not be visible by other users. Or your > master branch diverged that much that it requires completely different > approach to fix the issue, or even better, the problem is not anymore > present there. > > Also in case of cherry-pick from master into the maintenance branch > the resulting commit shall be blocked in master.

Solution 6 - Git

You can use git-cherry pick for this:

git cherry-pick $(git merge-base HEAD origin/branch)..$(git rev-parse <offending-commit-hash>^)
git cherry-pick <offending-commit-hash>..$(git rev-parse origin/branch)

This cherry-picks all commits from last common commit of current HEAD and origin/branch until commit to be ignored (note ^ telling cherry-pick to stop at parent of that commit).

Second command cherry-picks everything from <offending-commit-hash> with rest of origin/branch.

Note that some tinkering might be still needed if skipped over commit is needed to merge other patches (git will stop cherry-pick and tell you to resolve conflicts).

Solution 7 - Git

Create a third branch for the changes you want in master10 but not in master20. Always consider master10 as your "master", the most stable branch of all. The branch all other branches want to keep in sync with at all times.

Solution 8 - Git

Rather than revert or cherry-pick for this case, you need to get git to consider the changes you're skipping to be older than the ones you made.

So:

  1. merge the last commit before the commits you want to skip. This will, of course, merge all commits prior. git merge ccc
  2. merge the commits you want to skip. git merge fff --no-commit
  3. stage any merges, unstage all changes, undo all the changes. (Perhaps there are fool-proof commands for this, but I'd just do this part in a UI - however you know how)
  4. complete the empty merge git merge --continue
  5. merge the commits AFTER the one you wanted to skip. git merge source-branch-head

After Step 4, git will consider your branch more recent than that commit, since you dealt with it already (by choosing to keep YOUR versions of things).

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
QuestionBrad RobinsonView Question on Stackoverflow
Solution 1 - GitaraqnidView Answer on Stackoverflow
Solution 2 - GitAlexandre T.View Answer on Stackoverflow
Solution 3 - GitDustinView Answer on Stackoverflow
Solution 4 - GitGDwagView Answer on Stackoverflow
Solution 5 - GitDmytroView Answer on Stackoverflow
Solution 6 - GitTomas PruzinaView Answer on Stackoverflow
Solution 7 - GitwilhelmtellView Answer on Stackoverflow
Solution 8 - GitJason KlebanView Answer on Stackoverflow