唯杰地图 logo唯杰地图
VJMAP
EN

版本管理

vjcad/references/quickref/version-control.md 11.3 KB451 行
原文

分支管理、版本保存、分支合并、冲突解决的完整指南。

概念

版本结构

图纸 (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"]
    }
}

解决冲突

冲突解决策略:

  1. 选择源分支版本 - 使用功能分支的修改
  2. 选择目标分支版本 - 保留主分支的修改
  3. 手动合并 - 人工决定最终结果
// 冲突解决走 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