I want to use git log
to show all commits that do not match a given pattern. I know I can use the following to show all commits that do match a pattern:
git log --grep=<pattern>
How do I invert the sense of matching?
I am trying to ignore commits that have "bumped to version ..." in the message.
EDIT: I want my final output to be pretty verbose. e.g. git log --pretty --stat
. So output from git log --format=oneline
won't work for me.
Generate a list of all commits, subtract those whose log messages contain the offending pattern, and feed the result to git log
with your desired options. In the final stage, a couple of options to git log
are handy:
--stdin
In addition to the commit listed on the command line, read them from the standard input.
--no-walk
Only show the given revs, but do not traverse their ancestors.
You can do it with a single pipeline and process substitution.
#! /bin/bash
if (( $# < 1 )); then
echo >&2 "Usage: $0 pattern [<since>..<until>]"
exit 1
fi
pattern=$1
shift
git log --format=%H $@ |
grep -v -f <(git log --format=%H "--grep=$pattern" $@) |
git log --pretty --stat --stdin --no-walk
If you don't want to use bash, you could do it with Perl.
#! /usr/bin/env perl
use strict;
use warnings;
no warnings "exec";
sub usage { "Usage: $0 pattern\n" }
sub commits_to_omit {
my($pattern) = @_;
open my $fh, "-|", "git", "log", "--grep=$pattern", "--format=%H", @ARGV
or die "$0: exec: $!";
my %omit = map +($_ => 1), <$fh>;
%omit;
}
die usage unless @ARGV >= 1;
my $pattern = shift;
my %omit = commits_to_omit $pattern;
open my $all, "-|", "git", "log", "--format=%H", @ARGV
or die "$0: exec: $!";
open my $out, "|-", "git", "log", "--pretty", "--stat", "--stdin", "--no-walk"
or die "$0: exec: $!";
while (<$all>) {
print $out $_ unless $omit{$_};
}
Assuming one of the above is in your PATH as git-log-vgrep
and with a history of the form
$ git lola * b0f2a28 (tmp, feature1) D * 68f87b0 C * d311c65 B * a092126 A | * 83052e6 (HEAD, origin/master, master) Z | * 90c3d28 Y | * 4165a42 X | * 37844cb W |/ * f8ba9ea V
we could say
$ git log-vgrep X
to get Z, Y, W, and V.
You can also log other branches, so
$ git log-vgrep A tmp
gives D, C, B, and V; and
$ git log-vgrep C tmp~2..tmp
yields just D.
One limitation of the above implementations is if you use a pattern that matches all commits, e.g., .
or ^
, then you'll get HEAD. This is how git log
works:
$ git log --stdin --no-walk --pretty=oneline </dev/null 83052e62f0dc1c6ddfc1aff3463504a4bf23e3c4 Z