En inglés simple, ¿qué hace "git reset"?

674

He visto publicaciones interesantes que explican sutilezas sobre git reset.

Desafortunadamente, cuanto más lo leo, más parece que no lo entiendo completamente. Vengo de un fondo SVN y Git es un paradigma completamente nuevo. Obtuve mercurial fácilmente, pero Git es mucho más técnico.

Creo que git resetestá cerca hg revert, pero parece que hay diferencias.

Entonces, ¿qué hace exactamente git reset? Incluya explicaciones detalladas sobre:

  • las opciones --hard, --softy --merge;
  • la notación extraña que usa con HEADtales como HEAD^y HEAD~1;
  • casos de uso concretos y flujos de trabajo;
  • consecuencias en la copia de trabajo, el HEADy su nivel de estrés global.
e-satis
fuente
17
Creo que una referencia visual de Git da una buena idea de lo que sucede cuando se utilizan comandos comunes de git.

Respuestas:

992

En general, git resetla función 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 desprotegida) es así:

- A - B - C (HEAD, master)

y te das cuenta de que quieres que el maestro apunte a B, no a C, lo usarás git reset Bpara 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. Si corrieras git checkout B, obtendrías esto:

- A - B (HEAD) - C (master)

Has terminado en un estado HEAD separado. HEAD, árbol de trabajo, indexar todas las coincidencias B, pero la rama maestra se quedó atrás en C. Si realiza una nueva confirmación Den este momento, obtendrá esto, que probablemente no sea lo que desea:

- A - B - C (master)
       \
        D (HEAD)

Recuerde, reset 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 principales casos de uso git resetdentro de mis descripciones de las diversas opciones en la siguiente sección. Realmente se puede usar 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 apuntar / coincidir con una confirmación determinada.

Cosas a tener en cuenta

  • --hardpuede hacer que realmente pierdas el trabajo. Modifica tu árbol de trabajo.

  • git reset [options] commitpuede hacer que (tipo de) pierda confirmaciones. En el ejemplo de juguete anterior, perdimos el compromiso C. Todavía está en el repositorio, y puede encontrarlo mirando git reflog show HEADo git reflog show master, pero ya no es accesible desde ninguna rama.

  • Git elimina permanentemente tales confirmaciones después de 30 días, pero hasta entonces puede recuperar C al señalar una rama nuevamente ( git checkout C; git branch <new branch name>).

Argumentos

Parafraseando la página de manual, el uso más común es el 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 ser demasiado sorprendente.

Por ejemplo, git reset other-branch path/to/foorestablece todo en path / to / foo a su estado en otra rama, git reset -- .restablece el directorio actual a su estado en HEAD, y un simple git resetrestablece todo a su estado en HEAD.

El árbol de trabajo principal y las opciones de índice

Hay cuatro opciones principales para controlar lo que le sucede a su árbol de trabajo e índice durante el reinicio.

Recuerde, el índice es el "área de preparación" de git: es donde van las cosas cuando usted dice git adden preparación para comprometerse.

  • --hardhace que todo coincida con la confirmación a la que ha restablecido. Este es el más fácil de entender, probablemente. Todos sus cambios locales se ven afectados. Un uso principal es volar su trabajo pero no cambiar commits: git reset --hardsignifica git reset --hard HEAD, es decir, no cambie la rama sino que elimine 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 hacerte perder el trabajo, porque modifica tu árbol de trabajo. Asegúrese de que quiere tirar el trabajo local antes de ejecutarlo reset --hard.

  • --mixedes el valor predeterminado, es decir, git resetsignifica git 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 commit original y el que restablece se mostrará como modificaciones locales (o archivos sin seguimiento) con el estado de git. Úselo cuando se dé cuenta de que realizó algunas confirmaciones incorrectas, pero desea conservar todo el trabajo que ha realizado para poder arreglarlo y volver a comprometerse. Para confirmar, tendrá que agregar archivos al índice nuevamente ( git add ...).

  • --softno toca el índice ni el árbol de trabajo. Todos sus archivos están intactos como con --mixed, pero todos los cambios se muestran como changes to be committedcon el estado de git (es decir, registrado 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 no se ha modificado, por lo que puede confirmar de inmediato si lo desea: la confirmación resultante tendrá el mismo contenido que antes de restablecer.

  • --mergese agregó recientemente y está destinado a ayudarlo a cancelar una fusión fallida. Esto es necesario porque en git mergerealidad 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 --mergerestablece el índice (como --mixed: todos los cambios se muestran como modificaciones locales) y restablece los archivos afectados por la fusión, pero deja a los demás solos. Con suerte, esto restaurará todo a como estaba antes de la mala fusión. Por lo general, lo usará como git reset --merge(significado git reset --merge HEAD) porque solo desea restablecer la fusión, no mover la rama. ( HEADno 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 fusionarse en una rama que modificó los archivos C y D. La fusión falla por alguna razón, y decide cancelarla. Utiliza git reset --merge. Devuelve a C y D a su estado original HEAD, pero deja solo sus modificaciones en A y B, ya que no formaron parte del intento de fusión.

¿Quiere saber más?

Creo que man git resetes realmente bastante bueno para esto, quizás necesites un poco de la forma en que funciona git 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 opciones y casos son muy útiles. (Pero sí, son muy densos: transmiten 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 usted menciona es simplemente una abreviatura para especificar confirmaciones, sin tener que usar un nombre hash como 3ebe3f6. Está completamente documentado en la sección "especificando revisiones" de la página de manual para git-rev-parse, con muchos ejemplos y sintaxis relacionada. El caret y la tilde en realidad significan cosas diferentes :

  • HEAD~es la abreviatura de HEAD~1y significa el primer padre del commit. HEAD~2significa el primer padre del primer padre del commit. Piense HEAD~nen "n commits before HEAD" o "el ancestro de la enésima generación de HEAD".
  • HEAD^(o HEAD^1) también significa el primer padre del commit. HEAD^2significa el segundo padre del commit . Recuerde, un commit de fusión normal tiene dos padres: el primer padre es el commit de fusión y el segundo padre es el commit que se fusionó. En general, las fusiones pueden tener arbitrariamente muchos padres (fusiones de pulpos).
  • Los ^y ~los operadores pueden ser ensartados, como en HEAD~3^2, la segunda matriz de la ancestro tercera generación de HEAD, HEAD^^2, la segunda matriz de la primera matriz de HEAD, o incluso HEAD^^^, lo que es equivalente a HEAD~3.

caret y tilde

Cascabel
fuente
"Usarás git reset para moverlo allí". ¿por qué no usas git checkout para hacerlo?
e-satis
55
@ e-satis: git checkout moverá HEAD, pero dejará la rama donde estaba. Esto es para cuando quieres mover la rama.
Cascabel
Entonces, si entiendo bien, restablecer B haría: - A - B - C - B (maestro) mientras que la comprobación B haría - A - B (maestro).
e-satis
32
los documentos son buenos a pesar de que lleva mucho tiempo leerlos y son muy densos y lleva mucho tiempo verificar que dicen que funciona como si ya supieras cómo funciona. No parece que los documentos sean buenos para mí ...
Kirby
44
Esta respuesta SO da una explicación mucho más simple y comprensible: stackoverflow.com/questions/3528245/whats-the-difference-between-git-reset-mixed-soft-and-hard
Nitin Bansal
80

Recuerda que en gitti tienes:

  • el HEADpuntero , que te dice en qué compromiso estás trabajando
  • el árbol de trabajo , que representa el estado de los archivos en su sistema
  • el área de ensayo (también llamada índice ), que "cambia" las etapas para que luego puedan confirmarse juntas

Incluya explicaciones detalladas sobre:

--hard, --softY --merge;

En orden creciente de peligrosidad:

  • --softse mueve HEADpero no toca el área de preparación o el árbol de trabajo.
  • --mixedmueve HEADy actualiza el área de preparación, pero no el árbol de trabajo.
  • --mergese mueve HEAD, restablece el área de preparación e intenta mover todos los cambios en su árbol de trabajo al nuevo árbol de trabajo.
  • --hardmueve HEAD y ajusta tu área de preparación y árbol de trabajo al nuevo HEAD, tirando todo.

casos de uso y flujos de trabajo concretos;

  • Úselo --softcuando quiera pasar a otro commit 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.

-

  • Use --mixed(que es el valor predeterminado) cuando desea ver cómo se ven las cosas en otra confirmación, pero no desea perder los cambios que ya tiene.

  • Úselo --mergecuando desee moverse a un nuevo lugar, pero incorpore los cambios que ya tiene en ese árbol de trabajo.

  • Úselo --hardpara borrar todo y comenzar una nueva lista en el nuevo commit.

John Feminella
fuente
2
Ese no es el caso de uso previsto reset --merge. No realiza una fusión de tres vías. Realmente es solo para restablecer las fusiones en conflicto, como se describe en los documentos. Querrás usar checkout --mergepara hacer lo que estás hablando. Si también quieres mover la rama, creo que la única forma es hacer un seguimiento con un checkout / reset para arrastrarla.
Cascabel
@Jefromi »Sí, no lo dije muy bien. Por "un nuevo lugar" quise decir "un lugar fresco donde no tienes la fusión en conflicto".
John Feminella
1
Ah, ya veo. Creo que lo importante aquí es que, a menos que realmente sepas lo que estás haciendo, probablemente nunca quieras usarlo reset --mergecon ningún objetivo además (el predeterminado) HEAD, porque en casos además de abortar una fusión en conflicto, se tirará información que de otro modo podría guardar.
Cascabel
2
Encontré esta respuesta la más simple y útil
Jazzepi
Agregue información sobre estos comandos: git resety git reset -- ..
Flimm
35

La publicación Reset Demystified en el blog Pro Git da una explicación muy obvia sobre git resety git checkout.

Después de toda la útil discusión en la parte superior de esa publicación, el autor reduce las reglas a los siguientes tres simples pasos:

Eso es básicamente eso. El resetcomando sobrescribe estos tres árboles en un orden específico, deteniéndose cuando se lo indique.

  1. Mueva cualquier rama a la que apunte HEAD (pare si --soft)
  2. ENTONCES, haga que el índice se vea así (pare aquí a menos que --hard)
  3. ENTONCES, haga que el Directorio de trabajo se vea así

También hay --mergey --keepopciones, pero preferiría mantener las cosas simples por ahora - que serán para otro artículo.

Daniel Hershcovich
fuente
25

Cuando comprometes algo con git, primero tienes que organizar (agregar al índice) tus cambios. Esto significa que debe agregar todos los archivos que desea incluir en esta confirmación antes de que git los considere parte de la confirmación. Primero echemos un vistazo a la imagen de un repositorio de git: ingrese la descripción de la imagen aquí

entonces, es simple ahora. Tenemos que trabajar en el directorio de trabajo, creando archivos, directorios y todo. Estos cambios son cambios sin seguimiento. Para hacer un seguimiento de ellos, necesitamos agregarlos al índice git usando el comando git add . Una vez que se agregan al índice git. Ahora podemos confirmar estos cambios, si queremos llevarlo al repositorio de git.

Pero de repente nos enteramos al confirmar que tenemos un archivo adicional que agregamos en el índice que no es necesario para insertar en el repositorio de git. Significa que no queremos ese archivo en el índice. Ahora la pregunta es cómo eliminar ese archivo del índice 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:-

git reset

Borra su índice, deja intacto su directorio de trabajo. (simplemente desestimando todo).

Se puede usar con varias opciones con él. Hay tres opciones principales para usar con git reset: --hard, --soft y --mixed . Estos afectan lo que se restablece, además del puntero HEAD cuando se restablece.

Primero, --hard restablece todo. Su directorio actual sería exactamente como si hubiera estado siguiendo esa rama todo el tiempo. El directorio de trabajo y el índice se cambian a esa confirmación. Esta es la versión que uso con más frecuencia. git reset --hard es algo así como svn revert .

A continuación, todo lo contrario, —soft , no restablece el árbol de trabajo ni el índice. Solo mueve el puntero HEAD. Esto deja su estado actual con cualquier cambio diferente a la confirmación a la que está cambiando en su directorio y "puesta en escena" para la confirmación. Si realiza una confirmación localmente pero no ha enviado la confirmación 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 todavía están ahí, pero son "no organizados" y tendrían que ser git add'ed o git commit -a . usamos esto a veces si nos comprometimos más de lo que pretendíamos con git commit -a, podemos retroceder el commit con git reset --mixed, agregar las cosas que queremos comprometer y simplemente cometerlas.

Diferencia entre git revert y git reset : -


En palabras simples, git reset es un comando para "corregir errores no confirmados" y git revert es un comando para "corregir errores comprometidos" .

Significa que si cometimos algún error en algún cambio y nos comprometimos y presionamos lo mismo para git repo, entonces git revert es la solución. Y si en caso de que hayamos identificado el mismo error antes de presionar / confirmar, podemos usar git reset para solucionar el problema.

Espero que te ayude a deshacerte de tu confusión.

amor
fuente
2
Esta es una buena respuesta en inglés como lo solicitó OP.
Episodex
1
Aunque podría extrañar eso en tu respuesta. ¿Qué es git reset HEADpor defecto? --hard, --softO --mixed? Gran respuesta por cierto.
giannis christofakis
1
Gran respuesta, pero dejaría en claro que git reset --hardhará que pierdas algunos datos. Y hay un punto que podría estar equivocado (aunque no estoy 100% seguro ... ¡Todavía estoy aprendiendo!): Hablando de --mixedusted dice que "usamos esto a veces si nos comprometimos más de lo que pretendíamos con git commit -a". Quiso decir: "si escenificamos más de lo que significan para con git stage ."? Si realmente lo cometiste, creo que es demasiado tarde (como dices al final, git reset es un comando para "corregir errores no confirmados")
Fabio dice Reinstate Monica el
6

TL; DR

git resetrestablece la puesta en escena a la última confirmación. Utilícelo --hardtambién para restablecer 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. Tenía más sentido para mí seguir leyendo git reseten el contexto de deshacer cambios. Por ejemplo, mira esto:

Si git revert es una forma "segura" de deshacer cambios, puede pensar en git reset como el método peligroso. Cuando deshace con git reset (y las confirmaciones ya no son referenciadas por ninguna referencia o por el registro de referencias), no hay forma de recuperar la copia original, es una eliminación 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 confirmación, restablecer es una forma de mover la punta de una rama a una confirmación diferente. Esto se puede usar para eliminar confirmaciones de la rama actual.

Desde https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/commit-level-operations

Snowcrash
fuente
2

Tenga en cuenta que esta es una explicación simplificada que pretende ser un primer paso para tratar de comprender esta compleja funcionalidad.

Puede ser útil para los alumnos 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 (puesta en escena y lista para comprometerse)

git reset --mixed A(o git reset A) y verá las cosas de B y C en rojo (sin escena y listas para ser puestas en escena (verde) y luego confirmadas)

git reset --hard A y ya no verá los cambios de B y C en ningún lado (será como si nunca hubieran existido)


O para aquellos que usan un programa GUI como 'Tower' o 'SourceTree'

git reset --soft A y verá las cosas de B y C en el área de 'archivos en escena' listas para comprometerse

git reset --mixed A(o git reset A) y verá las cosas de B y C en el área de 'archivos no en escena' listas para ser movidas a puesta en escena y luego confirmadas

git reset --hard A y ya no verá los cambios de B y C en ningún lado (será como si nunca hubieran existido)

timhc22
fuente
1

Checkout señala la cabeza a un commit específico.

Restablecer señala una rama en una confirmación específica. (Una rama es un puntero a una confirmación).

Por cierto, si su cabeza no apunta a una confirmación a la que también apunta una rama, entonces tiene una cabeza separada. (resultó estar equivocado. Ver comentarios ...)

Ian Warburton
fuente
1
No es una trampa, pero (sí, de hecho es una trampa, pero vamos a agregarlo para completar) su tercera oración es técnicamente falsa. Digamos que su HEAD está apuntando a la rama B, que a su vez apunta a commit abc123. Si ahora realiza el pago, commit abc123, su HEAD y la rama B apuntan a commit abc123 Y su HEAD está desconectada. Comprometerse en este punto no actualizará la posición de la rama B. Podrías haber dicho "si tu cabeza no apunta a una rama, entonces tienes una cabeza separada"
RomainValeri
@RomainValeri ¿Qué hará el compromiso en esa circunstancia?
Ian Warburton el
1
La confirmación crearía confirmaciones a las que ninguna rama hace referencia, y la rama B seguiría apuntando a la misma confirmación abc123 incluso después de que se haya comprometido varias veces después de eso. Implica que estos commits se convertirían en candidatos para la recolección de basura cuando HEAD deja de apuntar al último commit en esta serie 'wild' de commits.
RomainValeri