Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 712
donquixote Avatar asked Feb 01 '16 05:02

donquixote


People also ask

How do I get the diff between two commits?

To see the changes between two commits, you can use git diff ID1.. ID2 , where ID1 and ID2 identify the two commits you're interested in, and the connector .. is a pair of dots. For example, git diff abc123.. def456 shows the differences between the commits abc123 and def456 , while git diff HEAD~1..

Can you cherry pick a range of commits?

Since Git 1.7. 2, you can cherry-pick a range of commits by using the dot notation. Note that using this command, the commit A will NOT be included into the cherry-pick. For example, back to the “master” branch, let's try to cherry-pick two commits into the feature branch.

Does git cherry pick rewrite history?

Running git rebase typically rewrites history and can appear to move entire branches around. This is likely the source of the undesired history you are seeing. In contrast, git cherry-pick replays the delta from one or more commits elsewhere in a repository's history.


2 Answers

@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.

like image 149
donquixote Avatar answered Sep 30 '22 17:09

donquixote


Since cherry-picking applies diff, it would be simpler to create a patch and apply it:

git checkout aaa/aaa
git cherry-pick -n bbb/bbb
git diff --cached > my.patch

Then checkout your regular branch and git apply my.patch

This takes commit 'bbb/bbb', compares it to its immediate ancestor, then applies that difference on top of 'aaa/aaa': conflicts can be expected.
Test if -X subtree=vendor/package helps in the case of a subtree.

like image 36
VonC Avatar answered Sep 30 '22 16:09

VonC