En términos sencillos, ¿qué hace "git reset"?
He visto publicaciones interesantes que explican sutilezas sobre git reset
.
Desafortunadamente, cuanto más leo sobre esto, más parece que no lo entiendo completamente. Vengo de una experiencia en SVN y Git es un paradigma completamente nuevo. Me volví voluble fácilmente, pero Git es mucho más técnico.
Creo git reset
que está cerca hg revert
, pero parece que hay diferencias.
Entonces, ¿qué hace exactamente git reset
? Incluya explicaciones detalladas sobre:
- las opciones
--hard
,--soft
y--merge
; - la extraña notación que usas con
HEAD
tales comoHEAD^
yHEAD~1
; - casos de uso concretos y flujos de trabajo;
- consecuencias en la copia de trabajo, el
HEAD
y su nivel de estrés global.
En general, git reset
la función de es tomar la rama actual y restablecerla para que apunte a otro lugar, y posiblemente traer consigo el índice y el árbol de trabajo. Más concretamente, si su rama maestra (actualmente desprotegida) es así:
- A - B - C (HEAD, master)
y te das cuenta de que quieres que master apunte a B, no a C, usarás git reset B
para moverlo allí:
- A - B (HEAD, master) # - C is still here, but there's no branch pointing to it anymore
Digresión: Esto es diferente de una compra. Si ejecutaras git checkout B
, obtendrías esto:
- A - B (HEAD) - C (master)
Has terminado en un estado HEAD desapegado. HEAD
, árbol de trabajo, índice todo coincide B
, pero la rama maestra quedó atrás en C
. Si realiza una nueva confirmación D
en este punto, obtendrá esto, que probablemente no sea lo que desea:
- A - B - C (master)
\
D (HEAD)
Recuerde, restablecer no realiza confirmaciones, simplemente actualiza una rama (que es un puntero a una confirmación) para que apunte a una confirmación diferente. El resto son sólo detalles de lo que sucede con su índice y árbol de trabajo.
Casos de uso
Cubro muchos de los principales casos de uso en git reset
mis descripciones de las distintas opciones en la siguiente sección. Realmente se puede utilizar para una amplia variedad de cosas; el hilo común es que todos ellos implican restablecer la rama, el índice y/o el árbol de trabajo para que apunte o coincida con una confirmación determinada.
Cosas con las que tener cuidado
--hard
puede hacer que usted realmente pierda el trabajo. Modifica tu árbol de trabajo.git reset [options] commit
puede hacer que (en cierto modo) pierda confirmaciones. En el ejemplo del juguete anterior, perdimos el compromisoC
. Todavía está en el repositorio y puede encontrarlo mirandogit reflog show HEAD
ogit reflog show master
, pero ya no se puede acceder a él desde ninguna sucursal.Git elimina permanentemente dichas confirmaciones después de 30 días, pero hasta entonces puedes recuperar C apuntando una rama hacia él nuevamente (
git checkout C; git branch <new branch name>
).
Argumentos
Parafraseando la página de manual, el uso más común es el del formulario git reset [<commit>] [paths...]
, que restablecerá las rutas dadas a su estado desde la confirmación dada. Si no se proporcionan las rutas, se restablece todo el árbol y, si no se proporciona la confirmación, se considera HEAD (la confirmación actual). Este es un patrón común en los comandos de git (por ejemplo, checkout, diff, log, aunque la semántica exacta varía), por lo que no debería sorprender demasiado.
Por ejemplo, git reset other-branch path/to/foo
restablece todo en la ruta/a/foo a su estado en otra rama, git reset -- .
restablece el directorio actual a su estado en HEAD y simplemente git reset
restablece todo a su estado en HEAD.
El árbol de trabajo principal y las opciones de índice.
Hay cuatro opciones principales para controlar lo que sucede con su árbol de trabajo y su índice durante el reinicio.
Recuerde, el índice es el "área de preparación" de git: es donde van las cosas cuando dice prepararse git add
para comprometerse.
--hard
hace que todo coincida con el compromiso que has restablecido. Probablemente esto sea lo más fácil de entender. Todos sus cambios locales se ven afectados. Un uso principal es eliminar su trabajo pero no cambiar las confirmaciones:git reset --hard
significagit reset --hard HEAD
, es decir, no cambiar la rama pero deshacerse de todos los cambios locales. El otro es simplemente mover una rama de un lugar a otro y mantener sincronizado el índice/árbol de trabajo. Este es el que realmente te puede hacer perder trabajo, porque modifica tu árbol de trabajo. Asegúrese de desechar el trabajo local antes de ejecutar cualquierareset --hard
.--mixed
es el valor predeterminado, es decir,git reset
significagit reset --mixed
. Restablece el índice, pero no el árbol de trabajo. Esto significa que todos sus archivos están intactos, pero cualquier diferencia entre la confirmación original y la que restableció se mostrará como modificaciones locales (o archivos sin seguimiento) con estado git. Utilízalo cuando te des cuenta de que has realizado algunas confirmaciones incorrectas, pero quieres conservar todo el trabajo que has realizado para poder arreglarlo y volver a confirmarlo. Para confirmar, deberá agregar archivos al índice nuevamente (git add ...
).--soft
no toca el índice ni el árbol de trabajo. Todos sus archivos están intactos como con--mixed
, pero todos los cambios se muestran comochanges to be committed
con el estado de git (es decir, registrados en preparación para la confirmación). Use esto cuando se dé cuenta de que ha realizado algunas confirmaciones incorrectas, pero todo el trabajo está bien; todo lo que necesita hacer es volver a confirmarlo de manera diferente. El índice no se modifica, por lo que puede confirmar inmediatamente si lo desea; la confirmación resultante tendrá el mismo contenido que tenía antes de restablecer.--merge
se agregó recientemente y está destinado a ayudarlo a cancelar una combinación fallida. Esto es necesario porquegit merge
en realidad le permitirá intentar una fusión con un árbol de trabajo sucio (uno con modificaciones locales) siempre que esas modificaciones estén en archivos que no se vean afectados por la fusión.git reset --merge
restablece el índice (como--mixed
: todos los cambios se muestran como modificaciones locales) y restablece los archivos afectados por la fusión, pero deja los demás en paz. Con suerte, esto restaurará todo a como estaba antes de la mala fusión. Generalmente lo usarás comogit reset --merge
(es decirgit reset --merge HEAD
) porque solo deseas restablecer la fusión, no mover la rama. (HEAD
aún no se ha actualizado porque falló la fusión)Para ser más concreto, supongamos que ha modificado los archivos A y B e intenta fusionarlos en una rama que modificó los archivos C y D. La fusión falla por algún motivo y decide abortarla. Tu usas
git reset --merge
. Devuelve C y D a cómo estaban enHEAD
, pero deja las modificaciones a A y B solas, ya que no formaban parte del intento de fusión.
¿Quiere saber más?
Creo que man git reset
es bastante bueno para esto; aunque tal vez necesites un poco de idea de cómo funciona git para que realmente lo entiendas. En particular, si se toma el tiempo para leerlas detenidamente, esas tablas que detallan los estados de los archivos en el índice y el árbol de trabajo para las distintas opciones y casos son muy, muy útiles. (Pero sí, son muy densos: transmiten una gran cantidad de la información anterior de una forma muy concisa).
Notación extraña
La "notación extraña" ( HEAD^
y HEAD~1
) que mencionas es simplemente una abreviatura para especificar confirmaciones, sin tener que usar un nombre hash como 3ebe3f6
. Está completamente documentado en la sección "especificar revisiones" de la página de manual de git-rev-parse, con muchos ejemplos y sintaxis relacionada. El signo de intercalación y la tilde en realidad significan cosas diferentes :
HEAD~
es la abreviatura deHEAD~1
y significa el primer padre de la confirmación.HEAD~2
significa el primer padre del compromiso. Piense enHEAD~n
"n confirmaciones antes de HEAD" o "el ancestro de enésima generación de HEAD".HEAD^
(oHEAD^1
) también significa el primer padre de la confirmación.HEAD^2
significa el segundo padre de la confirmación. Recuerde, una confirmación de fusión normal tiene dos padres: el primer padre es la confirmación fusionada y el segundo padre es la confirmación que se fusionó. En general, las fusiones pueden tener muchos padres arbitrariamente (fusiones de pulpo).- Los operadores
^
y~
se pueden encadenar, como enHEAD~3^2
, el segundo padre del antepasado de tercera generación deHEAD
,HEAD^^2
, el segundo padre del primer padre deHEAD
, o inclusoHEAD^^^
, lo que equivale aHEAD~3
.
Recuerda que en git
tienes:
- el
HEAD
puntero , que te indica en qué compromiso estás trabajando - el árbol de trabajo , que representa el estado de los archivos en su sistema
- el área de preparación (también llamada índice ), que "etapa" los cambios para que luego puedan confirmarse juntos
Incluya explicaciones detalladas sobre:
--hard
,--soft
y--merge
;
En orden creciente de peligrosidad:
--soft
se mueveHEAD
pero no toca el área de preparación ni el árbol de trabajo.--mixed
mueveHEAD
y actualiza el área de preparación, pero no el árbol de trabajo.--merge
mueveHEAD
, restablece el área de preparación e intenta mover todos los cambios en su árbol de trabajo al nuevo árbol de trabajo.--hard
MueveHEAD
y ajusta tu área de preparación y árbol de trabajo al nuevoHEAD
, tirando todo a la basura.
casos de uso y flujos de trabajo concretos;
- Úselo
--soft
cuando desee pasar a otra confirmación y arreglar las cosas sin "perder su lugar". Es bastante raro que necesites esto.
--
# git reset --soft example
touch foo // Add a file, make some changes.
git add foo //
git commit -m "bad commit message" // Commit... D'oh, that was a mistake!
git reset --soft HEAD^ // Go back one commit and fix things.
git commit -m "good commit" // There, now it's right.
--
Úselo
--mixed
(que es el valor predeterminado) cuando quiera ver cómo se ven las cosas en otra confirmación, pero no quiera perder ningún cambio que ya tenga.Úselo
--merge
cuando desee mudarse a un nuevo lugar pero incorpore los cambios que ya tiene en el árbol de trabajo.Úselo
--hard
para borrar todo y comenzar de nuevo en la nueva confirmación.
La publicación Reset Demystified en el blog Pro Git ofrece una explicación muy obvia sobre git reset
y git checkout
.
Después de toda la útil discusión al principio de esa publicación, el autor reduce las reglas a los siguientes tres simples pasos:
Básicamente eso es todo. El
reset
comando sobrescribe estos tres árboles en un orden específico y se detiene cuando usted se lo indica.
- Mueva cualquier rama a la que apunte HEAD (deténgase si
--soft
)- ENTONCES, haga que el Índice se vea así (deténgase aquí a menos que
--hard
)- ENTONCES, haga que el Directorio de Trabajo se vea así
--merge
También hay--keep
opciones, pero prefiero mantener las cosas más simples por ahora; eso será para otro artículo.
TL;DR
git reset
restablece Staging a la última confirmación. Úselo--hard
para restablecer también los archivos en su directorio de trabajo a la última confirmación.
VERSIÓN MÁS LARGA
Pero eso es obviamente simplista, de ahí las muchas respuestas bastante detalladas. Para mí tenía más sentido leer git reset
en el contexto de deshacer cambios. Por ejemplo, vea esto:
Si git revert es una forma "segura" de deshacer cambios, puedes pensar en git reset como un método peligroso. Cuando deshaces con git reset (y ninguna referencia o reflog ya no hace referencia a las confirmaciones), no hay forma de recuperar la copia original; es una deshacer permanente. Se debe tener cuidado al utilizar esta herramienta, ya que es uno de los únicos comandos de Git que tiene el potencial de perder su trabajo.
Desde https://www.atlassian.com/git/tutorials/undoing-changes/git-reset
y esto
En el nivel de confirmación, restablecer es una forma de mover la punta de una rama a una confirmación diferente. Esto se puede utilizar para eliminar confirmaciones de la rama actual.
From https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/commit-level-operations