# Git et GitFlow

Git est notre outil de collaboration principal lors du développement. Nous ne développons rien sans l'utiliser. Ce guide va traiter comment nous l'utilisons.

# Sourcetree

Pour travailler avec Git, nous recommandons l'utilisation de Sourcetree (opens new window), un GUI qui nous permet d'exécuter les commandes de Git plus intuitivement.

# Configuration

# Bitbucket et Git

Puisque nous utilisons Bitbucket (opens new window) et Github (opens new window), il faut configurer leur authentification.

# Force push

Lors d'un rebase il devient parfois nécessaire d'effectuer un « force push ». Pour ce faire, il faut activer l'option dans les paramètres de Sourcetree.

# Git flow

Notre structure sur nos projets suit la méthode Git Flow. Atlassian a un tutoriel (opens new window) sur le sujet.

# Structure

Voici les différentes branches que l'on retrouve sur un projet qui suit Git Flow :

  • develop : branche de développement (staging). Ce qui se trouve sur cette branche est considéré comme un « work in progress ». Cette branche est synchronisée avec un environnement de staging afin de pouvoir faire des démos.
  • master : branche des livrables (production). Ce qui se trouve sur cette branche est considéré live. Cette branche est synchronisée avec un environnement de production.
  • feature/... : branches de tâche en cours. Chaque tâche de développement a une feature. Elle est basée sur develop.
  • release/... : branches de création de livrables. Branche créée pour marquer un point stable et livrable, puis être merged dans master suite à ses modifications. Elle est basée sur develop.
  • hotfix/... : branches de correction. Branche créée pour corriger la dernière release. Elle est basée sur master.

WARNING

Il faut éviter de commit directement sur develop et master. Tout changement devrait être validé lors d'une pull request.

# Procédure de développement

Voici le processus que l'on suit pour réaliser une tâche de développement à l'aide de Git.

# Créer une branche

Avant de nous mettre à coder, nous créons notre feature. Imaginions que nous sommes responsables d'une carte sur Trello nommée « Créer la navigation » numérotée #64. Le nom d'une feature est composé de trois éléments :

  1. feature/ : pour grouper les features ensemble;
  2. #64 : le numéro de la carte Trello correspondant à la tâche;
  3. _create_navigation : le nom de la tâche en snake_case.

Le nom de notre branche est donc feature/#64_create_navigation.

TIP

Tout texte écrit dans le code ou sur Git est en anglais.

# Commettre un changement

Ensuite, nous effectuons une partie de nos changements dans le code. Lorsqu'on veut commettre notre progrès, on stage les fichiers modifiés puis on crée un commit. Le message d'un commit est composé de trois éléments :

  1. [#64] : le même numéro de carte Trello utilisé pour la branche;
  2. Add navigation markup : une courte description du changement (72 caractères maximum)
  3. I had to refactor the home page to fit in the navigation component. : plus d'information liée au commit sur une autre ligne (optionnel). À la discrétion du développeur.

Le message de notre commit est donc :

[#64] Add navigation markup

I had to refactor the home page to fit in the navigation component.

Après avoir fait un ou plusieurs commits, nous pouvons push nos changements. Ceci nous permet de les partager ou simplement de s'assurer qu'ils sont sauvegardés.

# Créer un pull request

Une fois la branche poussée et prête à faire valider par d'autres développeurs, nous créons un pull request. Il s'agit d'un rapport de tous les changements effectués pour la feature. Il facilite la revue du code.

Lors de la création d'un pull request il faut :

  • choisir notre branche,
  • choisir develop comme la branche visée,
  • choisir les développeurs responsables comme reviewers,
  • s'assigner au pull request (Github),
  • prévoir la suppression de la branche lors du merge (Bitbucket).

Par la suite, on crée le pull request et on vérifie s'il y a des conflits. Si c'est le cas, il faut de les régler.

# Régler un conflit

Un conflit apparaît lorsque les mêmes lignes de code ont été modifiées par deux sources différentes. Pour régler un conflit, il faut mettre à jour sa branche avec les changements les plus récents de la branche visée, c'est-à-dire develop en temps normal.

# Méthode 1 : merge

La manière la plus simple consiste à merge la branche visée dans notre branche. Ainsi, on simule le merge qui sera effectué par la PR sans modifier la branche cible.

# Méthode 2 : rebase

Il est aussi possible de résoudre des conflits à l'aide d'un rebase. Par contre, cette manière n'est normalement pas recommandée. En effet, la résolution d'un conflit par un rebase va demander de régler les conflits pour chaque commit dans l'historique de la branche. Au final, ceci va multiplier le temps de résolution et va aussi introduire de la confusion si la branche a beaucoup changée entre les commits.

Toutefois, il est parfois nécessaire de faire un rebase malgré la présence de conflits. De plus les rebases ont d'autres utilités.

WARNING

Lors de la résolution, d'un conflit il faut s'assurer autant que possible que chaque changement est respecté. Il est parfois nécessaire de consulter d'autres développeurs pour s'informer du but de ces changements. Il peut être difficile de récupérer un changement perdu après la résolution d'un conflit.

# Faire un merge

Un merge est une fusion de deux branches dans l'historique de commits. Il va généralement créer un commit avec les changements d'une autre branche. Il est utile pour les mises à jours et les résolutions de conflits. De plus, il est habituellement sans risque.

# Procédure

Voici comment faire sur Sourcetree :

  1. Pull les changements les plus récents de la branche cible;
  2. Checkout notre branche;
  3. Faire un clic droit sur la branche cible;
  4. Choisir « Merge [branche cible] into [notre branche] ».

Une fois le merge entamé, Sourcetree va afficher une alerte s'il y a des conflits. Dans le « file status », on peut voir quels fichiers ont ces conflits. Il faut aller dans chacun de ces fichiers et résoudre les conflits en modifiant les lignes de codes problématiques. Ensuite, on peut enregistrer puis stage ces changements. Il ne reste qu'à commettre le merge. À ce point, le message de commit est déjà créé par Sourcetree et on n'y touche pas.

# Scénario

Nous sommes sur la feature feature/#64_create_navigation. Depuis le début du développement de notre branche, des feature ont été approuvées et fusionnées à develop. Avant de soumettre pour évaluation, nous voulons tester ces fonctionnalités dans le contexte de notre feature. Alors, nous récupérons les nouveaux changements et nous effectuons le merge vers notre branche.

Après le merge, nous pouvons démarrer l'environnement de développement et tester l'application avec les fonctionnalités les plus récentes.

# Faire un rebase

Un rebase consiste à réécrire l'historique des commits en les déplaçant sur une autre base. Voici les différentes utilités d'un rebase :

  • Mise à jour d'une branche sans la création d'un commit de merge;
  • Regroupement de commits dans le graphique de l'arbre de commit pour une meilleure lisibilité;
  • Développement d'une feature sans que sa base se trouve sur develop.

Par contre, un rebase vient avec certains aspect négatifs :

  • Possibilité de perdre du code si les bonnes pratiques ne sont pas respectées;
  • Crée de la confusion pour les développeurs qui ont déjà récupéré la branche modifiée;
  • Détruit les dates réelles des commits.

# Les règles à suivre

Afin de limiter les aspects destructeurs et la confusion qui viennent avec un rebase, il faut s'assurer de respecter les règles suivantes :

  • Il ne doit pas y avoir de merge dans les commits déplacés. Par exemple, sur Sourcetree il devrait n'y avoir qu'un seul trait visible pour ces commits et aucun croisement de branches.
  • Préférer le merge lorsque la branche se retrouve déjà sur origin.
  • Préférer le merge lorsque la branche a déjà été récupérée par un ou plusieurs développeurs surtout si nous ne sommes pas le responsable de la branche.

WARNING

Si on choisit tout de même de faire le rebase, il faut informer chaque développeur qui a travaillé sur la branche qu'on désire faire un rebase et demander leur approbation. Ainsi, on évite de perdre les changements locaux sur d'autres machines.

# Procédure

Voici comment faire un rebase sur Sourcetree :

  1. Pull les changements les plus récents de la branche cible;
  2. Checkout notre branche;
  3. Faire un clic droit sur la branche cible;
  4. Choisir « Rebase current changes onto [branche cible] »;

S'il y a des conflits, il faut les résoudre comme on le fait pour un merge, la différence étant qu'il peut y avoir des conflits pour plusieurs commit qu'il faudra répéter le processus à chaque fois.

Si la branche se rebasée se trouvait sur origin, après un rebase la version locale lui devient incompatible. C'est dû au fait qu'en réécrivant l'historique de commit, Git ne peut considère chaque commit différent que les originaux. Il faut alors écraser la branche remote. Pour ce faire, on peut faire un « force push ». Sur Sourcetree, il faut cocher cette case lors du push (voir la configuration). Sinon, on peut aussi supprimer la branche remote avant un simple push.

# Scénario

Le jour précédent, nous avions complété feature/#65_create_home_layout et soumis un Pull Request pour évaluation. Nous voulions procéder avec la tâche « #66 Créer le hero de l'accueil », mais cette dernière avait besoin des fonctionnalités introduites par la feature #65. Alors, nous avions créé la feature/#66_create_home_hero à partir de la branche feature/#65_create_home_layout.

Aujourd'hui, nous constatons que la feature feature/#65_create_home_layout a été approuvée et que ses changements se retrouvent maintenant sur develop. Puisque toute feature doit être basée sur develop, nous voulons faire un rebase de notre branche.

Nous avions poussé notre branche la nuit dernière pour sauvegarder nos changements. Nous vérifions alors, comme possible, qu'il n'y a pas d'autres développeurs qui ont travaillé sur notre branche depuis. Une fois qu'on est assuré qu'on est en possession de chaque changement en lien à notre branche et develop, on débute le rebase sur develop.

Après le rebase, on peut continuer le développement de notre feature comme si elle avait toujours été basée sur develop.

# Playground

Vu la complexité des rebases et le risque associé, nous avons créé un repository Bitbucket (opens new window) pour faire nos tests avec cette méthode. Chaque membre peut librement l'utiliser pour faire des rebases dans un environnement sans risque.