Git Pull Force: cómo sobrescribir cambios locales con Git

Cuando aprenda a codificar, tarde o temprano también aprenderá sobre los sistemas de control de versiones. Y aunque hay muchas herramientas que compiten en este espacio, una de ellas es el estándar de facto utilizado por casi todos en la industria. Es tan popular que hay empresas que utilizan su nombre en su marca. Estamos hablando de Git, por supuesto.

Si bien Git es una herramienta poderosa, su poder está bien escondido. Hay algunos conceptos esenciales que debe comprender para llegar a ser realmente competente con Git. La buena noticia es que una vez que las aprenda, casi nunca tendrá problemas de los que no pueda escapar.

El flujo de trabajo típico

En un flujo de trabajo típico de Git, usará un repositorio local, un repositorio remoto y una o más ramas. Los repositorios almacenan toda la información sobre el proyecto, incluido su historial completo y todas las sucursales. Una rama es básicamente una colección de cambios que van desde un proyecto vacío al estado actual.

Después de clonar un repositorio, trabaja en su copia local e introduce nuevos cambios. Hasta que envíe los cambios locales al repositorio remoto, todo su trabajo estará disponible solo en su máquina.

Cuando finaliza una tarea, es hora de sincronizar con el repositorio remoto. Desea extraer los cambios remotos para mantenerse al día con el progreso del proyecto y desea impulsar los cambios locales para compartir su trabajo con otros.

Cambios locales

Todo está bien cuando usted y el resto de su equipo están trabajando en archivos totalmente separados. Pase lo que pase, no se pisarán el uno al otro.

Sin embargo, hay ocasiones en las que tú y tus compañeros de equipo introducen cambios simultáneamente en el mismo lugar. Y ahí es donde suelen empezar los problemas.

¿Has ejecutado alguna vez git pullsolo para ver a los temidos error: Your local changes to the following files would be overwritten by merge:? Tarde o temprano, todo el mundo se encuentra con ese problema.

Lo que es más confuso aquí es que no desea fusionar nada, solo tire, ¿verdad? En realidad, tirar es un poco más complicado de lo que pensabas.

¿Cómo funciona exactamente Git Pull?

Tirar no es una sola operación. Consiste en obtener datos del servidor remoto y luego fusionar los cambios con el repositorio local. Estas dos operaciones se pueden realizar manualmente si lo desea:

git fetch git merge origin/$CURRENT_BRANCH

La origin/$CURRENT_BRANCHparte significa que:

  • Git fusionará los cambios del repositorio remoto llamado origin(el que clonó)
  • que se han añadido a la $CURRENT_BRANCH
  • que aún no están presentes en su sucursal local registrada

Dado que Git solo realiza fusiones cuando no hay cambios no confirmados, cada vez que ejecute git pullcambios no confirmados podría meterse en problemas. Afortunadamente, ¡hay formas de salir de los problemas de una pieza!

Somos familia

Diferentes aproximaciones

Cuando tiene cambios locales no confirmados y aún desea obtener una nueva versión del servidor remoto, su caso de uso generalmente cae en uno de los siguientes escenarios. Ya sea:

  • no le importan los cambios locales y desea sobrescribirlos,
  • te preocupan mucho los cambios y te gustaría aplicarlos después de los cambios remotos,
  • desea descargar las modificaciones remotas pero aún no aplicarlas

Cada uno de los enfoques requiere una solución diferente.

No le importan los cambios locales

En este caso, solo desea eliminar todos los cambios locales no confirmados. Quizás modificó un archivo para experimentar, pero ya no necesita la modificación. Lo único que le importa es estar al día con el upstream.

Esto significa que agrega un paso más entre buscar los cambios remotos y combinarlos. Este paso restablecerá la rama a su estado sin modificar, lo que permitirá git mergeque funcione.

git fetch git reset --hard HEAD git merge origin/$CURRENT_BRANCH

Si no desea escribir el nombre de la sucursal cada vez que se ejecuta este comando, Git tiene un buen acceso rápido a la rama aguas arriba: @{u}. Una bifurcación ascendente es la bifurcación en el repositorio remoto al que empuja y busca.

This is how the above commands would look like with the shortcut:

git fetch git reset --hard HEAD git merge '@{u}'

We are quoting the shortcut in the example to prevent the shell from interpreting it.

You Very Much Care About the Local Changes

When your uncommitted changes are significant to you, there are two options. You can commit them and then perform git pull, or you can stash them.

Stashing means putting the changes away for a moment to bring them back later. To be more precise, git stash creates a commit that is not visible on your current branch, but is still accessible by Git.

To bring back the changes saved in the last stash, you use the git stash pop command. After successfully applying the stashed changes, this command also removes the stash commit as it is no longer needed.

The workflow could then look like this:

git fetch git stash git merge '@{u}' git stash pop

By default, the changes from the stash will become staged. If you want to unstage them, use the command git restore --staged (if using Git newer than 2.25.0).

You Just Want to Download the Remote Changes

The last scenario is a little different from the previous ones. Let's say that you are in the middle of a very messy refactoring. Neither losing the changes nor stashing them is an option. Yet, you still want to have the remote changes available to run git diff against them.

As you have probably figured out, downloading the remote changes does not require git pull at all! git fetch is just enough.

One thing to note is that by default, git fetch will only bring you changes from the current branch. To get all the changes from all the branches, use git fetch --all. And if you'd like to clean up some of the branches that no longer exist in the remote repository, git fetch --all --prune will do the cleaning up!

Some Automation

Have you heard of Git Config? It's a file where Git stores all of the user-configured settings. It resides in your home directory: either as ~/.gitconfig or ~/.config/git/config. You can edit it to add some custom aliases that will be understood as Git commands.

For example, to have a shortcut equivalent to git diff --cached (that shows the difference between the current branch and the staged files), you'd add the following section:

[alias] dc = diff --cached

After that, you can run git dc whenever you wish to review the changes. Going this way, we can set up a few aliases related to the previous use cases.

[alias] pull_force = !"git fetch --all; git reset --hard HEAD; git merge @{u}" pf = pull_force pull_stash = !"git fetch --all; git stash; git merge @{u}; git stash pop"

This way, running git pull_force will overwrite the local changes, while git pull_stash will preserve them.

The Other Git Pull Force

Curious minds may have already discovered that there is such a thing as git pull --force. However, this is a very different beast to what's presented in this article.

It may sound like something that would help us overwrite local changes. Instead, it lets us fetch the changes from one remote branch to a different local branch. git pull --force only modifies the behavior of the fetching part. It is therefore equivalent to git fetch --force.

Like git push, git fetch allows us to specify which local and remote branch do we want to operate on. git fetch origin/feature-1:my-feature will mean that the changes in the feature-1 branch from the remote repository will end up visible on the local branch my-feature. When such an operation modifies the existing history, it is not permitted by Git without an explicit --force parameter.

Just like git push --force allows overwriting remote branches, git fetch --force (or git pull --force) allows overwriting local branches. It is always used with source and destination branches mentioned as parameters. An alternative approach to overwriting local changes using git --pull force could be git pull --force "@{u}:HEAD".

Conclusion

El mundo de Git es vasto. Este artículo cubrió solo una de las facetas del mantenimiento del repositorio: incorporar cambios remotos en un repositorio local. Incluso este escenario cotidiano requería que analizáramos un poco más en profundidad los mecanismos internos de esta herramienta de control de versiones.

Conocer casos de uso reales te ayuda a comprender mejor cómo funciona Git bajo el capó. Esto, a su vez, te hará sentir empoderado cada vez que te metas en problemas. Todos hacemos eso de vez en cuando.