Opção de trailers em git --pretty option

Dec 20 2020

Eu estava tentando extrair um resumo das contribuições do git log e criar um resumo conciso disso e criar um excel / csv a partir dele para apresentar relatórios.

Eu tentei

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

e o CSV se parece com uma coluna CSV em branco no final.

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

e git logparece algo como

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

O que não consegui foi extrair a Differential Revisionstring usando o (trailers:key="Reviewed By")comando.

Não consegui descobrir muito sobre como fazer isso funcionar. Eu verifiquei o manual do git e tentei o que ele explica.

Existe algo que eu possa estar faltando neste comando? A saída esperada deve ter o texto https://codereviews.com/DD8822na última posição na saída CVS acima.

Respostas

3 fluffy Dec 20 2020 at 20:09

Não tenho certeza, mas:

  • as chaves do trailer não podem ter espaços em branco (portanto Reviewed By-> Reviewed-Bye Differential Revision-> Differential-Revision);
  • trailers não devem ser delimitados por novas linhas, mas separados da mensagem de commit commit (portanto, Reviewed Bysua pergunta não é considerada um trailer).

Eu também não recomendaria o uso de CSV, mas sim de TSV: a saída git não reconhece a sintaxe CSV (ponto-e-vírgula e escape de vírgula), portanto, o documento de saída pode ser gerado de forma não analisável.

Se suas mensagens de confirmação forem parecidas com esta (em -vez de espaços, sem novos delimitadores de linha):

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

Então, o seguinte comando funcionaria para você:

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)'

produzindo ID de commit curto, nome do autor, e-mail do autor, data, mensagem de commit, trailer Reviewed-Bye trailer Differential-Revisionpara sua saída de valores separados por tabulação.


Se você não pode mudar o velho cometer mensagens porque seu histórico não é seguro para fazer isso (ele é publicado, puxado por seus pares, suas ferramentas são ligados aos hashes publicados commit), então você tem que processar a git logsaída com sed, awk, perlou qualquer outra ferramenta de transformação de texto para gerar seu relatório. Digamos, processe algo como git log --pretty=format:'%x02%h%x1F%an%x1F%ae%x1F%aD%x1F%s%x1F%n%B'onde as linhas entre ^B(STX) e EOF devem ser analisadas de alguma forma (filtradas para os trailers nos quais você está interessado), em seguida, unidas às linhas do grupo começando com ^Be, em seguida, caracteres substituídos para substituir os separadores de campo e entrada por \te não personagem respectivamente.

Mas, novamente, se você pode editar o histórico corrigindo trailers de mensagem de commit (não tenho certeza de quanto isso pode afetar), eu recomendo que você faça isso e rejeite a ideia de scripts extras de processamento de trailers que não são reconhecidos git-interpret-trailerse simplesmente consertem o enviar mensagens.


Editar 1 (ferramentas de texto)

Se reescrever o histórico não for uma opção, a implementação de alguns scripts pode ajudá-lo. Sou muito fraco para escrever scripts / sed/ poderosos , mas deixe-me tentar.awkperl

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'

Como funciona:

  • gitgera um log feito de dados delimitados com códigos C0 C1 padrão assumindo que não existem tais caracteres suas mensagens de commit (STX, RS e US - eu realmente não sei se é um bom lugar para usá-los assim e se eu os aplico semanticamente correto);
  • gawk filtra a saída de log tentando analisar grupos iniciados por STX e extrair os trailers, gerando uma saída de "duas linhas" (cada linha ímpar para dados regulares, cada linha par para valores de trailer unidos por vírgula, mesmo para trailers ausentes);
  • sedjunta linhas pares e ímpares (os créditos vão para Karoly Horvath );
  • sed remove STX e RS;
  • sed substitui US por TAB.

Aqui está o trailers.awk(novamente eu não sou um awkcara e não tenho ideia de como o script a seguir é idiomático, mas parece funcionar):

#!/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()
}

Algumas palavras sobre como o awkscript funciona:

  • ele assume que os registros que não requerem processamento estão começando com STX;
  • ele tenta grepcada linha não "STX" para um Key Name: Valuepadrão e salva o resultado encontrado em um array temporário TRAILERS(que serve na verdade como um multimapa, como Map<String, List<String>>em Java) para cada registro;
  • cada registro é escrito como está, mas os trailers são escritos antes de detectar um novo registro ou no EOF.

Editar 2 (melhor awk)

Bem, sou muito fraco em awk, então, depois de ler mais sobre awkvariáveis ​​internas, descobri que o awkscript pode ser totalmente reimplementado e produzir uma saída tipo TSV pronta para usar sem qualquer pós-processamento com sedou perl. Portanto, a versão mais curta e aprimorada do script é:

#!/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: ")
}

Muito mais conciso, fácil de ler, entender e manter.