Les pull-request : comment ça marche, comment en faire une, comment en intégrer une ?

1

GitHub a popularisé le principe de pull-request et tous les autres outils de gestion de dépôt Git s’y sont mis : Bitbucket Cloud, Bitbucket Server (anciennement Stash), GitLab (sous le terme de merge-request).

Dans le principe c’est simple : pour contribuer à un projet sur l’une de ces plateformes :

  1. Forker le projet
  2. Créer une branche et travailler dessus
  3. Publier la branche sur son fork
  4. Créer la pull-request

Mais dans les faits, ça peut être un peu plus compliqué…

Nous allons voir étape par étape comment cela fonctionne et comment s’en servir au mieux.

Nous allons nous concentrer sur la situation de workflow triangulaire, c’est à dire de travail avec 3 dépôts :

  • Un dépôt de référence, conventionnellement appelé upstream
    C’est le dépôt du projet auquel nous voulons contribuer.
    Nous n’avons que les droits en lecture dessus.
  • Un dépôt de fork, conventionnellement référencé origin
    C’est une copie du dépôt de référence.
    Nous avons tous les droits dessus.
  • Un dépôt local
    C’est notre dépôt de travail.

Depuis la version 2.5, Git simplifie le travail avec ce type de workflow avec l’introduction de la référence <branch>@{push}.
Il existe déjà la référence <branch>@{upstream} qui permet de déterminer la branche distante traquée par une branche. Celle-ci est automatiquement définie lorsque vous faites un git checkout -b branch upstream_branch ou peut être explicitement définie par un git branch --set-upstream-to upstream_branch branch. Cette référence permet de déterminer la branche avec laquelle fusionner/rebaser lors d’un git pull. Elle permet aussi, si votre configuration push.default est à upstream, de déterminer la branche vers laquelle publier par défaut lors d’un git push.
Avec l’apparition de la référence <branch>@{push}, il est maintenant possible de mieux contrôler la branche vers laquelle publier par défaut. Celle-ci est configurée par les options de configuration :

  • remote.pushDefault (ou branch.<name>.pushRemote pour une branche spécifique) : pour indiquer le dépôt par défaut sur lequel publier
  • push.default : une valeur à current va provoquer une publication par défaut sur une branche portant le même nom que la branche courante dans le dépôt de publication

Il est possible de vérifier la valeur de ces références via les commandes suivantes (ici des valeurs dans un cas de workflow triangulaire) :

Dans notre exemple, nous allons apporter une contribution au logiciel ‘example’ publié sur http://git.example.com/org/example.
Ce dépôt contient 2 commits C1 et C2 et une branche par défaut master positionnée sur C2 :

Côté contributeur

Faire une contribution

Tout d’abord, il faut forker le projet.
Cette étape est simple : il suffit d’aller sur l’interface web du projet auquel nous voulons contribuer, en l’occurrence http://git.example.com/org/example, et de cliquer sur le bouton ‘Fork’ (c’est a priori le même sur GitHub, GitLab ou Bitbucket).
Cela va avoir pour effet de créer un clone du dépôt mais côté serveur.
Ce dépôt va alors être accessible à une URL du genre http://git.example.com/contributor/example.
Il nous appartient et nous avons tous les droits dessus.

Ensuite nous clonons localement notre dépôt :

Comme indiqué en introduction, nous allons configurer un peu pour publier par défaut sur notre fork origin :

Enfin, nous allons ajouter en remote le dépôt de référence avec le nom conventionnel upstream :

Voilà, nous pouvons enfin vraiment travailler ! Enfin presque…
Une bonne pratique est de toujours travailler sur une branche spécifique, jamais sur la branche cible de notre développement.

Ainsi nous allons travailler sur la branche contribution qui va automatiquement traquer la branche upstream/master.

Nous pouvons maintenant faire les modifications de code et commiter :

Publier et proposer une contribution

Avant publication, nous pouvons vérifier les commits que nous allons publier et où :

Pour publier ces modifications, il nous suffit alors simplement de faire un :

Ainsi, git va publier sur la branche configurée en publication ou @{push}, or, puisque nous avons défini les options remote.pushdefault=origin et push.default=current, c’est origin/contribution.

Enfin, il faut passer par l’interface web du projet pour créer la pull-request depuis votre branche contribution de votre fork vers la branche master du dépôt de référence.

Corriger une pull-request

Si vous avez des retours sur votre pull-request et des corrections à faire, il suffit de réitérer l’étape précédente.

Simplement corriger et commiter :

Puis publier :

La pull-request sera automatiquement mise à jour avec le(s) nouveau(x) commit(s).

Mettre à jour une pull-request

Il peut arriver que le dépôt de référence évolue pendant le temps de validation de votre pull-request, il vous faut alors la mettre à jour.

Se pose ici la question du merge vs. rebase mais je vais laisser d’autres répondre à ce débat, et opter pour ma recommandation dans ce cas : le rebase.
Vérifions ce que l’on va récupérer :

Et intégrons-le :

Cela va faire un rebase sur la dernière version de la branche traquée par votre branche locale ou @{upstream}, qui est upstream/master comme expliqué plus haut.

Et maintenant republions tout simplement.

Mais ça coince… Car la publication n’est pas fast-forward.
En effet, origin/contribution n’est pas un ancêtre de contribution.
Mais ce n’est pas grave, nous voulons justement écraser avec notre nouvelle version, donc nous allons forcer un peu, mais en s’assurant quand même que nous n’allons pas écraser quelque chose que nous ne connaîtrions pas (ce qui est peu probable dans le cas de notre fork personnel) :

La pull-request sera encore une fois automatiquement mise à jour.

Côté mainteneur

Voyons maintenant comment gérer les pull-requests qui nous sont soumises.

Vérifier une pull-request

Si la pull-request n’est pas en conflit et que nous pouvons nous contenter de lire la pull-request en ligne et que nous avons des tests automatisés, tant mieux.

Par contre, si vous avez besoin de la retravailler en local, les différents outils proposent des références spéciales pour récupérer les pull-requests :

Nous nous retrouvons alors avec une branche locale pull/{PR_NUMBER} sur laquelle nous pouvons travailler classiquement.

Intégrer une pull-request

Si tout se passe bien et que la pull-request peut être intégrée via l’interface web, parfait : simplement cliquer sur ‘fusionner’.
Mais parfois, si la branche cible a évolué de manière conflictuelle avec la pull-request (et que le contributeur ne peut la mettre à jour comme montré précédemment) ou que vous préférez simplement faire les choses vous-même, voici comment procéder.
Tout d’abord, récupérer la branche de la pull-request comme présenté dans le point précédent, ensuite, selon vos préférences, fusionner avec ou sans fast-forward, avec ou sans squashing :

Il ne reste plus qu’à publier :

La pull-request sera alors automatiquement fermée.

Conclusion

J’espère que vous comprenez un peu mieux comment fonctionne une pull-request et comment la manipuler de manière plus fine.

Partagez cet article.

A propos de l'auteur

Un commentaire

  1. Bonjour,

    Je voudrais vous soumettre un cas :
    j’ai dans mon upstream un dépôt provenant du split d’un dépôt monolithique. Celui-ci est donc en lecture seule mais je peux lui soumettre une contribution. Comment transférer la PR du dépôt splité au dépôt monolithique ?

    N’hésitez pas à me contacter si vous voulez echanger

Ajouter un commentaire