-
Notifications
You must be signed in to change notification settings - Fork 0
Issue 50
Issue #50 · PR #57 · 实现方案与验收记录
- 类型:功能
- 状态:已实现,待真机视觉验收
- Issue:https://github.com/ShaoClean/remote-git/issues/50
- PR:https://github.com/ShaoClean/remote-git/pull/57
- 最近更新:2026-09-20
-
代码基线:
fix/issue-50-image-diff-preview@7ab4eff,基于development@6eaf6f0
在查看包含图片文件变更的 Diff 时,图片只被标记为「二进制文件,无法显示文本差异」,无法直接查看图片内容,也无法比较修改前后的视觉差异。提交或审查 PNG、JPEG、GIF、WebP 等资源时,必须离开应用才能确认改动内容。
代码核对基线:development@6eaf6f0。getDiffNotice(apps/web/src/components/diff-lines.ts)在补丁匹配 Binary files 或 GIT binary patch 时统一返回二进制提示,检查器不区分图片与其他二进制文件。
在 Diff 中识别常见图片文件并提供可直接查看的预览;修改过的图片可同时查看变更前与变更后版本。无法预览的格式继续使用明确的二进制文件提示。
本次包含:
- 新增图片显示变更后版本,删除图片显示变更前版本,修改图片并排显示「变更前 / 变更后」。
- 显示文件名、格式、像素尺寸与文件大小。
- 工作区改动与提交历史两个入口使用同一套预览。
- 无法解码、超出大小限制、格式不受支持时显示明确提示,并回落到既有二进制文件展示方式。
本次不包含:像素级差异计算、图片标注编辑、复杂图像处理、叠加滑块对比模式。
只有「单文件补丁 + 纯二进制补丁 + 可渲染扩展名」三者同时成立才进入图片预览(getImageDiffKind)。文本 hunk、多文件整提交补丁、空新增文件、.svg / .bmp 等一律保持原有行为,因此文本 Diff 与其他二进制文件展示不受影响。
补丁头中的 new file mode / deleted file mode 用于区分新增、删除与修改三种预览模式。
新增 DiffImages(packages/ssh-client/src/diff-images.ts),按比较类型解析两侧对应的 Git 对象,与 git diff 的语义保持一致:
| 场景 | 变更前 | 变更后 |
|---|---|---|
| 未暂存 | 暂存区 :0:file
|
远端工作区磁盘内容(SFTP 读取) |
| 已暂存 | HEAD:file |
暂存区 :0:file
|
| 提交 | 父提交(或显式 parentCommit) |
该提交 |
-
base64 随 JSON 返回,而非新增二进制端点。桌面端 CSP 已是
img-src 'self' data:(apps/desktop/src/main.cjs),使用 data URL 渲染无需放宽任何安全策略。此结论在实现前核对了现有 CSP 配置。 - 某一侧不存在时返回 404,而非 400。新增与删除图片天然缺少一侧,属正常情况而非错误;页面据此显示「此版本不存在该图片」,不会误报为读取失败。
-
大小校验前置到传输之前。先用
cat-file -s(Git 对象)或lstat(工作区文件)判断体积,超过上限直接拒绝,避免传输超限内容。 -
二进制读取不经过文本解码器。
execCommand新增binary模式返回stdoutBytes,图片字节不会被 UTF-8 解码破坏。
- 单张图片上限 5 MiB(
DIFF_IMAGE_MAX_BYTES)。 - 不支持的格式、无法解码、超限、缺少读取权限均回落到既有二进制文件提示,不影响同一 Diff 中的其他内容。
- 预览为只读操作,不写入文件或暂存区。
- Windows 仓库沿用既有路径校验,拒绝反斜杠路径与 NTFS 备用数据流(如
icon.png:stream),且在建立连接前即拒绝。
- 动图(GIF)预览当前由浏览器默认自动播放,是否需要提供暂停控制尚未确认。
- 修改场景首期仅提供并排对比;是否追加叠加滑块模式待评估。
- 新增的常见图片文件在 Diff 中可直接预览。
- 删除的常见图片文件在 Diff 中可查看删除前的图片。
- 修改的图片可同时查看变更前和变更后的版本,并有清晰标识。
- 图片保持正确宽高比,超出可视区域时可自适应缩放(CSS
object-fit: contain与max-height,已在生产构建中包含;真机视觉确认待补充)。 - 不支持、无法解码或超出限制的图片会显示明确提示,不影响其他 Diff 内容。
- 文本文件及其他二进制文件的现有 Diff 行为不受影响。
代码提交 7ab4eff,环境 macOS (darwin arm64) + Node.js 22。
| 日期 | 代码提交 / 环境 | 命令或场景 | 结果与证据 |
|---|---|---|---|
| 2026-09-20 |
7ab4eff / macOS arm64 |
node --test tests/*.test.cjs(packages/ssh-client) |
通过:70 项,0 失败,2 项 Windows-only 跳过 |
| 2026-09-20 |
7ab4eff / macOS arm64 |
新增 tests/diff-images.test.cjs
|
通过:4 项,覆盖三种比较场景、只读性、降级与路径穿越拒绝 |
| 2026-09-20 |
7ab4eff / macOS arm64 |
npm test(apps/web) |
通过:79 项,0 失败,含新增 3 项 |
| 2026-09-20 |
7ab4eff / macOS arm64 |
npx jest src/repository(apps/server) |
通过:12 项,0 失败 |
| 2026-09-20 |
7ab4eff / macOS arm64 |
tsc -b(apps/web) |
通过:0 错误 |
| 2026-09-20 |
7ab4eff / macOS arm64 |
npm run build(apps/server,nest build) |
通过 |
| 2026-09-20 |
7ab4eff / macOS arm64 |
npm run build -w web(vite 生产构建) |
通过 |
| 2026-09-20 |
7ab4eff / macOS arm64 |
apps/server 全量 npm test
|
失败 4 项,属既有问题(见下) |
| — | — | 真机 UI 视觉验收(四种格式、动图播放、窄屏单列) |
未运行:需在桌面端实际打开观察,截图待补充至 assets/issue-50/
|
| — | — | Windows / Linux 平台回归 | 未运行:仅静态校验逻辑有测试覆盖,未在真实平台执行 |
packages/ssh-client/tests/diff-images.test.cjs 使用真实 Git 仓库与回环 SSH 服务:
- 提交比较返回修改图片的两侧字节,且与写入的 PNG 字节完全一致;首个提交无父版本、删除后无新版本均按「缺失」处理。
- 工作区与暂存区比较各自停留在正确的一侧;断言操作前后
.git/index与工作区文件字节完全未变,证明预览只读。 - 不支持的扩展名、超过 5 MiB、目标为目录、路径不存在、非法 side 取值,以及
../outside.png、/etc/icon.png、.git/config.png等穿越路径均被拒绝。 - 超限的暂存 blob 在传输前即被拒绝,且 SSH 连接保持可用。
apps/server 全量测试中,FileService / ConnectionController / FileController / GitController 的 "should be defined" 共 4 项失败。已用 git stash -u 在未改动的干净基线上复现出完全相同的 4 项失败,与本次变更无关,本次未做处理。
- 未验证平台:Windows 与 Linux 未实际运行;相关路径校验逻辑有单元测试覆盖,但无真机记录。不得据此将这两个平台标记为已验收。
- 未验证交互:真机 UI 视觉效果、GIF 自动播放行为、窄屏(≤820px)单列布局均未实际观察。
- 内存与传输:单张上限 5 MiB 且传输前校验;base64 带来约 1.33 倍膨胀,最坏约 6.7 MiB 单次响应。大量图片同时打开的压力未做专门测量。
-
SSH 生命周期:新增读取复用既有连接与
withSftp超时约定;超限时仅关闭当前命令通道,不影响同一连接上的其他工作。 -
既有环境问题(非本次引入):
packages/shared下嵌套的typescript@5.9.3无法识别ignoreDeprecations: "6.0",导致npm run build -w @remote-git/shared失败。该问题在未改动的主工作树上同样复现,本次改用仓库根部的 TypeScript 6.0.3 编译通过。建议另开 Issue 清理该嵌套依赖。 - CI 覆盖不足:当前工作流仅运行 commitlint,不执行单元测试或构建。PR 的绿标只代表提交信息合规,功能证据来自上表的本地执行记录。
-
回退方式:改动集中在新增模块(
diff-images.ts、ImageDiffView.tsx)与判定函数getImageDiffKind,git revert本次提交即可恢复原有二进制提示,无持久化状态残留。
- Issue:https://github.com/ShaoClean/remote-git/issues/50
- PR:https://github.com/ShaoClean/remote-git/pull/57
- 相关设计:Issue-18(新增文件的差异预览,共用
DiffViewer与diff-lines判定链路) - 索引:功能与优化设计
- 代码入口(基线
7ab4eff):-
packages/ssh-client/src/diff-images.ts— 两侧版本解析与读取 -
packages/shared/src/types/repository.ts— 图片类型、大小上限与扩展名映射 -
apps/server/src/repository/repository.controller.ts—GET :id/diff-image -
apps/web/src/components/ImageDiffView.tsx— 并排预览组件 -
apps/web/src/components/diff-lines.ts—getImageDiffKind判定
-
- 用户文档:
docs/workspace.md「文件差异」章节已补充预览范围与限制。