Aller au contenu

Git

Git, créé en 2005 par Linus Torvalds, est un système de gestion de versions décentralisé et open source. Avant sa création, le noyau Linux utilisait BitKeeper, mais des différends avec ses développeurs ont poussé Torvalds à concevoir Git, en privilégiant la performance, la sécurité et la décentralisation. Rapidement adopté par la communauté Linux pour sa flexibilité et sa capacité à gérer de grands projets, sa popularité a été renforcée par des plateformes comme GitHub. Aujourd'hui, Git est la norme pour la gestion de versions, influençant profondément le développement logiciel.

Caractéristiques principales

  • Décentralisation : Contrairement à certains systèmes de gestion de versions, Git est décentralisé, ce qui signifie que chaque développeur possède une copie locale complète de l'historique du projet.
  • Intégrité : Tous les changements et fichiers dans Git sont vérifiés avec une somme de contrôle SHA-1, assurant l'intégrité et la cohérence du code.
  • Branches : Git est particulièrement efficace pour gérer les branches, permettant aux développeurs de travailler en parallèle sur différentes fonctionnalités ou corrections, puis de les fusionner facilement.

GitHub, GitLab, Bitbucket

Ces plateformes hébergent des dépôts Git et offrent des outils pour collaborer sur des projets, gérer des issues, effectuer des revues de code, etc.

🚨 Git est largement adopté dans l'industrie du développement logiciel et est considéré comme un outil essentiel pour tout développeur. Il facilite la collaboration, le suivi des modifications et l'intégration continue.

➡️ Documentation officielle

Popularité

vcs-list.png

Source image

Repository

Un "repository" (ou dépôt en français) est un espace de stockage centralisé où sont conservés et gérés des données, des fichiers, et souvent des codes sources de projets. Dans le contexte des systèmes de gestion de versions comme Git, un repository permet de suivre et de gérer les changements apportés aux fichiers au fil du temps.

Historique des versions : Un repository conserve l'historique complet des modifications de chaque fichier. Chaque modification (ou ensemble de modifications) est associée à un "commit", qui contient des informations sur les changements, l'auteur, et un timestamp.

Branching et Merging : Les repositories permettent de créer des branches pour travailler sur différentes fonctionnalités ou corrections simultanément sans perturber la version principale ("main" ou anciennement "master"). Une fois le travail sur une branche terminé, il peut être fusionné (ou "merged") avec la branche principale.

Collaboration : Les repositories facilitent la collaboration. Plusieurs développeurs peuvent cloner (copier) le repository, apporter des modifications localement sur leurs propres clones, puis proposer leurs changements au repository central via des "pull requests" ou des "merge requests".

Décentralisation : Dans le cas de Git, chaque clone d'un repository est une copie complète avec l'ensemble de l'historique des versions. Cela signifie que même si un serveur central (comme GitHub ou GitLab) était indisponible, le travail pourrait continuer et tout l'historique serait préservé dans chaque clone.

Intégrité : Les systèmes comme Git garantissent l'intégrité des données en utilisant des mécanismes de cryptographie pour s'assurer que les données n'ont pas été altérées.

Remote vs. Local : Dans Git, un repository peut exister à la fois sur votre machine locale (repository local) et sur un serveur distant (repository remote), comme GitHub, GitLab ou Bitbucket. Les changements peuvent être synchronisés entre ces dépôts à l'aide de commandes telles que git push et git fetch.

Le plus gros projet utilisant Git

Le plus gros dépôt (repository) Git en terme de taille est probablement celui de Microsoft pour Windows. En 2017, Microsoft a annoncé avoir migré le code source de Windows vers Git.

Le dépôt de Windows est particulièrement massif :

  • Plus de 300 Go de données
  • Plus de 3.5 millions de fichiers
  • Des milliers de développeurs qui y contribuent

Composants d'un repository

Un repository est composé de trois zones principales ou composants qui interagissent entre elles pour gérer et suivre les changements apportés au code ou aux fichiers.

Working Directory (répertoire de travail)

  • C'est l'espace où vous effectuez vos modifications.
  • Il contient les fichiers du projet et reflète une version particulière du code.

Staging Area (zone d'index)

  • C'est une zone intermédiaire où les modifications apportées aux fichiers du répertoire de travail sont listées avant d'être officiellement enregistrées dans un commit.

Commit History (repository local)

  • C'est là où Git stocke les métadonnées et l'historique des versions du projet.
  • Essentiellement, c'est la base de données de Git.
  • Lorsque vous effectuez un commit, les modifications de la zone d'index sont enregistrées dans une nouvelle révision (commit) dans le répertoire Git.
  • Ce commit a une référence unique (un hash SHA-1) et contient des informations sur l'auteur, la date et un message lié au commit.

repository-component.png

Source image

Les informations relatives au repository Git se trouvent dans le dossier caché .git à la racine du dossier du projet.

git-folder.png

Commit

Un "commit" représente un ensemble de modifications apportées à des fichiers, enregistrées dans l'historique du repository. Il s'agit d'un instantané (ou snapshot) de l'état actuel des fichiers à un moment donné.

Lorsque vous effectuez un commit, vous enregistrez un instantané des modifications présentes dans la "Staging Area". Ces modifications peuvent concerner l'ajout, la modification ou la suppression de fichiers.

Chaque commit contient des métadonnées, y compris un identifiant unique (généralement un hash SHA-1), le nom et l'email de l'auteur du commit, une date et heure, ainsi qu'un message de commit décrivant la raison des modifications.

Les commits sont reliés entre eux dans une chaîne représentant l'historique des modifications. Chaque commit a des références vers son ou ses commits précédents, ce qui permet de retracer l'ordre et le contexte des modifications.

git-log.png

Source image

Décentralisation

Git est un système décentralisé mais souvent utilisé de manière centralisée.

Git est décentralisé :

  • Chaque utilisateur possède une copie complète du repository, y compris l'historique complet des commits.
  • Cela signifie que chaque clone du repository est, en soi, une sauvegarde complète de l'ensemble du projet.
  • Les utilisateurs peuvent travailler localement, faire des commits, créer des branches et fusionner des changements sans avoir besoin d'une connexion à un serveur central.
  • La décentralisation permet à plusieurs personnes de travailler simultanément sur différentes parties ou fonctionnalités d'un projet sans interférer les unes avec les autres.

Utilisé de manière centralisée :

  • Malgré sa nature décentralisée, dans de nombreux workflows, il existe un repository "central" ou "principal" sur des plateformes comme GitHub, GitLab ou Bitbucket.
  • C'est généralement le dépôt de référence à partir duquel tous les membres de l'équipe clonent le projet.
  • Le recours à un dépôt central facilite la collaboration, car il sert de point de synchronisation pour toutes les modifications apportées par différents collaborateurs.

git-architecture.png

Source image

Origin

"origin" est le nom par défaut donné à la référence du repository distant (ou remote) d'où un projet a été cloné initialement. En d'autres termes, lorsque vous clonez un repository depuis un serveur comme GitHub, GitLab ou Bitbucket, Git crée automatiquement une référence à ce repository distant et lui donne le nom "origin".

git-origin.png

Source image

Commandes (repository)

git clone

  • Cette commande permet de copier (ou cloner) un repository distant sur votre machine locale.
  • Elle crée un nouveau répertoire, y initialise un nouveau repository Git, et récupère tous les fichiers et l'historique de versions du repository distant.

git push

  • Cette commande permet d'envoyer vos commits depuis votre repository local vers un repository distant.
  • Vous "poussez" vos modifications pour les partager avec d'autres.

git fetch

  • Cette commande récupère les branches et commits du repository distant que vous n'avez pas encore dans votre repository local.
  • Contrairement à git pull, elle ne fusionne pas automatiquement les modifications dans votre branche courante.

git pull

  • Cette commande est essentiellement une combinaison de git fetch suivi d'un git merge.
  • Elle récupère les modifications du repository distant et tente ensuite de les fusionner dans votre branche courante.

git-repository-cmd.png

Source image

Statuts des fichiers

Les fichiers d'un projet peuvent se trouver dans différents états en fonction de leurs modifications et de leur position dans le workflow de Git.

Untracked

  • Les fichiers qui ne sont pas suivis par Git.
  • Cela signifie que ces fichiers sont présents dans le répertoire de travail, mais n'ont jamais été ajoutés à l'index de Git.

Tracked

  • Les fichiers suivis sont des fichiers qui ont été ajoutés à l'index de Git dans le passé.
  • Ces fichiers peuvent eux-mêmes avoir plusieurs statuts.

    Unmodified : Les fichiers n'ont subi aucune modification depuis le dernier commit.

    Modified : Les fichiers qui ont été modifiés après le dernier commit, mais n'ont pas encore été ajoutés à la zone d'index pour le prochain commit.

    Staged : Les fichiers qui ont été ajoutés à la zone d'index et qui sont prêts à être inclus dans le prochain commit.

    Ignored : Certains fichiers peuvent être définis pour être ignorés par Git, ce qui signifie que Git ne les reconnaîtra pas ni ne les suivra, même s'ils sont présents dans le répertoire de travail. Cela est courant pour les fichiers temporaires, les logs, les dossiers comme node_modules, etc. Les fichiers ou dossiers à ignorer sont généralement spécifiés dans un fichier nommé .gitignore.

git-file-status.png

Source image

Message de commit

Écrire des messages de commit clairs et informatifs est crucial pour maintenir un historique propre et compréhensible du code.

🚨 Les messages de commit s'écrivent en anglais !

Bonnes pratiques

  1. Le titre du commit doit être concis (pas plus de 50 caractères) et décrire brièvement la modification principale.
  2. Le titre commence par une lettre majuscule.
  3. Pas de ponctuation dans le titre.
  4. Le titre commence par un verbe d'action au mode impératif.

    Add login page for user authentication
    

    Écrivez le message comme s'il complétait la phrase "If applied, this commit will …"

  5. Le titre est séparé du corps du message (body) par une ligne vide.

    Add login page for user authentication
    
    The username and password fields must not be empty or an error message
    is displayed to the user.
    

    Si vous fournissez un body (pour les commits plus détaillés), assurez-vous qu'il y a une ligne vide entre le titre et le body.

    On utilise des phrases et donc la ponctuation dans le body. On peut aussi utiliser des listes.

  6. Chaque ligne du body est limitée à maximum 72 caractères pour assurer une bonne lisibilité.

  7. Pour les modifications plus complexes, le body est utilisé pour expliquer le "pourquoi" de la modification.

    Le "quoi" est généralement décrit par le titre.

    Le code (ou l'affichage des modifications) permet normalement de voir le "comment". Si ce n'est pas le cas, alors le code source devrait être commenté.

🚨 Chaque commit doit représenter un ensemble logique de changements. Cela rend les messages plus clairs et facilite la révision de l'historique du code.

  • Si vous avez besoin de plus de 50 caractères pour le titre du commit ou si vous avez de la peine à trouver un bon titre, cela signifie certainement que vous avez trop de changements dans votre commit et qu'il faut en faire plusieurs.

➡️ How to Write a Git Commit Message

➡️ Commit Guidelines

Fichier .gitignore

Le fichier .gitignore est un fichier spécial utilisé par Git pour ignorer intentionnellement certains fichiers ou dossiers lors du suivi des modifications. En d'autres termes, les fichiers ou dossiers spécifiés dans .gitignore ne seront pas ajoutés à l'index de Git, même s'ils sont modifiés ou nouveaux.

  • Les environnements de développement ou les systèmes d'exploitation génèrent souvent des fichiers temporaires ou de configuration qui n'ont pas besoin d'être versionnés. Par exemple, les fichiers de log, de cache, de configuration de l'affichage du dossier dans l'explorateur de fichiers, etc.

  • Dans certains projets, les dépendances (comme les bibliothèques ou frameworks) sont stockées dans des dossiers spécifiques et n'ont pas besoin d'être suivies car elles peuvent être facilement réinstallées. Par exemple, le dossier node_modules/ dans les projets Node.js.

  • Les fichiers contenant des clés secrètes, des mots de passe ou d'autres informations sensibles ne devraient jamais être versionnés. Ils peuvent être ignorés pour éviter de les ajouter accidentellement à un repository.

Syntaxe courante dans .gitignore

  • * représente n'importe quel nombre de caractères
  • ? représente un seul caractère
  • [abc] représente un caractère unique parmi ceux listés
  • **/ permet d'ignorer des dossiers à n'importe quel niveau de profondeur
  • / au début, spécifie que la règle s'applique seulement à la racine du projet
  • # permet d'ajouter des commentaires
# Ignorer tous les fichiers .log
*.log

# Ignorer le dossier des dépendances pour Node.js
node_modules/

# Ignorer le fichier de configuration spécifique
# du dossier config à la racine du projet
/config/secret-config.yml

➡️ Documentation officielle

Workflow

Les workflows Git décrivent des approches et des pratiques recommandées pour utiliser Git dans le développement de logiciels. Ces workflows aident les équipes à collaborer efficacement, à gérer plusieurs fonctionnalités simultanément, et à assurer la stabilité du code.

Centralized Workflow

git-centralized-workflow.png

Source image

  • Tous les développeurs travaillent directement sur la branche principale (main).
  • Ce workflow est très simple, mais il peut vite devenir problématique pour des projets non triviaux et/ou avec plusieurs développeurs.

Feature Branch Workflow

git-branch.png

Source image

  • Pour chaque nouvelle fonctionnalité ou correction de bug, une nouvelle branche (appelée "feature branch") est créée.
  • Cela permet de développer et de tester des modifications isolément avant de les fusionner dans la branche principale.
  • Les plateformes comme GitHub, GitLab ou Bitbucket favorisent ce workflow avec leurs mécanismes de "pull request" ou "merge request".

git-branch-simplified.png

Source image

git-branch-request.png

Source image

Gitflow Workflow

git-flow.png

Source image

  • Un ensemble strict de règles pour créer et fusionner des branches.
  • Il définit des branches spécifiques pour les fonctionnalités, les versions, les corrections de bugs et les versions "hotfix".
  • La branche develop est utilisée comme branche principale pour le développement, tandis que la branche main reflète le code en production.

➡️ A successful Git branching model

Note

Il existe d'autres workflows adaptés à des projets et/ou des tailles d'équipes de développement plus ou moins grands, respectivement en fonction des besoins et des outils utilisés pour le CI/CD.

Convention de nommage des branches

main : Branche contenant les versions finales du code (release).

develop : Branche contenant le code en cours de développement.

release-<version> (release-1.2.0) : Branche contenant les derniers ajouts avant la fusion sur la branche main.

hotfix-<version> (hotfix-1.2.1) : Branche contenant des corrections d'une version publiée (release).

feature/<feature-name> (feature/authentication) : Branche contenant les ajouts pour la réalisation d'une fonctionnalité.

Numérotation des versions

Syntaxe : major.minor.patch

  • Major : Changements majeurs ou réécriture complète du code
  • Minor : Ajouts de nouvelles fonctionnalités
  • Patch : Corrections de bug / problème / sécurité / …

Exemple : version 1.7.4

➡️ Semantic Versioning

Merge

Avec Git, il y a principalement deux types de fusion (ou "merge").

Merge Commit : Lorsque vous fusionnez une branche dans une autre (par exemple, fusionner une feature-branch dans main), Git crée un nouveau commit dans la branche de destination.

  • Cela crée un historique non linéaire, où vous pouvez voir clairement quand une fusion a eu lieu grâce au commit de fusion.
  • Cette méthode conserve tous les détails de l'historique, y compris les branches parallèles et les fusions, ce qui peut être utile pour comprendre le contexte complet d'un projet.
  • Cependant, cela peut aussi rendre l'historique plus compliqué à lire.

Rebase and Merge : Avant de fusionner, la branche source est "rebased" sur la branche de destination.

  • Le rebase prend tous les commits de la branche source et les "rejoue" sur la branche de destination, créant de nouveaux commits pour chaque commit original.
  • Après le rebase, la branche source est directement "fast-forwarded" vers le dernier commit, ce qui signifie qu'il n'y a pas de nouveau commit de fusion.
  • L'avantage est un historique linéaire, ce qui peut rendre l'historique plus propre et plus facile à suivre.
  • Cependant, puisque le rebase modifie l'historique (en créant de nouveaux commits pour les commits rebased), cela peut être source de confusion si plusieurs personnes travaillent sur la même branche.

Note

Rebase and Merge : C'est l'option par défaut, aussi appelé "merge fast-forward".

Merge Commit : Est utilisé lors d'un merge avec l'option --no-ff, aussi appelé "merge no-fast-forward".

🚨 Merge Commit est à utiliser pour toutes les fusions de branches dans le workflow Gitflow.

➡️ Source

git-merge.png

Source image