Revertir un archivo a una versión anterior en git

¿Existe una forma de recorrer los diferentes commits de un archivo? Digamos que he modificado un archivo 5 veces y quiero volver al cambio 2, después de que ya he confirmado y empujado a un repositorio.

A mi entender la única manera es mantener muchas ramas, ¿tengo razón? Si estoy en lo cierto voy a tener cientos de ramas en pocos días, así que probablemente no lo estoy entendiendo bien.

¿Alguien podría aclararlo, por favor?

Solución

Empecemos con una descripción cualitativa de lo que queremos hacer (gran parte de esto se dice en la respuesta de Ben Straub). Hemos hecho un cierto número de confirmaciones, cinco de las cuales han cambiado un archivo determinado, y queremos revertir el archivo a una de las versiones anteriores. En primer lugar, git no guarda los números de versión de los archivos individuales. Sólo registra el contenido - una confirmación es esencialmente una instantánea del árbol de trabajo, junto con algunos metadatos (por ejemplo, el mensaje de confirmación). Por lo tanto, tenemos que saber qué confirmación tiene la versión del archivo que queremos. Una vez que sepamos eso, tendremos que hacer un nuevo commit revirtiendo el archivo a ese estado. (No podemos' juguetear con el historial, porque ya hemos empujado este contenido, y la edición del historial se mete con todos los demás).

Así que vamos a empezar con la búsqueda de la confirmación correcta. Puedes ver los commits que han hecho modificaciones a un archivo(s) dado muy fácilmente:

git log path/to/file

Si sus mensajes de confirmación no son lo suficientemente buenos, y necesita ver lo que se hizo al archivo en cada confirmación, utilice la opción -p/--patch:

git log -p path/to/file

O, si prefieres la vista gráfica de gitk

gitk path/to/file

También puedes hacerlo una vez que hayas iniciado gitk a través del menú de vista; una de las opciones de vista es una lista de rutas a incluir.

De cualquier manera, podrás encontrar el SHA1 (hash) del commit con la versión del archivo que quieres. Ahora, todo lo que tienes que hacer es esto:

# get the version of the file from the given commit
git checkout  path/to/file
# and commit this modification
git commit

(El comando checkout primero lee el archivo en el índice, y luego lo copia en el árbol de trabajo, por lo que no hay necesidad de usar git add para añadirlo al índice en preparación para el commit).

Si tu archivo no tiene un historial sencillo (por ejemplo, renombrados y copias), mira el excelente comentario de VonC&#39. Se puede dirigir a git para que busque con más cuidado esas cosas, a expensas de la velocidad. Si estás seguro de que el historial es sencillo, no necesitas molestarte.

Comentarios (11)

Git no piensa en términos de versiones de archivos. Una versión en git es una instantánea de todo el árbol.

Teniendo en cuenta esto, lo que realmente quieres es un árbol que tenga el último contenido de la mayoría de los archivos, pero con el contenido de un archivo igual que hace 5 commits. Esto tomará la forma de un nuevo commit encima de los antiguos, y la última versión del árbol tendrá lo que quieres.

No sé si hay una línea que revierta un archivo al contenido de hace 5 commits, pero la solución de lo-fi debería funcionar: checkout master~5, copiar el archivo en otro lugar, checkout master, copiar el archivo de nuevo, y luego hacer commit.

Comentarios (2)

Puedes tomar un diff que deshaga los cambios que quieras y confirmarlo.

Por ejemplo, si quiere deshacer los cambios en el rango desde..hasta, haga lo siguiente

git diff to..from > foo.diff  # get a reverse diff
patch < foo.diff
git commit -a -m "Undid changes from..to".
Comentarios (5)