@hallaxius/forge 0.1.1 → 0.1.3
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/cli.js +31178 -21697
- package/package.json +12 -6
- package/src/cli.ts +10 -10
- package/src/commands/ci.ts +28 -0
- package/src/commands/commit.ts +2 -7
- package/src/commands/init.ts +3 -7
- package/src/commands/issue.ts +63 -0
- package/src/commands/pr.ts +65 -0
- package/src/commands/release.ts +26 -0
- package/src/commands/setup.ts +32 -33
- package/src/lib/auth.ts +95 -18
- package/src/lib/config.ts +12 -3
- package/src/lib/git.ts +397 -236
- package/src/lib/github.ts +160 -0
- package/src/lib/ui.ts +8 -14
- package/src/lib/validators.ts +0 -11
- package/src/version.const.ts +1 -1
- package/src/commands/archive.ts +0 -35
- package/src/commands/bisect.ts +0 -102
- package/src/commands/cherry-pick.ts +0 -57
- package/src/commands/clean.ts +0 -76
- package/src/commands/worktree.ts +0 -92
package/src/lib/git.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { diffLines } from "diff";
|
|
4
|
+
import git, {
|
|
5
|
+
currentBranch,
|
|
6
|
+
log as gitLog,
|
|
7
|
+
listBranches,
|
|
8
|
+
listRemotes,
|
|
9
|
+
listTags,
|
|
10
|
+
statusMatrix,
|
|
11
|
+
} from "isomorphic-git";
|
|
12
|
+
import http from "isomorphic-git/http/node";
|
|
13
|
+
import { resolveToken } from "./auth.js";
|
|
14
|
+
|
|
15
|
+
const dir = process.cwd();
|
|
4
16
|
|
|
5
17
|
interface StatusResult {
|
|
6
18
|
current: string;
|
|
@@ -29,354 +41,503 @@ interface StashEntry {
|
|
|
29
41
|
description: string;
|
|
30
42
|
}
|
|
31
43
|
|
|
44
|
+
function statusChar(head: number, workdir: number): string {
|
|
45
|
+
if (workdir === 0) return "D";
|
|
46
|
+
if (head === 0 && workdir === 2) return "?";
|
|
47
|
+
if (workdir === 2) return "M";
|
|
48
|
+
return " ";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function indexChar(head: number, stage: number): string {
|
|
52
|
+
if (head === 0 && stage === 2) return "A";
|
|
53
|
+
if (stage === 0 && head === 1) return "D";
|
|
54
|
+
if (stage === 2 && head === 1) return "M";
|
|
55
|
+
return " ";
|
|
56
|
+
}
|
|
57
|
+
|
|
32
58
|
export async function getStatus(): Promise<StatusResult> {
|
|
33
|
-
const
|
|
34
|
-
|
|
59
|
+
const matrix = await statusMatrix({ fs, dir });
|
|
60
|
+
|
|
61
|
+
const current = (await currentBranch({ fs, dir, fullname: false })) || "";
|
|
62
|
+
const logResult = await gitLog({ fs, dir, depth: 5 });
|
|
63
|
+
|
|
64
|
+
const files = matrix
|
|
65
|
+
.filter(
|
|
66
|
+
([_f, head, workdir, stage]) =>
|
|
67
|
+
head !== 1 || workdir !== 1 || stage !== 1,
|
|
68
|
+
)
|
|
69
|
+
.map(([filepath, head, workdir, stage]) => ({
|
|
70
|
+
path: filepath,
|
|
71
|
+
working_dir: statusChar(head as number, workdir as number),
|
|
72
|
+
index: indexChar(head as number, stage as number),
|
|
73
|
+
}));
|
|
35
74
|
|
|
36
75
|
return {
|
|
37
|
-
current
|
|
38
|
-
tracking:
|
|
39
|
-
ahead:
|
|
40
|
-
behind:
|
|
41
|
-
files
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
recentCommits: log.all.map((c) => ({
|
|
47
|
-
hash: c.hash,
|
|
48
|
-
date: c.date,
|
|
49
|
-
message: c.message,
|
|
76
|
+
current,
|
|
77
|
+
tracking: "",
|
|
78
|
+
ahead: 0,
|
|
79
|
+
behind: 0,
|
|
80
|
+
files,
|
|
81
|
+
recentCommits: logResult.map((c) => ({
|
|
82
|
+
hash: c.oid,
|
|
83
|
+
date: String(c.commit.author.timestamp),
|
|
84
|
+
message: c.commit.message,
|
|
50
85
|
})),
|
|
51
86
|
};
|
|
52
87
|
}
|
|
53
88
|
|
|
54
89
|
export async function commit(message: string): Promise<string> {
|
|
55
|
-
const
|
|
56
|
-
|
|
90
|
+
const name =
|
|
91
|
+
(await git.getConfig({ fs, dir, path: "user.name" })) || "Unknown";
|
|
92
|
+
const email =
|
|
93
|
+
(await git.getConfig({ fs, dir, path: "user.email" })) ||
|
|
94
|
+
"unknown@localhost";
|
|
95
|
+
|
|
96
|
+
const result = await git.commit({
|
|
97
|
+
fs,
|
|
98
|
+
dir,
|
|
99
|
+
message,
|
|
100
|
+
author: { name, email },
|
|
101
|
+
});
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function amendCommit(): Promise<void> {
|
|
106
|
+
const commits = await gitLog({ fs, dir, depth: 1 });
|
|
107
|
+
if (commits.length === 0) throw new Error("No commits to amend");
|
|
108
|
+
const { commit: c } = commits[0];
|
|
109
|
+
|
|
110
|
+
const name =
|
|
111
|
+
(await git.getConfig({ fs, dir, path: "user.name" })) || "Unknown";
|
|
112
|
+
const email =
|
|
113
|
+
(await git.getConfig({ fs, dir, path: "user.email" })) ||
|
|
114
|
+
"unknown@localhost";
|
|
115
|
+
|
|
116
|
+
await git.commit({
|
|
117
|
+
fs,
|
|
118
|
+
dir,
|
|
119
|
+
message: c.message,
|
|
120
|
+
author: c.author,
|
|
121
|
+
committer: { name, email },
|
|
122
|
+
tree: c.tree,
|
|
123
|
+
parent: c.parent.map((p) => p.toString()),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function add(filepaths: string | string[]): Promise<void> {
|
|
128
|
+
const files = Array.isArray(filepaths) ? filepaths : [filepaths];
|
|
129
|
+
for (const filepath of files) {
|
|
130
|
+
await git.add({ fs, dir, filepath });
|
|
131
|
+
}
|
|
57
132
|
}
|
|
58
133
|
|
|
59
134
|
export async function push(force: boolean = false): Promise<string> {
|
|
60
|
-
const
|
|
61
|
-
const result = await git.push(
|
|
135
|
+
const token = await resolveToken();
|
|
136
|
+
const result = await git.push({
|
|
137
|
+
fs,
|
|
138
|
+
http,
|
|
139
|
+
dir,
|
|
140
|
+
force,
|
|
141
|
+
token,
|
|
142
|
+
oauth2format: "github",
|
|
143
|
+
});
|
|
62
144
|
return result;
|
|
63
145
|
}
|
|
64
146
|
|
|
65
147
|
export async function pullRebase(): Promise<string> {
|
|
66
|
-
const
|
|
148
|
+
const token = await resolveToken();
|
|
149
|
+
await git.fetch({ fs, http, dir, token, oauth2format: "github" });
|
|
150
|
+
const current = (await currentBranch({ fs, dir })) || "";
|
|
151
|
+
const result = await git.merge({
|
|
152
|
+
fs,
|
|
153
|
+
dir,
|
|
154
|
+
theirs: current,
|
|
155
|
+
fastForwardOnly: true,
|
|
156
|
+
});
|
|
67
157
|
return result;
|
|
68
158
|
}
|
|
69
159
|
|
|
70
160
|
export async function log(maxCount: number = 10): Promise<LogEntry[]> {
|
|
71
|
-
const result = await
|
|
72
|
-
return result.
|
|
73
|
-
hash: c.
|
|
74
|
-
date: c.
|
|
75
|
-
message: c.message,
|
|
76
|
-
author: c.
|
|
161
|
+
const result = await gitLog({ fs, dir, depth: maxCount, ref: "HEAD" });
|
|
162
|
+
return result.map((c) => ({
|
|
163
|
+
hash: c.oid,
|
|
164
|
+
date: String(c.commit.author.timestamp),
|
|
165
|
+
message: c.commit.message,
|
|
166
|
+
author: c.commit.author.name,
|
|
77
167
|
}));
|
|
78
168
|
}
|
|
79
169
|
|
|
80
170
|
export async function diff(): Promise<string> {
|
|
81
|
-
const
|
|
82
|
-
|
|
171
|
+
const matrix = await statusMatrix({ fs, dir });
|
|
172
|
+
const changed = matrix.filter(
|
|
173
|
+
([_f, head, workdir, _stage]) => head !== 1 || workdir !== 1,
|
|
174
|
+
);
|
|
175
|
+
if (changed.length === 0) return "";
|
|
176
|
+
|
|
177
|
+
const headOid = await git.resolveRef({ fs, dir, ref: "HEAD" });
|
|
178
|
+
const output: string[] = [];
|
|
179
|
+
|
|
180
|
+
for (const [filepath, head, workdir] of changed) {
|
|
181
|
+
let oldContent = "";
|
|
182
|
+
let newContent = "";
|
|
183
|
+
|
|
184
|
+
if ((head as number) !== 0) {
|
|
185
|
+
try {
|
|
186
|
+
const blob = await git.readBlob({
|
|
187
|
+
fs,
|
|
188
|
+
dir,
|
|
189
|
+
oid: headOid,
|
|
190
|
+
filepath,
|
|
191
|
+
});
|
|
192
|
+
oldContent = new TextDecoder().decode(blob.blob);
|
|
193
|
+
} catch {}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if ((workdir as number) !== 0) {
|
|
197
|
+
try {
|
|
198
|
+
newContent = readFileSync(join(dir, filepath), "utf-8");
|
|
199
|
+
} catch {}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
output.push(`diff --git a/${filepath} b/${filepath}`);
|
|
203
|
+
const patch = diffLines(oldContent, newContent);
|
|
204
|
+
for (const part of patch) {
|
|
205
|
+
if (part.added) {
|
|
206
|
+
for (const line of part.value.split("\n").filter(Boolean)) {
|
|
207
|
+
output.push(`+${line}`);
|
|
208
|
+
}
|
|
209
|
+
} else if (part.removed) {
|
|
210
|
+
for (const line of part.value.split("\n").filter(Boolean)) {
|
|
211
|
+
output.push(`-${line}`);
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
for (const line of part.value.split("\n").filter(Boolean)) {
|
|
215
|
+
output.push(` ${line}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return output.join("\n");
|
|
83
222
|
}
|
|
84
223
|
|
|
85
224
|
export async function diffStaged(): Promise<string> {
|
|
86
|
-
const
|
|
87
|
-
|
|
225
|
+
const matrix = await statusMatrix({ fs, dir });
|
|
226
|
+
const changed = matrix.filter(
|
|
227
|
+
([_f, head, _workdir, stage]) => head !== 1 || (stage as number) !== 1,
|
|
228
|
+
);
|
|
229
|
+
if (changed.length === 0) return "";
|
|
230
|
+
|
|
231
|
+
const headOid = await git.resolveRef({ fs, dir, ref: "HEAD" });
|
|
232
|
+
const output: string[] = [];
|
|
233
|
+
|
|
234
|
+
for (const [filepath, head, _workdir, stage] of changed) {
|
|
235
|
+
let oldContent = "";
|
|
236
|
+
let newContent = "";
|
|
237
|
+
|
|
238
|
+
if ((head as number) !== 0) {
|
|
239
|
+
try {
|
|
240
|
+
const blob = await git.readBlob({
|
|
241
|
+
fs,
|
|
242
|
+
dir,
|
|
243
|
+
oid: headOid,
|
|
244
|
+
filepath,
|
|
245
|
+
});
|
|
246
|
+
oldContent = new TextDecoder().decode(blob.blob);
|
|
247
|
+
} catch {}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if ((stage as number) !== 0) {
|
|
251
|
+
try {
|
|
252
|
+
const blob = await git.readBlob({
|
|
253
|
+
fs,
|
|
254
|
+
dir,
|
|
255
|
+
oid: headOid,
|
|
256
|
+
filepath,
|
|
257
|
+
});
|
|
258
|
+
newContent = new TextDecoder().decode(blob.blob);
|
|
259
|
+
} catch {}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
output.push(`diff --git a/${filepath} b/${filepath} (staged)`);
|
|
263
|
+
const patch = diffLines(oldContent, newContent);
|
|
264
|
+
for (const part of patch) {
|
|
265
|
+
if (part.added) {
|
|
266
|
+
for (const line of part.value.split("\n").filter(Boolean)) {
|
|
267
|
+
output.push(`+${line}`);
|
|
268
|
+
}
|
|
269
|
+
} else if (part.removed) {
|
|
270
|
+
for (const line of part.value.split("\n").filter(Boolean)) {
|
|
271
|
+
output.push(`-${line}`);
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
for (const line of part.value.split("\n").filter(Boolean)) {
|
|
275
|
+
output.push(` ${line}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return output.join("\n");
|
|
88
282
|
}
|
|
89
283
|
|
|
90
284
|
export async function getBranches(): Promise<BranchesResult> {
|
|
91
|
-
const
|
|
285
|
+
const all = await listBranches({ fs, dir });
|
|
286
|
+
const current = (await currentBranch({ fs, dir, fullname: false })) || "";
|
|
92
287
|
return {
|
|
93
|
-
current
|
|
94
|
-
branches:
|
|
95
|
-
|
|
96
|
-
: [],
|
|
97
|
-
all: result.all || [],
|
|
288
|
+
current,
|
|
289
|
+
branches: all.filter((b) => b !== current),
|
|
290
|
+
all,
|
|
98
291
|
};
|
|
99
292
|
}
|
|
100
293
|
|
|
101
294
|
export async function createBranch(name: string): Promise<void> {
|
|
102
|
-
await git.branch(
|
|
295
|
+
await git.branch({ fs, dir, ref: name });
|
|
103
296
|
}
|
|
104
297
|
|
|
105
298
|
export async function deleteBranch(
|
|
106
299
|
name: string,
|
|
107
300
|
force: boolean = false,
|
|
108
301
|
): Promise<void> {
|
|
109
|
-
|
|
110
|
-
|
|
302
|
+
if (force) {
|
|
303
|
+
await git.deleteBranch({ fs, dir, ref: name });
|
|
304
|
+
} else {
|
|
305
|
+
await git.deleteBranch({ fs, dir, ref: name });
|
|
306
|
+
}
|
|
111
307
|
}
|
|
112
308
|
|
|
113
309
|
export async function switchBranch(name: string): Promise<void> {
|
|
114
|
-
await git.checkout(name);
|
|
310
|
+
await git.checkout({ fs, dir, ref: name });
|
|
115
311
|
}
|
|
116
312
|
|
|
117
313
|
export async function stash(): Promise<string> {
|
|
118
|
-
|
|
119
|
-
return
|
|
314
|
+
await git.stash({ fs, dir });
|
|
315
|
+
return "Stash saved";
|
|
120
316
|
}
|
|
121
317
|
|
|
122
318
|
export async function stashPop(): Promise<string> {
|
|
123
|
-
const
|
|
124
|
-
|
|
319
|
+
const stashes = await stashList();
|
|
320
|
+
if (stashes.length === 0) throw new Error("No stashes to pop");
|
|
321
|
+
|
|
322
|
+
try {
|
|
323
|
+
await git.stash({ fs, dir });
|
|
324
|
+
} catch {
|
|
325
|
+
throw new Error("Failed to apply stash");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const refs = await git.listRefs({ fs, dir, prefix: "refs/stash" });
|
|
329
|
+
if (refs.length > 0) {
|
|
330
|
+
await git.deleteRef({ fs, dir, ref: refs[0] });
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return "Stash popped";
|
|
125
334
|
}
|
|
126
335
|
|
|
127
336
|
export async function stashList(): Promise<StashEntry[]> {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
337
|
+
try {
|
|
338
|
+
const stashLog = await gitLog({ fs, dir, ref: "refs/stash" });
|
|
339
|
+
return stashLog.map((c, i) => ({
|
|
340
|
+
index: i + 1,
|
|
341
|
+
description: c.commit.message,
|
|
342
|
+
}));
|
|
343
|
+
} catch {
|
|
344
|
+
return [];
|
|
345
|
+
}
|
|
133
346
|
}
|
|
134
347
|
|
|
135
348
|
export async function tag(name: string, message?: string): Promise<string> {
|
|
136
349
|
if (message) {
|
|
137
|
-
await git.tag(
|
|
350
|
+
await git.tag({ fs, dir, ref: name, message });
|
|
138
351
|
} else {
|
|
139
|
-
await git.tag(
|
|
352
|
+
await git.tag({ fs, dir, ref: name });
|
|
140
353
|
}
|
|
141
354
|
return name;
|
|
142
355
|
}
|
|
143
356
|
|
|
144
357
|
export async function tagList(): Promise<string[]> {
|
|
145
|
-
|
|
146
|
-
return result.split("\n").filter(Boolean);
|
|
358
|
+
return listTags({ fs, dir });
|
|
147
359
|
}
|
|
148
360
|
|
|
149
361
|
export async function fetch(): Promise<string> {
|
|
150
|
-
const
|
|
362
|
+
const token = await resolveToken();
|
|
363
|
+
const result = await git.fetch({
|
|
364
|
+
fs,
|
|
365
|
+
http,
|
|
366
|
+
dir,
|
|
367
|
+
token,
|
|
368
|
+
oauth2format: "github",
|
|
369
|
+
});
|
|
151
370
|
return result;
|
|
152
371
|
}
|
|
153
372
|
|
|
154
373
|
export async function undo(): Promise<string> {
|
|
155
|
-
const
|
|
156
|
-
|
|
374
|
+
const commits = await gitLog({ fs, dir, depth: 1, ref: "HEAD" });
|
|
375
|
+
if (commits.length === 0) throw new Error("No commits to undo");
|
|
376
|
+
const c = commits[0];
|
|
377
|
+
if (c.commit.parent.length === 0) throw new Error("Cannot undo root commit");
|
|
378
|
+
|
|
379
|
+
const parentOid = c.commit.parent[0];
|
|
380
|
+
await git.writeRef({
|
|
381
|
+
fs,
|
|
382
|
+
dir,
|
|
383
|
+
ref: "HEAD",
|
|
384
|
+
value: parentOid.toString(),
|
|
385
|
+
force: true,
|
|
386
|
+
});
|
|
387
|
+
return `Undone to ${parentOid.toString().slice(0, 7)}`;
|
|
157
388
|
}
|
|
158
389
|
|
|
159
390
|
export async function getCurrentBranch(): Promise<string> {
|
|
160
|
-
|
|
161
|
-
return result.current;
|
|
391
|
+
return (await currentBranch({ fs, dir })) || "";
|
|
162
392
|
}
|
|
163
393
|
|
|
164
394
|
export async function clone(
|
|
165
395
|
url: string,
|
|
166
|
-
|
|
396
|
+
targetDir?: string,
|
|
167
397
|
options?: {
|
|
168
|
-
ssh?: boolean;
|
|
169
398
|
depth?: number;
|
|
170
399
|
branch?: string;
|
|
171
400
|
recurseSubmodules?: boolean;
|
|
172
401
|
},
|
|
173
402
|
): Promise<string> {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
403
|
+
const token = await resolveToken();
|
|
404
|
+
const cloneDir =
|
|
405
|
+
targetDir || url.split("/").pop()?.replace(".git", "") || "repo";
|
|
406
|
+
|
|
407
|
+
await git.clone({
|
|
408
|
+
fs,
|
|
409
|
+
http,
|
|
410
|
+
dir: cloneDir,
|
|
411
|
+
url,
|
|
412
|
+
singleBranch: !!options?.branch,
|
|
413
|
+
ref: options?.branch,
|
|
414
|
+
depth: options?.depth,
|
|
415
|
+
token,
|
|
416
|
+
oauth2format: "github",
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
return cloneDir;
|
|
181
420
|
}
|
|
182
421
|
|
|
183
422
|
export async function init(
|
|
184
|
-
|
|
423
|
+
targetDir?: string,
|
|
185
424
|
options?: { initialBranch?: string },
|
|
186
425
|
): Promise<void> {
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
interface RemoteEntry {
|
|
195
|
-
name: string;
|
|
196
|
-
url: string;
|
|
426
|
+
const initDir = targetDir || ".";
|
|
427
|
+
await git.init({
|
|
428
|
+
fs,
|
|
429
|
+
dir: initDir,
|
|
430
|
+
defaultBranch: options?.initialBranch,
|
|
431
|
+
});
|
|
197
432
|
}
|
|
198
433
|
|
|
199
434
|
export async function remoteAdd(name: string, url: string): Promise<void> {
|
|
200
|
-
await git.addRemote(name, url);
|
|
435
|
+
await git.addRemote({ fs, dir, remote: name, url });
|
|
201
436
|
}
|
|
202
437
|
|
|
203
438
|
export async function remoteRemove(name: string): Promise<void> {
|
|
204
|
-
await git.
|
|
439
|
+
await git.deleteRemote({ fs, dir, remote: name });
|
|
205
440
|
}
|
|
206
441
|
|
|
207
442
|
export async function remoteSetUrl(
|
|
208
443
|
name: string,
|
|
209
444
|
newUrl: string,
|
|
210
445
|
): Promise<void> {
|
|
211
|
-
await git.
|
|
446
|
+
await git.setConfig({
|
|
447
|
+
fs,
|
|
448
|
+
dir,
|
|
449
|
+
path: `remote.${name}.url`,
|
|
450
|
+
value: newUrl,
|
|
451
|
+
});
|
|
212
452
|
}
|
|
213
453
|
|
|
214
454
|
export async function remoteRename(
|
|
215
455
|
oldName: string,
|
|
216
456
|
newName: string,
|
|
217
457
|
): Promise<void> {
|
|
218
|
-
await git.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
458
|
+
const url = await git.getConfig({
|
|
459
|
+
fs,
|
|
460
|
+
dir,
|
|
461
|
+
path: `remote.${oldName}.url`,
|
|
462
|
+
});
|
|
463
|
+
if (!url) throw new Error(`Remote '${oldName}' not found`);
|
|
225
464
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
return result.map((r) => ({ name: r.name, url: r.refs.fetch }));
|
|
465
|
+
await remoteAdd(newName, url);
|
|
466
|
+
await remoteRemove(oldName);
|
|
229
467
|
}
|
|
230
468
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
branch?: string,
|
|
240
|
-
options?: { new?: boolean; detach?: boolean },
|
|
241
|
-
): Promise<void> {
|
|
242
|
-
const args = ["worktree", "add"];
|
|
243
|
-
if (options?.detach) args.push("--detach");
|
|
244
|
-
args.push(path);
|
|
245
|
-
if (branch) {
|
|
246
|
-
if (options?.new) args.push(branch);
|
|
247
|
-
else args.push(branch);
|
|
248
|
-
}
|
|
249
|
-
await git.raw(args);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export async function worktreeList(): Promise<WorktreeEntry[]> {
|
|
253
|
-
const result = await git.raw(["worktree", "list", "--porcelain"]);
|
|
254
|
-
const entries: WorktreeEntry[] = [];
|
|
255
|
-
const lines = result.split("\n");
|
|
256
|
-
let current: Partial<WorktreeEntry> = {};
|
|
257
|
-
for (const line of lines) {
|
|
258
|
-
if (line.startsWith("worktree ")) {
|
|
259
|
-
current.path = line.slice(9).trim();
|
|
260
|
-
} else if (line.startsWith("HEAD ")) {
|
|
261
|
-
current.hash = line.slice(5).trim();
|
|
262
|
-
} else if (line.startsWith("branch ")) {
|
|
263
|
-
current.branch = line.slice(7).trim().replace("refs/heads/", "");
|
|
264
|
-
} else if (line === "") {
|
|
265
|
-
if (current.path) entries.push(current as WorktreeEntry);
|
|
266
|
-
current = {};
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
if (current.path) entries.push(current as WorktreeEntry);
|
|
270
|
-
return entries;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
export async function worktreeRemove(
|
|
274
|
-
path: string,
|
|
275
|
-
force: boolean = false,
|
|
276
|
-
): Promise<void> {
|
|
277
|
-
const args = ["worktree", "remove"];
|
|
278
|
-
if (force) args.push("--force");
|
|
279
|
-
args.push(path);
|
|
280
|
-
await git.raw(args);
|
|
469
|
+
export async function remoteGetUrl(name: string): Promise<string> {
|
|
470
|
+
const url = await git.getConfig({
|
|
471
|
+
fs,
|
|
472
|
+
dir,
|
|
473
|
+
path: `remote.${name}.url`,
|
|
474
|
+
});
|
|
475
|
+
if (!url) throw new Error(`Remote '${name}' not found`);
|
|
476
|
+
return url;
|
|
281
477
|
}
|
|
282
478
|
|
|
283
|
-
export async function
|
|
284
|
-
|
|
285
|
-
):
|
|
286
|
-
const args = ["worktree", "prune"];
|
|
287
|
-
if (dryRun) args.push("--dry-run");
|
|
288
|
-
const result = await git.raw(args);
|
|
289
|
-
return result.split("\n").filter(Boolean);
|
|
479
|
+
export async function remoteList(): Promise<{ name: string; url: string }[]> {
|
|
480
|
+
const remotes = await listRemotes({ fs, dir });
|
|
481
|
+
return remotes.map((r) => ({ name: r.remote, url: r.url }));
|
|
290
482
|
}
|
|
291
483
|
|
|
292
484
|
export async function merge(
|
|
293
485
|
branch: string,
|
|
294
|
-
options?: {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (options?.noCommit) args.push("--no-commit");
|
|
300
|
-
args.push(branch);
|
|
301
|
-
const result = await git.raw(args);
|
|
302
|
-
return result;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
export async function cherryPick(
|
|
306
|
-
commits: string[],
|
|
307
|
-
options?: { noCommit?: boolean; mainline?: number },
|
|
308
|
-
): Promise<void> {
|
|
309
|
-
const args = ["cherry-pick"];
|
|
310
|
-
if (options?.noCommit) args.push("--no-commit");
|
|
311
|
-
if (options?.mainline) args.push("-m", String(options.mainline));
|
|
312
|
-
args.push(...commits);
|
|
313
|
-
await git.raw(args);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
export async function cherryPickContinue(): Promise<void> {
|
|
317
|
-
await git.raw(["cherry-pick", "--continue"]);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
export async function cherryPickAbort(): Promise<void> {
|
|
321
|
-
await git.raw(["cherry-pick", "--abort"]);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
export async function clean(options?: {
|
|
325
|
-
dryRun?: boolean;
|
|
326
|
-
force?: boolean;
|
|
327
|
-
exclude?: string;
|
|
328
|
-
}): Promise<string[]> {
|
|
329
|
-
const args = ["clean", "-d"];
|
|
330
|
-
if (options?.dryRun) args.push("-n");
|
|
331
|
-
if (options?.force) args.push("-f");
|
|
332
|
-
else args.push("-f");
|
|
333
|
-
if (options?.exclude) args.push("-x", options.exclude);
|
|
334
|
-
const result = await git.raw(args);
|
|
335
|
-
return result.split("\n").filter(Boolean);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
export async function archive(
|
|
339
|
-
format: string,
|
|
340
|
-
options?: { prefix?: string; output?: string; treeIsh?: string },
|
|
486
|
+
options?: {
|
|
487
|
+
noFF?: boolean;
|
|
488
|
+
squash?: boolean;
|
|
489
|
+
noCommit?: boolean;
|
|
490
|
+
},
|
|
341
491
|
): Promise<string> {
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
492
|
+
const name =
|
|
493
|
+
(await git.getConfig({ fs, dir, path: "user.name" })) || "Unknown";
|
|
494
|
+
const email =
|
|
495
|
+
(await git.getConfig({ fs, dir, path: "user.email" })) ||
|
|
496
|
+
"unknown@localhost";
|
|
497
|
+
|
|
498
|
+
const result = await git.merge({
|
|
499
|
+
fs,
|
|
500
|
+
dir,
|
|
501
|
+
theirs: branch,
|
|
502
|
+
ours: undefined,
|
|
503
|
+
noCommit: options?.noCommit,
|
|
504
|
+
squash: options?.squash,
|
|
505
|
+
fastForward: !options?.noFF,
|
|
506
|
+
author: { name, email },
|
|
507
|
+
committer: { name, email },
|
|
508
|
+
});
|
|
348
509
|
return result;
|
|
349
510
|
}
|
|
350
511
|
|
|
351
|
-
export
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
512
|
+
export default {
|
|
513
|
+
getStatus,
|
|
514
|
+
commit,
|
|
515
|
+
amendCommit,
|
|
516
|
+
add,
|
|
517
|
+
push,
|
|
518
|
+
pullRebase,
|
|
519
|
+
log,
|
|
520
|
+
diff,
|
|
521
|
+
diffStaged,
|
|
522
|
+
getBranches,
|
|
523
|
+
createBranch,
|
|
524
|
+
deleteBranch,
|
|
525
|
+
switchBranch,
|
|
526
|
+
stash,
|
|
527
|
+
stashPop,
|
|
528
|
+
stashList,
|
|
529
|
+
tag,
|
|
530
|
+
tagList,
|
|
531
|
+
fetch,
|
|
532
|
+
undo,
|
|
533
|
+
getCurrentBranch,
|
|
534
|
+
clone,
|
|
535
|
+
init,
|
|
536
|
+
remoteAdd,
|
|
537
|
+
remoteRemove,
|
|
538
|
+
remoteSetUrl,
|
|
539
|
+
remoteRename,
|
|
540
|
+
remoteGetUrl,
|
|
541
|
+
remoteList,
|
|
542
|
+
merge,
|
|
543
|
+
};
|