2016-04-29 17 views
25

Sto monitorando con git alcuni file di configurazione. Di solito faccio un git add -p interattivo, ma sto cercando un modo per aggiungere automaticamente tutte le linee nuove/modificate/cancellate che corrispondono a un modello. Altrimenti ci vorranno anni per fare tutte le divisioni interattive e aggiungere. git add ha un modello che corrisponde ai nomi dei file, ma non riesco a trovare nulla sul contenuto.Come posso aggiungere solo linee che corrispondono a un modello?

+0

Quindi, per essere chiari, si vuole 'git add' un file basato su un modello abbinato il suo _content_? –

+0

no, voglio solo aggiungere pezzi in un file, in base a un modello di linea. –

+0

Sono abbastanza sicuro che non sia possibile renderlo efficace contro interferenze arbitrarie. Puoi fornire esempi concreti delle modifiche che desideri identificare/accettare automaticamente? – jthill

risposta

3

ho messi in campo questo programma sperimentale e poco testato in TXR:

esempio di esecuzione: primo punto siamo nella repo:

$ git diff 
diff --git a/lorem.txt b/lorem.txt 
index d5d20a4..58609a7 100644 
--- a/lorem.txt 
+++ b/lorem.txt 
@@ -2,10 +2,14 @@ Lorem ipsum dolor sit amet, 
consectetur adipiscing elit, 
sed do eiusmod tempor 
incididunt ut labore et dolore 
-magna aliqua. Ut enim ad minim 
+minim 
+minim 
veniam, quis nostrud 
exercitation ullamco laboris 
+maxim 
+maxim 
nisi ut aliquip ex ea commodo 
+minim 
consequat. Duis aute irure 
dolor in reprehenderit in 
voluptate velit esse cillum 

E:

$ git diff --cached # nothing staged in the index 

L'obiettivo è di impegnare semplicemente le righe contenenti una corrispondenza per min:

$ txr addmatch.txr min lorem.txt 
patching file .merge_file_BilTfQ 

Ora qual è lo stato?

$ git diff 
diff --git a/lorem.txt b/lorem.txt 
index 7e1b4cb..58609a7 100644 
--- a/lorem.txt 
+++ b/lorem.txt 
@@ -6,6 +6,8 @@ minim 
minim 
veniam, quis nostrud 
exercitation ullamco laboris 
+maxim 
+maxim 
nisi ut aliquip ex ea commodo 
minim 
consequat. Duis aute irure 

E:

$ git diff --cached 
diff --git a/lorem.txt b/lorem.txt 
index d5d20a4..7e1b4cb 100644 
--- a/lorem.txt 
+++ b/lorem.txt 
@@ -2,10 +2,12 @@ Lorem ipsum dolor sit amet, 
consectetur adipiscing elit, 
sed do eiusmod tempor 
incididunt ut labore et dolore 
-magna aliqua. Ut enim ad minim 
+minim 
+minim 
veniam, quis nostrud 
exercitation ullamco laboris 
nisi ut aliquip ex ea commodo 
+minim 
consequat. Duis aute irure 
dolor in reprehenderit in 
voluptate velit esse cillum 

La roba corrispondente è nell'indice, e le non corrispondenti +maxim linee sono ancora non è stato classificato.

Codice in addmatch.txr:

@(next :args) 
@(assert) 
@pattern 
@file 
@(bind regex @(regex-compile pattern)) 
@(next (open-command `git diff @file`)) 
diff @diffjunk 
index @indexjunk 
--- a/@file 
+++ b/@file 
@(collect) 
@@@@ [email protected],@bflen [email protected],@aflen @@@@@(skip) 
@ (bind (nminus nplus) (0 0)) 
@ (collect) 
@ (cases) 
@line 
@  (bind zerocol " ") 
@ (or) 
[email protected] 
@  (bind zerocol "+") 
@  (require (search-regex line regex)) 
@  (do (inc nplus)) 
@ (or) 
[email protected] 
@  (bind zerocol "-") 
@  (require (search-regex line regex)) 
@  (do (inc nminus)) 
@ (or) 
[email protected] 
@;; unmatched - line becomes context line 
@  (bind zerocol " ") 
@ (end) 
@ (until) 
@/[^+\- ]/@(skip) 
@ (end) 
@ (set (bfline bflen afline aflen) 
     @[mapcar int-str (list bfline bflen afline aflen)]) 
@ (set aflen @(+ bflen nplus (- nminus))) 
@(end) 
@(output :into stripped-diff) 
diff @diffjunk 
index @indexjunk 
--- a/@file 
+++ b/@file 
@ (repeat) 
@@@@ [email protected],@bflen [email protected],@aflen @@@@ 
@ (repeat) 
@[email protected] 
@ (end) 
@ (end) 
@(end) 
@(next (open-command `git checkout-index --temp @file`)) 
@[email protected]\[email protected] 
@(try) 
@ (do 
    (with-stream (patch-stream (open-command `patch -p1 @tempname` "w")) 
     (put-lines stripped-diff patch-stream))) 
@ (next (open-command `git hash-object -w @tempname`)) 
@newsha 
@ (do (sh `git update-index --cacheinfo 100644 @newsha @file`)) 
@(catch) 
@ (fail) 
@(finally) 
@ (do 
    (ignerr [mapdo remove-path #`@tempname @tempname.orig @tempname.rej`])) 
@(end) 

Fondamentalmente la strategia è:

  • fare un po 'pattern matching sul git diff uscita per filtrare i fusti fino alle linee corrispondenti. Dobbiamo ricalcolare il conteggio delle righe "dopo" nell'intestazione del hunk e preservare le linee di contesto.

  • emette il diff filtrato in una variabile.

  • ottenere una copia pristine del file dall'indice utilizzando git checkout-index --temp. Questo comando emette il nome temporaneo che ha generato e lo catturiamo.

  • Ora inviare il diff filtrato/ridotto a patch -p1, destinando questo file temporaneo contenente la copia pristine dall'indice. Ok, ora abbiamo solo le modifiche che volevamo, applicate al file originale.

  • Quindi, creare un oggetto Git fuori dal file con patch, utilizzando git hash-object -w. Cattura l'hash che questo comando produce.

  • Infine, utilizzare git update-index --cacheinfo ... per immettere questo nuovo oggetto nell'indice sotto il nome del file originale, mettendo in pratica una modifica per il file.

Se questo viti, possiamo solo fare git reset per pulire l'indice, risolvere il nostro Scriptology rotto e riprovare.

L'allineamento cieco attraverso le linee + e - presenta problemi evidenti. Dovrebbe funzionare nel caso in cui i pattern corrispondano ai nomi delle variabili nei file di configurazione, piuttosto che nei contenuti. Per esempio.

sostituzione:

-CONFIG_VAR=foo 
+CONFIG_VAR=bar 

Qui, se abbiniamo il CONFIG_VAR, quindi entrambe le linee sono inclusi. Se corrispondiamo su foo nella parte destra, rompiamo le cose: finiamo con una patch che sottrae semplicemente la linea CONFIG_VAR=foo!

Ovviamente, questo potrebbe essere reso intelligente, tenendo conto della sintassi e della semantica del file di configurazione.

Come vorrei risolvere questo "per davvero" sarebbe scrivere un robusto parser di file di configurazione e re-generator (che conserva commenti, spazi bianchi e tutto). Quindi analizzare il nuovo file originale pristine per configare gli oggetti, migrare le modifiche corrispondenti da un oggetto all'altro e generare un file aggiornato per andare all'indice. Non scherzare con le patch.

+0

È impressionante. Sto sicuramente esaminando quel linguaggio TXR! –

-1

È possibile iniziare con git ls-files per ottenere un elenco di file di interesse per un determinato percorso. Quindi è possibile reindirizzare l'elenco in grep e limitare in base a una corrispondenza regolare. Infine, questo elenco ridotto di file può essere convogliata in via git addxargs git add:

git ls-files [path] | grep '^some regex goes here$' | xargs git add -p 

Questo approccio permetterà di applicare una regex intelligente che si spera possa ridurre il numero di file per la sessione interattiva git add. Per definizione, fare git add -p richiede l'interazione umana, quindi se dopo aver applicato lo schema hai ancora troppi file, dovresti trovare un altro approccio.

A proposito, a volte aiuta a cercare Stack Overflow prima della pubblicazione, dove potresti trovare messaggi molto utili come this one.

+1

Non penso che questo risponda alla sua domanda dato che dice di non voler fare la "divisione interattiva" –

+0

Non sono d'accordo. Può limitare a un percorso di interesse e la mia risposta dovrebbe funzionare bene. L'unico scenario in cui questo potrebbe richiedere troppo tempo è nel caso di una massiccia base di codice (ad esempio Windows in Microsoft), in cui non ha idea di dove si trovano i file. –

+1

Mi dispiace se non ero abbastanza preciso. Se vuole "aggiungere un file basato su un pattern abbinato al suo contenuto" (come hai chiesto sulla sua domanda); allora la tua risposta è perfettamente valida. Tuttavia SE egli desidera aggiungere HUNKS di file in base al fatto che corrispondano a un modello; allora il tuo metodo non funziona. –

4

Non credo sia possibile; dal git add -p mostra sempre i pezzi di modifiche; ma quel pezzo potrebbe contenere qualche riga che si desidera aggiungere (e corrisponde al modello) e una riga contenente le modifiche che non si desidera aggiungere.

volte affrontare un problema simile quando ho fatto due cambiamenti e vuole impegnarsi separatamente:

  • rinomina di una variabile
  • aggiungere alcune funzionalità

C'è una soluzione che uso :

  • Mettere da parte le mie modifiche (utilizzando git stash o semplicemente copiando il fi i)
  • rinomina la variabile (quindi ho rifatto la parte facile del mio lavoro; dal momento che la ridenominazione di una variabile è di solito presa cura di dall'IDE)
  • commettere questi cambiamenti
  • riapplicare le mie modifiche (utilizzando git stash pop o la copia dei file avanti)
  • commettere il resto dei miei cambiamenti
+0

È possibile, q.v. la mia risposta. –

+0

I è possibile SE E SOLO SE vuole aggiungere file corrispondenti a un pattern nel contenuto, ma NON se vuole aggiungere HUNKS (parte di file) corrispondente a un modello –

+0

Grazie per avermi segnalato così tanto. Continuo a pensare che se riesce a formulare una regex abbastanza intelligente e restrittiva, allora fare un 'git add -p'_or_interattivo o semplicemente aggiungere tutti i file potrebbe essere soddisfacente per il suo caso d'uso. –

1

Questo è, ovviamente, pazzo. Ma sai, abbiamo dei flussi di lavoro pazzi per il mio lavoro che occasionalmente mi chiedono e di solito c'è una buona ragione per la follia.

Ok, il modello è uno schema linea per linea o un modello "se il blocco lo contiene"? Se è linea per linea, forse potresti fare qualcosa di simile. Questo non funziona esattamente, ma è un inizio

git diff <file> | egrep '^[^+]|<pattern' > file.patch 
git stash 
git apply file.patch 

Se si deve applicare qualsiasi pezzo che ha il modello che stai cercando, allora si avrà bisogno di una, più di script più stateful per analizzare il vostro diff. In effetti, probabilmente è necessario comunque. Scorri attraverso la diff cercando i caratteri '@@' che indicano l'inizio di una sezione di diff. Buffer su quella sezione fino ad arrivare alla fine.Se hai eseguito il pattern in questione, invia la sezione, in caso contrario, quindi buttala via. Quindi applica quel nuovo diff come patch.

git diff <file> | parse_diff_script.sh > file.patch 
git stash 
git apply file.patch 
4

Ecco un modo:

  1. uso git diff > patch a fare una patch del diff corrente.

  2. uso gawk per effettuare una seconda patch solo di +/- righe corrispondenti al modello: rimuovere - da linee cancellate non seguono la struttura, eliminare + linee non corrispondenti al modello, modificare i numeri di riga intestazione fusto, uscita ogni modificata pezzo, ma non generare alcun hunk modificato che non abbia più alcuna modifica in essi.

  3. uso git stash save, apply patch, add -u, e stash pop da applicare e in scena la patch modificata e lasciare il resto dei cambiamenti unstaged.

questo ha funzionato per diversi casi di test, funziona su tutta la diff in una volta (tutti i file), ed è veloce.

#!/bin/sh 

diff=`mktemp` 
git diff > $diff 
[ -s $diff ] || exit 

patch=`mktemp` 

gawk -v pat="$1" ' 
function hh(){ 
    if(keep && n > 0){ 
    for(i=0;i<n;i++){ 
     if(i==hrn){ 
     printf "@@ -%d,%d +%d,%d @@\n", har[1],har[2],har[3],har[4]; 
     } 
     print out[i]; 
    } 
    } 
} 
{ 
    if(/^diff --git a\/.* b\/.*/){ 
    hh(); 
    keep=0; 
    dr=NR; 
    n=0; 
    out[n++]=$0 
    } 
    else if(NR == dr+1 && /^index [0-9a-f]+\.\.[0-9a-f]+ [0-9]+$/){ 
    ir=NR; 
    out[n++]=$0 
    } 
    else if(NR == ir+1 && /^\-\-\- a\//){ 
    mr=NR; 
    out[n++]=$0 
    } 
    else if(NR == mr+1 && /^\+\+\+ b\//){ 
    pr=NR; 
    out[n++]=$0 
    } 
    else if(NR == pr+1 && match($0, /^@@ \-([0-9]+),?([0-9]+)? \+([0-9]+),?([0-9]+)? @@/, har)){ 
    hr=NR; 
    hrn=n 
    } 
    else if(NR > hr){ 
    if(/^\-/ && $0 !~ pat){ 
     har[4]++; 
     sub(/^\-/, " ", $0); 
     out[n++] = $0 
    } 
    else if(/^\+/ && $0 !~ pat){ 
     har[4]--; 
    } 
    else{ 
     if(/^[+-]/){ 
     keep=1 
     } 
     out[n++] = $0 
    } 
    } 
} 
END{ 
    hh() 
}' $diff > $patch 

git stash save && 
    git apply $patch && 
    git add -u && 
    git stash pop 

rm $diff 
rm $patch 

arbitri:

git diffapply

unified diff format

gawkmatch groups to array

git add -u