261 lines
11 KiB
Markdown
261 lines
11 KiB
Markdown
+++
|
|
title = "Git Workflow"
|
|
description = "Git est un outil de versionnage très utile, voici comment je m'en sers."
|
|
date = 2021-05-10
|
|
|
|
[taxonomies]
|
|
tags = ["git", "workflow"]
|
|
+++
|
|
|
|
Git est un outil de versionnage très utile, voici comment je m'en sers.
|
|
|
|
<!-- more -->
|
|
|
|
> À 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).
|
|
|
|
[TL;DR](#tldr)
|
|
|
|
## 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), et sinon de faire automatiquement un rebase :
|
|
|
|
```bash
|
|
git config pull.rebase true
|
|
```
|
|
|
|
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)) :
|
|
|
|
```conf
|
|
[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 :
|
|
|
|
```bash
|
|
git log --all --decorate --oneline --graph
|
|
```
|
|
|
|
Pour avoir des messages de commits unifiés et cohérents avec de jolis alias :
|
|
|
|
```conf
|
|
fix = "!f() { git commit -sm \"fix: $1\"; }; f"
|
|
feat = "!f() { git commit -sm \"feat: $1\"; }; f"
|
|
misc = "!f() { git commit -sm \"misc: $1\"; }; f"
|
|
wip = "!f() { git commit -sm \"wip: $1\"; }; f"
|
|
```
|
|
|
|
## 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 :
|
|
|
|
```bash
|
|
git checkout -b nom_de_la_nouvelle_branche
|
|
```
|
|
|
|
Et pour basculer de branche en branche :
|
|
|
|
```bash
|
|
git checkout nom_de_la_branche
|
|
```
|
|
|
|
Si vous ne savez pas sur quelle branche vous vous trouvez, vous pouvez faire :
|
|
|
|
```bash
|
|
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](images/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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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 {#tldr}
|
|
|
|
On commence par demander à git de ne pull que s'il n'y a pas de divergence avec
|
|
la version distante de notre branche, et sinon, de rebase notre branche automatiquement :
|
|
|
|
```bash
|
|
git config pull.rebase true
|
|
```
|
|
|
|
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 profondeur) : [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).
|