Reputation: 111716
In a Git code repository I want to list all commits that contain a certain word. I tried this
git log -p | grep --context=4 "word"
but it does not necessarily give me back the filename (unless it's less that five lines away from the word I searched for. I also tried
git grep "word"
but it gives me only present files and not the history.
How do I search the entire history, so I can follow changes on a particular word? I intend to search my codebase for occurrences of word to track down changes (search in files history).
Upvotes: 870
Views: 272808
Reputation: 323892
If you want to find all commits where the commit message contains a given word, use
git log --grep=word
If you want to find all commits where "word" was added or removed in the file contents (to be more exact: where the number of occurrences of "word" changed), i.e., search the commit contents, use a so-called 'pickaxe' search with
git log -Sword
In modern Git there is also
git log -Gword
to look for differences whose added or removed line matches "word" (also commit contents).
A few things to note:
-G
by default accepts a regex, while -S
accepts a string, but it can be modified to accept regexes using the --pickaxe-regex
.-S
finds commits where the number of occurrences of "word" changed, while -G
finds commits where "word" appears in the diff.-S<regex> --pickaxe-regex
and -G<regex>
do not do exactly the same thing.The git diff
documentation has a nice explanation of the difference:
To illustrate the difference between
-S<regex> --pickaxe-regex
and-G<regex>
, consider a commit with the following diff in the same file:+ return frotz(nitfol, two->ptr, 1, 0); ... - hit = frotz(nitfol, mf2.ptr, 1, 0);
While
git log -G"frotz\(nitfol"
will show this commit,git log -S"frotz\(nitfol" --pickaxe-regex
will not (because the number of occurrences of that string did not change).
This will show the commits containing the search terms, but if you want to see the actual changes in those commits instead you can use --patch
:
git log -G"searchTerm" --patch
This can then be piped to grep
to isolate the output just to display commit diff lines with that search term. A common use-case is to display diff lines with that search term in commits since and including a given commit - 3b5ab0f2a1
in this example - like so:
git log 3b5ab0f2a1^.. -G"searchTerm" --patch | grep searchTerm
Upvotes: 1274
Reputation: 3602
This is useful in combination with BFG (Git filter branch - not to be confused with git-filter-branch) and git-filter-repo. It just gets the file paths so that you can feed them into one of the two tools I just mentioned.
# Get all unique filepaths of files matching 'password'
# Source: https://stackoverflow.com/a/69714869/10830091
git rev-list --all | (
while read revision; do
git grep -F --files-with-matches 'password' $revision | cat | sed "s/[^:]*://"
done
) | sort | uniq
# Get all unique filenames matching 'password'
# Source: https://stackoverflow.com/a/69714869/10830091
git rev-list --all | (
while read revision; do
git grep -F --files-with-matches 'password' $revision | cat | sed "s/[^:]*://"
done
) | xargs basename | sort | uniq
This second command is useful for BFG, because it only accept file names and not repo-relative/system-absolute paths.
There you go. Enjoy using these Bash snippets for as much agony as they caused to me. I hate Bash, so why do I keep using it?
Any of the following options mean the same (git-rep documentation):
-l
--files-with-matches
--name-only
Instead of showing every matched line, show only the names of files that contain Blockquote
As for -F
, well, it just means use a fixed string instead a regex for pattern interpretation. A source.
Another useful note that belongs here: You can throw in -i
or --ignore-case
to be case insensitive.
sed "s/[^:]*://"
| sort | uniq
Who wants duplicate paths? Not you, not me! Oh hey look, they are sorted too! Enjoy.
Source: me. I have used this for as long as I can remember.
(man sort
and man uniq
)
xargs basename
You would think | basename
would work, but no. It does not accept input standard input, but as command line arguments. Here's an explanation for that. Go figure! basename
basically returns the stem filename without its leading path. man basename
.
Sure, just slap a realpath
at the end. Like so:
) | sort | uniq | xargs realpath
Of course you have to use xargs
because realpath
does not use standard input for input. It uses command-line arguments. Just like dirname
.
Upvotes: 4
Reputation: 9088
If you want search for sensitive data in order to remove it from your Git history (which is the reason why I landed here), there are tools for that. GitHub as a dedicated help page for that issue.
Here is the gist of the article:
The BFG Repo-Cleaner is a faster, simpler alternative to git filter-branch for removing unwanted data. For example, to remove your file with sensitive data and leave your latest commit untouched), run:
bfg --delete-files YOUR-FILE-WITH-SENSITIVE-DATA
To replace all text listed in passwords.txt wherever it can be found in your repository's history, run:
bfg --replace-text passwords.txt
See the BFG Repo-Cleaner's documentation for full usage and download instructions.
Upvotes: 0
Reputation: 107
To use a Boolean connector on a regular expression:
git log --grep '[0-9]*\|[a-z]*'
This regular expression searches for the regular expression [0-9]* or [a-z]* in commit messages.
Upvotes: 4
Reputation: 166803
You can try the following command:
git log --patch --color=always | less +/searching_string
or using grep
in the following way:
git rev-list --all | GIT_PAGER=cat xargs git grep 'search_string'
Run this command in the parent directory where you would like to search.
Upvotes: 12
Reputation: 1082
After a lot of experimentation, I can recommend the following, which shows commits that introduce or remove lines containing a given regexp, and displays the text changes in each, with colours showing words added and removed.
git log --pickaxe-regex -p --color-words -S "<regexp to search for>"
Takes a while to run though... ;-)
Upvotes: 52
Reputation: 615
One more way/syntax to do it is: git log -S "word"
Like this you can search for example git log -S "with whitespaces and stuff @/#ü !"
Upvotes: 12
Reputation: 49833
git log
's pickaxe will find commits with changes including "word" with git log -Sword
Upvotes: 269
Reputation: 7140
vim-fugitive is versatile for that kind of examining in Vim.
Use :Ggrep
to do that. For more information you can install vim-fugitive and look up the turorial by :help Grep
. And this episode: exploring-the-history-of-a-git-repository will guide you to do all that.
Upvotes: 1