2010-03-29 12 views
41

Si bien reset y checkout tienen diferentes usos la mayor parte del tiempo, no puedo ver la diferencia que hay entre estos dos.¿Hay alguna diferencia entre "git reset --hard hash" y "git checkout hash"?

Probablemente haya uno o nadie se haya molestado en agregar una opción --hard para hacer algo que el básico checkout puede hacer.

Quizás haya una diferencia en la forma en que verá la historia?

+1

Cubrí esto en una actualización de mi respuesta a una de sus preguntas anteriores - mire el arte ascii cerca de la parte superior, particularmente donde dice "Digression: ..." (por mucho que me gustaría más representante para volver a responder aquí) – Cascabel

+0

Creo que puede publicar su respuesta aquí y obtener un representante de ella. Si alguien busca este conocimiento en particular, no encontrará la otra publicación. Éste apunta a un tema muy específico, y merece tener su página separada. Por cierto, parece que eres mi mentor de Git :-) harigato, sensei! –

+1

Pero lo entiendo, la diferencia es que el restablecimiento mueve la bifurcación y no se paga. –

Respuesta

52

Esta respuesta se cita principalmente de mi respuesta a una pregunta anterior: git reset in plain english.

Los dos son muy diferentes. Resultan en el mismo estado para su índice y árbol de trabajo, pero el historial resultante y la rama actual no son lo mismo.

Suponga que su historia se parece a esto, con la rama principal comprobado actualmente fuera:

- A - B - C (HEAD, master) 

y se queda git reset --hard B. Usted obtendrá esto:

- A - B (HEAD, master)  # - C is still here, but there's no 
          # branch pointing to it anymore 

Se podría realmente conseguir ese efecto si se utiliza o --mixed--soft también - la única diferencia es lo que sucede a su árbol de trabajo y el índice. En el caso --hard, el árbol de trabajo y el índice coinciden con B.

Ahora supongamos que ejecuta git checkout B en su lugar. Obtendrá esto:

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

Terminó en un estado HEAD separado. HEAD, árbol de trabajo, indexar todas las coincidencias B, lo mismo que con el restablecimiento completo, pero la rama principal se quedó atrás en C. Si comete un nuevo commit D en este punto, que obtendrá esto, que probablemente no es lo que quiere:

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

Por lo tanto, se utiliza la caja para, así, echa un vistazo a que cometió. Puedes jugar con él, hacer lo que quieras, pero has dejado atrás tu rama. Si quiere que la rama se mueva también, usa restablecer.

+5

+1 como de costumbre. Este hilo (http://marc.info/?l=git&m=120955970704567&w=2) también agregó un efecto secundario: si estás en el medio de una fusión (por ejemplo, cuando hay conflictos de fusión, o después de 'git merge - -no-commit'), 'git reset --hard' olvida la fusión, pero' git checkout -f' no lo hace; por lo tanto, un 'git commit' después de que el último crearía un commit de fusión, que generalmente no es lo que quieres. – VonC

+0

@VonC: ¡y un punto adicional excelente para ti, como de costumbre! – Cascabel

14

Si la documentación provista con Git no lo ayuda, eche un vistazo a A Visual Git Reference por Mark Lodato.

En particular, si usted está comparando git checkout <non-branch> con git reset --hard <non-branch> (enlaces directos):

git checkout master~3 http://marklodato.github.com/visual-git-guide/checkout-detached.svg.png

git reset --hard master~3 http://marklodato.github.com/visual-git-guide/reset-commit.svg.png

Tenga en cuenta que en el caso de git reset --hard master~3 que dejas atrás una parte del DAG de revisiones - algunos de commits no son referenciados por ninguna rama. Esos están protegidos por (por defecto) 30 días por reflog; finalmente serían podados (eliminados).

6

git-reset hash establece la referencia de la rama con el hash dada, y opcionalmente comprueba hacia fuera, con --hard.

git-checkout hash establece el árbol de trabajo con el hash dado; y a menos que el hash sea un nombre de rama, terminarás con una cabeza separada.

en última instancia, ofertas de git con 3 cosas:

    working tree (your code) 
------------------------------------------------------------------------- 
        index/staging-area 
------------------------------------------------------------------------- 
     repository (bunch of commits, trees, branch names, etc) 

git-checkout por defecto simplemente actualiza el índice y el árbol de trabajo, y puede actualizar opcionalmente algo en el repositorio (con la opción -b)

git-reset por defecto simplemente actualiza el repositorio y el índice, y, opcionalmente, el árbol de trabajo (con la opción --hard)

Se puede pensar en el repositorio de esta manera:

HEAD -> master 

refs: 
    master -> sha_of_commit_X 
    dev -> sha_of_commit_Y 

objects: (addressed by sha1) 

    sha_of_commit_X, sha_of_commit_Y, sha_of_commit_Z, sha_of_commit_A .... 

git-reset manipula lo que las referencias rama apuntan.

Suponga que su historia es así:

  T--S--R--Q [master][dev] 
     /
    A--B--C--D--E--F--G [topic1] 
        \ 
        Z--Y--X--W [topic2][topic3] 

Tenga en cuenta que las ramas son sólo nombres que avanzan automáticamente cuando se cometió.

Así que tienen las siguientes ramas:

master -> Q 
dev -> Q 
topic1 -> G 
topic2 -> W 
topic3 -> W 

Y su rama actual es topic2, es decir, los puntos de la cabeza a los Tema2.

HEAD -> topic2 

Entonces, git reset X se restablecerá el nombre topic2 señalar a X; lo que significa que si hace un commit P en branch topic2, las cosas se verán así:

  T--S--R--Q [master][dev] 
     /
    A--B--C--D--E--F--G [topic1] 
        \ 
        Z--Y--X--W [topic3] 
          \ 
          P [topic2] 
Cuestiones relacionadas