Primero, aclaremos qué es HEAD y qué significa cuando se separa.
HEAD es el nombre simbólico para el commit actualmente desprotegido. Cuando HEAD no está separado (la situación "normal" 1 : tiene una rama desprotegida), HEAD señala la "referencia" de una rama y la rama apunta a la confirmación. HEAD está "unido" a una rama. Cuando realiza una nueva confirmación, la rama a la que apunta HEAD se actualiza para que apunte a la nueva confirmación. HEAD sigue automáticamente ya que solo apunta a la rama.
git symbolic-ref HEAD
rendimientos refs/heads/master
La rama llamada "maestro" está desprotegida.
git rev-parse refs/heads/master
ceder 17a02998078923f2d62811326d130de991d1a95a
Ese compromiso es la punta actual o "cabeza" de la rama maestra.
git rev-parse HEAD
también rinde 17a02998078923f2d62811326d130de991d1a95a
Esto es lo que significa ser una "referencia simbólica". Apunta a un objeto a través de alguna otra referencia.
(Las referencias simbólicas se implementaron originalmente como enlaces simbólicos, pero luego se cambiaron a archivos simples con interpretación adicional para que pudieran usarse en plataformas que no tienen enlaces simbólicos).
Tenemos HEAD
→ refs/heads/master
→17a02998078923f2d62811326d130de991d1a95a
Cuando HEAD está separado, apunta directamente a un commit, en lugar de señalar indirectamente a uno a través de una rama. Puedes pensar en un HEAD separado como si estuviera en una rama sin nombre.
git symbolic-ref HEAD
falla con fatal: ref HEAD is not a symbolic ref
git rev-parse HEAD
cede 17a02998078923f2d62811326d130de991d1a95a
Dado que no es una referencia simbólica, debe apuntar directamente al compromiso en sí.
Tenemos HEAD
→17a02998078923f2d62811326d130de991d1a95a
Lo importante para recordar con un HEAD separado es que si el commit al que apunta no tiene referencia (ninguna otra referencia puede alcanzarlo), entonces se convertirá en "colgante" cuando compruebe algún otro commit. Eventualmente, tales confirmaciones pendientes se podarán a través del proceso de recolección de basura (de forma predeterminada, se conservan durante al menos 2 semanas y pueden mantenerse más tiempo al hacer referencia al reflog de HEAD).
1
Está perfectamente bien hacer un trabajo "normal" con una CABEZA separada, solo tiene que hacer un seguimiento de lo que está haciendo para evitar tener que sacar el historial eliminado del registro.
Los pasos intermedios de un rebase interactivo se realizan con un HEAD separado (parcialmente para evitar contaminar el reflog de la rama activa). Si finaliza la operación de rebase completa, actualizará su rama original con el resultado acumulativo de la operación de rebase y volverá a conectar HEAD a la rama original. Supongo que nunca completó completamente el proceso de rebase; esto lo dejará con un HEAD separado que apunte al commit que fue procesado más recientemente por la operación de rebase.
Para recuperarse de su situación, debe crear una rama que apunte al compromiso al que apunta su HEAD separado:
git branch temp
git checkout temp
(estos dos comandos se pueden abreviar como git checkout -b temp
)
Esto volverá a conectar su CABEZA a la nueva temp
rama.
A continuación, debe comparar la confirmación actual (y su historial) con la rama normal en la que esperaba trabajar:
git log --graph --decorate --pretty=oneline --abbrev-commit master origin/master temp
git diff master temp
git diff origin/master temp
(Probablemente quiera experimentar con las opciones de registro: agregar -p
, dejar de --pretty=…
ver todo el mensaje de registro, etc.)
Si su nueva temp
sucursal se ve bien, es posible que desee actualizar (por ejemplo) master
para señalarla:
git branch -f master temp
git checkout master
(estos dos comandos se pueden abreviar como git checkout -B master temp
)
Luego puede eliminar la rama temporal:
git branch -d temp
Finalmente, probablemente querrás impulsar la historia restablecida:
git push origin master
Es posible que deba agregar --force
al final de este comando para presionar si la rama remota no se puede "reenviar rápidamente" a la nueva confirmación (es decir, eliminó o reescribió alguna confirmación existente, o de lo contrario reescribió un poco de historial).
Si estaba en medio de una operación de rebase, probablemente debería limpiarlo. Puede verificar si se estaba procesando un cambio de base buscando el directorio .git/rebase-merge/
. Puede limpiar manualmente el rebase en progreso simplemente borrando ese directorio (por ejemplo, si ya no recuerda el propósito y el contexto de la operación de rebase activa). Por lo general, lo usaría git rebase --abort
, pero eso hace un restablecimiento adicional que probablemente desee evitar (mueve HEAD de nuevo a la rama original y lo restablece a la confirmación original, lo que deshacerá parte del trabajo que hicimos anteriormente).