Réponse courte
Vous avez omis le fait que vous avez exécuté git push
, obtenu l'erreur suivante, puis exécuté 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.
Bien que Git essaie d'être utile, ses conseils «git pull» ne sont probablement pas ce que vous voulez faire .
Si vous êtes:
- En travaillant uniquement sur une "branche de fonctionnalité" ou "branche de développeur" , vous pouvez exécuter
git push --force
pour mettre à jour la télécommande avec vos commits post-rebase ( selon la réponse de user4405677 ).
- 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 dev
à jour avec les modifications de master
, vous devriez, au lieu de courir git rebase master dev
, exécuter git merge master
pendant que vous êtes allumé 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éorganisez les validations, vous modifierez les hachages de validation; le rebasage (lorsqu'il fait quelque chose) changera les hachages de validation. Avec cela, le résultat de l'exécution git rebase master dev
, où dev
est désynchronisé avec master
, créera de nouveaux commits (et donc des hachages) avec le même contenu que ceux sur dev
mais avec les commits master
insérés avant eux.
Vous pouvez vous retrouver dans une situation comme celle-ci de plusieurs manières. Je peux penser à deux façons:
- Vous pourriez avoir des commits sur
master
lesquels vous souhaitez baser votre dev
travail
- Vous pourriez avoir des commits sur
dev
qui ont déjà été poussés vers une télécommande, que vous procédez ensuite à la modification (reformuler les messages de commit, réorganiser les commits, squash commits, etc.)
Comprenons mieux 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 là que vous devrez me croire sur parole: il existe 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éorganisez les commits, squashing s'engage 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 hachages de validation sont différents. C'est un comportement attendu puisque vous avez changé quelque chose (n'importe quoi) à leur sujet. C'est bon, MAIS:
Essayer de pousser vous montrera une erreur (et un indice 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 exécutons 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 en double localement. Si nous git push
devions exécuter, nous les enverrions au serveur.
Pour éviter d'arriver à ce stade, nous aurions pu courir git push --force
(où nous avons plutôt couru git pull
). Cela aurait envoyé nos commits avec les nouveaux hachages au serveur sans problème. Pour résoudre le problème à ce stade, nous pouvons réinitialiser avant d'exécuter git pull
:
Regardez le reflog ( git reflog
) pour voir ce que le hachage commettras était 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
Ci-dessus, nous voyons que ba7688a
c'était le commit auquel nous étions avant de courir git pull
. Avec ce hachage de validation en main, nous pouvons revenir à cela ( git reset --hard ba7688a
) et ensuite exécuter 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 continuiez à travailler sur des commits en double, vous avez vraiment fait un gâchis pour vous-même. La taille du désordre est proportionnelle au nombre de commits que vous avez au-dessus des doublons.
À 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 validations en double, mais conserver les validations que nous avons basées sur elles - nous souhaitons conserver C6 à C10. Comme pour la plupart des choses, il existe plusieurs façons de procéder:
Soit:
- Créez une nouvelle branche au dernier commit dupliqué 1 ,
cherry-pick
chaque commit (C6 à C10 inclus) sur cette nouvelle branche, et traitez cette nouvelle branche comme canonique.
- Exécuter
git rebase --interactive $commit
, où $commit
est le commit avant les deux commits dupliqués 2 . Ici, nous pouvons carrément supprimer les lignes des doublons.
1 Peu importe lequel des deux vous choisissez, l'un ba7688a
ou l' autre 2a2e220
fonctionne bien.
2 Dans l'exemple, ce serait 85f59ab
.
TL; DR
Définir advice.pushNonFastForward
sur false
:
git config --global advice.pushNonFastForward false