Risposta breve
Hai omesso il fatto di aver eseguito git push
, ottenuto il seguente errore e quindi eseguito git pull
:
To git@bitbucket.org:username/test1.git
! [rejected] dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@bitbucket.org:username/test1.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Nonostante Git cerchi di essere d'aiuto, il suo consiglio "git pull" molto probabilmente non è quello che vuoi fare .
Se sei:
- Lavorare su un "ramo di caratteristica" o "ramo developer" da solo , quindi è possibile eseguire
git push --force
l'aggiornamento del telecomando con i tuoi post-commit rebase ( come da risposta di user4405677 ).
- Lavorando su un ramo con più sviluppatori contemporaneamente, probabilmente non dovresti usarlo
git rebase
in primo luogo. Per aggiornare dev
con le modifiche da master
, dovresti, invece di correre git rebase master dev
, correre git merge master
mentre sei acceso dev
( come da risposta di Justin ).
Una spiegazione leggermente più lunga
Ogni hash di commit in Git si basa su una serie di fattori, uno dei quali è l'hash del commit che lo precede.
Se riordini i commit, cambierai gli hash dei commit; il rebasing (quando fa qualcosa) cambierà gli hash di commit. Con ciò, il risultato dell'esecuzione git rebase master dev
, dove non dev
è sincronizzato con master
, creerà nuovi commit (e quindi hash) con lo stesso contenuto di quelli attivi dev
ma con i commit master
inseriti prima di essi.
Puoi finire in una situazione come questa in diversi modi. Due modi in cui posso pensare:
- Potresti avere impegni su
master
cui vuoi basare il tuo dev
lavoro
- Potresti avere commit su
dev
che sono già stati inviati a un telecomando, che poi procedi a modificare (riformulare i messaggi di commit, riordinare i commit, squash commit, ecc.)
Comprendiamo meglio cosa è successo: ecco un esempio:
Hai un repository:
2a2e220 (HEAD, master) C5
ab1bda4 C4
3cb46a9 C3
85f59ab C2
4516164 C1
0e783a3 C0
Si procede quindi alla modifica dei commit.
git rebase --interactive HEAD~3 # Three commits before where HEAD is pointing
(Qui è dove devi credermi sulla parola: ci sono molti modi per cambiare i commit in Git. In questo esempio ho cambiato l'ora di C3
, ma tu stai inserendo nuovi commit, cambiando i messaggi di commit, riordinando i commit, schiacciare i commit insieme, ecc.)
ba7688a (HEAD, master) C5
44085d5 C4
961390d C3
85f59ab C2
4516164 C1
0e783a3 C0
È qui che è importante notare che gli hash di commit sono diversi. Questo è un comportamento previsto poiché hai cambiato qualcosa (qualsiasi cosa) su di loro. Va bene, MA:
Provare a spingere mostrerà un errore (e suggerirà che dovresti eseguire git pull
).
$ git push origin master
To git@bitbucket.org:username/test1.git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'git@bitbucket.org:username/test1.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Se corriamo git pull
, vediamo questo registro:
7df65f2 (HEAD, master) Merge branch 'master' of bitbucket.org:username/test1
ba7688a C5
44085d5 C4
961390d C3
2a2e220 (origin/master) C5
85f59ab C2
ab1bda4 C4
4516164 C1
3cb46a9 C3
0e783a3 C0
Oppure, mostrato in un altro modo:
E ora abbiamo commit duplicati a livello locale. Se dovessimo eseguire git push
, li invieremo al server.
Per evitare di arrivare a questa fase, avremmo potuto correre git push --force
(dove invece abbiamo corso git pull
). Ciò avrebbe inviato i nostri commit con i nuovi hash al server senza problemi. Per risolvere il problema in questa fase, possiamo ripristinare prima di eseguire git pull
:
Guarda il reflog ( git reflog
) per vedere qual era l'hash del commit prima di essere eseguito git pull
.
070e71d HEAD@{1}: pull: Merge made by the 'recursive' strategy.
ba7688a HEAD@{2}: rebase -i (finish): returning to refs/heads/master
ba7688a HEAD@{3}: rebase -i (pick): C5
44085d5 HEAD@{4}: rebase -i (pick): C4
961390d HEAD@{5}: commit (amend): C3
3cb46a9 HEAD@{6}: cherry-pick: fast-forward
85f59ab HEAD@{7}: rebase -i (start): checkout HEAD~~~
2a2e220 HEAD@{8}: rebase -i (finish): returning to refs/heads/master
2a2e220 HEAD@{9}: rebase -i (start): checkout refs/remotes/origin/master
2a2e220 HEAD@{10}: commit: C5
ab1bda4 HEAD@{11}: commit: C4
3cb46a9 HEAD@{12}: commit: C3
85f59ab HEAD@{13}: commit: C2
4516164 HEAD@{14}: commit: C1
0e783a3 HEAD@{15}: commit (initial): C0
Sopra vediamo che ba7688a
era il commit in cui eravamo prima di correre git pull
. Con quell'hash di commit in mano possiamo reimpostare su that ( git reset --hard ba7688a
) e quindi eseguire git push --force
.
E abbiamo finito.
Ma aspetta, ho continuato a basare il lavoro sui commit duplicati
Se in qualche modo non hai notato che i commit erano duplicati e hai continuato a lavorare sopra i commit duplicati, hai davvero combinato un pasticcio per te stesso. La dimensione del pasticcio è proporzionale al numero di commit che hai in cima ai duplicati.
Che aspetto ha:
3b959b4 (HEAD, master) C10
8f84379 C9
0110e93 C8
6c4a525 C7
630e7b4 C6
070e71d (origin/master) Merge branch 'master' of bitbucket.org:username/test1
ba7688a C5
44085d5 C4
961390d C3
2a2e220 C5
85f59ab C2
ab1bda4 C4
4516164 C1
3cb46a9 C3
0e783a3 C0
Oppure, mostrato in un altro modo:
In questo scenario vogliamo rimuovere i commit duplicati, ma mantenere i commit che abbiamo basato su di essi: vogliamo mantenere da C6 a C10. Come per la maggior parte delle cose, ci sono diversi modi per farlo:
O:
- Crea un nuovo ramo all'ultimo commit duplicato 1 ,
cherry-pick
ogni commit (da C6 a C10 compreso) su quel nuovo ramo e considera quel nuovo ramo come canonico.
- Esegui
git rebase --interactive $commit
, dov'è $commit
il commit prima di entrambi i commit duplicati 2 . Qui possiamo eliminare completamente le righe per i duplicati.
1 Non importa quale dei due scegli, ba7688a
o 2a2e220
funziona bene.
2 Nell'esempio sarebbe 85f59ab
.
TL; DR
Imposta advice.pushNonFastForward
su false
:
git config --global advice.pushNonFastForward false