Skip to content

Commit 6def9d5

Browse files
committed
chore(merge): 合并 Git 身份规则化,gitconfig 脚本改为配置驱动
2 parents f698522 + 9b41035 commit 6def9d5

14 files changed

Lines changed: 1456 additions & 34 deletions

File tree

‎.gitignore‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ env.ps1
66
apiKey
77
enableProxy
88
macEnableProxy.ps1
9-
gitconfig_company.ps1
109
*.local.json
1110
*.local.yaml
1211
*.local.yml
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Git Identity Rules Guidelines
2+
3+
> 本规范记录 `scripts/pwsh/misc/gitconfig_personal.ps1` 与 `config/git/gitconfig.local.json` 的身份 profile、`includeIf` 规则渲染与审计判定契约。
4+
5+
## Scenario: 按 remote 地址自动选择 Git 提交身份
6+
7+
### 1. Scope / Trigger
8+
9+
- Trigger: 修改 `scripts/pwsh/misc/gitconfig_personal.ps1`、`config/git/gitconfig.local.example.json` 或 `tests/GitConfigPersonal.Tests.ps1` 中与 profile 匹配、规则渲染、身份审计有关的逻辑;或需要新增一个公司 / 个人 Git 平台。
10+
- Scope: 身份数据在不提交的 `config/git/gitconfig.local.json`;规则安装到各平台自己的 `~/.gitconfig`;脚本本体不含任何真实姓名或邮箱。
11+
- Design intent: 公司仓用真名 + 企业邮箱、个人仓用个人身份,这件事必须由 Git 按 remote 地址自动判定,而不是靠人记得在每个新 clone 里跑一次脚本——漏跑就会用错身份提交,且已推送的 commit 不能 amend 改回来。
12+
13+
### 2. Signatures
14+
15+
- `pwsh ./scripts/pwsh/misc/gitconfig_personal.ps1 [-ShowCurrent] [-Recurse] [-Path <dir>]`
16+
- `pwsh ./scripts/pwsh/misc/gitconfig_personal.ps1 -InstallRules [-WhatIf]`
17+
- `pwsh ./scripts/pwsh/misc/gitconfig_personal.ps1 -ClearLocal [-WhatIf]`
18+
- `pwsh ./scripts/pwsh/misc/gitconfig_personal.ps1 -ProfileName <name> [-Local]`
19+
20+
### 3. Contracts
21+
22+
- 配置顶层是 `profiles` 与 `default`;每个 profile 必须有 `name` 与 `email`,`hosts` 可选。
23+
- 只有带 `hosts` 的 profile 参与自动匹配并渲染成规则;没有 `hosts` 的 profile 只能由 `-ProfileName` 手工指定。
24+
- 配置加载走 `psutils/modules/config.psm1` 的 `Resolve-ConfigSources`,不自写 JSON 解析。嵌套的 profile 定义由脚本自己规范化(`ConvertTo-ConfigHashtable` 只做浅层转换)。
25+
- 写入类动作(`-InstallRules` / `-ClearLocal` / `-ProfileName`)必须支持 `-WhatIf`;写 `~/.gitconfig` 前创建带可读时间戳的 `.bak`。
26+
- 规则安装用 `git config -f <目标文件>` 逐键写入,不手工拼接文件文本,以保留 `[credential]`、`[safe]` 等既有段落;同名 key 覆盖使得重复安装天然幂等。
27+
- 默认行为是只读审计。不提供批量写入身份的入口:`-Recurse` 只用于扫描,`-ClearLocal` 只作用于当前仓库。
28+
- 全局默认身份不由本脚本改写;规则只负责在命中的 remote 上覆盖它。
29+
30+
### 4. includeIf 模式渲染:`**` 不跨 `/` 的陷阱
31+
32+
`hasconfig:remote.*.url:` 的条件值按 wildmatch + `WM_PATHNAME` 匹配:`*` 不跨越 `/`,
33+
`**` 只有在**紧跟 `/` 或位于模式开头**时才跨越 `/`,否则退化成普通 `*`。
34+
35+
SSH 的 scp 简写 `git@host:group/repo.git` 里 `**` 前面是冒号,因此:
36+
37+
| 模式 | `git@host:group/repo.git` | `git@host:repo.git` |
38+
|---|:---:|:---:|
39+
| `git@host:**` | ✗ 不匹配(`**` 退化,不跨 `/`) | ✓ |
40+
| `git@host:*` | ✗ | ✓ |
41+
| `git@host:*/**` | ✓ | ✗ |
42+
43+
所以每个 host 必须渲染成四条,缺一不可:
44+
45+
```text
46+
git@<host>:* # scp 简写,单级路径
47+
git@<host>:*/** # scp 简写,带分组的多级路径
48+
ssh://git@<host>/** # 显式 ssh scheme
49+
https://<host>/** # https
50+
```
51+
52+
判例:只写 `git@gitlab.xhgjdev.com:**` 时,`git@gitlab.xhgjdev.com:digital-rd-governance/onboarding-sandbox.git`
53+
不命中,仓库静默落回全局个人身份;而单级路径的仓库命中,看起来"规则是生效的"。
54+
55+
### 5. 审计判定:必须读来源文件,不能只比对值
56+
57+
判断身份是否健康时必须用 `git config --show-origin --get user.email` 读**来源文件**,
58+
并用 `git config --local --get` 单独判断是否存在仓库级覆盖。只比对邮箱字符串会把
59+
"值碰巧正确但来自硬编码的仓库级 `[user]`"误判成健康——这类仓库换台机器重新 clone 就会用错身份。
60+
61+
| 状态 | 含义 |
62+
|---|---|
63+
| `OK` | 规则生效,身份正确 |
64+
| `LocalOverride` | 身份正确但靠仓库级覆盖维持,可 `-ClearLocal` 交还规则 |
65+
| `RuleGap` | 仓库级覆盖顶着一个规则期望之外的身份,说明该 remote 未被任何 `hosts` 覆盖 |
66+
| `Mismatch` | 生效身份与期望 profile 不符,且不是仓库级覆盖造成的 |
67+
| `NoIdentity` | 未配置 `user.email` |
68+
69+
### 6. 平台边界
70+
71+
- WSL 与 Windows 各有独立的 `HOME` 和 `~/.gitconfig`,规则必须**各安装一次**;只在一侧安装会让另一侧的提交静默使用全局默认身份。
72+
- 规则文件里的 `path` 是所在平台的本地路径。不要在 WSL 里把规则写进 Windows 的 `~/.gitconfig`:写进去的会是 `/mnt/c/...`,Windows 的 Git 不认。需要在 Windows 侧安装时,用 Windows 的 `pwsh.exe` 执行。
73+
- 通过 UNC(`\\wsl.localhost\...`)执行脚本会被 Windows 执行策略拦截,需要一次性 `-ExecutionPolicy Bypass`,不要为此改系统级策略。
74+
75+
### 7. Validation & Error Matrix
76+
77+
| Condition | Expected Behavior |
78+
|-----------|-------------------|
79+
| 配置文件不存在 | 抛错并指向 `gitconfig.local.example.json`,不回退到内置身份 |
80+
| profile 缺 `name` 或 `email` | 抛错,指出是哪个 profile |
81+
| `default` 指向未定义的 profile | 抛错 |
82+
| profile 没有 `hosts` | 跳过规则渲染,不报错 |
83+
| 规则未安装 | 审计输出提示缺失条数并指引 `-InstallRules`,不把未安装报成身份写错 |
84+
| `-Recurse` 但本机没有 fd | 抛错说明依赖,不静默只扫当前目录 |
85+
| 目标仓库没有仓库级覆盖时 `-ClearLocal` | 打印说明并返回 `$false`,不视为错误 |
86+
87+
### 8. Tests Required
88+
89+
- 每种 remote 形态(scp 多级 / scp 单级 / `ssh://` / `https://` / 其它平台 / IP 入口 / 无 remote)到期望 profile 的匹配。
90+
- 规则渲染必须断言同时含 `:*` 与 `:*/**` 两条,防止回归成单条 `:**`。
91+
- 审计五种状态各一例,重点覆盖"值正确但来自仓库级覆盖"判成 `LocalOverride`。
92+
- `-WhatIf` 下不产生任何文件写入;实际执行时生成 `.bak` 且保留无关段落。
93+
- 重复 `-InstallRules` 后规则条数不增长(幂等)。

‎.trellis/spec/infra/index.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
| [Hammerspoon Plugin Contract](./hammerspoon-plugins.md) | macOS Hammerspoon 插件目录、配置合并、部署 manifest 与验证契约 | Active |
1919
| [macOS Finder Quick Actions Contract](./macos-quick-actions.md) | Finder 快捷操作 workflow、Services 安装位置、通用 runner 和 AppleScript quoting 契约 | Active |
2020
| [OpenSSH Server (Windows)](./openssh.md) | Windows OpenSSH Server 模板、Enable-WindowsOpenSsh 启用流程、sshd_config 加固与本机运行态边界 | Active |
21+
| [Git Identity Rules](./git-identity-rules.md) | 按 remote 自动选择提交身份:profile 配置、includeIf 模式渲染(`**` 不跨 `/` 陷阱)、审计判定与双平台安装边界 | Active |
2122
| [Package Source Transactions](./package-sources.md) | 跨平台 source 模式、事务、adapter、Stage 0、drift 与恢复合同 | Active |
2223
| [Install Orchestrator](./install-orchestrator.md) | 根安装入口、Stage 1 步骤注册、Preset、失败传播与 source cleanup 合同 | Active |
2324
| [macOS Install Pipeline](./macos-install-pipeline.md) | macOS Stage 0、Core/Full 叶子、桌面集成幂等与 99 验证合同 | Active |
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{"file": ".trellis/spec/pwsh-scripts/package/index.md", "reason": "校验 Quality Check 要求与 Git Pathspec Contract 是否满足"}
2+
{"file": ".trellis/spec/pwsh-scripts/package/config-loading.md", "reason": "校验配置来源顺序与测试要求(Tests Required 一节)是否落实"}
3+
{"file": ".trellis/spec/pwsh-scripts/package/pester-performance.md", "reason": "新增 Pester 测试需符合分层、命令发现与报告路径约定"}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{"file": ".trellis/spec/pwsh-scripts/package/index.md", "reason": "脚本位于 scripts/pwsh/misc,需遵循该包的 Pre-Development Checklist、Git Pathspec Contract 与 Quality Check"}
2+
{"file": ".trellis/spec/pwsh-scripts/package/config-loading.md", "reason": "新增的本地 JSON 配置必须走 psutils Resolve-ConfigSources,不自写解析器"}
3+
{"file": ".trellis/spec/guides/code-reuse-thinking-guide.md", "reason": "复用 gitconfig_company.ps1 既有的 fd 仓库发现逻辑与 psutils 既有能力,避免重复造轮子"}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Git 身份规则化:gitconfig 脚本按本地配置驱动
2+
3+
## Goal
4+
5+
让 git 提交身份由 `includeIf` 规则按 remote URL 自动决定,`gitconfig_personal.ps1` 从"逐仓写入身份"改为"安装规则 + 审计 + 清理覆盖",身份数据放进一份不提交的本地配置。
6+
7+
## Background
8+
9+
公司 GitLab(`gitlab.xhgjdev.com`)要求真名 + 企业邮箱,个人 GitHub / Gitee 用个人身份。本机全局身份是个人身份,靠脚本逐仓写入公司身份来切换,有三个问题:
10+
11+
- 根目录 `gitconfig_company.ps1`(未跟踪)的 `-Recurse` 会把当前目录下所有仓库(含个人仓)无差别写成公司身份。仓库里的 `config/git/renameCommit.ps1` / `.sh` 正是为"误切换造成个人信息泄露"准备的抢救脚本。
12+
- `scripts/pwsh/misc/gitconfig_personal.ps1` 默认写**全局**个人身份,`-local` 给当前仓写个人邮箱,是同一问题的镜像。
13+
- 新 clone 的仓库全靠人记得跑脚本,漏了就用错身份提交;已推送的 commit 不能 amend 改回来。
14+
15+
WSL 侧已手工配好四条 `includeIf "hasconfig:remote.*.url:..."`,八种 remote 形态实测通过;Windows 侧 `C:/Users/mudssky/.gitconfig` 仍是纯个人身份、无规则。
16+
17+
## Requirements
18+
19+
### R1 配置与代码分离
20+
21+
- 身份数据放 `config/git/gitconfig.local.json`,不提交(已被 `.gitignore` 现有的 `*.local.json` 覆盖,不新增条目)。
22+
- 同目录提供全占位符的 `gitconfig.local.example.json` 并提交,沿用仓库既有 `.local.example` 配对模式。
23+
- 配置声明 profile(name / email / hosts)与默认 profile;`hosts` 是 `includeIf` 规则的来源。
24+
- 脚本本体不含任何真实姓名或邮箱。
25+
26+
### R2 脚本能力
27+
28+
扩展 `scripts/pwsh/misc/gitconfig_personal.ps1`,不新增脚本文件、不改文件名:
29+
30+
- `-ShowCurrent` [`-Recurse`]:只读审计,报告期望 profile、实际生效值**及其来源文件**、是否存在仓库级覆盖、规则是否未覆盖。
31+
- `-InstallRules`:把配置渲染成 `includeIf` 写入当前平台 `~/.gitconfig`,并生成 `~/.gitconfig-<profile>`。
32+
- `-ClearLocal`:清除当前仓库的仓库级 `[user]` 覆盖,不递归。
33+
- `-Profile <name>` [`-Local`]:兜底手工写入,用于规则覆盖不到的 remote(如 IP 形式)。
34+
- 移除"无参数即写全局个人身份"的旧默认行为,改为打印审计结果。
35+
36+
### R3 安全边界
37+
38+
- 写 `~/.gitconfig` 前创建带可读时间戳的 `.bak`(AGENTS.md 第 5 条)。
39+
- 写入类操作支持 `-WhatIf`。
40+
- 不批量写入身份;`-Recurse` 只用于只读扫描。
41+
- 不改写任何已有提交历史。
42+
43+
### R4 收尾
44+
45+
- 删除根目录 `gitconfig_company.ps1` 及 `.gitignore` 中对应条目。
46+
- 更新 `README.md` 与 `docs/scripts-index.md` 中该脚本的描述。
47+
48+
## Constraints
49+
50+
- 配置加载走 `psutils/modules/config.psm1` 的 `Resolve-ConfigSources`,不自写 JSON 解析(`.trellis/spec/pwsh-scripts/package/config-loading.md`)。
51+
- 保持原有 `-local` / `-showCurrent` 调用方式可用。
52+
- 不在本次处理 IP 形式的公司入口(`192.168.27.234`、`192.168.27.159:18080`);审计应把它们报成"规则未覆盖"而非静默。
53+
54+
## Acceptance Criteria
55+
56+
- [ ] `config/git/gitconfig.local.example.json` 已提交且只含占位符;`gitconfig.local.json` 未被跟踪
57+
- [ ] 脚本本体 `git grep` 不到任何真实企业邮箱或姓名
58+
- [ ] `-ShowCurrent -Recurse` 能对公司仓、个人仓、无 remote 目录三类分别给出正确的期望身份、实际值和来源文件
59+
- [ ] `-ShowCurrent` 能把"值碰巧正确但来自仓库级覆盖"标记为可清理,而不是判成健康
60+
- [ ] `-InstallRules` 为每个 host 渲染四条模式,含 `git@<host>:*` 与 `git@<host>:*/**`;Windows 与 WSL 两侧执行结果一致
61+
- [ ] `-InstallRules` / `-ClearLocal` 在 `-WhatIf` 下不产生任何文件写入,实际执行前生成 `.bak`
62+
- [ ] 个人 GitHub / Gitee 仓身份不受影响;`~/.gitconfig` 既有 `[credential]`、`[safe]` 段落原样保留
63+
- [ ] `tests/GitConfigPersonal.Tests.ps1` 覆盖规则匹配、规则渲染、审计判定、配置来源优先级、`-WhatIf`
64+
- [ ] `pnpm qa` 与 `pnpm test:pwsh:all` 通过
65+
- [ ] 根目录 `gitconfig_company.ps1` 已删除,README 与 scripts-index 描述已更新
66+
67+
## Notes
68+
69+
- git 的 `**` 只有紧跟 `/` 或位于模式开头时才跨路径分隔符。SSH scp 写法 `git@host:group/repo.git` 里 `**` 前面是 `:`,会退化成普通 `*` 而匹配失败——这是实测踩到的坑,必须由渲染逻辑展开成 `:*` 与 `:*/**` 两条,并写进 spec。
70+
- 判断"实际生效身份"必须读 `--show-origin` 的来源文件;只比对邮箱字符串会把硬编码覆盖误判为健康。
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"id": "git-identity-rules",
3+
"name": "git-identity-rules",
4+
"title": "Git 身份规则化:gitconfig 脚本按本地配置驱动",
5+
"description": "",
6+
"status": "completed",
7+
"dev_type": null,
8+
"scope": null,
9+
"package": null,
10+
"priority": "P2",
11+
"creator": "mudssky",
12+
"assignee": "mudssky",
13+
"createdAt": "2026-09-11",
14+
"completedAt": "2026-09-11",
15+
"branch": "feat/git-identity-rules",
16+
"base_branch": "master",
17+
"worktree_path": null,
18+
"commit": null,
19+
"pr_url": null,
20+
"subtasks": [],
21+
"children": [],
22+
"parent": null,
23+
"relatedFiles": [],
24+
"notes": "",
25+
"meta": {}
26+
}

‎.trellis/workspace/mudssky/index.md‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
<!-- @@@auto:current-status -->
1010
- **Active File**: `journal-2.md`
11-
- **Total Sessions**: 74
12-
- **Last Active**: 2026-09-10
11+
- **Total Sessions**: 75
12+
- **Last Active**: 2026-09-11
1313
<!-- @@@/auto:current-status -->
1414

1515
---
@@ -19,7 +19,7 @@
1919
<!-- @@@auto:active-documents -->
2020
| File | Lines | Status |
2121
|------|-------|--------|
22-
| `journal-2.md` | ~131 | Active |
22+
| `journal-2.md` | ~154 | Active |
2323
| `journal-1.md` | ~1997 | Archived |
2424
<!-- @@@/auto:active-documents -->
2525

@@ -31,6 +31,7 @@
3131

3232
| # | Date | Title | Commits | Branch |
3333
|---|------|-------|---------|--------|
34+
| 75 | 2026-09-11 | Git 身份规则化:gitconfig 脚本按本地配置驱动 | `5b45c671`, `bb8eae45`, `576f9960` | `feat/git-identity-rules` |
3435
| 74 | 2026-09-10 | browser-debug LAN 指南自动探测 Tailscale 地址 | `40138881` | `master` |
3536
| 73 | 2026-09-09 | Windows WSL 自启接入流水线与 SSHCopyID 安装 | `5a996894` | `master` |
3637
| 72 | 2026-09-09 | shell 层接管登录 profile:受管块替代 01 直写 | `fde35788` | `master` |

‎.trellis/workspace/mudssky/journal-2.md‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,26 @@ brainstorm 勘察确认 WSL 自启机制层已存在(windows/wsl AtStartup+S4U
129129
### Status
130130

131131
[OK] **Completed**
132+
133+
134+
## Session 75: Git 身份规则化:gitconfig 脚本按本地配置驱动
135+
136+
**Date**: 2026-09-11
137+
**Task**: Git 身份规则化:gitconfig 脚本按本地配置驱动
138+
**Branch**: `feat/git-identity-rules`
139+
140+
### Summary
141+
142+
把 gitconfig_personal.ps1 从逐仓写入身份改为 profile 配置驱动:身份数据移到不提交的 config/git/gitconfig.local.json,带 hosts 的 profile 渲染成 includeIf 规则由 Git 按 remote 自动选身份。新增 -InstallRules/-ShowCurrent/-Recurse/-ClearLocal/-ProfileName,移除无参数即写全局个人身份的旧行为,删除根目录 gitconfig_company.ps1。固化两个踩到的坑:includeIf 的 ** 紧跟冒号时不跨 / 会让 scp 简写漏匹配,每个 host 必须渲染 :* 与 :*/** 四条模式;判断身份健康必须读 --show-origin 来源文件,否则仓库级硬编码覆盖会被误判成健康。新增 35 个 Pester 用例与 infra spec,WSL 与 Windows 两侧已实测安装并验证八种 remote 形态。
143+
144+
### Git Commits
145+
146+
| Hash | Message |
147+
|------|---------|
148+
| `5b45c671` | (see git log) |
149+
| `bb8eae45` | (see git log) |
150+
| `576f9960` | (see git log) |
151+
152+
### Status
153+
154+
[OK] **Completed**

‎README.md‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,14 @@ CI 中已提供独立工作流 `.github/workflows/qa-benchmark.yml`,
504504

505505
#### `gitconfig_personal`
506506

507-
**功能**: Git 个人配置设置
507+
**功能**: 按 profile 管理 Git 提交身份
508+
509+
身份数据来自不提交的 `config/git/gitconfig.local.json`(照同目录 `gitconfig.local.example.json` 自建)。带 `hosts` 的 profile 可渲染成 `includeIf` 规则,让 Git 按 remote 地址自动选身份。
510+
511+
- 默认(或 `-ShowCurrent`):只读审计,报告期望 profile、实际生效身份及其来源文件;加 `-Recurse` 递归扫描
512+
- `-InstallRules`:把规则安装到当前平台的 `~/.gitconfig`(WSL 与 Windows 需各装一次),写入前自动备份
513+
- `-ClearLocal`:清掉当前仓库的仓库级 `[user]` 覆盖,把身份判定交还给规则
514+
- `-ProfileName <name> [-Local]`:兜底手工写入,用于规则覆盖不到的 remote(如 IP 形式地址)
508515

509516
### IDE 和编辑器
510517

0 commit comments

Comments
 (0)