origin - git reset soft
En un lenguaje sencillo, ¿qué hace "git reset"? (7)
He visto publicaciones interesantes que explican las sutilezas sobre el git reset
.
Desafortunadamente, cuanto más leo sobre esto, más parece que no lo entiendo completamente. Vengo de un fondo SVN y Git es un paradigma completamente nuevo. Me puse mercurial fácilmente, pero Git es mucho más técnico.
Creo que git reset
está cerca de hg revert
, pero parece que hay diferencias.
Entonces, ¿qué hace exactamente git reset
? Por favor incluya explicaciones detalladas sobre:
- las opciones
--hard
,--soft
y--merge
; - la notación extraña que usas con
HEAD
comoHEAD^
yHEAD~1
; - Casos de uso concretos y flujos de trabajo;
- consecuencias sobre la copia de trabajo, la
HEAD
y su nivel de estrés global.
Cuando confirma algo para git, primero tiene que escalonar (agregar al índice) sus cambios. Esto significa que tiene que agregar todos los archivos que desea incluir en esta confirmación antes de que git los considere parte de la confirmación. Primero veamos la imagen de un repositorio git:
por lo tanto, es simple ahora. Tenemos que trabajar en el directorio de trabajo, creando archivos, directorios y todo. Estos cambios son cambios sin seguimiento. Para que sean rastreados, necesitamos agregarlos al índice de git usando el comando git add . Una vez que se agregan al índice git. Ahora podemos confirmar estos cambios, si queremos enviarlo al repositorio git.
Pero, de repente, nos dimos cuenta de que tenemos un archivo adicional que no agregamos en el índice y no es necesario para ingresar en el repositorio git. Significa que no queremos ese archivo en el índice. Ahora la pregunta es cómo eliminar ese archivo del índice de git. Ya que usamos git add para ponerlos en el índice, sería lógico usar git rm ? ¡Incorrecto! git rm simplemente eliminará el archivo y agregará la eliminación al índice. Entonces, ¿qué hacer ahora?
Utilizar:-
reinicio de git
Borra su índice, deja su directorio de trabajo intacto. (Simplemente deshaciéndolo todo).
Se puede usar con varias opciones. Hay tres opciones principales para usar con git reset: --hard, --soft y --mixed . Esto afecta a lo que se restablece, además del puntero HEAD, cuando se reinicia.
Primero, --hard lo reinicia todo. Su directorio actual sería exactamente como lo haría si hubiera estado siguiendo esa rama todo el tiempo. El directorio de trabajo y el índice se cambian a ese compromiso. Esta es la versión que uso más a menudo. git reset - hard es algo así como svn revertir .
A continuación, todo lo contrario, —soft , no restablece el árbol de trabajo ni el índice. Solo mueve el puntero HEAD. Esto deja a su estado actual con cualquier cambio diferente al compromiso al que está cambiando en su lugar en el directorio, y "preparado" para el compromiso. Si realiza una confirmación localmente pero no la ha enviado al servidor git, puede restablecer la confirmación anterior y volver a comprometerse con un buen mensaje de confirmación.
Finalmente, --mixed restablece el índice, pero no el árbol de trabajo. Por lo tanto, todos los cambios aún están allí, pero están "sin etapas" y deberían ser agregados o comprometidos . usamos esto a veces si cometimos más de lo que queríamos con git commit -a, podemos retroceder el commit con git reset --mixed, agregar las cosas que queremos comprometer y simplemente confirmarlas.
Diferencia entre git revert y git reset : -
En palabras sencillas, git reset es un comando para "corregir errores no confirmados" y git revert es un comando para "corregir errores cometidos" .
Significa que si hemos cometido algún error en algún cambio y nos hemos comprometido y enviado lo mismo a git repo, entonces git revert es la solución. Y si en el caso de que hayamos identificado el mismo error antes de presionar / cometer, podemos usar git reset para solucionar el problema.
Espero que te ayude a deshacerte de tu confusión.
En general, la función de git reset
es tomar la rama actual y restablecerla para que apunte a otro lugar, y posiblemente traer el índice y el árbol de trabajo. Más concretamente, si su rama maestra (actualmente verificada) es así:
- A - B - C (HEAD, master)
y te das cuenta de que quieres que el maestro apunte a B, no a C, 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 un pago y envío. Si corrieras git checkout B
, obtendrías esto:
- A - B (HEAD) - C (master)
Has terminado en un estado HEAD separado. HEAD
, árbol de trabajo, indexe todas las coincidencias B
, pero la rama maestra se dejó 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, solo actualiza una rama (que es un puntero a una confirmación) para apuntar a una confirmación diferente. El resto son solo detalles de lo que sucede con su índice y árbol de trabajo.
Casos de uso
Cubro muchos de los casos de uso principales para el git reset
dentro de mis descripciones de las diversas opciones en la siguiente sección. Realmente puede ser utilizado para una amplia variedad de cosas; El hilo común es que todos ellos implican el restablecimiento de la rama, el índice y / o el árbol de trabajo para apuntar / coincidir con un compromiso dado.
Cosas a tener cuidado
--hard
puede hacer que realmente pierdas el trabajo. Modifica tu árbol de trabajo.git reset [options] commit
puede hacer que (más o menos) pierdas confirmaciones. En el ejemplo de juguete anterior, perdimos commitC
Todavía está en el repositorio, y puedes encontrarlo viendogit reflog show HEAD
ogit reflog show master
, pero en realidad ya no es accesible desde ninguna rama.Git elimina de forma permanente dichos compromisos después de 30 días, pero hasta entonces puede recuperar C al apuntar una rama nuevamente (
git checkout C; git branch <new branch name>
).
Argumentos
Parafraseando la página del manual, el uso más común es de la forma 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 toma como HEAD (la confirmación actual). Este es un patrón común en todos los comandos de git (por ejemplo, checkout, diff, log, aunque la semántica exacta varía), por lo que no debería ser demasiado sorprendente.
Por ejemplo, git reset other-branch path/to/foo
restablece todo en ruta / to / foo a su estado en otra rama, git reset -- .
restablece el directorio actual a su estado en HEAD, y un simple 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 e índice durante el restablecimiento.
Recuerde, el índice es el "área de preparación" de git, es donde van las cosas cuando dice git add
como preparación para el compromiso.
--hard
hace que todo coincida con el compromiso que has restablecido. Esto es lo más fácil de entender, probablemente. Todos tus cambios locales son aplastados. Un uso principal es eliminar tu trabajo, pero no cambiar las confirmaciones:git reset --hard
significagit reset --hard HEAD
, es decir, no cambies la rama perogit reset --hard HEAD
todos los cambios locales. El otro es simplemente mover una rama de un lugar a otro y mantener sincronizado el árbol de índice / trabajo. Este es el que realmente puede hacer que pierdas trabajo, porque modifica tu árbol de trabajo. Asegúrese de que desea deshacerse del trabajo local antes de ejecutar cualquierreset --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 el compromiso original y el que reinicie se mostrará como modificaciones locales (o archivos sin seguimiento) con el estado de git. Use esto cuando se dé cuenta de que realizó algunos compromisos incorrectos, pero desea conservar todo el trabajo que ha realizado para poder solucionarlo y volver a comprometerse. Para confirmar, deberás agregar archivos al índice nuevamente (git add ...
).--soft
no toca el índice o 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, se verificaron en preparación para la confirmación). Use esto cuando se dé cuenta de que ha cometido algunos errores, pero el trabajo es bueno, todo lo que necesita hacer es volver a comprometerse de manera diferente. El índice está intacto, por lo que puede cometer inmediatamente si lo desea: el cometer resultante tendrá todo el mismo contenido donde estaba antes de reiniciar.--merge
se agregó recientemente y está destinado a ayudarlo a abortar una combinación fallida. Esto es necesario porquegit merge
realmente le permitirá intentar una combinación con un árbol de trabajo sucio (uno con modificaciones locales) siempre que esas modificaciones se encuentren en archivos no afectados por la combinación.git reset --merge
restablece el índice (como--mixed
- todos los cambios se muestran como modificaciones locales), y restablece los archivos afectados por la combinación, pero deja a los demás solos. Esperemos que esto restaure todo a cómo estaba antes de la fusión defectuosa. Por lo general, logit reset --merge
comogit reset --merge
(lo que significagit reset --merge HEAD
) porque solo deseas restablecer la fusión, no mover la rama. (HEAD
no se ha actualizado todavía, ya que la fusión falló)Para ser más concreto, suponga que ha modificado los archivos A y B, e intenta fusionar en una rama que modificó los archivos C y D. La combinación falla por alguna razón, y decide abortarla. Utiliza
git reset --merge
. Hace que C y D vuelvan a ser como enHEAD
, pero deja las modificaciones a A y B solo, ya que no formaron parte de la fusión intentada.
¿Quiere saber más?
Creo que man git reset
es realmente bueno para esto, tal vez necesitas un poco de la forma en que Git funciona para que realmente se hundan. En particular, si se toma el tiempo de leerlos cuidadosamente, esas tablas que detallan los estados de los archivos en el índice y el árbol de trabajo para todas las diversas opciones y casos son muy útiles. (Pero sí, son muy densos, están transmitiendo una gran cantidad de la información anterior en 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 comillas, sin tener que usar un nombre de hash como 3ebe3f6
. Está completamente documentado en la sección "Especificación de revisiones" de la página de manual de git-rev-parse, con muchos ejemplos y sintaxis relacionada. El caret y la tilde en realidad significan cosas diferentes :
-
HEAD~
es la abreviatura deHEAD~1
y significa el primer padre del commit.HEAD~2
significa el primer padre del primer padre del commit. Piense enHEAD~n
como "n confirmado antes de HEAD" o "el antecesor de la enésima generación de HEAD". -
HEAD^
(oHEAD^1
) también significa el primer padre del commit.HEAD^2
significa el segundo padre del commit. Recuerde, una confirmación de fusión normal tiene dos padres: el primer padre es la confirmación combinada y el segundo principal es la confirmación que se fusionó. En general, las fusiones pueden tener arbitrariamente muchos padres (fusiones de pulpos). - Los operadores
^
y~
pueden unirse, como enHEAD~3^2
, el segundo padre del antepasado de la tercera generación deHEAD
,HEAD^^2
, el segundo padre del primer padre deHEAD
, o inclusoHEAD^^^
, que es equivalente aHEAD~3
.
La comprobación apunta la cabeza a un compromiso específico.
Restablecer apunta una rama a una confirmación específica. (Una rama es un puntero a un commit).
Incidentalmente, si su cabeza no apunta a un compromiso al que también apunta una rama, entonces tiene una cabeza separada.
EDITAR : la reclamación final es incorrecta. Ver comentarios... :)
La publicación Reset Demystified en el blog Pro Git ofrece una explicación muy sencilla sobre el git reset
git checkout
y la git checkout
.
Después de toda la discusión útil en la parte superior de esa publicación, el autor reduce las reglas a los siguientes tres pasos simples:
Eso es básicamente eso. El comando de
reset
sobrescribe estos tres árboles en un orden específico, deteniéndose cuando usted lo indica.
- Mueva los puntos HEAD de la rama a (detener si
--soft
)- ENTONCES, haga que el Índice se vea así (deténgase aquí a menos que
--hard
)- ENTONCES, hacer que el Directorio de Trabajo se vea así.
También hay opciones de
--keep
y--keep
, pero preferiría mantener las cosas más simples por ahora, eso será para otro artículo.
Recuerda que en git
tienes:
- el puntero
HEAD
, que le indica en qué compromiso está 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 "cambia" las etapas para que luego puedan comprometerse juntas
Por favor incluya explicaciones detalladas sobre:
--hard
,--soft
y--merge
;
En orden creciente de peligrosidad:
-
--soft
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 su área de preparación y árbol de trabajo al nuevoHEAD
, desechando todo.
casos de uso concretos y flujos de trabajo;
- Use
--soft
cuando quiera pasar a otro commit y parche 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.
-
Use
--mixed
(que es el valor predeterminado) cuando quiera ver cómo se ven las cosas en otro commit, pero no quiere perder ningún cambio que ya tenga.Use
--merge
cuando quiera moverse a un nuevo lugar pero incorpore los cambios que ya tiene en ese árbol de trabajo.Utilice
--hard
para borrar todo y comenzar una nueva pizarra en el nuevo commit.
TL; DR
git reset
restablece Staging a la última confirmación. Utilice--hard
para restablecer también los archivos en su directorio de trabajo hasta la última confirmación.
VERSIÓN MÁS LARGA
Pero eso es obviamente simplista, de ahí las muchas respuestas bastante verbosas. Tenía más sentido para mí leer sobre git reset
en el contexto de deshacer cambios. Por ejemplo, ver esto:
Si git revertir es una forma “segura” de deshacer los cambios, puede pensar que git reset es el método peligroso. Cuando se deshace con git reset (y las confirmaciones ya no son referenciadas por ningún ref o el reflog), no hay manera de recuperar la copia original, es un deshacer permanente. Se debe tener cuidado al usar 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 compromiso, restablecer es una forma de mover la punta de una rama a un compromiso diferente. Esto se puede utilizar para eliminar confirmaciones de la rama actual.
Desde https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/commit-level-operations
Tenga en cuenta que esta es una explicación simplificada que pretende ser un primer paso para comprender esta compleja funcionalidad.
Puede ser útil para los aprendices visuales que desean visualizar el estado de su proyecto después de cada uno de estos comandos:
Para aquellos que usan Terminal con el color activado (git config --global color.ui auto):
git reset --soft A
y verás las cosas de B y C en verde (preparadas y listas para comprometerse)
git reset --mixed A
(o git reset A
) y verás las cosas de B y C en rojo (sin etapas y listas para ser preparadas (verde) y luego confirmadas)
git reset --hard A
y ya no verás los cambios de B y C en ningún lugar (será como si nunca hubieran existido)
O para aquellos que usan un programa GUI como ''Tower'' o ''SourceTree''
git reset --soft A
y verás las cosas de B y C en el área de ''archivos organizados'' listos para cometer
git reset --mixed A
(o git reset A
) y verás las cosas de B y C en el área de ''archivos sin etapas'' listas para ser movidas a las etapas y luego confirmadas
git reset --hard A
y ya no verás los cambios de B y C en ningún lugar (será como si nunca hubieran existido)