Chapitre 915 minDébutant
Pull request et revue de code sur GitHub
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- ouvrir une pull request claire ;
- faire et recevoir une revue de code ;
- choisir entre les trois façons de fusionner une pull request ;
- protéger la branche
maind'un dépôt.
4 exercices corrigés1 schéma
Une pull request (PR) est une demande d'intégration : tu proposes de fusionner une branche dans une autre, et ton équipe relit les modifications, les commente et les approuve avant la fusion. GitLab appelle la même chose une merge request. Ce chapitre explique comment ouvrir une pull request utile, comment faire et recevoir une revue de code (commentaires, suggestions, approbation), les trois façons de fusionner, la protection de la branche main, et les équivalences avec GitLab et Gitea.
Pourquoi une pull request
Fusionner ta branche dans main sur ton poste puis pousser fonctionne, mais personne n'a relu ton code avant qu'il arrive dans la version de référence. La pull request ajoute une étape :
- tu pousses ta branche ;
- tu ouvres une pull request qui dit « voici ce que je propose d'intégrer dans
main, et pourquoi » ; - un coéquipier relit, commente, demande des corrections ; tu les apportes en poussant de nouveaux commits sur la même branche, et la pull request se met à jour toute seule ;
- les tests automatiques passent (chapitre 12) ;
- le relecteur approuve, et la branche est fusionnée.
Tant que la pull request n'est pas fusionnée, main n'est pas touchée. On attrape les erreurs avant qu'elles arrivent dans la version de référence, toute l'équipe sait ce qui change, et la discussion reste attachée au code, consultable des années plus tard. Pour toi, c'est aussi une trace : une pull request relue et fusionnée prouve le travail en équipe et la démarche de validation.
Ouvrir une pull request
Après git push -u origin feature/export, GitHub affiche dans le terminal un lien direct, et sur la page du dépôt un bandeau Compare & pull request. Dans le formulaire :
- base est la branche qui reçoit (
main), compare la branche qui propose (feature/export) ; - le titre dit ce que fait la pull request, comme un bon message de commit : « Ajoute l'export CSV des notes de frais » ;
- la description donne le contexte et facilite la relecture.
Une bonne description répond à quatre questions :
## Pourquoi
Les comptables ressaisissent les notes dans leur tableur. Ticket #18.
## Ce qui change
- Nouvelle route `GET /notes/export?mois=2026-10` qui renvoie un CSV
- Bouton « Exporter » sur la page des notes validées
- Les montants sont au format français (virgule décimale, séparateur `;`)
## Comment tester
1. Se connecter avec le compte compta1
2. Page « Notes validées », bouton « Exporter »
3. Ouvrir le fichier dans un tableur : 14 lignes pour octobre 2026
## Points d'attention
Les notes refusées sont exclues de l'export : à confirmer avec la comptabilité.
L'écriture Closes #18 (ou Fixes #18, Resolves #18) dans la description relie la pull request à l'issue 18 : à la fusion dans la branche par défaut, l'issue est fermée automatiquement.
Dans la colonne de droite, tu désignes les relecteurs (Reviewers), la personne responsable (Assignees), des étiquettes (Labels) et l'éventuel jalon. Si ton travail n'est pas prêt mais que tu veux un premier avis, ouvre la pull request en brouillon (Create draft pull request) : elle ne peut pas être fusionnée tant que tu ne la marques pas Ready for review.
Avec GitHub CLI, tout se fait depuis le terminal :
gh pr create --base main --title "Ajoute l'export CSV des notes de frais" --body-file description.md --reviewer hugo-dupre
gh pr list # les pull requests ouvertes
gh pr checkout 23 # récupère la branche de la PR 23 pour la tester
gh pr view 23 --web # l'ouvre dans le navigateur
La revue de code
La revue de code (code review) consiste à relire le code d'un autre avant son intégration. L'onglet Files changed d'une pull request affiche toutes les modifications. Le relecteur peut :
- commenter une ligne ou un groupe de lignes, en cliquant sur le + dans la marge ;
- proposer une modification exacte avec le bouton suggestion, qui insère un bloc
```suggestiondans le commentaire : l'auteur l'applique en un clic avec Commit suggestion, sans ouvrir son éditeur ; - cocher Viewed sur les fichiers déjà relus, pour suivre son avancement.
Un exemple de suggestion :
Le séparateur décimal doit suivre la locale française pour que le tableur lise les montants.
```suggestion
const montant = note.montant.toFixed(2).replace('.', ',');
```
Les commentaires d'une revue s'envoient ensemble, avec le bouton Review changes et l'une des trois conclusions :
| Conclusion | Signification |
|---|---|
| Comment | Des remarques, sans se prononcer sur la fusion |
| Approve | Le code peut être fusionné |
| Request changes | Des corrections sont nécessaires avant la fusion |
L'auteur répond à chaque commentaire, corrige en poussant de nouveaux commits sur la branche, puis marque la conversation comme résolue (Resolve conversation) et redemande une relecture.
Ce que le relecteur vérifie, dans l'ordre :
- Le besoin. La pull request fait-elle ce que demande le ticket, et seulement ça ?
- La justesse. Cas limites (liste vide, montant nul, date au dernier jour du mois), gestion des erreurs.
- La sécurité. Requêtes préparées, droits vérifiés côté serveur, aucun secret dans le code (cours de sécurité des applications).
- Les tests. Le comportement nouveau est-il testé, les tests existants passent-ils ?
- La lisibilité. Noms clairs, fonctions courtes, pas de code mort ni de
console.logoublié.
Comment commenter. On commente le code, pas la personne. Une question ouvre la discussion mieux qu'un ordre : « Que se passe-t-il si la liste est vide ? » plutôt que « Ça plante ». Distingue ce qui bloque de ce qui est un avis : beaucoup d'équipes préfixent les remarques facultatives par « Suggestion : » ou « Détail : ». Et dis aussi ce qui est bien fait.
Comment recevoir une revue. Une remarque sur ton code n'est pas une remarque sur toi. Réponds à chaque commentaire, même par « Corrigé dans 4e1f0a2 ». Si tu n'es pas d'accord, explique pourquoi : la revue est une discussion.
Les trois façons de fusionner une pull request
Le bouton vert de la pull request propose trois variantes, que l'administrateur du dépôt peut restreindre dans Settings :
| Bouton | Ce qu'il fait | Historique de main |
|---|---|---|
| Create a merge commit | Fusionne avec un commit de fusion (--no-ff) | Tous les commits de la branche, plus un commit Merge pull request #23 from … |
| Squash and merge | Regroupe tous les commits de la branche en un seul | Un commit par pull request, au titre de la PR |
| Rebase and merge | Rejoue chaque commit de la branche au-dessus de main, sans commit de fusion | Linéaire, tous les commits de la branche, avec de nouvelles empreintes |
Squash and merge est souvent le bon choix pour une petite équipe : tes commits intermédiaires (« wip », « corrige la revue ») disparaissent, et main contient un commit clair par fonctionnalité, facile à annuler avec git revert. Create a merge commit garde le détail des étapes. L'essentiel est que l'équipe choisisse une méthode et s'y tienne.
Après la fusion, clique sur Delete branch, et en local :
git switch main
git pull
git branch -d feature/export # -D si la PR a été fusionnée par squash ou rebase
git fetch --prune # oublie les branches supprimées sur le serveur
Après un Squash and merge ou un Rebase and merge, les commits de ta branche n'existent pas tels quels dans main : git branch -d croit la branche non fusionnée et refuse. Utilise -D après avoir vérifié que la pull request est bien fusionnée.
Protéger la branche main
Une règle orale (« on ne pousse pas sur main ») finit toujours par être oubliée un soir de rendu. GitHub permet de l'imposer. Dans Settings, Rules, Rulesets (ou dans les règles de protection de branche classiques, Branches), crée une règle pour la branche main :
| Règle | Effet |
|---|---|
| Require a pull request before merging | Le push direct sur main est refusé ; tout passe par une pull request |
| Required approvals : 1 | Il faut au moins une approbation pour fusionner |
| Dismiss stale approvals when new commits are pushed | Un nouveau commit après l'approbation exige une nouvelle relecture |
| Require status checks to pass | Les tests de l'intégration continue doivent être verts avant la fusion |
| Require conversation resolution | Tous les commentaires de la revue doivent être résolus |
| Block force pushes | Personne ne peut réécrire l'historique de main |
| Restrict deletions | Personne ne peut supprimer main |
Un push direct est alors refusé :
remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: Changes must be made through a pull request.
! [remote rejected] main -> main (protected branch hook declined)
Avec l'offre gratuite de GitHub, ces règles s'appliquent aux dépôts publics ; pour un dépôt privé, il faut GitHub Pro (gratuit avec le pack étudiant) ou un abonnement d'organisation. Un fichier CODEOWNERS, placé dans .github/, désigne automatiquement les relecteurs selon les fichiers modifiés : /api/ @lina-morel demande la relecture de Lina pour toute modification du dossier api/.
Équivalences avec GitLab et Gitea
En stage, tu travailleras peut-être sur GitLab, souvent installé sur les serveurs de l'entreprise, ou sur Gitea et Forgejo, légers et libres. Les commandes Git ne changent pas ; seuls les noms des fonctions de la forge diffèrent.
| GitHub | GitLab | Gitea, Forgejo |
|---|---|---|
| Pull request (PR) | Merge request (MR) | Pull request |
| Draft pull request | Draft merge request | Pull request préfixée WIP: ou brouillon |
| Issue | Issue | Issue |
| Projects (tableau) | Issue boards | Projects |
GitHub Actions (.github/workflows/*.yml) | GitLab CI/CD (.gitlab-ci.yml) | Gitea Actions, Forgejo Actions : syntaxe proche de celle de GitHub Actions |
| Runner hébergé par GitHub | Runner partagé de GitLab.com ou runner de l'entreprise | Runner (act_runner) à installer soi-même |
| Branch protection, rulesets | Protected branches, approval rules | Branch protection |
| Organization | Group | Organization |
| Fork | Fork | Fork |
| GitHub Pages | GitLab Pages | Pas d'équivalent intégré |
gh (GitHub CLI) | glab | tea |
Une merge request GitLab fonctionne exactement comme une pull request : branche, description, revue, approbation, fusion. GitLab propose les mêmes options de fusion, dont le squash.
Exercices
Exercice 1 sur 4 Rédiger une pull request Réussi À revoir
Hugo a terminé la branche fix/12-filtre-date : la liste des notes filtrées par mois oubliait les notes du dernier jour du mois. La cause : la requête comparait la date à 2026-10-31 00:00:00. Il a corrigé la requête et ajouté un test. Rédige le titre et la description de sa pull request.
Voir la réponseMasquer la réponse de l'exercice 1
Une réponse possible :
Titre : Corrige le filtre par mois qui oubliait le dernier jour
## Pourquoi
Les notes du 31 octobre n'apparaissaient pas dans la liste d'octobre. Closes #12.
## Ce qui change
- La requête compare la date à `date < premier jour du mois suivant` au lieu de `date <= dernier jour à 00:00:00`
- Un test couvre une note datée du dernier jour du mois à 18 h
## Comment tester
1. `npm test` : le nouveau test passe
2. Créer une note datée du 31/10/2026 à 18:00, filtrer sur octobre 2026 : elle apparaît
## Points d'attention
Même correction à prévoir sur l'export CSV, qui utilise la même requête (ticket à créer).
Ce qui compte : un titre qui dit ce que fait la correction, la cause, le lien Closes #12, une procédure de test reproductible.
Tu avais trouvé ?
Exercice 2 sur 4 Faire une revue Réussi À revoir
Voici un extrait de la pull request de Lina :
app.delete('/notes/:id', async (req, res) => {
console.log('suppression', req.params.id);
await db.query(`DELETE FROM note WHERE id = ${req.params.id}`);
res.send('ok');
});
Écris trois commentaires de revue, en indiquant pour chacun s'il bloque la fusion.
Voir la réponseMasquer la réponse de l'exercice 2
- Bloquant, sécurité. « L'identifiant est collé dans la requête SQL : une injection SQL est possible. Peux-tu passer par une requête préparée ? » Suggestion :
await db.execute('DELETE FROM note WHERE id = ?', [req.params.id]); - Bloquant, droits. « N'importe quel utilisateur connecté peut supprimer n'importe quelle note. Il faudrait vérifier que la note appartient à l'utilisateur, ou qu'il a le rôle comptable, et répondre 403 sinon. »
- Non bloquant. « Suggestion : retirer le
console.log, et répondre204 No Content(ou404si aucune ligne n'a été supprimée) plutôt que le texteok, pour suivre les conventions REST. »
Tu avais trouvé ?
Exercice 3 sur 4 Quel bouton de fusion ? Réussi À revoir
La branche feature/export contient 9 commits : 3 commits qui racontent les étapes (« Ajoute la route d'export », « Ajoute le bouton », « Ajoute les tests de l'export ») et 6 commits de correction de revue (« corrige la revue », « oups »…). Pour chaque bouton, combien de commits ajoute la fusion à l'historique de main ? main a avancé depuis la création de la branche.
- Create a merge commit
- Squash and merge
- Rebase and merge
Voir la réponseMasquer la réponse de l'exercice 3
- 10 : les 9 commits de la branche, plus le commit de fusion.
- 1 : un seul commit qui regroupe toutes les modifications.
- 9 : les 9 commits, rejoués au-dessus de
main, avec de nouvelles empreintes, sans commit de fusion.
Le meilleur résultat serait de nettoyer la branche avant la fusion avec un rebase interactif (les 6 corrections fusionnées dans les 3 commits d'étapes), puis Rebase and merge ou Create a merge commit. Sans nettoyage, Squash and merge donne un historique propre.
Tu avais trouvé ?
Exercice 4 sur 4 Protéger main Réussi À revoir
L'équipe de Sorbier Frais veut que : personne ne pousse directement sur main ; chaque modification soit approuvée par l'autre membre ; les tests passent avant toute fusion ; personne ne puisse réécrire l'historique de main. Le dépôt est privé, sur le compte personnel de Lina, qui a le pack étudiant. Quelles règles active-t-elle, et pourquoi le pack étudiant compte-t-il ici ?
Voir la réponseMasquer la réponse de l'exercice 4
Sur la branche main : Require a pull request before merging avec 1 approbation requise (et de préférence Dismiss stale approvals), Require status checks to pass en sélectionnant le job de tests, Block force pushes (et Restrict deletions). Le pack étudiant donne GitHub Pro : sans lui, les règles de protection ne sont pas disponibles sur un dépôt privé d'un compte gratuit.
Tu avais trouvé ?