Опция трейлеров в git --pretty option

Dec 20 2020

Я пытался извлечь сводку вкладов из журнала git, составить краткое изложение этого и создать на его основе excel / csv для представления отчетов.

Я действительно пробовал

git log --after="2020-12-10" --pretty=format:'"%h","%an","%ae","%aD","%s","(trailers:key="Reviewed By")"'

и CSV выглядит с пустым столбцом CSV в конце.

...
"7c87963cc","XYZ","[email protected]","Tue Dec 8 17:40:13 2020 +0000","[TTI] Add support for target hook in compiler.", ""
...

и git logвыглядит что-то вроде

commit 7c87963cc
Author: XYZ <[email protected]>
Date:   Tue Dec 8 17:40:13 2020 +0000

    [TTI] Add support for target hook in compiler.

    This adds some code in the TabeleGen ...
    This is my body of commit.

    Reviewed By: Sushant

    Differential Revision: https://codereviews.com/DD8822

Что мне не удалось, так это извлечь Differential Revisionстроку с помощью (trailers:key="Reviewed By")команды.

Я не мог найти много информации о том, как заставить это работать. Я проверил руководство по git и попробовал то, что он объясняет.

Что-то мне может не хватать в этой команде? Ожидаемый вывод должен содержать текст https://codereviews.com/DD8822в последней позиции в приведенном выше выводе CVS.

Ответы

3 fluffy Dec 20 2020 at 20:09

Я не уверен, но:

  • ключи трейлера не могут иметь пробелов (поэтому Reviewed By-> Reviewed-Byи Differential Revision-> Differential-Revision);
  • трейлеры не должны быть разделены новыми строками, а должны быть отделены от сообщения фиксации фиксации (поэтому Reviewed Byиз вашего вопроса не рассматривается как трейлер).

Я бы также не рекомендовал использовать CSV, но вместо этого использовал TSV: git output не знает синтаксиса CSV (экранирование точек с запятой и запятых), поэтому выходной документ может быть сгенерирован без возможности анализа.

Если ваши сообщения фиксации будут выглядеть так ( -вместо пробелов, без разделителей новой строки):

commit 7c87963cc
Author: XYZ <[email protected]>
Date:   Tue Dec 8 17:40:13 2020 +0000

    [TTI] Add support for target hook in compiler.

    This adds some code in the TabeleGen ...
    This is my body of commit.

    Reviewed-By: Sushant
    Differential-Revision: https://codereviews.com/DD8822

Тогда вам подойдет следующая команда:

git log --pretty=format:'%h%x09%an%x09%ae%x09%aD%x09%s%x09%(trailers:key=Reviewed-By,separator=%x20,valueonly)%x09%(trailers:key=Differential-Revision,separator=%x20,valueonly)'

создание короткого идентификатора фиксации, имени автора, адреса электронной почты автора, даты, сообщения фиксации, трейлера Reviewed-Byи трейлера Differential-Revisionдля вывода значений, разделенных табуляцией.


Если вы не можете изменить старые сообщения фиксации , потому что ваша история не является безопасной для этого (она опубликована, запряженных коллег, ваши инструменты связаны с опубликованным совершить хэш), то вы должны обработать git logвывод с sed, awk, perl, или любым другой инструмент преобразования текста для создания вашего отчета. Скажем, обработайте что-то вроде того, git log --pretty=format:'%x02%h%x1F%an%x1F%ae%x1F%aD%x1F%s%x1F%n%B'где строки между ^B(STX) и EOF должны быть как-то проанализированы (отфильтрованы для интересующих вас трейлеров), затем присоединены к их групповым строкам, начиная с ^B, а затем заменены символы, чтобы заменить разделители полей и записей на \tи нет характер соответственно.

Но опять же, если вы можете редактировать историю, исправляя трейлеры сообщений фиксации (не уверен, насколько это может повлиять), я бы порекомендовал вам сделать это, а затем отвергнуть идею дополнительных трейлеров обработки скриптов, которые не распознаются, git-interpret-trailersи просто исправить зафиксировать сообщения.


Редактировать 1 (текстовые инструменты)

Если переписывание истории невозможно, то реализация некоторых сценариев может вам помочь. Я довольно слаб в написании мощных скриптов sed/ awk/ perl, но позвольте мне попробовать.

git log --pretty=format:'%x02%h%x1F%an%x1F%ae%x1F%aD%x1F%s%x1F%n%B' \
    | gawk -f trailers.awk \
    | sed '$!N;s/\n/\x1F/' \
    | sed 's/[\x02\x1E]//g' \
    | sed 's/\x1F/\x09/g'

Как это работает:

  • gitгенерирует журнал, состоящий из данных, разделенных стандартными кодами C0 C1, предполагая, что таких символов нет в ваших сообщениях фиксации (STX, RS и US - я действительно не знаю, хорошее ли это место для их использования, и если я их применяю семантически корректно);
  • gawk фильтрует вывод журнала, пытаясь проанализировать группы, запущенные STX, и извлечь трейлеры, генерируя «двухстрочный» вывод (каждая нечетная строка для обычных данных, каждая четная строка для значений трейлера, соединенных запятыми, даже для отсутствующих трейлеров);
  • sedобъединяет нечетные и четные строки попарно (кредиты принадлежат Кароли Хорвату );
  • sed убирает STX и RS;
  • sed заменяет US на TAB.

Вот trailers.awk(опять же, я не awkпарень и понятия не имею, насколько идиоматичен следующий сценарий, но, похоже, он работает):

#!/usr/bin/awk -f

BEGIN {
    FIRST = 1
    delete TRAILERS
}

function print_joined_array(array) {
    if ( !length(array) ) {
        return
    }
    for ( i in array ) {
        if ( i > 0 ) {
            printf(",")
        }
        printf("%s", array[i])
    }
    printf("\x1F")
}

function print_trailers() {
    if ( FIRST ) {
        FIRST = 0
        return
    }
    print_joined_array(TRAILERS["Reviewed By"])
    print_joined_array(TRAILERS["Differential Revision"])
    print ""
}

/^\x02/ {
    print_trailers()
    print $0
    delete TRAILERS
}

match($0, /^([-_ A-Za-z0-9]+):\s+(.*)\s*/, M) {
    TRAILERS[M[1]][length(TRAILERS[M[1]])] = M[2]
}

END {
    print_trailers()
}

Пару слов о том, как awkработает скрипт:

  • предполагается, что записи, не требующие обработки, начинаются с STX;
  • он пытается найти grepкаждую строку, отличную от STX, для Key Name: Valueшаблона и сохраняет найденный результат во временный массив TRAILERS(который фактически служит мульти-картой, как Map<String, List<String>>в Java) для каждой записи;
  • каждая запись записывается как есть, но трейлеры записываются либо до обнаружения новой записи, либо при EOF.

Редактировать 2 (лучше awk)

Что ж, я очень слаб в этом awk, поэтому, как только я awkузнал больше о внутренних переменных, я понял, что awkскрипт можно полностью переопределить и создать готовый к использованию TSV-подобный вывод без какой-либо пост-обработки с помощью sedили perl. Итак, более короткая и улучшенная версия сценария:

#!/bin/bash

git log --pretty=format:'%x1E%h%x1F%an%x1F%ae%x1F%aD%x1F%s%x1F%B%x1E' \
    | gawk -f trailers.awk
#!/usr/bin/awk -f

BEGIN {
    RS = "\x1E"
    FS = "\x1F"
    OFS = "\x09"
}

function extract(array, trailer_key, __buffer) {
    for ( i in array ) {
        if ( index(array[i], trailer_key) > 0 ) {
            if ( length(__buffer) > 0 ) {
                __buffer = __buffer ","
            }
            __buffer = __buffer substr(array[i], length(trailer_key))
        }
    }
    return __buffer
}

NF > 1 {
    split($6, array, "\n")
    print $1, $2, $3, $4, $5, extract(array, "Reviewed By: "), extract(array, "Differential Revision: ")
}

Намного более кратко, легче читать, понимать и поддерживать.