2023

After the last commit, I modified a bunch of files in my working copy, but I want to undo the changes to one of those files, as in reset it to the same state as the most recent commit.

However, I only want to undo the working copy changes of just that one file alone, nothing else with it.

How do I do that?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
hasen
  • 161,647
  • 65
  • 194
  • 231

15 Answers15

2647

You can use

git checkout -- file

You can do it without the -- (as suggested by nimrodm), but if the filename looks like a branch or tag (or other revision identifier), it may get confused, so using -- is best.

You can also check out a particular version of a file:

git checkout v1.2.3 -- file         # tag v1.2.3
git checkout stable -- file         # stable branch
git checkout origin/master -- file  # upstream master
git checkout HEAD -- file           # the version from the most recent commit
git checkout HEAD^ -- file          # the version before the most recent commit
Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
  • 44
    what's the difference between HEAD and HEAD^? – hasen Mar 28 '09 at 22:06
  • 74
    HEAD is the most recent commit on the current branch, and HEAD^ is the commit before that on the current branch. For the situation you describe, you could use git checkout HEAD -- filename. – Paul Mar 28 '09 at 22:21
  • 18
    In short "git checkout sha-reference -- filename" where the sha-reference is a reference to the sha of a commit, in any form (branch, tag, parent, etc.) – lprsd Mar 02 '10 at 15:46
  • 1
    HEAD^ , sorry but i am out of touch here little bit. What is last char in HEAD^? Can't find it in windows keyboard – mamu Oct 14 '10 at 02:18
  • 1
    @mamu It's a caret; on a US keyboard, you usually get it by typing Shift-6. – Brian Campbell Oct 14 '10 at 02:44
  • `git checkout HEAD^ -- filename` to revert to file state before last commit – Naoise Golden Feb 15 '13 at 15:57
  • 36
    NOTE: If the file is already staged, you need to reset it, first. `git reset HEAD ; git checkout -- ` – Olie Jun 13 '13 at 21:56
  • Then does that mean you can you do `HEAD^^` for 2 commits from the most recent? – ahnbizcad May 13 '14 at 05:20
  • 14
    @gwho Yes, you can do `HEAD^^` for 2 commits from the most recent, or `HEAD^^^` for 3 commits back. You can also use `HEAD~2`, or `HEAD~3`, which gets more convenient if you want to go more commits back, while `HEAD^2` means "the second parent of this commit"; because of merge commits, a commit can have more than one previous commit, so with `HEAD^` a number selects which of those parents, while with `HEAD~` a number always selects the first parent but that number of commits back. See [`git help rev-parse`](https://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html) for more details. – Brian Campbell May 13 '14 at 15:34
  • @BrianCampbell How can I do the inverse, keep local changes in one file and discard incoming change. I have 'error: Your local changes to the following files would be overwritten by merge:' – Erick Asto Oblitas Feb 04 '16 at 12:47
  • `HEAD` is just a movable pointer that can point to anywhere. Search the `DETACHED HEAD` section from here: https://git-scm.com/docs/git-checkout – smwikipedia May 27 '16 at 02:57
  • PUT THIS INTO MEMORY. – Daniel Viglione Jul 28 '16 at 16:56
  • 1
    `git checkout -- /path/to/file/FileName.cc` – Kristof Pal Aug 03 '16 at 12:13
  • This soooo doesn't do what it's meant to. No changes to the file. – Owl Aug 17 '16 at 08:19
  • 1
    **Remember to provide the full path to file**, e.g `git checkout -- src/js/filename`!!! – zenoh Jul 11 '20 at 10:23
208

Just use

git checkout filename

This will replace filename with the latest version from the current branch.

WARNING: your changes will be discarded — no backup is kept.

nimrodm
  • 23,081
  • 7
  • 58
  • 59
  • 29
    @duckx it's to disambiguate branch names from filenames. if you say `git checkout x` and x happens to be a branch name as well as a file name, I'm not sure what the default behavior is but I think git will assume you want to switch to branch x. When you use `--` you're saying that what follows is file name(s). – hasen Mar 04 '15 at 16:15
  • 3
    ic thanks for clearing that up. everyone just assumes you know what -- means when they show you examples. and its not something you can google easily too. – Patoshi パトシ Mar 04 '15 at 18:05
  • 3
    Looks like the answer was edited to remove the `--` from it. While still correct, as @hasen points out, if there is an ambiguity between filename and branch names you may end up with very undesired behavior here! – BrainSlugs83 Mar 14 '15 at 18:14
  • 3
    I like it the way it is, without `--`, nice and easy. When you name branches using file names, there must be bad thinking somewhere... – Marco Faustinelli Apr 21 '17 at 16:00
155
git checkout <commit> <filename>

I used this today because I realized that my favicon had been overwritten a few commits ago when I upgrated to drupal 6.10, so I had to get it back. Here is what I did:

git checkout 088ecd favicon.ico
neoneye
  • 50,398
  • 25
  • 166
  • 151
  • 1
    How do I get the commit (of a previously deleted file) except of scrolling throw tons of "git log --stat" output? – Alex Mar 01 '12 at 09:40
  • 4
    IMO it's kind of difficult via the commandline to scan through gits log and find the right file. It's much easier with a GUI app, such as http://www.sourcetreeapp.com/ – neoneye Mar 02 '12 at 06:46
  • 6
    `git log --oneline ` will give you a more compact log, and only include changes to the specific file – rjmunro Feb 05 '14 at 11:51
  • 1
    alternatively, you can use `git reflog ` – ygesher Aug 05 '15 at 07:08
  • This is a universal answer. If unwanted content was already committed then most of the other answers will not work, since they target restoring the file content from the last commit. – Dmitry Melnikov Nov 07 '22 at 09:39
100

If your file is already staged (happens when you do a git add etc after the file is edited) to unstage your changes.

Use

git reset HEAD <file>

Then

git checkout <file>

If not already staged, just use

git checkout <file>
thanikkal
  • 3,326
  • 3
  • 27
  • 45
  • 3
    This has been more helpful than the accepted one haha. It's easy to forget what changes have been staged and what haven't, so resetting helped. Although I also tried "git reset --hard" before, it did not do what "git reset HEAD" did. I wonder why? – Arman Bimatov Oct 06 '14 at 14:21
36

I have Done through git bash:

(use "git checkout -- <file>..." to discard changes in working directory)

  1. Git status. [So we have seen one file wad modified.]
  2. git checkout -- index.html [i have changed in index.html file :
  3. git status [now those changes was removed]

enter image description here

Py-Coder
  • 2,024
  • 1
  • 22
  • 28
26

Git 2.23 introduced a restore to do just that, in an attempt, I think, to make the answer to these kind of questions straightforward.

git restore [--] <pathspec>...

As always, the -- could be needed but when a file name starts with a dash. (The confusion with a branch name is not possible here, as restore's perimeter does not include branches, unlike the do-all checkout)

To be complete, restore can also restore staged files with --staged, and restore from a different commit than HEAD with --source=<tree>.

P-Gn
  • 23,115
  • 9
  • 87
  • 104
26

If you want to just undo the previous commit's changes to that one file, you can try this:

git checkout branchname^ filename

This will checkout the file as it was before the last commit. If you want to go a few more commits back, use the branchname~n notation.

sykora
  • 96,888
  • 11
  • 64
  • 71
  • 1
    This won't remove the changes from the commit, it will just apply the diff to the version on the HEAD. – FernandoEscher Feb 27 '13 at 18:32
  • 2
    While true, the original poster just wanted to revert his working copy modifications (I think), not revert changes from the last commit. The original poster's question was a little unclear, so I can understand the confusion. –  May 30 '14 at 02:34
  • 2
    maybe not OP's use, but I was looking for how to overwrite my branch with the master's copy - this works quite nicely when replacing `branchname^` – Alex Mar 14 '19 at 17:18
12

This answers is for command needed for undoing local changes which are in multiple specific files in same or multiple folders (or directories). This answers specifically addresses question where a user has more than one file but the user doesn't want to undo all local changes:

if you have one or more files you could apply the same command (git checkout -- file ) to each of those files by listing each of their location separated by space as in:

git checkout -- name1/name2/fileOne.ext nameA/subFolder/fileTwo.ext

mind the space above between name1/name2/fileOne.ext nameA/subFolder/fileTwo.ext

For multiple files in the same folder:

If you happen to need to discard changes for all of the files in a certain directory, use the git checkout as follows:

git checkout -- name1/name2/*

The asterisk in the above does the trick of undoing all files at that location under name1/name2.

And, similarly the following can undo changes in all files for multiple folders:

git checkout -- name1/name2/* nameA/subFolder/*

again mind the space between name1/name2/* nameA/subFolder/* in the above.

Note: name1, name2, nameA, subFolder - all of these example folder names indicate the folder or package where the file(s) in question may be residing.

huelbois
  • 6,762
  • 1
  • 19
  • 21
Nirmal
  • 1,229
  • 1
  • 15
  • 31
9

I always get confused with this, so here is a reminder test case; let's say we have this bash script to test git:

set -x
rm -rf test
mkdir test
cd test
git init
git config user.name test
git config user.email test@test.com
echo 1 > a.txt
echo 1 > b.txt
git add *
git commit -m "initial commit"
echo 2 >> b.txt
git add b.txt
git commit -m "second commit"
echo 3 >> b.txt

At this point, the change is not staged in the cache, so git status is:

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   b.txt

no changes added to commit (use "git add" and/or "git commit -a")

If from this point, we do git checkout, the result is this:

$ git checkout HEAD -- b.txt
$ git status
On branch master
nothing to commit, working directory clean

If instead we do git reset, the result is:

$ git reset HEAD -- b.txt
Unstaged changes after reset:
M   b.txt
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   b.txt

no changes added to commit (use "git add" and/or "git commit -a")

So, in this case - if the changes are not staged, git reset makes no difference, while git checkout overwrites the changes.


Now, let's say that the last change from the script above is staged/cached, that is to say we also did git add b.txt at the end.

In this case, git status at this point is:

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   b.txt

If from this point, we do git checkout, the result is this:

$ git checkout HEAD -- b.txt
$ git status
On branch master
nothing to commit, working directory clean

If instead we do git reset, the result is:

$ git reset HEAD -- b.txt
Unstaged changes after reset:
M   b.txt
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   b.txt

no changes added to commit (use "git add" and/or "git commit -a")

So, in this case - if the changes are staged, git reset will basically make staged changes into unstaged changes - while git checkout will overwrite the changes completely.

sdaau
  • 36,975
  • 46
  • 198
  • 278
5

I restore my files using the SHA id, What i do is git checkout <sha hash id> <file name>

Beto
  • 772
  • 2
  • 12
  • 26
5

For me only this one worked

git checkout -p filename

enter image description here

Ramesh Bhupathi
  • 408
  • 7
  • 10
3

git checkout is the old way of doing this. After the 2020 update the preferred way is:

git restore file.txt

See this duplicate answer (be sure to scroll down to the 2020 update): https://stackoverflow.com/a/31281748/12763497

acolchagoff
  • 1,926
  • 3
  • 18
  • 30
brethvoice
  • 350
  • 1
  • 4
  • 14
1

If you have not yet pushed or otherwise shared your commit:

git diff --stat HEAD^...HEAD | \
fgrep filename_snippet_to_revert | cut -d' ' -f2 | xargs git checkout HEAD^ --
git commit -a --amend
Jesse Glick
  • 24,539
  • 10
  • 90
  • 112
0

If it is already committed, you can revert the change for the file and commit again, then squash new commit with last commit.

Gina
  • 1
-3
git checkout a3156ae4913a0226caa62d8627e0e9589b33d04c -p */SearchMaster.jsp

Breakdown: a3156ae4913a0226caa62d8627e0e9589b33d04c = This is the hash value of the commit. It's on my own personal branch (not master).

The -p flag is the path.

*/SearchMaster.jsp is the filename.

Gene
  • 10,819
  • 1
  • 66
  • 58