111

I have the following branches:

  • master
  • production

and the following remote branches:

  • origin/master
  • origin/production

I have a script that fetches the origin/master branch and gets the diff of what changed from my last fetch (log -p master..origin/master). Then I merge origin/master.

The commits found are pushed to a code review tool.

I want to push the successful commits – and only them – to the production branch, and then of course to origin/production.

How can I do so?

Also, I have 2 scripts running: the one that fetch from origin/master, push commits details to a database, and merge, and the other that I'm currently writing that will have to push the successful commits.

I'd like to have those 2 scripts running while avoiding race conditions/merge conflict. Since I only want to work with specified commits, maybe there's a way to get rid of the commits that I don't want?

Sylvain
  • 3,202
  • 5
  • 27
  • 27
  • 1
    What do you mean by 'successful commits'? – bdonlan May 19 '09 at 04:45
  • 1
    the one that have been reviewed and marked as successful. it doesn't really matter here, the important is that there are commits I want to keep and push to another branch, and others I want to get rid of/ignore. – Sylvain May 19 '09 at 05:04

2 Answers2

343

The term I think you're looking for is a 'cherry pick'. That is, take a single commit from the middle of one branch and add it to another:

A-----B------C
 \
  \
   D

becomes

A-----B------C
 \
  \
   D-----C'

This, of course, can be done with the git cherry-pick command.

The problem with this commit is that git considers commits to include all history before them - thus, if you have three commits like so:

A-----B-----C

And try to get rid of B, you have to create an entirely new commit like so:

A-----------C'

Where C' has a different SHA-1 ID. Likewise, cherry picking a commit from one branch to another basically involves generating a patch, then applying it, thus losing history that way as well.

This changing of commit IDs breaks git's merging functionality among other things (though if used sparingly there are heuristics that will paper over this). More importantly though, it ignores functional dependencies - if C actually used a function defined in B, you'll never know.

Perhaps a better way to handle this would be to have more fine grained branches. That is, instead of just having a 'master', have 'featureA', 'bugfixB', etc. Perform code review on an entire branch at a time - where each branch is very focused on doing only one thing - and then merge that one branch when you're done. This is the workflow that git is designed for, and what it's good at :)

If you insist on dealing with things at the level of patches, you may want to look at darcs - it considers a repository to be a set of patches, and thus cherry picking becomes the fundamental operation. However this has its own set of problems, such as being very slow :)

Edit: Also, I'm not sure I understand your second question, about the two scripts. Maybe you could describe it in more detail, possibly as a separate question to keep things from getting confusing?

Ninjakannon
  • 3,751
  • 7
  • 53
  • 76
bdonlan
  • 224,562
  • 31
  • 268
  • 324
  • About my second question, I just want to make sure that the processes of fetching changes (1st script) and pushing given commits to another location (2nd script) can work without having race condition / merging conflict, while working with different branches. But in the end I guess that oesn't matter since I could merge the 2 scripts into one so the 2 scripts don't work simultaneously :) – Sylvain May 19 '09 at 05:09
  • 11
    *"This changing of commit IDs breaks git's merging functionality among other things"* **@bdonlan please explain how the merging functionality is braked. What does it mean?** – Narek Sep 19 '13 at 12:46
  • 7
    @Narek He probably means that changes in commit C' will clash with the same changes in commit C when you will merge the second branch. That's the consequence of losing history behind commit C. – bytefu Nov 18 '13 at 08:29
  • 1
    "And try to get rid of B" - why are you trying to get rid of B? – d512 Feb 22 '15 at 04:54
  • 3
    @user1334007, he means that before it used to be A-B-C. Now, due to your cherry pick of C , your branch is A-D-C', which no longer contains 'B'. – AnneTheAgile Apr 10 '15 at 19:25
  • My understanding was that C' would not include the actual changes from B. So, after cherry-picking, B would not appear on the history of the branch AND the changes in B would not appear in C'. However, I did a c-p today and I am getting merge conflicts. When I try to resolve the merge I see changes from B in C' (the same file was changed in B and C). Is this expected behaviour? Should it be trying to pull changes from B into C' where the same file changed in B and C? I did not expect so but it appears so. I would like to understand what gitdoes under the hood when running a cherry-pick. – CodeClimber Nov 10 '16 at 13:05
  • We have a master Branch and release branches. If there is a bug in a release Branch we fix it there and merge it back into the master. No Problem, but what if we need to apply this fix on multiple release branches. Is there any alternative to cherry-picking then? – Christian Rapp Sep 14 '20 at 14:23
1

I realise this is an old question, but is referenced here: How to merge a specific commit in Git

Hence, a newer answer: Use feature branches and pull requests.

What this looks like, where fA is a commit with feature A, and fB is a commit with feature B:

            fA   fC (bad commit, don't merge)
           /  \ /
master ----A----B----C
                \  /
                 fB

Pull requests are associated with GitHub's functionality, but really all I mean is that someone has the responsibility of merging the feature branches into master.

Community
  • 1
  • 1
MattJenko
  • 1,208
  • 8
  • 11