@co0ontty/wand 1.43.8 → 1.45.0
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/build-info.json +3 -3
- package/dist/git-quick-commit.d.ts +9 -0
- package/dist/git-quick-commit.js +209 -26
- package/dist/server-session-routes.js +3 -0
- package/dist/types.d.ts +2 -0
- package/dist/web-ui/content/scripts.js +33 -33
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +3 -3
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
3
|
-
"builtAt": "2026-05-
|
|
4
|
-
"version": "1.
|
|
2
|
+
"commit": "6adb3d09e7da58dbba2f153c9f2248b96b7f5271",
|
|
3
|
+
"builtAt": "2026-05-31T13:48:24.173Z",
|
|
4
|
+
"version": "1.45.0",
|
|
5
5
|
"channel": "stable"
|
|
6
6
|
}
|
|
@@ -10,6 +10,11 @@ interface QuickCommitOptions {
|
|
|
10
10
|
/** When `tag` is empty, ask Claude to generate one based on the diff + commit message. */
|
|
11
11
|
autoTag?: boolean;
|
|
12
12
|
push?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 是否把 commit / tag / push 递归进入各 submodule 内部。默认 false:
|
|
15
|
+
* 只处理父仓库自身(含已变化的 submodule 指针),不碰 submodule 内部 dirty。
|
|
16
|
+
*/
|
|
17
|
+
submodule?: boolean;
|
|
13
18
|
}
|
|
14
19
|
export declare class QuickCommitError extends Error {
|
|
15
20
|
readonly code: QuickCommitErrorCode;
|
|
@@ -35,6 +40,10 @@ interface PushOptions {
|
|
|
35
40
|
cwd: string;
|
|
36
41
|
pushCommits?: boolean;
|
|
37
42
|
pushTags?: boolean;
|
|
43
|
+
/** 是否同时把各 submodule 的 HEAD(+ 同名 tag)分别推送到各自远端分支。 */
|
|
44
|
+
submodule?: boolean;
|
|
45
|
+
/** `submodule` + `pushTags` 时,要连带推送到 submodule 的同名 tag。 */
|
|
46
|
+
tagName?: string;
|
|
38
47
|
}
|
|
39
48
|
export declare function runPush(opts: PushOptions): Promise<PushResult>;
|
|
40
49
|
export declare function runQuickCommit(opts: QuickCommitOptions): Promise<QuickCommitResult>;
|
package/dist/git-quick-commit.js
CHANGED
|
@@ -121,6 +121,24 @@ function parsePorcelainV2(raw) {
|
|
|
121
121
|
}
|
|
122
122
|
return out;
|
|
123
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* 仓库是否声明了 submodule(读 repo 根的 .gitmodules)。不能只看 `git status`——一个
|
|
126
|
+
* clean 的 submodule 不会出现在 status 里,但「是否提供 Submodule 选项」应基于声明而非当前改动。
|
|
127
|
+
*/
|
|
128
|
+
function repoDeclaresSubmodule(repoRoot) {
|
|
129
|
+
if (!repoRoot)
|
|
130
|
+
return false;
|
|
131
|
+
const gitmodules = `${repoRoot}/.gitmodules`;
|
|
132
|
+
if (!existsSync(gitmodules))
|
|
133
|
+
return false;
|
|
134
|
+
try {
|
|
135
|
+
const out = runGitAllowEmpty(["config", "-f", gitmodules, "--get-regexp", "\\.path$"], repoRoot).trim();
|
|
136
|
+
return out.length > 0;
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
124
142
|
export function getGitStatus(cwd) {
|
|
125
143
|
if (!cwd || !existsSync(cwd)) {
|
|
126
144
|
return { isGit: false, error: "工作目录不存在。" };
|
|
@@ -241,6 +259,8 @@ export function getGitStatus(cwd) {
|
|
|
241
259
|
behind,
|
|
242
260
|
lastCommit,
|
|
243
261
|
latestTag,
|
|
262
|
+
// 基于全量 allEntries 判定(不受 files 的 200 条 slice 影响),供前端决定是否渲染 Submodule 球。
|
|
263
|
+
hasSubmodule: allEntries.some((e) => e.isSubmodule),
|
|
244
264
|
};
|
|
245
265
|
}
|
|
246
266
|
export class QuickCommitError extends Error {
|
|
@@ -445,6 +465,7 @@ ${diff}`;
|
|
|
445
465
|
*/
|
|
446
466
|
function doPush(opts) {
|
|
447
467
|
const { cwd, pushCommits, pushTags } = opts;
|
|
468
|
+
const recurseFlag = `--recurse-submodules=${opts.recurseSubmodules ?? "check"}`;
|
|
448
469
|
let pushedCommits = false;
|
|
449
470
|
let pushedTags = false;
|
|
450
471
|
let hasUpstream = false;
|
|
@@ -459,10 +480,10 @@ function doPush(opts) {
|
|
|
459
480
|
try {
|
|
460
481
|
if (pushCommits) {
|
|
461
482
|
if (hasUpstream) {
|
|
462
|
-
runGit(["push",
|
|
483
|
+
runGit(["push", recurseFlag], cwd, GIT_PUSH_TIMEOUT_MS);
|
|
463
484
|
}
|
|
464
485
|
else {
|
|
465
|
-
runGit(["push", "-u",
|
|
486
|
+
runGit(["push", "-u", recurseFlag, pushRemote, "HEAD"], cwd, GIT_PUSH_TIMEOUT_MS);
|
|
466
487
|
}
|
|
467
488
|
pushedCommits = true;
|
|
468
489
|
}
|
|
@@ -538,19 +559,63 @@ export async function runTagHead(opts) {
|
|
|
538
559
|
};
|
|
539
560
|
}
|
|
540
561
|
export async function runPush(opts) {
|
|
541
|
-
const { cwd, pushCommits = true, pushTags = false } = opts;
|
|
562
|
+
const { cwd, pushCommits = true, pushTags = false, submodule = false, tagName } = opts;
|
|
542
563
|
assertGitWorkTree(cwd);
|
|
543
564
|
if (!pushCommits && !pushTags) {
|
|
544
565
|
throw new QuickCommitError("没有要推送的内容。", "NOTHING_TO_PUSH");
|
|
545
566
|
}
|
|
546
|
-
|
|
567
|
+
// 纳入 submodule:逐个把声明的 submodule 的 HEAD 推到各自远端分支(已是最新则 git 自身 no-op)。
|
|
568
|
+
// 这覆盖「先 commit(含 submodule)后补 Push & Close」的场景——此时 submodule 已 clean、
|
|
569
|
+
// status 看不到,但本地可能仍领先远端。父仓库随后用 recurse=no 推送。
|
|
570
|
+
let subPushErrors = [];
|
|
571
|
+
if (submodule) {
|
|
572
|
+
const { base, infos } = collectSubmodulesForPush(cwd);
|
|
573
|
+
if (infos.length > 0) {
|
|
574
|
+
const subPush = pushSubmodules(base, infos, { pushTags: !!(pushTags && tagName), tagName });
|
|
575
|
+
subPushErrors = subPush.errors;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const outcome = doPush({ cwd, pushCommits, pushTags, recurseSubmodules: submodule ? "no" : "check" });
|
|
547
579
|
return {
|
|
548
|
-
ok: !outcome.error,
|
|
580
|
+
ok: !outcome.error && subPushErrors.length === 0,
|
|
549
581
|
pushedCommits: outcome.pushedCommits,
|
|
550
582
|
pushedTags: outcome.pushedTags,
|
|
551
|
-
error: outcome.error,
|
|
583
|
+
error: outcome.error || (subPushErrors.length ? subPushErrors.join(";") : undefined),
|
|
552
584
|
};
|
|
553
585
|
}
|
|
586
|
+
/**
|
|
587
|
+
* 解析一个 submodule 当前 HEAD 应推到哪个远端分支。submodule 经 `git submodule update`
|
|
588
|
+
* 后通常处于 detached HEAD,不能直接 `git push`,必须显式 `HEAD:<branch>`,否则父仓库
|
|
589
|
+
* `--recurse-submodules=on-demand` 会以「HEAD does not match the named branch」整体失败。
|
|
590
|
+
* 优先取远端默认分支(`<remote>/HEAD` 指向)→ 当前命名分支 → 回退 main/master。
|
|
591
|
+
*/
|
|
592
|
+
function resolveSubmodulePushBranch(subCwd, remote) {
|
|
593
|
+
const prefix = `${remote}/`;
|
|
594
|
+
try {
|
|
595
|
+
const ref = runGit(["symbolic-ref", "--short", `refs/remotes/${remote}/HEAD`], subCwd);
|
|
596
|
+
const name = (ref.startsWith(prefix) ? ref.slice(prefix.length) : ref).trim();
|
|
597
|
+
if (name)
|
|
598
|
+
return name;
|
|
599
|
+
}
|
|
600
|
+
catch {
|
|
601
|
+
// ignore — fall through
|
|
602
|
+
}
|
|
603
|
+
try {
|
|
604
|
+
const cur = runGit(["branch", "--show-current"], subCwd);
|
|
605
|
+
if (cur)
|
|
606
|
+
return cur;
|
|
607
|
+
}
|
|
608
|
+
catch {
|
|
609
|
+
// ignore
|
|
610
|
+
}
|
|
611
|
+
try {
|
|
612
|
+
runGit(["rev-parse", "--verify", `refs/remotes/${remote}/main`], subCwd);
|
|
613
|
+
return "main";
|
|
614
|
+
}
|
|
615
|
+
catch {
|
|
616
|
+
return "master";
|
|
617
|
+
}
|
|
618
|
+
}
|
|
554
619
|
/**
|
|
555
620
|
* 在 commit 父仓库之前,先在每个内部 dirty / untracked 的 submodule 里
|
|
556
621
|
* 执行一次 `git add -A` + `git commit -m <msg>`,让父仓库的 add -A
|
|
@@ -615,12 +680,109 @@ function commitDirtySubmodules(parentCwd, message) {
|
|
|
615
680
|
hash = runGit(["rev-parse", "--short", "HEAD"], subCwd);
|
|
616
681
|
}
|
|
617
682
|
catch { /* ignore */ }
|
|
618
|
-
|
|
683
|
+
const remote = resolvePushRemote(subCwd);
|
|
684
|
+
const branch = resolveSubmodulePushBranch(subCwd, remote);
|
|
685
|
+
commits.push({ path: entry.path, hash, remote, branch });
|
|
619
686
|
}
|
|
620
687
|
return { commits, errors };
|
|
621
688
|
}
|
|
689
|
+
/** 对刚提交的 submodule 打上与父仓库同名的 tag。已存在同名 tag 则记为非致命跳过。 */
|
|
690
|
+
function tagSubmodules(parentCwd, subInfos, tagName) {
|
|
691
|
+
const tagged = [];
|
|
692
|
+
const errors = [];
|
|
693
|
+
for (const info of subInfos) {
|
|
694
|
+
const subCwd = `${parentCwd}/${info.path}`;
|
|
695
|
+
try {
|
|
696
|
+
runGit(["rev-parse", "--verify", `refs/tags/${tagName}`], subCwd);
|
|
697
|
+
errors.push(`submodule ${info.path} 已存在 tag ${tagName},跳过`);
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
catch {
|
|
701
|
+
// 不存在 → 继续打 tag
|
|
702
|
+
}
|
|
703
|
+
try {
|
|
704
|
+
runGit(["tag", tagName], subCwd);
|
|
705
|
+
tagged.push(info.path);
|
|
706
|
+
}
|
|
707
|
+
catch (error) {
|
|
708
|
+
errors.push(`submodule ${info.path} 打 tag 失败:${getGitErrorMessage(error)}`);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
return { tagged, errors };
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* 对每个 submodule 单独把当前 HEAD 推送到其远端分支(`HEAD:refs/heads/<branch>`),
|
|
715
|
+
* 解决 detached HEAD 无法直接 push 的问题;`pushTags` 时连带把同名 tag 推上去。
|
|
716
|
+
* 单个 submodule 失败收集为非致命错误。
|
|
717
|
+
*/
|
|
718
|
+
function pushSubmodules(parentCwd, subInfos, opts) {
|
|
719
|
+
const pushed = [];
|
|
720
|
+
const errors = [];
|
|
721
|
+
for (const info of subInfos) {
|
|
722
|
+
const subCwd = `${parentCwd}/${info.path}`;
|
|
723
|
+
if (!existsSync(subCwd)) {
|
|
724
|
+
errors.push(`submodule ${info.path} 路径不存在`);
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
try {
|
|
728
|
+
runGit(["push", info.remote, `HEAD:refs/heads/${info.branch}`], subCwd, GIT_PUSH_TIMEOUT_MS);
|
|
729
|
+
}
|
|
730
|
+
catch (error) {
|
|
731
|
+
errors.push(`submodule ${info.path} 推送失败:${getGitErrorMessage(error)}`);
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
if (opts.pushTags && opts.tagName) {
|
|
735
|
+
try {
|
|
736
|
+
runGit(["push", info.remote, `refs/tags/${opts.tagName}`], subCwd, GIT_PUSH_TIMEOUT_MS);
|
|
737
|
+
}
|
|
738
|
+
catch (error) {
|
|
739
|
+
errors.push(`submodule ${info.path} 推送 tag 失败:${getGitErrorMessage(error)}`);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
pushed.push(info.path);
|
|
743
|
+
}
|
|
744
|
+
return { pushed, errors };
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* 枚举仓库声明的全部 submodule(读 repo 根的 .gitmodules),为「单独 push」准备
|
|
748
|
+
* remote + 目标分支。用于结果面板的 Push & Close:此时 submodule 多已 clean,
|
|
749
|
+
* status 里看不到,但本地 HEAD 可能仍领先远端,需要逐个尝试推送(up-to-date 则 no-op)。
|
|
750
|
+
*/
|
|
751
|
+
function collectSubmodulesForPush(cwd) {
|
|
752
|
+
let base;
|
|
753
|
+
try {
|
|
754
|
+
base = runGit(["rev-parse", "--show-toplevel"], cwd);
|
|
755
|
+
}
|
|
756
|
+
catch {
|
|
757
|
+
base = cwd;
|
|
758
|
+
}
|
|
759
|
+
let raw;
|
|
760
|
+
try {
|
|
761
|
+
raw = runGitAllowEmpty(["config", "-f", `${base}/.gitmodules`, "--get-regexp", "\\.path$"], base);
|
|
762
|
+
}
|
|
763
|
+
catch {
|
|
764
|
+
return { base, infos: [] };
|
|
765
|
+
}
|
|
766
|
+
const infos = [];
|
|
767
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
768
|
+
const trimmed = line.trim();
|
|
769
|
+
if (!trimmed)
|
|
770
|
+
continue;
|
|
771
|
+
// 行格式:submodule.<name>.path <relpath>
|
|
772
|
+
const rel = trimmed.split(/\s+/).slice(1).join(" ").trim();
|
|
773
|
+
if (!rel)
|
|
774
|
+
continue;
|
|
775
|
+
const subCwd = `${base}/${rel}`;
|
|
776
|
+
if (!existsSync(subCwd))
|
|
777
|
+
continue;
|
|
778
|
+
const remote = resolvePushRemote(subCwd);
|
|
779
|
+
const branch = resolveSubmodulePushBranch(subCwd, remote);
|
|
780
|
+
infos.push({ path: rel, hash: "", remote, branch });
|
|
781
|
+
}
|
|
782
|
+
return { base, infos };
|
|
783
|
+
}
|
|
622
784
|
export async function runQuickCommit(opts) {
|
|
623
|
-
const { cwd, language, autoMessage, customMessage, tag, autoTag, push } = opts;
|
|
785
|
+
const { cwd, language, autoMessage, customMessage, tag, autoTag, push, submodule } = opts;
|
|
624
786
|
assertGitWorkTree(cwd);
|
|
625
787
|
// 先 add 一次让我们能在 collectStagedDiff 看到完整改动(包含 submodule 指针),
|
|
626
788
|
// AI 生成 message 时也基于这个 staged diff。
|
|
@@ -646,7 +808,11 @@ export async function runQuickCommit(opts) {
|
|
|
646
808
|
submoduleHasDirty = parsePorcelainV2(porcelain).some((e) => e.isSubmodule && (e.submoduleState?.hasTrackedChanges || e.submoduleState?.hasUntracked));
|
|
647
809
|
}
|
|
648
810
|
catch { /* keep submoduleHasDirty=false */ }
|
|
649
|
-
|
|
811
|
+
// 默认不纳入 submodule:只有显式 opts.submodule 时,submodule 内部 dirty 才计入「有改动」。
|
|
812
|
+
if (!parentHasStaged && !(submodule && submoduleHasDirty)) {
|
|
813
|
+
if (submoduleHasDirty && !submodule) {
|
|
814
|
+
throw new QuickCommitError("父仓库没有改动;检测到 submodule 内部有改动,拖入 Submodule 球可一起提交。", "NOTHING_TO_COMMIT");
|
|
815
|
+
}
|
|
650
816
|
throw new QuickCommitError("没有任何改动可以提交。", "NOTHING_TO_COMMIT");
|
|
651
817
|
}
|
|
652
818
|
let message;
|
|
@@ -659,22 +825,25 @@ export async function runQuickCommit(opts) {
|
|
|
659
825
|
throw new QuickCommitError("commit message 不能为空。", "EMPTY_MESSAGE");
|
|
660
826
|
}
|
|
661
827
|
}
|
|
662
|
-
//
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
828
|
+
// 仅当用户显式纳入 submodule 时,才进入各 submodule 内部提交其 dirty 改动;
|
|
829
|
+
// 父仓库随后再 add 一次,picks up 新的 submodule 指针。
|
|
830
|
+
let submoduleOutcome = { commits: [], errors: [] };
|
|
831
|
+
if (submodule) {
|
|
832
|
+
submoduleOutcome = commitDirtySubmodules(cwd, message);
|
|
833
|
+
if (submoduleOutcome.commits.length > 0) {
|
|
834
|
+
try {
|
|
835
|
+
runGit(["add", "-A"], cwd, 5000);
|
|
836
|
+
}
|
|
837
|
+
catch (error) {
|
|
838
|
+
throw new QuickCommitError(`父仓库 add submodule 指针失败:${getGitErrorMessage(error)}`, "GIT_ADD_FAILED");
|
|
839
|
+
}
|
|
840
|
+
// 重新评估父仓库是否有 staged 内容:submodule 指针变了这里应为真。
|
|
841
|
+
try {
|
|
842
|
+
stagedFiles = runGitAllowEmpty(["diff", "--cached", "--name-only"], cwd).trim();
|
|
843
|
+
parentHasStaged = stagedFiles.length > 0;
|
|
844
|
+
}
|
|
845
|
+
catch { /* keep stale value */ }
|
|
676
846
|
}
|
|
677
|
-
catch { /* keep stale value */ }
|
|
678
847
|
}
|
|
679
848
|
if (!parentHasStaged) {
|
|
680
849
|
// submodule 都提交了但父仓库还是没有 staged —— 通常意味着 .gitmodules 没动
|
|
@@ -710,18 +879,32 @@ export async function runQuickCommit(opts) {
|
|
|
710
879
|
catch (error) {
|
|
711
880
|
throw new QuickCommitError(`git tag 失败:${getGitErrorMessage(error)}`, "GIT_TAG_FAILED");
|
|
712
881
|
}
|
|
882
|
+
// 纳入 submodule 时给刚提交的 submodule 打同名 tag(非致命,失败不阻断父仓库流程)。
|
|
883
|
+
if (submodule && submoduleOutcome.commits.length > 0) {
|
|
884
|
+
tagSubmodules(cwd, submoduleOutcome.commits, tagName);
|
|
885
|
+
}
|
|
713
886
|
}
|
|
714
887
|
let pushed = false;
|
|
715
888
|
let pushError;
|
|
716
889
|
if (push) {
|
|
890
|
+
// 纳入 submodule:先把各 submodule 的 HEAD(+ 同名 tag)分别推到各自远端分支,
|
|
891
|
+
// 解决 detached HEAD 无法被父仓库 on-demand 递归推送的问题;父仓库随后用
|
|
892
|
+
// recurse=no 单独推(submodule 已就绪)。否则父仓库用 recurse=check 做安全校验。
|
|
893
|
+
const includeSub = !!submodule && submoduleOutcome.commits.length > 0;
|
|
894
|
+
let subPushErrors = [];
|
|
895
|
+
if (includeSub) {
|
|
896
|
+
const subPush = pushSubmodules(cwd, submoduleOutcome.commits, { pushTags: !!tagName, tagName });
|
|
897
|
+
subPushErrors = subPush.errors;
|
|
898
|
+
}
|
|
717
899
|
const outcome = doPush({
|
|
718
900
|
cwd,
|
|
719
901
|
pushCommits: true,
|
|
720
902
|
// Push only the freshly-created tag — avoids surprising users by pushing stale local tags.
|
|
721
903
|
pushTags: tagName ? [tagName] : false,
|
|
904
|
+
recurseSubmodules: includeSub ? "no" : "check",
|
|
722
905
|
});
|
|
723
|
-
pushed = outcome.pushedCommits && (tagName ? outcome.pushedTags : true);
|
|
724
|
-
pushError = outcome.error;
|
|
906
|
+
pushed = outcome.pushedCommits && (tagName ? outcome.pushedTags : true) && subPushErrors.length === 0;
|
|
907
|
+
pushError = outcome.error || (subPushErrors.length ? subPushErrors.join(";") : undefined);
|
|
725
908
|
}
|
|
726
909
|
return {
|
|
727
910
|
ok: true,
|
|
@@ -426,6 +426,7 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
426
426
|
tag: typeof body.tag === "string" ? body.tag : undefined,
|
|
427
427
|
autoTag: !!body.autoTag,
|
|
428
428
|
push: !!body.push,
|
|
429
|
+
submodule: !!body.submodule,
|
|
429
430
|
});
|
|
430
431
|
res.json(result);
|
|
431
432
|
}
|
|
@@ -506,6 +507,8 @@ export function registerSessionRoutes(app, processes, structured, storage, defau
|
|
|
506
507
|
cwd: snapshot.cwd,
|
|
507
508
|
pushCommits: body.pushCommits !== false,
|
|
508
509
|
pushTags: !!body.pushTags,
|
|
510
|
+
submodule: !!body.submodule,
|
|
511
|
+
tagName: typeof body.tag === "string" ? body.tag : undefined,
|
|
509
512
|
});
|
|
510
513
|
res.json(result);
|
|
511
514
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -198,6 +198,8 @@ export interface GitStatusResult {
|
|
|
198
198
|
};
|
|
199
199
|
/** Most recent tag reachable from HEAD (`git describe --tags --abbrev=0`), if any. */
|
|
200
200
|
latestTag?: string;
|
|
201
|
+
/** True 当仓库声明了 submodule(任一改动条目为 submodule)。前端据此决定是否渲染 Submodule 球。 */
|
|
202
|
+
hasSubmodule?: boolean;
|
|
201
203
|
error?: string;
|
|
202
204
|
}
|
|
203
205
|
export interface QuickCommitResult {
|