@peiyanlu/git-utils 0.0.0-stage → 0.0.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/LICENSE +21 -0
- package/README.md +23 -2
- package/dist/index.d.mts +607 -0
- package/dist/index.mjs +1096 -0
- package/package.json +54 -3
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1096 @@
|
|
|
1
|
+
import { runGit, runGitSync } from "@peiyanlu/command-utils";
|
|
2
|
+
import { splitNonEmptyLines } from "@peiyanlu/ts-utils";
|
|
3
|
+
// src/raw.ts
|
|
4
|
+
/**
|
|
5
|
+
* 引用解析操作
|
|
6
|
+
* @defaults `git rev-parse <args>`
|
|
7
|
+
*/
|
|
8
|
+
const gitRevParse = async (args, options) => {
|
|
9
|
+
return runGit(["rev-parse", ...args], options);
|
|
10
|
+
};
|
|
11
|
+
/** {@link gitRevParse} 的同步版本 */
|
|
12
|
+
const gitRevParseSync = (args, options) => {
|
|
13
|
+
return runGitSync(["rev-parse", ...args], options);
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* 提交遍历操作
|
|
17
|
+
* @defaults `git rev-list <args>`
|
|
18
|
+
*/
|
|
19
|
+
const gitRevList = async (args, options) => {
|
|
20
|
+
return runGit(["rev-list", ...args], options);
|
|
21
|
+
};
|
|
22
|
+
/** {@link gitRevList} 的同步版本 */
|
|
23
|
+
const gitRevListSync = (args, options) => {
|
|
24
|
+
return runGitSync(["rev-list", ...args], options);
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* 引用遍历操作
|
|
28
|
+
* @defaults `git for-each-ref <args>`
|
|
29
|
+
*/
|
|
30
|
+
const gitForEachRef = async (args, options) => {
|
|
31
|
+
return runGit(["for-each-ref", ...args], options);
|
|
32
|
+
};
|
|
33
|
+
/** {@link gitForEachRef} 的同步版本 */
|
|
34
|
+
const gitForEachRefSync = (args, options) => {
|
|
35
|
+
return runGitSync(["for-each-ref", ...args], options);
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* 暂存操作
|
|
39
|
+
* @defaults `git add <args>`
|
|
40
|
+
*/
|
|
41
|
+
const gitAdd = async (args, options) => {
|
|
42
|
+
return runGit(["add", ...args], options);
|
|
43
|
+
};
|
|
44
|
+
/** {@link gitAdd} 的同步版本 */
|
|
45
|
+
const gitAddSync = (args, options) => {
|
|
46
|
+
return runGitSync(["add", ...args], options);
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* 分支操作
|
|
50
|
+
* @defaults `git branch <args>`
|
|
51
|
+
*/
|
|
52
|
+
const gitBranch = async (args, options) => {
|
|
53
|
+
return runGit(["branch", ...args], options);
|
|
54
|
+
};
|
|
55
|
+
/** {@link gitBranch} 的同步版本 */
|
|
56
|
+
const gitBranchSync = (args, options) => {
|
|
57
|
+
return runGitSync(["branch", ...args], options);
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* 迁出操作
|
|
61
|
+
* @defaults `git checkout <args>`
|
|
62
|
+
*/
|
|
63
|
+
const gitCheckout = async (args, options) => {
|
|
64
|
+
return runGit(["checkout", ...args], options);
|
|
65
|
+
};
|
|
66
|
+
/** {@link gitCheckout} 的同步版本 */
|
|
67
|
+
const gitCheckoutSync = (args, options) => {
|
|
68
|
+
return runGitSync(["checkout", ...args], options);
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* 提交操作
|
|
72
|
+
* @defaults `git commit <args>`
|
|
73
|
+
*/
|
|
74
|
+
const gitCommit = async (args = [], options) => {
|
|
75
|
+
return runGit(["commit", ...args], options);
|
|
76
|
+
};
|
|
77
|
+
/** {@link gitCommit} 的同步版本 */
|
|
78
|
+
const gitCommitSync = (args = [], options) => {
|
|
79
|
+
return runGitSync(["commit", ...args], options);
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* 配置操作
|
|
83
|
+
* @defaults `git config <args>`
|
|
84
|
+
*/
|
|
85
|
+
const gitConfig = async (args, options) => {
|
|
86
|
+
return runGit(["config", ...args], options);
|
|
87
|
+
};
|
|
88
|
+
/** {@link gitConfig} 的同步版本 */
|
|
89
|
+
const gitConfigSync = (args, options) => {
|
|
90
|
+
return runGitSync(["config", ...args], options);
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* 符号引用操作
|
|
94
|
+
* @defaults `git symbolic-ref <args>`
|
|
95
|
+
*/
|
|
96
|
+
const gitSymbolicRef = async (args, options) => {
|
|
97
|
+
return runGit(["symbolic-ref", ...args], options);
|
|
98
|
+
};
|
|
99
|
+
/** {@link gitSymbolicRef} 的同步版本 */
|
|
100
|
+
const gitSymbolicRefSync = (args, options) => {
|
|
101
|
+
return runGitSync(["symbolic-ref", ...args], options);
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* 引用更新操作
|
|
105
|
+
* @defaults `git update-ref <args>`
|
|
106
|
+
*/
|
|
107
|
+
const gitUpdateRef = async (args, options) => {
|
|
108
|
+
return runGit(["update-ref", ...args], options);
|
|
109
|
+
};
|
|
110
|
+
/** {@link gitUpdateRef} 的同步版本 */
|
|
111
|
+
const gitUpdateRefSync = (args, options) => {
|
|
112
|
+
return runGitSync(["update-ref", ...args], options);
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* 推送操作
|
|
116
|
+
* @defaults `git push <args>`
|
|
117
|
+
*/
|
|
118
|
+
const gitPush = async (args, options) => {
|
|
119
|
+
return runGit(["push", ...args], options);
|
|
120
|
+
};
|
|
121
|
+
/** {@link gitPush} 的同步版本 */
|
|
122
|
+
const gitPushSync = (args, options) => {
|
|
123
|
+
return runGitSync(["push", ...args], options);
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* 远程操作
|
|
127
|
+
* @defaults `git remote <args>`
|
|
128
|
+
*/
|
|
129
|
+
const gitRemote = async (args, options) => {
|
|
130
|
+
return runGit(["remote", ...args], options);
|
|
131
|
+
};
|
|
132
|
+
/** {@link gitRemote} 的同步版本 */
|
|
133
|
+
const gitRemoteSync = (args, options) => {
|
|
134
|
+
return runGitSync(["remote", ...args], options);
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* tag 操作
|
|
138
|
+
* @defaults `git tag <args>`
|
|
139
|
+
*/
|
|
140
|
+
const gitTag = async (args, options) => {
|
|
141
|
+
return runGit(["tag", ...args], options);
|
|
142
|
+
};
|
|
143
|
+
/** {@link gitTag} 的同步版本 */
|
|
144
|
+
const gitTagSync = (args, options) => {
|
|
145
|
+
return runGitSync(["tag", ...args], options);
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* 重置操作
|
|
149
|
+
* @defaults `git reset <args>`
|
|
150
|
+
*/
|
|
151
|
+
const gitReset = async (args, options) => {
|
|
152
|
+
return runGit(["reset", ...args], options);
|
|
153
|
+
};
|
|
154
|
+
/** {@link gitReset} 的同步版本 */
|
|
155
|
+
const gitResetSync = (args, options) => {
|
|
156
|
+
return runGitSync(["reset", ...args], options);
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* 恢复操作
|
|
160
|
+
* @defaults `git restore <args>`
|
|
161
|
+
*/
|
|
162
|
+
const gitRestore = async (args, options) => {
|
|
163
|
+
return runGit(["restore", ...args], options);
|
|
164
|
+
};
|
|
165
|
+
/** {@link gitRestore} 的同步版本 */
|
|
166
|
+
const gitRestoreSync = (args, options) => {
|
|
167
|
+
return runGitSync(["restore", ...args], options);
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* 回退操作
|
|
171
|
+
* @defaults `git revert <args>`
|
|
172
|
+
*/
|
|
173
|
+
const gitRevert = async (args, options) => {
|
|
174
|
+
return runGit(["revert", ...args], options);
|
|
175
|
+
};
|
|
176
|
+
/** {@link gitRevert} 的同步版本 */
|
|
177
|
+
const gitRevertSync = (args, options) => {
|
|
178
|
+
return runGitSync(["revert", ...args], options);
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* 远程引用查询操作
|
|
182
|
+
* @defaults `git ls-remote <args>`
|
|
183
|
+
*/
|
|
184
|
+
const gitLsRemote = async (args, options) => {
|
|
185
|
+
return runGit(["ls-remote", ...args], options);
|
|
186
|
+
};
|
|
187
|
+
/** {@link gitLsRemote} 的同步版本 */
|
|
188
|
+
const gitLsRemoteSync = (args, options) => {
|
|
189
|
+
return runGitSync(["ls-remote", ...args], options);
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* 状态查询操作
|
|
193
|
+
* @defaults `git status <args>`
|
|
194
|
+
*/
|
|
195
|
+
const gitStatus = async (args = [], options) => {
|
|
196
|
+
return runGit(["status", ...args], options);
|
|
197
|
+
};
|
|
198
|
+
/** {@link gitStatus} 的同步版本 */
|
|
199
|
+
const gitStatusSync = (args = [], options) => {
|
|
200
|
+
return runGitSync(["status", ...args], options);
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* 对象查看操作
|
|
204
|
+
* @defaults `git show <args>`
|
|
205
|
+
*/
|
|
206
|
+
const gitShow = async (args, options) => {
|
|
207
|
+
return runGit(["show", ...args], options);
|
|
208
|
+
};
|
|
209
|
+
/** {@link gitShow} 的同步版本 */
|
|
210
|
+
const gitShowSync = (args, options) => {
|
|
211
|
+
return runGitSync(["show", ...args], options);
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* 差异比较操作
|
|
215
|
+
* @defaults `git diff <args>`
|
|
216
|
+
*/
|
|
217
|
+
const gitDiff = async (args = [], options) => {
|
|
218
|
+
return runGit(["diff", ...args], options);
|
|
219
|
+
};
|
|
220
|
+
/** {@link gitDiff} 的同步版本 */
|
|
221
|
+
const gitDiffSync = (args = [], options) => {
|
|
222
|
+
return runGitSync(["diff", ...args], options);
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* 暂存区保存操作
|
|
226
|
+
* @defaults `git stash <args>`
|
|
227
|
+
*/
|
|
228
|
+
const gitStash = async (args = [], options) => {
|
|
229
|
+
return runGit(["stash", ...args], options);
|
|
230
|
+
};
|
|
231
|
+
/** {@link gitStash} 的同步版本 */
|
|
232
|
+
const gitStashSync = (args = [], options) => {
|
|
233
|
+
return runGitSync(["stash", ...args], options);
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* 删除文件操作
|
|
237
|
+
* @defaults `git rm <args>`
|
|
238
|
+
*/
|
|
239
|
+
const gitRm = async (args, options) => {
|
|
240
|
+
return runGit(["rm", ...args], options);
|
|
241
|
+
};
|
|
242
|
+
/** {@link gitRm} 的同步版本 */
|
|
243
|
+
const gitRmSync = (args, options) => {
|
|
244
|
+
return runGitSync(["rm", ...args], options);
|
|
245
|
+
};
|
|
246
|
+
/**
|
|
247
|
+
* 移动文件操作
|
|
248
|
+
* @defaults `git mv <args>`
|
|
249
|
+
*/
|
|
250
|
+
const gitMv = async (args, options) => {
|
|
251
|
+
return runGit(["mv", ...args], options);
|
|
252
|
+
};
|
|
253
|
+
/** {@link gitMv} 的同步版本 */
|
|
254
|
+
const gitMvSync = (args, options) => {
|
|
255
|
+
return runGitSync(["mv", ...args], options);
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* 对象内容操作
|
|
259
|
+
* @defaults `git cat-file <args>`
|
|
260
|
+
*/
|
|
261
|
+
const gitCatFile = async (args, options) => {
|
|
262
|
+
return runGit(["cat-file", ...args], options);
|
|
263
|
+
};
|
|
264
|
+
/** {@link gitCatFile} 的同步版本 */
|
|
265
|
+
const gitCatFileSync = (args, options) => {
|
|
266
|
+
return runGitSync(["cat-file", ...args], options);
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* 日志查询操作
|
|
270
|
+
* @defaults `git log <args>`
|
|
271
|
+
*/
|
|
272
|
+
const gitLog = async (args, options) => {
|
|
273
|
+
return runGit(["log", ...args], options);
|
|
274
|
+
};
|
|
275
|
+
/** {@link gitLog} 的同步版本 */
|
|
276
|
+
const gitLogSync = (args, options) => {
|
|
277
|
+
return runGitSync(["log", ...args], options);
|
|
278
|
+
};
|
|
279
|
+
/**
|
|
280
|
+
* 仓库初始化操作
|
|
281
|
+
* @defaults `git init <args>`
|
|
282
|
+
*/
|
|
283
|
+
const gitInit = async (args, options) => {
|
|
284
|
+
return runGit(["init", ...args], options);
|
|
285
|
+
};
|
|
286
|
+
/** {@link gitInit} 的同步版本 */
|
|
287
|
+
const gitInitSync = (args, options) => {
|
|
288
|
+
return runGitSync(["init", ...args], options);
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* 获取远程更新操作
|
|
292
|
+
* @defaults `git fetch <args>`
|
|
293
|
+
*/
|
|
294
|
+
const gitFetch = async (args, options) => {
|
|
295
|
+
return runGit(["fetch", ...args], options);
|
|
296
|
+
};
|
|
297
|
+
/** {@link gitFetch} 的同步版本 */
|
|
298
|
+
const gitFetchSync = (args, options) => {
|
|
299
|
+
return runGitSync(["fetch", ...args], options);
|
|
300
|
+
};
|
|
301
|
+
// src/add.ts
|
|
302
|
+
/**
|
|
303
|
+
* 暂存所有文件
|
|
304
|
+
* @defaults `git add --all`
|
|
305
|
+
*/
|
|
306
|
+
const gitAddAll = async (args = []) => {
|
|
307
|
+
await gitAdd(["--all", ...args]);
|
|
308
|
+
};
|
|
309
|
+
/**
|
|
310
|
+
* 暂存已追踪文件
|
|
311
|
+
* @defaults `git add --update`
|
|
312
|
+
*/
|
|
313
|
+
const gitAddTracked = async (args = []) => {
|
|
314
|
+
await gitAdd(["--update", ...args]);
|
|
315
|
+
};
|
|
316
|
+
// src/branch.ts
|
|
317
|
+
/**
|
|
318
|
+
* 获取当前分支
|
|
319
|
+
* @defaults `git branch --show-current`
|
|
320
|
+
* */
|
|
321
|
+
const gitBranchCurrent = async () => {
|
|
322
|
+
const branch = await gitBranch(["--show-current"]);
|
|
323
|
+
if (!branch) throw new Error("Detached HEAD");
|
|
324
|
+
return branch;
|
|
325
|
+
};
|
|
326
|
+
/**
|
|
327
|
+
* 重命名本地分支
|
|
328
|
+
* @defaults `git branch -m <old-name> <new-name>`
|
|
329
|
+
*/
|
|
330
|
+
const gitBranchMove = async (oldName, newName) => {
|
|
331
|
+
await gitBranch([
|
|
332
|
+
"-m",
|
|
333
|
+
oldName,
|
|
334
|
+
newName
|
|
335
|
+
]);
|
|
336
|
+
};
|
|
337
|
+
/**
|
|
338
|
+
* 重命名本地分支(强制)
|
|
339
|
+
* @defaults `git branch -M <old-name> <new-name>`
|
|
340
|
+
*/
|
|
341
|
+
const gitBranchMoveForce = async (oldName, newName) => {
|
|
342
|
+
await gitBranch([
|
|
343
|
+
"-M",
|
|
344
|
+
oldName,
|
|
345
|
+
newName
|
|
346
|
+
]);
|
|
347
|
+
};
|
|
348
|
+
/**
|
|
349
|
+
* 删除本地分支
|
|
350
|
+
* @defaults `git branch -d <branch>`
|
|
351
|
+
*/
|
|
352
|
+
const gitBranchDelete = async (branch) => {
|
|
353
|
+
await gitBranch(["-d", branch]);
|
|
354
|
+
};
|
|
355
|
+
/**
|
|
356
|
+
* 删除本地分支(强制)
|
|
357
|
+
* @defaults `git branch -D <branch>`
|
|
358
|
+
*/
|
|
359
|
+
const gitBranchDeleteForce = async (branch) => {
|
|
360
|
+
await gitBranch(["-D", branch]);
|
|
361
|
+
};
|
|
362
|
+
/** 获取所有分支 */
|
|
363
|
+
const getLocalBranches = async () => {
|
|
364
|
+
const res = await gitBranch(["--format", "%(refname:short)"]);
|
|
365
|
+
return splitNonEmptyLines(res);
|
|
366
|
+
};
|
|
367
|
+
const getRemoteBranches = async () => {
|
|
368
|
+
const res = await gitBranch([
|
|
369
|
+
"--remotes",
|
|
370
|
+
"--format",
|
|
371
|
+
"%(refname:short)"
|
|
372
|
+
]);
|
|
373
|
+
return splitNonEmptyLines(res);
|
|
374
|
+
};
|
|
375
|
+
// src/checkout.ts
|
|
376
|
+
/**
|
|
377
|
+
* 创建并签出
|
|
378
|
+
* @defaults `git checkout -b <branch> <start-point>`
|
|
379
|
+
* @fallback `git checkout <branch>`
|
|
380
|
+
* @see
|
|
381
|
+
* ```
|
|
382
|
+
* git branch <branch> <start-point>
|
|
383
|
+
* git checkout <branch>
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
const gitCheckoutBranch = async (branch, startpoint) => {
|
|
387
|
+
await gitCheckout([
|
|
388
|
+
"-b",
|
|
389
|
+
branch,
|
|
390
|
+
...startpoint ? [startpoint] : []
|
|
391
|
+
], { error: "throw" }).catch((_) => gitCheckout([branch], { error: "throw" }));
|
|
392
|
+
};
|
|
393
|
+
/**
|
|
394
|
+
* 强制创建/重置并签出
|
|
395
|
+
* @defaults `git checkout -B <branch> <start-point>`
|
|
396
|
+
* @see
|
|
397
|
+
* ```
|
|
398
|
+
* git checkout <start-point>
|
|
399
|
+
* git branch -f <branch> HEAD
|
|
400
|
+
* git checkout <branch>
|
|
401
|
+
* ```
|
|
402
|
+
*/
|
|
403
|
+
const gitCheckoutBranchForce = async (branch, startpoint) => {
|
|
404
|
+
await gitCheckout([
|
|
405
|
+
"-B",
|
|
406
|
+
branch,
|
|
407
|
+
...startpoint ? [startpoint] : []
|
|
408
|
+
]);
|
|
409
|
+
};
|
|
410
|
+
/**
|
|
411
|
+
* 创建孤儿分支
|
|
412
|
+
* @defaults `git checkout --orphan <branch>`
|
|
413
|
+
*/
|
|
414
|
+
const gitCheckoutBranchOrphan = async (branch, startpoint) => {
|
|
415
|
+
await gitCheckout([
|
|
416
|
+
"--orphan",
|
|
417
|
+
branch,
|
|
418
|
+
...startpoint ? [startpoint] : []
|
|
419
|
+
]);
|
|
420
|
+
};
|
|
421
|
+
// src/commit.ts
|
|
422
|
+
/**
|
|
423
|
+
* 提交
|
|
424
|
+
* @defaults `git commit --message message <args>`
|
|
425
|
+
*/
|
|
426
|
+
const gitCommitMessage = async (message, args = []) => {
|
|
427
|
+
await gitCommit([
|
|
428
|
+
"--message",
|
|
429
|
+
message,
|
|
430
|
+
...args
|
|
431
|
+
]);
|
|
432
|
+
};
|
|
433
|
+
/**
|
|
434
|
+
* 空内容提交
|
|
435
|
+
* @defaults `git commit --message message --allow-empty <args>`
|
|
436
|
+
*/
|
|
437
|
+
const gitCommitAllowEmpty = async (message, args = []) => {
|
|
438
|
+
await gitCommitMessage(message, ["--allow-empty", ...args]);
|
|
439
|
+
};
|
|
440
|
+
/**
|
|
441
|
+
* 修正提交
|
|
442
|
+
* @defaults `git commit --message message --amend <args>`
|
|
443
|
+
*/
|
|
444
|
+
const gitCommitAmend = async (message, args = []) => {
|
|
445
|
+
await gitCommitMessage(message, ["--amend", ...args]);
|
|
446
|
+
};
|
|
447
|
+
/**
|
|
448
|
+
* 修正提交 保留信息
|
|
449
|
+
* @defaults `git commit --amend --no-edit <args>`
|
|
450
|
+
*/
|
|
451
|
+
const gitCommitAmendNoEdit = async (args = []) => {
|
|
452
|
+
await gitCommit([
|
|
453
|
+
"--amend",
|
|
454
|
+
"--no-edit",
|
|
455
|
+
...args
|
|
456
|
+
]);
|
|
457
|
+
};
|
|
458
|
+
// src/config.ts
|
|
459
|
+
/**
|
|
460
|
+
* 获取指定的 git 配置
|
|
461
|
+
* @defaults `git config [--global] --get <key>`
|
|
462
|
+
*/
|
|
463
|
+
const gitConfigGet = async (key, global = false) => {
|
|
464
|
+
return gitConfig([
|
|
465
|
+
...global ? ["--global"] : [],
|
|
466
|
+
"--get",
|
|
467
|
+
key
|
|
468
|
+
]);
|
|
469
|
+
};
|
|
470
|
+
/**
|
|
471
|
+
* 指定的 git 配置
|
|
472
|
+
* @defaults `git config [--global] <key> <value>`
|
|
473
|
+
*/
|
|
474
|
+
const gitConfigSet = async (key, value, global = false) => {
|
|
475
|
+
await gitConfig([
|
|
476
|
+
...global ? ["--global"] : [],
|
|
477
|
+
key,
|
|
478
|
+
value
|
|
479
|
+
]);
|
|
480
|
+
};
|
|
481
|
+
/**
|
|
482
|
+
* 移除指定配置
|
|
483
|
+
* @defaults `git config [--global] --unset <key>`
|
|
484
|
+
*/
|
|
485
|
+
const gitConfigUnset = async (key, global = false) => {
|
|
486
|
+
await gitConfig([
|
|
487
|
+
...global ? ["--global"] : [],
|
|
488
|
+
"--unset",
|
|
489
|
+
key
|
|
490
|
+
]);
|
|
491
|
+
};
|
|
492
|
+
/**
|
|
493
|
+
* 配置列表
|
|
494
|
+
* @defaults `git config [--global] --list`
|
|
495
|
+
*/
|
|
496
|
+
const gitConfigList = async (global = false) => {
|
|
497
|
+
return gitConfig([...global ? ["--global"] : [], "--list"]);
|
|
498
|
+
};
|
|
499
|
+
// src/tag.ts
|
|
500
|
+
/**
|
|
501
|
+
* 创建 lightweight tag
|
|
502
|
+
* @defaults `git tag <tag> <args>`
|
|
503
|
+
*/
|
|
504
|
+
const gitTagLightweight = async (tag, args = []) => {
|
|
505
|
+
await gitTag([tag, ...args]);
|
|
506
|
+
};
|
|
507
|
+
/**
|
|
508
|
+
* 创建 annotated tag
|
|
509
|
+
* @defaults `git tag --annotate --message <msg> <tag> <args>`
|
|
510
|
+
*/
|
|
511
|
+
const gitTagAnnotated = async (tag, message = tag, args = []) => {
|
|
512
|
+
await gitTag([
|
|
513
|
+
"--annotate",
|
|
514
|
+
"--message",
|
|
515
|
+
message,
|
|
516
|
+
tag,
|
|
517
|
+
...args
|
|
518
|
+
]);
|
|
519
|
+
};
|
|
520
|
+
/**
|
|
521
|
+
* 创建 signed tag
|
|
522
|
+
* @defaults `git tag --sign --message <msg> <tag> <args>`
|
|
523
|
+
*/
|
|
524
|
+
const gitTagSigned = async (tag, message = tag, args = []) => {
|
|
525
|
+
await gitTag([
|
|
526
|
+
"--sign",
|
|
527
|
+
"--message",
|
|
528
|
+
message,
|
|
529
|
+
tag,
|
|
530
|
+
...args
|
|
531
|
+
]);
|
|
532
|
+
};
|
|
533
|
+
/**
|
|
534
|
+
* 删除本地 tag
|
|
535
|
+
* @defaults `git tag --delete <tag> <args>`
|
|
536
|
+
*/
|
|
537
|
+
const gitTagDelete = async (tag, args = []) => {
|
|
538
|
+
await gitTag([
|
|
539
|
+
"--delete",
|
|
540
|
+
tag,
|
|
541
|
+
...args
|
|
542
|
+
]);
|
|
543
|
+
};
|
|
544
|
+
/** {@link gitTagDelete} 的同步版本 */
|
|
545
|
+
const gitTagDeleteSync = (tag, args = []) => {
|
|
546
|
+
gitTagSync([
|
|
547
|
+
"--delete",
|
|
548
|
+
tag,
|
|
549
|
+
...args
|
|
550
|
+
]);
|
|
551
|
+
};
|
|
552
|
+
let GitSorter = /* @__PURE__ */ function(GitSorter) {
|
|
553
|
+
GitSorter["NAME_ASC"] = "v:refname";
|
|
554
|
+
GitSorter["NAME_DESC"] = "-v:refname";
|
|
555
|
+
GitSorter["DATE_ASC"] = "creatordate";
|
|
556
|
+
GitSorter["DATE_DESC"] = "-creatordate";
|
|
557
|
+
return GitSorter;
|
|
558
|
+
}({});
|
|
559
|
+
/**
|
|
560
|
+
* 获取本地所有 tag
|
|
561
|
+
* @defaults `git for-each-ref refs/tags/<match> --format "%(refname:short)" --sort <sort>
|
|
562
|
+
* --exclude refs/tags/<match> --count <count>`
|
|
563
|
+
*/
|
|
564
|
+
const getLocalTags = async (match = "*", exclude = "*-beta.*", sort = "-v:refname", count = 0) => {
|
|
565
|
+
const excludes = [exclude].flat().flatMap((k) => [`--exclude`, `refs/tags/${k}`]);
|
|
566
|
+
const res = await gitForEachRef([
|
|
567
|
+
`refs/tags/${match}`,
|
|
568
|
+
...excludes,
|
|
569
|
+
"--format",
|
|
570
|
+
"%(refname:short)",
|
|
571
|
+
`--sort`,
|
|
572
|
+
`${sort}`,
|
|
573
|
+
`--count`,
|
|
574
|
+
`${count}`
|
|
575
|
+
]);
|
|
576
|
+
return res ? splitNonEmptyLines(res) : [];
|
|
577
|
+
};
|
|
578
|
+
/**
|
|
579
|
+
* 获取远程所有 tag
|
|
580
|
+
* @defaults `git ls-remote --tags --refs --sort <sort> <remote>`
|
|
581
|
+
*/
|
|
582
|
+
const getRemoteTags = async (remote = "origin", sort = "-v:refname") => {
|
|
583
|
+
const res = await gitLsRemote([
|
|
584
|
+
"--tags",
|
|
585
|
+
"--refs",
|
|
586
|
+
"--sort",
|
|
587
|
+
sort,
|
|
588
|
+
remote
|
|
589
|
+
]);
|
|
590
|
+
return res ? splitNonEmptyLines(res).map((t) => t.split("refs/tags/")[1]) : [];
|
|
591
|
+
};
|
|
592
|
+
/** 获取最新 tag */
|
|
593
|
+
const getLatestTag = async (match = "*", exclude = "*-beta.*") => {
|
|
594
|
+
const [latestTag] = await getLocalTags(match, exclude, "-v:refname", 1);
|
|
595
|
+
return latestTag;
|
|
596
|
+
};
|
|
597
|
+
/** 获取上一个 tag */
|
|
598
|
+
const getPreviousTag = async (tag, match = "*", exclude = "*-beta.*") => {
|
|
599
|
+
const all = await getLocalTags(match, exclude, "-v:refname", 0);
|
|
600
|
+
return all[all.findIndex((k) => tag === k) + 1];
|
|
601
|
+
};
|
|
602
|
+
// src/misc.ts
|
|
603
|
+
/** 初始化裸仓库,模拟远程仓库 */
|
|
604
|
+
const initBareRepo = async (dir) => {
|
|
605
|
+
await gitInit(["--bare", dir]);
|
|
606
|
+
};
|
|
607
|
+
/** 初始化仓库 */
|
|
608
|
+
const initRepo = async (branch = "master") => {
|
|
609
|
+
await gitInit(["--initial-branch", branch]);
|
|
610
|
+
};
|
|
611
|
+
/** 判断指定目录是否是 git 仓库 */
|
|
612
|
+
const isGitRepo = async (dir = ".") => {
|
|
613
|
+
return runGit([
|
|
614
|
+
"-C",
|
|
615
|
+
dir,
|
|
616
|
+
"status"
|
|
617
|
+
], {
|
|
618
|
+
error: "throw",
|
|
619
|
+
trimEnd: true
|
|
620
|
+
}).then((_) => true).catch((_) => false);
|
|
621
|
+
};
|
|
622
|
+
/** 判断指定目录是否是 git 裸仓库 */
|
|
623
|
+
const isGitBareRepo = async (dir = ".") => {
|
|
624
|
+
return gitRevParse(["--is-bare-repository"], {
|
|
625
|
+
trimEnd: true,
|
|
626
|
+
cwd: dir
|
|
627
|
+
}).then((res) => "true" === res);
|
|
628
|
+
};
|
|
629
|
+
/** 判断工作区是否干净 */
|
|
630
|
+
const isWorkingTreeClean = async () => {
|
|
631
|
+
const status = await gitStatus(["--porcelain"]);
|
|
632
|
+
return !Boolean(status);
|
|
633
|
+
};
|
|
634
|
+
/**
|
|
635
|
+
* 判断当前分支是否已设置 upstream
|
|
636
|
+
* @defaults `git rev-parse --abbrev-ref --symbolic-full-name "<branch>@{u}"`
|
|
637
|
+
*/
|
|
638
|
+
const hasUpstream = async (branch) => {
|
|
639
|
+
const ref = branch ? `${branch}@{u}` : "@{u}";
|
|
640
|
+
const upstream = await gitRevParse([
|
|
641
|
+
"--abbrev-ref",
|
|
642
|
+
"--symbolic-full-name",
|
|
643
|
+
ref
|
|
644
|
+
]);
|
|
645
|
+
return Boolean(upstream);
|
|
646
|
+
};
|
|
647
|
+
/**
|
|
648
|
+
* 获取完整 hash
|
|
649
|
+
* @defaults `git rev-parse <rev>`
|
|
650
|
+
*/
|
|
651
|
+
const getFullHash = async (rev) => {
|
|
652
|
+
return gitRevParse([rev]);
|
|
653
|
+
};
|
|
654
|
+
/**
|
|
655
|
+
* 获取短 hash
|
|
656
|
+
* @defaults `git rev-parse --short <rev>`
|
|
657
|
+
*/
|
|
658
|
+
const getShortHash = async (rev) => {
|
|
659
|
+
return gitRevParse(["--short", rev]);
|
|
660
|
+
};
|
|
661
|
+
/**
|
|
662
|
+
* 提取所有分支
|
|
663
|
+
* @defaults `git fetch --all --prune`
|
|
664
|
+
*/
|
|
665
|
+
const fetchAllPrune = async () => {
|
|
666
|
+
await gitFetch(["--all", "--prune"]);
|
|
667
|
+
};
|
|
668
|
+
/**
|
|
669
|
+
* 获取当前工作区状态
|
|
670
|
+
* @defaults `git status --short --untracked-files=no`
|
|
671
|
+
*/
|
|
672
|
+
const getShortStatus = async () => {
|
|
673
|
+
return gitStatus(["--short"]);
|
|
674
|
+
};
|
|
675
|
+
/**
|
|
676
|
+
* 为 git status / changeset 输出添加颜色
|
|
677
|
+
*
|
|
678
|
+
* M -> 黄色(修改)
|
|
679
|
+
* A -> 绿色(新增)
|
|
680
|
+
* D -> 红色(删除)
|
|
681
|
+
* @param {string} log `git status --short` 输出
|
|
682
|
+
* @returns {string}
|
|
683
|
+
*/
|
|
684
|
+
const coloredStatus = (log) => {
|
|
685
|
+
const colorStatusChar = (ch) => {
|
|
686
|
+
switch (ch) {
|
|
687
|
+
case "M": return `\x1b[33m${ch}\x1b[39m`;
|
|
688
|
+
case "A": return `\x1b[32m${ch}\x1b[39m`;
|
|
689
|
+
case "D": return `\x1b[31m${ch}\x1b[39m`;
|
|
690
|
+
case "?": return `\x1b[34m${ch}\x1b[39m`;
|
|
691
|
+
default: return ch;
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
const colorStatus = (status) => status.split("").map(colorStatusChar).join("");
|
|
695
|
+
return splitNonEmptyLines(log).map((line) => {
|
|
696
|
+
const status = line.slice(0, 2);
|
|
697
|
+
const file = line.slice(3);
|
|
698
|
+
return `${colorStatus(status)} \x1b[2m${file}\x1b[22m`;
|
|
699
|
+
}).join("\n");
|
|
700
|
+
};
|
|
701
|
+
/**
|
|
702
|
+
* 计算 changelog 的 commit 范围
|
|
703
|
+
* @param {boolean} isIncrement 是否为版本递增发布
|
|
704
|
+
* @param {string} match tag match
|
|
705
|
+
* @param {string} exclude tag exclude
|
|
706
|
+
* @returns {Promise<{from: string, to: string}>}
|
|
707
|
+
*/
|
|
708
|
+
const resolveChangelogRange = async (isIncrement = true, match = "*", exclude = "*-beta.*") => {
|
|
709
|
+
const latestTag = await getLatestTag(match, exclude);
|
|
710
|
+
if (!latestTag) return {
|
|
711
|
+
from: "",
|
|
712
|
+
to: "HEAD"
|
|
713
|
+
};
|
|
714
|
+
const previousTag = await getPreviousTag(latestTag);
|
|
715
|
+
if (!isIncrement && previousTag) return {
|
|
716
|
+
from: previousTag,
|
|
717
|
+
to: `${latestTag}^1`
|
|
718
|
+
};
|
|
719
|
+
return {
|
|
720
|
+
from: latestTag,
|
|
721
|
+
to: "HEAD"
|
|
722
|
+
};
|
|
723
|
+
};
|
|
724
|
+
/**
|
|
725
|
+
* 获取指定范围内的 commit 日志
|
|
726
|
+
* @defaults `git log --pretty=format:"* %s (%h)" <from>...<to> -- <scope>`
|
|
727
|
+
*/
|
|
728
|
+
const getLogSince = async (from = "", to = "HEAD", scope) => {
|
|
729
|
+
const cmd = ["--pretty=format:* %s (%h)"];
|
|
730
|
+
if (from) cmd.push(`${from}...${to}`);
|
|
731
|
+
if (scope) cmd.push(...["--", scope]);
|
|
732
|
+
return gitLog(cmd);
|
|
733
|
+
};
|
|
734
|
+
/** 判断字符串是否为合法 remote 名称 */
|
|
735
|
+
const isRemoteName = async (remote) => {
|
|
736
|
+
const res = await gitRevParse(["--verify", `refs/remotes/${remote}`]);
|
|
737
|
+
return Boolean(res);
|
|
738
|
+
};
|
|
739
|
+
/** 统计自 tag 以来的提交数量 */
|
|
740
|
+
const countCommitsSince = async (tag) => {
|
|
741
|
+
const ref = tag ? `${tag}...HEAD` : "HEAD";
|
|
742
|
+
return gitRevList([ref, "--count"], { error: "throw" }).then(Number).catch((_) => 0);
|
|
743
|
+
};
|
|
744
|
+
/**
|
|
745
|
+
* 将 HEAD 指向指定分支(不会切换工作区)
|
|
746
|
+
*/
|
|
747
|
+
const gitSetHeadBranch = async (branch) => {
|
|
748
|
+
await gitSymbolicRef(["HEAD", `refs/heads/${branch}`]);
|
|
749
|
+
};
|
|
750
|
+
/** 删除当前分支引用 */
|
|
751
|
+
const gitDeleteHeadRef = async () => {
|
|
752
|
+
await gitUpdateRef(["-d", "HEAD"]);
|
|
753
|
+
};
|
|
754
|
+
/**
|
|
755
|
+
* 创建一个没有任何提交的分支状态。
|
|
756
|
+
*/
|
|
757
|
+
const createUnbornBranch = async (branch) => {
|
|
758
|
+
await gitSymbolicRef(["HEAD", `refs/heads/${branch}`]);
|
|
759
|
+
await gitUpdateRef(["-d", "HEAD"]);
|
|
760
|
+
};
|
|
761
|
+
// src/push.ts
|
|
762
|
+
/**
|
|
763
|
+
* 推送 tag 到远程
|
|
764
|
+
* @defaults `git push <remote> refs/tags/<tag> <args>`
|
|
765
|
+
*/
|
|
766
|
+
const gitPushTag = async (remote, tag, args = []) => {
|
|
767
|
+
await gitPush([
|
|
768
|
+
remote,
|
|
769
|
+
`refs/tags/${tag}`,
|
|
770
|
+
...args
|
|
771
|
+
]);
|
|
772
|
+
};
|
|
773
|
+
/**
|
|
774
|
+
* 推送分支到远程
|
|
775
|
+
* 自动处理 upstream
|
|
776
|
+
* @defaults `git push <remote> <branch> [--set-upstream] <args>`
|
|
777
|
+
*/
|
|
778
|
+
const gitPushBranch = async (remote, branch, args = []) => {
|
|
779
|
+
const u = !await hasUpstream() ? ["--set-upstream"] : [];
|
|
780
|
+
await gitPush([
|
|
781
|
+
remote,
|
|
782
|
+
branch,
|
|
783
|
+
...u,
|
|
784
|
+
...args
|
|
785
|
+
]);
|
|
786
|
+
};
|
|
787
|
+
/**
|
|
788
|
+
* 删除远程引用(分支、tag)
|
|
789
|
+
* @defaults `git push <remote> <ref> --delete <args>`
|
|
790
|
+
*/
|
|
791
|
+
const gitPushDeleteRef = async (remote, ref, args = []) => {
|
|
792
|
+
await gitPush([
|
|
793
|
+
remote,
|
|
794
|
+
ref,
|
|
795
|
+
"--delete",
|
|
796
|
+
...args
|
|
797
|
+
]);
|
|
798
|
+
};
|
|
799
|
+
// src/remote.ts
|
|
800
|
+
/**
|
|
801
|
+
* 获取 url
|
|
802
|
+
* @defaults `git remote get-url <args>`
|
|
803
|
+
*/
|
|
804
|
+
const gitRemoteGetUrl = async (args) => {
|
|
805
|
+
return gitRemote(["get-url", ...args]);
|
|
806
|
+
};
|
|
807
|
+
/**
|
|
808
|
+
* 设置 url
|
|
809
|
+
* @defaults `git remote set-url <args>`
|
|
810
|
+
*/
|
|
811
|
+
const gitRemoteSetUrl = async (args) => {
|
|
812
|
+
await gitRemote(["set-url", ...args]);
|
|
813
|
+
};
|
|
814
|
+
/**
|
|
815
|
+
* 添加远程
|
|
816
|
+
* @defaults `git remote add <name> <url>`
|
|
817
|
+
*/
|
|
818
|
+
const gitRemoteAdd = async (name, url) => {
|
|
819
|
+
await gitRemote([
|
|
820
|
+
"add",
|
|
821
|
+
name,
|
|
822
|
+
url
|
|
823
|
+
]);
|
|
824
|
+
};
|
|
825
|
+
/**
|
|
826
|
+
* 重命名远程
|
|
827
|
+
* @defaults `git remote rename <old-name> <new-name>`
|
|
828
|
+
*/
|
|
829
|
+
const gitRemoteRename = async (oldName, newName) => {
|
|
830
|
+
await gitRemote([
|
|
831
|
+
"rename",
|
|
832
|
+
oldName,
|
|
833
|
+
newName
|
|
834
|
+
]);
|
|
835
|
+
};
|
|
836
|
+
/**
|
|
837
|
+
* 移除远程
|
|
838
|
+
* @defaults `git remote remove <name>`
|
|
839
|
+
*/
|
|
840
|
+
const gitRemoteRemove = async (name) => {
|
|
841
|
+
await gitRemote(["remove", name]);
|
|
842
|
+
};
|
|
843
|
+
/** 获取关联的所有远程 */
|
|
844
|
+
const getRemoteNames = async () => {
|
|
845
|
+
const res = await gitRemote([]);
|
|
846
|
+
return res ? splitNonEmptyLines(res) : [];
|
|
847
|
+
};
|
|
848
|
+
/** 获取远程地址 */
|
|
849
|
+
const getRemoteUrl = async (remote) => {
|
|
850
|
+
return gitRemoteGetUrl([remote]);
|
|
851
|
+
};
|
|
852
|
+
/** 获取所有远程 */
|
|
853
|
+
const getRemoteList = async () => {
|
|
854
|
+
const remotes = await getRemoteNames();
|
|
855
|
+
return Promise.all(remotes.map(async (name) => {
|
|
856
|
+
return {
|
|
857
|
+
name,
|
|
858
|
+
url: await gitRemoteGetUrl([name])
|
|
859
|
+
};
|
|
860
|
+
}));
|
|
861
|
+
};
|
|
862
|
+
// src/undo.revert.ts
|
|
863
|
+
/**
|
|
864
|
+
* 安全撤销一个已提交(并可能已 push)的 commit
|
|
865
|
+
* @defaults `git revert <commit>`
|
|
866
|
+
*/
|
|
867
|
+
const gitRevertCommit = async (hash) => {
|
|
868
|
+
await gitRevert([hash]);
|
|
869
|
+
};
|
|
870
|
+
// src/undo.reset.ts
|
|
871
|
+
const headArg = (count = 1) => {
|
|
872
|
+
if (count < 0) throw new RangeError("count must be >= 0");
|
|
873
|
+
return count === 0 ? "HEAD" : `HEAD~${count}`;
|
|
874
|
+
};
|
|
875
|
+
/**
|
|
876
|
+
* 撤销最近的 commit(移动 HEAD + 取消暂存)
|
|
877
|
+
* - 暂存区被重置到目标 commit
|
|
878
|
+
* - 未提交修改保持不变
|
|
879
|
+
* @defaults `git reset --mixed HEAD~<count>`
|
|
880
|
+
*/
|
|
881
|
+
const gitResetMixed = async (count = 1) => {
|
|
882
|
+
await gitReset(["--mixed", headArg(count)]);
|
|
883
|
+
};
|
|
884
|
+
/**
|
|
885
|
+
* 撤销最近的 commit(完全重置)
|
|
886
|
+
* - 暂存区 + 工作区都会被重置到目标 commit
|
|
887
|
+
* - 未提交修改会丢失
|
|
888
|
+
* @defaults `git reset --hard HEAD~<count>`
|
|
889
|
+
*/
|
|
890
|
+
const gitResetHard = async (count = 1) => {
|
|
891
|
+
await gitReset(["--hard", headArg(count)]);
|
|
892
|
+
};
|
|
893
|
+
/**
|
|
894
|
+
* 撤销最近的 commit(仅移动 HEAD)
|
|
895
|
+
* - 暂存区保持不变
|
|
896
|
+
* - 未提交修改保持不变
|
|
897
|
+
* @defaults `git reset --soft HEAD~<count>`
|
|
898
|
+
*/
|
|
899
|
+
const gitResetSoft = async (count = 1) => {
|
|
900
|
+
await gitReset(["--soft", headArg(count)]);
|
|
901
|
+
};
|
|
902
|
+
/**
|
|
903
|
+
* 撤销最近的 commit(安全重置)
|
|
904
|
+
* - 行为类似 --hard
|
|
905
|
+
* - 若会覆盖工作区未提交修改则直接失败
|
|
906
|
+
* @defaults `git reset --keep HEAD~<count>`
|
|
907
|
+
*/
|
|
908
|
+
const gitResetKeep = async (count = 1) => {
|
|
909
|
+
await gitReset(["--keep", headArg(count)]);
|
|
910
|
+
};
|
|
911
|
+
/** {@link gitResetMixed} 的同步版本 */
|
|
912
|
+
const gitResetMixedSync = (count = 1) => {
|
|
913
|
+
gitResetSync(["--mixed", headArg(count)]);
|
|
914
|
+
};
|
|
915
|
+
/** {@link gitResetHard} 的同步版本 */
|
|
916
|
+
const gitResetHardSync = (count = 1) => {
|
|
917
|
+
gitResetSync(["--hard", headArg(count)]);
|
|
918
|
+
};
|
|
919
|
+
/** {@link gitResetSoft} 的同步版本 */
|
|
920
|
+
const gitResetSoftSync = (count = 1) => {
|
|
921
|
+
gitResetSync(["--soft", headArg(count)]);
|
|
922
|
+
};
|
|
923
|
+
/** {@link gitResetKeep} 的同步版本 */
|
|
924
|
+
const gitResetKeepSync = (count = 1) => {
|
|
925
|
+
gitResetSync(["--keep", headArg(count)]);
|
|
926
|
+
};
|
|
927
|
+
// src/undo.restore.ts
|
|
928
|
+
/**
|
|
929
|
+
* 用【暂存区】覆盖【工作区】单个文件
|
|
930
|
+
* - 用 Index 覆盖 Working Tree
|
|
931
|
+
* - 不影响暂存区(Index)
|
|
932
|
+
* @defaults `git restore --worktree <file>`
|
|
933
|
+
*/
|
|
934
|
+
const gitRestoreFile = async (file) => {
|
|
935
|
+
await gitRestore(["--worktree", file]);
|
|
936
|
+
};
|
|
937
|
+
/**
|
|
938
|
+
* 用【暂存区】覆盖【工作区】所有文件
|
|
939
|
+
* - 用 Index 覆盖 Working Tree
|
|
940
|
+
* - 不影响暂存区(Index)
|
|
941
|
+
* @defaults `git --worktree restore .`
|
|
942
|
+
*/
|
|
943
|
+
const gitRestoreAll = async () => {
|
|
944
|
+
await gitRestore(["--worktree", "."]);
|
|
945
|
+
};
|
|
946
|
+
/**
|
|
947
|
+
* 用 指定 source 中的文件内容覆盖【工作区】文件
|
|
948
|
+
* - 仅修改 Working Tree
|
|
949
|
+
* - 不影响暂存区(Index)
|
|
950
|
+
* - 不会改变提交历史(HEAD)
|
|
951
|
+
* @defaults `git restore --worktree --source <source> <file>`
|
|
952
|
+
* @see source = `git rev-parse <expr>`
|
|
953
|
+
*/
|
|
954
|
+
const gitRestoreWorktreeFileFrom = async (source, file) => {
|
|
955
|
+
await gitRestore([
|
|
956
|
+
"--worktree",
|
|
957
|
+
"--source",
|
|
958
|
+
source,
|
|
959
|
+
file
|
|
960
|
+
]);
|
|
961
|
+
};
|
|
962
|
+
/**
|
|
963
|
+
* 用指定 source 中的文件内容覆盖【工作区】所有文件
|
|
964
|
+
* - 仅修改 Working Tree
|
|
965
|
+
* - 不影响暂存区(Index)
|
|
966
|
+
* - 不会改变提交历史(HEAD)
|
|
967
|
+
* @defaults `git restore --worktree --source <source> .`
|
|
968
|
+
* @see source = `git rev-parse <expr>`
|
|
969
|
+
*/
|
|
970
|
+
const gitRestoreWorktreeAllFrom = async (source) => {
|
|
971
|
+
await gitRestore([
|
|
972
|
+
"--worktree",
|
|
973
|
+
"--source",
|
|
974
|
+
source,
|
|
975
|
+
"."
|
|
976
|
+
]);
|
|
977
|
+
};
|
|
978
|
+
/**
|
|
979
|
+
* 用指定 source 覆盖【暂存区】中的文件
|
|
980
|
+
* - 用 source 覆盖 Index
|
|
981
|
+
* - 不影响工作区(Working Tree)
|
|
982
|
+
* - 不会改变提交历史(HEAD)
|
|
983
|
+
* @defaults `git restore --staged --source <source> <file>`
|
|
984
|
+
* @see source = `git rev-parse <expr>`
|
|
985
|
+
*/
|
|
986
|
+
const gitRestoreIndexFileFrom = async (source, file) => {
|
|
987
|
+
await gitRestore([
|
|
988
|
+
`--staged`,
|
|
989
|
+
"--source",
|
|
990
|
+
source,
|
|
991
|
+
file
|
|
992
|
+
]);
|
|
993
|
+
};
|
|
994
|
+
/**
|
|
995
|
+
* 用指定 source 覆盖整个【暂存区】
|
|
996
|
+
* - 用 source 覆盖 Index
|
|
997
|
+
* - 不影响工作区(Working Tree)
|
|
998
|
+
* - 不会改变提交历史(HEAD)
|
|
999
|
+
* @defaults `git restore --staged --source <source> .`
|
|
1000
|
+
* @see source = `git rev-parse <expr>`
|
|
1001
|
+
*/
|
|
1002
|
+
const gitRestoreIndexAllFrom = async (source) => {
|
|
1003
|
+
await gitRestore([
|
|
1004
|
+
`--staged`,
|
|
1005
|
+
"--source",
|
|
1006
|
+
source,
|
|
1007
|
+
"."
|
|
1008
|
+
]);
|
|
1009
|
+
};
|
|
1010
|
+
/**
|
|
1011
|
+
* 用指定 source 覆盖指定文件(暂存区 + 工作区)
|
|
1012
|
+
* - 将 Index 和 Working Tree 恢复到 source 状态
|
|
1013
|
+
* - 不会移动 HEAD
|
|
1014
|
+
* - 不会改变提交历史
|
|
1015
|
+
* @defaults `git restore --staged --worktree --source <source> <file>`
|
|
1016
|
+
* @see source = `git rev-parse <expr>`
|
|
1017
|
+
*/
|
|
1018
|
+
const gitRestoreFileFrom = async (source, file) => {
|
|
1019
|
+
await gitRestore([
|
|
1020
|
+
"--staged",
|
|
1021
|
+
"--worktree",
|
|
1022
|
+
"--source",
|
|
1023
|
+
source,
|
|
1024
|
+
file
|
|
1025
|
+
]);
|
|
1026
|
+
};
|
|
1027
|
+
/**
|
|
1028
|
+
* 用指定 source 覆盖所有文件(暂存区 + 工作区)
|
|
1029
|
+
* - 将 Index 和 Working Tree 恢复到 source 状态
|
|
1030
|
+
* - 不会移动 HEAD
|
|
1031
|
+
* - 不会改变提交历史
|
|
1032
|
+
* @defaults `git restore --staged --worktree --source <source> .`
|
|
1033
|
+
* @see source = `git rev-parse <expr>`
|
|
1034
|
+
*/
|
|
1035
|
+
const gitRestoreAllFrom = async (source) => {
|
|
1036
|
+
await gitRestore([
|
|
1037
|
+
"--staged",
|
|
1038
|
+
"--worktree",
|
|
1039
|
+
"--source",
|
|
1040
|
+
source,
|
|
1041
|
+
"."
|
|
1042
|
+
]);
|
|
1043
|
+
};
|
|
1044
|
+
// src/undo.unstaged.ts
|
|
1045
|
+
/**
|
|
1046
|
+
* 取消单个文件的暂存状态
|
|
1047
|
+
* - 用 HEAD 覆盖暂存区(Index)
|
|
1048
|
+
* - 不影响工作区(Working Tree)
|
|
1049
|
+
* - 等价于撤销 git add
|
|
1050
|
+
* @defaults `git restore --staged <file>`
|
|
1051
|
+
*/
|
|
1052
|
+
const gitUnstageFile = async (file) => {
|
|
1053
|
+
await gitRestore(["--staged", file]);
|
|
1054
|
+
};
|
|
1055
|
+
/**
|
|
1056
|
+
* 取消所有文件的暂存状态
|
|
1057
|
+
* - 用 HEAD 覆盖暂存区(Index)
|
|
1058
|
+
* - 不影响工作区(Working Tree)
|
|
1059
|
+
* - 等价于撤销所有 git add
|
|
1060
|
+
* @defaults `git restore --staged .`
|
|
1061
|
+
*/
|
|
1062
|
+
const gitUnstageAll = async () => {
|
|
1063
|
+
await gitRestore(["--staged", "."]);
|
|
1064
|
+
};
|
|
1065
|
+
// src/undo.discard.ts
|
|
1066
|
+
/**
|
|
1067
|
+
* 丢弃指定文件的所有修改(暂存区 + 工作区)
|
|
1068
|
+
* - 强制将文件恢复到 HEAD 状态
|
|
1069
|
+
* - 同时重置 Index 和 Working Tree
|
|
1070
|
+
* - 不会改变提交历史(HEAD)
|
|
1071
|
+
* @defaults `git restore --source=HEAD --staged --worktree <file>`
|
|
1072
|
+
*/
|
|
1073
|
+
const gitDiscardFile = async (file) => {
|
|
1074
|
+
await gitRestore([
|
|
1075
|
+
"--staged",
|
|
1076
|
+
"--worktree",
|
|
1077
|
+
"--source=HEAD",
|
|
1078
|
+
file
|
|
1079
|
+
]);
|
|
1080
|
+
};
|
|
1081
|
+
/**
|
|
1082
|
+
* 丢弃所有文件的所有修改(暂存区 + 工作区)
|
|
1083
|
+
* - 强制将整个工作区恢复到 HEAD 状态
|
|
1084
|
+
* - 行为等价于 git reset --hard HEAD(按文件粒度)
|
|
1085
|
+
* - 不会删除未跟踪文件(untracked files)
|
|
1086
|
+
* @defaults `git restore --source=HEAD --staged --worktree .`
|
|
1087
|
+
*/
|
|
1088
|
+
const gitDiscardAll = async () => {
|
|
1089
|
+
await gitRestore([
|
|
1090
|
+
"--staged",
|
|
1091
|
+
"--worktree",
|
|
1092
|
+
"--source=HEAD",
|
|
1093
|
+
"."
|
|
1094
|
+
]);
|
|
1095
|
+
};
|
|
1096
|
+
export { GitSorter, coloredStatus, countCommitsSince, createUnbornBranch, fetchAllPrune, getFullHash, getLatestTag, getLocalBranches, getLocalTags, getLogSince, getPreviousTag, getRemoteBranches, getRemoteList, getRemoteNames, getRemoteTags, getRemoteUrl, getShortHash, getShortStatus, gitAdd, gitAddAll, gitAddSync, gitAddTracked, gitBranch, gitBranchCurrent, gitBranchDelete, gitBranchDeleteForce, gitBranchMove, gitBranchMoveForce, gitBranchSync, gitCatFile, gitCatFileSync, gitCheckout, gitCheckoutBranch, gitCheckoutBranchForce, gitCheckoutBranchOrphan, gitCheckoutSync, gitCommit, gitCommitAllowEmpty, gitCommitAmend, gitCommitAmendNoEdit, gitCommitMessage, gitCommitSync, gitConfig, gitConfigGet, gitConfigList, gitConfigSet, gitConfigSync, gitConfigUnset, gitDeleteHeadRef, gitDiff, gitDiffSync, gitDiscardAll, gitDiscardFile, gitFetch, gitFetchSync, gitForEachRef, gitForEachRefSync, gitInit, gitInitSync, gitLog, gitLogSync, gitLsRemote, gitLsRemoteSync, gitMv, gitMvSync, gitPush, gitPushBranch, gitPushDeleteRef, gitPushSync, gitPushTag, gitRemote, gitRemoteAdd, gitRemoteGetUrl, gitRemoteRemove, gitRemoteRename, gitRemoteSetUrl, gitRemoteSync, gitReset, gitResetHard, gitResetHardSync, gitResetKeep, gitResetKeepSync, gitResetMixed, gitResetMixedSync, gitResetSoft, gitResetSoftSync, gitResetSync, gitRestore, gitRestoreAll, gitRestoreAllFrom, gitRestoreFile, gitRestoreFileFrom, gitRestoreIndexAllFrom, gitRestoreIndexFileFrom, gitRestoreSync, gitRestoreWorktreeAllFrom, gitRestoreWorktreeFileFrom, gitRevList, gitRevListSync, gitRevParse, gitRevParseSync, gitRevert, gitRevertCommit, gitRevertSync, gitRm, gitRmSync, gitSetHeadBranch, gitShow, gitShowSync, gitStash, gitStashSync, gitStatus, gitStatusSync, gitSymbolicRef, gitSymbolicRefSync, gitTag, gitTagAnnotated, gitTagDelete, gitTagDeleteSync, gitTagLightweight, gitTagSigned, gitTagSync, gitUnstageAll, gitUnstageFile, gitUpdateRef, gitUpdateRefSync, hasUpstream, headArg, initBareRepo, initRepo, isGitBareRepo, isGitRepo, isRemoteName, isWorkingTreeClean, resolveChangelogRange };
|