版本管理
分支管理、版本保存、分支合并、冲突解决的完整指南。
概念
版本结构
图纸 (mapid/version)
├── main 分支
│ ├── base (初始版本)
│ ├── patch-001
│ ├── patch-002
│ └── patch-003 (当前)
└── feature-xxx 分支
├── base (从 main 分叉)
└── patch-001
Patch 版本链
base → patch-001 → patch-002 → patch-003
每个 Patch 记录相对于父版本的增量变更:
- 新增的实体
- 修改的实体
- 删除的实体 ID
- 图层变更
- 元数据(作者、时间、备注等)
创建分支
命令方式
// 打开图纸浏览器
await Engine.editor.executerWithOp('OPENFROMSERVER');
// 在版本上右键选择「创建分支」
API 方式
const { DrawingManagerService, BranchCreateDialog, message } = vjcad;
const drawingManager = new DrawingManagerService();
// 方式1:通过对话框创建
const dialog = new BranchCreateDialog();
const result = await dialog.showDialog({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
fromBranchName: 'main',
fromPatchId: 'base'
});
if (result && result.action === 'create') {
const createResult = await drawingManager.createBranch({
type: result.type,
mapid: result.mapid,
version: result.version,
sourceBranch: result.fromBranchName,
sourcePatchId: result.fromPatchId,
branchName: result.newBranchName
});
if (createResult.status) {
message.info(`分支 "${result.newBranchName}" 创建成功!`);
}
}
// 方式2:直接调用 API
const createResult = await drawingManager.createBranch({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
sourceBranch: 'main',
sourcePatchId: 'base',
branchName: 'feature-new-layer'
});
保存版本(Patch)
命令方式
await Engine.editor.executerWithOp('SAVESERVER');
API 方式
const { DrawingManagerService, Engine } = vjcad;
const drawingManager = new DrawingManagerService();
// 1. 从服务器打开图纸(获取原始数据)
const openResult = await drawingManager.openDrawing({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
branch: 'main'
// 不传 patchId = 该分支最新 patch;传 'base' 会打开原始导入版本,且 latestPatchId 也回传 'base'
});
// 2. 加载到编辑器
const virtualFile = new File([openResult.webcadJson], 'doc', { type: 'application/json' });
await Engine.view.openDbDoc(virtualFile, openResult.webcadData);
// 必须先设 serverSource:openDbDoc 不设置它,而 setOriginalJson 在没有 serverSource 时直接返回 false
// (之后 getOriginalJson 为 null,saveDrawing 会退化成整图保存而不是增量 patch)
Engine.currentDoc.serverSource = {
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
branchName: 'main',
lastPatchId: openResult.latestPatchId || 'base'
};
// 保存原始数据(用于 diff 计算)
await Engine.currentDoc.setOriginalJson(openResult.webcadJson);
// 3. 编辑图纸...
// (添加、修改、删除实体)
// 4. 保存(计算 diff 生成 patch)
const currentDoc = Engine.currentDoc;
const currentJson = JSON.stringify(currentDoc.toDb());
const originalJson = await currentDoc.getOriginalJson();
const saveResult = await drawingManager.saveDrawing({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
branchName: 'main',
originalJson: originalJson,
currentJson: currentJson,
parentId: currentDoc.serverSource.lastPatchId,
drawingName: '我的修改',
author: '用户名',
remark: '添加了新图层'
});
if (saveResult.status) {
if (saveResult.patchId === 'no_change') {
console.log('没有需要保存的修改');
} else {
console.log(`保存成功! Patch ID: ${saveResult.patchId}`);
// 更新本地原始数据与父版本(下次保存以本次 patch 为 parentId)
await currentDoc.setOriginalJson(currentJson);
currentDoc.serverSource.lastPatchId = saveResult.patchId;
}
} else if (saveResult.conflict && saveResult.conflict.hasConflict) {
console.warn('检测到与其他用户的修改冲突');
} else {
console.error(`保存失败: ${saveResult.error}`);
}
合并分支
API 方式
const { DrawingManagerService, message } = vjcad;
const drawingManager = new DrawingManagerService();
// 将 feature 分支合并到 main
const mergeResult = await drawingManager.mergeBranch({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
sourceBranch: 'feature-new-layer',
targetBranch: 'main',
remark: '合并新图层功能'
});
if (mergeResult.status) {
message.info(`合并成功!Patch ID: ${mergeResult.patchId}`);
} else if (mergeResult.conflict && mergeResult.conflict.hasConflict) {
message.warn('检测到冲突,需要手动解决');
// 处理冲突...
} else {
message.error(`合并失败: ${mergeResult.error}`);
}
查看版本历史
API 方式
const { DrawingManagerService } = vjcad;
const drawingManager = new DrawingManagerService();
// 获取分支列表(包含各分支的 patch 信息)
const branches = await drawingManager.listBranches({
type: 'imports',
mapid: 'drawing-123',
version: 'v1'
});
console.log('分支列表:', branches.map(b => b.name));
// 获取 main 分支的 patch 列表
const mainBranch = branches.find(b => b.name === 'main');
const patches = mainBranch ? mainBranch.patches : [];
console.log(`共 ${patches.length} 个版本`);
// 显示版本详情
for (const patch of patches) {
console.log(`- ${patch.id}: ${patch.remark || '(无备注)'} by ${patch.author}`);
}
打开特定版本
// 打开指定 Patch 版本
const result = await drawingManager.openDrawing({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
branch: 'main',
patchId: 'patch-001' // 指定版本
});
删除版本/分支
删除 Patch
const deleteResult = await drawingManager.deletePatch({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
branch: 'main',
patchId: 'patch-003'
});
if (deleteResult.status) {
console.log('Patch 已删除');
}
删除分支
const deleteResult = await drawingManager.deleteBranch({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
branchName: 'feature-old'
});
if (deleteResult.status) {
console.log('分支已删除');
}
冲突处理
当两个分支修改了同一个实体时,合并会产生冲突。
检测冲突
const mergeResult = await drawingManager.mergeBranch({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
sourceBranch: 'feature-a',
targetBranch: 'main'
});
if (mergeResult.conflict && mergeResult.conflict.hasConflict) {
// 字段是 conflictingEntities(不是 entities);元素是 IConflictEntityInfo
const conflicts = mergeResult.conflict.conflictingEntities;
console.log(`冲突实体数: ${conflicts.length}`);
for (const c of conflicts) {
console.log(`实体 ${c.entityId} (${c.entityType}):`);
console.log(` - 服务端版本: ${JSON.stringify(c.serverVersion)}`);
console.log(` - 客户端版本: ${JSON.stringify(c.clientVersion)}`);
console.log(` - 冲突属性: ${c.conflictingProperties}`); // 如 ["position/x","color"]
}
}
解决冲突
冲突解决策略:
- 选择源分支版本 - 使用功能分支的修改
- 选择目标分支版本 - 保留主分支的修改
- 手动合并 - 人工决定最终结果
// 冲突解决走 mergeBranch 的 conflictResolution 参数(按实体 ID 指定取舍)
const mergeResult = await drawingManager.mergeBranch({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
sourceBranch: 'feature-a',
targetBranch: 'main',
conflictResolution: {
[entityId]: { choice: 'source' } // 'source' | 'target',可附 entityData
}
});
完整工作流示例
const { DrawingManagerService, Engine, LineEnt, CircleEnt, message } = vjcad;
const drawingManager = new DrawingManagerService();
// === 1. 创建功能分支 ===
const createResult = await drawingManager.createBranch({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
sourceBranch: 'main',
sourcePatchId: 'base',
branchName: 'feature-new-layer'
});
if (!createResult.status) {
throw new Error(`创建分支失败: ${createResult.error}`);
}
message.info('功能分支创建成功');
// === 2. 在功能分支上编辑 ===
const openResult = await drawingManager.openDrawing({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
branch: 'feature-new-layer',
patchId: 'base'
});
const virtualFile = new File([openResult.webcadJson], 'doc', { type: 'application/json' });
await Engine.view.openDbDoc(virtualFile, openResult.webcadData);
Engine.currentDoc.serverSource = { // 先设来源,否则 setOriginalJson 直接返回 false
type: 'imports', mapid: 'drawing-123', version: 'v1',
branchName: 'feature-new-layer', lastPatchId: openResult.latestPatchId || 'base'
};
await Engine.currentDoc.setOriginalJson(openResult.webcadJson);
// 添加新图形
const circle = new CircleEnt([100, 100], 50);
circle.setDefaults();
circle.color = 1;
Engine.addEntities(circle);
// === 3. 保存修改 ===
const currentJson = JSON.stringify(Engine.currentDoc.toDb());
const saveResult = await drawingManager.saveDrawing({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
branchName: 'feature-new-layer',
originalJson: openResult.webcadJson,
currentJson: currentJson,
parentId: openResult.latestPatchId || 'base',
drawingName: '新功能',
author: '开发者',
remark: '添加红色圆形'
});
message.info(`保存成功: ${saveResult.patchId}`);
// === 4. 合并到主分支 ===
const mergeResult = await drawingManager.mergeBranch({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
sourceBranch: 'feature-new-layer',
targetBranch: 'main',
remark: '合并新功能'
});
if (mergeResult.status) {
message.info('合并成功!');
// === 5. 清理功能分支(可选)===
await drawingManager.deleteBranch({
type: 'imports',
mapid: 'drawing-123',
version: 'v1',
branchName: 'feature-new-layer'
});
message.info('功能分支已删除');
} else if (mergeResult.conflict?.hasConflict) {
message.warn('存在冲突,需要手动解决');
}
API 参考
DrawingManagerService
| 方法 | 说明 |
|---|---|
createBranch(params) |
创建分支 |
deleteBranch(params) |
删除分支 |
listBranches(params) |
获取分支列表 |
mergeBranch(params) |
合并分支 |
saveDrawing(params) |
保存版本(Patch) |
deletePatch(params) |
删除版本 |
openDrawing(params) |
打开图纸 |
分支操作参数
| 参数 | 说明 |
|---|---|
type |
图纸类型:'imports' | 'designs' |
mapid |
图纸 ID |
version |
版本号(如 'v1') |
branchName |
分支名称 |
sourceBranch |
源分支 |
targetBranch |
目标分支 |
sourcePatchId |
源 Patch ID |