git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: Dealing with an upstream cherry-picked branch

From
Avery Pennarun <apenwarr@gmail.com>
Date
Mar 15, 2010, 18:46 UTC
Message-ID
<32541b131003151146qeb6b15bqa9317c6d56443e8e@mail.gmail.com>
In-Reply-To
<76718491003142117w4fd10449j51deef27548c4d2e@mail.gmail.com>
On Sun, Mar 14, 2010 at 11:17 PM, Jay Soffian <jaysoffian@gmail.com> wrote:
Show 5 quoted lines
> The question is how to best integrate the fixes on upstream-a into
> local-master, w/o causing a headache when upstream cuts the next
> tentative release branch, at which point upstrea-master will again
> need to be merged into local-master (and that will also have other
> local development). Here are two options I've considered:
You forgot option 0: tell upstream not to do that.

0a) Have them do their bugfixes directly in the upstream-a branch, then merge sometimes from upstream-a to upstream-master. Then when they cut upstream-b from upstream-master, it should be an easy merge for you (since they've already resolved any conflicts between upstream-a and upstream-master as they arose over time).

0b) If they really need to do their bugfixes on upstream-master and then cherry-pick them back to upstream-a, have them merge upstream-a into upstream-master sometimes *anyway*, resolving any (probably trivial) conflicts as they arise. I say the conflicts should be trivial because they're just cherry-picks anyway, so git will mostly notice they're identical and ignore them. This is easy to do a little at a time, but gets more complicated if you wait a long time between merges.

0c) Like 0b), only use 'git merge -s ours' to merge from upstream-a into upstream-master. This assumes that upstream-a *never* has a fix other than one that is already in upstream-master, so that all conflicts necessarily resolve to exactly what's already in upstream-master. This makes it easier for downstream people to merge but doesn't actually cost any extra effort.

As for your original suggestions:
> 1) Create a local-a integration branch, merged from upstream-a and
> local-master. Keep this branch up-to-date by periodically merging
> local-master and upstream-a:

This will be inconvenient since local-master won't actually be useful; if upstream-a contains a critical patch, you won't be able to test your changes in local-master until you merge them into local-a. Thus the history of local-master, while "clean", will actually be meaningless.

Show 7 quoted lines
> 2) Periodically merge upstream-a into local-master:
> [...]
> Then when it is next time to merge upstream-master into local-master either:
>
> (a) Backout the upstream-a merges to local-master by reverting the
> merge commits which introduced them to local-master, then merge
> upstream-master.

Don't try to revert merge commits; that generally ends in disaster, both in terms of your tree's correctness and your ability to accurately retrace the history of your branch. Possibly you can make it work, but I don't know anybody who has. Even if you can, you'll still hate yourself in the morning.

> (b) Just merge upstream-master and carefully deal with all the
> conflicts. I think this will necessarily be an evil merge.

This is probably not as hard as it sounds, particularly if upstream-a is *purely* a subset (in terms of cherry-picks, not in terms of history) of upstream-master. I'd recommend something like this (just once when it's time to move to a new release branch):

       git checkout -b mergey upstream-master
       # take the history of upstream-a but not the content:
       git merge -s ours upstream-a
       # assuming local-master is your branch based on upstream-a:
       git checkout -b local-b local-master
       # merge the changes from upstream-a to upstream-master into local-b:
       git merge mergey
> (c) Create a new branch at point Ma and cherry-pick only the local
> commits from local-master past point Ma. This essentially gives me the
> clean local-master I would've had if I'd been doing (1) all along.
This will work, but the short form for exactly the same operation is:
        git checkout -b local-b local-master
        git rebase --onto upstream-master upstream-a

It will result in a cleaner history, and importantly, one that upstream would probably be willing to merge someday. You won't have any extra cherry-picked commits confusingly merged into your history with the original commits.

The disadvantage is that each of your branches will have a disjoint history: there will be no branch showing how you got from local-a to local-b, since they're actually two totally different things. In git, the ideal case is that if you look at the history of HEAD, you can see the *entire* evolution of the product, and using a different merge-base for each branch gets in the way of that.

The best option above, IMHO, is my proposed option 0. But any of the others will work.

Have fun,
Avery
Previous: Jay SoffianNext: Jay Soffian
Message 2 of 7 in “Dealing with an upstream cherry-picked branch”
  1. Jay SoffianMar 15, 2010
  2. Avery PennarunMar 15, 2010
  3. Jay SoffianMar 15, 2010
  4. Avery PennarunMar 15, 2010
  5. Junio C HamanoMar 15, 2010
  6. Jay SoffianMar 16, 2010
  7. Junio C HamanoMar 16, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.