-S matches literal substrings. -G matches regex against the diff content.
git log -G"AKIA[0-9A-Z]{16}" --all -p # any AWS access key shape
git log -G"console\.(log|error|warn)" -- src/ # any console call added/removed
git log -G"TODO\(.*\):" --since="2 months ago" # TODOs with author tags
The key difference is broader than just "regex":
-S"foo" matches only commits where the number of occurrences of "foo" changed. A commit that moves a line containing "foo" without changing the count doesn't match.
-G"pattern" matches any commit whose diff has an added or removed line matching the pattern. Unlike a plain grep, it only looks at changed lines — unchanged context lines don't count.
That makes -G more inclusive but also noisier. Use -S when you want "the commit that introduced/removed this exact thing" and -G when you want "any commit that touched a line matching this shape."
A common pattern: hunt for any line referencing a fragile API.
git log -G"deprecatedApi\(" --all --oneline
Combine with -p to read the actual diffs, or with --name-only to just see which files were touched.
Performance note: regex matching is slower than literal substring counting. On large repos, scope with -- <path> or a --since= window. -G over years of unrestricted history can take minutes.
When in doubt: try -S first (fast, precise). Fall back to -G when the thing you're looking for has variation (whitespace, optional args, alternative forms).