¿Cómo puedo restablecer o revertir un archivo a una revisión específica?

4519

He realizado algunos cambios en un archivo que se ha confirmado varias veces como parte de un grupo de archivos, pero ahora quiero restablecer / revertir los cambios en él a una versión anterior.

He hecho un a git logjunto con un git diffpara encontrar la revisión que necesito, pero simplemente no tengo idea de cómo volver el archivo a su estado anterior en el pasado.

Odia_
fuente
11
Después de revertir, no se olvide --cachedal verificar git diff. enlace
Geoffrey Hale
55
Encontré tu pregunta cuando busqué en Google la mía. Pero después de leer la solución, verifiqué mi registro y descubrí que había realizado los cambios como un compromiso independiente, por lo que hice que git revertir para ese compromiso, y todo lo demás se mantuvo como lo quería. No es una solución, solo otra forma de hacerlo a veces.
sudo97

Respuestas:

6140

Asumir el hash de la confirmación que desea es c5f567:

git checkout c5f567 -- file1/to/restore file2/to/restore

La página del comando man git checkout brinda más información.

Si desea volver al commit antes c5f567, agregue ~1(donde 1 es el número de commits que desea volver, puede ser cualquier cosa):

git checkout c5f567~1 -- file1/to/restore file2/to/restore

Como nota al margen, siempre me he sentido incómodo con este comando porque se usa tanto para cosas comunes (cambio entre ramas) como para cosas inusuales y destructivas (descartando cambios en el directorio de trabajo).

Greg Hewgill
fuente
12
@shadowhand: ¿Hay alguna forma de revertir eso, entonces es la versión justo después?
aliteralmind
16
@aliteralmind: No, desafortunadamente, la notación de acceso directo del historial de Git solo retrocede en el historial.
Greg Hewgill
46
Si va a usar un nombre de sucursal para abcde (por ejemplo develop), querrá git checkout develop -- file/to/restore(tenga en cuenta el doble guión)
Ohad Schneider
8
@aliteralmind: En realidad, sí, hay una manera de hacerlo: "git log --reverse -1 --ancestry-path yourgitrev..master" y luego usa las opciones apropiadas para obtener la git rev. --ancestry-path "dibujará una línea" entre dos commits y -1 le mostrará solo una versión, y --reverse asegurará que la primera entrada emitida sea la más antigua.
Chris Cogdon
66
Personalmente, encuentro HEAD ^ más fácil de escribir que HEAD ~ 1 :)
juzzlin
606

Puede revisar rápidamente los cambios realizados en un archivo con el comando diff:

git diff <commit hash> <filename>

Luego, para revertir un archivo específico a ese commit, use el comando reset:

git reset <commit hash> <filename>

Es posible que deba usar la --hardopción si tiene modificaciones locales.

Un buen flujo de trabajo para administrar puntos de referencia es usar etiquetas para marcar puntos en su línea de tiempo. No puedo entender su última oración, pero lo que puede desear es divergir una rama de un punto anterior en el tiempo. Para hacer esto, use el útil comando de pago:

git checkout <commit hash>
git checkout -b <new branch name>

A continuación, puede volver a expresar eso en su línea principal cuando esté listo para fusionar esos cambios:

git checkout <my branch>
git rebase master
git checkout master
git merge <my branch>
Chris Lloyd
fuente
77
El comando 'git checkout <commit hash>' me ha devuelto mi versión anterior del proyecto exactamente esto para lo que estaba buscando Gracias Chris.
vidur punj
48
Para revertir el archivo git checkout <commit hash> <filename>funcionó mejor para mí quegit reset
Motti Strom
3
Quería una versión temprana de un solo archivo porque había sobrescrito 150 líneas con una copia / pegado mal elegida. git checkout <commit hash> <filename>trabajó para mi. Esta no debería ser la respuesta aceptada, en mi humilde opinión. git resetNo.
Harperville
24
no se puede usar git resetpara restablecer un solo archivo, obtendrá un errorfatal: Cannot do hard reset with paths
leve
13
Lo que dijo Slier: no puedes git reset --hard <commit hash> <filename>. Esto generará un error con fatal: Cannot do hard reset with paths.lo que Motti Strom dijo: usegit checkout <commit hash> <filename>
Hawkeye Parker
366

Puede usar cualquier referencia a un git commit, incluido el SHA-1 si es más conveniente. El punto es que el comando se ve así:

git checkout [commit-ref] -- [filename]

foxxtrot
fuente
22
¿Cuál es la diferencia entre esta respuesta, que tiene --, y la respuesta que no?
2rs2ts
80
En git, un '-' antes de la lista de archivos le dice a git que todos los siguientes argumentos deben interpretarse como nombres de archivo, no como nombres de rama o cualquier otra cosa. Es un desambigador útil a veces.
foxxtrot
49
El '-' no es solo una convención git, sino algo que se encuentra en varios lugares en la línea de comandos * nix. rm -- -f(eliminar un archivo llamado -f) parece ser el ejemplo canónico. Más detalles aquí
Hawkeye Parker,
77
Simplemente agregue a lo que dijo @HawkeyeParker, el rmcomando usa getopt (3) para analizar sus argumentos. getoptes el comando para analizar argumentos de comando. gnu.org/software/libc/manual/html_node/Getopt.html
Devy
2
@Honey Sí, eso es lo que quiero decir, y sí, probablemente no sea común en absoluto. He visto ese ejemplo en varios lugares, tal vez solo para hacerlo memorable: rm -f es conocido por ser aterrador / peligroso. Pero el punto es que en * nix un nombre de archivo puede comenzar con un '-', y esto confundirá a varios intérpretes de línea de comandos que, cuando ven un '-', esperan que siga una opción de comando. Podría ser cualquier archivo que comience con '-'; por ejemplo, "-mySpecialFile".
Hawkeye Parker
288
git checkout -- foo

Eso se restablecerá fooa HEAD. Tú también puedes:

git checkout HEAD^ foo

para una revisión de vuelta, etc.

Greg Hewgill
fuente
12
Sugeriría usar la sintaxis git checkout -- foopara evitar errores si foohay algo especial (como un directorio o un archivo llamado -f). Con git, si no está seguro, siempre prefije todos los archivos y directorios con el argumento especial --.
Mikko Rantalainen
8
Una nota adicional al comentario de Mikko: --no es un comando git y no es especial para git. Es un bash incorporado para indicar el final de las opciones de comando. Puede usarlo con muchos otros comandos bash también.
matthaeus
14
@matthaeus tampoco es específico de bash ni una función de shell. Es una convención implementada en muchos comandos diferentes (y compatible con getopt).
Greg Hewgill
2
No, no-- es una palabra especial incorporada en bash. Pero es una convención común compatible con muchos analizadores de línea de comandos y utilizada por muchas CLI, incluido git.
Emil Lundberg
123

Y para volver a la última versión confirmada, que se necesita con mayor frecuencia, puede usar este comando más simple.

git checkout HEAD file/to/restore
CDR
fuente
2
¿Cuál es la diferencia entre esto (git checkout HEAD file / to / restore) y git reset --hard file / to / restore ???
Motti Shneor
2
1) más fácil de recordar de manera más general 2) no se preocupe presionar Enter antes de ingresar el nombre del archivo
Roman Susi
105

Tuve el mismo problema justo ahora y encontré esta respuesta más fácil de entender ( commit-refes el valor SHA del cambio en el registro al que desea volver):

git checkout [commit-ref] [filename]

Esto colocará esa versión anterior en su directorio de trabajo y desde allí puede confirmarla si lo desea.

bbrown
fuente
91

Si sabe cuántas confirmaciones necesita volver, puede usar:

git checkout master~5 image.png

Esto supone que está en la masterrama, y ​​la versión que desea es 5 confirmaciones.

Ron DeVera
fuente
80

Creo que lo he encontrado ... de http://www-cs-students.stanford.edu/~blynn/gitmagic/ch02.html

A veces solo quieres regresar y olvidarte de cada cambio pasado un cierto punto porque todos están equivocados.

Empezar con:

$ git log

que le muestra una lista de confirmaciones recientes y sus hash SHA1.

A continuación, escriba:

$ git reset --hard SHA1_HASH

para restaurar el estado a una confirmación dada y borrar todas las confirmaciones más nuevas del registro de forma permanente.

jdee
fuente
24
Git nunca elimina nada. Sus antiguas confirmaciones todavía están allí, pero a menos que haya una punta de rama que las señale, ya no se podrá acceder a ellas. git reflog todavía los mostrará hasta que limpies tu repositorio con git-gc.
Bombe
1
@Bombe: Gracias por la información. Había revisado una versión antigua de un archivo. Después de leer su comentario, pude usar "gitref" para buscar el hash SHA1 parcial y usar "checkout" para volver a la versión más reciente. Otros usuarios de git pueden encontrar útil esta información.
Winston C. Yang
44
posiblemente seguido por ungit push --force
bshirley
44
Si tiene cambios no confirmados, los perderá si realiza un reinicio de git --hard
Boklucius
55
@Bombe: "Git nunca elimina nada. Tus antiguas confirmaciones todavía están allí, pero a menos que haya una punta de rama que las señale, ya no se podrá acceder a ellas". - pero las confirmaciones como esta se eliminan después de un tiempo establecido, por lo que "Git nunca elimina nada" no es cierto.
Bulwersator
61

Esto funcionó para mí:

git checkout <commit hash> file

Luego comete el cambio:

git commit -a
v2k
fuente
54

Tienes que tener cuidado cuando dices "retroceder". Si solía tener una versión de un archivo en commit $ A, y luego realizó dos cambios en dos commits separados $ B y $ C (entonces lo que está viendo es la tercera iteración del archivo), y si dice " Quiero volver al primero ", ¿lo dices en serio?

Si desea deshacerse de los cambios tanto en la segunda como en la tercera iteración, es muy simple:

$ git checkout $A file

y luego cometes el resultado. El comando pregunta "Quiero revisar el archivo del estado registrado por el commit $ A".

Por otro lado, lo que quiso decir es deshacerse del cambio que introdujo la segunda iteración (es decir, commit $ B), mientras mantiene lo que commit $ C hizo en el archivo, querrá revertir $ B

$ git revert $B

Tenga en cuenta que quien haya creado commit $ B puede no haber sido muy disciplinado y haber cometido un cambio totalmente no relacionado en el mismo commit, y esta reversión puede tocar archivos que no sean archivos que ve cambios ofensivos, por lo que es posible que desee verificar el resultado cuidadosamente después de hacerlo entonces.


fuente
Hice esto, pero luego un "archivo de registro git" diría que estaba en el commit original, HEAD. Parecía que "git checkout" estaba fallando. Sin embargo, un estado de git mostraba que el archivo realmente había cambiado y un "archivo con diferencias de git" mostraría los cambios reales. Además, un "estado de git" mostró que el archivo también cambió. Así que no use "git log" aquí para rastrear qué archivos cambiaron.
Frederick Ollinger
37

Divertidamente, git checkout foono funcionará si la copia de trabajo está en un directorio llamado foo; Sin embargo, ambos git checkout HEAD fooy git checkout ./foo:

$ pwd
/Users/aaron/Documents/work/foo
$ git checkout foo
D   foo
Already on "foo"
$ git checkout ./foo
$ git checkout HEAD foo
Aaron Maenpaa
fuente
26
ogit checkout -- foo
knittl
32

Así es como rebasefunciona:

git checkout <my branch>
git rebase master
git checkout master
git merge <my branch>

Asume que tienes

---o----o----o----o  master
    \---A----B       <my branch>

Los primeros dos comandos ... commit git checkout git rebase master

... verifique la rama de cambios que desea aplicar a la masterrama. El rebasecomando toma los commits de <my branch>(que no se encuentran en master) y los vuelve a aplicar al encabezado de master. En otras palabras, el padre de la primera confirmación <my branch>ya no es una confirmación previa en la masterhistoria, sino el jefe actual de master. Los dos comandos son los mismos que:

git rebase master <my branch>

Puede ser más fácil recordar este comando ya que tanto las ramas "base" como "modificar" son explícitas.

. El resultado final de la historia es:

---o----o----o----o   master
                   \----A'----B'  <my branch>

Los dos últimos comandos ...

git checkout master
git merge <my branch>

... haga una combinación de avance rápido para aplicar todos los <my branch>cambios master. Sin este paso, no se agrega la confirmación de rebase master. El resultado final es:

---o----o----o----o----A'----B'  master, <my branch>

mastery <my branch>ambos de referencia B'. Además, desde este punto es seguro eliminar la <my branch>referencia.

git branch -d <my branch>
cmcginty
fuente
26

A partir de git v2.23.0 hay un nuevo método de restauración de git que se supone que asume parte de lo que git checkoutfue responsable (incluso la respuesta aceptada menciona que git checkoutes bastante confuso). Vea los aspectos más destacados de los cambios en el blog de Github .

El comportamiento predeterminado de este comando es restaurar el estado de un árbol de trabajo con el contenido proveniente del sourceparámetro (que en su caso será un hash de confirmación).

Entonces, según la respuesta de Greg Hewgill (suponiendo que el hash de confirmación es c5f567), el comando se vería así:

git restore --source=c5f567 file1/to/restore file2/to/restore

O si desea restaurar el contenido de una confirmación antes de c5f567:

git restore --source=c5f567~1 file1/to/restore file2/to/restore
mjarosie
fuente
24

Primer reinicio del cabezal para el archivo de destino

git reset HEAD path_to_file

Segunda comprobación de ese archivo

git checkout -- path_to_file
Gulshan Maurya
fuente
44
+1, aunque no estoy seguro de la intención de reiniciar HEAD. Puede o no ser necesario. En mi situación, solo quería revertir un archivo en particular a la versión en el repositorio (que mantiene intactos los cambios locales restantes. Solo ejecutar el segundo paso anterior fue suficiente para mí
fkl
Sí, solo necesito ejecutar el segundo comando. Me gusta -> shellhacks.com/git-revert-file-to-previous-commit
javaPlease42
22

¡git-aliases, awk y funciones de shell al rescate!

git prevision <N> <filename>

donde <N>es el número de revisiones del archivo para revertir el archivo <filename>.
Por ejemplo, para pagar la revisión previa inmediata de un solo archivo x/y/z.c, ejecute

git prevision -1 x/y/z.c

¿Cómo funciona git prevision?

Agregue lo siguiente a su gitconfig

[alias]
        prevision = "!f() { git checkout `git log --oneline $2 |  awk -v commit="$1" 'FNR == -commit+1 {print $1}'` $2;} ;f"

El comando básicamente

  • realiza un git logen el archivo especificado y
  • selecciona el commit-id apropiado en el historial del archivo y
  • ejecuta git checkouta en el commit-id para el archivo especificado.

Esencialmente, todo lo que uno haría manualmente en esta situación,
envuelto en un hermoso y eficiente git-alias - git-prevision

TheCodeArtist
fuente
20

Tengo que conectar EasyGit aquí, que es un contenedor para hacer que git sea más accesible para los principiantes sin confundir a los usuarios experimentados. Una de las cosas que hace es darle más significadogit revert . En este caso, simplemente diría:

eg revert foo/bar foo/baz

Aristóteles Pagaltzis
fuente
1
Debería ser eg revert --in REVISON -- FILENAME. El --ines importante. Para los usuarios de Windows: Abra git bash. Ejecutar echo %PATH. La primera ruta debe estar en su directorio de usuarios que termine en bin. Crea ese camino. Almacenar, por ejemplo, allí. Nómbralo eg. No eg.txt.
koppor
20

En el caso de que desee revertir un archivo a una confirmación previa (y el archivo que desea revertir ya confirmado) puede usar

git checkout HEAD^1 path/to/file

o

git checkout HEAD~1 path/to/file

Luego, simplemente escenifique y confirme la "nueva" versión.

Armado con el conocimiento de que un commit puede tener dos padres en el caso de una fusión, debe saber que HEAD ^ 1 es el primer padre y HEAD ~ 1 es el segundo padre.

Cualquiera de las dos funcionará si solo hay un padre en el árbol.

Encantamientos modernos
fuente
19

Muchas sugerencias aquí, la mayoría en la línea de git checkout $revision -- $file. Un par de alternativas oscuras:

git show $revision:$file > $file

Y también, uso mucho esto solo para ver una versión particular temporalmente:

git show $revision:$file

o

git show $revision:$file | vim -R -

(OBS: $filedebe tener el prefijo ./si es una ruta relativa para git show $revision:$filetrabajar)

Y lo más extraño:

git archive $revision $file | tar -x0 > $file
Peter V. Mørch
fuente
1
Esta es una buena alternativa si no está seguro de qué versión de confirmación desea y necesita "echar un vistazo" sin sobrescribir su directorio de trabajo.
wisbucky
18

Tenga en cuenta, sin embargo, que git checkout ./fooy git checkout HEAD ./foo no son exactamente lo mismo; caso en punto:

$ echo A > foo
$ git add foo
$ git commit -m 'A' foo
Created commit a1f085f: A
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 foo
$ echo B >> foo
$ git add foo
$ echo C >> foo
$ cat foo
A
B
C
$ git checkout ./foo
$ cat foo
A
B
$ git checkout HEAD ./foo
$ cat foo
A

(La segunda addetapa el archivo en el índice, pero no se confirma).

Git checkout ./foosignifica revertir ruta ./foodesde el índice ; agregar le HEADindica a Git que revierta esa ruta en el índice a su HEADrevisión antes de hacerlo.

Damien Diederen
fuente
14

Para mí, ninguna de las respuestas parecía realmente clara y, por lo tanto, me gustaría agregar la mía, que parece muy fácil.

Tengo un commit abc1y luego he realizado varias (o una modificación) en un archivo file.txt.

Ahora digamos que eché a perder algo en el archivo file.txty quiero volver a una confirmación anterior abc1.

1 git checkout file.txt.: esto eliminará los cambios locales, si no los necesita

2 git checkout abc1 file.txt.: esto traerá su archivo a su versión deseada

3 git commit -m "Restored file.txt to version abc1".: esto confirmará tu reversión.

  1. git push : esto empujará todo en el repositorio remoto

Entre los pasos 2 y 3, por supuesto, puede hacer git statuspara comprender lo que está sucediendo. Por lo general, debería ver lo file.txtya agregado y es por eso que no hay necesidad de a git add.

desmond13
fuente
2
Bien, supongo que los pasos 1. y 2. son mutuamente excluyentes: si abc1 es su último compromiso, no hay necesidad de 2. y si hubo otros compromisos después de abc1, puede hacer directamente 2.
Jean Paul
13
  1. Git revierte el archivo a una confirmación específica
git checkout Last_Stable_commit_Number -- fileName

2.Git revierte el archivo a una rama específica

git checkout branchName_Which_Has_stable_Commit fileName
ireshika piyumalie
fuente
11

Para ir a una versión de confirmación anterior del archivo, obtenga el número de confirmación, diga eb917a1 y luego

git checkout eb917a1 YourFileName

Si solo necesita volver a la última versión comprometida

git reset HEAD YourFileName
git checkout YourFileName

Esto simplemente lo llevará al último estado confirmado del archivo

shah1988
fuente
10

git checkout ref | commitHash - filePath

p.ej

git checkout HEAD~5 -- foo.bar
or 
git checkout 048ee28 -- foo.bar
Amos Folarin
fuente
10

Muchas de las respuestas aquí dicen usar git reset ... <file>o, git checkout ... <file>pero al hacerlo, perderá todas las modificaciones en <file>commit después del commit que desea revertir.

Si desea revertir los cambios de un commit en un solo archivo, tal como git revertlo haría pero solo para un archivo (o digamos un subconjunto de los archivos de commit), sugiero usar ambos git diffy git applyasí (con <sha>= el hash del comprometerse desea revertir):

git diff <sha>^ <sha> path/to/file.ext | git apply -R

Básicamente, primero generará un parche correspondiente a los cambios que desea revertir, y luego aplicará el parche en reversa para eliminar esos cambios.

Por supuesto, no funcionará si las líneas revertidas han sido modificadas por alguna confirmación entre <sha1>y HEAD(conflicto).

Vince
fuente
Esa debería ser la respuesta aprobada. ¿Puedo sugerir una versión ligeramente simplificada:git show -p <sha> path/to/file.ext|git apply -R
Amaury D
puede usar en <sha>^!lugar de<sha>^ <sha>
desagradable
8

Use git logpara obtener la clave hash para una versión específica y luego usegit checkout <hashkey>

Nota: No olvides escribir el hash antes del último. El último hash señala tu posición actual (HEAD) y no cambia nada.

mustafakyr
fuente
7

Obviamente, alguien necesita escribir un libro inteligible sobre git o debe explicarse mejor en la documentación. Ante este mismo problema supuse que

cd <working copy>
git revert master

deshacería la última confirmación que parece hacer.

Ian

Ian Davis
fuente
7

Puedes hacerlo en 4 pasos:

  1. revierta todo el commit con el archivo que desea revertir específicamente: creará un nuevo commit en su rama
  2. reinicio suave que confirma: elimina la confirmación y mueve los cambios al área de trabajo
  3. seleccione manualmente los archivos para revertirlos y confirmarlos
  4. suelte todos los demás archivos en su área de trabajo

Lo que necesita escribir en su terminal :

  1. git revert <commit_hash>
  2. git reset HEAD~1
  3. git add <file_i_want_to_revert> && git commit -m 'reverting file'
  4. git checkout .

buena suerte

Nir M.
fuente
¿No revierte TODOS los cambios?
arcee123
1
@ arcee123 Sí, pero el reinicio posterior deshace la reversión de todos los cambios. El problema es que git-revertsolo opera en todo el repositorio, por lo que para compensar tenemos que deshacer todo lo demás.
Timothy
2
Recomiendo usar: 1. git revert --no-commit <commit_hash>2. git reset HEADEsto ahorra una confirmación adicional flotando y realiza todos los cambios solo en su directorio de trabajo.
Timothy
La respuesta de @ greg-hewgill es mejor y acertada. Este es pésimo y no debe usarse.
Daniel Tranca
Esto es exactamente lo que se necesita para una verdadera reversión de archivos específicos. Necesitaba deshacer los cambios en algunos archivos de una confirmación anterior que ya se había enviado al repositorio remoto. Revertí, restablecí y confirmé el resultado: git revert _oldcommit_ --no-commit git reset -- _unchanged1_ _unchanged2_ ... git commit -m "branch without changes to specific files"la nueva sugerencia de rama reflejó todos los cambios, excepto los archivos revertidos.
Suncat2000
7

Este es un paso muy simple. Revise el archivo para la identificación de confirmación que queremos, aquí una identificación de confirmación antes, y luego solo git commit correct y listo.

# git checkout <previous commit_id> <file_name>
# git commit --amend

Esto es muy útil. Si queremos llevar cualquier archivo a cualquier ID de confirmación anterior en la parte superior de la confirmación, podemos hacerlo fácilmente.

Abhishek Dwivedi
fuente
5
git revert <hash>

Revertirá un commit dado. Parece que crees que git revertsolo afecta la confirmación más reciente.

Eso no resuelve su problema, si desea revertir un cambio en un archivo específico y esa confirmación cambió más que ese archivo.

Otón
fuente
5

si confirma un archivo incorrecto en sus últimas confirmaciones, siga las instrucciones:

  1. árbol de código abierto, cambie a esta confirmación

árbol de código abierto

  1. cambie las líneas y encuentre su confirmación de que el archivo incorrecto se envió como confirmación

ingrese la descripción de la imagen aquí

  1. puedes ver la lista de tus cambios en ese commit lista de archivos en el árbol de origen
  2. selecciónelo y luego haga clic en ... botones del lado derecho ... haga clic en el archivo inverso
  3. luego puede verlo en la pestaña de estado del archivo en la parte inferior izquierda y luego hacer clic en unstage:

pestaña de estado del archivo

  1. abra su código visual de estudio y vuelva atrás al confirmar sus archivos eliminados
  2. después de todo, puedes ver los resultados en tu última confirmación en el árbol de origen

ingrese la descripción de la imagen aquí

sable tabatabaee yazdi
fuente