Visualizza solo blocchi rilevanti di un diff / patch basato su un regexp


20

git log -G<regex> -pè uno strumento meraviglioso per cercare nella cronologia di una base di codice le modifiche che corrispondono allo schema specificato. Tuttavia, può essere schiacciante individuare l'hunk rilevante nell'output diff / patch in un mare di hunk per lo più irrilevanti.

Naturalmente è possibile cercare l'output della git logstringa / regex originale, ma ciò fa ben poco per ridurre il rumore visivo e la distrazione di molte modifiche non correlate.

Continuando a leggere git log, vedo --pickaxe-allche c'è l'esatto contrario di quello che voglio: allarga l'output (all'intero changeset), mentre io voglio limitarlo (allo specifico hunk).

In sostanza, sto cercando un modo per analizzare "in modo intelligente" il diff / patch in singoli hunk e quindi eseguire una ricerca su ogni hunk (indirizzando solo le linee modificate), scartare gli hunk che non corrispondono e produrre quelli quello fa.

Esiste uno strumento come quello che descrivo? Esiste un approccio migliore per ottenere gli hunk abbinati / interessati?

Alcune ricerche iniziali che ho fatto ...

  • Se fosse possibile grepl'output diff / patch e rendere dinamici i valori delle opzioni di contesto, ad esempio tramite regexps anziché il conteggio delle righe, ciò potrebbe essere sufficiente. Ma grepnon è esattamente costruito in questo modo (né sto necessariamente richiedendo quella funzione).

  • Ho trovato la suite patchutils , che inizialmente sembrava che potesse soddisfare le mie esigenze. Ma dopo aver letto le sue manpagine, gli strumenti non sembrano gestire hunk corrispondenti basati su regexps. (Possono accettare un elenco di hunk, però ...)

  • Alla fine mi sono imbattuto in splitpatch.rb , che sembra gestire bene l'analisi della patch, ma avrebbe bisogno di essere aumentato in modo significativo per gestire la lettura delle patch stdin, abbinando gli hunk desiderati e quindi emettendo gli hunk.


1
Non esattamente quello che hai chiesto, ma prova git log -Gfoo | meno + / foo
James Youngman

Risposte:


7

qui /programming//a/35434714/5305907 è descritto un modo per fare ciò che stai cercando. effettivamente:

git diff -U1 | grepdiff 'console' --output-matching=hunk

Mostra solo gli hunk che corrispondono alla stringa "console" fornita.


Grazie. grepdiffè fondamentalmente quello che voglio; devo aver perso la sua opzione di corrispondenza hunk! tuttavia ... le informazioni di commit di git sono rimosse da grepdiff, quindi una volta individuato il pezzo rilevante, è necessario divinare il commit sha dall'oggetto / blob sha nell'intestazione diff - un'operazione piuttosto costosa. (vedi stackoverflow.com/a/223890/2284440 ) sarebbe qualcosa di similegit find-object SHA --reverse | head -1 | cut -c 1-7 | { read sha ; git log -1 $sha; }
wrksprfct

nota anche che esiste una versione del golanggrepdiff che è più barebone in termini di argomenti accettati. nota che quando il hunk abbinato è l'ultimo hunk in un diff, include erroneamente l'intestazione git commit del seguente commit — qualcosa che mi ha completamente confuso fino a quando ho capito cosa stava succedendo!
wrksprfct

0

Non è esattamente quello che stai chiedendo, ma un modo per accedere a hunk è la modalità di aggiunta interattiva. Ciò richiede di verificare il commit dopo la patch che ti interessa

git checkout COMMIT_ID

quindi tornare indietro di un altro passo nel VCS, ma non nella directory di lavoro

git reset --soft HEAD^

(A questo punto, la differenza tra l'indice e la directory di lavoro corrisponderà alla patch che ti interessa.)

Ora puoi eseguire git add -p. Questo avvierà una sessione interattiva che ha /un'opzione, che ti consente di individuare blocchi in cui una riga corrisponde a una regex. Particolarmente utile se si desidera effettivamente elaborare ulteriormente quelle patch (come, preparare una scelta parziale di ciliegia).

Sfortunatamente, almeno in questo momento il /comando add -pfunziona solo all'interno di un singolo file, quindi potrebbe essere necessario saltare diversi file non rilevanti.


0

Basandomi sulla risposta sopra di @nagu e sulle altre risposte collegate, sono stato in grado di git log -Gmostrare solo gli hunk pertinenti.

  1. Per prima cosa crea uno script da qualche parte nel tuo $ PATH con questo contenuto:

    #!/bin/bash
    
    # pickaxe-diff : external diff driver for Git.
    #                To be used with the pickaxe options (git [log|show|diff[.*] [-S|-G])
    #                to only show hunks containing the searched string/regex.
    
    path=$1
    old_file=$2
    old_hex=$3
    old_mode=$4
    new_file=$5
    new_hex=$6
    new_mode=$7
    
    filtered_diff=$(diff -u -p $old_file $new_file | \
                    grepdiff "$GREPDIFF_REGEX" --output-matching=hunk | \
                    grep -v -e '+++ ' -e '--- ')
    
    a_path="a/$path"
    b_path="b/$path"
    
    echo "diff --git $a_path $b_path"
    echo "index $old_hex..$new_hex $old_mode"
    echo "--- $a_path"
    echo "+++ $b_path"
    echo "$filtered_diff"
  2. Chiama git log -Ge dì a Git di usare lo pickaxe-diffscript come driver diff esterno:

    export GREPDIFF_REGEX=<string>; 
    GIT_EXTERNAL_DIFF=pickaxe-diff git log -p --ext-diff -G $GREPDIFF_REGEX

    Questo utilizzerà lo script pickaxe-diff solo per generare i diff, quindi il resto git logdell'output (commit hash, messaggio, ecc.) Non sarà toccato.

Avvertenza
Il modo in cui il piccone Git funziona è che limita l'output ai file i cui hunk cambiano la stringa / regex specificata. Ciò significa che se un altro hunk in questi file contiene anche la stringa di ricerca / regex, ma non la modifica, verrà comunque visualizzato con lo script sopra. Questa è una limitazione di grepdiff. Esiste una richiesta pull aperta nel progetto patchutils per aggiungere un --only-matchingflag a grepdiff, che fornirebbe le funzionalità necessarie per filtrare correttamente questi hunk.


Ho scritto una mia soluzione in questo senso .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.