Why does `git reset --hard HEAD~X` leave untracked files behind?

1.9k views Asked by At

From git reset --hard HEAD leaves untracked files behind:

When I run git reset --hard HEAD, it's supposed to reset to a pristine version of what you pulled, as I understand it. Unfortunately, it leaves files lying around, as a git status shows a big list of untracked files.

How do you tell git "Just bring it back to EXACTLY what was in the last pull, nothing more, nothing less"?

To get rid of these files I have to run git clean -df.

Can somebody please explain why it works this way and which files will become untracked?

2

There are 2 answers

2
Mark Adelsberger On BEST ANSWER

Updated per notes in the comments.

Last question first:

which files will become untracked?

None. But files that are already untracked should remain untracked and not be affected. This is consistent with behavior of most git commands (except those that explicitly affect untracked files).

I say should remain untracked, though, because there is one case where even that's not true: If the commit to which you're resetting has a file at the same path as your current untracked file, then the work tree version is irreversibly clobbered. This is very un-git behavior and IMO is a bug, but there it is.

Can somebody please explain why it works this way[?]

Because if git were to implicitly remove or modify an untracked file, you would have no way to recover what that file looked like before git messed with it. If you wanted the file under git's control, git assumes you would've added and maybe committed it. Since you haven't (the file is untracked), git usually won't mess with it unless you tell it clearly that it should.

So back to the premise of the original question:

When I run git reset --hard HEAD, it's supposed to reset to a pristine version of what you pulled, as I understand it

Nope. The docs are clear that only the tracked state is reverted.

0
poke On

git reset --hard will only replace all currently tracked files with the state of the commit you are resetting to. This has the following effect:

  • All pending changes to tracked files will be removed.
  • All changes to tracked files of the target commit will be removed.
  • Files that are neither tracked by the current commit nor by the target commit will be left as they are.

This means that, usually, ignored files and new files will be left alone.

The way this works is consistent with how Git operates with tracked/untracked files in general. To Git, untracked files are kind of invisible and they will never be touched by Git. This is even true when you check out a branch where a file is tracked that was previously untracked (but existed): Git will refuse to do that because the state of the untracked file would be lost.

Git will in general try very hard not to lose any local changes. By saying git reset --hard you are essentially saying “I know that this will get rid of all pending changes to the files you know, so throw those away”. But this does not include any claim over local untracked files, so those will be kept as they are.