¿Por qué una twig de filter no operativa crea divergencia y cómo soluciono eso?

Tengo una situación en la que fusioné un par de años de commits en un repository. Uno de los commits tenía un comentario que era una copy de un logging de Address Sanitizer relacionado con la corrección.

Eso no suena tan mal, excepto que los loggings de Sanitizer de direcciones se ven así:

==10856==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62a00000b201 at pc 0x47df61 bp 0x7fffffff2ca0 sp 0x7fffffff2c98 READ of size 1 at 0x62a00000b201 thread T0 #0 0x47df60 in Expand_Series ../src/core/m-series.c:145 #1 0x47e5a7 in Extend_Series ../src/core/m-series.c:187 #2 0x466e0c in Scan_Quote ../src/core/l-scan.c:462 #3 0x46a797 in Scan_Token ../src/core/l-scan.c:918 #4 0x46e263 in Scan_Block ../src/core/l-scan.c:1188 ... 

Y en eso sube a # 250 más o less en este caso. GitHub escanea para patrones #XXX y si coinciden con un número de problema, ponga una nota sobre la mención en el problema al que se hace reference. Entonces, de repente, GitHub cree que este compromiso está remarcando cada problema y la request de extracción, y lo hará durante un time.

Pensé que usaría un git filter-branch ya que realmente no me importa romper con la historia (tenía que hacer una twig de filter para deshacerme de algunas cosas que no quería) . Sin embargo, hice esa otra twig de filter antes de hacer una fusión y continuar con el trabajo. Ahora que me he dado count de que está apareciendo en GitHub, me gustaría volver y volver a escribirlo y no me importa si cada compromiso en cada twig después de este punto obtiene un nuevo hash. Eso está bien conmigo.

La reescritura me puso a trabajar, pero lo que no puedo entender es por qué hay tanta divergencia. Parece haber hecho una reescritura que está afectando las cosas antes de hacer ningún cambio en el comentario. Como una testing simple, probé lo que pensé que debería ser una no-operación:

 git filter-branch -f --msg-filter 'sed "s/a/a/g"' -- --all 

No soy una persona sed, pero tengo entendido que rehacería todos los posts de confirmación y sustituiría por a . (Ayn Rand estaría contento)

No diverge tantas confirmaciones como mi reemploop real … 600 en lugar de 1000. Pero que diverja en absoluto indica que tengo algún tipo de malentendido aquí. ¿Cómo puedo reescribir ese post de confirmación en el historial sin dañar las confirmaciones además de las que ocurren después … y get el efecto en todas las twigs?

Si hay un post existente que no termina con una nueva línea, sed agregará uno (al less algunas versiones de sed, incluida la que probé aquí):

 $ printf 'foo\nbar' foo bar$ printf 'foo\nbar' | sed 's/a/a/' foo bar $ 

lo que significa que su filter de post de testing podría haber alterado un post. En function de sus resultados, supongo que al less una confirmación, unos 600 confirmaciones de algunos consejos de sucursales, se modificó de esta manera. (He visto este mismo problema yo mismo antes).

(Otra posibilidad es algún tipo de normalización Unicode, aunque no he visto eso con sed ).

Suponiendo que este es el caso, el truco para usted será encontrar un command que no afecte a otras confirmaciones. Una buena $GIT_COMMIT es usar la variable de entorno $GIT_COMMIT para identificar los commit (s) a tocar, y asegurarse de hacer algo que realmente no sea operativo (un cat msg-filter podría funcionar mejor que sed , por ejemplo) en todos otros commits:

 ... --msg-filter 'if [ $GIT_COMMIT == <the one> ]; then fix_msg; else cat; fi' ... 

En cuanto a get el efecto en todas las twigs, tu -- --all debería hacer el truco ya.


Parece que ya sabes por qué las confirmaciones restantes obtienen SHA-1 nuevos, pero solo para completarlo includeé esto también. Puede omitir esta parte, está aquí para que otras personas lean la pregunta.

Si se modifica una confirmación, se obtiene una nueva SHA-1 (por definición, ya que SHA-1 es la sum de verificación del contenido de la confirmación). No es gran cosa hasta el momento, pero digamos que solo hay cinco commits (todos en master en este caso, no es que importe) y modificaremos el middle con un filter filter-branch:

 A <- B <- C <- D <- E [original] 

Digamos que el SHA-1 real para C comienza con 30001 ). Ahora construyamos un resultado parcial, en el medio de la operación de la twig de filter:

 A <- B <- C' 

Digamos, por alguna extraña coincidencia, el nuevo SHA-1 comienza con 30002 , versión 2 de Commit 3.

Echemos un vistazo a (parte de) el compromiso original D :

 $ git cat-file -p HEAD^ tree 954019cba5244a4a135ff62258660b3d2e3a8087 parent 30001... 

Commit D refiere, por número, para confirmar C Entonces filter-branch , mientras no cambia nada más sobre D , debe build un nuevo commit D' que diga parent 30002... :

 A <- B <- C' <- D' 

Del mismo modo, filter-branch se ve obligado a copyr el antiguo commit E al nuevo E' :

 A <- B <- C' <- D' <- E' [replacement] 

Por lo tanto, cualquier filter-branch que modifique alguna confirmación, también cambia todas las confirmaciones posteriores. (Esto también es cierto para git rebase . De hecho, git rebase y git filter-branch son como primos. Ambos simplemente leen commits existentes, aplican algunos cambios y escriben los resultados como nuevos commits; filter-branch hace todo programáticamente, es decir, no --interactive modo --interactive y tiene un set muy amplio y complejo de especificaciones para realizar cambios, y luego puede aplicarlo a múltiples twigs, en lugar de a una única twig).

Hay un lugar extra que podría ser el culpable (y fue en mi caso) . Considerar:

 $ git cat-file -p 20b9cd59c6c6a1a2bccfb2ddb9af68c083a28698 tree dee80bcd856b23aceb8946473bf64d9aef0fe629 parent b12dc8b9388dc0a2ae34563426043a612d296195 author XXX <xxx@example.com> 1355477802 +0200 committer XXX <xxx@example.com> 1355478447 +0200 encoding cp1251 Add (literally) three characters to one file that will inadvertently create hours of fun for people years later. 

Es la encoding, en este caso Windows 1251 . La persona que lo encontró lo resumió así:

msg-filter obtiene el post sin formatting, sin meta-información de encoding. Por lo tanto, incluso cuando utiliza un filter de msg transparente de 8 bits (como un gato normal), la confirmación re-creada no contendrá esa metainformación de encoding.

(Eso es ligeramente impreciso, porque el filter obtiene la información de encoding, puede leerla a través de la variable de env GIT_COMMIT. Es la salida, que no controla la encoding. Al less no sé cómo …)

Él arregló el desorder general en nuestra situación particular usando Puntos de injerto . Eso está más allá de mi conocimiento actual de git, así que no intentaré explicarlo.