How to checkout an old commit and make it a new commit
I wanted to “follow-up” with another question about this matter:
Checkout old commit and make it a new commit
But they say “Don’t Do That!” so it seems I must ask a new question. (Even though it is the same question where the answer I think is best for me didn’t work as expected…
If I have commits
A-B-C-D-E-F (imagine these are all SHA’s).
I want to make the entire repository exactly as
C and then commit that to create
'G' which is exactly like
C. So that when I am done the log is A-B-C-D-E-F-G even though C & G are identical.
One answer indicated cherry-pick, which seemed perfect, except it made me resolve every conflict between
F. I looked at the documentation and I tried
--force anyway, and then the recommendation for
--patch was closest but it wasn’t absolute. So is there a way to make cherry-pick just choose the
C version of the conflict?
The other closest answer would be to checkout
C, but I couldn’t find the magic word that would keep it on the same branch. The recent training I completed said use the “magic dash dash” at the end to tell
git you want this on the current branch, but no matter what I do it creates a “(no branch)” branch.
As I am sure you can tell, I am pretty new to git (and command line in general) but I tried to figure it out on my own. If you could use the verbose versions of what you recommend then it sticks in my head better and would be greatly appreciated. (
-a = --annotate or
-a = --albuquerque?)
It seems so simple, and exactly what you might want to do with git — go back a previous commit without losing the intermediary commits in case you change your mind.
7 Solutions collect form web for “How to checkout an old commit and make it a new commit”
Do you mind generating:
A-B-C-D-E-F-F'-E'-D' where the state of the code at
D' is exactly as it was at
D')? In that case, just revert
D. If you do not want the intermediate steps,
revert but do not apply, and then commit. That is:
$ git revert -n HEAD $ git revert -n HEAD~ $ git revert -n HEAD~2 $ git commit -m 'Revert D,E,and F'
After this, the current HEAD is G, and the two commits G and C should contain the same code tree. You can verify by checking the output of
git cat-file -p HEAD | grep ^tree and
git cat-file -p C | grep ^tree. Both of those commands should give the same output.
But it’s not really clear why you want to keep the intermediate commits. If you want them for posterity, then do something like:
git branch old-stuff,
git reset C That will set your current branch back to
F can still be viewed in the branch named
I thihk you need to use
git cherry-pick --strategy=recursive -X ours C
The recursive strategy can take the following options:
This option forces conflicting hunks to be auto-resolved cleanly by favoring our version. […]
The “ours” and “theirs” refer to the two commits being merged. However, I’m not sure which way around
git cherry-pick treats the commits, so you might need
-X theirs instead. You could make a new branch, to try it and see.
If you want to end up with:
(not sure, I’m confused by “Where D, E and F are right there.”)
Then the best way to get there is
rebase -i, and squash
Could you describe what you want as: “make your working tree the same as C, then commit that”? If so, here’s a way to do that:
git checkout C # working tree same as C. But also moves HEAD to C... git reset F # ...so move HEAD back to F, leaving working tree alone git commit -a # commit working tree
EDIT this might produce a bunch of merge conflicts, so it doesn’t address that part of your question.
EDIT3 Again, the selected answer to your previous question solves this: by deleting everything first, there is no conflict.
BTW: it would be nice to make the working tree equal to commit C without changing HEAD, but I don’t think there’s a (porcelain) to do that. Both
git checkout and
git reset change the current branch if you want the whole commit.
git checkout can extract individual files to the working tree without moving the current branch, but then you have to extract each file individually, not the whole commit…
EDIT2 oh I see from the selected answer to your previous question, you can do this. I had tried
git checkout C -- "*", but you need
git checkout C -- . So it would just be:
git checkout C -- . # working tree same as C, without moving branch (NB ".") git commit -a
Judging from your comment to my other answer (which I am not deleting, since that solution is more correct than this, but this seems to be what you want), you can just use the plumbing command
commit-tree as follows:
git reset $( git commit-tree $tree -p F -m 'Revert D,E, and F' )
where $tree is the hash of the tree object underlying commit C. (The output of
git cat-file -p C | grep ^tree ). This will create a new commit in which the underlying tree is identical to that of commit C, but the parent of the new commit is commit F. It also sets the current branch to point to that new commit, making it the new HEAD.
Looks like you want C to be the final state of your git repository.
Take “A-B-C-D-E-F-G” as an example,
You can just revert G,F,E,D in order using
git revert [commitID]
After G,F,E,D are all reverted, your repository will be staying in the state same as C.
An alternative way for local branch only is:
git resert --hard HEAD~4
This will reset your local branch to commit C and drop D,E,F,G.