Git Pull Force - Come sovrascrivere le modifiche locali con Git

Quando impari a programmare, prima o poi imparerai anche a conoscere i sistemi di controllo della versione. E mentre ci sono molti strumenti concorrenti in questo spazio, uno di questi è lo standard de facto utilizzato da quasi tutti nel settore. È così popolare che ci sono aziende che usano il suo nome nel loro marchio. Stiamo parlando di Git, ovviamente.

Sebbene Git sia uno strumento potente, il suo potere è ben nascosto. Ci sono alcuni concetti essenziali che devi capire per diventare veramente esperto con Git. La buona notizia è che una volta che li impari, difficilmente ti imbatterai in guai da cui non puoi sfuggire.

Il tipico flusso di lavoro

In un tipico flusso di lavoro Git utilizzerai un repository locale, un repository remoto e uno o più rami. I repository memorizzano tutte le informazioni sul progetto, inclusa l'intera cronologia e tutti i rami. Un ramo è fondamentalmente una raccolta di modifiche che portano da un progetto vuoto allo stato corrente.

Dopo aver clonato un repository, lavori sulla tua copia locale e introduci nuove modifiche. Fino a quando non invii le modifiche locali al repository remoto, tutto il tuo lavoro è disponibile solo sulla tua macchina.

Quando finisci un'attività, è il momento di sincronizzarti con il repository remoto. Si desidera eseguire il pull delle modifiche remote per tenere il passo con l'avanzamento del progetto e si desidera eseguire il push delle modifiche locali per condividere il proprio lavoro con gli altri.

Modifiche locali

Tutto va bene quando tu e il resto del tuo team lavorate su file completamente separati. Qualunque cosa accada, non vi calpesterete a vicenda.

Tuttavia, ci sono momenti in cui tu ei tuoi compagni di squadra introducete simultaneamente modifiche nello stesso punto. E di solito è lì che iniziano i problemi.

Hai mai giustiziato git pullsolo per vedere i temuti error: Your local changes to the following files would be overwritten by merge:? Prima o poi, tutti si imbattono in quel problema.

Ciò che è più confuso qui è che non vuoi unire nulla, basta tirare, giusto? In realtà, tirare è un po 'più complicato di quanto potresti aver pensato.

Come funziona esattamente Git Pull?

Il pull non è una singola operazione. Consiste nel recuperare i dati dal server remoto e quindi unire le modifiche con il repository locale. Queste due operazioni possono essere eseguite manualmente se lo desideri:

git fetch git merge origin/$CURRENT_BRANCH

La origin/$CURRENT_BRANCHparte significa che:

  • Git unirà le modifiche dal repository remoto denominato origin(quello da cui hai clonato)
  • che sono stati aggiunti a $CURRENT_BRANCH
  • che non sono già presenti nella tua filiale locale controllata

Poiché Git esegue unioni solo quando non ci sono modifiche non salvate, ogni volta che esegui git pullcon modifiche non salvate potresti metterti nei guai. Fortunatamente, ci sono modi per uscire dai guai tutto d'un pezzo!

Siamo una famiglia

Approcci diversi

Quando sono presenti modifiche locali non salvate e si desidera comunque estrarre una nuova versione dal server remoto, il caso d'uso rientra in genere in uno dei seguenti scenari. O:

  • non ti interessano le modifiche locali e vuoi sovrascriverle,
  • ti interessano molto le modifiche e vorresti applicarle dopo le modifiche remote,
  • si desidera scaricare le modifiche remote ma non applicarle ancora

Ciascuno degli approcci richiede una soluzione diversa.

Non ti interessano i cambiamenti locali

In questo caso, vuoi solo eliminare tutte le modifiche locali non salvate. Forse hai modificato un file per sperimentare, ma non hai più bisogno della modifica. Tutto ciò che ti interessa è essere aggiornato con l'upstream.

Ciò significa che aggiungi un ulteriore passaggio tra il recupero delle modifiche remote e l'unione di esse. Questo passaggio ripristinerà il ramo allo stato non modificato, consentendo così git mergedi funzionare.

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

Se non si desidera digitare il nome del ramo ogni volta che si esegue questo comando, Git ha una bella scorciatoia che punta al ramo di monte: @{u}. Un ramo a monte è il ramo nel repository remoto da cui esegui il push e il recupero.

Ecco come apparirebbero i comandi precedenti con il collegamento:

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

Stiamo citando la scorciatoia nell'esempio per impedire alla shell di interpretarla.

Ti preoccupi molto dei cambiamenti locali

Quando le modifiche non salvate sono significative per te, ci sono due opzioni. Puoi impegnarli e poi esibirli git pull, oppure puoi nasconderli.

Nascondere significa mettere da parte le modifiche per un momento per ripristinarle più tardi. Per essere più precisi, git stashcrea un commit che non è visibile sul tuo ramo corrente, ma è comunque accessibile da Git.

Per ripristinare le modifiche salvate nell'ultima scorta, utilizzare il git stash popcomando. Dopo aver applicato con successo le modifiche nascoste, questo comando rimuove anche il commit stash in quanto non è più necessario.

Il flusso di lavoro potrebbe quindi assomigliare a questo:

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

Per impostazione predefinita, le modifiche dalla scorta verranno messe in scena. Se vuoi rimuoverli dallo stage, usa il comando git restore --staged(se usi Git più recente di 2.25.0).

Vuoi solo scaricare le modifiche remote

L'ultimo scenario è leggermente diverso dai precedenti. Diciamo che sei nel mezzo di un refactoring molto disordinato. Né perdere le modifiche né metterle da parte è un'opzione. Tuttavia, vuoi ancora avere le modifiche remote disponibili per eseguirle git diff.

Come probabilmente avrai capito, il download delle modifiche remote non richiede git pullaffatto! git fetchè appena sufficiente.

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

Il mondo di Git è vasto. Questo articolo ha trattato solo uno degli aspetti della manutenzione del repository: incorporare modifiche remote in un repository locale. Anche questo scenario quotidiano ci ha richiesto di guardare leggermente più in profondità nei meccanismi interni di questo strumento di controllo delle versioni.

Imparare casi d'uso reali ti aiuta a capire meglio come funziona Git sotto il cofano. Questo, a sua volta, ti farà sentire potenziato ogni volta che ti metti nei guai. Lo facciamo tutti di tanto in tanto.