可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Consider a git repository, where a file was once deleted.
git rm path/to/file
git commit -a -m"testing"
Ok, now I want to see the git log
for the file, but I receive the classic error message:
git log path/to/file
fatal: ambiguous argument 'path/to/file': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions
The solution is simple - add --
:
git log -- path/to/file
But... why? Why is this needed? What is the rationale here? Can't git do an educated guess, that this might have been a file once? I understand the "ambiguity" problem - but there never was a tag by that name. If the file was once deleted, and no tag is present, then choosing "the file interpretation" is always the good choice.
On the other hand, it's possible to have a tag named the same as a file, which git log
handles pretty well:
fatal: ambiguous argument 'path/to/file': both revision and filename
Use '--' to separate filenames from revisions
This behavior seems inconsistent. Could anyone explain what the developers of git had in mind?
回答1:
git log
could be used on files as well as on branches, tags and so on.
Assume you have a folder called a/b/c
, you'll get the commits for this folder using
git log a/b/c
That's fine.
You could also have a branch called d/e/f
. You'll get the commits for this branch using
git log d/e/f
That's fine too.
Things start to get complicated if the item where git log
should work on could not be clearly determined. If you're stupid and call your branch a/b/c
too, git has no clue whose log shall be printed: that of the branch a/b/c
or the log of your directory a/b/c
? Therefore, you have to tell a bit more about the information you want to receive:
- show the log of the branch
a/b/c
:
git log a/b/c --
- show the log of the folder
a/b/c
in the current branch:
git log -- a/b/c
- show the log of the folder
a/b/c
in the a/b/c
branch:
git log a/b/c -- a/b/c
With the deleted file, you have a similar problem: there's neither a file called path/to/file
present in the working copy, nor is there a branch called path/to/file
. This is the reason why you have to specify what you want.
Of course, git could know that there was a file called path/to/file
20.000 revisions ago but this would require (worst case) to search the entire history of your project whether such a file existed or not.
By explicitly specifying the file path after the --
, you tell git:
search harder for that file, even if it takes hours
Conclusion (answering your question):
in your case, the --
is needed because otherwise git log
would work slower in general.
回答2:
Why the difference when the file exists versus when it doesn't? Let's look at the cases:
- A revision and filename both exist. Obviously ambiguous.
- A revision exists and filename doesn't. Obviously not ambiguous.
- A revision doesn't exist and a filename does. Obviously not ambiguous.
- A revision doesn't exist and a filename doesn't either. Ambiguous?
Indeed it is.
To know whether a file ever existed at some path means to walk history, opening each commit, opening each tree and walking the tree to see if this path existed then. On a large repository with a lot of commits and a deep path... this could get expensive, especially on a slow disk.
But! You say. Isn't that exactly what git log filename
does anyway? And the answer is yes, it is. The difference is that when I run git log filename
and the file is known to exist, then git-log
knows that I want to spend the time to get this history.
If, instead, I run git log foo
and foo is not a revision or a file that currently exists then it would need to invest the time to walk the entire graph just to tell me that foo
was ambiguous.
Ouch.
So you're welcome to say git log -- filename
to tell git that you really want it to go walking the graph. Otherwise, it will decline.
Side note:
git merely stat(2)
s the argument you give on the command line to determine if the file exists. It doesn't look in the index, nor does it open up your HEAD tree. It could, of course, do those things, which would allow you to use git log filename
where filename
was a deletion not staged for commit. This seems like a very reasonable change.
回答3:
there never was a tag by that name.
Why do you say that? git tag path/to/file
works just fine.
It really is as simple as it looks: git rm
takes pathnames only; git log
takes ref names and path names, refnames first, and anything that could be a pathname could also be a ref name -- this isn't so much a rule for what could be a ref name as the definition.
On small projects it'd be easy for git log to decide that if it's valid at all it has to have been a path to a source file at some point, but at this point you have to make a judgement call balancing the likelihood and cost of all the possible screwups here. Is it likelier that you're asking for the logs for a file that no longer exists, or that you fatfingered an existing path name or an existing ref? git log remote/ref
is very common. I think git's just presuming a name that matches nothing current is likeliest to be a typo.