@kud/ai-conventional-commit-cli 3.1.1 → 3.2.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/dist/chunk-2WRUFO3O.js +689 -0
- package/dist/chunk-FYJNHXAR.js +700 -0
- package/dist/chunk-HOUMTU6H.js +699 -0
- package/dist/chunk-SNV4RWS4.js +696 -0
- package/dist/index.js +31 -15
- package/dist/reword-KUE3IVBE.js +212 -0
- package/dist/reword-MCQOCOZ2.js +212 -0
- package/dist/reword-T44WTP5I.js +212 -0
- package/dist/reword-UE5IP5V3.js +212 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
formatCommitTitle,
|
|
17
17
|
renderCommitBlock,
|
|
18
18
|
sectionTitle
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-FYJNHXAR.js";
|
|
20
20
|
|
|
21
21
|
// src/index.ts
|
|
22
22
|
import { Cli, Command, Option } from "clipanion";
|
|
@@ -207,15 +207,24 @@ import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
|
207
207
|
import { join } from "path";
|
|
208
208
|
import { select } from "@inquirer/prompts";
|
|
209
209
|
async function runGenerate(config) {
|
|
210
|
+
const debug = process.env.AICC_DEBUG === "true";
|
|
211
|
+
const dbg = (msg, pairs = {}) => {
|
|
212
|
+
if (!debug) return;
|
|
213
|
+
const kvStr = Object.entries(pairs).map(([k, v]) => chalk.dim(k + "=") + chalk.yellow(String(v))).join(" ");
|
|
214
|
+
console.error(chalk.dim("[ai-cc]") + chalk.cyan("[generate]"), chalk.white(msg), kvStr || "");
|
|
215
|
+
};
|
|
210
216
|
const startedAt = Date.now();
|
|
211
217
|
const provider = new OpenCodeProvider(config.model);
|
|
212
218
|
provider.warmup();
|
|
213
219
|
const { files, hasStagedChanges } = await getStagedFilesAndDiff();
|
|
220
|
+
dbg("staged files", { files: files.length, hasStagedChanges });
|
|
214
221
|
if (!hasStagedChanges) {
|
|
222
|
+
await provider.close();
|
|
215
223
|
console.log("No staged changes.");
|
|
216
224
|
return;
|
|
217
225
|
}
|
|
218
226
|
if (!files.length) {
|
|
227
|
+
await provider.close();
|
|
219
228
|
console.log("No diff content detected after staging. Aborting.");
|
|
220
229
|
return;
|
|
221
230
|
}
|
|
@@ -271,21 +280,28 @@ async function runGenerate(config) {
|
|
|
271
280
|
let raw;
|
|
272
281
|
let plan;
|
|
273
282
|
let candidates = [];
|
|
274
|
-
const [style, plugins] = await runStep(
|
|
275
|
-
|
|
276
|
-
|
|
283
|
+
const [style, plugins] = await runStep("Profiling style", async () => {
|
|
284
|
+
const t = Date.now();
|
|
285
|
+
const result = await Promise.all([
|
|
277
286
|
getRecentCommitMessages(config.styleSamples).then(buildStyleProfile),
|
|
278
287
|
loadPlugins(config)
|
|
279
|
-
])
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
"
|
|
287
|
-
|
|
288
|
-
);
|
|
288
|
+
]);
|
|
289
|
+
dbg("profiling done", { ms: Date.now() - t });
|
|
290
|
+
return result;
|
|
291
|
+
});
|
|
292
|
+
messages = await runStep("Building prompt", async () => {
|
|
293
|
+
const t = Date.now();
|
|
294
|
+
const result = buildGenerationMessages({ files, style, config, mode: "single" });
|
|
295
|
+
dbg("prompt built", { ms: Date.now() - t });
|
|
296
|
+
return result;
|
|
297
|
+
});
|
|
298
|
+
raw = await runStep("Calling model", async () => {
|
|
299
|
+
const t = Date.now();
|
|
300
|
+
dbg("model call start", { model: config.model });
|
|
301
|
+
const result = await provider.chat(messages, { maxTokens: config.maxTokens });
|
|
302
|
+
dbg("model call done", { ms: Date.now() - t, responseChars: result.length });
|
|
303
|
+
return result;
|
|
304
|
+
});
|
|
289
305
|
plan = await runStep("Parsing response", async () => extractJSON(raw));
|
|
290
306
|
candidates = await runStep(
|
|
291
307
|
"Analyzing changes",
|
|
@@ -1047,7 +1063,7 @@ var RewordCommand = class extends Command {
|
|
|
1047
1063
|
description: "Auto-confirm commit without prompting"
|
|
1048
1064
|
});
|
|
1049
1065
|
async execute() {
|
|
1050
|
-
const { runReword } = await import("./reword-
|
|
1066
|
+
const { runReword } = await import("./reword-KUE3IVBE.js");
|
|
1051
1067
|
const config = await loadConfig();
|
|
1052
1068
|
if (this.style) config.style = this.style;
|
|
1053
1069
|
if (this.model) config.model = this.model;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OpenCodeProvider,
|
|
3
|
+
abortMessage,
|
|
4
|
+
animateHeaderBase,
|
|
5
|
+
borderLine,
|
|
6
|
+
buildRefineMessages,
|
|
7
|
+
createPhasedSpinner,
|
|
8
|
+
extractJSON,
|
|
9
|
+
finalSuccess,
|
|
10
|
+
formatCommitTitle,
|
|
11
|
+
renderCommitBlock,
|
|
12
|
+
sectionTitle
|
|
13
|
+
} from "./chunk-FYJNHXAR.js";
|
|
14
|
+
|
|
15
|
+
// src/workflow/reword.ts
|
|
16
|
+
import chalk from "chalk";
|
|
17
|
+
import ora from "ora";
|
|
18
|
+
import inquirer from "inquirer";
|
|
19
|
+
import { simpleGit } from "simple-git";
|
|
20
|
+
var git = simpleGit();
|
|
21
|
+
async function getCommitMessage(hash) {
|
|
22
|
+
try {
|
|
23
|
+
const raw = await git.show([`${hash}`, "--quiet", "--format=%P%n%B"]);
|
|
24
|
+
const lines = raw.split("\n");
|
|
25
|
+
const parentsLine = lines.shift() || "";
|
|
26
|
+
const parents = parentsLine.trim().length ? parentsLine.trim().split(/\s+/) : [];
|
|
27
|
+
const message = lines.join("\n").trim();
|
|
28
|
+
if (!message) return null;
|
|
29
|
+
const [first, ...rest] = message.split("\n");
|
|
30
|
+
const body = rest.join("\n").trim() || void 0;
|
|
31
|
+
return { title: first, body, parents };
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function isAncestor(ancestor, head) {
|
|
37
|
+
try {
|
|
38
|
+
const mb = (await git.raw(["merge-base", ancestor, head])).trim();
|
|
39
|
+
const anc = (await git.revparse([ancestor])).trim();
|
|
40
|
+
return mb === anc;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function runReword(config, hash) {
|
|
46
|
+
const startedAt = Date.now();
|
|
47
|
+
const commit = await getCommitMessage(hash);
|
|
48
|
+
if (!commit) {
|
|
49
|
+
console.log(`Commit not found: ${hash}`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (commit.parents.length > 1) {
|
|
53
|
+
console.log("Refusing to reword a merge commit (multiple parents).");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (process.stdout.isTTY) {
|
|
57
|
+
await animateHeaderBase("ai-conventional-commit", config.model);
|
|
58
|
+
borderLine();
|
|
59
|
+
}
|
|
60
|
+
sectionTitle("Original commit");
|
|
61
|
+
borderLine(chalk.yellow(commit.title));
|
|
62
|
+
if (commit.body) {
|
|
63
|
+
commit.body.split("\n").forEach((l) => l.trim().length ? borderLine(l) : borderLine());
|
|
64
|
+
}
|
|
65
|
+
borderLine();
|
|
66
|
+
const instructions = [
|
|
67
|
+
"Improve clarity & conformity to Conventional Commits while preserving meaning."
|
|
68
|
+
];
|
|
69
|
+
const syntheticPlan = {
|
|
70
|
+
commits: [
|
|
71
|
+
{
|
|
72
|
+
title: commit.title,
|
|
73
|
+
body: commit.body,
|
|
74
|
+
score: 0,
|
|
75
|
+
reasons: []
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
};
|
|
79
|
+
const provider = new OpenCodeProvider(config.model);
|
|
80
|
+
const phased = createPhasedSpinner(ora);
|
|
81
|
+
let refined = null;
|
|
82
|
+
try {
|
|
83
|
+
phased.phase("Preparing prompt");
|
|
84
|
+
const messages = buildRefineMessages({
|
|
85
|
+
originalPlan: syntheticPlan,
|
|
86
|
+
index: 0,
|
|
87
|
+
instructions,
|
|
88
|
+
config
|
|
89
|
+
});
|
|
90
|
+
phased.phase("Calling model");
|
|
91
|
+
const raw = await provider.chat(messages, { maxTokens: config.maxTokens });
|
|
92
|
+
phased.phase("Parsing response");
|
|
93
|
+
refined = await extractJSON(raw);
|
|
94
|
+
} catch (e) {
|
|
95
|
+
phased.spinner.fail("Reword failed: " + (e?.message || e));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
phased.stop();
|
|
99
|
+
if (!refined || !refined.commits.length) {
|
|
100
|
+
console.log("No refined commit produced.");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const candidate = refined.commits[0];
|
|
104
|
+
candidate.title = formatCommitTitle(candidate.title, {
|
|
105
|
+
allowGitmoji: config.style === "gitmoji" || config.style === "gitmoji-pure",
|
|
106
|
+
mode: config.style
|
|
107
|
+
});
|
|
108
|
+
sectionTitle("Proposed commit");
|
|
109
|
+
renderCommitBlock({
|
|
110
|
+
title: chalk.yellow(candidate.title),
|
|
111
|
+
body: candidate.body,
|
|
112
|
+
hideMessageLabel: true
|
|
113
|
+
});
|
|
114
|
+
borderLine();
|
|
115
|
+
const resolvedHash = (await git.revparse([hash])).trim();
|
|
116
|
+
const headHash = (await git.revparse(["HEAD"])).trim();
|
|
117
|
+
const isHead = headHash === resolvedHash || headHash.startsWith(resolvedHash);
|
|
118
|
+
const ok = config.yes || (await inquirer.prompt([
|
|
119
|
+
{
|
|
120
|
+
type: "list",
|
|
121
|
+
name: "ok",
|
|
122
|
+
message: isHead ? "Amend HEAD with this message?" : "Apply rewrite (history will change)?",
|
|
123
|
+
choices: [
|
|
124
|
+
{ name: "Yes", value: true },
|
|
125
|
+
{ name: "No", value: false }
|
|
126
|
+
],
|
|
127
|
+
default: 0
|
|
128
|
+
}
|
|
129
|
+
])).ok;
|
|
130
|
+
if (!ok) {
|
|
131
|
+
borderLine();
|
|
132
|
+
abortMessage();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const full = candidate.body ? `${candidate.title}
|
|
136
|
+
|
|
137
|
+
${candidate.body}` : candidate.title;
|
|
138
|
+
if (isHead) {
|
|
139
|
+
try {
|
|
140
|
+
await git.commit(full, { "--amend": null });
|
|
141
|
+
} catch (e) {
|
|
142
|
+
borderLine("Failed to amend HEAD: " + (e?.message || e));
|
|
143
|
+
borderLine();
|
|
144
|
+
abortMessage();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
borderLine();
|
|
148
|
+
finalSuccess({ count: 1, startedAt });
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const ancestorOk = await isAncestor(resolvedHash, headHash);
|
|
152
|
+
if (!ancestorOk) {
|
|
153
|
+
borderLine("Selected commit is not an ancestor of HEAD.");
|
|
154
|
+
borderLine("Cannot safely rewrite automatically.");
|
|
155
|
+
borderLine();
|
|
156
|
+
abortMessage();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
let mergesRange = "";
|
|
160
|
+
try {
|
|
161
|
+
mergesRange = (await git.raw(["rev-list", "--merges", `${resolvedHash}..HEAD`])).trim();
|
|
162
|
+
} catch {
|
|
163
|
+
}
|
|
164
|
+
if (mergesRange) {
|
|
165
|
+
sectionTitle("Unsafe automatic rewrite");
|
|
166
|
+
borderLine("Merge commits detected between target and HEAD.");
|
|
167
|
+
borderLine("Falling back to manual instructions (preserving previous behavior).");
|
|
168
|
+
borderLine();
|
|
169
|
+
sectionTitle("Apply manually");
|
|
170
|
+
borderLine(`1. git rebase -i ${resolvedHash}~1 --reword`);
|
|
171
|
+
borderLine("2. Mark the line as reword if needed.");
|
|
172
|
+
borderLine("3. Replace the message with:");
|
|
173
|
+
borderLine();
|
|
174
|
+
borderLine(candidate.title);
|
|
175
|
+
if (candidate.body) candidate.body.split("\n").forEach((l) => borderLine(l || void 0));
|
|
176
|
+
borderLine();
|
|
177
|
+
abortMessage();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
const tree = (await git.raw(["show", "-s", "--format=%T", resolvedHash])).trim();
|
|
182
|
+
const parent = commit.parents[0];
|
|
183
|
+
const args = ["commit-tree", tree];
|
|
184
|
+
if (parent) args.push("-p", parent);
|
|
185
|
+
args.push("-m", full);
|
|
186
|
+
const newHash = (await git.raw(args)).trim();
|
|
187
|
+
const currentBranch = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
|
|
188
|
+
const rebaseTarget = currentBranch === "HEAD" ? "HEAD" : currentBranch;
|
|
189
|
+
await git.raw(["rebase", "--onto", newHash, resolvedHash, rebaseTarget]);
|
|
190
|
+
const afterBranch = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
|
|
191
|
+
if (afterBranch === "HEAD" && rebaseTarget !== "HEAD") {
|
|
192
|
+
try {
|
|
193
|
+
await git.checkout([rebaseTarget]);
|
|
194
|
+
} catch {
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
sectionTitle("Updated commit");
|
|
198
|
+
borderLine(`Rewrote ${resolvedHash.slice(0, 7)} \u2192 ${newHash.slice(0, 7)}`);
|
|
199
|
+
renderCommitBlock({ title: candidate.title, body: candidate.body, hideMessageLabel: true });
|
|
200
|
+
borderLine();
|
|
201
|
+
finalSuccess({ count: 1, startedAt });
|
|
202
|
+
} catch (e) {
|
|
203
|
+
borderLine("Automatic rewrite failed: " + (e?.message || e));
|
|
204
|
+
borderLine("If a rebase is in progress, resolve conflicts then run: git rebase --continue");
|
|
205
|
+
borderLine("Or abort with: git rebase --abort");
|
|
206
|
+
borderLine();
|
|
207
|
+
abortMessage();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
export {
|
|
211
|
+
runReword
|
|
212
|
+
};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OpenCodeProvider,
|
|
3
|
+
abortMessage,
|
|
4
|
+
animateHeaderBase,
|
|
5
|
+
borderLine,
|
|
6
|
+
buildRefineMessages,
|
|
7
|
+
createPhasedSpinner,
|
|
8
|
+
extractJSON,
|
|
9
|
+
finalSuccess,
|
|
10
|
+
formatCommitTitle,
|
|
11
|
+
renderCommitBlock,
|
|
12
|
+
sectionTitle
|
|
13
|
+
} from "./chunk-2WRUFO3O.js";
|
|
14
|
+
|
|
15
|
+
// src/workflow/reword.ts
|
|
16
|
+
import chalk from "chalk";
|
|
17
|
+
import ora from "ora";
|
|
18
|
+
import inquirer from "inquirer";
|
|
19
|
+
import { simpleGit } from "simple-git";
|
|
20
|
+
var git = simpleGit();
|
|
21
|
+
async function getCommitMessage(hash) {
|
|
22
|
+
try {
|
|
23
|
+
const raw = await git.show([`${hash}`, "--quiet", "--format=%P%n%B"]);
|
|
24
|
+
const lines = raw.split("\n");
|
|
25
|
+
const parentsLine = lines.shift() || "";
|
|
26
|
+
const parents = parentsLine.trim().length ? parentsLine.trim().split(/\s+/) : [];
|
|
27
|
+
const message = lines.join("\n").trim();
|
|
28
|
+
if (!message) return null;
|
|
29
|
+
const [first, ...rest] = message.split("\n");
|
|
30
|
+
const body = rest.join("\n").trim() || void 0;
|
|
31
|
+
return { title: first, body, parents };
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function isAncestor(ancestor, head) {
|
|
37
|
+
try {
|
|
38
|
+
const mb = (await git.raw(["merge-base", ancestor, head])).trim();
|
|
39
|
+
const anc = (await git.revparse([ancestor])).trim();
|
|
40
|
+
return mb === anc;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function runReword(config, hash) {
|
|
46
|
+
const startedAt = Date.now();
|
|
47
|
+
const commit = await getCommitMessage(hash);
|
|
48
|
+
if (!commit) {
|
|
49
|
+
console.log(`Commit not found: ${hash}`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (commit.parents.length > 1) {
|
|
53
|
+
console.log("Refusing to reword a merge commit (multiple parents).");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (process.stdout.isTTY) {
|
|
57
|
+
await animateHeaderBase("ai-conventional-commit", config.model);
|
|
58
|
+
borderLine();
|
|
59
|
+
}
|
|
60
|
+
sectionTitle("Original commit");
|
|
61
|
+
borderLine(chalk.yellow(commit.title));
|
|
62
|
+
if (commit.body) {
|
|
63
|
+
commit.body.split("\n").forEach((l) => l.trim().length ? borderLine(l) : borderLine());
|
|
64
|
+
}
|
|
65
|
+
borderLine();
|
|
66
|
+
const instructions = [
|
|
67
|
+
"Improve clarity & conformity to Conventional Commits while preserving meaning."
|
|
68
|
+
];
|
|
69
|
+
const syntheticPlan = {
|
|
70
|
+
commits: [
|
|
71
|
+
{
|
|
72
|
+
title: commit.title,
|
|
73
|
+
body: commit.body,
|
|
74
|
+
score: 0,
|
|
75
|
+
reasons: []
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
};
|
|
79
|
+
const provider = new OpenCodeProvider(config.model);
|
|
80
|
+
const phased = createPhasedSpinner(ora);
|
|
81
|
+
let refined = null;
|
|
82
|
+
try {
|
|
83
|
+
phased.phase("Preparing prompt");
|
|
84
|
+
const messages = buildRefineMessages({
|
|
85
|
+
originalPlan: syntheticPlan,
|
|
86
|
+
index: 0,
|
|
87
|
+
instructions,
|
|
88
|
+
config
|
|
89
|
+
});
|
|
90
|
+
phased.phase("Calling model");
|
|
91
|
+
const raw = await provider.chat(messages, { maxTokens: config.maxTokens });
|
|
92
|
+
phased.phase("Parsing response");
|
|
93
|
+
refined = await extractJSON(raw);
|
|
94
|
+
} catch (e) {
|
|
95
|
+
phased.spinner.fail("Reword failed: " + (e?.message || e));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
phased.stop();
|
|
99
|
+
if (!refined || !refined.commits.length) {
|
|
100
|
+
console.log("No refined commit produced.");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const candidate = refined.commits[0];
|
|
104
|
+
candidate.title = formatCommitTitle(candidate.title, {
|
|
105
|
+
allowGitmoji: config.style === "gitmoji" || config.style === "gitmoji-pure",
|
|
106
|
+
mode: config.style
|
|
107
|
+
});
|
|
108
|
+
sectionTitle("Proposed commit");
|
|
109
|
+
renderCommitBlock({
|
|
110
|
+
title: chalk.yellow(candidate.title),
|
|
111
|
+
body: candidate.body,
|
|
112
|
+
hideMessageLabel: true
|
|
113
|
+
});
|
|
114
|
+
borderLine();
|
|
115
|
+
const resolvedHash = (await git.revparse([hash])).trim();
|
|
116
|
+
const headHash = (await git.revparse(["HEAD"])).trim();
|
|
117
|
+
const isHead = headHash === resolvedHash || headHash.startsWith(resolvedHash);
|
|
118
|
+
const ok = config.yes || (await inquirer.prompt([
|
|
119
|
+
{
|
|
120
|
+
type: "list",
|
|
121
|
+
name: "ok",
|
|
122
|
+
message: isHead ? "Amend HEAD with this message?" : "Apply rewrite (history will change)?",
|
|
123
|
+
choices: [
|
|
124
|
+
{ name: "Yes", value: true },
|
|
125
|
+
{ name: "No", value: false }
|
|
126
|
+
],
|
|
127
|
+
default: 0
|
|
128
|
+
}
|
|
129
|
+
])).ok;
|
|
130
|
+
if (!ok) {
|
|
131
|
+
borderLine();
|
|
132
|
+
abortMessage();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const full = candidate.body ? `${candidate.title}
|
|
136
|
+
|
|
137
|
+
${candidate.body}` : candidate.title;
|
|
138
|
+
if (isHead) {
|
|
139
|
+
try {
|
|
140
|
+
await git.commit(full, { "--amend": null });
|
|
141
|
+
} catch (e) {
|
|
142
|
+
borderLine("Failed to amend HEAD: " + (e?.message || e));
|
|
143
|
+
borderLine();
|
|
144
|
+
abortMessage();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
borderLine();
|
|
148
|
+
finalSuccess({ count: 1, startedAt });
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const ancestorOk = await isAncestor(resolvedHash, headHash);
|
|
152
|
+
if (!ancestorOk) {
|
|
153
|
+
borderLine("Selected commit is not an ancestor of HEAD.");
|
|
154
|
+
borderLine("Cannot safely rewrite automatically.");
|
|
155
|
+
borderLine();
|
|
156
|
+
abortMessage();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
let mergesRange = "";
|
|
160
|
+
try {
|
|
161
|
+
mergesRange = (await git.raw(["rev-list", "--merges", `${resolvedHash}..HEAD`])).trim();
|
|
162
|
+
} catch {
|
|
163
|
+
}
|
|
164
|
+
if (mergesRange) {
|
|
165
|
+
sectionTitle("Unsafe automatic rewrite");
|
|
166
|
+
borderLine("Merge commits detected between target and HEAD.");
|
|
167
|
+
borderLine("Falling back to manual instructions (preserving previous behavior).");
|
|
168
|
+
borderLine();
|
|
169
|
+
sectionTitle("Apply manually");
|
|
170
|
+
borderLine(`1. git rebase -i ${resolvedHash}~1 --reword`);
|
|
171
|
+
borderLine("2. Mark the line as reword if needed.");
|
|
172
|
+
borderLine("3. Replace the message with:");
|
|
173
|
+
borderLine();
|
|
174
|
+
borderLine(candidate.title);
|
|
175
|
+
if (candidate.body) candidate.body.split("\n").forEach((l) => borderLine(l || void 0));
|
|
176
|
+
borderLine();
|
|
177
|
+
abortMessage();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
const tree = (await git.raw(["show", "-s", "--format=%T", resolvedHash])).trim();
|
|
182
|
+
const parent = commit.parents[0];
|
|
183
|
+
const args = ["commit-tree", tree];
|
|
184
|
+
if (parent) args.push("-p", parent);
|
|
185
|
+
args.push("-m", full);
|
|
186
|
+
const newHash = (await git.raw(args)).trim();
|
|
187
|
+
const currentBranch = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
|
|
188
|
+
const rebaseTarget = currentBranch === "HEAD" ? "HEAD" : currentBranch;
|
|
189
|
+
await git.raw(["rebase", "--onto", newHash, resolvedHash, rebaseTarget]);
|
|
190
|
+
const afterBranch = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
|
|
191
|
+
if (afterBranch === "HEAD" && rebaseTarget !== "HEAD") {
|
|
192
|
+
try {
|
|
193
|
+
await git.checkout([rebaseTarget]);
|
|
194
|
+
} catch {
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
sectionTitle("Updated commit");
|
|
198
|
+
borderLine(`Rewrote ${resolvedHash.slice(0, 7)} \u2192 ${newHash.slice(0, 7)}`);
|
|
199
|
+
renderCommitBlock({ title: candidate.title, body: candidate.body, hideMessageLabel: true });
|
|
200
|
+
borderLine();
|
|
201
|
+
finalSuccess({ count: 1, startedAt });
|
|
202
|
+
} catch (e) {
|
|
203
|
+
borderLine("Automatic rewrite failed: " + (e?.message || e));
|
|
204
|
+
borderLine("If a rebase is in progress, resolve conflicts then run: git rebase --continue");
|
|
205
|
+
borderLine("Or abort with: git rebase --abort");
|
|
206
|
+
borderLine();
|
|
207
|
+
abortMessage();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
export {
|
|
211
|
+
runReword
|
|
212
|
+
};
|