Actualizar el nombre del autor en todos los commit en git después de una date en particular

Lo intenté pero no pude encontrar cómo puedo comparar GIT_COMMITTER_DATE con una date específica para actualizar el correo electrónico del autor y el confirmador. Esto es lo que intenté, pero debo agregar la comparación de dates en condiciones adicionales para actualizar las confirmaciones particulares; de lo contrario, está recogiendo muy viejo se compromete también …

Aquí AFTER_DATE es la date después de la cual deseo actualizar el correo electrónico del autor.

git filter-branch --env-filter ' NAME="Kautsya Kanu" WRONG_EMAIL="kauts_kanu@Kautsyas-MacBook-Pro.local" CORRECT_EMAIL="example@gmail.com" AFTER_DATE="2017-03-31" if [ "$GIT_COMMITTER_EMAIL" = "$WRONG_EMAIL" -a "$GIT_COMMITTER_NAME" = "$NAME" ] then export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL" fi if [ "$GIT_AUTHOR_EMAIL" = "$WRONG_EMAIL" -a "$GIT_AUTHOR_NAME" = "$NAME" ] then export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL" fi' --tag-name-filter cat -- --branches --tags 

Si TL; DR: mira la sección inferior, donde sugiero lo que realmente quieres hacer en su lugar.

Cómo probar las dates en git filter-branch

Hay varias partes de este problema y debe decidir cuáles le interesan.

  • Todos los commits de Git tienen dos sellos de time: autor y committer.
  • Las marcas de time tienen dos partes: un estilo de Unix "segundos desde el 1 de enero de 1970" y una zona horaria.
  • git filter-branch copys commits; selectores (pasados ​​a git rev-list ) seleccionan qué commits se copyn. Para hacer la copy, Git efectivamente extrae la confirmación original, ejecuta todos sus filters para realizar los cambios que desee y luego realiza una nueva confirmación del resultado. Si el nuevo compromiso es bit por bit idéntico al original, es el original, es decir, la "copy" no ocupa espacio adicional.
  • Después de hacer la copy, todas las references "positivas" (nombres de twig y label) se asignan a las copys.

El último paso de reasignación es por qué, después de que git filter-branch completa, el git log muestra las copys nuevas (modificadas) en lugar de las confirmaciones originales (no modificadas).


1 Una gran cantidad de git filter-branch es simplemente un código para evitar hacer el lento y estúpido modo "extraer, modificar, rebuild", porque es increíblemente lento. Ser inteligente al respecto puede acelerar las cosas, de modo que en lugar de tomar, digamos, 24 horas, un command de filter de twig podría completarse en solo diez minutos. Pero para comprender lo que está haciendo, puede usar este model mental más simple: esencialmente está copyndo de nuevo el repository completo a un nuevo repository en el que los nuevos commits probablemente tengan diferentes numbers de ID de hash.


La parte difícil: marcas de time

En muchos lugares, Git te permite escribir expresiones simples como 2017-03-31 . Filter-branch no es uno de estos lugares. Además, cada filter es un poco de script de shell. Hay testings básicas integradas en el shell: test $num1 -ge $num2 , por ejemplo, probará si el valor numérico expandido de $num1 es mayor o igual que el valor expandido de $num2 .

Cuando está filtrando un compromiso, Git ha puesto las dates de autor y committer en las variables de entorno GIT_AUTHOR_DATE y GIT_COMMITTER_DATE . Lo que hay en estos, sin embargo, es precisamente lo que está en el compromiso en sí. Echemos un vistazo a un ejemplo de compromiso:

 $ git cat-file -p HEAD | sed 's/@/ /' tree 52cbd85298ed8ee95776f93ddb0eede02a36c539 parent 71e38d40e93aa1357dad53f1599c2a1c00b54dc5 author Junio C Hamano <gitster pobox.com> 1491895625 -0700 committer Junio C Hamano <gitster pobox.com> 1491895625 -0700 Eleventh batch for 2.13 Signed-off-by: Junio C Hamano <gitster pobox.com> 

Entonces GIT_AUTHOR_DATE , en este caso, contendría 1491895625 -0700 , y `GIT_COMMITTER_DATE es idéntico. A modo de comparación, aquí hay un fragment de una confirmación donde son diferentes:

 author Hiroshi Shirosaki <h.shirosaki gmail.com> 1488779947 +0900 committer Eric Wong <e 80x24.org> 1488922143 +0000 

Si decide usar marcas de time, debe averiguar cómo comparar las marcas de time que le interesan, con el equivalente a "2017-03-31". Si ignoramos la información de la zona horaria , podemos simplemente elegir un número como 1491000000 y convertirlo a la hora local:

 $ date -r 1491000000 Fri Mar 31 15:40:00 PDT 2017 

entonces, si usa 3:40 PM, hora del Pacífico, funciona a su favor, ese número funcionaría. Entonces podemos comparar GIT_AUTHOR_DATE y / o GIT_COMMITTER_DATE a 1491000000, aunque todavía tenemos que cortar el molesto desfase de la zona horaria:

 date_exceeds_1491000000() { test $1 -ge 1491000000 } 

y ahora podemos escribir:

 if date_exceeds_1491000000 $GIT_AUTHOR_DATE 

y deje que la split del argumento del caparazón se encargue de las cosas ( $1 será la parte antes del espacio en blanco, con $2 sosteniendo la zona).

Depende de usted descubrir cómo combinar el ejemplo anterior con su filter de entorno; Lo más importante aquí es si desea filtrar en function de la date del autor , la date del comitente o alguna combinación de ambos.

Una manera más fácil, pero con algunas advertencias: no usar las marcas de time

Por otro lado, podría optar por no probar las marcas de time directamente en el código de filter-bifurcación. En su lugar, puede dejar que los especificadores de git rev-list elijan las marcas de time. Eso le permite usar – desde --since , lo que le permite escribir convenientes dates de estilo "2017-03-31":

 git filter-branch ... --since=2017-03-31 ... 

Esto es mucho más simple y tiene la ventaja de no molestarse en copyr los commits que vienen antes de la timestamp. Sin embargo, tiene defectos. Estos pueden no ser fatales, pero debes considerarlos.

En primer lugar, ya no se controla qué date de GIT_AUTHOR_DATE y GIT_COMMITTER_DATE se utilizan. Git usa la timestamp committer. Si ese es el que quieres, ¡genial! 🙂

En segundo lugar, hay un problema si los commits tienen dates en order "asimétrico". Considere la siguiente parte de una twig:

 ...--E--F--G--H--... 

donde cada letra mayúscula representa un compromiso. Supongamos que la date de compromiso de E es ayer, F es hace dos semanas, G es hoy, y H es hace una semana. En otras palabras, cometer E , que sucedió primero de estos cuatro, ocurrió ayer; luego, dos semanas antes de eso, el compromiso F sucedió después del compromiso E

Esto parece imposible, y no es tan común. Pero sucede, especialmente si las confirmaciones se realizan en diferentes computadoras y una de ellas tiene la date incorrecta. Si confirmo algo ahora, y luego establezco la date en dos semanas, y luego confirmo otra cosa, la confirmación posterior tiene la marca de hora anterior.

En este caso, git rev-list puede omitir algunos commits, dejando "agujeros" en los commits seleccionados. Cuando se alimenta a git filter-branch , esto provocará que la copy también omita esos commits. Si el corte de --since es hace unos días, --since E y G , pero no F y H , para que la nueva cadena lea:

 ...--E'--G'--... 

donde E' es la nueva copy de E , y G' es la nueva copy de G

Si sus sellos de date y hora de confirmación están todos en el order correcto, esto nunca sucede, y todo está bien: puede usar – desde --since para limitar las confirmaciones que se copyrán.


2 Git tiene algunas optimizaciones que intentan dejar de ejecutar las confirmaciones, en function de sus marcas de time. He visto que estos cortes se comprometen de maneras "interesantes" que pueden afectar esto. Creo que estas optimizaciones están algo rotas: Git probablemente debería escanear todo el repository y establecer un indicador de configuration, por ejemplo, core.trust-internal-timestamps , en true si las core.trust-internal-timestamps time de confirmación son correctas y en false si no, con el valor inicial unset significa "necesidad de escanear y establecer en function del resultado". Un clon inicial puede escanear y establecer el valor, y agregar nuevas confirmaciones ( git fetch y git commit por ejemplo) puede verificar si se ajusta el valor establecido de true a false si las confirmaciones entrantes infringen las reglas de timestamp.

Tampoco está muy claro cómo se supone que funcionan las compensaciones de zona horaria. Internamente, Git solo hace un strtoul sobre el valor numérico, ignorando el huso horario. Ver la function parse_commit_date en commit.c .


Resumen, más el método más simple de todos

Puede probar las dates de forma explícita, haciendo su propia conversión desde 2017-03-31 al número y utilizando el command de test (que también es el [ command que ya está usando para comparar el nombre del autor y el correo electrónico); o puede usar --since limitar el set de confirmaciones que copy. ¿Qué usar, y si, de todos --since , es suficiente depende de usted?

Hay una última forma de hacer esto que es mucho más simple, pero no es lo que pediste. Aún debes considerarlo. Simplemente ejecute git log --topo-order (o utilice --all --decorate --oneline --graph , que puede y debe recordar como A DOG: a ll, d ecorate, o neline, g raph; tenga en count que --graph --topo-order implica --topo-order ) y escanea el resultado hasta que veas algunos buenos lugares para detener el filtrado. Anote estos valores hash de confirmación y añádalos como references negativas a sus selectores de revisión:

 git filter-branch --env-filter ...your-filter-here... \ --tag-name-filter cat -- --branches --tags \ ^123456789abcdef ^feedbeefdeadc0ffee 

(aquí 123456789abcdef es la abreviatura de uno de esos valores hash de confirmación). Los hash de confirmación negados detienen que git filter-branch examine esa confirmación y cualquier confirmación anterior accesible desde esa confirmación, por lo que no los copyrá. Como no está utilizando un --parent-filter , cualquier confirmación posterior a ese punto, que copy, se referirá a la confirmación original no copyda. Esta evitación de copy está basada en charts , por lo que no hay posibilidad de que se "saltee" se comprometa de la manera que hay para --since desde --since , si las dates están un poco rotas.

En otras palabras, las confirmaciones que no copys se quedan y no se modifican. No toman time para filtrar, lo que hace que git filter-branch vaya más rápido, tal vez mucho más rápido, convirtiendo un trabajo de filtrado de cuatro horas en un trabajo de diez segundos, por ejemplo (esto depende de cuántos commits copie y cuántos no lo haga t ). Y, se ocupa de no tocar confirmaciones "anteriores" a algún punto, independientemente de sus marcas de date internas.