Reputation: 23135
Lets say I've got a branch, with a number of commits:
A->B->C
I then create a patch, between A
and C
that ignores whitespace changes. I then apply the patch to A
.
A->B->C
\
->D
But I don't want to lose my commit history, I just want an additional commit that removes any whitespace changes from part of the code not relevant to my patch.
So I want my commit history to look like this:
A->B->C->D
Where D
is just the commit which removes any changes which are solely whitespace when compared to A
.
This there anyway to achieve this? Note I don't want to merge C
and D
, D
should contain all of C
s changes except whitespace changes to things unchanged since A
. Merging C
into D
I fear might put back in the whitespace changes I was trying to clean up.
Upvotes: 0
Views: 184
Reputation: 489083
First, a minor (but important) technical note. I assume that in
A->B->C
you mean to imply that A
is the parent commit of B
, and B
is the parent commit of C
.
In Git, these parent/child relationships exist in the graph but are backwards:
A<-B<-C
That is, children know who their parents are, but parents have no idea who their children are. (This is because commits, once made, are immutable. There is nowhere to record their children! The children are made when the parents exist, so the children record their parent hash IDs, but not vice versa.)
It sounds to me like you want to copy the snapshot that is in D
to a new commit D'
that comes after C
:
A--B--C--D' <-- branch1
\
D <-- branch2
(such that the output from git diff
comparing commits D
and D'
in either order is always empty). To do that, don't do any of the stuff below. Instead, just check out branch1
, remove every file (in case there are files in C
that do not exist in D
—you can skip this step if that's not the case), then extract every file from D'
into the index and work-tree, and then commit:
<make sure you are at the top level of your repository>
$ git checkout branch1
$ git rm -r . # remove ALL files (optional)
$ git checkout branch2 -- . # restore the to-keep-files from D
$ git commit -C branch2 # make new commit, using message from D
There is a slightly shorter (and maybe less scary looking, but it does the same thing) variant that replaces the git rm -r .; git checkout branch2 -- .
using git read-tree
, but it's harder to reason about, so I use this one for illustration.
You mention in a comment:
I just need a way to make a patch file from C->D
When you use git show
to turn a commit (a snapshot) into a patch (changes since parent commit), Git simply runs git diff
with two commit hashes:
or:
git diff <hash-of-A> <hash-of-D>
This is what gives you the A vs D
patch, for instance, when you use git log -p
or git show
on commit D
.
You can always run git diff
yourself, manually, to compare commit C
vs commit D
; all you need to do is supply anything that identifies the two specific commits. Their raw hash IDs are the simplest, but you can use any of the names described in the gitrevisions documentation.
As Camusensei suggests and evolutionxbox mentions, you can use git cherry-pick
or git rebase
to compute the diff from A
to D
and then apply that diff to C
. Using cherry-pick is what git rebase
does; the difference between:
git checkout <branch-name-that-identifies-C>
git cherry-pick <any-name-that-identifies-D>
and:
git checkout <branch-name-that-identifies-D>
git rebase <any-name-that-identifies-C>
is that the rebase
operation makes the branch name that used to identify commit D
now identify the copy D'
. Both operations add a new commit D'
that is a copy of D
, but the cherry-pick command does it while you are on the branch that used to identify C
and now identifies D'
. That is, the resulting set of commits is the same; the difference lies in which branch names identify which commits.
Neither of these looks at C
vs D
directly: they both look at A
vs D
and how to insert those changes into the snapshot obtained by extracting commit C
. So this is not at all equivalent to computing C
vs D
and then applying that to C
.
Upvotes: 2
Reputation: 1563
git checkout D; git rebase C
will do what you want to:
A->B->C->D'
You will still have a merge to do in case of conflicts to create the D'
commit.
Upvotes: 2