dsh-recall-plugin 2.1.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/lib/scripts.posix.js +30 -0
- package/lib/scripts.pwsh.js +46 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
本文件格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循语义化版本。
|
|
4
4
|
|
|
5
|
+
## [2.1.1] - 2026-08-29
|
|
6
|
+
|
|
7
|
+
issue #12 换行符字节保真修复(patch)。单测 227 项全绿,双平台真实模板端到端复验(pwsh 全新/存量迁移 + POSIX 实弹)31 项全过,`check:dsh` 巡检一致。
|
|
8
|
+
|
|
9
|
+
### 修复
|
|
10
|
+
|
|
11
|
+
- **快照/回退换行符失真(issue #12)**:影子仓库固化为字节保真语义——`info/attributes`(`FIDELITY_ATTRS`)对全部路径关闭 EOL 转换、clean filter、`$Id$` 展开、`export-ignore`/`export-subst`、`working-tree-encoding`。根因是 `git archive`(回退恢复路径)与 `git add`(捕获路径)都会应用快照树里项目自己的 `.gitattributes`:`text=auto` + Windows 缺省 `core.eol=native` 会把 LF 转 CRLF,仓库级 `core.autocrlf=false` 挡不住(属性驱动的转换看 `core.eol`)。存量归一化索引经一次性 `git add --renormalize -- ':(top)'` 迁移(标记文件 `attrs-v1.stamp` 防重复,迁移失败不阻塞快照)。连带修复 `export-ignore` 声明让文件从回退归档中静默消失的同类缺口。注意:回退到本版之前的旧快照仍会还原归一化内容(旧 blob 信息已物理丢失),本版起的新快照字节保真。
|
|
12
|
+
|
|
5
13
|
## [2.1.0] - 2026-08-29
|
|
6
14
|
|
|
7
15
|
改进专项与审查修复、环境诊断批次(错误治理 / POSIX home 三档 / 并发治理)、双平台实弹冒烟(Windows + WSL2 Ubuntu 26.04)后发版。单测 224 项、官方 API 探针、`verify:host`、`check:dsh` 全绿。
|
package/lib/scripts.posix.js
CHANGED
|
@@ -43,6 +43,14 @@ export const STALE_LOCK_MIN = 5
|
|
|
43
43
|
// 仍在窗口内。
|
|
44
44
|
export const HEARTBEAT_TTL_S = 900
|
|
45
45
|
|
|
46
|
+
// 影子仓库 info/attributes 固化内容(issue #12 字节保真):与 pwsh 版同名
|
|
47
|
+
// 常量逐字同值(scripts-contract 钉),完整语义与逐项动机见 scripts.pwsh.js
|
|
48
|
+
// 同名常量注释。一句话:git archive/add 都会应用快照树里项目自己的
|
|
49
|
+
// .gitattributes(text=auto + 缺省 core.eol=native 的转换,仓库级
|
|
50
|
+
// autocrlf=false 挡不住),info/attributes 优先级最高、对全部路径一票否决,
|
|
51
|
+
// 快照 capture/restore 两侧逐字节保真。
|
|
52
|
+
export const FIDELITY_ATTRS = '* -text -filter -ident -export-ignore -export-subst -working-tree-encoding'
|
|
53
|
+
|
|
46
54
|
// bash/cat 输出无 BOM;保留同名导出维持两套模板接口一致(幂等无害)
|
|
47
55
|
export function stripBom(text) {
|
|
48
56
|
return text.replace(/^\uFEFF/, '')
|
|
@@ -201,12 +209,33 @@ export function ensureGitScript(store, gitExe, base) {
|
|
|
201
209
|
'"$git" --git-dir="$g" config core.longpaths true',
|
|
202
210
|
'"$git" --git-dir="$g" config core.autocrlf false',
|
|
203
211
|
'"$git" --git-dir="$g" config advice.addEmbeddedRepo false',
|
|
212
|
+
// 属性固化(issue #12,内容见 FIDELITY_ATTRS):info/ 由 git init 自带
|
|
213
|
+
// (info/exclude 模板,excludeSyncBlock 同样依赖),无需建目录;每次
|
|
214
|
+
// ensureGit 重写幂等,存量仓库升级后首次 init 自然补上。
|
|
215
|
+
'printf ' + psq(FIDELITY_ATTRS + '\n') + ' > "$g/info/attributes"',
|
|
204
216
|
excludeSyncBlock(store.excludeFile, base),
|
|
205
217
|
'stamp="$g/gc.stamp"',
|
|
206
218
|
'if [ -f "$stamp" ]; then printf \'GIT_OK %s\\n\' "$(head -n1 "$stamp" 2>/dev/null)"; else echo GIT_OK; fi'
|
|
207
219
|
].join('\n')
|
|
208
220
|
}
|
|
209
221
|
|
|
222
|
+
// 存量归一化迁移(issue #12,语义与动机详见 pwsh 版 attrsMigrateBlock 注释):
|
|
223
|
+
// 属性固化后旧索引条目仍指向归一化 blob(stat 缓存时序依赖地跳过重哈希),
|
|
224
|
+
// --renormalize 按当前属性重哈希一次;无 pathspec 是空操作,必须带 ':(top)'
|
|
225
|
+
// 顶层魔法 pathspec。标记文件每仓库至多跑一次;失败(老 git 无该选项等)
|
|
226
|
+
// 只跳过标记下条消息重试,不连累快照主流程(set -e 下 || rc=$? 捕获)。
|
|
227
|
+
// 依赖外层已定义的 $git/$g/$root;仅 snapshotScript 使用。
|
|
228
|
+
function attrsMigrateBlock() {
|
|
229
|
+
return [
|
|
230
|
+
'mig_stamp="$g/attrs-v1.stamp"',
|
|
231
|
+
'if [ ! -f "$mig_stamp" ]; then',
|
|
232
|
+
' migrc=0',
|
|
233
|
+
' "$git" --git-dir="$g" --work-tree="$root" add --renormalize --ignore-errors -- \':(top)\' >/dev/null 2>&1 || migrc=$?',
|
|
234
|
+
' if [ "$migrc" -le 1 ]; then printf \'1\\n\' > "$mig_stamp" 2>/dev/null || true; fi',
|
|
235
|
+
'fi',
|
|
236
|
+
].join('\n')
|
|
237
|
+
}
|
|
238
|
+
|
|
210
239
|
// 快照:add -A → write-tree → commit-tree(孤儿提交)→ tag(语义同 pwsh 版)。
|
|
211
240
|
// tag -f:事件重放/重发会产生重复 messageId,裸 tag 对已存在 tag fatal
|
|
212
241
|
// 导致整条快照失败;-f 把 tag 指到最新提交,语义为「同一条消息取最新状态」。
|
|
@@ -219,6 +248,7 @@ export function snapshotScript(root, store, gitExe, messageId, base) {
|
|
|
219
248
|
heartbeatBlock(),
|
|
220
249
|
dropGitlinksBlock(),
|
|
221
250
|
excludeSyncBlock(store.excludeFile, base),
|
|
251
|
+
attrsMigrateBlock(),
|
|
222
252
|
// fail-open add(issue #7 加固,语义见 pwsh 版同款注释):--ignore-errors
|
|
223
253
|
// 下「无法索引的路径」以退出码 1 结束但索引已落盘;≥2 才是真 fatal,
|
|
224
254
|
// 显式退出让 runShell 抛错(set -e 对 add 非零本会终止,但 || rc=$?
|
package/lib/scripts.pwsh.js
CHANGED
|
@@ -33,6 +33,25 @@ export const MAX_FILE_BYTES = 104857600
|
|
|
33
33
|
export const STALE_LOCK_MIN = 5
|
|
34
34
|
export const HEARTBEAT_TTL_S = 900
|
|
35
35
|
|
|
36
|
+
// 影子仓库 info/attributes 固化内容(issue #12 字节保真,两套模板同值,
|
|
37
|
+
// scripts-contract 钉)。为什么必须:回退恢复走 git archive、捕获走 add,
|
|
38
|
+
// 两者都会应用「快照树里项目自己的 .gitattributes」——text=auto(极常见)
|
|
39
|
+
// + 缺省 core.eol=native(Windows 即 CRLF)会让 archive 把 LF 转 CRLF,
|
|
40
|
+
// 仓库级 core.autocrlf=false 挡不住(属性驱动的转换看 core.eol,不看
|
|
41
|
+
// autocrlf,实测)。info/attributes 是优先级最高的属性源,对全部路径
|
|
42
|
+
// 无条件关闭内容转换,capture/restore 两侧逐字节保真;也顺带治掉
|
|
43
|
+
// ensureGit 的 git config 写入静默失败(I14:pwsh 对 native 非零不抛)
|
|
44
|
+
// 后裸露 system autocrlf 的帮凶路径。逐项语义:
|
|
45
|
+
// -text 关闭全部 EOL 转换(含 autocrlf/text/eol 全部语义)
|
|
46
|
+
// -filter 关闭 clean/smudge 外部过滤命令(LFS 类,防内容被改写/存指针)
|
|
47
|
+
// -ident 关闭 $Id$ 展开
|
|
48
|
+
// -export-ignore 防归档静默丢文件(回退恢复缺文件零报错,实测)
|
|
49
|
+
// -export-subst 防归档内容 $Format:$ 替换
|
|
50
|
+
// -working-tree-encoding 防编码转码
|
|
51
|
+
// 快照是备份/还原不是 VCS:字节保真优先于用户在 .gitattributes 里声明的
|
|
52
|
+
// 任何转换策略——那是给 git 仓库的,不是给影子快照库的。
|
|
53
|
+
export const FIDELITY_ATTRS = '* -text -filter -ident -export-ignore -export-subst -working-tree-encoding'
|
|
54
|
+
|
|
36
55
|
// 去除 PS 5.1 Set-Content -Encoding utf8 写出的 BOM:JSON 解析前必须剥掉,
|
|
37
56
|
// 否则 JSON.parse 把 BOM 当正文首字符直接抛错。
|
|
38
57
|
export function stripBom(text) {
|
|
@@ -160,6 +179,9 @@ export function migrateScript(src, dst) {
|
|
|
160
179
|
// autocrlf=true 时的 LF/CRLF stderr 警告;addEmbeddedRepo=false:嵌套仓库
|
|
161
180
|
// hint/warning 走 stderr,在 DSH shell(EAP=Stop)下会让整条脚本非零退出,
|
|
162
181
|
// 必须在仓库级配置里静默掉。
|
|
182
|
+
// info/attributes 固化(issue #12,内容见 FIDELITY_ATTRS):info/ 由 git init
|
|
183
|
+
// 自带(info/exclude 模板,excludeSyncBlock 同样依赖),无需建目录;每次
|
|
184
|
+
// ensureGit 重写幂等,存量仓库升级后首次 init 自然补上。
|
|
163
185
|
// 结尾回读 gc.stamp(maintenance.js 上次 gc 时间戳):让重启后的 gc 节流
|
|
164
186
|
// 不归零——没有它,天天重启 DSH 的用户每次开机第一条消息都会触发一次
|
|
165
187
|
// 全量 gc,纯浪费。
|
|
@@ -176,12 +198,35 @@ export function ensureGitScript(store, gitExe, base) {
|
|
|
176
198
|
'& $git --git-dir=$g config core.longpaths true',
|
|
177
199
|
'& $git --git-dir=$g config core.autocrlf false',
|
|
178
200
|
'& $git --git-dir=$g config advice.addEmbeddedRepo false',
|
|
201
|
+
"$attrDir = Join-Path $g 'info'",
|
|
202
|
+
"Set-Content -LiteralPath (Join-Path $attrDir 'attributes') -Value '" + FIDELITY_ATTRS + "' -Encoding ascii",
|
|
179
203
|
excludeSyncBlock(store.excludeFile, base),
|
|
180
204
|
"$stamp = Join-Path $g 'gc.stamp'",
|
|
181
205
|
"if (Test-Path -LiteralPath $stamp) { Write-Output ('GIT_OK ' + [String](Get-Content -LiteralPath $stamp -TotalCount 1 -ErrorAction SilentlyContinue)) } else { Write-Output 'GIT_OK' }"
|
|
182
206
|
].join('\n')
|
|
183
207
|
}
|
|
184
208
|
|
|
209
|
+
// 存量归一化迁移(issue #12):属性固化后,索引里的旧条目仍指向归一化
|
|
210
|
+
// blob 的哈希——stat 缓存让裸 add -A 时序依赖地跳过重哈希(racy 复查只在
|
|
211
|
+
// 「add 与文件同秒写入」时触发,实测不可靠),归一化残留会一直潜伏进新
|
|
212
|
+
// 快照。--renormalize 对全部跟踪文件按当前属性(无转换)重哈希一次;
|
|
213
|
+
// 无 pathspec 时它是空操作(实测「Nothing specified, nothing added」),
|
|
214
|
+
// 必须带 ':(top)' 顶层魔法 pathspec(cwd 无关),也不能加
|
|
215
|
+
// --literal-pathspecs(会废掉魔法解析)。标记文件保证每仓库至多跑一次;
|
|
216
|
+
// 失败(老 git 无该选项退出 129、偶发锁冲突等)只跳过标记、下条消息重试,
|
|
217
|
+
// 不 throw——迁移是 best-effort,不能让老 git 机器的快照从此全挂。
|
|
218
|
+
// 依赖外层已定义的 $git/$g/$root;仅 snapshotScript 使用(execute 的救援
|
|
219
|
+
// 安全快照在回退前必先跑一次快照,回退链路天然被覆盖)。
|
|
220
|
+
function attrsMigrateBlock() {
|
|
221
|
+
return [
|
|
222
|
+
"$migStamp = Join-Path $g 'attrs-v1.stamp'",
|
|
223
|
+
'if (-not (Test-Path -LiteralPath $migStamp)) {',
|
|
224
|
+
" & $git --git-dir=$g --work-tree=$root add --renormalize --ignore-errors -- ':(top)'",
|
|
225
|
+
' if ($LASTEXITCODE -le 1) { Set-Content -LiteralPath $migStamp -Value 1 -Encoding ascii -ErrorAction SilentlyContinue }',
|
|
226
|
+
'}',
|
|
227
|
+
].join('\n')
|
|
228
|
+
}
|
|
229
|
+
|
|
185
230
|
// 快照:git add -A 增量同步 index(.gitignore/exclude 语义由 git 统一处理),
|
|
186
231
|
// write-tree 生成树、commit-tree 生成无父孤儿提交、tag 保对象可达。
|
|
187
232
|
// 不做 parent 链、不修剪:像 TraeWork 一样保留全量历史,tag 永远可查。
|
|
@@ -197,6 +242,7 @@ export function snapshotScript(root, store, gitExe, messageId, base) {
|
|
|
197
242
|
heartbeatBlock(),
|
|
198
243
|
dropGitlinksBlock(),
|
|
199
244
|
excludeSyncBlock(store.excludeFile, base),
|
|
245
|
+
attrsMigrateBlock(),
|
|
200
246
|
// fail-open add(issue #7 加固):--ignore-errors 让「个别路径无法索引」
|
|
201
247
|
// (无提交的嵌入式仓库、不可读文件等)以退出码 1 结束但索引照常落盘,
|
|
202
248
|
// 快照缺个别路径可接受,好过整条快照 fatal。退出码 ≥2 才是真 fatal
|