@unifan/pi-commit-zh 1.0.10 → 1.0.12
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/index.ts +39 -4
- package/package.json +1 -1
- package/src/git.ts +73 -4
package/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
commitWithMsg,
|
|
4
4
|
getChangedFiles,
|
|
5
5
|
getStagedDiff,
|
|
6
|
+
getUnpushedCommits,
|
|
6
7
|
getUnstagedDiff,
|
|
7
8
|
isGitRepo,
|
|
8
9
|
pushCurrentBranch,
|
|
@@ -114,7 +115,37 @@ export default function (pi: ExtensionAPI) {
|
|
|
114
115
|
const changedFiles = await getChangedFiles(ctx.cwd);
|
|
115
116
|
|
|
116
117
|
if (!stagedDiff && !unstagedDiff) {
|
|
117
|
-
|
|
118
|
+
if (andPush) {
|
|
119
|
+
const unpushed = await getUnpushedCommits(ctx.cwd);
|
|
120
|
+
if (unpushed.length > 0) {
|
|
121
|
+
notify(`当前工作区干净,但检测到有 ${unpushed.length} 个未推送提交,正在推送到远端...`, "info");
|
|
122
|
+
const pushRes = await pushCurrentBranch(ctx.cwd, (msg) => notify(msg, "info"));
|
|
123
|
+
if (pushRes.ok) {
|
|
124
|
+
const successMsg = pushRes.autoRebased
|
|
125
|
+
? `🚀 已自动完成变基 (pull --rebase),并成功将 ${unpushed.length} 个本地提交推送至远端!`
|
|
126
|
+
: `🚀 成功将本地未推送的 ${unpushed.length} 个提交推送至远端!`;
|
|
127
|
+
notify(successMsg, "info");
|
|
128
|
+
pi.sendMessage({
|
|
129
|
+
customType: "pi-commit-result",
|
|
130
|
+
content: `### 🚀 远端推送完成\n\n当前工作区无未提交的修改,已成功将本地 **${unpushed.length} 个历史提交** 推送至远端分支:\n\n\`\`\`text\n${unpushed.join("\n")}\n\`\`\`\n\n${successMsg}`,
|
|
131
|
+
display: true,
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
if (pushRes.hasConflict) {
|
|
135
|
+
notify("⚠️ 远端拉取变基产生代码冲突!请对比修改和上下文解决冲突,严禁直接使用 ours/theirs。", "warning");
|
|
136
|
+
} else {
|
|
137
|
+
notify(`⚠️ 推送失败: ${pushRes.output}`, "error");
|
|
138
|
+
}
|
|
139
|
+
pi.sendMessage({
|
|
140
|
+
customType: "pi-commit-result",
|
|
141
|
+
content: `### ⚠️ 推送到远端未完成\n\n${pushRes.output}`,
|
|
142
|
+
display: true,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
notify("当前工作区干净,且所有本地提交均已同步至远端,无需提交和推送。", "info");
|
|
118
149
|
return;
|
|
119
150
|
}
|
|
120
151
|
|
|
@@ -150,8 +181,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
150
181
|
notify(successMsg, "info");
|
|
151
182
|
pushText = `\n\n${successMsg}`;
|
|
152
183
|
} else {
|
|
153
|
-
|
|
154
|
-
|
|
184
|
+
if (pushRes.hasConflict) {
|
|
185
|
+
notify("⚠️ 远端拉取变基产生代码冲突!请对比修改和上下文解决冲突,严禁直接使用 ours/theirs。", "warning");
|
|
186
|
+
} else {
|
|
187
|
+
notify(`⚠️ 推送失败: ${pushRes.output}`, "error");
|
|
188
|
+
}
|
|
189
|
+
pushText = `\n\n${pushRes.output}`;
|
|
155
190
|
}
|
|
156
191
|
}
|
|
157
192
|
|
|
@@ -178,7 +213,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
178
213
|
});
|
|
179
214
|
|
|
180
215
|
pi.registerCommand("commit-push", {
|
|
181
|
-
description: "智能 Git 提交并推流:生成标准中文 Commit 后自动提交并执行 git push (
|
|
216
|
+
description: "智能 Git 提交并推流:生成标准中文 Commit 后自动提交并执行 git push (支持自动变基与未推送提交自动推流)",
|
|
182
217
|
handler: async (args, ctx) => {
|
|
183
218
|
await handleCommitCommand(args, ctx, true);
|
|
184
219
|
},
|
package/package.json
CHANGED
package/src/git.ts
CHANGED
|
@@ -45,6 +45,27 @@ export async function getChangedFiles(cwd: string): Promise<string[]> {
|
|
|
45
45
|
.filter(Boolean);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
export async function getUnpushedCommits(cwd: string): Promise<string[]> {
|
|
49
|
+
const res = await runGit(cwd, ["log", "@{u}..HEAD", "--oneline"]);
|
|
50
|
+
if (res.ok && res.stdout.trim()) {
|
|
51
|
+
return res.stdout.trim().split("\n").filter(Boolean);
|
|
52
|
+
}
|
|
53
|
+
const statusRes = await runGit(cwd, ["status", "--porcelain=v1", "-b"]);
|
|
54
|
+
if (statusRes.ok) {
|
|
55
|
+
const match = statusRes.stdout.match(/\[ahead\s+(\d+)\]/);
|
|
56
|
+
if (match && match[1]) {
|
|
57
|
+
const count = parseInt(match[1], 10);
|
|
58
|
+
if (count > 0) {
|
|
59
|
+
const logRes = await runGit(cwd, ["log", "-n", String(count), "--oneline"]);
|
|
60
|
+
if (logRes.ok && logRes.stdout.trim()) {
|
|
61
|
+
return logRes.stdout.trim().split("\n").filter(Boolean);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
|
|
48
69
|
export async function stageAll(cwd: string): Promise<boolean> {
|
|
49
70
|
const res = await runGit(cwd, ["add", "-A"]);
|
|
50
71
|
return res.ok;
|
|
@@ -58,13 +79,23 @@ export async function commitWithMsg(cwd: string, message: string): Promise<{ ok:
|
|
|
58
79
|
export async function pushCurrentBranch(
|
|
59
80
|
cwd: string,
|
|
60
81
|
onLog?: (msg: string) => void,
|
|
61
|
-
): Promise<{ ok: boolean; output: string; autoRebased?: boolean }> {
|
|
62
|
-
|
|
82
|
+
): Promise<{ ok: boolean; output: string; autoRebased?: boolean; hasConflict?: boolean; conflictFiles?: string[] }> {
|
|
83
|
+
let initialPush = await runGit(cwd, ["push"]);
|
|
63
84
|
if (initialPush.ok) {
|
|
64
85
|
return { ok: true, output: initialPush.stdout || "推送成功" };
|
|
65
86
|
}
|
|
66
87
|
|
|
67
|
-
|
|
88
|
+
let pushError = `${initialPush.stderr} ${initialPush.stdout}`.toLowerCase();
|
|
89
|
+
|
|
90
|
+
if (pushError.includes("no upstream branch") || pushError.includes("set-upstream")) {
|
|
91
|
+
if (onLog) onLog("未关联远端分支,正在自动关联并推送 (git push -u origin HEAD)...");
|
|
92
|
+
const setUpstream = await runGit(cwd, ["push", "-u", "origin", "HEAD"]);
|
|
93
|
+
if (setUpstream.ok) {
|
|
94
|
+
return { ok: true, output: setUpstream.stdout || "推送成功" };
|
|
95
|
+
}
|
|
96
|
+
pushError = `${setUpstream.stderr} ${setUpstream.stdout}`.toLowerCase();
|
|
97
|
+
}
|
|
98
|
+
|
|
68
99
|
const needsPull =
|
|
69
100
|
pushError.includes("fetch first") ||
|
|
70
101
|
pushError.includes("non-fast-forward") ||
|
|
@@ -80,9 +111,47 @@ export async function pushCurrentBranch(
|
|
|
80
111
|
const pullRebase = await runGit(cwd, ["pull", "--rebase"]);
|
|
81
112
|
if (!pullRebase.ok) {
|
|
82
113
|
const rebaseErr = `${pullRebase.stderr} ${pullRebase.stdout}`;
|
|
114
|
+
|
|
115
|
+
// 检索冲突文件清单
|
|
116
|
+
const conflictDiff = await runGit(cwd, ["diff", "--name-only", "--diff-filter=U"]);
|
|
117
|
+
let conflictFiles = conflictDiff.ok && conflictDiff.stdout.trim()
|
|
118
|
+
? conflictDiff.stdout.trim().split("\n").map((f) => f.trim()).filter(Boolean)
|
|
119
|
+
: [];
|
|
120
|
+
|
|
121
|
+
if (conflictFiles.length === 0) {
|
|
122
|
+
const statusRes = await runGit(cwd, ["status", "--porcelain"]);
|
|
123
|
+
if (statusRes.ok && statusRes.stdout.trim()) {
|
|
124
|
+
conflictFiles = statusRes.stdout
|
|
125
|
+
.split("\n")
|
|
126
|
+
.filter((l) => /^(UU|AA|UD|DU|DD|AU|UA)\s+/.test(l.trim()))
|
|
127
|
+
.map((l) => l.trim().slice(3).trim());
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const filesListText = conflictFiles.length > 0
|
|
132
|
+
? `\n\n📌 **发生冲突的文件 (${conflictFiles.length} 个)**:\n${conflictFiles.map((f) => `- \`${f}\``).join("\n")}`
|
|
133
|
+
: "";
|
|
134
|
+
|
|
135
|
+
const conflictOutput = `⚠️ **远端有新提交,拉取变基 (git pull --rebase) 时与本地提交产生代码冲突!**${filesListText}
|
|
136
|
+
|
|
137
|
+
🚨 **【代码冲突解决核心铁律(重中之重)】**:
|
|
138
|
+
1. **严禁直接无脑使用 \`--theirs\` 或 \`--ours\`**:绝对不要盲目使用其中一方覆盖另一方,否则极易覆盖远端同事的代码或丢失本地修复!
|
|
139
|
+
2. **必须根据对比修改和上下文解决冲突**:
|
|
140
|
+
- 逐个打开冲突文件,对照 \`<<<<<<< HEAD\`(远端基线)与 \`>>>>>>>\`(本地提交)的具体改动;
|
|
141
|
+
- 结合周围类、函数、接口调用与业务上下文,理解两边的意图后逐行融合成正确的最终代码。
|
|
142
|
+
|
|
143
|
+
🛠️ **解决冲突操作步骤**:
|
|
144
|
+
1. 对比并手工/智能修改上述冲突文件,消除所有冲突标记;
|
|
145
|
+
2. 执行 \`git add <已解决文件>\` 标记冲突已解决;
|
|
146
|
+
3. 执行 \`git rebase --continue\` 完成变基;
|
|
147
|
+
4. 变基成功后重新执行 \`/commit-push\` 推送至远端;
|
|
148
|
+
(若需放弃本次变基恢复原状,可执行 \`git rebase --abort\`)。\n\n底层详细输出:\n${rebaseErr}`;
|
|
149
|
+
|
|
83
150
|
return {
|
|
84
151
|
ok: false,
|
|
85
|
-
output:
|
|
152
|
+
output: conflictOutput,
|
|
153
|
+
hasConflict: true,
|
|
154
|
+
conflictFiles,
|
|
86
155
|
};
|
|
87
156
|
}
|
|
88
157
|
|