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?
risposta
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.
È impressionante. Sto sicuramente esaminando quel linguaggio TXR! –
È 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 add
xargs 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.
Non penso che questo risponda alla sua domanda dato che dice di non voler fare la "divisione interattiva" –
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. –
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. –
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
È possibile, q.v. la mia risposta. –
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 –
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. –
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
Ecco un modo:
uso
git diff > patch
a fare una patch del diff corrente.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.uso
git stash save
,apply patch
,add -u
, estash 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:
Quindi, per essere chiari, si vuole 'git add' un file basato su un modello abbinato il suo _content_? –
no, voglio solo aggiungere pezzi in un file, in base a un modello di linea. –
Sono abbastanza sicuro che non sia possibile renderlo efficace contro interferenze arbitrarie. Puoi fornire esempi concreti delle modifiche che desideri identificare/accettare automaticamente? – jthill