Réponse courte
Vous avez omis le fait que vous avez couru git push
J'ai obtenu l'erreur suivante, et j'ai ensuite exécuté le programme 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.
Malgré le fait que Git essaie d'être utile, son conseil "git pull" n'est probablement pas ce que vous voulez faire. .
Si vous l'êtes :
- Travailler sur une "feature branch" ou une "developer branch". seul alors vous pouvez exécuter
git push --force
pour mettre à jour la télécommande avec vos commits post-rebase ( selon la réponse de l'utilisateur4405677 ).
- Travailler sur une branche avec plusieurs développeurs en même temps, alors vous ne devriez probablement pas utiliser
git rebase
en premier lieu. Pour mettre à jour dev
avec des changements de master
vous devriez, au lieu d'exécuter git rebase master dev
, courir git merge master
pendant que dev
( selon la réponse de Justin ).
Une explication un peu plus longue
Chaque hachage de commit dans Git est basé sur un certain nombre de facteurs, dont l'un est le hachage du commit qui le précède.
Si vous réordonnez les commits, vous changerez les hashs des commits ; le rebasage (quand il fait quelque chose) changera les hashs des commits. Avec cela, le résultat de l'exécution de git rebase master dev
, donde dev
n'est pas synchrone avec master
créera nouveau (et donc des hachages) avec le même contenu que ceux de l'application dev
mais avec les commits sur master
inséré avant eux.
Vous pouvez vous retrouver dans une telle situation de plusieurs façons. Deux façons auxquelles je peux penser :
- Vous pourriez avoir des commits sur
master
que vous voulez baser votre dev
travailler sur
- Vous pourriez avoir des commits sur
dev
qui ont déjà été poussés vers un serveur distant, que vous pouvez ensuite modifier (reformulation des messages de validation, réorganisation des validations, compression des validations, etc.)
Essayons de mieux comprendre ce qui s'est passé - voici un exemple :
Vous avez un référentiel :
2a2e220 (HEAD, master) C5
ab1bda4 C4
3cb46a9 C3
85f59ab C2
4516164 C1
0e783a3 C0
Vous procédez ensuite à la modification des commits.
git rebase --interactive HEAD~3 # Three commits before where HEAD is pointing
(C'est ici que vous devrez me croire sur parole : il y a plusieurs façons de modifier les commits dans Git. Dans cet exemple, j'ai changé l'heure de C3
mais vous insérez de nouveaux commits, modifiez les messages de commit, réordonnez les commits, écrasez les commits ensemble, etc.)
ba7688a (HEAD, master) C5
44085d5 C4
961390d C3
85f59ab C2
4516164 C1
0e783a3 C0
C'est là qu'il est important de noter que les hashs de commit sont différents. C'est un comportement attendu puisque vous avez changé quelque chose (n'importe quoi) à leur sujet. C'est correct, MAIS :
Essayer de pousser vous montrera une erreur (et vous indiquera que vous devriez exécuter 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.
Si nous courons git pull
nous voyons ce journal :
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
Ou, montré d'une autre manière :
Et maintenant nous avons des commits dupliqués localement. Si nous devions exécuter git push
nous les enverrions au serveur.
Pour éviter d'arriver à ce stade, nous aurions pu exécuter git push --force
(où nous avons plutôt exécuté git pull
). Cela aurait envoyé nos commits avec les nouveaux hashs au serveur sans problème. Pour résoudre le problème à ce stade, nous pouvons revenir à la situation antérieure à l'exécution de la commande git pull
:
Regardez le reflog ( git reflog
) pour voir quel était le hash de la livraison avant nous avons couru 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
Nous voyons ci-dessus que ba7688a
était le commit où nous étions avant de lancer git pull
. Avec ce hash de commit en main, nous pouvons réinitialiser le tout ( git reset --hard ba7688a
), puis exécutez git push --force
.
Et nous avons terminé.
Mais attendez, j'ai continué à baser le travail sur les commits dupliqués.
Si vous n'avez pas remarqué que les commits étaient dupliqués et que vous avez continué à travailler sur des commits dupliqués, vous vous êtes vraiment mis dans le pétrin. La taille du désordre est proportionnelle au nombre de commits que vous avez au-dessus des duplicatas.
Ce à quoi ça ressemble :
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
Ou, montré d'une autre manière :
Dans ce scénario, nous voulons supprimer les commits dupliqués, mais garder les commits que nous avons basés sur eux - nous voulons garder C6 à C10. Comme pour la plupart des choses, il y a plusieurs façons de procéder :
Soit :
- Créer une nouvelle branche au dernier commit dupliqué 1 ,
cherry-pick
chaque commit (C6 à C10 inclus) sur cette nouvelle branche, et traiter cette nouvelle branche comme canonique.
- Ou courir
git rebase --interactive $commit
, donde $commit
est l'engagement avant aux deux commits dupliqués 2 . Ici, nous pouvons carrément supprimer les lignes pour les doublons.
1 Le choix de l'un ou l'autre n'a pas d'importance. ba7688a
o 2a2e220
fonctionnent bien.
2 Dans l'exemple, ce serait 85f59ab
.
TL;DR
Définir advice.pushNonFastForward
a false
:
git config --global advice.pushNonFastForward false