239

On Git, say I mess up my commits, and I want to make the version 3 commits ago as the new version. If I do git checkout xxxx, it creates a new branch and it seems like I can only merge it? Could I make this the new "master version"?

I want:

A-B-C-D-E

to become

A-B-C-D-E-F

where F has exactly the same content as C

If I use git revert xxxx instead, it seems like it definitely will have conflicts and I need to manually resolve it.

What I really want is just make the old commit at some point the new commit, regardless of what's in my working directory or the latest commit.

How would I go about doing this?

Arslan Ali
  • 17,418
  • 8
  • 58
  • 76
huggie
  • 17,587
  • 27
  • 82
  • 139
  • 1
    just `git checkout . ` don't miss the final dot in the command – Ibrahim Tayseer Mar 12 '20 at 10:20
  • 2
    @IbrahimTayseer Yes, but `git rm -r .` is fairly necessary prior to that command, otherwise if there is any file that is present in the newer version but not in the older version is still kept around. – huggie Mar 13 '20 at 00:24
  • yes you are right :) – Ibrahim Tayseer Mar 13 '20 at 07:21
  • Not a duplicate, IMO, since this question wants to keep the previous commits in linear history for future reference. I had the same need, and svick's answer below worked for me, whereas the "duplicate" question was less helpful. – ultraGentle Nov 02 '21 at 13:46

6 Answers6

244
git rm -r .
git checkout HEAD~3 .
git commit

After the commit, files in the new HEAD will be the same as they were in the revision HEAD~3.

svick
  • 236,525
  • 50
  • 385
  • 514
34

It sounds like you just want to reset to C; that is make the tree:

A-B-C

You can do that with reset:

git reset --hard HEAD~3

(Note: You said three commits ago so that's what I wrote; in your example C is only two commits ago, so you might want to use HEAD~2)


You can also use revert if you want, although as far as I know you need to do the reverts one at a time:

git revert HEAD     # Reverts E
git revert HEAD~2   # Reverts D

That will create a new commit F that's the same contents as D, and G that's the same contents as C. You can rebase to squash those together if you want

Michael Mrozek
  • 169,610
  • 28
  • 168
  • 175
32

eloone did it file by file with

git checkout <commit-hash> <filename>

but you could checkout all files more easily by doing

git checkout <commit-hash> .
Nick Weisser
  • 910
  • 1
  • 9
  • 21
  • 2
    This causes rejection when pushing to origin. – 2540625 May 06 '16 at 22:43
  • 2
    This works for me. Pushing to origin wasn't a problem. – Noffls Mar 02 '17 at 08:47
  • 2
    so `git checkout ` will detach `HEAD` (push gets rejected), `git checkout .` should checkout `.` (all changes) from the commit to your working-tree, which you can apply as a new commit. You can also detach `HEAD` and branch off that commit. It should then be at `HEAD` for the new branch and you can commit there. The `.` is important. – Brandon G Jul 11 '18 at 17:57
23

This is exactly what I wanted to do. I was not sure of the previous command git cherry-pick C, it sounds nice but it seems you do this to get changes from another branch but not on same branch, has anyone tried it?

So I did something else which also worked : I got the files I wanted back from the old commit file by file

git checkout <commit-hash> <filename>

ex : git checkout 08a6497b76ad098a5f7eda3e4ec89e8032a4da51 file.css

-> this takes the files as they were from the old commit

Then I did my changes. And I committed again.

git status (to check which files were modified)
git diff (to check the changes you made)
git add .
git commit -m "my message"

I checked my history with git log, and I still have my history along with my new changes made from the old files. And I could push too.

Note that to go back to the state you want you need to put the hash of the commit before the unwanted changes. Also make sure you don't have uncommitted changes before you do that.

eloone
  • 4,248
  • 2
  • 32
  • 35
12

git cherry-pick C

where C is the commit hash for C. This applies the old commit on top of the newest one.

Adrian Parsons
  • 171
  • 1
  • 4
  • 20
    `git cherry-pick C` takes the *changes* introduced by C and applies them on top of E. This is not what the OP asked for. He wants to have the files in the exact state that they were in C, which `git checkout` provides. – gotgenes May 30 '13 at 13:06
2

The other answers so far create new commits that undo what is in older commits. It is possible to go back and "change history" as it were, but this can be a bit dangerous. You should only do this if the commit you're changing has not been pushed to other repositories.

The command you're looking for is git rebase --interactive

If you want to change HEAD~3, the command you want to issue is git rebase --interactive HEAD~4. This will open a text editor and allow you to specify which commits you want to change.

Practice on a different repository before you try this with something important. The man pages should give you all the rest of the information you need.

haydenmuhl
  • 5,998
  • 7
  • 27
  • 32
  • 2
    Vote down. 1. the question explicitly asks for keeping older commits, so that might be a reason why other answers attempt to do that. 2. as you said, the commits might have been published and there's no need to introduce comments on what that means with rebasing. – tishma Feb 22 '16 at 10:57