Documentation
Historique du projet
Instantanés de projet locaux compatibles Git, différences structurelles, branches révisables et mise en miroir GitHub en option.
Traduit automatiquement de l'anglais. Voir l'original en anglais
Copie de travail et historique
Les enregistrements SQLite de l'application constituent la copie de travail actuelle du projet. @cadcamfun/vcs stocke les blobs, arbres, commits et références compatibles Git via un backend d'objet injecté ; le sidecar conserve ce backend dans SQLite. Les opérations d'historique fonctionnent localement sans exécutable Git ni compte GitHub.
Un commit capture le projet et ses ressources de conception/fabrication associées. Les branches pointent vers les commits, tandis que HEAD identifie la branche extraite. Les tables de versions de ressources coexistent avec cet historique au niveau du projet ; une version de dessin et une validation de projet ont des portées différentes.
Workflow de validation et de branchement
- Lire
GET /api/projects/:id/vcs/statuspour voir la branche actuelle, HEAD et les chemins modifiés. - Créer une branche avec
POST /vcs/branches, puis vérifiez-le avecPOST /vcs/checkout. Les chemins de branche ici sont relatifs au préfixe API du projet. - Modifier les ressources du projet et valider avec
POST /vcs/commitet un message non vide. - Inspecter
GET /vcs/logetGET /vcs/diff. Le journal prend en charge une révision, une limite et un chemin suivi facultatif. - Ouvrez une pull request de projet, examinez ses modifications structurelles et fusionnez-la après avoir résolu les révisions bloquantes ou les conflits.
L'extraction, la restauration et les opérations qui déplacent la branche de travail refusent une copie de travail sale si nécessaire. Une restauration enregistre une nouvelle validation représentant le contenu antérieur, éventuellement limité aux chemins sélectionnés. Il préserve l’histoire ultérieure plutôt que de la réécrire.
Contenu de l'instantané
project.json
drawings/<id>.json
components/<id>.json
toolpaths/<id>.json
materials/<id>.json
tools/<id>.json
machine-configs/<id>.json
costs/<id>.json
.cadcamfun/workflows/<name>.yml Un fichier JSON par entité sépare les modifications non liées. L'instantané comprend des éléments de bibliothèque référencés par des parcours d'outils et des estimations de coûts de production afin que les données de fabrication restaurées puissent conserver leurs dépendances. Les identifiants d'entité utilisés comme noms de fichiers sont limités aux lettres, chiffres, traits de soulignement et traits d'union sécurisés, avec un maximum de 128 caractères.
Les clés JSON sont triées de manière récursive et les fichiers utilisent une jolie impression stable avec une nouvelle ligne finale. Les tableaux conservent leur ordre. Des fichiers texte supplémentaires peuvent voyager avec un instantané au niveau du package ; le point de terminaison de validation publique accepte les fichiers supplémentaires uniquement sous .cadcamfun/workflows/ avec un nom de fichier YAML sécurisé.
Les objets utilisent le format d'objet libre de git, octet par octet, donc les ID d'objet correspondent à ceux calculés par git. Les entrées de l'arborescence sont soit des fichiers (mode 100644) ou des répertoires (mode 40000). Une signature de validation enregistre le nom, l'e-mail, l'heure en secondes Unix et un +hhmm/-hhmm compenser. Les branches sont stockées sous refs/heads/, et la branche par défaut d'un projet est main (packages/vcs/src/object.ts, repository.ts).
Différer et fusionner la sémantique
Le référentiel calcule une base de fusion, gère directement les côtés inchangés et effectue des fusions structurelles à trois voies pour le JSON modifié. Les modifications indépendantes des propriétés d'objet peuvent fusionner sans conflit. Les modifications incompatibles simultanées sur le même scalaire ou tableau sont signalées via des pointeurs JSON ; les tableaux ne sont pas fusionnés élément par élément.
Les différences d'entité incluent le chemin, le type de ressource, l'identifiant et le nom lorsqu'ils sont disponibles, le statut ajouté/modifié/supprimé et les modifications de propriété avec les valeurs avant/après. Les fichiers non JSON utilisent un comportement de conflit au niveau du fichier. Une fusion de référentiel avec des conflits renvoie leurs chemins et pointeurs sans écrire de validation fusionnée.
Demandes d'extraction et autorisations
Les demandes de tirage de projets sont en ligne sur /api/projects/:id/pulls. En ouvrir un, on enregistre un titre, un corps, une tête, une base et un type d'auteur. Les réponses détaillées incluent la demande d'extraction, les révisions et les modifications. Prise en charge des avis comment, approve, et request-changes, avec chemin facultatif et pointeur de propriété.
Le dernier verdict de non-commentaire par réviseur contrôle un bloc de demande de modification. Une fusion est refusée alors qu'un tel verdict demande des modifications. La mise en œuvre ne nécessite pas d’approbation positive lorsqu’il n’y a pas de verdict de blocage. Les différences de demandes d'extraction fusionnées utilisent la validation de fusion enregistrée afin que les mouvements de branche ultérieurs ne modifient pas cet enregistrement de révision.
La lecture de l'historique, la validation, le branchement, l'ouverture des demandes d'extraction et la révision nécessitent un accès au projet. Les déplacements de copie de travail et la maintenance des branches partagées nécessitent le propriétaire ou un administrateur/gestionnaire de l'organisation, comme l'exige le gestionnaire. Les opérations sales ou conflictuelles renvoient 409 ; les actions protégées renvoient 403 ; les références ou ressources manquantes renvoient 404 ; d'autres opérations non valides renvoient 400.
Un acteur de l'IA ne peut pas s'engager, restaurer ou fusionner dans main. L'assistant utilise une branche de proposition et ouvre une demande d'extraction pour qu'une personne puisse l'examiner. Les enregistrements d’audit et de notification identifient les modifications apportées par l’assistant.
Miroir GitHub en option
GET et PUT /api/projects/:id/vcs/remote lire ou définir un owner/repo miroir GitHub ; null le détache. Utilisation pousser et tirer POST /vcs/push et /vcs/pull, avec le jeton GitHub de l'utilisateur dans x-github-token pour cet appel.
Push transfère les objets accessibles et met à jour la branche distante sans écraser les validations distantes non extraites. Pull récupère la branche distante et la fusionne localement, refusant une copie de travail sale. Les modifications JSON incompatibles et divergentes renvoient des conflits plutôt que de remplacer silencieusement le contenu local.
Le transport sidecar utilise les opérations objet/référence GitHub REST. Le jeton GitHub est fourni par requête et n'est pas stocké en tant que données de projet. Une pull request locale est un enregistrement d’examen d’application ; ce n'est pas automatiquement une pull request GitHub.
Instantanés et restauration
snapshotToFiles convertit un instantané de projet en contenu de fichier canonique ; filesToSnapshot reconstruit les listes de domaines. ProjectVcsRepo lit les ressources de la base de données actuelle et applique les instantanés restaurés. La restauration de l'ensemble du projet peut modifier plusieurs tables associées, alors examinez sa différence et effacez la copie de travail avant de changer d'historique.
API de transport et d'objet
Le package VCS exporte le codage et la lecture d'objets, Repository, les adaptateurs de magasin, les assistants canoniques JSON et de fusion, la conversion d'instantanés, les différences d'entités, la synchronisation GitHub et les interfaces de transport. Les tests peuvent injecter un backend d'objet en mémoire ou un faux transport au lieu d'accéder au référentiel d'un utilisateur.
Points d’entrée de mise en œuvre
packages/vcs/src/repository.ts possède les validations, les références, la traversée, la restauration et la fusion ; json.ts possède une fusion structurelle; snapshot.ts définit les ressources suivies ; diff.ts décrit les changements d'entité ; github.ts et transport.ts mettre en œuvre la mise en miroir. Sidecar project-vcs.ts ajoute la politique des acteurs et la persistance, tandis que http/vcs.ts fournit des autorisations de transport et des événements.
Opérations d’historique et automatisation
Les définitions de workflow sont versionnées avec le projet et exécutées sur les fichiers validés. Une validation peut déclencher ces flux de travail une fois l'historique enregistré ; un workflow ayant échoué n'annule pas la validation. Gardez les sauvegardes de ressources locales, les validations de projet, les résultats de flux de travail et la synchronisation à distance distincts lors du diagnostic de l'état d'un projet.
Essayez le référentiel sans données d'application
La couture de magasin d'objets permet à un exercice d'intégration de s'engager et de créer des branches entièrement en mémoire. Exécutez ceci dans un espace de travail qui dépend de @cadcamfun/vcs, tel que apps/sidecar. Cela ne touche pas la base de données de projet du bureau.
import { Repository, memoryBackend, canonicalJson } from "@cadcamfun/vcs"
const repo = new Repository(memoryBackend())
const author = { name: "Workshop", email: "workshop@example.invalid", time: 0, tz: "+0000" }
repo.commit({ "project.json": canonicalJson({ name: "Blank", revision: 1 }) }, {
message: "Initial blank", author
})
repo.createBranch("proposal/wider-blank")
repo.checkout("proposal/wider-blank")
repo.commit({ "project.json": canonicalJson({ name: "Blank", revision: 2 }) }, {
message: "Record revised blank", author
})
console.log(repo.currentBranch()) // proposal/wider-blank
console.log(repo.log().length) // 2 Repository.commit reçoit l'arborescence complète des fichiers pour ce commit, pas un correctif. Le sidecar construit cette arborescence à partir d'enregistrements de base de données de travail et de fichiers texte transportés. Une intégration au niveau du package doit préserver les fichiers qu'elle a l'intention de conserver. L'exemple en mémoire n'applique pas la propriété du sidecar ni la politique des acteurs de l'IA ; ceux-ci sont appliqués par le service du projet et la couche HTTP.
Source: packages/vcs/src/store.ts, repository.ts, et apps/sidecar/src/services/project-vcs.ts.
Examinez, restaurez et synchronisez délibérément
Enregistrez les modifications apportées à la copie de travail avant de changer de branche. Une nouvelle branche nécessite une validation source résoluble, donc initialisez un projet non validé avec sa première validation avant la création de branche. La création d'une branche crée uniquement une référence ; l'extraction est une opération distincte qui applique son instantané à la base de données de travail.
Pour une demande d'extraction, comparez la tête à sa base de fusion et examinez les différences au niveau de la propriété. Un commentaire ultérieur n'efface pas un verdict de demande de modification précédent : le réviseur doit soumettre un verdict d'approbation ultérieur. Un PR fusionné peut avancer rapidement ou créer une validation de fusion en fonction de l'ascendance du référentiel ; inspectez la validation de fusion enregistrée au lieu de supposer un nombre fixe de parents.
Lors de la fusion avec la base extraite, le service nécessite une copie de travail propre et applique le nouvel instantané de base. Lorsqu'une autre branche est extraite, la fusion de la base du PR met à jour la référence de cette branche sans remplacer la base de données de travail actuelle. Une extraction ultérieure charge cette base mise à jour.
Synchronisez l'historique validé par push et pull. Le contenu de l'interface utilisateur non enregistré et l'état de la base de données non validé ne sont pas des instantanés distants. Un rejet à distance est une demande de récupération/fusion en premier ; ce n'est pas une instruction pour écraser la branche distante.
Source: packages/vcs/src/repository.ts, github.ts, et apps/sidecar/src/services/project-vcs.ts.
Récupération des échecs de l'historique
| Échec | Action suivante |
|---|---|
NothingToCommit | Comparez les fichiers de travail de la base de données avec HEAD. Enregistrer à nouveau les mêmes valeurs de ressources ne crée pas une arborescence différente. |
| Branche invalide ou existante | Choisissez un nouveau nom de référence sécurisé. Les noms ne peuvent pas contenir de traversée, de doubles barres obliques, de fragments d'expression de référence ou de suffixe barre oblique finale/.lock. |
| Modifications non validées | Validez ou restaurez/rejetez délibérément les ressources prévues avant l'extraction, l'extraction ou une opération de copie de travail protégée. |
| Fusionner le conflit avec le chemin/les pointeurs | Inspectez ces propriétés dans les historiques de base/tête, créez une résolution révisée et réessayez. L’échec de la fusion du référentiel n’a pas créé de validation de fusion partielle. |
| Un évaluateur a demandé des modifications | Résolvez les commentaires et obtenez un verdict de non-commentaire mis à jour de la part de cet évaluateur. |
| Les acteurs de l'IA ne peuvent pas agir sur le principal | Utilisez une branche de proposition d’IA et une pull request révisée par un humain. La modification d'un indicateur de confirmation ne supprime pas la restriction d'acteur. |
Source: packages/vcs/src/repository.ts, apps/sidecar/src/services/project-vcs.ts, et http/vcs.ts. Le Référence API répertorie les schémas de demande et de réponse réels.
Disposition du référentiel
Un projet est stocké dans le référentiel sous la forme d'un fichier JSON par entité. L'édition d'un dessin ne touche donc jamais à un fichier de parcours d'outil, et la mise en page se lit naturellement sur GitHub. snapshotToFiles écrit cette mise en page à partir d'un ProjectSnapshot, et filesToSnapshot le relit.
| Liste d'instantanés | Annuaire | Contenu |
|---|---|---|
project | project.json | Métadonnées du projet. |
drawings, components, toolpaths | mêmes noms | Entités de conception et de fabrication. |
materials, tools, machineConfigs | materials/, tools/, machine-configs/ | Éléments de bibliothèque référencés par les parcours d'outils, versionnés avec le projet afin qu'un parcours d'outil restauré les trouve toujours. |
costs | costs/ | Estimations des coûts de production des parcours d'outils. |
extra | n'importe quel chemin | Fichiers texte stockés textuellement : workflows, README, G-code généré. |
Valider, créer une branche, comparer et fusionner dans le code
Le Repository la classe fonctionne sur n’importe quel backend de stockage. Le sidecar le soutient avec SQLite (tables GitObject et GitRef); memoryBackend() est utilisé pour les tests et les expériences. L'exemple suivant est complet et son résultat est affiché dans les commentaires.
import { Repository, memoryBackend, snapshotToFiles, describeDiff } from "@cadcamfun/vcs"
const repo = new Repository(memoryBackend())
const me = { name: "Ada", email: "ada@example.com", time: 1767225600, tz: "+0100" }
const files = (width: number, height: number) => snapshotToFiles({
project: { id: "p1", name: "Bracket" },
drawings: [{ id: "d1", name: "Plate", width, height }],
components: [], toolpaths: [],
})
const base = repo.commit(files(40, 25), { message: "base", author: me })
repo.currentBranch() // "main"
repo.createBranch("a"); repo.createBranch("b")
repo.checkout("a"); const a = repo.commit(files(50, 25), { message: "a: width 50", author: me })
repo.checkout("b"); repo.commit(files(40, 30), { message: "b: height 30", author: me })
describeDiff(repo, base, a)
// [{ path: "drawings/d1.json", kind: "drawings", id: "d1", name: "Plate",
// status: "modified", changes: [{ pointer: "/width", before: 40, after: 50 }] }]
repo.mergeInto("a", "b", me) // { kind: "merge", oid: "46e7…" }
// different properties of the same entity merge cleanly
repo.checkout("b"); repo.commit(files(60, 30), { message: "b: width 60", author: me })
repo.mergeInto("a", "b", me)
// { kind: "conflict", conflicts: [{ path: "drawings/d1.json", pointers: ["/width"] }] }
repo.createBranch("bad..name") // throws: Invalid branch name bad..name
repo.commit(sameFilesAsHead, { message: "x", author: me })
// throws: No changes since the last commit (unless allowEmpty: true) Les fusions sont à trois au niveau des propriétés JSON. Lorsque les deux parties modifient différentes propriétés d’une entité, la fusion réussit. Lorsque les deux modifient la même propriété en des valeurs différentes, le résultat est un conflict qui répertorie les pointeurs RFC 6901 à l'intérieur de chaque fichier. Les tableaux de longueur égale sont comparés par index, donc un sommet déplacé apparaît comme un changement ; des tableaux de différentes longueurs sont comparés dans leur ensemble.
| Résultat de la fusion | Signification |
|---|---|
up-to-date | Rien à fusionner ; renvoie le commit actuel. |
fast-forward | Le pointeur de branche a avancé ; aucun commit de fusion n'a été créé. |
merge | Un commit de fusion a été créé. |
conflict | Aucun engagement ; conflicts répertorie les chemins et les pointeurs à résoudre. |
| Méthode de référentiel | But |
|---|---|
commit(files, { message, author, allowEmpty? }) | Écrivez un arbre complet en tant que nouveau commit sur la branche actuelle. |
log(rev = "HEAD", limit?) / fileLog(path, rev?, limit?) | Historique de la branche / d'un fichier. |
createBranch(name, from = "HEAD"), deleteBranch, checkout | Gestion de succursale. Les noms ne doivent pas contenir .. ou //, se termine par / ou .lock, ou contenir @{. |
tag(name, rev = "HEAD"), tags() | Balises légères sous refs/tags/. |
diff(from, to) | Modifications au niveau du fichier : ajoutées, modifiées, supprimées, avec des ID de blob. |
restorePaths(rev, paths, author, message?) | Nouveau commit restaurant les fichiers sélectionnés à partir d’une ancienne révision. |
revertTo(rev, author, message?) | Nouveau commit dont l'arborescence équivaut à une ancienne révision ; l'histoire n'est pas réécrite. |
merge(theirs, author) / mergeInto(into, theirs, author) | Fusion à trois dans la branche actuelle/une branche nommée. |
isAncestor, mergeBase | Requêtes graphiques. |
Règles appliquées par l'application
Le service d'historique de projet du sidecar ajoute des règles au-dessus du référentiel :
| Code de panne | Quand |
|---|---|
blocked | Un acteur de l’IA tente de s’engager ou de fusionner main (le message dit d'utiliser un ai/* branche et ouvre une pull request), ou une pull request est fusionnée alors que le dernier verdict d'un réviseur est request-changes. |
dirty | La copie de travail contient des modifications non validées et l'opération les écraserait : "Modifications non validées dans N fichier(s) ; validez-les ou supprimez-les d'abord". |
conflict | Une fusion a produit des conflits ; l'échec porte la liste des conflits. |
nothing-to-commit | L'arborescence est inchangée depuis le dernier commit. |
not-found | Branche, validation, objet ou demande d'extraction inconnue. |
remote | La synchronisation GitHub a échoué. |
invalid | Autre entrée non valide, par exemple la révision ou la fusion d'une demande d'extraction qui n'est pas ouverte. |
Seul le dernier verdict de non-commentaire de chaque évaluateur compte. Un réviseur qui a demandé des modifications et les a ensuite approuvées ne bloque plus la fusion. Les validations, les événements de demande d'extraction et les avis sont publiés sous forme d'événements en direct, de sorte que le bureau et le compagnon sont mis à jour sans interrogation.
Compatible en octets avec git
Les ID d'objet sont calculés exactement comme git les calcule. Le transfert vers GitHub via l'API Git Data conserve chaque ID de validation identique des deux côtés, et un git clone du miroir affiche le même historique que l'application. GitHub n'accepte que les mises à jour de référence à avance rapide ; tirez et fusionnez d'abord localement lorsque la télécommande a bougé.
Un véritable objet de commit
{
"oid": "44d2d03d23b27fbbc4b388e32e15dfb0f39fff87",
"parents": [],
"message": "First version\n",
"author": { "name": "Local user", "email": "local@cadcamfun.local", "time": 1792037121 }
} oidest un identifiant hexadécimal de 40 caractères de l'objet de validation stocké dansGitObject.- Le premier commit n'a pas de parents ; un commit de fusion en a deux.
- Le message est stocké avec une nouvelle ligne de fin, comme le fait Git.
- Sans un
x-session-token, l'auteur est le profil local de l'installation.timeest en secondes Unix.
| Itinéraire | Corps | Requis |
|---|---|---|
POST …/vcs/commit | message, authorKind, files | message |
POST …/vcs/branches | name, from | name |
POST …/vcs/checkout | branch | branch |
POST …/vcs/restore | rev, paths | rev |
POST …/pulls | title, body, head, base, authorKind | title, head |