Pular para o conteúdo
CADCAMFUN
Docs

Documentação

História do projeto

Instantâneos de projetos locais compatíveis com Git, diferenças estruturais, ramificações revisáveis e espelhamento GitHub opcional.

Traduzido automaticamente do inglês. Ver o original em inglês

Cópia de trabalho e histórico

Os registros SQLite da aplicação são a cópia de trabalho atual do projeto. @cadcamfun/vcs armazena blobs, árvores, commits e referências compatíveis com Git por meio de um back-end de objeto injetado; o sidecar persiste nesse backend no SQLite. As operações de histórico funcionam localmente sem um executável Git ou uma conta GitHub.

Um commit captura o projeto e seus recursos de design/fabricação relacionados. As ramificações apontam para commits, enquanto HEAD identifica a ramificação com check-out. As tabelas de versões de recursos coexistem com esse histórico em nível de projeto; uma versão em desenho e um commit do projeto têm escopos diferentes.

Fluxo de trabalho de commit e ramificação

  1. Ler GET /api/projects/:id/vcs/status para ver o branch atual, HEAD e os caminhos alterados.
  2. Crie uma filial com POST /vcs/branches, então confira com POST /vcs/checkout. Os caminhos de ramificação aqui são relativos ao prefixo da API do projeto.
  3. Edite os recursos do projeto e confirme com POST /vcs/commit e uma mensagem não vazia.
  4. Inspecionar GET /vcs/log e GET /vcs/diff. O log oferece suporte a revisão, limite e caminho rastreado opcional.
  5. Abra uma solicitação pull de projeto, revise suas alterações estruturais e mescle após resolver revisões de bloqueio ou conflitos.

Check-out, restauração e operações que movem a ramificação de trabalho recusam uma cópia de trabalho suja quando necessário. Uma restauração registra um novo commit representando o conteúdo anterior, opcionalmente limitado aos caminhos selecionados. Preserva a história subsequente em vez de reescrevê-la.

Conteúdo do instantâneo

Layout do repositório do projeto
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

Um arquivo JSON por entidade mantém as alterações não relacionadas separadas. O instantâneo inclui itens de biblioteca referenciados por percursos da ferramenta de ferramentas e estimativas de custos de produção para que os dados de fabricação restaurados possam manter suas dependências. Os identificadores de entidade usados como nomes de arquivos são restritos a letras seguras, dígitos, sublinhados e hifens, com no máximo 128 caracteres.

As chaves JSON são classificadas recursivamente e os arquivos usam impressão bonita estável com uma nova linha final. As matrizes mantêm sua ordem. Arquivos de texto extras podem viajar com um instantâneo em nível de pacote; o endpoint de commit público aceita arquivos extras apenas em .cadcamfun/workflows/ com um nome de arquivo YAML seguro.

Os objetos usam o formato de objeto solto do git, byte por byte, portanto, os IDs dos objetos correspondem aos computados por git. As entradas da árvore são arquivos (modo 100644) ou diretórios (modo 40000). Uma assinatura de commit registra nome, e-mail, tempo de segundos Unix e um +hhmm/-hhmm desvio. As filiais são armazenadas em refs/heads/, e o branch padrão de um projeto é main (packages/vcs/src/object.ts, repository.ts).

Semântica de comparação e mesclagem

O repositório calcula uma base de mesclagem, lida diretamente com os lados inalterados e executa mesclagens estruturais de três vias para JSON alterado. Edições independentes de propriedades de objetos podem ser mescladas sem conflito. Edições simultâneas incompatíveis no mesmo escalar ou array são relatadas por meio de ponteiros JSON; arrays não são mesclados elemento por elemento.

As diferenças de entidade incluem caminho, tipo de recurso, identificador e nome quando disponível, status adicionado/modificado/excluído e alterações de propriedade com valores antes/depois. Arquivos não JSON usam comportamento de conflito no nível do arquivo. Uma mesclagem de repositório com conflitos retorna seus caminhos e ponteiros sem escrever um commit mesclado.

Solicitações pull e permissões

Solicitações pull do projeto estão disponíveis em /api/projects/:id/pulls. A abertura de um registra um título, corpo, cabeçalho, base e tipo de autor. As respostas detalhadas incluem solicitação pull, revisões e alterações. Suporte para avaliações comment, approve, e request-changes, com caminho opcional e ponteiro de propriedade.

O último veredicto sem comentários por revisor controla um bloco de alterações de solicitação. Uma mesclagem é recusada enquanto qualquer veredicto solicitar alterações. A implementação não requer aprovação positiva quando não há veredicto de bloqueio. As diferenças de solicitação pull mescladas usam o commit de mesclagem registrado para que a movimentação posterior da ramificação não altere esse registro de revisão.

A leitura do histórico, a confirmação, a ramificação, a abertura de solicitações pull e a revisão exigem acesso ao projeto. As movimentações da cópia de trabalho e a manutenção da filial compartilhada exigem o proprietário ou um administrador/gerente da organização, conforme imposto pelo manipulador. Operações sujas ou conflitantes retornam 409; ações protegidas retornam 403; referências ou recursos ausentes retornam 404; outras operações inválidas retornam 400.

Um ator de IA não pode confirmar, restaurar ou fundir-se em main. O assistente usa uma ramificação de proposta e abre uma solicitação pull para uma pessoa revisar. Os registros de auditoria e notificação identificam alterações originadas pelo assistente.

Espelho opcional do GitHub

GET e PUT /api/projects/:id/vcs/remote leia ou defina um owner/repo Espelho GitHub; null o separa. Uso de empurrar e puxar POST /vcs/push e /vcs/pull, com o token GitHub do usuário em x-github-token para essa chamada.

Push transfere objetos acessíveis e atualiza a ramificação remota sem substituir commits remotos não extraídos. Pull busca o branch remoto e o mescla localmente, recusando uma cópia de trabalho suja. Edições JSON divergentes e incompatíveis retornam conflitos em vez de substituir silenciosamente o conteúdo local.

O transporte secundário usa operações de objeto/ref REST do GitHub. O token GitHub é fornecido por solicitação e não é armazenado como dados do projeto. Uma solicitação pull local é um registro de revisão de aplicativo; não é automaticamente uma solicitação pull do GitHub.

Instantâneos e restauração

snapshotToFiles converte um instantâneo do projeto em conteúdo de arquivo canônico; filesToSnapshot reconstrói as listas de domínios. ProjectVcsRepo lê os recursos atuais do banco de dados e aplica instantâneos restaurados. A restauração de todo o projeto pode alterar várias tabelas relacionadas, portanto revise sua diferença e limpe a cópia de trabalho antes de mudar o histórico.

APIs de transporte e objeto

O pacote VCS exporta codificação e leitura de objetos, Repository, adaptadores de armazenamento, JSON canônico e auxiliares de mesclagem, conversão de instantâneo, diferenças de entidade, sincronização do GitHub e interfaces de transporte. Os testes podem injetar um back-end de objeto na memória ou transporte falso em vez de acessar o repositório de um usuário.

Pontos de entrada de implementação

packages/vcs/src/repository.ts possui commits, refs, atravessamento, restauração e mesclagem; json.ts possui fusão estrutural; snapshot.ts define recursos rastreados; diff.ts descreve alterações de entidade; github.ts e transport.ts implementar espelhamento. Sidecar project-vcs.ts acrescenta política e persistência dos atores, enquanto http/vcs.ts fornece autorização de transporte e eventos.

Histórico de operações e automação

As definições de fluxo de trabalho são versionadas com o projeto e executadas em arquivos confirmados. Um commit pode acionar esses fluxos de trabalho após o registro do histórico; um fluxo de trabalho com falha não desfaz a confirmação. Mantenha os salvamentos de recursos locais, as confirmações do projeto, os resultados do fluxo de trabalho e a sincronização remota distintos ao diagnosticar o estado de um projeto.

Experimente o repositório sem dados do aplicativo

A costura de armazenamento de objetos permite que um exercício de integração seja confirmado e ramificado inteiramente na memória. Execute isso dentro de um espaço de trabalho que depende de @cadcamfun/vcs, como apps/sidecar. Ele não afeta o banco de dados do projeto da área de trabalho.

Dois commits e um branch de proposta
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 recebe a árvore de arquivos completa desse commit, não um patch. O sidecar constrói esta árvore a partir de registros de banco de dados de trabalho e arquivos de texto transportados. Uma integração em nível de pacote deve preservar os arquivos que pretende reter. O exemplo in-memory não se aplica à propriedade de sidecar ou à política de atores de IA; eles são aplicados pelo serviço do projeto e pela camada HTTP.

Fonte: packages/vcs/src/store.ts, repository.ts, e apps/sidecar/src/services/project-vcs.ts.

Revise, restaure e sincronize deliberadamente

Registre as alterações na cópia de trabalho antes de mudar de ramificação. Uma nova ramificação requer um commit de origem resolvível, então inicialize um projeto não confirmado com seu primeiro commit antes da ramificação. Criar uma ramificação cria apenas uma referência; checkout é uma operação separada que aplica seu instantâneo ao banco de dados em funcionamento.

Para uma solicitação pull, compare o head com sua base de mesclagem e revise as diferenças no nível da propriedade. Um comentário posterior não elimina um veredicto de alteração de solicitação anterior: o revisor deve enviar um veredicto de aprovação subsequente. Um PR mesclado pode avançar rapidamente ou criar um commit de mesclagem de acordo com a ancestralidade do repositório; inspecione o commit de mesclagem registrado em vez de assumir um número fixo de pais.

Ao mesclar na base com check-out, o serviço requer uma cópia de trabalho limpa e aplica o novo instantâneo de base. Quando outra ramificação é retirada, a mesclagem da base do PR atualiza a referência dessa ramificação sem substituir o banco de dados de trabalho atual. Uma verificação subsequente carrega essa base atualizada.

Push e pull sincronizam o histórico confirmado. O conteúdo da UI não salvo e o estado do banco de dados não confirmado não são instantâneos remotos. Uma rejeição remota é uma solicitação para buscar/mesclar primeiro; não é uma instrução para substituir a ramificação remota.

Fonte: packages/vcs/src/repository.ts, github.ts, e apps/sidecar/src/services/project-vcs.ts.

Recuperar-se de falhas do histórico

FalhaPróxima ação
NothingToCommitCompare os arquivos de trabalho do banco de dados com HEAD. Salvar novamente os mesmos valores de recurso não cria uma árvore diferente.
Filial inválida ou existenteEscolha um novo nome de referência seguro. Os nomes não podem conter travessia, barras duplas, fragmentos de expressão de referência ou uma barra final/sufixo .lock.
Alterações não confirmadasConfirme ou restaure/descarte deliberadamente os recursos pretendidos antes de fazer checkout, pull ou uma operação de cópia de trabalho protegida.
Mesclar conflito com caminho/ponteirosInspecione essas propriedades nos históricos base/cabeça, crie uma resolução revisada e tente novamente. A falha na mesclagem do repositório não criou uma confirmação de mesclagem parcial.
Um revisor solicitou alteraçõesResolva o feedback e obtenha um veredicto atualizado sem comentários desse revisor.
Os intervenientes na IA não podem agir nos principaisUse uma ramificação de proposta de IA e uma solicitação pull revisada por humanos. Alterar um sinalizador de confirmação não remove a restrição do ator.

Fonte: packages/vcs/src/repository.ts, apps/sidecar/src/services/project-vcs.ts, e http/vcs.ts. O Referência da API lista os esquemas reais de solicitação e resposta.

Layout do repositório

Um projeto é armazenado no repositório como um arquivo JSON por entidade. A edição de um desenho, portanto, nunca toca em um arquivo de percurso, e o layout é lido naturalmente no GitHub. snapshotToFiles escreve esse layout a partir de um ProjectSnapshot, e filesToSnapshot lê de volta.

/ project.json desenhos/<id>.jsoncomponentes/<id>.json percursos da ferramentas/<id>.jsonmateriais/<id>.json ferramentas/<id>.jsonmachine-configs/<id>.json custos/<id>.json .cadcamfun/workflows/<nome>.yml um arquivo por entidade → diferenças e mesclagens permanecem locais para o que mudou
Lista de instantâneosDiretórioContente
projectproject.jsonMetadados do projeto.
drawings, components, toolpathsmesmos nomesEntidades de design e fabricação.
materials, tools, machineConfigsmaterials/, tools/, machine-configs/Itens de biblioteca aos quais os percursos da ferramenta fazem referência, versionados com o projeto para que um percurso restaurado ainda os encontre.
costscosts/Estimativas de custos de produção dos percursos da ferramenta.
extraqualquer caminhoArquivos de texto armazenados literalmente: fluxos de trabalho, README, código G gerado.

Confirmar, ramificar, comparar e mesclar no código

O Repository class funciona em qualquer back-end de armazenamento. O sidecar faz backup com SQLite (tabelas GitObject e GitRef); memoryBackend() é usado para testes e experimentos. O exemplo a seguir está completo e sua saída é mostrada nos comentários.

history-demo.ts
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)

As mesclagens são triviais no nível das propriedades JSON. Quando ambos os lados alteraram propriedades diferentes de uma entidade, a fusão foi bem-sucedida. Quando ambos alteram a mesma propriedade para valores diferentes, o resultado é um conflict que lista ponteiros RFC 6901 dentro de cada arquivo. Matrizes de comprimento igual são comparadas por índice, portanto, um vértice movido aparece como uma alteração; matrizes de comprimentos diferentes são comparadas como um todo.

Mesclar resultadoSignificado
up-to-dateNada para mesclar; retorna o commit atual.
fast-forwardO ponteiro do ramo avançou; nenhum commit de mesclagem foi criado.
mergeUm commit de mesclagem foi criado.
conflictSem compromisso; conflicts lista caminhos e ponteiros para resolver.
Método de repositórioPropósito
commit(files, { message, author, allowEmpty? })Escreva uma árvore completa como um novo commit no branch atual.
log(rev = "HEAD", limit?) / fileLog(path, rev?, limit?)Histórico da ramificação/de um arquivo.
createBranch(name, from = "HEAD"), deleteBranch, checkoutGestão de filiais. Os nomes não devem conter .. ou //, terminar em / ou .lockou contém @{.
tag(name, rev = "HEAD"), tags()Tags leves abaixo refs/tags/.
diff(from, to)Alterações no nível do arquivo: adicionadas, modificadas, excluídas, com IDs de blob.
restorePaths(rev, paths, author, message?)Novo commit restaurando arquivos selecionados de uma revisão mais antiga.
revertTo(rev, author, message?)Novo commit cuja árvore é igual a uma revisão mais antiga; a história não é reescrita.
merge(theirs, author) / mergeInto(into, theirs, author)Mesclagem de três vias na ramificação atual/uma ramificação nomeada.
isAncestor, mergeBaseConsultas de gráfico.

Regras aplicadas pelo aplicativo

O serviço de histórico do projeto secundário adiciona regras ao repositório:

Código de falhaQuando
blockedUm ator de IA tenta se comprometer ou fundir main (a mensagem diz para usar um ai/* ramificar e abrir uma solicitação pull), ou uma solicitação pull é mesclada enquanto o veredicto mais recente de um revisor é request-changes.
dirtyA cópia de trabalho possui alterações não confirmadas e a operação as substituiria: "Alterações não confirmadas em N arquivo(s); confirme ou descarte-as primeiro".
conflictUma fusão produziu conflitos; a falha carrega a lista de conflitos.
nothing-to-commitA árvore permanece inalterada desde o último commit.
not-foundRamificação, confirmação, objeto ou solicitação pull desconhecida.
remoteA sincronização do GitHub falhou.
invalidOutra entrada inválida, por exemplo, revisão ou mesclagem de uma solicitação pull que não está aberta.

Somente o último veredicto sem comentários de cada revisor conta. Um revisor que solicitou alterações e posteriormente aprovou não bloqueia mais a mesclagem. Commits, eventos de pull request e revisões são publicados como eventos ao vivo, de modo que a área de trabalho e o complemento são atualizados sem pesquisa.

Compatível com bytes com git

Os IDs dos objetos são calculados exatamente como o git os calcula. Enviar para o GitHub por meio da API Git Data mantém todos os IDs de commit idênticos em ambos os lados e um git clone do espelho mostra o mesmo histórico do aplicativo. O GitHub aceita apenas atualizações de referência de avanço rápido; puxe e mescle localmente primeiro quando o controle remoto for movido.

Um objeto de commit real

POST /api/projects/:id/vcs/commit
{
  "oid": "44d2d03d23b27fbbc4b388e32e15dfb0f39fff87",
  "parents": [],
  "message": "First version\n",
  "author": { "name": "Local user", "email": "local@cadcamfun.local", "time": 1792037121 }
}
  • oid é um ID hexadecimal de 40 caracteres do objeto de commit armazenado em GitObject.
  • O primeiro commit não tem pais; um commit de mesclagem tem dois.
  • A mensagem é armazenada com uma nova linha final, como faz o Git.
  • Sem um x-session-token, o autor é o perfil local da instalação. time está em segundos Unix.
RotaCorpoObrigatório
POST …/vcs/commitmessage, authorKind, filesmessage
POST …/vcs/branchesname, fromname
POST …/vcs/checkoutbranchbranch
POST …/vcs/restorerev, pathsrev
POST …/pullstitle, body, head, base, authorKindtitle, head

Em 3D

Um gráfico de commit Arraste para orbitar · role para ampliar · arraste com o botão direito para deslocar
The last 14 commits of origin/main from git log --format=%h|%p: each pull request branch commit (back lane) is merged into main (front lane, orange merge commits), then the docs commits. A project history in the app is the same kind of graph: commits with parents, refs pointing at them.