🌿 Comprendre JS
git
Comprendre Git

Comprendre git

Traduire l'article https://medium.com/@jake.page91/the-guide-to-git-i-never-had-a89048d4703a (opens in a new tab)

🩺 Les médecins ont des stéthoscopes. 🔧 Les mécaniciens ont des clés à molette. 👨‍💻 Les développeurs ont Git.

Git est un outil essentiel pour les développeurs. Il est probable que la plupart d'entre nous l'utilisent au quotidien. Mais le connait-on si bien ?

Qu'est-ce que git ?

Git est un système de contrôle de version (Version Contrôle System, ou VCS). Cet outil omniprésent nous permet de stocker, modifier et collaborer sur du code avec d'autres développeurs.

En tant que développeurs, notre journée s'articule autour de la lecture, de l'écriture et de la révision du code. Git est sans doute l'un des outils les plus importants que nous utilisons. Maîtriser les fonctionnalités et les capacités de Git est l'un des meilleurs investissements que vous pouvez faire pour vous-même en tant que développeur.

Les bases

Commençons par quelques concepts fondamentaux de Git.

Branches

Dans un dépôt Git, vous trouverez une ligne principale de développement, généralement nommée "main" ou "master" (désormais dépréciée), à partir de laquelle plusieurs branches divergent. Ces branches représentent des flux de travail simultanés, permettant aux développeurs de travailler sur plusieurs fonctionnalités ou correctifs en parallèle au sein du même projet.

Commits

Les commits Git servent de paquets de code mis à jour, capturant un instantané du code du projet à un moment donné. Chaque commit enregistre les modifications apportées depuis le dernier commit, constituant ensemble une histoire complète du développement du projet.

Lorsque vous référencez des commits, vous utiliserez généralement son identifiant cryptographique unique. Exemple :

git show abc123def456789

Cela montre des informations détaillées sur le commit avec ce hash.

Tags

Les tags Git servent de repères dans l'historique Git, marquant généralement des étapes importantes du développement d'un projet, comme des versions ou des releases. Ces tags sont inestimables pour marquer des points spécifiques dans le temps, représentant souvent les points de départ ou les réalisations majeures dans le parcours d'un projet.

HEAD

Le commit le plus récent sur la branche actuellement consultée est indiqué par le HEAD, servant de pointeur vers une référence quelconque dans le dépôt. Lorsque vous êtes sur une branche spécifique, HEAD pointe vers le dernier commit sur cette branche. Parfois, au lieu de pointer vers la pointe d'une branche, HEAD peut pointer directement vers un commit spécifique (état détaché de HEAD).

Stages

Comprendre les stages de Git est crucial pour naviguer dans votre flux de travail Git. Ils représentent les transitions logiques où les modifications de vos fichiers se produisent avant d'être commises dans le dépôt. Voyons les différents stages de Git :

Working Directory 👷

Le répertoire de travail est l'endroit où vous éditez, modifiez et créez des fichiers pour votre projet. Il représente l'état actuel de vos fichiers sur votre machine locale.

Staging Area 🚉

La zone de staging est comme une zone de préparation ou une zone de pré-commit où vous préparez vos modifications avant de les commettre dans le dépôt. Commande utile ici : git add. Aussi git rm peut être utilisé pour retirer des modifications de la zone de staging.

Local Repository 🗄️

Le dépôt local est l'endroit où Git stocke de manière permanente les modifications commises. Il vous permet de revoir l'historique de votre projet, de revenir à des états précédents et de collaborer avec d'autres sur la même base de code. Vous pouvez commettre les modifications prêtes dans la zone de staging avec : git commit.

Remote Repository 🛫

Le dépôt distant est un emplacement centralisé, généralement hébergé sur un serveur (comme GitHub, GitLab ou Bitbucket), où vous pouvez partager et collaborer avec d'autres sur votre projet. Vous pouvez utiliser des commandes comme git push et git pull pour pousser/tirer vos modifications commises de votre dépôt local vers le dépôt distant.

Par ou commencer ?

Vous devez commencer quelque part, et dans Git, c'est votre espace de travail. Vous pouvez forker ou cloner un dépôt existant et avoir une copie de cet espace de travail, ou si vous commencez complètement à zéro dans un nouveau dossier local sur votre machine, vous devez le transformer en dépôt git avec git init.

L'étape suivante, cruciale et à ne pas négliger, est de configurer vos identifiants.

Configuration des Identifiants

Lorsque vous effectuez des opérations de push et pull vers un dépôt distant, il est fastidieux de taper votre nom d'utilisateur et votre mot de passe à chaque fois. Évitez cela en exécutant simplement la commande suivante :

git config --global credential.helper store

La première fois que vous interagissez avec le dépôt distant, Git vous demandera de saisir votre nom d'utilisateur et votre mot de passe. Par la suite, vous ne serez plus sollicité.

Il est important de noter que les identifiants sont stockés en clair dans un fichier .git-credentials.

Pour vérifier les identifiants configurés, vous pouvez utiliser la commande suivante :

git config --global credential.helper

Travailler avec les Branches

Lorsque vous travaillez localement, il est crucial de savoir sur quelle branche vous vous trouvez. Ces commandes sont utiles :

Pour afficher les branches dans le dépôt local :

git branch 

Pour créer une branche directement :

git branch feature-branch-name

Pour passer d'une branche à une autre, utilisez :

git switch

Vous pouvez également utiliser :

git checkout

Pour basculer vers une branche qui n'existe pas encore en utilisant le drapeau -b :

git checkout -b feature-branch-name

Pour vérifier l'état du dépôt, utilisez :

git status

Une excellente façon d'avoir toujours une vue claire de votre branche actuelle est de l'afficher directement dans le terminal. De nombreux modules complémentaires de terminal peuvent vous aider à le faire. En voici un exemple.

Travailler avec les Commits

Lors de la manipulation des commits, utilisez git commit -m pour enregistrer les modifications, git amend pour modifier le commit le plus récent, et essayez de respecter les conventions de message de commit.

# Assurez-vous d'ajouter un message à chaque commit
git commit -m "message significatif"

Si vous avez des modifications à apporter à votre dernier commit, vous n'avez pas besoin de créer un autre commit. Vous pouvez utiliser le drapeau --amend pour amender le commit le plus récent avec vos modifications mises en scène.

# faites vos modifications
git add .
git commit --amend

Cela ouvrira votre éditeur de texte par défaut pour modifier le message de commit si nécessaire.

git push origin your_branch --force

⚠️ Soyez prudent en utilisant --force, car cela peut écraser l'historique de la branche cible. Son application sur la branche main/master devrait être généralement évitée.

En règle générale, il est préférable de commettre plus souvent que moins, pour éviter de perdre des progrès ou de réinitialiser accidentellement les modifications non mises en scène. Vous pouvez réécrire l'histoire plus tard en combinant plusieurs commits (squash) ou en effectuant un rebase interactif.

Utilisez git log pour afficher une liste chronologique des commits, en commençant par le plus récent et en remontant dans le temps.

Manipuler l'historique

Tout l'intérêt de git est de gérer l'évolution du code au cours du temps. Git est une sorte de caméra qui filme l'histoire du code source dans un dépôt de code.

Donc il comporte des fonctionalités pour manipuler l'histoire ! C'est souvent là que les problèmes surviennent :)

Le rebase réécrit l'historique des commits, le squash combine plusieurs commits en un seul, et le cherry-pick sélectionne des commits spécifiques.

Rebase et Merge

Il est utile de comparer le rebase au merge, car leur objectif est le même, mais ils y parviennent de différentes manières. La différence cruciale est que le rebase réécrit l'historique du projet. Un choix souhaité pour les projets qui valorisent un historique clair et facilement compréhensible.

En revanche, le merge conserve les historiques des deux branches en générant un nouveau commit de merge.

Lors d'un rebase, l'historique des commits de la branche de fonctionnalité est restructuré car il est déplacé sur le HEAD de la branche principale.

Rebase Git

Le flux de travail ici est assez simple. Assurez-vous d'être sur la branche que vous souhaitez rebaser et récupérez les dernières modifications du dépôt distant :

git checkout your_branch
git fetch

Maintenant, choisissez la branche sur laquelle vous voulez faire le rebase et exécutez cette commande :

git rebase upstream_branch

Après avoir fait le rebase, vous devrez peut-être forcer le push de vos modifications si la branche a déjà été poussée vers un dépôt distant :

git push origin your_branch --force

⚠️ Soyez prudent en utilisant --force, car cela peut écraser l'historique de la branche cible. Son application sur la branche main/master devrait être généralement évitée.

Squashing

Le "squashing" dans Git est utilisé pour condenser plusieurs commits en un seul commit cohérent.

Le concept est facile à comprendre et particulièrement utile si la méthode d'unification du code utilisée est le rebase. Puisque l'historique sera modifié, il est important de prendre en compte les effets sur l'historique du projet. Il m'est parfois arrivé de rencontrer des difficultés pour effectuer un squash, notamment en utilisant le rebase interactif. Heureusement, nous avons des outils pour nous aider. Voici ma méthode préférée pour faire un squash, qui implique de déplacer le pointeur HEAD de X commits en arrière tout en conservant les modifications mises en scène.

# Changez le nombre après HEAD~ en fonction des commits que vous souhaitez squasher
git reset --soft HEAD~X
git commit -m "Votre message de commit squasché"
git push origin your_branch --force

⚠️ Soyez prudent en utilisant --force, car cela peut écraser l'historique de la branche cible. Son application sur la branche main/master devrait être généralement évitée.

Cherry-picking

Le cherry-picking est utile pour incorporer sélectivement des modifications d'une branche à une autre, surtout lorsqu'il n'est pas souhaitable ou possible de fusionner des branches entières. Cependant, il est important d'utiliser le cherry-picking avec parcimonie, car cela peut entraîner des commits en double et des histoires divergentes si mal appliqué.

Pour effectuer cela, vous devez d'abord identifier le hash du commit que vous souhaitez choisir, vous pouvez le faire avec git log. Une fois que vous avez identifié le hash du commit, vous pouvez exécuter :

git checkout target_branch
git cherry-pick <commit-hash> # Répétez cette commande si plusieurs commits sont souhaités
git push origin target_branch

Commandes Avancées Git

Signer des Commits

Signer des commits est un moyen de vérifier l'authenticité et l'intégrité de vos commits dans Git. Cela vous permet de signer cryptographiquement vos commits en utilisant votre clé GPG (GNU Privacy Guard), assurant ainsi à Git que vous êtes bien l'auteur du commit. Voici les étapes à suivre :

# Générer une clé GPG
gpg --gen-key
# Configurer Git pour utiliser votre clé GPG
git config --global user.signingkey <your-gpg-key-id>
# Ajouter la clé publique à votre compte GitHub
# Signer vos commits avec le flag -S
git commit -S -m "Votre message de commit"
# Voir les commits signés
git log --show-signature

Git Reflog

Un sujet que nous n'avons pas encore exploré est celui des références Git. Ce sont des pointeurs vers divers objets dans le dépôt, principalement des commits, mais aussi des tags et des branches. Ils servent de points nommés dans l'historique de Git, permettant aux utilisateurs de naviguer dans la chronologie du dépôt et d'accéder à des instantanés spécifiques du projet. Savoir naviguer dans les références git peut être très utile, et vous pouvez utiliser git reflog pour ce faire. Voici quelques-uns des avantages :

  • Récupération de commits ou de branches perdus
  • Débogage et résolution de problèmes
  • Annulation d'erreurs

Rebase Interactif

Le rebase interactif est une fonctionnalité puissante de Git qui permet de réécrire l'historique des commits de manière interactive. Il vous permet de modifier, réorganiser, combiner ou supprimer des commits avant de les appliquer à une branche.

Pour l'utiliser, vous devez vous familiariser avec les actions possibles telles que :

  • Pick ("p")
  • Reword ("r")
  • Edit ("e")
  • Squash ("s")
  • Drop ("d")

Voici une vidéo utile pour apprendre à effectuer un rebase interactif dans le terminal. J'ai également lié un outil utile à la fin de cet article.

Collaboration avec Git

Origin vs Upstream

L'origine est le dépôt distant par défaut associé à votre dépôt Git local lorsque vous le clonez. Si vous avez forké un dépôt, alors ce fork devient par défaut votre dépôt "origin". En revanche, "upstream" fait référence au dépôt original à partir duquel votre dépôt a été forké. Pour garder votre dépôt forké à jour avec les dernières modifications du projet original, vous devez récupérer (git fetch) les modifications du dépôt "upstream" et les fusionner ou les rebaser dans votre dépôt local.

Pour voir les dépôts distants associés à votre dépôt Git local, exécutez :

git remote -v

Conflits

Ne paniquez pas, lorsque vous essayez de fusionner ou de rebaser une branche et que des conflits sont détectés, cela signifie simplement qu'il y a des modifications conflictuelles entre différentes versions du même fichier ou des mêmes fichiers dans votre dépôt, et elles peuvent être facilement résolues (la plupart du temps).

Ils sont généralement indiqués dans les fichiers affectés, où Git insère des marqueurs de conflit <<<<<<<, ======= et >>>>>>> pour mettre en évidence les sections conflictuelles. Décidez quelles modifications conserver, modifier ou supprimer, en veillant à ce que le code résultant ait un sens et conserve la fonctionnalité prévue.

Après avoir résolu manuellement les conflits dans les fichiers en conflit, retirez les marqueurs de conflit <<<<<<<, ======= et >>>>>>> et ajustez le code si nécessaire. Enregistrez les modifications dans les fichiers en conflit une fois que vous êtes satisfait de la résolution.

Si vous avez des problèmes pour résoudre les conflits, cette vidéo explique bien le processus.

En maîtrisant ces commandes et concepts avancés de Git, vous serez mieux préparé à gérer des scénarios complexes et à collaborer efficacement avec d'autres développeurs.

Workflows Populaires

Git est un outil. Il ne prescrit pas une façon unique de s'en servir.

Si vous travaillez sur un projet open source, vous aurez probablement une approche différente de celle d'un projet privé d'une entreprise qui édite un logiciel en SaaS.

Dans une petite équipe ou en solo, ce sera aussi différent des processus utilisés dans une grosse structure avec de nombreuses équipes de développeurs.

Voici un petit panoram des workflows possibles pour travailler à plusieurs sur un code source.

1 branche par fonctionnalité (features branch) 🌱

Chaque nouvelle fonctionnalité ou correction de bug est développée dans sa propre branche, puis fusionnée dans la branche principale une fois terminée.

  • Force : Isolation des modifications et réduction des conflits.
  • Faiblesse : Peut devenir complexe et nécessiter une gestion rigoureuse des branches.

Gitflow 🌊

Gitflow définit un modèle de branches strict avec des branches prédéfinies pour différents types de tâches de développement. Il inclut des branches de longue durée telles que main, develop, des branches de fonctionnalités, des branches de release, et des branches de hotfix.

  • Force : Convient aux projets avec des versions planifiées et une maintenance à long terme.
  • Faiblesse : Peut être excessivement complexe pour les petites équipes.

Forking 🍴

Dans ce workflow, chaque développeur clone le dépôt principal, mais au lieu de pousser des modifications directement, ils poussent des modifications vers leur propre fork du dépôt. Les développeurs créent ensuite des pull requests pour proposer des modifications au dépôt principal, permettant la révision de code et la collaboration avant la fusion. C'est le workflow que nous utilisons pour collaborer sur les dépôts open-source de Glasskube.

  • Force : Encourage la collaboration des contributeurs externes sans accorder un accès direct en écriture au dépôt principal.
  • Faiblesse : La synchronisation entre les forks et le dépôt principal peut être difficile.

Pull Request ⏩

Similaire au workflow de forking, mais au lieu de forker, les développeurs créent des branches de fonctionnalités directement dans le dépôt principal.

  • Force : Facilite la révision de code, la collaboration et le partage de connaissances parmi les membres de l'équipe.
  • Faiblesse : La dépendance aux réviseurs de code humains peut introduire des délais dans le processus de développement.

Trunk-based 🪵

Si vous faites partie d'une équipe axée sur l'itération rapide et la livraison continue, vous pourriez utiliser le développement basé sur le trunk où les développeurs travaillent directement sur la branche principale en commitant des modifications petites et fréquentes.

  • Force : Favorise l'itération rapide, l'intégration continue, et se concentre sur la livraison de petites modifications fréquentes en production.
  • Faiblesse : Nécessite des pipelines de test et de déploiement automatisés robustes pour assurer la stabilité de la branche principale, peut ne pas convenir aux projets avec des calendriers de release stricts ou des développements de fonctionnalités complexes.

Pourquoi le Forking ?

Le forking est fortement recommandé pour collaborer sur des projets Open Source, car vous avez un contrôle total sur votre propre copie du dépôt. Vous pouvez apporter des modifications, expérimenter de nouvelles fonctionnalités ou corriger des bugs sans affecter le projet original.

💡 Ce que j'ai mis longtemps à comprendre, c'est que même si les dépôts forkés commencent comme des entités séparées, ils conservent une connexion avec le dépôt original. Cette connexion vous permet de suivre les modifications du projet original et de synchroniser votre fork avec les mises à jour effectuées par d'autres. C'est pourquoi même lorsque vous poussez vers votre dépôt origin, vos modifications apparaîtront également sur le dépôt distant.

En comprenant et en maîtrisant ces workflows Git populaires, vous serez mieux équipé pour choisir celui qui convient le mieux à votre équipe et à votre projet, améliorant ainsi votre efficacité et votre collaboration dans le développement de logiciels.

Résumé des principales commandes

# Clone a Repository
git clone <repository_url>
 
# Stage Changes for Commit
git add <file(s)>
 
# Commit Changes
git commit -m "Commit message"
 
# Push Changes to the Remote Repository
git push
 
# Force Push Changes (use with caution)
git push --force
 
# Reset Working Directory to Last Commit
git reset --hard
 
# Create a New Branch
git branch <branch_name>
 
# Switch to a Different Branch
git checkout <branch_name>
 
# Merge Changes from Another Branch
git merge <branch_name>
 
# Rebase Changes onto Another Branch (use with caution)
git rebase <base_branch>
 
# View Status of Working Directory
git status
 
# View Commit History
git log
 
# Undo Last Commit (use with caution)
git reset --soft HEAD^
 
# Discard Changes in Working Directory
git restore <file(s)>
 
# Retrieve Lost Commit References
git reflog
 
# Interactive Rebase to Rearrange Commits
git rebase --interactive HEAD~3