git cherry-pick a diff between commits without common history?

donquixote picture donquixote · Feb 1, 2016 · Viewed 7.2k times · Source

Scenario: A repository with totally disconnected pieces of history. E.g. from different remotes that have no common git history. (This typically occurs in scenarios with git subtree.)

Is it possible to cherry-pick the diff between these two commits, even if they have no common history? (And the active commit has no common history with either of these two)

I tried this (into a subdirectory with -X subtree=sub/dir option):

git cherry-pick -X subtree=vendor/package aaa/aaa..bbb/bbb

Without subtree, this would have been simpler:

git cherry-pick aaa/aaa..bbb/bbb

Where aaa/aaa and bbb/bbb are two commits with disconnected history.

Unfortunately, this does not work: The aaa/aaa..bbb/bbb is not read as a diff, but something else.

The same can be shown with git show aaa/aaa..bbb/bbb, which is quite different from git diff aaa/aaa bbb/bbb. E.g. if both have identical files, then git diff will be empty, but git show a..b will show only b, but not a.


Note: My personal use case is with a subtree background. I mention this to avoid an artificial use case, where people typically start debating the validity of the use case instead of the actual question.

An ideal answer will first address the general case, and then address the subtree case.

Answer

donquixote picture donquixote · Feb 1, 2016

@VonC points in a good direction. Credit and +1 for this idea.

it would be simpler to create a patch an apply it:

However, this can be done with a simple one-liner (I tried this, and it works for me):

git diff aaa/aaa bbb/bbb | git apply --directory=vendor/package

Or in a non-subtree scenario:

git diff aaa/aaa bbb/bbb | git apply

No need for git checkout, git cherry-pick, or creating a local file.
You then still need to commit this stuff, obviously..

And here is how to do it without adding and/or fetching any subtree remotes. Instead, you can clone the package repositories into separate directories outside the main project repo.

git --git-dir="/path/to/package/repo/.git" diff aaa bbb | git apply --directory=vendor/package

The way I understand it, this is all still mostly equivalent to the usual git subtree work flow. I mean, the git history of the main project will be the same. Correct me if I'm wrong.