¿Cómo puedo deshacer la última confirmación en un repositorio desnudo de git?

91

Teniendo en cuenta que hay varios comandos de git que no tienen sentido en un repositorio simple (porque los repositorios simples no usan índices y no tienen un directorio de trabajo),

git reset --hard HEAD^ 

no es una solución para eliminar el último cambio en dicho repositorio.

Buscando a través de Internet, todo lo que pude encontrar relacionado con el tema es esto , en el que se me presentan tres formas de hacerlo:
1. "actualizar la referencia manualmente (lo que implica plomería)";
2. " git push -fdesde un repositorio no simple";
3. " git branch -f this $that".

¿Qué solución crees que es más apropiada o qué otras formas existen para hacerlo? Desafortunadamente, la documentación que encontré sobre los repositorios desnudos de git es bastante pobre.

Lavinia-Gabriela Dobrovolschi
fuente
8
@ Lavinia-Garbriela Dobrovol No use las cosas complicadas a continuación. Estás intentando mover HEAD a una confirmación diferente y para eso está destinado git reset, incluso en un repositorio simple. Según mi respuesta a continuación, use: git reset --soft <commit> Con --soft, no intenta cambiar un árbol de trabajo y un índice que no existe, por lo que git le permite hacer el reinicio sin problemas.
Hazok

Respuestas:

130

Puede utilizar el git update-refcomando. Para eliminar la última confirmación, usaría:

$ git update-ref HEAD HEAD^

O si no está en la rama de la que no puede eliminar la última confirmación:

$ git update-ref refs/heads/branch-name branch-name^

También puede pasar un sha1 si lo desea:

$ git update-ref refs/heads/branch-name a12d48e2

Consulte la documentación del comando git-update-ref .

Sylvain Defresne
fuente
@ Lavinia-Gabriela Dobrovolschi: cierto, no estaba familiarizado con la sintaxis exacta.
VonC
@VonC Puede especificar <ref> in git update-ref <ref> <newvalue>para que sea la rama derecha, como "refs / heads / master" en lugar de HEAD, por ejemplo. Espero no haber entendido mal su pregunta.
Lavinia-Gabriela Dobrovolschi
@Sylvain: +1 buena edición. @ Lavinia-Gabriela Dobrovolschi gracias por las precisiones. Eso es mucho más práctico (supongo que si tiene acceso directo al servidor remoto).
VonC
3
Los ejemplos son engañosos con respecto al branch-nameargumento. Cuando se utiliza update-refcon una "rama", es absolutamente necesario especificar el nombre de referencia completo de la rama (es decir, anteponer refs/heads/al nombre corto normal de la rama). Si solo usa el nombre corto, terminará creando / actualizando en $GIT_DIR/branch-namelugar de $GIT_DIR/refs/heads/branch-name. La existencia de ambos branch-namey refs/heads/branch-nameprovocará advertencias "refname ... is ambiguous".
Chris Johnsen
Esta respuesta es mucho más complicada que la que propuso Zach. Y su solución funciona bien.
Krystian
32

Si usa lo siguiente en un repositorio vacío:

git reset --soft <commit>

entonces no se encuentra con los problemas que tiene al usar --hardy las --mixedopciones en un repositorio simple ya que no está tratando de cambiar algo que el repositorio simple no tiene (es decir, árbol de trabajo e índice). En su caso, específicamente, querrá usar (desde el repositorio desnudo):

git reset --soft HEAD^

Para cambiar de sucursal en el repositorio remoto, haga lo siguiente:

git symbolic-ref HEAD refs/heads/<branch_name>

Para ver el uso actual de la rama seleccionada:

git symbolic-ref HEAD

https://mirrors.edge.kernel.org/pub/software/scm/git/docs/git-symbolic-ref.html

Hazok
fuente
3
¿Cómo seleccionas la rama que quieres mover? Su ejemplo funciona bien en el maestro pero git checkout other_branchno funciona de forma desnuda.
Gauthier
1
Hmmm ... Me pregunto quién me votó en contra de esto. La pregunta no preguntaba cómo cambiar de sucursal en un repositorio remoto, sino cómo restablecer en un repositorio vacío. Para cambiar la rama predeterminada en un repositorio remoto, use git symbolic-ref HEAD refs / heads / <branch_name>.
Hazok
7

El git push -fdebería funcionar bien:
si clona ese repositorio desnudo, elimine la última confirmación ( git reset --hard HEAD^como menciona, pero en un repositorio local no desnudo) y presione hacia atrás ( -f):

  • no cambia ningún SHA1 para las otras confirmaciones anteriores a la que eliminó.
  • está seguro de que retrocede el contenido exacto del repositorio sin el compromiso adicional (porque lo acaba de clonar primero).
VonC
fuente
@ VonC Hola Von, te he visto responder mucho en Git, así que quería preguntarte ... Tenía curiosidad, ¿por qué, git reset --soft <sha1>como se muestra en mi respuesta a continuación, no sería la práctica recomendada para mover HEAD en un repositorio simple?
Hazok
Supongo que otra razón por la que pregunto es que el uso de un reinicio por software para repositorios simples no es información que esté disponible fácilmente y muchos foros parecen tener soluciones innecesariamente complejas cuando parece que el reinicio por software es la mejor práctica debido a la menor cantidad de escritura y la menor oportunidad. por error.
Hazok
2
@Zach: a reset --softdebería funcionar cuando se hace directamente en un repositorio vacío. Sospecho que esto rara vez se hace porque un repositorio simple es generalmente un repositorio ascendente (es decir, un repositorio al que está enviando datos) y, la mayoría de las veces, no tiene acceso local directo a él. Pero si lo hace, entonces este es ciertamente otro buen ejemplo de reset --softuso " " (como en stackoverflow.com/questions/5203535/… ) Entonces , haga +1 a su respuesta.
VonC
2

También puede usar la notación git refspec y hacer algo como esto:

git push -f origin +<commit you want to revert to>:<destination_head | branch_name>

Esto fuerza la actualización de la rama de destino (como se indica en la referencia) a la confirmación de origen como se indica en la +<object ref>parte.

alup
fuente
2
excepto cuando hay una ACL en la rama, que suele ser el caso si "necesita hacerlo en el repositorio desnudo" ...
David Schmitt