@shelken/pi-co-authored-by 0.2.8 → 0.2.9

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/lib/commit.ts +121 -32
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,7 +7,7 @@ Co-Authored-By: <model> <noreply@pi.dev>
7
7
  Generated-By: pi <version>
8
8
  ```
9
9
 
10
- 实现:session 级临时 `core.hooksPath` + `prepare-commit-msg`;结束时清理。
10
+ 实现:bash 期间在真实 hooks 目录临时安装 `prepare-commit-msg`(不改 `core.hooksPath`);pid 引用计数,shell 退出时最后一个引用卸装并恢复原 hook。
11
11
 
12
12
  初始 fork 自 [bruno-garcia/pi-co-authored-by](https://github.com/bruno-garcia/pi-co-authored-by)。
13
13
 
package/lib/commit.ts CHANGED
@@ -20,7 +20,10 @@ export function removeCommitHookDirectory(hooksDir: string | undefined): void {
20
20
  rmSync(hooksDir, { recursive: true, force: true });
21
21
  }
22
22
 
23
- /** Add process-local Git hook configuration and trailer metadata to a bash command. */
23
+ /**
24
+ * 注入 trailer 元数据,并在当前仓库临时安装 prepare-commit-msg。
25
+ * 不设置 core.hooksPath,避免污染 git config;shell 退出时恢复原 hook。
26
+ */
24
27
  export function wrapBashWithCommitHook(
25
28
  cmd: string,
26
29
  hooksDir: string,
@@ -38,24 +41,129 @@ function buildEnvironmentPrefix(
38
41
  coAuthor: string,
39
42
  generatedBy: string,
40
43
  ): string {
41
- return `__pi_co_authored_by_git_config_index="\${GIT_CONFIG_COUNT:-0}"
42
- export PI_CO_AUTHORED_BY_GIT_CONFIG_INDEX="$__pi_co_authored_by_git_config_index"
43
- export PI_CO_AUTHORED_BY_CO_AUTHOR=${shellQuote(coAuthor)}
44
+ const hookSrc = join(hooksDir, "prepare-commit-msg");
45
+ return `export PI_CO_AUTHORED_BY_CO_AUTHOR=${shellQuote(coAuthor)}
44
46
  export PI_CO_AUTHORED_BY_GENERATED_BY=${shellQuote(generatedBy)}
45
- export "GIT_CONFIG_KEY_\${__pi_co_authored_by_git_config_index}=core.hooksPath"
46
- export "GIT_CONFIG_VALUE_\${__pi_co_authored_by_git_config_index}=${escapeDoubleQuotedAssignmentValue(hooksDir)}"
47
- export GIT_CONFIG_COUNT="$((__pi_co_authored_by_git_config_index + 1))"
48
- unset __pi_co_authored_by_git_config_index`;
47
+ export PI_CO_AUTHORED_BY_HOOK_SRC=${shellQuote(hookSrc)}
48
+
49
+ __pi_co_authored_by_installed_hooks=""
50
+
51
+ __pi_co_authored_by_resolve_hooks_dir() {
52
+ local configured hooks_dir toplevel
53
+ configured="$(command git config --get core.hooksPath 2>/dev/null || true)"
54
+ if [ -n "$configured" ]; then
55
+ case "$configured" in
56
+ /*) hooks_dir="$configured" ;;
57
+ *)
58
+ toplevel="$(command git rev-parse --show-toplevel 2>/dev/null)" || return 1
59
+ hooks_dir="$toplevel/$configured"
60
+ ;;
61
+ esac
62
+ else
63
+ hooks_dir="$(command git rev-parse --git-path hooks 2>/dev/null)" || return 1
64
+ fi
65
+ # 绝对路径,避免 cwd 变化后 cleanup 找不到
66
+ case "$hooks_dir" in
67
+ /*) ;;
68
+ *) hooks_dir="$(CDPATH= cd -- "$hooks_dir" 2>/dev/null && pwd)" || return 1 ;;
69
+ esac
70
+ printf '%s' "$hooks_dir"
71
+ }
72
+
73
+ # 清掉已死进程的 ref,避免 SIGKILL 后永远占坑
74
+ __pi_co_authored_by_prune_refs() {
75
+ local refs_dir="$1" f pid
76
+ [ -d "$refs_dir" ] || return 0
77
+ for f in "$refs_dir"/*; do
78
+ [ -e "$f" ] || continue
79
+ pid="\${f##*/}"
80
+ case "$pid" in
81
+ ''|*[!0-9]*) rm -f "$f" ;;
82
+ *) kill -0 "$pid" 2>/dev/null || rm -f "$f" ;;
83
+ esac
84
+ done
85
+ }
86
+
87
+ # 登记本 shell 对 hooks_dir 的引用(见 marker 也要登记,否则 sticky dirty)
88
+ __pi_co_authored_by_register() {
89
+ local hooks_dir="$1" refs_dir
90
+ refs_dir="$hooks_dir/.pi-co-authored-by-refs"
91
+ mkdir -p "$refs_dir" 2>/dev/null || return 0
92
+ __pi_co_authored_by_prune_refs "$refs_dir"
93
+ : > "$refs_dir/$$" 2>/dev/null || return 0
94
+ case "
95
+ $__pi_co_authored_by_installed_hooks
96
+ " in
97
+ *"
98
+ $hooks_dir
99
+ "*) ;;
100
+ *)
101
+ __pi_co_authored_by_installed_hooks="$__pi_co_authored_by_installed_hooks
102
+ $hooks_dir"
103
+ ;;
104
+ esac
105
+ }
106
+
107
+ __pi_co_authored_by_ensure_hook() {
108
+ local hooks_dir hook
109
+ hooks_dir="$(__pi_co_authored_by_resolve_hooks_dir)" || return 0
110
+ hook="$hooks_dir/prepare-commit-msg"
111
+ mkdir -p "$hooks_dir" 2>/dev/null || return 0
112
+
113
+ if [ -f "$hook" ] && grep -q 'PI_CO_AUTHORED_BY_HOOK_MARKER' "$hook" 2>/dev/null; then
114
+ __pi_co_authored_by_register "$hooks_dir"
115
+ return 0
116
+ fi
117
+
118
+ if [ -e "$hook" ] && [ ! -e "$hooks_dir/prepare-commit-msg.pi-user" ]; then
119
+ mv "$hook" "$hooks_dir/prepare-commit-msg.pi-user"
120
+ fi
121
+
122
+ cp "$PI_CO_AUTHORED_BY_HOOK_SRC" "$hook"
123
+ chmod +x "$hook"
124
+ __pi_co_authored_by_register "$hooks_dir"
125
+ }
126
+
127
+ __pi_co_authored_by_cleanup() {
128
+ local hooks_dir hook refs_dir
129
+ printf '%s\n' "$__pi_co_authored_by_installed_hooks" | while IFS= read -r hooks_dir; do
130
+ [ -n "$hooks_dir" ] || continue
131
+ refs_dir="$hooks_dir/.pi-co-authored-by-refs"
132
+ rm -f "$refs_dir/$$" 2>/dev/null || true
133
+ __pi_co_authored_by_prune_refs "$refs_dir"
134
+ # 仍有活进程占用:只退自己的 ref,不卸 hook
135
+ if [ -n "$(ls -A "$refs_dir" 2>/dev/null)" ]; then
136
+ continue
137
+ fi
138
+ rmdir "$refs_dir" 2>/dev/null || true
139
+ hook="$hooks_dir/prepare-commit-msg"
140
+ if [ -f "$hook" ] && grep -q 'PI_CO_AUTHORED_BY_HOOK_MARKER' "$hook" 2>/dev/null; then
141
+ rm -f "$hook"
142
+ if [ -e "$hooks_dir/prepare-commit-msg.pi-user" ]; then
143
+ mv "$hooks_dir/prepare-commit-msg.pi-user" "$hook"
144
+ fi
145
+ fi
146
+ done
147
+ }
148
+
149
+ trap '__pi_co_authored_by_cleanup' EXIT
150
+ __pi_co_authored_by_ensure_hook
151
+
152
+ git() {
153
+ __pi_co_authored_by_ensure_hook
154
+ command git "$@"
155
+ }`;
49
156
  }
50
157
 
51
158
  function buildPrepareCommitMsgHook(): string {
52
159
  return `#!/bin/sh
160
+ # PI_CO_AUTHORED_BY_HOOK_MARKER
53
161
  set -u
54
162
 
55
163
  message_file="$1"
56
164
 
57
165
  if [ -n "\${PI_CO_AUTHORED_BY_CO_AUTHOR:-}" ] && [ -n "\${PI_CO_AUTHORED_BY_GENERATED_BY:-}" ]; then
58
- git \
166
+ command git \
59
167
  -c trailer.co-authored-by.ifExists=addIfDifferent \
60
168
  -c trailer.generated-by.ifExists=replace \
61
169
  interpret-trailers \
@@ -65,25 +173,10 @@ if [ -n "\${PI_CO_AUTHORED_BY_CO_AUTHOR:-}" ] && [ -n "\${PI_CO_AUTHORED_BY_GENE
65
173
  "$message_file"
66
174
  fi
67
175
 
68
- if [ -n "\${PI_CO_AUTHORED_BY_GIT_CONFIG_INDEX:-}" ]; then
69
- __pi_config_index="$PI_CO_AUTHORED_BY_GIT_CONFIG_INDEX"
70
- unset "GIT_CONFIG_KEY_$__pi_config_index"
71
- unset "GIT_CONFIG_VALUE_$__pi_config_index"
72
- export GIT_CONFIG_COUNT="$__pi_config_index"
73
- fi
74
-
75
- original_hooks_path="$(git config --get core.hooksPath || true)"
76
- if [ -n "$original_hooks_path" ]; then
77
- case "$original_hooks_path" in
78
- /*) original_hook="$original_hooks_path/prepare-commit-msg" ;;
79
- *) original_hook="$(git rev-parse --show-toplevel)/$original_hooks_path/prepare-commit-msg" ;;
80
- esac
81
- else
82
- original_hook="$(git rev-parse --git-path hooks/prepare-commit-msg)"
83
- fi
84
-
85
- if [ -x "$original_hook" ] && [ "$original_hook" != "$0" ]; then
86
- "$original_hook" "$@"
176
+ hooks_dir="$(dirname "$0")"
177
+ user_hook="$hooks_dir/prepare-commit-msg.pi-user"
178
+ if [ -x "$user_hook" ]; then
179
+ "$user_hook" "$@"
87
180
  fi
88
181
  `;
89
182
  }
@@ -91,7 +184,3 @@ fi
91
184
  function shellQuote(value: string): string {
92
185
  return `'${value.replace(/'/g, `'\\''`)}'`;
93
186
  }
94
-
95
- function escapeDoubleQuotedAssignmentValue(value: string): string {
96
- return value.replace(/[\\"$`]/g, "\\$&");
97
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shelken/pi-co-authored-by",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "agent 执行 git commit 时自动追加 Co-Authored-By / Generated-By trailer",
5
5
  "type": "module",
6
6
  "license": "MIT",