Opción de remolques en git - opción bonita

Dec 20 2020

Estaba tratando de extraer un resumen de las contribuciones de git log y crear un resumen conciso de eso y crear un excel / csv a partir de él para presentar informes.

lo intenté

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

y el CSV se ve como con una columna CSV en blanco al final.

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

y se git logve 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

Lo que no pude tener éxito fue extraer la Differential Revisioncadena usando el (trailers:key="Reviewed By")comando.

No pude encontrar mucho sobre cómo hacer que esto funcione. Revisé el manual de git y probé lo que explica.

¿Hay algo que pueda faltar en este comando? La salida esperada debe tener el texto https://codereviews.com/DD8822en la última posición en la salida CVS anterior.

Respuestas

3 fluffy Dec 20 2020 at 20:09

No estoy seguro pero:

  • las claves de avance no pueden tener espacios en blanco (por lo tanto Reviewed By-> Reviewed-Byy Differential Revision-> Differential-Revision);
  • Los trailers no deben estar delimitados por nuevas líneas, sino separados del mensaje de confirmación de confirmación (por lo tanto, Reviewed Bysu pregunta no se considera un avance).

Tampoco recomendaría usar CSV, pero usar TSV en su lugar: la salida de git no es consciente de la sintaxis de CSV (punto y coma y comas que se escapan), por lo tanto, el documento de salida puede generarse no se puede analizar.

Si sus mensajes de confirmación se verían así (en -lugar de espacios, sin nuevos delimitadores de línea):

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

Entonces el siguiente comando funcionaría para usted:

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

produciendo un ID de confirmación corto, nombre de autor, correo electrónico del autor, fecha, mensaje de confirmación, avance Reviewed-Byy avance Differential-Revisionpara su salida de valores separados por tabulaciones.


Si usted no puede cambiar la vieja mensajes de confirmación debido a que su historia no es seguro para hacer esto (que se haya publicado, tirado por pares, sus herramientas están unidos a los hashes publicados commit), entonces usted tiene que procesar la git logsalida con sed, awk, perl, o cualquier otra herramienta de transformación de texto para generar su informe. Digamos, procese algo como git log --pretty=format:'%x02%h%x1F%an%x1F%ae%x1F%aD%x1F%s%x1F%n%B'dónde las líneas entre ^B(STX) y EOF deben analizarse de alguna manera (filtrar los trailers que le interesan), luego unirse a sus líneas de grupo comenzando con ^B, y luego reemplazar el carácter para reemplazar los separadores de campo y entrada con \ty no personaje respectivamente.

Pero nuevamente, si puede editar el historial arreglando los avances de los mensajes de confirmación (no estoy seguro de cuánto puede afectar), le recomiendo que lo haga y luego rechace la idea de scripts adicionales que procesan los avances que no son reconocidos por git-interpret-trailersy simplemente corrija el cometer mensajes.


Editar 1 (herramientas de texto)

Si reescribir el historial no es una opción, entonces implementar algunos scripts puede ayudarlo. Estoy bastante débil en la escritura de gran alcance sed/ awk/ perlguiones, pero voy a tratar de.

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'

Cómo funciona:

  • gitgenera un registro hecho de datos delimitados con códigos C0 C1 estándar asumiendo que no hay tales caracteres en sus mensajes de confirmación (STX, RS y US - realmente no sé si es un buen lugar para usarlos así y si los aplico semánticamente correcto);
  • gawk filtra la salida del registro tratando de analizar los grupos iniciados por STX y extraer los avances, generando una salida de "dos filas" (cada línea impar para datos regulares, cada línea par para valores de avance unidos por comas incluso para avances faltantes);
  • sedune líneas pares e impares por pares (los créditos van para Karoly Horvath );
  • sed elimina STX y RS;
  • sed reemplaza US a TAB.

Aquí está el trailers.awk(de nuevo, no soy un awkchico y no tengo idea de lo idiomático que es el siguiente script, pero 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()
}

Un par de palabras sobre cómo funciona el awkguión:

  • asume que los registros que no requieren procesamiento comienzan con STX;
  • intenta grepbuscar un Key Name: Valuepatrón en cada línea que no sea "STX" y guarda el resultado encontrado en una matriz temporal TRAILERS(que en realidad sirve como un multimapa, como Map<String, List<String>>en Java) para cada registro;
  • cada registro se escribe como está, pero los avances se escriben antes de detectar un nuevo registro o en EOF.

Editar 2 (mejor awk)

Bueno, soy realmente débil awk, así que una vez que leí más sobre awklas variables internas, descubrí que el awkscript se puede volver a implementar por completo y producir una salida similar a TSV lista para usar sin ningún procesamiento posterior con sedo perl. Entonces, la versión más corta y mejorada del script es:

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

Mucho más conciso, más fácil de leer, comprender y mantener.