Git: Discard all changes to a single file since a certain commit
git, git-checkout
Solution
Except for the rename issue, the general approach (`git checkout <rev> -- <path>`) you suggested first is the obvious right way to go. If that fails, the file must have been added, so you can simply take "failure" as "indication to remove".
Except, again, for the rename issue.
Here, you could use the `git log --follow` approach you suggested. Follow, watching for rename ops, and collect them up. You can use `--name-status` to observe renames, e.g.:
$ git log --follow --oneline --name-status -- builtin/var.c
f9bc573 ident: rename IDENT_ERROR_ON_NO_NAME to IDENT_STRICT
M builtin/var.c
c2e86ad Fix sparse warnings
M builtin/var.c
2bc8c1a var: run setup_git_directory_gently() sooner
M builtin/var.c
81b50f3 Move 'builtin-*' into a 'builtin/' subdirectory
R100 builtin-var.c builtin/var.c
64778d2 Make 'git var GIT_PAGER' always print the configured pager
M builtin-var.c
9fabb6d Fix 'git var' usage synopsis
M builtin-var.c
55b6745 make "git var" a built-in
R096 var.c builtin-var.c
6361824 Teach git var about GIT_PAGER
M var.c
[snip]
(I ran the above on the git sources themselves). There's a fairly big hiccup here, as you must know the new name of the file, not the old one, but perhaps that's the case in question.
Having followed the renames, you can then `git checkout <rev> -- <path-by-name-at-the-time>`. The `--name-status` part will also show if the file was created in a rev preceding the historical rev (the status will be `A`, added), and you will know to remove the file.
That leaves out the "file was removed" case, which conflicts with the basic idea that you know the new name of the file. When file `foo` is removed, it has only an old name in the first place.
There is, as far as I can tell, no good way to handle this last case. You can assume that if `foo` existed in the "old" commit and you have no new name for it, it must have been removed; but if the idea of following renames is to start with an old name, this does not work in git: the `--follow` option only works when starting with recent history and moving back in time.
Problem
I want to "undo" all local changes to one specific file, i.e. return to this file's state at origin/master: - if it existed and was modified => revert to old version. - if it was added => delete file. - if it was removed => re-add file. - if it was renamed => rename back to old name. - etc. `git checkout origin/master -- path/to/file.txt` works for modified files but fails notably for files that were added and thus did not exist at origin/master ("No such file or directory"). If need be I can rely on commits only ever changing one file. (Then perhaps I could parse the SHAs from `git log --follow --oneline --decorate origin/master.. path/to/file.txt` and then revert (or rebase/drop) those commits. But this seems manual and hacky and I'd prefer a more declarative solution...) UPDATE: To clarify, the local changes have been committed. There may be multiple commits with changes to the file.