Skip to content

fix(integration-branch): preserve worktree paths with line breaks - #8603

Merged
huangruiteng merged 5 commits into
loopx-project:mainfrom
gcl-coder:fix/integration-worktree-nul
Nov 12, 2026
Merged

huangruiteng merged 5 commits into
loopx-project:mainfrom
gcl-coder:fix/integration-worktree-nul

Conversation

@gcl-coder

Copy link
Copy Markdown
Contributor

Problem

_worktree_for_branch reads git worktree list --porcelain as LF-delimited text. Git prints each worktree path verbatim, so a registered worktree whose directory name contains LF or CR is split into several fields. The resolver can then return the path prefix instead of the real checkout.

This matters because the caller uses that result for worktree cleanliness checks, reset, cleanup, and other integration-branch operations. A torn path can point at the wrong checkout.

The previous repair handled str.splitlines() separators such as U+0085, but LF and CR remain record/field boundaries in the text-oriented output.

Change

  • Run git worktree list --porcelain -z as bytes.
  • Split records on the documented NUL pair and fields on NUL, decoding only the complete path field.
  • Share the existing timeout and owned-process retirement path between text and byte Git reads.
  • Add real Git worktree cases for both LF and CR in the checkout path while retaining the U+0085 regression.

Validation

  • python3 -m pytest tests/capabilities/test_integration_branch.py -q -> 17 passed on the new tree.
  • The focused resolver tests -> 3 passed, including real LF and CR worktree paths.
  • ruff check and git diff --check pass.

The behavioral reproduction is POSIX-only because the test creates directory names containing LF and CR. Git's NUL-framed inventory is used for both platforms; Windows-specific filesystem behavior was not independently exercised.

`git worktree list --porcelain` frames fields on LF and prints paths
verbatim. A worktree path containing LF or CR was split into multiple
fields, so `_worktree_for_branch` could return a prefix and cleanup or
reset could target the wrong checkout.

Read `worktree list --porcelain -z` as bytes, split records on NUL pairs
and paths on NUL, and keep the bounded Git process lifecycle shared by
text and byte reads. Add real LF and CR worktree regressions.

Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent · gpt-6.1-sol · OpenAI · runtime_reported · max

本次评审对应提交 64347d95515f6cb2df645a341f0a09e6bdacfa93。

[P2] 保留 Git 2.35 上普通路径的同步。 真实 Git 2.35.8 上,普通路径的 configure 和 preview 均成功,基线 execute 成功,新提交的 execute 返回 invalid_state,Git 报 unknown switch z。 无条件启用 -z

动机 / Motivation

维护本地集成分支并用已评审源分支定期同步的使用者。原路径解析会把 LF 或 CR 当成字段边界。前缀恰好是另一工作树时,基线重置了另一分支,直到最终读回才报错。新提交按 NUL 读取路径。含 LF 的实际目标通过同步,含 CR 的脏目标在写入前拒绝,两个用例的相邻分支都保持原提交。使用 macOS 上隔离的真实 Git 2.35.8 和 2.56.0、真实 CLI 及本地文件系统。Windows 和托管 CI 未验证。真实 Git 2.35.8 上,普通路径的 configure 和 preview 均成功,基线 execute 成功,新提交的 execute 返回 invalid_state,Git 报 unknown switch z。

改动思路 / Approach

保留原 Git I/O、进程回收与发布前检查。在工作树枚举处确认格式兼容性,并按实际注册路径作出决定。 现有 Git 进程管理可以共用。路径字段应保留字节边界,解码后再判断实际工作树是否干净。

具体改动 / Changes

关键代码讲解

按已有同步合同验收。EXACT-ATTACHED-PATH 和 DIRTY-TARGET-REFUSAL 通过,SUPPORTED-SYNC-COMPATIBILITY 未满足。 Git 2.56 上,基线遇到 LF 路径时改动了前缀同名的另一分支。新提交只更新配置的分支,实际 checkout 的 tree 与预览一致。脏 CR 路径保持未跟踪文件、两条分支和原计划。解析只在找到完整路径字段后才解码,由原有的 clean 检查决定是否可写。 工作树解析

对主干的风险 / Risks

真实 Git 2.35.8 上,普通路径的 configure 和 preview 均成功,基线 execute 成功,新提交的 execute 返回 invalid_state,Git 报 unknown switch z。 旧版 Git 的原始命令退出 129。新提交保留了分支和原计划,但无法同步任何需要更新候选的普通工作树。 2.36 引入了此选项。 Git 2.36 发行说明

语义与 CI 对齐

用相同的真实 configure、preview、execute 和 status 命令验证两个提交。Git 2.56 下 17 项能力测试、Ruff、DCO 和 diff 检查通过。 Git 2.35.8 下预览仍通过,执行兼容性失败。保留旧版本失败与现代 Git 正反例。

我的整体评价 / Verdict

建议保留现代 Git 的 NUL 路径修复,并补齐旧版本的执行兼容性及回归用例。同步成功后继续读回实际分支和计划。 本次改动涉及 CLI 与本地 Git。 未验证 Windows。

English verdict: REQUEST_CHANGES. The NUL parser fixes wrong-worktree resets on modern Git. Git 2.35.8 now rejects ordinary sync --execute. Preserve a safe supported path or explicitly qualify a new Git minimum.


def _worktree_for_branch(repo: Path, branch: str) -> Path | None:
result = _git(repo, "worktree", "list", "--porcelain")
result = _git_bytes(repo, "worktree", "list", "--porcelain", "-z")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 保留旧版 Git 的普通路径同步。 用实际 Git 2.35.8 在干净的普通 linked worktree 复现。主干的 configure、预览和 sync --execute 均成功。当前提交预览成功,但执行因 unknown switch z 返回 invalid_state。请保留可验证的旧版路径处理,或者先确认新的最低 Git 版本并在入口和文档中明确要求。已有普通路径、带 LF 的精确 checkout 和脏 CR 路径都需要保留验证。 Git 2.36 发行说明

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 a0877755315f891127ea72bc2458227228422c81 修复。

_worktree_for_branch 先尝试 NUL 分帧;Git 2.35 返回 unknown switch/option 时,回退到 worktree list --porcelain 的 LF 记录,并严格校验 worktree/HEAD/branch 等字段结构。普通路径(包括带空格路径)仍能解析并进入 sync --execute;含 LF 或 CR 的路径在旧格式下无法无损表示,会明确返回 Git 2.36+ 说明并 fail closed,不再返回路径前缀或误重置其他工作树。

新增回归:

  • legacy Git stub 拒绝 -z 时,普通 worktree 解析和完整 sync --execute 成功,并验证 linked worktree 的 HEAD 等于 candidate;
  • LF 与 CR 路径在 legacy stub 下抛出明确错误;
  • 现代 Git 的 NUL 分帧 LF/CR 用例保留。

本地 tests/capabilities/test_integration_branch.py 21 项通过,Ruff 与 git diff --check 通过。远端 CI 刚推送,当前仍在排队,最终结果以实际检查状态为准。

Git 2.35 rejects 'worktree list -z' with an unknown switch, so every ordinary sync failed before resolving its checkout. Fall back to the line-framed porcelain inventory and keep only structural records; paths that a line-framed format cannot represent now fail closed with an explicit Git 2.36 message instead of resolving a prefix.

Add regressions using a legacy Git stub for ordinary worktree resolution and sync --execute, plus LF and CR paths that must be refused.

Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent · gpt-6.1-sol · OpenAI · runtime_reported · reasoning_effort=max

评审提交 a087775。

[P2] 保留 bare 仓库的普通 linked checkout 同步。 从 bare 仓库的普通 linked checkout 执行,Git 2.35.8 在清单第一条返回 bare 字段。主干成功同步,当前提交在实际路径不含换行时也退出 2 并报告换行错误。 相关代码

[P2] 验证 checkout 的分支归属后再重置。 Git 2.35.8 上,含 LF 的目录名可以形成看似完整的记录。当前提交把候选写到相邻分支,配置的集成分支保持原提交,计划文件已改写,随后 sync 才退出 2。 相关代码

动机 / Motivation

用 LoopX 同步本地集成分支的开发者和定时任务。 当工作树目录名包含换行,旧解析可能截断路径,把源分支的更新写到另一条分支。当前提交在 Git 2.56.0 上能定位真实工作树,并在脏工作树上停止。 同步应只更新配置中的集成分支,并在无法确认工作树时于写入前停止。 本次评审覆盖现有本地同步流程及 Git 兼容性。 Git 2.35.8 的回退仍有一个正常工作树被拒绝的回归,以及一个可重置相邻分支的缺口。

改动思路 / Approach

保留现代 Git 的 NUL 分帧和现有 Git 进程回收。旧格式仅应提供候选路径,写入前需从实际 checkout 确认分支和所属仓库。 保留 NUL 读取和现有 Git 进程管理,收敛旧版回退到经过证明的 checkout。 Git 注册表拥有工作树与分支的对应关系,integration-branch 的写入函数负责核实操作对象。

具体改动 / Changes

关键代码讲解

  • _git。文本命令沿用原有超时与进程回收,读取普通 Git 响应。
  • _git_bytes。同一命令执行器按原始字节读工作树清单,保留 Git 的 NUL 记录边界。
  • _worktree_for_branch。优先用现代 Git 的完整路径记录定位配置分支,旧版转入兼容解析。
  • _worktree_for_branch_line_framed。旧版回退假定每条记录都有 HEAD,也接受路径内可组成的提前记录边界。
  • _update_integration_branch。在确认引用未移动且目标路径干净后重置当前 checkout,实际分支归属仍需在写入前核实。

依据 loopx/capabilities/integration_branch/README.md(3a5899f00cab97294c20459ac8508d16dc317492)的 Preview and sync 段落核对三项。EXACT-ATTACHED-PATH 在旧版的构造路径未满足,当前失败发生在写入之后。DIRTY-TARGET-REFUSAL 通过真实 Git 2.56.0 的脏 CR 用例。SUPPORTED-SYNC-COMPATIBILITY 已恢复普通路径,bare common dir 的正常路径仍未满足。

从 64347d9 到当前提交新增旧版回退。我复验了上一轮的 Git 2.35.8 普通路径,当前可以完成配置、预览、执行和 in_sync 读回。完整差异另包含现代路径处理和 Git 读取抽取,21 项定向测试通过。Git 规定 bare 记录可以只有 bare 字段。 Git porcelain 格式

对主干的风险 / Risks

从 bare 仓库的普通 linked checkout 执行,Git 2.35.8 在清单第一条返回 bare 字段。主干成功同步,当前提交在实际路径不含换行时也退出 2 并报告换行错误。 识别合法的 bare 记录并跳过它,在同一清单继续核实目标 linked checkout。 Git 2.35.8 上,含 LF 的目录名可以形成看似完整的记录。当前提交把候选写到相邻分支,配置的集成分支保持原提交,计划文件已改写,随后 sync 才退出 2。 在任何 reset 前核对候选的实际分支和 Git common directory。清单无法证明分支归属时停止,保留全部引用和计划。 现有用例覆盖了普通旧版路径和简单 LF/CR,但没有覆盖合法 bare 记录或路径内容碰到格式字段的情况。

语义与 CI 对齐

使用真实 Git 2.35.8 执行 integration-branch configure、sync 和 sync --execute,分别验证 bare common dir 与含 LF 的相邻路径。 预览必须保持只读,失败时应从实际引用和计划文件读回零写入。独立运行覆盖 macOS 上的 Git 2.35.8 和 2.56.0,Windows 运行时与托管 CI 保持未验证。

我的整体评价 / Verdict

保留现代 Git 的路径修复。持续同步在普通旧版 checkout 上已恢复,但两条旧版路径仍会让操作者中断或需要恢复分支。用上述两条真实旧版 Git 用例确认回退后再复审。

English verdict: REQUEST_CHANGES at a087775. Git 2.35.8 still rejects an ordinary linked checkout of a bare repository and can reset a neighbouring branch when a path resembles porcelain fields.

# The porcelain format puts exactly one HEAD field directly after the
# worktree line. A path containing LF or CR shifts that field, so the
# record is no longer safely attributable to a checkout.
if index >= len(lines) or not lines[index].startswith("HEAD "):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 保留 bare 仓库的普通 linked checkout 同步。

从 bare 仓库的普通 linked checkout 执行,Git 2.35.8 在清单第一条返回 bare 字段。主干成功同步,当前提交在实际路径不含换行时也退出 2 并报告换行错误。 识别合法的 bare 记录并跳过它,在同一清单继续核实目标 linked checkout。 用真实旧版 Git 建立 bare common dir 与带空格的 linked checkout,要求原生 sync 返回 in_sync。

@gcl-coder gcl-coder Nov 12, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已按这两点补齐,HEAD 298b14b2c8373627aa2fa5ce9b499a8a419b164a:

  • 旧格式回退现在识别并跳过唯一的 bare 主工作树记录;其余记录必须恰好有一个 HEAD,重复 HEAD、bare 与 HEAD 混合、以及没有 HEAD 的 branch/detached 记录都会 fail closed。
  • 重置前新增 checkout 归属校验:从目标 checkout 读取真实 --show-toplevel、--git-common-dir 和 symbolic-ref HEAD,分别核对路径、共同仓库和配置集成分支。校验失败发生在 clean/reset 和计划写入之前,错误 checkout 的引用与计划都不会变化。
  • 回归覆盖:裸仓库加普通 linked checkout 的旧格式清单(跳过 bare 记录后正常同步并读回 in_sync);解析结果错误地落到另一分支 checkout 时必须拒绝重置,且两个引用和计划保持原值。

本地 tests/capabilities/test_integration_branch.py 为 23 passed,Ruff 和 git diff --check 通过。请复审。

raise IntegrationBranchError(_LEGACY_WORKTREE_PATH_ERROR)
index += 1
if branch_ref == target_ref:
return path

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 验证 checkout 的分支归属后再重置。

Git 2.35.8 上,含 LF 的目录名可以形成看似完整的记录。当前提交把候选写到相邻分支,配置的集成分支保持原提交,计划文件已改写,随后 sync 才退出 2。 在任何 reset 前核对候选的实际分支和 Git common directory。清单无法证明分支归属时停止,保留全部引用和计划。 在真实旧版 Git 中断言两个分支、各 checkout 和计划文件在拒绝时均未写。

…rship

The line-framed fallback rejected a bare repository's main worktree record because it expected a HEAD field, so an ordinary linked checkout could not sync on Git 2.35. Accept records that carry the bare field and skip them, while requiring every other record to be a complete HEAD-framed checkout.

Verify the top-level path, common Git directory and attached branch before resetting a checkout. A worktree path containing line breaks can still fabricate a complete record in the line-framed inventory, so ownership is confirmed from the actual checkout before any write.

Regressions cover a bare repository with a linked integration worktree under a legacy Git stub and a parsed checkout on another branch that must not be reset or recorded in the plan.

Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent · gpt-6.1-sol · OpenAI · runtime_reported · reasoning_effort=max

评审提交 298b14b,对照当前主干 9ce3a46 和上一评审提交 a087775。

[P2] 保留 checkout 路径中的原始字符。 Git 2.35.8 上恢复普通 bare 仓库的同步,并在重置前拒绝伪造记录。Git 2.56.0 上,干净的含 CR、末尾 LF 或末尾空格的路径被误拒,上一提交三种路径都能同步。 三种路径均能预览候选树。执行退出 2,提示不是 checkout 根。分支、计划与其他引用均未改。 用现有字节读取处理两个 rev-parse 路径输出,只去掉 Git 添加的终止符,保留目录名的原始字符。 路径读回

[P2] 恢复旧 Git 在本地化环境下的同步。 同一实际 Git 2.35.8 在 C 和法语 locale 都以 129 拒绝 -z。主干两者都能同步,候选在法语环境拒绝普通路径,保留分支和计划。 按可核验的 Git 特性选择格式,或对特性探测固定诊断语言。其他 Git 失败保持拒绝。 回退判定

动机 / Motivation

用 LoopX 定期同步本地集成分支的开发者和定时任务。 旧版 Git 会把含换行的实际路径截到另一 checkout,上一提交也会拒绝普通 bare 仓库。 Git 2.35.8 上恢复普通 bare 仓库的同步,并在重置前拒绝伪造记录。Git 2.56.0 上,干净的含 CR、末尾 LF 或末尾空格的路径被误拒,上一提交三种路径都能同步。 本轮验证覆盖 macOS 隔离仓库与实际 CLI。Windows、生产安装和托管 CI 未验证。

改动思路 / Approach

本次完整改动限于现有 integration-branch CLI 的 Git 读取、工作树核验和对应测试。 Git 拥有实际工作树、HEAD 和 common directory,原计划决定本次本地更新的分支。 保留发布前的仓库和分支归属核验,在同一个 Git I/O owner 中保真读回路径。 NUL 解析单测能保留 CR/LF,完整 sync 随后还会通过 rev-parse 二次读回路径。 真实旧 Git 中,bare 第一条记录可跳过,普通 linked checkout 的结果树等于预览。构造假记录时,上一提交改写相邻源分支和计划,当前在写入前拒绝。脏 CR checkout 和未跟踪文件保留。

具体改动 / Changes

关键代码讲解

按 loopx/capabilities/integration_branch/README.md 在 3a5899f 的同步合同核对。

条款 读回
EXACT-ATTACHED-PATH 普通目录和中间 LF 可同步。CR、末尾 LF、末尾空格三项预览后误拒。
DIRTY-TARGET-REFUSAL 脏 CR 工作树的未跟踪文件、分支和计划保留。
SUPPORTED-SYNC-COMPATIBILITY 旧 Git 的 bare 正常路径恢复。同一实际 Git 2.35.8 在 C 和法语 locale 都以 129 拒绝 -z。主干两者都能同步,候选在法语环境拒绝普通路径,保留分支和计划。
LOCAL-WRITE-BOUNDARY 伪造记录在重置前被拒绝,原计划及两个分支保留。

对主干的风险 / Risks

三种路径均能预览候选树。执行退出 2,提示不是 checkout 根。分支、计划与其他引用均未改。 真实 Git 返回的路径字节与工作树登记一致。Python 文本读取把 CR 变为 LF,随后 strip() 移除路径末尾的空白。候选在写入前退出,同一计划可以在修复后重试。真实旧 Git 中,bare 第一条记录可跳过,普通 linked checkout 的结果树等于预览。构造假记录时,上一提交改写相邻源分支和计划,当前在写入前拒绝。脏 CR checkout 和未跟踪文件保留。

语义与 CI 对齐

23 项能力测试、显式隔离运行时下原 CLI smoke、Ruff、完整差异检查与当前主干合入模拟通过。 隐式路径下原 smoke 先命中本机双运行时提示,指定隔离路径后通过。本轮验证覆盖 macOS 隔离仓库与实际 CLI。Windows、生产安装和托管 CI 未验证。

我的整体评价 / Verdict

请求修改。用现有字节读取处理两个 rev-parse 路径输出,只去掉 Git 添加的终止符,保留目录名的原始字符。 按可核验的 Git 特性选择格式,或对特性探测固定诊断语言。其他 Git 失败保持拒绝。 保留分支归属核验,并补上三种现代路径的完整同步回归。

English verdict: REQUEST_CHANGES. Actual Git 2.56 rejects clean linked worktrees containing CR or ending in LF or a space. The previous head synchronized all three. The two earlier legacy fixes pass, while the locale-dependent fallback rejects an ordinary path under French Git 2.35.8 diagnostics.


expected_ref = f"refs/heads/{branch}"
try:
top = _git(worktree, "rev-parse", "--show-toplevel").stdout.strip()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 三种路径均能预览候选树。执行退出 2,提示不是 checkout 根。分支、计划与其他引用均未改。 用现有字节读取处理两个 rev-parse 路径输出,只去掉 Git 添加的终止符,保留目录名的原始字符。 用三种实际路径跑完整 sync --execute,核对工作树和计划。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 4b4688ec789ec893a0666c6a9474a3460e2970cd 修复。checkout 归属校验中的 --show-toplevel 和 --git-common-dir 现在走字节读取,只移除 Git 追加的一个最终换行符,再用 os.fsdecode 保留路径自身字符;symbolic-ref 也不再经过文本模式。

新增完整 sync --execute 回归,分别覆盖目录名以 LF、CR 和空格结尾:执行后工作树 HEAD 等于候选、计划完成写入,并读回 in_sync。本地 27 项能力测试、Ruff 和 git diff --check 均通过。请复审。

# NUL framing arrived in Git 2.36. Older Git rejects the switch before
# writing a record; keep ordinary paths working through the line-framed
# inventory, which stays fail-closed for values it cannot represent.
if "unknown switch" not in str(exc).lower() and "unknown option" not in str(exc).lower():

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 同一实际 Git 2.35.8 在 C 和法语 locale 都以 129 拒绝 -z。主干两者都能同步,候选在法语环境拒绝普通路径,保留分支和计划。 按可核验的 Git 特性选择格式,或对特性探测固定诊断语言。其他 Git 失败保持拒绝。 同一真实 Git 二进制在 C 和 fr_FR.UTF-8 都执行并读回。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 4b4688ec789ec893a0666c6a9474a3460e2970cd 修复。格式选择不再解析本地化的 usage 错误:先读取 git version,Git 2.36 及以上使用 worktree list --porcelain -z,更早版本进入原有 line-framed 回退;版本无法解析时 fail closed,其他 Git 读取失败仍直接拒绝。

回归中的 legacy Git wrapper 报告 2.35.8,并返回法语 localized -z 诊断、设置 LC_ALL=fr_FR.UTF-8;普通 linked worktree 仍能解析和同步。本地 27 项能力测试、Ruff 和 git diff --check 均通过。请复审。

…at by version

Read the checkout path and common directory as bytes and remove only the newline Git appends, so a valid worktree name ending in LF, CR or whitespace is compared exactly before any reset.

Choose the worktree inventory format from `git version` instead of parsing a localized usage error. Git 2.36 added `worktree list -z`; older releases keep using the fail-closed line-framed inventory.

Add full sync regressions for those path suffixes and a localized legacy-Git diagnostic.

Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent · gpt-6.1-sol · OpenAI · runtime_reported · reasoning_effort=max

复审提交:4b4688ec789ec893a0666c6a9474a3460e2970cd。
[P2] 同步入口仍会丢掉调用路径末尾的换行,并写入相邻仓库。 仓库根识别

动机 / Motivation

从本地 checkout 运行集成分支同步的开发者与定时任务。 这版修复了目标 worktree 的换行路径和旧版 Git 的语言差异。如果带换行的是调用命令的 checkout,入口仍可能选中另一个仓库。 在实际 Git 2.56.0 上,合并基点和当前提交都复现了指定仓库未改、相邻仓库却完成同步。只把入口改为复用现有字节读取的隔离对照会正确提示指定仓库缺计划,两边都保持原样。 实测 macOS 和隔离 Git 仓库。Git 2.56.0、2.35.8 和真实法语诊断均覆盖。本轮未运行 Windows、生产安装或远端 CI。

入口的仓库识别仍会丢失路径字符,需要和本 PR 已修的 checkout 验证共用读取规则。 集成分支同步合同 要求更新配置的本地集成分支。把调用路径的完整性纳入同一合同。 核验项:target_checkout_bytes_and_old_git_preserved, writes_remain_in_requested_repository。

改动思路 / Approach

Git 是 checkout 根、common directory 和分支归属的来源,计划只在正确解析的仓库内生效。复用新增字节路径读取,覆盖入口与重置前核验。 保留既有 NUL 分帧、旧 Git 选择和执行权限,只补同一 Git I/O 路径的仓库根识别。 当前对目标使用 NUL 清单和字节路径。旧 Git 按版本进入原格式检查。入口查找计划也应使用同一个路径读取规则。 Git worktree 文档

具体改动 / Changes

  • Git 版本和清单 真实 Git 2.35.8 的 C 和法语诊断均拒绝 -z,当前普通 checkout 仍同步。普通 bare linked checkout 同步,歧义清单拒绝且不改变分支和计划。
  • 目标归属核验 真实 Git 2.56.0 在普通、内嵌 LF/CR、末尾 LF/空格的目标目录都同步到预览树。脏 CR checkout 保持原文件和分支。 上一提交 298b14b 对 CR、末尾 LF、末尾空格及法语旧 Git 均退出 2。当前对应路径成功并读回预览树。
  • 现有字节路径 helper 隔离对照只复用新字节 helper 进行入口读回,结果 exit 2 / 指定仓库缺计划,两仓库 refs 和计划不变。 原普通同步和 bare linked checkout 在合并基点及当前通过。能力测试合并基点 15 项,当前 27 项。

对主干的风险 / Risks

调用目录末尾有 LF,旁边存在去掉该 LF 后同名的独立仓库。对前者执行 sync,命令返回 synced,却改了后者的集成分支和计划。两边源分支不变。 这项故障在合并基点就存在,仍直接影响本 PR 所核对的仓库和 checkout 归属。隔离对照确认只修入口读法就能让同步按指定目录查计划。实际 Git 两版本、Ruff、签署、差异、双语 RFC 和主干合入模拟均已检查。

我的整体评价 / Verdict

请求修改。进入计划解析前要先保留用户给出的仓库路径,使后续归属核验基于正确仓库。 让 _repository_root 也复用 _git_path_output,入口只移除 Git 添加的一个终止符。补带换行的调用 checkout 与独立相邻仓库的完整同步对照。 正常目标路径、旧 Git 和脏目标的现有回归应继续通过。

English verdict: REQUEST_CHANGES. Head 4b4688e. The three target-path cases and localized Git 2.35.8 now pass. Repository-root discovery still strips a trailing LF and can update a different repository on both base and head. Reuse the byte-path reader before looking up the plan.

return candidate.resolve()


def _git_path_output(repo: Path, *args: str) -> str:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 调用目录末尾有 LF,旁边存在去掉该 LF 后同名的独立仓库。对前者执行 sync,命令返回 synced,却改了后者的集成分支和计划。两边源分支不变。 基点同样存在。请在 _repository_root 复用这个 helper。实际 Git 从带末尾 LF 的 checkout 调用 sync;无本地计划应拒绝,另一独立仓库的 ref 和计划必须保持。再覆盖正常配置的带 LF checkout。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 edd731add9a782e49b2e143915953acaed0bc1b6 修复。_repository_root 现在复用 _git_path_output,通过字节模式读取 --show-toplevel,只移除 Git 追加的一个最终换行符,再交给 Path.resolve()。

新增两项完整同步回归:两个独立仓库目录名只差调用路径末尾的 LF。已配置的带 LF checkout 会同步自己的集成分支和计划,而相邻仓库的 ref 与计划保持不变;没有本地计划的带 LF checkout 会以 plan not found 拒绝,相邻仓库同样保持不变。

本地能力测试现在为 29 passed,Ruff 和 git diff --check 通过。请复审。

Resolve the repository root through the byte path reader so a checkout whose path ends in LF or whitespace is not redirected to an adjacent repository. The entry point now strips only the terminal newline emitted by Git, matching the ownership checks added for reset.

Add full sync regressions for two independent repositories whose names differ by a trailing LF. The configured checkout must sync itself while leaving its neighbor's integration ref and plan unchanged, and a checkout without a local plan must fail before reading the neighbor.

Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent · gpt-6.1-sol · OpenAI · runtime_reported · reasoning_effort=max

复审提交:edd731add9a782e49b2e143915953acaed0bc1b6。
通过。上轮发现的带换行调用目录会误写相邻仓库的问题,已在本提交修复。

动机 / Motivation

从本地 checkout 同步集成分支的开发者和定时任务。 以末尾有 LF 的 checkout 调用同步时,合并基点和上一版会同步另一个同名仓库;当前版本按原始路径查找计划。 实际 Git 2.56.0 对照:请求仓库没有计划时当前返回 exit 2 / plan not found,两个仓库的集成分支和相邻计划均未变。 独立验证为 macOS 上的实际 Git 2.56.0、2.35.8 及源码 CLI。未验证 Windows、已安装版本或远端 CI。

在本次同输入及兼容对照中未发现剩余阻塞项。 依据 loopx/capabilities/integration_branch/README.md(3a5899f00cab97294c20459ac8508d16dc317492)。 核验项:IB-REQUESTED-REPO, IB-CHECKOUT-PARITY, IB-FAIL-CLOSED。

改动思路 / Approach

现有 Git 命令执行器与集成分支 capability 继续负责取路径、核对归属和写入;调用根及目标共用路径解码。 仅修复现有同步的 Git 路径与归属处理;没有新 CLI、状态或写入权限。 解析实际仓库及计划 → 预览候选 → 校验归属 → 更新选定 ref 和计划。 只复用现有 Git IO 与检查,不新增 API 或存储。

具体改动 / Changes

普通、内嵌 LF/CR、末尾 LF/空格的实际 checkout 均同步到预览树,其余引用不变;已配置的 LF 调用仓库只更新自身分支。 Git 2.35.8 的 bare linked checkout 正常同步;法语下旧版 -z 诊断退出 129,实际普通同步仍成功;含伪 LF 记录的旧版清单拒绝且不写 refs 或计划。 精确提交的集成测试 29 项通过。

对主干的风险 / Risks

旧 Git 对不能可靠归属的路径会拒绝,代价是需要换到可判定的 checkout 后重试。旧 Git 法语、bare checkout、脏目标和双仓库对照已覆盖。 两项结论都依据实际命令的可观测结果,已安装环境和远端状态另由交付检查判定。 Ruff、签署、diff、双语 RFC 和与 708c68e 的合入模拟已检查。

我的整体评价 / Verdict

上一轮阻塞项在相同输入上已经由误写变为准确拒绝,且同仓库的正例仍可执行。 实际 Git 2.56.0 对照:请求仓库没有计划时当前返回 exit 2 / plan not found,两个仓库的集成分支和相邻计划均未变。 未改变渲染界面;该行为通过源码命令和调用方回读。

English verdict: APPROVE. Head edd731a. The original trailing-LF caller is now resolved without touching the neighboring repository. Verified with real modern and legacy Git, including a localized diagnostic and ambiguous old-format output.

@huangruiteng
huangruiteng merged commit 2b45afd into loopx-project:main Nov 12, 2026
32 of 48 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants