git push –force and how to deal with it

Ti sei mai trovato in una situazione in cui un comando git sbagliato ha portato il caos nel repo del tuo progetto? Le persone commettono errori, e a volte questi errori possono costare ore del tempo del tuo team. In questo tutorial, vi mostreremo come recuperare da uno sfortunato git push --force rapidamente.

Non diventiamo troppo sicuri. Prima o poi succederà. Mentre lavorate con diversi telecomandi nello stesso repository git, alla fine vi capiterà di git push --force in master (o un altro ramo importante con cui non si dovrebbe mai scherzare).

Questo può succedere, per esempio, quando si fa il deploy con Deis o Heroku che usano telecomandi git separati per costruire e distribuire un’applicazione. Dopo una lunga giornata di lavoro, è incredibilmente facile eseguire git push --force invece del solito git push --force deis master.

Oops! In un batter d’occhio, i tuoi compagni di squadra hanno perso tutto il loro ultimo lavoro. È ora di affrontare la loro rabbia.

Tuttavia, come ci dice un’eccellente guida, DON’T PANIC! La cosa buona è che voi usate git, e questo significa che tutto può essere aggiustato.

Migliore delle ipotesi: qualcun altro che sta lavorando allo stesso codice ha tirato fuori una versione recente del master appena prima che voi lo rompeste. Allora tutto quello che dovete fare è andare nella chat del vostro team e chiedere a quella persona di forzare il push delle sue recenti modifiche.

Se siete fortunati, il loro repository locale avrà la storia completa dei commit, il vostro errore sarà sovrascritto con codice fresco, e nulla sarà perso. Ma se non siete così fortunati? Allora, continuate a leggere!

Caso 1: Siete stati l’ultima persona a spingere su master prima dell’errore

Buone notizie! Hai tutto il necessario per annullare il tuo errore davanti ai tuoi occhi. Basta non chiudere o cancellare il tuo terminale. Per prima cosa, andate nella chat della vostra squadra e confessate i vostri peccati. Chiedi alla gente di non fare casino con il repo per il prossimo minuto o giù di lì mentre tu sistemi le cose.

Nell’output del comando git push --force nella tua shell cerca una linea che assomigli a questa:

 + deadbeef...f00f00ba master -> master (forced update)

Il primo gruppo di simboli (che sembra il prefisso SHA di un commit) è la chiave del salvataggio. deadbeef è il tuo ultimo buon commit al master appena prima che tu infliggessi il danno.

Quindi tutto ciò di cui hai bisogno è di… forzare il push (combattendo il fuoco con il fuoco!) di questo commit indietro al ramo master, sopra uno cattivo.

$ git push --force origin deadbeef:master

Congratulazioni! Hai salvato la giornata. Ora è il momento di imparare dai tuoi errori.

Caso 2: master è stato cambiato da qualcun altro prima che tu facessi casino

Così, poco prima che tu facessi git push --force qualcuno ha chiuso un mucchio di richieste di pull, e il master ora non assomiglia per niente alla tua copia locale. Non puoi più fare git push --force sha1:master perché non hai commit recenti in locale (e non puoi averli con git fetch perché non appartengono più a nessun ramo). Comunque, mantieni la calma e chiedi ai tuoi compagni di stare lontani dal remoto per un po’.

Abbiamo il vantaggio che GitHub non rimuove immediatamente i commit non raggiungibili. Naturalmente, non è possibile nemmeno recuperarli, ma c’è un workaround.

Nota che funzionerà solo se stai “guardando” il repository, in modo che tutto ciò che accade in esso appaia in un feed visualizzato nella tua pagina principale di GitHub. Aprite quel feed e cercate qualcosa come questo:

an hour agoUsername pushed to master at org/repo - deadbeef Implement foo - deadf00d Fix bar

Ora potete:

  • Comporre un URL https://github.com/org/repo/tree/deadbeef, dove deadbeef è un hash dell’ultimo buon commit al ramo danneggiato;
  • Aprire il commutatore rami/tags nell’interfaccia web di GitHub;

GitHub branch/tag switcher

GitHub branch/tag switcher

  • Crea un nome per un nuovo ramo temporaneo (es.g., master-before-force-push);
  • Clicca “Crea ramo”.

Ora puoi recuperare tutti i commit mancanti:

$ git fetchFrom github.com:org/repo * master-before-force-push -> origin/master-before-force-push

Il tuo problema è ora ridotto a quello descritto in un esempio precedente:

$ git push --force origin origin/master-before-force-push:master

Se hai ancora bisogno che il tuo lavoro sia nel master, basta fare il rebase sopra di esso:

$ git rebase origin/master

Come evitare questo disastro in futuro

  1. GitHub e GitLab hanno una caratteristica chiamata “rami protetti”.” Segna master, develop, stable, o qualsiasi altro ramo cruciale come protetto e a nessuno sarà permesso di forzare il push in esso. È sempre possibile “sproteggere” temporaneamente un ramo se hai davvero bisogno di sovrascrivere la storia.

    Leggi la documentazione di GitHub per i dettagli.

  2. Invece dell’opzione --force, usa --force-with-lease. Fermerà l’operazione di push se qualcuno ha spinto sullo stesso ramo mentre ci stavi lavorando (e non ha tirato nessuna modifica).

    Vedi questo articolo di Atlassian.

  3. Crea degli alias per i comandi che usano git push --force per evitare azioni distruttive accidentali:

    # ~/.gitconfig deploy = "!git push --force deis \"$(git rev-parse --abbrev-ref HEAD):master\""

    Leggi il capitolo di ProGit sugli alias.

  4. Non fare mai i tuoi esperimenti nel ramo principale di un repository! git checkout -b experiments è il tuo migliore amico.

Pro Tip: git push ha molte opzioni. --force e --all lavorano insieme particolarmente bene. Puoi usare questa combo quando ritorni al progetto dopo diversi mesi di inattività. Fai una prova se ti senti particolarmente avventuroso!

Leave a Reply