Skip to content
English

Git ​

CapyCode initie à Git avec un vrai dépôt : isomorphic-git (Git en JavaScript) travaille sur /workspace/.git, dans le même système de fichiers LightningFS que les fichiers de l'élève. Pour les débutants, Git est proposé sous la forme d'une extension intégrée (src/services/extensions/, src/stores/extensions.ts), désactivée par défaut pour garder une interface épurée au premier abord.

Une fois activée depuis le panneau Extensions (Ctrl+Maj+X), on s'en sert depuis l'interface (panneau Git, graphe, comparaisons) ou depuis le terminal (git …). Les deux passent par le même service, si bien que l'interface suit toujours, quelle que soit la façon dont on a agi. Si la page hôte (Capytale) désactive Git (gitEnabled: false), l'extension est masquée et verrouillée.

Le service : src/services/git/ ​

GitService est composé de trois parties :

ClasseRôle
GitRepository (repository.ts)Lecture : branches, tags, statut, historique, branche courante
GitInspection (inspection.ts)Contenus d'un fichier (HEAD, index, copie de travail), diffs, statistiques des commits ; reçoit le GitRepository à lire
GitService (index.ts)Opérations qui modifient le dépôt et leurs notifications ; possède un repo et une inspection, et expose aussi leurs lectures

Le singleton gitService suffit donc aux appelants. La séparation rend visible ce qui modifie le dépôt (et doit notifier) de ce qui le lit ; la composition permet de remplacer ou de tester une partie seule. Les autres lecteurs du dépôt, comme GitGraphService (src/services/gitGraph.ts), reçoivent un GitService et lisent par son repo : aucun n'accède au système de fichiers de son côté.

Les opérations et leurs notifications ​

Toute opération passe par mutate(change, task, worktree) :

  • change vaut index (indexer, désindexer, abandonner une modification) ou refs (commit, branches, tags, checkout, fusion) ; refs l'emporte quand les deux se mêlent ;
  • les opérations imbriquées (stageAll → add) ne notifient qu'une fois, à la fin de la plus extérieure ;
  • worktree signale qu'isomorphic-git a écrit des fichiers de travail (checkout, fusion) sans passer par le VFS : le service appelle alors vfs.notifyChange() pour que l'arborescence et les onglets suivent.

gitService.batch(task) regroupe plusieurs opérations (le rejeu d'un historique partagé) en une seule notification refs.

Opérations disponibles : init, add, stageAll, resetIndex, discardFile, commit, createBranch, renameBranch, deleteBranch, checkout, checkoutBranch, merge, abortMerge, createTag, deleteTag ; et en lecture, getMergeState.

Ce qu'il faut savoir d'isomorphic-git ​

  • Binaire. Les objets sont compressés : tout ce qui est sous .git se lit et s'écrit en Uint8Array (règle absolue du projet).
  • Chemins relatifs. isomorphic-git attend des chemins relatifs à /workspace : getRelativeWorkspacePath ou workspace.relative.
  • Fusion. git.merge met à jour les références et l'index mais pas les fichiers de travail : le service fait un checkout forcé de la branche après une fusion réussie. Pour une fusion en conflit, voir plus bas.
  • Statut. Dans statusMatrix, stage === 3 veut dire « indexé, puis modifié de nouveau », pas « conflit » : l'état conflict d'un fichier vient de la fusion en cours (GitService.status).
  • Statut et dates. Le statut compare taille et date de modification : un fichier réécrit avec la même taille dans la même milliseconde peut paraître inchangé (piège rencontré dans les tests).
  • Identité. Les commits sont signés par gitService.author (workspace.setAuthor), une identité par défaut tant que Capytale ne fournit pas celle de l'élève.

La fusion en conflit ​

Une fusion en conflit ne s'annule pas : comme avec Git, elle reste en cours jusqu'à ce que l'élève la termine ou l'abandonne.

  • Avant. merge refuse de partir d'une copie de travail modifiée (les fichiers non suivis sont permis) : isomorphic-git ne saurait pas toujours la restaurer à l'abandon. Pendant une fusion, une nouvelle fusion et un changement de branche (checkoutBranch) sont refusés.
  • Le conflit. git.merge est appelé avec abortOnConflict: false : les fichiers en conflit reçoivent les marqueurs <<<<<<< main / ======= / >>>>>>> feature. isomorphic-git n'indexe que les conflits ; le service indexe lui-même les fichiers fusionnés sans conflit, comme Git. merge renvoie alors { conflicts } au lieu de lever une erreur.
  • L'état de fusion est gardé comme dans Git, dans .git/MERGE_HEAD (le commit fusionné) et .git/MERGE_MSG (le message du futur commit, suivi de # Conflicts: et des fichiers) : il survit au rechargement et à l'export ZIP, et le vrai Git le reconnaît (mergeState.ts). getMergeState() le lit, avec les fichiers non résolus : ceux qui gardent des marqueurs (src/utils/conflicts.ts). status() les marque conflict.
  • Terminer. Pendant une fusion, commit crée le commit de fusion (parents : HEAD et le commit fusionné), puis efface l'état. Il lève MergeConflictError tant qu'un fichier garde des marqueurs, ou qu'un fichier résolu n'est pas indexé (isomorphic-git n'expose pas les conflits de l'index : il refuse le commit, UnmergedPathsError).
  • Abandonner. abortMerge appelle git.abortMerge, retire les fichiers que la fusion avait ajoutés (isomorphic-git les laisse), puis efface l'état.

Le store git ​

src/stores/git.ts tient ce que l'interface affiche : branche courante, branches, tags, statut des fichiers (avec des compteurs et un index par chemin, consulté par chaque nœud de l'explorateur), historique, message de commit en cours.

Il s'abonne au VFS et au service Git, et choisit la réaction la moins coûteuse :

NotificationRéaction
fichier de travail écrit, créé, supprimé ; indexstatut seul
quelque chose sous .git, reset, refstout : branches, tags, statut, historique

Le store expose aussi mergeState, la fusion en cours, relu avec le statut : une résolution enregistrée dans un fichier suffit à la faire passer pour résolue. finishMerge indexe les fichiers qui étaient en conflit (les autres le sont déjà) et crée le commit de fusion, avec le message saisi ou celui de la fusion ; abortMerge l'abandonne.

Les rafraîchissements concurrents sont regroupés (coalesce). Quand la page hôte désactive Git, le store se comporte comme sans dépôt et ne lit plus rien (voir Intégration dans une page hôte).

L'interface ​

  • GitPanel (sidebar/) et ses morceaux dans sidebar/git/ : fichiers indexés et modifiés (GitFileItem), message et bouton de commit, menus de branche et de fusion (GitBranchMenu, GitMergeMenu, sur MenuButton), historique (GitHistorySection, GitHistoryCommit, infobulle GitCommitTooltip).
  • GitMergeBanner (sidebar/git/) : pendant une fusion en conflit, en tête du panneau, les fichiers à résoudre (un clic les ouvre), une explication, et les boutons « Terminer la fusion » (désactivé tant qu'un fichier garde des marqueurs) et « Abandonner la fusion ». Les fichiers en conflit portent la lettre C dans le panneau, l'explorateur et les onglets ; un clic sur l'un d'eux dans le panneau ouvre le fichier plutôt que sa comparaison. Pendant la fusion, le bouton Valider fait le commit de fusion même sans changement indexé.
  • useGitActions (composable) : les actions qui demandent une confirmation ou un message (fusionner, terminer ou abandonner une fusion, créer une branche, abandonner des modifications…), communes au panneau, au graphe et aux comparaisons. Terminer et abandonner sont aussi des commandes de la palette (git.finishMerge, git.abortMerge).
  • Blocs de conflit dans l'éditeur : surlignés, avec des boutons pour garder une version, l'autre ou les deux, et les commandes équivalentes au clavier (voir Éditeur).
  • Graphe (GitGraphView et editor/git-graph/) : un onglet spécial (git://graph). GitGraphService calcule les nœuds, leurs voies et leurs couleurs, et les tracés SVG des liens ; GitGraphTable est une liste navigable au clavier (motif listbox), GitCommitDetails les fichiers d'un commit.
  • Comparaisons (DiffEditor, @codemirror/merge) : onglets diff://staged:…, diff://unstaged:… ou diff://commit:… (module specialTabs du store editor), rafraîchis à chaque notification du service Git.
  • Statuts : src/services/gitStatusMeta.ts donne pour chaque statut sa lettre (comme git status --short), sa couleur, son nom et une explication pour débutants, traduits.

Dans le terminal ​

src/services/terminal/git/ implémente git et ses sous-commandes, rangées par famille : init, status, add, restore, reset, commit (changes.ts), switch, checkout, branch, merge (branches.ts), log, diff, show (history.ts), tag (tags.ts). Chaque sous-commande est un GitSubcommand (exécution, aide, complétion), et passe par ctx.ws.git. Voir Terminal.

Une fusion en conflit s'y vit comme avec Git : git merge annonce chaque CONFLICT (content), git status montre les chemins non fusionnés (UU en version courte), git add indexe un fichier même s'il garde des marqueurs mais prévient, git commit (sans -m, avec le message de la fusion) ou git merge --continue termine, git merge --abort abandonne.

Ajouter une fonction Git ​

  1. L'opération dans GitService (par mutate), la lecture dans GitRepository ou GitInspection.
  2. L'état à afficher dans le store git, s'il ne suit pas déjà les notifications.
  3. L'action d'interface dans useGitActions si elle demande confirmation ; la commande dans gitCommands (src/commands/catalog.ts) ; la sous-commande du terminal dans src/services/terminal/git/.
  4. Vérifier qu'elle disparaît quand Git est désactivé par la page hôte (règle d'AGENTS.md).
  5. Tests dans tests/git/.