tipos tag remove practices etiquetas crear best git git-add

git - tag - ¿Cómo puedo agregar solo líneas que coincidan con un patrón?



git tag best practices (5)

Estoy siguiendo con git algunos archivos de configuración. Por lo general hago un git add -p interactivo, pero estoy buscando una manera de agregar automáticamente todas las líneas nuevas / modificadas / eliminadas que coincidan con un patrón. De lo contrario, me llevará años envejecer y agregar toda la división interactiva. git add tiene un patrón que coincide con los nombres de archivo, pero no puedo encontrar nada sobre el contenido.


Aquí hay una manera:

  1. use git diff > patch para hacer un patch de la diferencia actual.

  2. use gawk para hacer un segundo parche solo de +/- líneas que coincidan con el patrón: eliminar - de las líneas eliminadas que no coincidan con el patrón, eliminar + líneas que no coincidan con el patrón, modificar los números de línea del encabezado del fragmento, generar cada fragmento modificado, pero no t genera cualquier fragmento modificado que ya no tenga ningún cambio en ellos.

  3. use git stash save , apply patch , add -u y stash pop para aplicar y escalone el parche modificado y deje el resto de los cambios sin programar.

esto funcionó para varios casos de prueba, funciona en todo el diferencial a la vez (todos los archivos), y es rápido.

#!/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

refs:

git diff apply

formato diff unificado

gawk grupos a la matriz

git add -u


Esto es, por supuesto, una locura. Pero, sabes, tenemos algunos flujos de trabajo locos en mi trabajo que ocasionalmente pregunto y generalmente hay una buena razón para la locura.

Ok, ¿es el patrón un patrón línea por línea o un patrón "si el fragmento lo contiene"? Si es línea por línea, quizás podrías hacer algo como esto. Esto no funcionará exactamente, pero es un comienzo.

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

Si tiene que aplicar cualquier fragmento que tenga el patrón que está buscando, entonces necesitará un script más largo y con más estado para analizar su diff. De hecho, eso es probablemente necesario de todos modos. Rastrear a través del diff buscando caracteres ''@@'' que indiquen el comienzo de una sección de diff. Amontona esa sección hasta que llegues al final. Si se ha topado con el patrón en cuestión, imprima la sección, si no, entonces deséchela. A continuación, aplique esa nueva diferencia como un parche.

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


No creo que sea posible; ya que git add -p siempre te muestra trozos de cambios; pero ese fragmento podría contener alguna línea que quisieras agregar (y que coincida con tu patrón) y una línea que contenga cambios que no quieras agregar.

A veces me enfrento a un problema similar cuando hice dos cambios y quiero cometerlos por separado:

  • renombrar una variable
  • añadir alguna funcionalidad

Hay una solución que utilizo:

  • Poner mis cambios a un lado (usando git stash o simplemente copiando los archivos)
  • renombrar la variable (así que rehago la parte fácil de mi trabajo; ya que el IDE generalmente se encarga de renombrar una variable)
  • cometer estos cambios
  • volver a aplicar mis cambios (usando git stash pop o copiando los archivos de nuevo)
  • cometer el resto de mis cambios

Puede comenzar con git ls-files para obtener una lista de archivos de interés para una ruta determinada. Luego puede canalizar esa lista en grep y restringir en función de una coincidencia de expresiones regulares. Finalmente, esta lista reducida de archivos se puede canalizar en git add través de xargs git add :

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

Este enfoque le permitirá aplicar una expresión regular inteligente que, con suerte, puede reducir la cantidad de archivos para su sesión interactiva de git add . Por definición, hacer git add -p requiere interacción humana, por lo que si después de aplicar el patrón todavía tiene demasiados archivos, debería encontrar otro enfoque.

Por cierto, a veces es útil buscar Desbordamiento de pila antes de publicar, donde puede encontrar publicaciones muy útiles como esta .


Saqué este programa experimental y mal probado en TXR :

Ejecución de la muestra: primero dónde estamos en el repositorio:

$ 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

Y:

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

El objetivo es simplemente cometer las líneas que contienen una coincidencia para min :

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

Ahora que es el estado?

$ 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

Y:

$ 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

El material coincidente está en el índice, y las líneas de +maxim coincidencia aún no se han organizado.

Código en 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) @@@@ -@bfline,@bflen +@afline,@aflen @@@@@(skip) @ (bind (nminus nplus) (0 0)) @ (collect) @ (cases) @line @ (bind zerocol " ") @ (or) +@line @ (bind zerocol "+") @ (require (search-regex line regex)) @ (do (inc nplus)) @ (or) -@line @ (bind zerocol "-") @ (require (search-regex line regex)) @ (do (inc nminus)) @ (or) -@line @;; 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) @@@@ -@bfline,@bflen +@afline,@aflen @@@@ @ (repeat) @zerocol@line @ (end) @ (end) @(end) @(next (open-command `git checkout-index --temp @file`)) @tempname@/t@file @(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)

Básicamente la estrategia es:

  • haga un poco de coincidencia de patrones en la salida de git diff para filtrar los trozos hasta las líneas correspondientes. Debemos volver a calcular el recuento de líneas "después" en el encabezado del hunk y preservar las líneas de contexto.

  • la salida filtrada dif en una variable.

  • obtenga una copia prístina del archivo del índice utilizando git checkout-index --temp . Este comando genera el nombre temporal que ha generado y lo capturamos.

  • Ahora envíe la diferencia filtrada / reducida al patch -p1 , patch -p1 este archivo temporal que contiene la copia original del índice. Bien, ahora tenemos los cambios que queríamos, aplicados al archivo original.

  • A continuación, cree un objeto Git fuera del archivo parchado, utilizando git hash-object -w . Captura el hash que genera este comando.

  • Por último, use git update-index --cacheinfo ... para ingresar este nuevo objeto en el índice con el nombre del archivo original, git update-index --cacheinfo ... efectiva un cambio para el archivo.

Si esto se equivoca, solo podemos hacer un git reset de Git para borrar el índice, corregir nuestra secuencia de comandos rota e intentarlo de nuevo.

Solo hacer coincidencias ciegas a través de las líneas + y - tiene problemas obvios. Debería funcionar en el caso en que los patrones coincidan con los nombres de las variables en los archivos de configuración, en lugar del contenido. P.ej

Reemplazo:

-CONFIG_VAR=foo +CONFIG_VAR=bar

Aquí, si coincidimos en CONFIG_VAR , entonces se incluyen ambas líneas. Si coincidimos en foo en el lado derecho, rompemos cosas: terminamos con un parche que simplemente resta la línea CONFIG_VAR=foo !

Obviamente, esto podría hacerse inteligente, teniendo en cuenta la sintaxis y la semántica del archivo de configuración.

Cómo resolvería esto "de verdad" sería escribir un analizador y re-generador de archivos de configuración (que conserva los comentarios, espacios en blanco y todo). Luego analice el nuevo y original archivo original para configurar objetos, migre los cambios correspondientes de un objeto a otro y genere un archivo actualizado para ir al índice. No te metas con los parches.