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.
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..
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.
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.
@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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With