Git Pull Force - Comment écraser les modifications locales avec Git

Lorsque vous apprenez à coder, tôt ou tard, vous en apprendrez également sur les systèmes de contrôle de version. Et bien qu'il existe de nombreux outils concurrents dans cet espace, l'un d'eux est la norme de facto utilisée par presque tout le monde dans l'industrie. Il est si populaire que certaines entreprises utilisent son nom dans leur image de marque. Nous parlons de Git, bien sûr.

Bien que Git soit un outil puissant, sa puissance est bien cachée. Il y a quelques concepts essentiels que vous devez comprendre pour devenir vraiment compétent avec Git. La bonne nouvelle est qu'une fois que vous les aurez appris, vous ne rencontrerez presque jamais de problèmes auxquels vous ne pourrez échapper.

Le flux de travail typique

Dans un workflow Git typique, vous utiliserez un référentiel local, un référentiel distant et une ou plusieurs branches. Les référentiels stockent toutes les informations sur le projet, y compris son historique complet et toutes les branches. Une branche est essentiellement une collection de changements menant d'un projet vide à l'état actuel.

Après avoir cloné un référentiel, vous travaillez sur votre copie locale et introduisez de nouvelles modifications. Jusqu'à ce que vous transmettiez les modifications locales au référentiel distant, tout votre travail n'est disponible que sur votre machine.

Lorsque vous avez terminé une tâche, il est temps de se synchroniser avec le référentiel distant. Vous souhaitez extraire les modifications à distance pour suivre la progression du projet et vous souhaitez pousser les modifications locales pour partager votre travail avec d'autres.

Changements locaux

Tout va bien lorsque vous et le reste de votre équipe travaillez sur des fichiers totalement séparés. Quoi qu'il arrive, vous ne vous marcherez pas sur les pieds.

Cependant, il y a des moments où vous et vos coéquipiers introduisez simultanément des changements au même endroit. Et c'est généralement là que les problèmes commencent.

Avez-vous déjà exécuté git pulluniquement pour voir les redoutés error: Your local changes to the following files would be overwritten by merge:? Tôt ou tard, tout le monde se heurte à ce problème.

Ce qui est plus déroutant ici, c'est que vous ne voulez rien fusionner, juste tirer, non? En fait, tirer est un peu plus compliqué que vous ne le pensez.

Comment fonctionne exactement Git Pull?

La traction n'est pas une opération unique. Il consiste à récupérer les données du serveur distant, puis à fusionner les modifications avec le référentiel local. Ces deux opérations peuvent être effectuées manuellement si vous le souhaitez:

git fetch git merge origin/$CURRENT_BRANCH

La origin/$CURRENT_BRANCHpartie signifie que:

  • Git fusionnera les modifications du référentiel distant nommé origin(celui à partir duquel vous avez cloné)
  • qui ont été ajoutés au $CURRENT_BRANCH
  • qui ne sont pas déjà présents dans votre succursale de départ locale

Étant donné que Git n'effectue des fusions que lorsqu'il n'y a pas de modifications non validées, chaque fois que vous exécutez des git pullmodifications non validées peut vous causer des problèmes. Heureusement, il existe des moyens de se sortir des ennuis en un seul morceau!

Nous sommes une famille

Différentes approches

Lorsque vous avez des modifications locales non validées et que vous souhaitez toujours extraire une nouvelle version du serveur distant, votre cas d'utilisation s'inscrit généralement dans l'un des scénarios suivants. Soit:

  • vous ne vous souciez pas des modifications locales et souhaitez les écraser,
  • vous vous souciez beaucoup des changements et aimeriez les appliquer après les changements à distance,
  • vous souhaitez télécharger les modifications à distance mais pas encore les appliquer

Chacune des approches nécessite une solution différente.

Vous ne vous souciez pas des changements locaux

Dans ce cas, vous souhaitez simplement supprimer toutes les modifications locales non validées. Vous avez peut-être modifié un fichier pour expérimenter, mais vous n'avez plus besoin de la modification. Tout ce qui vous importe, c'est d'être à jour avec l'amont.

Cela signifie que vous ajoutez une étape supplémentaire entre la récupération des modifications distantes et leur fusion. Cette étape réinitialisera la branche à son état non modifié, permettant ainsi git mergede fonctionner.

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

Si vous ne voulez pas taper le nom de la branche à chaque fois que vous exécutez cette commande, Git a un raccourci agréable pointant vers la branche amont: @{u}. Une branche en amont est la branche du référentiel distant vers laquelle vous poussez et récupérez.

Voici à quoi ressembleraient les commandes ci-dessus avec le raccourci:

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

Nous citons le raccourci dans l'exemple pour empêcher le shell de l'interpréter.

Vous vous souciez beaucoup des changements locaux

Lorsque vos modifications non validées sont importantes pour vous, il existe deux options. Vous pouvez les valider et les exécuter git pull, ou vous pouvez les cacher.

Cacher signifie ranger les changements pendant un moment pour les ramener plus tard. Pour être plus précis, git stashcrée un commit qui n'est pas visible sur votre branche actuelle, mais qui est toujours accessible par Git.

Pour ramener les modifications enregistrées dans le dernier stash, vous utilisez la git stash popcommande. Après avoir appliqué avec succès les modifications cachées, cette commande supprime également la validation cachée car elle n'est plus nécessaire.

Le flux de travail pourrait alors ressembler à ceci:

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

Le monde de Git est vaste. Cet article ne couvrait qu'une des facettes de la maintenance du référentiel: l'incorporation de modifications à distance dans un référentiel local. Même ce scénario quotidien nous a obligé à examiner un peu plus en profondeur les mécanismes internes de cet outil de contrôle de version.

L'apprentissage de cas d'utilisation réels vous aide à mieux comprendre comment Git fonctionne sous le capot. Cela, à son tour, vous donnera le sentiment d'être autonome chaque fois que vous vous posez des problèmes. Nous faisons tous cela de temps en temps.