Programa
En determinadas situaciones, puede que deseemos restablecer el estado de un local Git local para alinearla con el repositorio remoto, descartando así cualquier cambio local. Esto se conoce comúnmente comotirón forzado .
Esto crea la idea errónea de que la forma de archivarlo es utilizar la opción --force
en el comando git pull
. En este artículo aprendemos que son cosas distintas.
Podemos sobrescribir nuestro estado local con el estado remoto utilizando los comandos que se indican a continuación, sustituyendo por el nombre de tu rama. Esta acción borrará permanentemente todos tus cambios locales en esa rama.
git fetch
git reset --hard origin/<branch_name>
Este artículo profundizará en los detalles de cómo funcionan estos comandos. Vamos más allá de las soluciones rápidas para comprender la mecánica subyacente, aprendiendo así métodos más eficaces para lograr el resultado deseado sin riesgo de perder cambios importantes. También aclaramos el concepto erróneo y aprendemos qué hace realmente la opción --force
de git pull
.
Cuándo considerar la posibilidad de sobrescribir los cambios locales
En general, sobrescribir cambios locales de esta forma va en contra del uso previsto de Git. Para casi cualquier situación, existe un flujo de trabajo Git adecuado para resolverla. Sin embargo, algunas de ellas suelen requerir un conocimiento más profundo de Git y tardan más tiempo en ejecutarse. Aunque no sea la mejor práctica, sobrescribir los cambios locales a veces puede ser la forma más rápida de hacer el trabajo.
En mi caso, esto suele ocurrir cuando trabajo en una función con la que no estoy muy familiarizado. Empiezo creando una rama y haciendo algunos commits. Cuando me atasco, pido ayuda a un compañero. Empiezan desde un commit anterior, antes de que yo cometiera el error, resuelven el problema y empujan sus cambios. En este punto, necesito sustituir mi versión local por la versión remota actualizada, descartando mis cambios locales, para poder seguir trabajando en la función.
Otro ejemplo es cuando hay un conflicto, y creo que es más difícil o lleva más tiempo solucionar el conflicto que volver a implementar nuestros cambios en la última versión del código.
Independientemente del motivo, asegúrate siempre de que esos cambios realmente no son necesarios y pueden eliminarse con seguridad, o haz una copia de seguridad de tu rama para evitar perder trabajo importante.
Cómo sobrescribir correctamente los cambios locales
El primer paso es buscar (descargar) el contenido más reciente del repositorio remoto en nuestra máquina local. Esto se hace utilizando elcomando fetch
de git:
git fetch
Por defecto, esto recuperará el contenido de la rama actual, que suele ser lo que necesitamos. Podemos especificar opcionalmente la rama que deseamos descargar (sustituyendo por el nombre de la rama que queremos obtener):
git fetch origin/<branch_name>
También podemos utilizar la opción --all
para descargarlo todo:
git fetch –all
Obtener los cambios del repositorio remoto no afectará a los archivos de tu directorio de la rama de trabajo actual. El comando fetch
descarga los cambios del repositorio remoto pero no los fusiona. El repositorio local mantiene una vista tanto de los archivos locales como de los archivos remotos, y la obtención actualiza la vista de los archivos remotos.
Tras descargar los cambios del repositorio remoto, podemos utilizar el comando reset
de git para restablecer la rama de trabajo actual a un estado determinado:
git reset --hard origin/<branch_name>
La opción --hard
es necesaria para asegurarse de que los cambios locales son sobrescritos por los cambios remotos. En el comando anterior, elegimos sustituir la rama de trabajo actual por origin/
, que corresponde a la versión remota de esa rama que se acaba de obtener.
El comando git reset
borrará los cambios locales, aunque estén confirmados. Por tanto, se recomienda crear una copia de seguridad local de la rama actual antes de ejecutar ese comando. Esto puede hacerse utilizando:
git branch <name_of_the_backup_branck>
Sustituye por el nombre que quieras que tenga la rama de copia de seguridad.
Poniéndolo todo junto, podemos anular todos los cambios locales de una rama determinada con la última versión remota haciendo lo siguiente:
git fetch
git branch <name_of_the_backup_branch>
git reset --hard origin/<branch_name>
Limpieza completa borrando los archivos no rastreados
Git no tocará los archivos no rastreados (archivos que nunca utilizaron git add
). Si además queremos borrar estos archivos, podemos hacerlo utilizando:
git clean -fdx
Este es un comando peligroso que se utiliza para eliminar archivos no rastreados del directorio de trabajo. Es esencial utilizar este comando con precaución, ya que borra permanentemente estos archivos, haciendo imposible recuperarlos mediante Git. Aquí tienes un desglose de las opciones del comando:
-f
: Esta opción obliga a realizar la operación de limpieza. Git se negará a borrar los archivos no rastreados del directorio de trabajo sin esta bandera como medida de seguridad para evitar la pérdida accidental de datos.-d
: Esto indica agit clean
que elimine no sólo los archivos no rastreados, sino también los directorios no rastreados. Por defecto,git clean
sólo se dirige a archivos sin seguimiento e ignora los directorios.-x
: Por defecto,git clean
respetará las reglas de.gitignore
y no eliminará los archivos o directorios que coincidan con estos patrones. Incluir -x anula este comportamiento, indicando a Git que elimine también los archivos/directorios ignorados por.gitignore
u otras reglas de ignorar. Esto da lugar a una limpieza muy exhaustiva, eliminando todos los archivos no rastreados del repositorio, incluidos los resultados de la compilación, los archivos de registro y otros artefactos generados que normalmente se ignoran.
Comprender Git Pull
La forma habitual de actualizar nuestro repositorio local con los cambios remotos es utilizando el comando git pull
. Sin embargo, este comando no puede utilizarse para anular nuestros cambios locales porque git pull
intentará fusionar nuestra versión local con la remota.
Bajo el capó, git pull
realiza los siguientes pasos:
- Utiliza
git fetch
para descargar la última versión del repositorio remoto. - Utiliza
git merge
para fusionar las ramas local y remota.
Debido al paso de fusión, git pull
no es un comando adecuado en situaciones en las que queramos ignorar y descartar los cambios locales.
¿Qué te parece el git pull --force
?
Consultando la documentación del comando git pull
vemos que ofrece la opción --force
. Es un error común pensar que esta opción forzará la ejecución del comando git pull
borrando los cambios locales con los remotos.
Cuando proporcionamos la opción --force
a git pull
, esta opción se transmite a git fetch
. Así, git pull --force
realiza los siguientes pasos:
git fetch --force
git merge
Hemos aprendido que fetch se utiliza para actualizar la vista local del repositorio remoto. Si la única diferencia entre tu repositorio local actual y el repositorio remoto son algunos commits que faltan (la rama local está por detrás de la versión remota), entonces git fetch
simplemente actualizará la vista local añadiendo esos nuevos commits al historial.
Sin embargo, podría darse el caso de que alguien reescribiera el historial de confirmaciones de la rama remota haciéndola incompatible con la vista local. En este caso, fetch no funcionará.
Podemos evitarlo utilizando la opción --force
, que forzará a Git a ignorar el historial local y sobrescribirlo con el historial remoto.
Vemos que la opción --force
está relacionada con forzar la sobreescritura del historial de confirmaciones y no tiene nada que ver con ignorar los cambios locales en el directorio de la rama de trabajo.
Integrar los cambios locales
Sobrescribir los cambios locales con los remotos no es una práctica habitual de Git, pero puede ser útil en ciertos casos en los que no queremos lidiar con conflictos o no nos importan nuestros cambios locales.
Fusión
Normalmente, se retiraban los cambios y se solucionaban los conflictos. La gente tiende a tener miedo a enfrentarse a los conflictos. Algunos conflictos pueden ser abrumadores de tratar, pero no suele ser el caso. Se produce un conflicto cuando:
- Tanto la rama local como la rama remota tienen cambios en la misma parte de un archivo.
- Un archivo se borra por un lado y se modifica por otro.
En estos casos, Git no puede adivinar qué cambios queremos conservar, y tenemos que hacérselo saber manualmente. Git resaltará el conflicto en los archivos de la siguiente manera:
Para resolver un conflicto, editamos el archivo borrando estas líneas excepto las que queremos conservar. Después de tratar todos los conflictos, podemos confirmar los cambios. Para una guía en profundidad, consulta este tutorial sobre cómo resolver conflictos de fusión en Git.
Seleccionando
Si realmente queremos hacer que nuestra versión local coincida primero con la versión remota y luego integrar eventualmente nuestros cambios, podemos traer los commits de la rama de copia de seguridad a la rama actual, así:
git cherry-pick <commit_hash>
Aquí, es el hash del commit que queremos integrar. Esto supone que antes de hacer
git reset
, creamos una rama de copia de seguridad con git branch
y que los cambios locales habían sido confirmados.
Para encontrar el , podemos listar los hashes de commit en la rama de copia de seguridad utilizando el siguiente comando:
git log <name_of_the_backup_branck>
Guardando
Alternativamente, si no queremos confirmar nuestros cambios o crear una rama de copia de seguridad, podemos almacenar nuestros cambios antes de fusionarlos utilizando:
git stash
Este comando puede utilizarse para guardar temporalmente el trabajo actual sin confirmarlo en el historial de ramas. El almacenamiento aparta tus modificaciones, permitiéndote tener un directorio de trabajo limpio.
Después de reiniciar, podemos recuperar los cambios utilizando el siguiente comando:
git stash pop
Utilizando el almacenamiento, todo el flujo de trabajo sería más o menos así:
git fetch
git stash
git reset --hard origin/<branch_name>
git stash pop
Para saber más, consulta esta hoja de trucos con stash y otros comandos git.
Conclusión
A pesar de no ser una práctica recomendada, a veces nos encontramos en una situación en la que sólo queremos hacer que nuestra rama local coincida con el contenido de la rama remota, descartando al mismo tiempo nuestros cambios locales.
Existe la idea errónea de que esto se consigue utilizando git pull --force
. Esto es incorrecto porque git pull --force
combina git fetch --force
y git merge
. Por lo tanto, intenta fusionar los cambios locales con los remotos, sin sobrescribirlos.
La forma correcta de sobrescribir los cambios locales con el contenido del repositorio remoto es:
git fetch
git reset --hard origin/<branch_name>
El comando git reset
debe utilizarse con precaución porque borrará los cambios locales, aunque se hayan confirmado. Si teníamos commits locales y creamos una rama de copia de seguridad, podemos traerlos de vuelta utilizando git cherry-pick
. Si no confirmamos y queremos guardar temporalmente nuestro trabajo local, podemos utilizar git stash
antes de restablecer y, a continuación, git stash pop
para recuperar nuestros cambios.