Как инвертировать `git log --grep =<pattern>` или Как показать git log, которые не соответствуют шаблону

Я хочу использовать git log показать все коммиты, которые не соответствуют заданному шаблону. Я знаю, что могу использовать следующее, чтобы показать все коммиты, которые соответствуют шаблону:

git log --grep=<pattern>

Как я могу инвертировать смысл соответствия?

Я пытаюсь игнорировать коммиты, которые "столкнулись с версией..." в сообщении.

РЕДАКТИРОВАТЬ: я хочу, чтобы мой окончательный вывод был довольно многословным. например git log --pretty --stat, Итак, выход из git log --format=oneline не будет работать для меня.

8 ответов

Решение

Создайте список всех коммитов, вычтите те, чьи сообщения журнала содержат шаблон с ошибками, и отправьте результат в git log с вашими желаемыми опциями. На заключительном этапе пара вариантов git log удобны:

--stdin
В дополнение к коммиту, указанному в командной строке, прочитайте их из стандартного ввода.

--no-walk
Показывайте только данные обороты, но не проходите через своих предков.

Вы можете сделать это с помощью одного конвейера и процесса подстановки.

#! /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

Если вы не хотите использовать bash, вы можете сделать это с помощью 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{$_};
}

Предполагая, что один из вышеперечисленных находится в вашем PATH как git-log-vgrep и с историей формы

$ git lola
* b0f2a28 (tmp, feature1) D
* 68f87b0 C
* d311c65 B
* a092126 A
| * 83052e6 (ГОЛОВА, происхождение / мастер, мастер) Z
| * 90c3d28 Y
| * 4165a42 X
| * 37844 куб.
|/  
* f8ba9ea V

мы могли бы сказать

$ git log-vgrep X

чтобы получить Z, Y, W и V.

Вы также можете войти в другие ветви, так

$ git log-vgrep A tmp

дает D, C, B и V; а также

$ git log-vgrep C tmp~2..tmp

дает только D.

Одним из ограничений вышеуказанных реализаций является то, что вы используете шаблон, который соответствует всем коммитам, например, . или же ^тогда вы получите ГОЛОВУ. Вот как git log работает:

$ git log --stdin --no-walk --pretty = oneline 
                                        
                                    
                                

Это будет возможно с Git 2.4+ (2 квартал 2015 г.): см. Commit 22dfa8a Кристофа Юнгханса ( junghans ):

log: учат --invert-grep вариант

" git log --grep=<string> msgstr "показывает только коммиты с сообщениями, которые соответствуют данной строке, но иногда полезно иметь возможность показывать только коммиты, которые не имеют определенных сообщений (например," показывать мне те, которые не являются фиксациями FIXUP ").

Первоначально мы имели invert-grep флаг в grep_opt, но потому что " git grep --invert-grep "не имеет смысла, кроме как в сочетании с" --files-with-matches ", который уже покрыт" --files-without-matches ", оно было перенесено в редакцию структуры.
Чтобы иметь флаг там, функция лучше выражает функцию.

Когда запускаются только что вставленные два теста, история фиксирует сообщения initial "," second "," third "," fourth "," fifth "," sixth " а также " Second ", совершенные в таком порядке.
Коммиты, которые тоже не совпадают th " или же " Sec " является " second " а также " initial "Только для регистра без учета регистра" initial " Матчи.

--invert-grep

Ограничьте вывод коммитов сообщениями, которые не соответствуют шаблону, указанному в --grep=<pattern>,

Пример:

Я сначала grep сообщение с "секвенсор" в них:

vonc@voncm C:\Users\vonc\prog\git\git

> git log -2 --pretty="tformat:%s" --grep=sequencer
Merge branch 'js/sequencer-wo-die'
sequencer: ensure to release the lock when we could not read the index

Если я хочу сообщения без секвенсора:

> git log -2 --pretty="tformat:%s" --grep=sequencer --invert-grep
Second batch for 2.11
Merge branch 'js/git-gui-commit-gpgsign'

Относительно простой метод с большой гибкостью - использовать git log с опцией -z, переданной в awk. Опция -z добавляет нули между записями коммитов, что облегчает анализ с помощью awk:

git log --color=always -z | awk -v RS=\\0

(color = всегда требуется для сохранения цвета, когда на выходе получается труба). Затем просто добавить любое логическое выражение, которое вы хотите, которое работает с каждым полем. Например, при этом будут напечатаны все записи, в которых адрес электронной почты автора не от fugly.com, а день принятия был воскресенье:

git log --color=always -z | awk -v RS=\\0 '!/Author:.*fugly.com>/ && /Date:.* Sun /'

Еще одна приятная вещь - это то, что вы можете добавить любой параметр форматирования или диапазон ревизий в журнал git, и он все еще работает.

И последнее: если вы хотите разбить на страницы, используйте "less -r", чтобы сохранить цвета.

РЕДАКТИРОВАТЬ: изменил использовать -v в awk, чтобы сделать его немного проще.

Как и в ответе the the briguy, grep также имеет опцию -z, позволяющую ему работать со строками с нулевым символом в конце, а не со строками. Тогда это будет так же просто, как инвертировать совпадение:

git log -z --color | grep -vz "bumped to version"

В целях безопасности вы можете использовать только в сообщении фиксации. Чтобы сделать это с помощью grep, вам нужно использовать жемчужные выражения, чтобы соответствовать символам новой строки в строках с нулевым символом в конце. Чтобы пропустить заголовок:

git log -z | grep -Pvz '^commit.*\nAuthor:.*\nDate:.*\n[\S\s]*bumped to version'

Или с цветом:

git log -z --color | \
  grep -Pvz '^.....commit.*\nAuthor:.*\nDate:.*\n[\S\s]*bumped to version'

Наконец, если вы используете --stat, вы также можете сопоставить начало этого вывода, чтобы избежать совпадения имен файлов, содержащих строку коммита. Таким образом, полный ответ на вопрос будет выглядеть так:

log -z --color --pretty --stat | \
  grep -Pvz '^.....commit.*\nAuthor:.*\nDate:.*\n[\S\s]*?bumped to version[\S\s]*?\n [^ ]'

Обратите внимание, что grep -P описывается как "очень экспериментальный" на моей странице руководства. Может быть лучше использовать pcregrep вместо этого, который использует libpcre, см. Как дать шаблон для новой строки в grep?, Хотя grep -P прекрасно работает для меня, и я понятия не имею, если pcregrep имеет параметр -z или эквивалентный.

Получите список всех коммитов, содержащих ваш результат, затем отфильтруйте их хэши.

git log --format=oneline | grep -v `git log --grep="bumped to version" --format="%h"`

Насколько я могу судить, это невозможно сделать напрямую с одной командной строкой; вам нужно будет сделать что-то, как предлагает Джастин Лилли, а затем запустить 'git log' в результирующем списке хэшей, например:

git log --format="%h" | grep -v `git log -1 --grep="bumped to version" --format="%h"` > good-hashes
for h in `cat good-hashes`; do
    PAGER=cat git log -1 --pretty --stat $h
done

должен сделать свое дело.

Как уже упоминалось в VonC, лучший вариант - это обновить Git 2.4.0 (который в настоящее время установлен на RC2). Но даже если вы не можете этого сделать, нет причин для сложных сценариев. (Gnu) awk one-liner должен сделать это. git log имеет полезное -z опция разделения коммитов по NUL-символу, что облегчает их анализ:

git log -z --pretty --stat | awk 'BEGIN{ RS="\0"; FS="\n\n" } !match($2, /<pattern>/)'

Если у вас нет gnu awk, вам, вероятно, следует установить его. Или перенесите этот скрипт в вашу конкретную версию awk, которую я оставлю в качестве упражнения для читателя;-).

git log --pretty --stat | grep -v "bumped to version"
Другие вопросы по тегам