# 创建 / 列出分支

> 文档：`vjcad/doc/version/versioning.md`
> 示例：`vjcad/example/version/01createbranch.js`
> ⚠️ 已按 d.ts 核对：`listBranches` 收**参数对象**返回 `IBranchInfo[]`；`createBranch` 字段是 `sourceBranch/sourcePatchId`（不是 `parentBranch/parentPatchId`）。

## 1. 列出已有分支

```js
const drawingManager = new DrawingManagerService();

// 参数是对象：{ type: 'imports'|'designs', mapid?, version?, designPath? }
const branches = await drawingManager.listBranches({ type: 'imports', mapid, version });
// IBranchInfo[]: [{ name, createdAt, createdBy, description?, patchCount, latestPatchId,
//                  sourceInfo?: IBranchSourceInfo, patches?: IPatchInfo[] }]

branches.forEach(b => {
    console.log(`${b.name}（最新 ${b.latestPatchId}），共 ${b.patchCount} 个补丁`);
    // b.patches[]: { id, parentId?, time?, author?, remark?, editAreas?, editLayers?, tileAlpha? }
});
```

`listBranches` 返回所有分支以及每个分支的 patch 链。常用于：

- 切分支前列出可选项
- 历史浏览（参见 [history](history.md)）

## 2. 创建分支

### 2.1 命令式（弹出 BranchCreateDialog）

```js
class CreateBranchByDialogCommand {
    async main() {
        const drawingManager = new DrawingManagerService();
        const dialog = new BranchCreateDialog();
        const src = Engine.currentDoc.serverSource;

        // IBranchCreateDialogConfig: {type, mapid?, version?, designPath?, fromBranchName, fromPatchId}
        const result = await dialog.showDialog({
            type: 'imports',
            mapid:   src.mapid,
            version: src.version,
            fromBranchName: src.branchName,
            fromPatchId:    src.lastPatchId
        });

        // IBranchCreateDialogResult: {action:'create'|'cancel', newBranchName?, mapid?, version?, fromBranchName?, fromPatchId?}
        if (result && result.action === 'create') {
            const res = await drawingManager.createBranch({
                type: 'imports',
                mapid: result.mapid,
                version: result.version,
                branchName: result.newBranchName,      // 结果里叫 newBranchName
                sourceBranch: result.fromBranchName,   // 入参叫 sourceBranch/sourcePatchId
                sourcePatchId: result.fromPatchId
            });
            // ICreateBranchResponse: {status, error?, errorCode?, branchName?}
            if (res.status) message.success(`分支 ${res.branchName} 创建成功`);
        }
    }
}
```

### 2.2 API 式（自定义业务流程）

```js
await drawingManager.createBranch({
    type: 'imports',
    mapid:        'test',
    version:      'v1',
    branchName:   'feature-abc',           // 新分支名
    sourceBranch: 'main',                  // 来源分支（字段名是 sourceBranch）
    sourcePatchId: 'base'                  // 来源补丁（'base' 表示初始）
});
```

> 完整代码：`vjcad/example/version/01createbranch.js`

## 3. 切换到某分支

vjcad 没有"切分支"命令；切分支等同于 **重新打开图纸到该分支**：

```js
// 用 OPENFROMSERVER 命令
await Engine.editor.executerWithOp('OPENFROMSERVER');
// 在弹框里选 mapid / version / branch / patch

// 或 API：drawingManager.openDrawing 支持 branch / patchId，然后喂给 openDbDoc
const r = await drawingManager.openDrawing({
    type: 'imports', mapid: 'test', version: 'v1',
    branch: 'feature-abc',       // IOpenDrawingParams.branch
    patchId: 'p3'                // 指定补丁；为空取最新
});
if (r.success) {
    const f = new File([r.webcadJson], 'd.webcad', { type: 'application/json' });
    await Engine.view.openDbDoc(f, r.webcadData);   // 签名是 (File, dbData, readonly?)
    // openDbDoc 新建的文档没有 serverSource，必须手动设置，且要在 setOriginalJson 之前
    Engine.currentDoc.serverSource = {
        type: 'imports', mapid: 'test', version: 'v1',
        branchName: 'feature-abc',
        lastPatchId: r.latestPatchId || 'base'
    };
    await Engine.currentDoc.setOriginalJson(r.webcadJson);   // 无 serverSource 时直接返回 false
}
```

`openDrawing` + `openDbDoc` **不会** 自动设置 `serverSource`（只有 `OPENFROMSERVER` 命令内部会设）；漏设则后续 `SAVESERVER` / `saveDrawing` 不知道分支和父补丁。

## 4. 删除分支

```js
// IDeleteBranchParams: {type, mapid?, version?, designPath?, branchName}
await drawingManager.deleteBranch({
    type: 'imports', mapid: 'test', version: 'v1',
    branchName: 'feature-abc'
});
```

> ⚠️ **不可逆**。常见 UI 加二次确认 / 输入分支名校对。

## 5. 字段说明

`createBranch` 入参 `ICreateBranchParams`：

| 字段 | 说明 |
|---|---|
| `type` | `'imports' \| 'designs'` |
| `mapid` / `version` / `designPath` | 图纸定位（imports 用 mapid+version，designs 用 designPath） |
| `branchName` | 新分支名（不能与已有重名；必须匹配 `^[a-zA-Z0-9_-]+$`） |
| `sourceBranch` | 源分支（如 `main`） |
| `sourcePatchId` | 源补丁 id（`'pN'`）；`'base'` 表示从源分支的 base 出发；不支持 `'latest'` |
| `author?` / `description?` | 元数据 |

`listBranches` 返回 `IBranchInfo[]`：

```ts
{
    name: string;                  // 分支名（不是 branchName）
    createdAt?: string;
    createdBy?: string;
    description?: string;
    patchCount: number;
    latestPatchId: string;         // 分支最新补丁（不是 parentPatchId）
    sourceInfo?: IBranchSourceInfo;
    patches?: IPatchInfo[];        // { id, parentId?, time?, author?, remark? }
}
```

## 红旗

- 分支名 **必须** 匹配 `^[a-zA-Z0-9_-]+$`，否则服务端直接拒绝（`Invalid branch name...`）；中文 / 空格 / 点号都不行
- `sourcePatchId` 只接受 `'base'` 或具体补丁 id（`'p1'`、`'p2'`…）；**没有 `'latest'`**（会报 `Invalid patch ID`），要从最新出发先 `listBranches` 取 `latestPatchId`。`'base'` 指 **源分支** 的 base：从 `main@base` 建的分支共用原始 `base.vcad`，其它情况会在新分支下生成分叉点快照 `branchBase.vcad` 作为它的 base
- 创建分支不会自动切到该分支；继续在原分支保存依然是原分支的补丁
- `listBranches` 返回的 `name` 字段，不要再访问 `branchName`/`parentBranch`/`parentPatchId`（不存在）
- 大分支历史可能很长，`listBranches` 返回数据量较大时考虑后端分页
- 删除分支前请确认是否有人在用（可能引起他人本地 `serverSource.lastPatchId` 失效）

## 参考（官方文档 MCP）

- 文档：`vjcad/doc/version/versioning.md`
- 示例：`vjcad/example/version/01createbranch.js`
