blog/src/git_flow.md

243 lines
11 KiB
Markdown
Raw Normal View History

# Git Workflow
> À noter que ce guide est destiné aux personnes qui ont déjà utilisé git auparavant,
> il ne s'agit pas ici d'un tutoriel pour débutants (bien que je sois ici assez
> exhaustif dans mes explications).
Git est un outil de versionnage très utile, voici comment je m'en sers.
[TL;DR](#TL;DR)
## Les fondations
Vous êtes bien installé devant votre écran, les doigts ne demandent qu'à
furieusement taper les touches de votre clavier, mais avant le top départ, il faut
préparer le terrain ! Voici quelques commandes **primordiales** (pour ne pas dire
**obligatoires**) à réaliser avant de se lancer dans un projet.
Premièrement, je n'aime pas la manière qu'a git de faire des commits de fusion
lors d'un pull qui se passe mal (divergence entre votre version locale et distante
par exemple, aka. vous avez committé et quelqu'un d'autre aussi en même temps).
Nous allons donc demander à git de ne pull que lorsque l'historique est linéaire
(fast-forward) :
```git
git config pull.ff only
```
En cas de divergence, un simple `git rebase origin/branche_concernée` vous sortira
d'affaire !
Ensuite, il nous arrive de vouloir avoir une vue plus graphique de notre historique
git. Voici un alias, à placer dans votre `~/.gitconfig`, qui vous permet d'afficher
un graphique de vos branches et vos commits de manière plus visuelle que le `git log`
de base de git (honteusement volés [ici](https://stackoverflow.com/a/9074343)) :
```git
[alias]
lg1 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
lg2 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all
lg = !"git lg1"
```
Si vous n'avez pas besoin d'autant de décorations, ou que vous ne pouvez pas définir
d'alias, cette commande fait quelque chose d'assez similaire :
```git
git log --all --decorate --oneline --graph
```
## Des branches, des branches, et encore des branches
Quand on travaille à plusieurs sur git, il devient presque obligatoire d'avoir
des branches pour éviter de se sentir "à l'étroit" dans son `repo`. Pour pouvoir
collaborer et avancer sur un projet en groupe sans se marcher sur les pieds les
uns des autres, les branches deviennent l'outil indispensable de git.
J'utilise les branches de la manière suivante :
- les branches ont une durée de vie **très courte**
- une fois que la branche a rempli son rôle et qu'elle a été fusionnée, elle doit être supprimée
- une branche par fonctionnalité ou par résolution de problème
- les noms des branches sont libres *tant qu'ils sont clairs*
- c'est mieux d'avoir comme nom la fonctionnalité développée ou son rôle
- lorsqu'une branche sert à développer une résolution de bug ou un `fix`, il est mieux de l'inclure dans le nom
- lorsque vous travaillez à plusieurs sur la même fonctionnalité, il est conseillé de
faire une branche par personne en partant de la branche princpale de cette fonctionnalité
Évidemment, une branche fait exception : `master` (ou `main`, peu importe),
qui est une version "propre", "stable", et qui doit présenter le projet dans un état
fonctionnel, tel qu'il serait montré à un client ou à un responsable.
Il est possible d'avoir une seconde branche sur les projets plus importants, qui se
nomme alors `develop` et qui est la version en développement actif d'un projet,
mais qui ne peut pas être fusionnée sur la branche princpale pour des raisons de
stabilité par exemple, ou qui n'est pas prête pour la mise en production.
Pour créer une branche fille à partir de la branche sur laquelle vous êtes :
```git
git checkout -b nom_de_la_nouvelle_branche
```
Et pour basculer de branche en branche :
```git
git checkout nom_de_la_branche
```
Si vous ne savez pas sur quelle branche vous vous trouvez, vous pouvez faire :
```git
git branch
```
## Rebaser, c'est la clef
> Cette partie peut être ignorée si vous ne pouvez pas réaliser de `push force`
> (ou équivalent). En effet, un `rebase` modifie fondamentalement l'historique de
> votre git. Si vous n'êtes pas sûr de vous, n'en faites pas. Je ne suis pas
> responsable si vous perdez des données.
Votre fonctionnalité est développée et testée, vous voulez la fusionner avec votre
branche parente ? Vous voulez récupérer ce qui est sur votre branche parente et
que vous aimeriez récupérer sur la votre ? Il est temps de rebase !
Lorsque l'on crée une branche, celle-ci a un `commit` de "départ", depuis lequel
elle se *base* pour faire du versionnage en parallèle des autres branches. Parfois
il est nécessaire de récupérer du travail qui a été mis sur la branche mère dans
notre branche de travail, par exemple la résolution d'un bogue handicapant.
Un `rebase` va faire changer votre commit de départ et l'échanger avec un autre.
Prenons le schéma suivant :
![Schéma d'un rebase](assets/images/git_flow/git_rebase.svg)
Nous avons ici la branche `new_feature` qui part d'un commit de master. Disons
que vous êtes en train de développer une fonctionnalité importante, et qu'un bug
vous ralentit dans l'exécution de vos tests, mais qu'il a été réglé après la création
de votre branche, et qu'il est disponible sur `master`. Pour le récupérer sur votre
branche, vous n'avez qu'à `rebase` votre branche sur la dernière version de `master`.
La commande pour faire ceci serait alors :
```git
git rebase nom_de_branche_parente
```
Il est important de noter que vous risquez d'avoir des conflits au cours de cette
opération. En cas de conflit, vous pouvez les régler, puis faire `git add fichier_en_conflit`
et enfin `git rebase --continue`. Cela aura pour effet de **réécrire** l'historique
git de votre branche, d'où l'importance de faire un push en force, puisque votre
version locale et la/les version(s) distante(s) n'auront plus rien à voir.
### Quel rapport avec ce git flow
Comme dit plus haut, les branches ont une existence précise et bien définie dans
le temps. Une branche n'a pas a être fusionnée dans sa branche parente tant que
son contenu n'est pas complet, ou au moins utilisable. Cela ne fait pas vraiment
de sens de "polluer" une branche parente dans le seul but d'en récupérer le nouveau
contenu. Les opérations de rebase sont donc ici fréquentes.
## Fusion non préparée = fusion refusée
Votre fonctionnalité ou correctif est terminé(e), votre branche est prête à être
fusionnée ? En vérité, pas tout à fait. Il faut maintenant préparer la fusion !
Afin de faciliter au maximum la fusion de votre branche, il va falloir faire un
dernier rebase. Le but de ce rebase est ici de vérifier que le rôle de la branche
et du contenu qu'elle propose sont toujours fonctionnels et qu'aucun bogue n'a été
intruduit par les nouveautés disponibles sur la branche parente. Cela va aussi
vous permettre de gérer les conflits avec la branche mère au sein même de la votre.
Une fusion n'introduira donc pas de bogue, et vous restez pleinement responsable
de votre branche.
Enfin, vous n'êtes pas responsables de la fusion, un collègue ou responsable va
d'abord vérifier votre code et le contenu que vous apportez et approuver (ou rejeter)
la fusion que vous demandez. Grâce aux opérations de rebasage successifs que vous
avez fait durant la durée de vie de votre branche, la fusion se fera sans encombre.
À noter que si vous avez des outils tels que les [Merge Requests](https://docs.gitlab.com/ee/user/project/merge_requests/getting_started.html)
chez GitLab, ou [Pull Requests](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests)
chez GitHub, n'hésitez surtout pas à les utiliser ! Ces outils permettront de garder
un suivi des fusions, et d'expliquer son but à un responsable qui ne les a pas
forcément toutes en tête.
Pour fusionner une branche dans celle sur laquelle vous vous trouvez :
```git
git merge nom_de_la_branche_à_fusionner_dans_celle_sur_laquelle_vous_êtes
```
## Au revoir vieille branche
> Les opérations décrites ici peuvent entraîner une perte de code. Je ne suis pas
> responsable si vous supprimez une branche avant qu'elle n'ait été fusionnée.
> Ne supprimez une branche que si vous êtes sûr qu'elle n'a plus d'utilité.
Votre branche est désormais fusionnée ? C'est la fin pour elle, il est désormais
temps de lui dire au revoir !
Commençons d'abord par la supprimer sur le repo distant :
```git
git push -d origin nom_de_branche
```
Ici, le serveur distant s'appelle `origin`, comme c'est le cas la plupart du temps.
Vos collègues peuvent synchroniser leurs branches et faire disparaître leurs
références locales à des branches fantômes distantes avec la commande `git fetch -p` (le
`-p` étant ici pour "prune").
Enfin, il est temps de supprimer votre copie locale de cette branche :
```git
git branch -d nom_de_branche
```
À noter que le `-d` ne supprimera votre branche que si elle a été fusionnée. Il est
possible de forcer la suppression d'une branche locale non fusionnée avec l'option
`-D` qui équivaut à un `git --delete --force` sur la branche.
"Don't cry because it's over, smile because it happened." - *Dr. Seuss*
## TL;DR
On commence par demander à git de ne pull que s'il n'y a pas de divergence avec
la version distante de notre branche :
```git
git config pull.ff only
```
Ensuite on utilise une branche par fonctionnalité et par correctif. Une branche a
un rôle précis et une durée de vie très courte. Lorsque plusieurs développeurs
travaillent sur la même branche, ils ne doivent pas exclure la possibilité de créer
des branches personnelles.
`master` est en tous temps propre et présentable. Une branche intermédiaire peut
être utilisée pour éviter de polluer inutilement la branche principale. Elle pourra
s'appeler `develop` par exemple.
Les rebase sont fréquents et servent à récupérer sur une branche fille du nouveau
contenu de sa branche mère.
La fusion se déroule sans accroc, un rebase doit être fait avant pour gérer les
conflits sur la branches fille.
Une fois la branche fusionnée, elle doit être supprimée.
## Informations supplémentaires
### Vocabulaire
- `repo` : raccourci de `repository`, qui est l'environnement de travail de git,
c'est là dedans que l'on créé ses branches, et que l'on versionne son travail.
### Pour aller plus loin
- Le fonctionnement des objets, commits et arbres sur git (en profondeurn): [vidéo](https://youtu.be/MyvyqdQ3OjI).
- Le fonctionnement des branches sur git (en profondeur) : [vidéo](https://youtu.be/mhZQRBp8dXE).
- Comment git stocke son arborescence de fichiers : [article](https://book.git-scm.com/book/en/v2/Git-Internals-Git-Objects).
- Les commandes associées à `git branch` : [documentation](https://git-scm.com/docs/git-branch).