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.