8

I have two branches in my repository (for the purposes of this question): master and performance_testing. I have received changes for the master branch. I need to put them into performance_testing as well. I need to keep the two branches existent and separate, so merging would not be appropriate. I guess I could introduce the changes in one branch and commit, then do the same in the other branch. But this seems to be error-prone, and I would think that git would have some way to do this more directly. How do I do this?

bob.sacamento
  • 6,283
  • 10
  • 56
  • 115

4 Answers4

8

Often what you would do in this scenario is cherry picking, which involves applying only a sub-set of the commits of one branch onto another.

For example, you have commits A, B, C, D, E, F on master and you wish to bring commits B, C and D into performance_testing:

git checkout performance_testing
git cherry-pick B^..D

Or you can list individual commits in multiple, individual cherry-pick commands. This is useful when you don't necessarily want a continuous series of commits. For example:

git cherry-pick B D

Note that B comes before D in the history.

For more detail, see: How to cherry-pick multiple commits (which also includes some great diagrams I won't poach into this answer).

Yes, there are many different options to choose from. This is a basic and common solution which the reader can choose to apply now that they understand how it works. git rebase --onto is another option, as is different branch management, but without a very specific scenario in mind, cherry-picking should get the most mileage.

msanford
  • 11,803
  • 11
  • 66
  • 93
  • Thanks. Did this. Looks like the changes got put into a commit OK. The funny thing is, it looks like that commit is in the master branch. If I checkout `performance_testing`, git log shows the latest entry as SHA 348f3e... in *master*, and if I checkout 'master', git log shows the latest entry as SHA 825f44... in master. This seems odd. Is this correct? What happens when I try to do later commits in `performance_timing`? Thanks. – bob.sacamento Jul 18 '17 at 14:48
  • @bob.sacamento This is normal because the ancestor of the commit is part of the data used to make the commit's hash, so the same diff with different parents will have different IDs. It's what let you use `git cherry-pick` with a *commit-ish* alone, for example. – msanford Jul 18 '17 at 14:51
  • Thanks again. Now, if I check out `performance_testing` and make some changes and commit, will the commit go on `performance_testing`, as I hope, or on `master`? – bob.sacamento Jul 18 '17 at 16:51
  • 1
    @bob.sacamento Correct. The commits will go on whatever branch you've currently checked out. If you decide you want to put those commits on `master` as well, just cherry pick them the same way. – msanford Jul 18 '17 at 16:55
  • 1
    Thanks for all your help! – bob.sacamento Jul 18 '17 at 21:28
8

The best approach would be creating a feature branch for the changes and merging it at the end in master and performance_testing. Cherry-picking is considered as an anti-pattern.

To create the feature branch, first find the most recent common ancestor of 2 branches:

git merge-base master performance_testing

Then create feature_branch using the output of the previous command:

git branch feature_branch <output of merge-base command>

Switch to feature_branch branch, make the changes and merge it at the end in master and performance_testing.

If you have changed same files in 2 branches you will get a conflict. It's inevitable, even with cherry-picking, but easy to solve.

The advantage of this approach over cherry-picking is that you will get an uniformed log, with the same commit hashes on both branches.

Deniz
  • 793
  • 11
  • 20
  • This is how I like to do things, though I wouldn't go so far as "anti-pattern" myself, I consider it a judgment call, downsides either way to go with the upsides and pick your balance. But I do pretty much always make the merge call myself. I understand not liking pointless merge commits, but I don't understand disliking meaningful ones. – jthill Jul 18 '17 at 14:37
  • 1
    I don't understand this answer: isn't `performance_testing` *the feature branch* in this case. Presumably some code has been merged from another branch into `master` and those changes are to be performance tested? – msanford Jul 18 '17 at 14:49
  • ``performance_testing``is a feature branch but we need a separate one for the new changes. They are common to `master` and ``performance_testing`` that's why they can be put into a separate feature branch. – Deniz Jul 18 '17 at 15:03
  • From where would you derive such a feature branch? From the last common ancestor of `master` and `performance_testing`? If you don't use that ancestor, you would introduce unwanted changes when you later merge the feature branch into `master` and/or `performance_testing`. Isn't cherry-picking a more transparent and less error-prone solution after all? – Jpsy Apr 18 '19 at 09:18
  • @Jpsy yes, I would create the feature branch from the last common ancestor of `master` and `performance_testing`. I've just updated my answer. It's a more transparent approach than cherry-picking since you will get an uniformed commit log. – Deniz Apr 18 '19 at 12:46
  • This is the best, and most correct answer. If you all haven't seen this excellent blog series by Raymond Chen of MSFT at his blog, The Old New Thing: [Stop cherry-picking, start merging](https://devblogs.microsoft.com/oldnewthing/20180323-01/?p=98325), you should give it a read. – fourpastmidnight Sep 23 '22 at 17:15
1

You can use git cherry-pick to apply (copies of) one or more commits onto a new branch.

The new commits will "have the same effect as" the originals (same diff, same message etc.) but are otherwise independent.

Useless
  • 64,155
  • 6
  • 88
  • 132
1

I need to keep the two branches existent and separate, so merging would not be appropriate.

That doesn't necessarily follow; after a merge both branches still exist and are still separate. The case where merging would be inappropriate is if each branch currently contains changes that cannot be in the other.

When you say you "received changes for the master branch", what exactly does that mean? If you mean someone pushed changes to the origin/master, then that limits your options. What would otherwise be the best solution (creating a branch at a merge base between master and performance_testing, applying the changes there, and then merging that branch into both master and performance_testing) would become complicated by the need for a history rewrite affecting already-shared commits (to remove the direct application of the changes to master).

If the changes were directly applied to master, and master contains changes that cannot be brought into performance_testing: then unless coordinating a history rewrite is acceptable, your remaining option probably is to cherry-pick the change. In my opinion cherry-picking is recommended way to often and has significant drawbacks, but if you've ruled out every other solution then this is what remains.

Mark Adelsberger
  • 42,148
  • 4
  • 35
  • 52