Dagger TypeScript SDK 中的 ChangesetWithChangesetOpts:Changeset 合并冲突策略参数详解
【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger
本篇基于 Dagger 0.21 版 TypeScript SDK 参考文档中的类型别名ChangesetWithChangesetOpts展开,讲解该参数对象在Changeset.withChangeset()合并操作中的作用、其唯一可选属性onConflict的取值枚举(ChangesetMergeConflict的五种冲突处理策略),并结合开源仓库源码说明底层基于 git 三方合并的冲突处理实现,帮助你掌握如何安全地合并多个 Changeset(工作区变更集)。
类型定义:ChangesetWithChangesetOpts 是什么
在 0.21 版的 TypeScript SDK 参考文档中,该类型被定义为一个对象类型别名:
> **ChangesetWithChangesetOpts** = `object`它只包含一个可选属性:
| 属性 | 类型 | 说明 |
|---|---|---|
onConflict? | ChangesetMergeConflict | 合并发生冲突时(What to do on a merge conflict)要采取的行为 |
该定义的原始出处是 TypeScript SDK 生成的客户端代码 client.gen.ts:
export type ChangesetWithChangesetOpts = { /** * What to do on a merge conflict */ onConflict?: ChangesetMergeConflict }与之相邻的还有另一个结构几乎相同的类型ChangesetWithChangesetsOpts(见 client.gen.ts),它服务于withChangesets()批量合并方法,唯一区别是其onConflict属性使用的是ChangesetsMergeConflict枚举——这一点在文末的对比小节中会展开。
onConflict:ChangesetMergeConflict 的五个冲突策略
onConflict的取值来自ChangesetMergeConflict枚举,在 client.gen.ts 中定义为五种策略:
export enum ChangesetMergeConflict { /** * Attempt the merge and fail if git merge fails due to conflicts */ Fail = "FAIL", /** * Fail before attempting merge if file-level conflicts are detected */ FailEarly = "FAIL_EARLY", /** * Let git create conflict markers in files. For modify/delete conflicts, * keeps the modified version. Fails on binary conflicts. */ LeaveConflictMarkers = "LEAVE_CONFLICT_MARKERS", /** * The conflict is resolved by applying the version of the calling changeset */ PreferOurs = "PREFER_OURS", /** * The conflict is resolved by applying the version of the other changeset */ PreferTheirs = "PREFER_THEIRS", }各策略的语义与适用场景:
FAIL(默认语义上的兜底策略):先尝试执行 git 合并,如果合并因冲突失败则报错。适合"能合则合、合不上就停下来"的场景。FAIL_EARLY:在真正尝试合并之前,先做文件级冲突检测,一旦发现双方修改了同一个文件就立即失败。好处是失败时不会留下任何部分合并结果,且报错信息更直接。LEAVE_CONFLICT_MARKERS:让 git 在文件中写入冲突标记(<<<<<<<等)继续往下走;对于"一方修改、一方删除"的冲突保留被修改的版本;遇到二进制文件冲突则失败。适合把冲突文件留给人工或后续步骤裁决的流程。PREFER_OURS:所有冲突都以调用方(即执行withChangeset的那个 Changeset)的版本为准解决。PREFER_THEIRS:所有冲突都以传入的另一个 Changeset 的版本为准解决。
需要注意一个版本前提:从核心源码 changeset.go 可以看到,FAIL_EARLY和FAIL两个枚举值被AfterVersion("v0.15.0")版本门控——引擎 0.15.0 之前的旧版 SDK 代码生成只会暴露带作用域前缀的枚举值,而这两个值自 v0.15.0 起才以独立枚举形式注册。也就是说,使用FAIL/FAIL_EARLY时前提是你的引擎版本不低于 0.15.0;而LEAVE_CONFLICT_MARKERS、PREFER_OURS、PREFER_THEIRS则是直接注册、不受此限制。
在 withChangeset() 中的实际使用
ChangesetWithChangesetOpts是Changeset.withChangeset()方法的第二个可选参数。该方法声明于 client.gen.ts,文档注释明确说明了默认行为:
/** * Add changes to an existing changeset * * By default the operation will fail in case of conflicts, for instance a * file modified in both changesets. The behavior can be adjusted using * onConflict argument * @param changes Changes to merge into the actual changeset * @param opts.onConflict What to do on a merge conflict */ withChangeset = ( changes: Changeset, opts?: ChangesetWithChangesetOpts, ): Changeset => { const metadata = { onConflict: { is_enum: true, value_to_name: ChangesetMergeConflictValueToName, }, } const ctx = this._ctx.select("withChangeset", { changes, ...opts, __metadata: metadata, }) return new Changeset(ctx) }典型用法示例:
// 合并两个变更集,冲突时保留"我们的"版本 const merged = workspaceChangeset.withChangeset(anotherChangeset, { onConflict: ChangesetMergeConflict.PreferOurs, }); // 或者:先检测冲突,冲突时立即失败,避免产生部分合并结果 const safeMerge = workspaceChangeset.withChangeset(anotherChangeset, { onConflict: ChangesetMergeConflict.FailEarly, });不传opts时,方法按默认的"遇冲突即失败"语义执行;当传入onConflict时,SDK 通过__metadata中的value_to_name映射把枚举值转换为 GraphQL 输入名(如PREFER_OURS)再下发给引擎。
源码层面:冲突策略如何落地
该参数最终被映射到引擎核心层的WithChangeset实现,位于 core/changeset.go。其核心逻辑是:
- 分别计算两侧 Changeset 的文件路径列表(
ComputePaths); - 用
ourPaths.CheckConflicts(theirPaths)做文件级冲突预检测; - 若检测到冲突且策略为
FailEarlyOnConflict,直接返回冲突错误,不做合并——这正是FAIL_EARLY与FAIL的本质区别; - 否则物化双方的变更内容(
ch.content(ctx)/other.content(ctx)),调用gitMergeChangesets执行git 三方合并(3-way merge); - 用合并结果构造新的 Changeset(
newChangesetFromMerge)。
核心的 Go 枚举定义与 GraphQL 层枚举一一对应,见 core/changeset.go:
const ( // FailEarlyOnConflict fails before attempting merge if file-level conflicts are detected. FailEarlyOnConflict WithChangesetMergeConflict = iota // FailOnConflict attempts the merge and fails if git merge fails due to conflicts. FailOnConflict // LeaveConflictMarkers lets git create conflict markers in files. ... LeaveConflictMarkers // PreferOursOnConflict uses -X ours strategy and resolves modify/delete by preferring ours. PreferOursOnConflict // PreferTheirsOnConflict uses -X theirs strategy and resolves modify/delete by preferring theirs. PreferTheirsOnConflict )从这段源码可以推断出 TypeScript 枚举文档注释背后的具体实现机制:PREFER_OURS/PREFER_THEIRS实际上是 git 的-X ours/-X theirs合并策略,并且在 modify/delete 冲突上额外强制偏向"保留修改版"。GraphQL schema 侧的字段注册(withChangeset节点)位于 core/schema/directory.go,其中还特别注明两个合并相关字段使用不同的枚举类型。
与 ChangesetWithChangesetsOpts 的对比:批量合并时的限制
当需要把多个Changeset 一次性合并进同一个变更集时,应使用withChangesets(),其参数类型为ChangesetWithChangesetsOpts,onConflict使用ChangesetsMergeConflict枚举。SDK 注释(client.gen.ts)指出:
批量合并使用 git 的octopus merge(章鱼合并)策略,比链式调用多个
withChangeset更高效;但只支持FAIL和FAIL_EARLY两种冲突策略(octopus merge 无法使用-X ours/theirs)。
核心实现 core/changeset.go 印证了这一点:
- 会先过滤掉空 Changeset(并受
maxParallelChangesets = 8的并发上限约束); - 若只剩一个待合并对象,则退化为更高效的两路合并
WithChangeset,策略映射为FailEarlyOnConflicts → FailEarlyOnConflict、其余 →FailOnConflict; - 否则执行 octopus merge 前的成对冲突检测(
checkAllPairwiseConflicts),在FAIL_EARLY策略下检测到冲突即返回错误。
因此选用哪个参数对象/枚举的决策原则是:两个变更集合并用ChangesetWithChangesetOpts(五种策略全可用);三个及以上批量合并用ChangesetWithChangesetsOpts(仅限FAIL/FAIL_EARLY)。
小结
ChangesetWithChangesetOpts虽然只是一个含单个可选属性的参数对象,但它承载了 Dagger 工作区变更集合并的核心安全语义:默认遇冲突即失败,可通过onConflict显式声明FAIL_EARLY、FAIL、LEAVE_CONFLICT_MARKERS、PREFER_OURS、PREFER_THEIRS五种处理策略(其中前两者要求引擎 ≥ 0.15.0)。底层由 git 三方合并驱动,PREFER_*对应-X ours/theirs,LEAVE_CONFLICT_MARKERS会把冲突标记留在文件中供后续处理。相关参考文档可进一步查阅 Changeset 类 与 ChangesetMergeConflict 枚举 页面,以及本类型别名原文 ChangesetWithChangesetOpts.md。
【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考