Votre git push affiche rejected et non-fast-forward ? C'est
le symptôme le plus courant en travail d'équipe : quelqu'un a poussé
des commits pendant que vous travailliez. Ce guide vous montre comment
débloquer la situation avec pull --rebase, quand utiliser
--force-with-lease, et comment diagnostiquer les divergences entre
votre branche locale et le remote.
Prérequis : Remotes fondamentaux et Branches distantes.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Comprendre pourquoi un push est rejeté (divergence de branches)
- Résoudre le rejet avec
pull --rebasepuis re-push propre - Utiliser
--force-with-leaseen toute sécurité quand nécessaire - Diagnostiquer les divergences entre local et remote
- Configurer
pull.rebasepour éviter les merges parasites automatiques
Comprendre le message d'erreur
Section intitulée « Comprendre le message d'erreur »Un push rejeté n'est jamais une panne : c'est un garde-fou. Git
n'accepte un push que s'il peut faire avancer la branche distante en
ligne droite, sans rien perdre. C'est ce qu'on appelle une mise à jour
fast-forward : tous les commits déjà présents sur le remote restent
accessibles depuis le nouveau sommet de branche. Dès que ce n'est plus
vrai, le serveur refuse et affiche non-fast-forward.
Quand vous tentez un push et que le remote a avancé entre-temps :
! [rejected] main -> main (non-fast-forward)error: failed to push some refs to 'origin'hint: Updates were rejected because the tip of your current branchhint: is behind its remote counterpart.En clair : votre branche locale est en retard par rapport au remote. Git refuse de pusher parce que cela écraserait les commits de vos collègues.
Remote: A - B - C - D (commit de votre collègue)Local: A - B - C - E (votre commit)Le remote a D, vous avez E. Git ne peut pas simplement ajouter E après D car il remplacerait D.
Solution standard : git pull --rebase
Section intitulée « Solution standard : git pull --rebase »La réponse au rejet consiste à intégrer d'abord ce que le remote contient, puis à repousser. Le rebase fait cette intégration en déplaçant vos commits au sommet de la branche distante, ce qui restaure la condition fast-forward que Git exigeait. Cette opération réécrit vos commits locaux (ils changent de SHA, l'empreinte qui identifie un commit), mais ils ne sont pas encore publiés : personne d'autre ne s'appuie dessus.
Dans 90% des cas, la solution est simple :
git pull --rebaseCette commande fait deux choses :
- Récupère les nouveaux commits du remote (
git fetch) - Rejoue vos commits locaux après les commits du remote
Résultat :
Avant: A - B - C - D (remote) \- E (local)
Après: A - B - C - D - E' (linéaire ✓)Votre commit E est rejoué comme E' (nouveau SHA, même contenu) après le commit D de votre collègue. L'historique reste linéaire.
Ensuite, poussez normalement :
git pushSi des conflits apparaissent
Section intitulée « Si des conflits apparaissent »Un conflit survient quand vos commits et ceux du remote modifient
les mêmes lignes d'un même fichier. Le rebase s'arrête alors sur le
commit fautif et attend votre arbitrage : tant que vous n'avez pas
terminé la séquence, le dépôt reste dans un état intermédiaire signalé
par git status.
-
Résolvez les conflits dans chaque fichier
Fenêtre de terminal git status# Éditez les fichiers listés sous "Unmerged paths" -
Ajoutez les fichiers résolus
Fenêtre de terminal git add fichier-resolu.py -
Continuez le rebase
Fenêtre de terminal git rebase --continue -
Poussez
Fenêtre de terminal git push
Pour annuler le rebase en cours et revenir à l'état d'avant :
git rebase --abortAlternative : git pull (merge)
Section intitulée « Alternative : git pull (merge) »Si vous préférez un merge classique au lieu d'un rebase :
git pullCela crée un commit de merge :
A - B - C - D --- M (merge commit) \- E ─┘Résultat fonctionnel identique, mais l'historique contient un point de fusion supplémentaire. Le push fonctionne ensuite normalement.
Configurer pull.rebase par défaut
Section intitulée « Configurer pull.rebase par défaut »Sans réglage explicite, git pull fait un merge et fabrique un
commit de fusion à chaque divergence, même minuscule. Sur un dépôt
d'équipe actif, l'historique se remplit vite de ces commits qui
n'apportent aucune information. Le paramètre pull.rebase change ce
comportement une bonne fois pour toutes, au niveau de votre poste.
Pour ne plus avoir à taper --rebase à chaque pull :
git config --global pull.rebase trueVérification :
git config --global pull.rebase# trueDésormais, git pull fera automatiquement un rebase au lieu d'un
merge.
Quand utiliser --force-with-lease
Section intitulée « Quand utiliser --force-with-lease »--force-with-lease force le push mais vérifie d'abord que
personne n'a poussé entre-temps. C'est un « force push sécurisé ».
Cas légitimes
Section intitulée « Cas légitimes »Toutes ces situations ont un point commun : vous avez réécrit
l'historique de commits déjà publiés. Le remote détient encore les
anciens SHA, votre branche locale porte les nouveaux, et aucune des
deux versions ne descend de l'autre. Un pull --rebase ne réglerait
rien ici, il empilerait les deux variantes du même travail.
| Situation | Pourquoi le push est rejeté | Solution |
|---|---|---|
Vous avez fait --amend sur un commit déjà poussé | Le SHA a changé | --force-with-lease |
Vous avez fait un rebase -i sur une branche poussée | Les SHA ont changé | --force-with-lease |
Vous avez fait un reset sur une branche poussée | Des commits ont disparu | --force-with-lease |
git push --force-with-leaseDifférence avec --force
Section intitulée « Différence avec --force »La sécurité de --force-with-lease repose sur votre branche de suivi
distante (origin/main en local), qui mémorise l'état du remote au
moment de votre dernier fetch. Git compare cette référence à ce que le
serveur détient réellement et abandonne si les deux diffèrent.
Conséquence pratique : un git fetch juste avant le push rafraîchit
cette référence et désactive de fait la protection.
| Commande | Comportement |
|---|---|
git push --force | Écrase le remote sans vérification, dangereux |
git push --force-with-lease | Vérifie que le remote n'a pas changé depuis votre dernier fetch, sûr |
Diagnostiquer une divergence
Section intitulée « Diagnostiquer une divergence »Avant toute correction, commencez toujours par un git fetch : sans
lui, votre branche de suivi distante (origin/main) reflète un état
périmé et vos comparaisons portent sur des données fausses. Le fetch
télécharge les nouveaux commits sans toucher à votre copie de travail,
il est donc sans risque. Les trois commandes ci-dessous répondent
chacune à une question différente : qui a quoi, combien, et comment les
branches se sont séparées.
Quand la situation est confuse, diagnostiquez avant d'agir :
Voir l'état des branches locale vs remote
Section intitulée « Voir l'état des branches locale vs remote »Les trois points de HEAD...origin/main demandent la différence
symétrique : les commits propres à chaque côté, jamais ceux qu'ils
partagent. L'option --left-right préfixe chaque ligne pour indiquer à
quelle branche elle appartient.
git fetchgit log --oneline --left-right HEAD...origin/mainSortie typique :
< a1b2c3d (HEAD -> main) votre commit local< d4e5f6a votre commit local> f7a8b9c (origin/main) commit du remote> 1234abc commit du remote<= commits présents localement mais pas sur le remote>= commits sur le remote mais pas localement
Compter les commits d'écart
Section intitulée « Compter les commits d'écart »Ici les deux points signifient « ce qui est accessible à droite mais
pas à gauche » : l'ordre des deux références décide donc du sens du
comptage. Deux résultats non nuls confirment une divergence ; un
seul côté à zéro signale un simple retard, que git pull --rebase règle
sans réécrire quoi que ce soit.
git rev-list --count HEAD..origin/main# Nombre de commits que le remote a en plus
git rev-list --count origin/main..HEAD# Nombre de commits que vous avez en plusVisualiser le graphe
Section intitulée « Visualiser le graphe »L'option --all est indispensable : sans elle, git log n'affiche que
l'historique de la branche courante et la divergence reste invisible.
Avec elle, les branches locales et les branches de suivi distantes
apparaissent dans le même graphe.
git log --oneline --graph --allCela montre clairement où les branches ont divergé.
Cas spécial : push rejeté après un rebase local
Section intitulée « Cas spécial : push rejeté après un rebase local »Si vous avez rebasé votre feature branch sur main localement, les SHA
de vos commits ont changé. Le push sera rejeté car le remote a les
anciens SHA :
# Vous avez fait :git switch feature/logingit rebase main
# Le push est rejeté :git push# ! [rejected] feature/login -> feature/login (non-fast-forward)Solution, c'est un cas légitime pour --force-with-lease (si vous
êtes seul sur cette branche) :
git push --force-with-leaseDépannage : problèmes courants
Section intitulée « Dépannage : problèmes courants »Tous les rejets ne viennent pas d'une divergence. Une branche de suivi absente, une mauvaise branche courante ou un push vers une référence distante qui n'existe pas produisent des messages voisins mais appellent des corrections différentes. Repérez d'abord le symptôme exact avant d'appliquer une commande de réécriture.
| Symptôme | Cause probable | Solution |
|---|---|---|
rejected après pull --rebase | Conflit non résolu pendant le rebase | git rebase --continue après résolution, ou git rebase --abort |
--force-with-lease aussi rejeté | Quelqu'un a poussé depuis votre dernier fetch | git fetch puis réessayez, ou faites pull --rebase |
| Push rejeté sur une branche que personne ne touche | Votre tracking branch est mal configurée | git push -u origin feature/login |
everything up-to-date mais les commits manquent | Vous n'êtes pas sur la bonne branche | Vérifiez git branch et git log --oneline -5 |
| Push OK mais les commits n'apparaissent pas sur GitHub | Poussé sur la mauvaise branche distante | git push origin feature/login:feature/login |
À retenir
Section intitulée « À retenir »- Push rejeté = le remote a avancé, c'est normal en équipe
git pull --rebaserésout 90% des cas en gardant un historique linéaire--force-with-leaseest le force push sécurisé, uniquement après un amend/rebase sur une branche personnelle- Jamais
--forcesur une branche partagée pull.rebase = trueévite d'avoir à taper--rebaseà chaque fois- Diagnostiquez avec
git log --left-right HEAD...origin/mainavant d'agir dans le doute