ChatGPT解决这个技术问题 Extra ChatGPT

Undo working copy modifications of one file in Git?

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?


B
Brian Campbell

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

what's the difference between HEAD and HEAD^?
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.
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.)
NOTE: If the file is already staged, you need to reset it, first. git reset HEAD <filename> ; git checkout -- <filename>
@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 for more details.
n
nimrodm

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.


@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).
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.
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!
I like it the way it is, without --, nice and easy. When you name branches using file names, there must be bad thinking somewhere...
n
neoneye
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

How do I get the commit (of a previously deleted file) except of scrolling throw tons of "git log --stat" output?
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 sourcetreeapp.com
git log --oneline <filename> will give you a more compact log, and only include changes to the specific file
alternatively, you can use git reflog <filename>
t
thanikkal

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>

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?
P
Py-Coder

I have Done through git bash:

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

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

https://i.stack.imgur.com/onOB1.jpg


s
sykora

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.


This won't remove the changes from the commit, it will just apply the diff to the version on the HEAD.
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.
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^
h
huelbois

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.


P
P-Gn

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>.


Maybe obvious for some but it wasn't for me: To just restore everything: git restore .
s
sdaau

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.


B
Beto

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


R
Ramesh Bhupathi

For me only this one worked

git checkout -p filename

https://i.stack.imgur.com/Ev1Zj.png


J
Jesse Glick

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

G
Gina

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


Adding specific commands to use would help the original poster and future visitors.
b
brethvoice

git restore file

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


G
Gene
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.