¿Hay alguna manera de recuperar una confirmación que se omitió accidentalmente durante una reorganización?

8 minutos de lectura

avatar de usuario
Miltón

Cuando sucede que un compromiso útil se omite accidentalmente durante una operación de rebase, ¿hay alguna esperanza de que Git mantenga una referencia que pueda volver a aplicarse?

Fue una reorganización no interactiva con muchos archivos binarios en la que pasé demasiado tiempo en un estado de ánimo de gatillo feliz usando git rebase --skippor lo que no hubo ningún mensaje de error, solo una actitud pésima.

Esto parece un escenario de recuperación de bloqueo del disco duro, pero en lugar de perseguir inodos fantasmas, debería haber una forma de filtrar los objetos de árbol perdidos dentro .git/objects y recuperarlos con vida.

  • ¿Qué quiere decir con una confirmación que se “omitió” accidentalmente durante una reorganización? ¿Está diciendo que dejó la confirmación fuera de la lista de editores durante una reorganización interactiva, o que la reorganización simplemente no aplicó la confirmación como se suponía que debía hacerlo? ¿Cuál fue el comando que usaste? ¿Cuáles fueron los mensajes de error?

    usuario456814

    08/08/2014 a las 21:57

  • @Cupcake, fue una reorganización no interactiva con muchos archivos binarios en los que pasé demasiado tiempo con un estado de ánimo feliz usando git rebase --skippor lo que no hubo ningún mensaje de error, solo una actitud pésima.

    – Milton

    8 de agosto de 2014 a las 22:04

cuando corres git rebase (interactivo o no), git básicamente hace una serie de cherry-pick operaciones para copiar su cadena de confirmación original en una nueva cadena. usemos o para las confirmaciones originales y dibujar el fragmento de gráfico de confirmación para la rama branch saliendo de la rama main:

        o1 - o2 - o3 - o4   <-- branch
      /
..- * - x                   <-- main

Ahora puedes correr git rebase para copiar todo lo viejo o se compromete a nuevos n se compromete, pero basado en xla punta de mainen lugar de basarse *, el antiguo punto base de fusión. Para que se parezca aún más a lo que sucedió, dejemos “accidentalmente” uno:

        o1 - o2 - o3 - o4   <-- ???
      /
..- * - x                   <-- main
          \
            n1 - n3 - n4    <-- branch

los ??? la etiqueta anterior representa la referencia de git (nombre de la rama, nombre de la etiqueta o cualquier otra etiqueta adecuada) que apunta o apunta a la confirmación o4. Todos tus antiguos compromisos todavía están ahí. siempre y cuando haya un nombre que los señale. Si hay no nombre, todavía se quedan hasta git gc los limpia (pero no quieres que eso suceda, así que no corras git gc 🙂 ).

La pregunta importante, entonces, es: “¿qué nombre o nombres podemos usar (y git) para encontrar o4?” Resulta que hay al menos dos:

  • uno o más en un “reflog”, y
  • uno deletreado ORIG_HEAD.

los ORIG_HEAD uno es el más fácil de usar, pero ese nombre también lo usan otros comandos (git mergepor ejemplo) así que tienes que ver si sigue siendo correcto:

$ git log ORIG_HEAD

Si eso le da la cadena correcta, dése un nombre más permanente que apunte a cometer o4. Este puede ser el nombre de una rama (por lo tanto, “resucita” la rama anterior con un nombre nuevo), o un nombre de etiqueta, o de hecho cualquier otro nombre, pero la rama y la etiqueta son los más fáciles:

$ git branch zombie ORIG_HEAD

(Tu no tener para hacer esto, y a medida que se sienta más cómodo con git, puede omitir este paso, pero probablemente sea bueno hacerlo hasta entonces).


Y si ORIG_HEAD ha sido golpeado (por ejemplo, por otra reorganización, o fusión, o lo que sea)? Bueno, entonces están los reflogs.

Hay un reflog para HEAD, y por defecto, otro reflog para cada branch-name. En este caso el que se usaría sería el reflog para branch:

$ git reflog branch
$ git log -g branch

pero solo puedes usar git reflog para mostrar el uno para HEAD (este es más ruidoso, por lo que mirando el solo por branch podría ser mejor):

$ git reflog
$ git log -g

En algún lugar de toda esa salida, debería poder encontrar commit o4. Es posible que encuentre muchas otras confirmaciones que se parezcan a o4por eso git log -g puede ser útil ya que le permitirá encontrar el verdadero (o correcto) o4.

En cualquier caso, suponiendo que finalmente encuentre un “nombre relativo” de estilo reflog (como [email protected]{1} o [email protected]{yesterday}), puede encontrar el SHA-1 sin procesar, o usar ese nombre relativo, para resucitar una vez más la versión zombie de branch:

$ git branch zombie [email protected]{yesterday}

o:

$ git branch zombie feedd0gf00d

o lo que sea.


Todo esto hace es darle un nombre, zombie, donde había tres signos de interrogación en el dibujo del gráfico. Todavía tiene que usar eso para encontrar el compromiso descartado, en este caso compromiso o2. Puede encontrarlo por SHA-1 sin procesar (leyendo git log) y vuelva a establecer la base y extraiga ese, o selecciónelo para agregar una copia a n4o lo que sea.

Si todo lo que quieres hacer es establecer branch volver a comprometerse o4incluso puedes prescindir por completo de la rama de zombis y simplemente hacer un git reset --hard mientras estaba en la rama branch:

$ git checkout branch           # if needed
$ git reset --hard feedd0gf00d

o:

$ git reset --hard ORIG_HEAD

Tenga en cuenta que la cosa después reset --hard es solo ninguna compromiso-ID. los --hard hace reset elimine su árbol de trabajo y reemplácelo con el compromiso de destino, mientras que el reset La acción en sí le dice a git: “haz que la rama actual apunte al ID de confirmación que estoy a punto de darte, independientemente de la confirmación de la sugerencia de rama que nombre en este momento”.

En otras palabras, después de su git rebase termina y descubres que te quedaste fuera o2 al hacer el n1 - n3 - n4 cadena, si inmediatamente git reset --hard ORIG_HEADgit cambia esto:1

        o1 - o2 - o3 - o4   <-- ORIG_HEAD
      /
..- * - x                   <-- main
          \
            n1 - n3 - n4    <-- HEAD=branch

a esto:

        o1 - o2 - o3 - o4   <-- ORIG_HEAD, HEAD=branch
      /
..- * - x                   <-- main
          \
            n1 - n3 - n4    [abandoned]

los [abandoned] cadena de n commits todavía está en el repositorio, por supuesto: hay un nombre que apunta a n4 en los reflogs!

(Las entradas de reflog eventualmente caducan, de forma predeterminada, después de 30 a 90 días, según los detalles que aún no sean interesantes, y una vez que caducan y no hay nombre por el cual encontrar n4 o o4 o lo que sea, luego git gc los limpiará y eliminará.)


1Tenga en cuenta que he añadido el HEAD= notación a este gráfico, para indicar en qué rama se encuentra. Esta HEAD= stuff es en realidad una muy buena aproximación a cómo git realiza un seguimiento de la rama en la que te encuentras. En el .git directorio, hay un archivo llamado HEAD¡y ese archivo simplemente contiene el nombre de la rama actual!2 Si escribe un nuevo nombre en el archivo, git cambia su idea de en qué rama se encuentra (sin cambiar nada más). eso es exactamente lo que git reset --soft hace: escribir un nuevo nombre en HEAD. (Utilizando --mixed agrega un poco más de acción: git reset luego actualiza el índice/área de ensayo; y usando --hard agrega aún más: git reset luego borra los contenidos del directorio de trabajo, reemplazándolos con lo que hayas puesto en el HEAD expediente.)

2En el modo “HEAD separado”, el archivo contiene el SHA-1 sin procesar del actual cometeren lugar del nombre del actual rama. Esa, de hecho, es la verdadera diferencia entre estar “en una rama” y estar en modo “CABEZA separada”. Cuando git quiere saber cuál es el compromiso actual esmira el archivo HEAD. Si tiene un SHA-1 sin procesar, esa es la respuesta. Si tiene un nombre de rama, git lee el nombre de la rama para obtener el SHA-1 sin formato. Esas son las únicas dos configuraciones permitidas; nada más debe estar en el HEAD expediente.

lo hace git reflog ¿trabajo para ti? Creo que todavía debería estar en el recolector de basura a menos que ejecutaras git gc

  • No hay un “recolector de basura” donde se almacenan las confirmaciones pendientes hasta que se recolectan como basura.

    usuario456814

    08/08/2014 a las 21:56

  • @perlsufi, tal vez un breve recibo sobre cómo usar este comando para rastrear y revivir la confirmación omitida. No tengo muchas pistas sobre cómo podría ser útil en ese procedimiento y esos purge Las palabras en la página de manual me están asustando.

    – Milton

    8 de agosto de 2014 a las 22:09

  • Después de volver a registrar, obtenga el SHA1 que desea, luego git merge that commit

    – AgileDan

    08/08/2014 a las 22:20

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad