Git avancé : branches, Pull Requests et résolution de conflits de fusion
- Comprendre les branches - la métaphore de la photocopie
- Créer et naviguer entre les branches
- Le workflow Git Flow et Feature Branch
- Travailler en parallèle sur plusieurs fonctionnalités
- Pousser une branche et ouvrir une Pull Request sur GitHub
- Relire et merger une Pull Request
- Comprendre et résoudre les conflits de fusion
- Stratégies avancées : rebase, squash, stash
- Cheatsheet - toutes les commandes de branches
1. Comprendre les branches - la métaphore du cahier brouillon
Imaginez votre projet comme un document officiel. main est l'original (propre, stable, toujours fonctionnel). Créer une branche, c'est photocopier ce document sur un cahier brouillon : vous pouvez rayer, corriger, expérimenter librement. L'original reste intact. Quand vous êtes satisfait, vous recopiez vos modifications sur l'original : c'est la fusion (merge).
En pratique, une branche Git est simplement un pointeur vers un commit. Elle ne copie pas les fichiers (elle est légère, instantanée et ne coûte presque rien à créer).
2. Créer et naviguer entre les branches
Voici les commandes fondamentales pour gérer les branches au quotidien :
# Voir toutes les branches (locale + distantes)
git branch -a
# Créer une nouvelle branche (sans basculer dessus)
git branch feat/page-contact
# Créer ET basculer sur la nouvelle branche (la façon la plus courante)
git checkout -b feat/page-contact
# Syntaxe moderne équivalente (Git 2.23+)
git switch -c feat/page-contact
# Basculer sur une branche existante
git checkout main
git switch main
# Voir sur quelle branche on est
git branch --show-current
# Supprimer une branche locale (après fusion)
git branch -d feat/page-contact
# Forcer la suppression (branche non mergée)
git branch -D feat/abandonnee
Vérifier son état avant de changer de branche
Avant de faire git switch, assurez-vous que votre working directory est propre. Des modifications non committées peuvent être emportées d'une branche à l'autre, ce qui crée de la confusion :
git status # doit afficher "nothing to commit, working tree clean"
git switch main
Si vous avez des modifications en cours non prêtes pour un commit, utilisez git stash pour les mettre de côté temporairement (voir section 8).
3. Le workflow Feature Branch - la règle d'or
Le Feature Branch Workflow est simple : une fonctionnalité = une branche. Vous ne commitez jamais directement sur main. Chaque modification passe par une branche dédiée et une Pull Request. Ce workflow fonctionne aussi bien seul qu'en équipe.
Ce cycle garantit que main ne contient que du code relu et validé. Chaque branche représente une unité de travail isolée et traçable dans l'historique Git.
4. Travailler en parallèle sur plusieurs fonctionnalités
Voici un scénario concret : vous développez simultanément la page de contact et le système d'authentification de votre blog Django. Ce sont deux fonctionnalités indépendantes (elles méritent deux branches distinctes).
Préparer les deux branches depuis main
# Partir d'un main à jour
git switch main
git pull
# Branche 1 : système de tags pour les articles
git switch -c feat/tags
# ... développez les modèles, vues, templates pour les tags ...
git add .
git commit -m "feat: ajout du modèle Tag avec slug auto"
git add .
git commit -m "feat: filtre des articles par tag dans les vues"
# Revenir sur main pour créer la 2ème branche
git switch main
# Branche 2 : authentification utilisateur
git switch -c feat/auth
# ... développez l'inscription, connexion, déconnexion ...
git add .
git commit -m "feat: formulaire d'inscription avec validation email"
git add .
git commit -m "feat: vue de connexion et gestion des sessions"
Basculer entre les branches en cours de travail
# Voir l'état de toutes vos branches
git branch -v
# * feat/auth a3f9c12 feat: vue de connexion et gestion des sessions
# feat/tags 7b2e891 feat: filtre des articles par tag dans les vues
# main f1d4a20 init: initialisation du projet Django
# Basculer sur feat/tags pour y continuer le travail
git switch feat/tags
# ... ajoutez un template pour la page de liste par tag ...
git commit -m "feat: template liste-par-tag.html avec pagination"
# Revenir sur feat/auth
git switch feat/auth
Garder sa branche à jour avec main
Si d'autres commits sont arrivés sur main pendant que vous travailliez sur votre branche (ex. un collègue a mergé une PR), mettez votre branche à jour pour éviter un conflit massif à la fusion :
# Depuis votre branche feat/auth
git switch feat/auth
# Option 1 : merge de main dans votre branche (crée un commit de merge)
git merge main
# Option 2 : rebase sur main (historique plus linéaire - voir section 8)
git rebase main
5. Pousser une branche et ouvrir une Pull Request sur GitHub
Une fois votre fonctionnalité prête, poussez la branche sur GitHub et ouvrez une Pull Request (PR) : c'est la demande formelle de fusionner votre travail dans main.
Pousser la branche sur GitHub
# Pousser la branche et la lier au dépôt distant (-u crée le tracking)
git push -u origin feat/tags
# Les pushes suivants sur cette branche se résument à :
git push
Ouvrir la Pull Request sur GitHub
Allez sur votre dépôt GitHub. Une bannière jaune apparaît : "feat/tags had recent pushes - Compare & pull request". Cliquez dessus.
Remplissez la Pull Request :
- Titre : court et descriptif (ex. "Ajout du système de tags pour les articles")
- Description : expliquez ce que fait la PR, pourquoi ces choix techniques, les tests effectués. Plus c'est détaillé, plus la relecture est rapide.
- Reviewers : assignez un ou plusieurs relecteurs si vous travaillez en équipe
- Labels : feature, bug, documentation… pour organiser les PRs
Cliquez sur "Create pull request". GitHub affiche automatiquement le diff de tous vos commits et vérifie s'il y a des conflits avec main.
Mettre à jour une PR après relecture
Un relecteur a laissé des commentaires sur votre PR ? Pas besoin d'en ouvrir une nouvelle. Commitez et poussez simplement sur la même branche (GitHub met la PR à jour automatiquement) :
# Après avoir pris en compte les retours
git add blog/templates/blog/liste-par-tag.html
git commit -m "fix: correction de l'affichage des tags vides suite à relecture"
git push
# La Pull Request sur GitHub se met à jour instantanément
6. Relire et merger une Pull Request
Si vous êtes le relecteur (ou que vous mergez votre propre PR après validation), voici le processus sur GitHub et en ligne de commande.
Relire sur GitHub
Dans l'onglet Files changed de la PR, vous pouvez commenter ligne par ligne. Trois options de review :
Choisir la stratégie de merge
GitHub propose trois boutons au moment de merger. Le choix impacte l'historique Git :
Nettoyer après le merge
# Après avoir mergé la PR sur GitHub, mettre à jour main localement
git switch main
git pull
# Supprimer la branche locale (elle n'est plus utile)
git branch -d feat/tags
# Supprimer la branche distante sur GitHub (si pas fait automatiquement)
git push origin --delete feat/tags
# Voir les branches distantes supprimées qui traînent encore en local
git remote prune origin
7. Comprendre et résoudre les conflits de fusion
Un conflit survient quand Git ne peut pas fusionner automatiquement deux branches parce que les deux ont modifié le même endroit du même fichier de manière différente. Ce n'est pas une erreur — c'est Git qui vous dit : "j'ai deux versions, dis-moi laquelle garder."
Reproduire un conflit — scénario concret
Imaginons que vous et un collègue avez tous les deux modifié la fonction liste_articles dans blog/views.py sur des branches différentes :
def liste_articles(request):
articles = Article.publies.all()
paginator = Paginator(articles, 10)
page = request.GET.get('page')
articles = paginator.get_page(page)
return render(request,
'blog/liste.html',
{'articles': articles})def liste_articles(request):
tag = request.GET.get('tag')
articles = Article.publies.all()
if tag:
articles = articles.filter(
tags__slug=tag)
return render(request,
'blog/liste.html',
{'articles': articles})Quand Git tente de fusionner ces deux branches dans main, il génère des marqueurs de conflit dans le fichier :
<<<<<<< HEAD (votre version)
def liste_articles(request):
articles = Article.publies.all()
paginator = Paginator(articles, 10)
page = request.GET.get('page')
articles = paginator.get_page(page)
return render(request, 'blog/liste.html', {'articles': articles})
======= (séparateur)
def liste_articles(request):
tag = request.GET.get('tag')
articles = Article.publies.all()
if tag:
articles = articles.filter(tags__slug=tag)
return render(request, 'blog/liste.html', {'articles': articles})
>>>>>>> feat/filtres (leur version)
Décoder les marqueurs de conflit
Résoudre le conflit manuellement
La résolution consiste à supprimer les marqueurs et à écrire la version finale que vous souhaitez conserver. Ici, la bonne solution est de combiner les deux fonctionnalités — pagination ET filtrage par tag :
# blog/views.py — version résolue, combinant les deux apports
from django.core.paginator import Paginator
def liste_articles(request):
tag = request.GET.get('tag')
articles = Article.publies.all()
# Filtre par tag (apport de feat/filtres)
if tag:
articles = articles.filter(tags__slug=tag)
# Pagination (apport de feat/pagination)
paginator = Paginator(articles, 10)
page = request.GET.get('page')
articles = paginator.get_page(page)
return render(request, 'blog/liste.html', {
'articles': articles,
'tag_actif': tag,
})
Finaliser la résolution
# 1. Vérifier qu'il ne reste plus de marqueurs de conflit
git diff
# 2. Ajouter le fichier résolu au staging
git add blog/views.py
# 3. Vérifier l'état — tous les conflits doivent être résolus
git status
# 4. Terminer le merge avec un commit
git commit -m "merge: résolution du conflit pagination + filtres dans liste_articles"
# 5. Pousser
git push
Utiliser un outil visuel pour résoudre les conflits
Pour les conflits complexes (plusieurs zones dans un fichier, ou plusieurs fichiers), un outil graphique est très utile. VS Code gère nativement les conflits avec des boutons "Accept Current", "Accept Incoming", "Accept Both" :
# Ouvrir VS Code sur les fichiers en conflit
code .
# Ou configurer un outil de merge dédié (ex. vimdiff)
git config --global merge.tool vimdiff
git mergetool
Annuler un merge qui tourne mal
# Annuler un merge en cours (avant de committer)
git merge --abort
# Annuler un merge déjà committé (crée un commit d'annulation)
git revert -m 1 HEAD
8. Stratégies avancées : rebase, squash et stash
git rebase — réécrire l'historique pour le rendre linéaire
Le rebase déplace vos commits comme s'ils avaient été créés à partir de la dernière version de main. Le résultat est un historique linéaire et propre, sans commits de merge parasites.
# Depuis votre branche feat/auth, intégrer les nouveaux commits de main
git switch feat/auth
git rebase main
# En cas de conflit pendant le rebase :
# 1. Résoudre le conflit dans le fichier
# 2. git add fichier-resolu.py
# 3. git rebase --continue
# Pour annuler le rebase :
# git rebase --abort
git stash — mettre des modifications de côté
Vous êtes en plein milieu d'une modification et on vous demande de corriger un bug urgent sur main. git stash met vos modifications de côté comme dans une pile, vous laissant repartir d'un working directory propre :
# Mettre les modifications en cours de côté
git stash push -m "WIP: ajout pagination article"
# Vérifier la pile de stashs
git stash list
# stash@{0}: On feat/tags: WIP: ajout pagination article
# Basculer sur main, corriger, committer, revenir
git switch main
git switch -c fix/bug-urgent
# ... correction ...
git commit -m "fix: correction affichage date article"
git switch main
git merge fix/bug-urgent
git push
# Revenir sur feat/tags et récupérer le stash
git switch feat/tags
git stash pop # applique le dernier stash et le supprime de la pile
git commit --amend — corriger le dernier commit
# Vous venez de committer mais vous avez oublié un fichier
git add blog/templates/blog/detail.html
git commit --amend --no-edit # ajoute le fichier au dernier commit sans changer le message
# Corriger seulement le message du dernier commit
git commit --amend -m "feat: vue détail article avec meta SEO"
9. Cheatsheet — toutes les commandes de branches
Conclusion : les branches, le vrai superpouvoir de Git
Vous savez maintenant créer des branches pour chaque fonctionnalité, travailler en parallèle sans vous bloquer mutuellement, soumettre votre travail via des Pull Requests documentées, et résoudre les conflits de fusion avec méthode. Ces compétences transforment radicalement la façon de travailler — seul comme en équipe.
La prochaine étape logique de cette série est le déploiement en production : configurer PostgreSQL à la place de SQLite, installer Gunicorn comme serveur WSGI, et servir votre blog Django avec Nginx sur un serveur Linux. Ce sera l'objet du prochain article.