March 5th, 2009
|12:41 pm - Fun with "git log --grep"|
Earlier I showed a somewhat advanced feature of "git grep" command, and hinted that a limited subset of the feature is available also in "git log" to look for commits that have specified strings in their commit log messages.
This entry is the promised follow-up.
First, we learn the very basic form:
git log --grep=frotz --grep=nitfol --since=1.month
This finds the commits that happened within the last month and mentioned either frotz or nitfol in their commit log messages. As with the usual "git grep", giving more than one patterns means "this or that".
Two options, "--author=name" and "--committer=name" are also supported internally with the same "grep" mechanism. You can ask:
git log --grep=frotz --author=Linusto find changes written by Linus or mentioned frotz (the use of or semantics here often feels unintuitive and less useful, but we will come to that shortly).
The following attempt to overcome this does not work as you expect, for two reasons:
git log --grep=frotz --and --author=Linus One is simply because "git log" family does not support the --and option.
But a more fundamental reason why this does not work is because "grep" is line oriented. "git grep -e frotz --and -e nitfol" looks for lines that have both frotz and nitfol, but obviously the line that records Linus's name does not have the word frotz; the above will never match.
That is partly the reason why "git log" family does not support --and option to begin with. The and semantics is not very useful in the context of "git log" command, and this is true even if we limit the discussion to the message part. For example, even if we had --and option to "git log" family, this command line:
git log --grep=frotz --and --grep=nitfol will not find a commit if its log message has a line break in the sentence "This commit fixes the frotz feature and adds support for the nitfol feature" somewhere between the two words you are looking for.
Does that mean the "grep" feature in "git log" family can practically only find one item at a time and not very useful?
Not really. I did not explain "git grep --all-match" in the earlier entry, but it is exactly what we want to use here, so we digress a bit.
We learned that this finds files with a line that has both frotz and nitfol:
git grep -l -e frotz --and -e nitfol But often you would want to see a file that has both frotz and nitfol, not necessarily on the same line but anywhere in the same file. The above is not what you want to use for that purpose.
There is an aspect of "grep" that cares only about the existence of matches with the pattern in a file (this is true not just with "git grep" but also is true with the regular "grep"). The exit status of the command is zero (success) if there is a match, and the -l option shows the names of the files that have matches, but it does not actually show the lines that matched. "git grep" extends this concept.
"git grep" allows you to say "I am looking for this or that patterns, but please consider a file successfully matches with them only if you found matches with all of the patterns", with the --all-match option (the regular "grep" does not let you do this). Using this mechanism:
git grep -l --all-match -e frotz -e nitfol will give the names of the files that have both frotz and nitfol somewhere in them. The matches do not have to be on the same line; they can be anywhere in the same file.
The way "git log" family uses the grep mechanism is exactly for this "does the whole thing has a match?" aspect , not "show me the lines that match these criteria" aspect. It allows you to use this mechanism like so:
git log --all-match --grep=frotz --author=Linus This will show commits that mention frotz and written by Linus.