git rebase sin cambiar las marcas de tiempo de confirmación

157

¿Tendría sentido realizar git rebasemientras se conservan las marcas de tiempo de confirmación?

Creo que una consecuencia sería que la nueva sucursal no necesariamente tendrá fechas de confirmación cronológicamente. ¿Es eso teóricamente posible? (por ejemplo, usando comandos de plomería; solo curiosidad aquí)

Si es teóricamente posible, ¿es posible en la práctica con rebase, no cambiar las marcas de tiempo?

Por ejemplo, suponga que tengo el siguiente árbol:

master <jun 2010>
  |
  :
  :
  :     oldbranch <feb 1984>
  :     /
oldcommit <jan 1984>

Ahora, si me rebase oldbranchen masterla fecha de la confirmó cambios desde febrero 1984 hasta Junio 2010. ¿Es posible cambiar ese comportamiento para que comprometen la marca de tiempo no se cambia? Al final obtendría así:

      oldbranch <feb 1984>
      /
 master <jun 2010>
    |
    :

¿Eso tendría sentido? ¿Se permite incluso en git tener un historial en el que una confirmación anterior tenga una confirmación más reciente como padre?

Olivier Verdier
fuente
3
Es extraño que la respuesta a la pregunta sea "no necesitas hacer nada, así es como funciona por defecto". Pero ahora suponga que desea que el compromiso se ordene en el orden de fechas adecuado al hacer un rebase (que es un escenario bastante natural si lo piensa). Ahora, no pude encontrar cómo lograr eso, y
publiqué
1
David menciona otra opción para restablecer la fecha confirmador: git rebase --committer-date-is-author-date SHA. Vea mi respuesta editada a continuación
VonC
Acabo de escribir una respuesta extensa sobre una pregunta similar cuyo autor intentó las respuestas explicadas aquí y no pudo aplicarlas de manera satisfactoria.
axiac

Respuestas:

149

Actualización de junio de 2014: David Fraser menciona en los comentarios una solución que también se detalla en " Cambiar marcas de tiempo al cambiar el nombre de git branch ", utilizando la opción --committer-date-is-author-date(introducida inicialmente en enero de 2009 en commit 3f01ad6

Tenga en cuenta que la --committer-date-is-author-dateopción parece dejar la marca de tiempo del autor y establecer la marca de tiempo del confirmador para que sea la misma que la marca de tiempo del autor original, que es lo que quería el OP Olivier Verdier .

Encontré el último commit con la fecha correcta e hice:

git rebase --committer-date-is-author-date SHA

Ver git am:

--committer-date-is-author-date

De manera predeterminada, el comando registra la fecha del mensaje de correo electrónico como la fecha del autor del compromiso y utiliza la hora de creación del compromiso como la fecha del confirmador.
Esto permite al usuario mentir sobre la fecha del confirmador utilizando el mismo valor que la fecha del autor .


(Respuesta original, junio de 2012)

Podría intentar, para un rebase no interactivo

git rebase --ignore-date

(de esta respuesta SO )

Esto se pasa a git am, que menciona:

 --ignore-date

De manera predeterminada, el comando registra la fecha del mensaje de correo electrónico como la fecha del autor del compromiso y utiliza la hora de creación del compromiso como la fecha del confirmador.
Esto permite al usuario mentir sobre la fecha del autor utilizando el mismo valor que la fecha del confirmador.

Para git rebase, esta opción es "Incompatible con la opción --interactiva".

Dado que puede cambiar a voluntad la marca de tiempo de la fecha de confirmación anterior (con git filter-branch), supongo que puede organizar su historial de Git con cualquier orden de fecha de confirmación que desee / necesite, ¡incluso establecerlo en el futuro! .


Como Olivier menciona en su pregunta, la fecha del autor nunca cambia con una rebaja;
Del libro Pro Git :

  • El autor es la persona que originalmente escribió el trabajo,
  • mientras que el responsable es la última persona que aplicó el trabajo

Entonces, si envía un parche a un proyecto y uno de los miembros principales aplica el parche, ambos obtendrán crédito.

Para ser más claro, en este caso, como comenta Olivier:

¡ --ignore-dateHace lo contrario de lo que estaba tratando de lograr !
Es decir, borra la marca de tiempo del autor y la reemplaza con las marcas de tiempo de commit.
Entonces, la respuesta correcta a mi pregunta es:
No haga nada, ya git rebase que en realidad no cambia las marcas de tiempo de los autores de forma predeterminada.


VonC
fuente
1
Interesante sobre las fechas arbitrarias para cometer. Sin embargo, git rebase --ignore-dateno funciona. Cambia las fechas de los commits rebaseados.
Olivier Verdier
@ Olivier: extraño: ¿hiciste un rebase no interactivo? Y entre la Fecha del Autor y la Fecha del Comprador, ¿estamos seguros de monitorear la fecha "correcta"?
VonC
1
Gracias, VonC, la diferencia entre la marca de tiempo del autor y el confirmador, eso es lo que aclara de repente. Escribí la respuesta a mi pregunta en mi publicación, pero siéntase libre de adaptar su respuesta para reflejar eso.
Olivier Verdier
44
para ser más precisos: ¡ --ignore-datehace lo contrario de lo que estaba tratando de lograr! Es decir, borra la marca de tiempo del autor y la reemplaza con las marcas de tiempo de commit. Entonces, la respuesta correcta a mi pregunta es: no haga nada, ya git rebaseque en realidad no cambia las marcas de tiempo de los autores de forma predeterminada.
Olivier Verdier
55
Tenga en cuenta que la --committer-date-is-author-dateopción parece dejar la marca de tiempo del autor y establecer la marca de tiempo del confirmador para que sea la misma que la marca de tiempo del autor original, que es lo que Olivier quería ...
David Fraser
118

Si ya ha arruinado las fechas de confirmación (tal vez con una nueva versión) y desea restablecerlas a sus fechas de autor correspondientes, puede ejecutar:

git filter-branch --env-filter 'GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE; export GIT_COMMITTER_DATE'

Andy
fuente
1
Acabo de intentar esto, pero sin efecto. Tengo la salida folowing: WARNING: Ref 'refs/heads/master' is unchanged. Estoy usando git versión 1.7.9.5 en Linux (64 bits)
Markus N.
20
Me gustaría agregar otro enfoque si ya lo arruinaste pero no quieres recorrer todo el historial: de git rebase --committer-date-is-author-date <base_branch> esta manera, git restablecerá la fecha de confirmación solo para las confirmaciones aplicadas a <base_branch> (que probablemente sea el mismo nombre de rama que usaste cuando lo arruinaste).
speakman
La respuesta aceptada no funcionó en 2016, pero la respuesta de @ speakman sí.
Theodore R. Smith
2
La respuesta de @ speakman no funcionó en octubre de 2016, ¡pero Andy sí!
Amedee Van Gasse
2
Esto no funciona en Windows. Pude hacerlo funcionar con Windows Bash.
vaindil
33

Una pregunta crucial de Von C me ayudó a entender lo que está sucediendo: cuando cambias el nombre, cambia la marca de tiempo del cometer , pero no la marca de tiempo del autor , que de repente todo tiene sentido. Entonces mi pregunta en realidad no era lo suficientemente precisa.

La respuesta es que rebase en realidad no cambia las marcas de tiempo del autor (no es necesario que haga nada para eso), lo cual me conviene perfectamente.

Olivier Verdier
fuente
3
+1: tengo un alias git en su lugar ( coderwall.com/p/euwpig/a-better-git-log ) que aparentemente usa la marca de tiempo del confirmador, lo que me estaba confundiendo. Gitk y git log muestran la marca de tiempo del autor.
1615903
15

De forma predeterminada, git rebase establecerá la marca de tiempo del confirmador en el momento en que se crea la nueva confirmación, pero mantendrá intacta la marca de tiempo del autor. La mayoría de las veces, este es el comportamiento deseado, pero en algunos escenarios, tampoco queremos cambiar la marca de tiempo del cometedor. ¿Cómo podemos lograr eso? Bueno, aquí está el truco que suelo hacer.

Primero, asegúrese de que cada uno de los commits que está a punto de rebase tenga un mensaje de commit único y una marca de tiempo del autor (Aquí es donde el truco necesita mejoras, aunque actualmente satisface mis necesidades).

Antes de la modificación, registre la marca de tiempo del confirmador, la marca de tiempo del autor y el mensaje de confirmación de todas las confirmaciones que se volverán a crear en un archivo.

#NOTE: BASE is the commit where your rebase begins
git log --pretty='%ct %at %s' BASE..HEAD > hashlog

Luego, deje que tenga lugar el rebase real.

Finalmente, reemplazamos la marca de tiempo del confirmador actual con la registrada en el archivo si el mensaje de confirmación es el mismo usando git filter-branch.

 git filter-branch --env-filter '__date=$(__log=$(git log -1 --pretty="%at %s" $GIT_COMMIT); grep -m 1 "$__log" ../../hashlog | cut -d" " -f1); test -n "$__date" && export GIT_COMMITTER_DATE=$__date || cat'

Si algo sale mal, solo paga git reflogo todas las refs/original/referencias.

Además, puede hacer lo mismo que la marca de tiempo del autor.

Por ejemplo, si la marca de tiempo del autor de algunos commits está fuera de orden, y sin reorganizar estos commits, solo queremos que la marca de tiempo del autor se muestre en orden, entonces los siguientes comandos ayudarán.

git log --pretty='%at %s' COMMIT1..COMMIT2 > hashlog
join -1 1 -2 1 <(cat hashlog | cut -f 1 | sort -nr | awk '{ print NR" "$1 }') <(cat hashlog | awk '{ print NR" "$0 }') | cut -d" " -f2,4- > hashlog_
mv hashlog_ hashlog
git filter-branch --env-filter '__date=$(__log=$(git log -1 --pretty="%s" $GIT_COMMIT); grep -m 1 "$__log" ../../hashlog | cut -d" " -f1); test -n "$__date" && export GIT_AUTHOR_DATE=$__date || cat'
weynhamz
fuente
Este es un gran truco! Me permitió reescribir solo 75 commits en lugar de 1100+ usando las otras respuestas.
Audun
¡Esto es fantástico! ¿Hay alguna forma de modificar el script para preservar también el committer original?
David DeMar
@DavidDeMar debería ser lo mismo, solo cambie el registro git --pretty para grabar el correo electrónico original y modifique el script en consecuencia.
weynhamz