@gitkraken/core-gitlens 0.16.0 → 0.16.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/CHANGELOG.md +15 -1
- package/dist/git/cache.js +2 -2
- package/dist/git/cache.js.map +1 -1
- package/dist/git/run.types.d.ts +3 -0
- package/dist/git/watching/classifyChange.js +3 -0
- package/dist/git/watching/classifyChange.js.map +1 -1
- package/dist/git/watching/watchSession.d.ts +2 -0
- package/dist/git/watching/watchSession.js +9 -1
- package/dist/git/watching/watchSession.js.map +1 -1
- package/dist/git-cli/cliGitProvider.d.ts +1 -1
- package/dist/git-cli/cliGitProvider.js +7 -4
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/exec/commandClassifier.d.ts +18 -3
- package/dist/git-cli/exec/commandClassifier.js +562 -16
- package/dist/git-cli/exec/commandClassifier.js.map +1 -1
- package/dist/git-cli/exec/exec.d.ts +2 -2
- package/dist/git-cli/exec/git.d.ts +31 -4
- package/dist/git-cli/exec/git.js +105 -43
- package/dist/git-cli/exec/git.js.map +1 -1
- package/dist/git-cli/exec/headState.d.ts +32 -0
- package/dist/git-cli/exec/headState.js +78 -0
- package/dist/git-cli/exec/headState.js.map +1 -0
- package/dist/git-cli/providers/branches.js +14 -4
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/operations.d.ts +6 -7
- package/dist/git-cli/providers/operations.js +46 -64
- package/dist/git-cli/providers/operations.js.map +1 -1
- package/dist/git-cli/providers/refs.d.ts +4 -1
- package/dist/git-cli/providers/refs.js +13 -9
- package/dist/git-cli/providers/refs.js.map +1 -1
- package/dist/utils/env/node/exec.d.ts +38 -1
- package/dist/utils/env/node/exec.js +14 -6
- package/dist/utils/env/node/exec.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getRefChangeKind } from "../../git/watching/changeEvent.js";
|
|
1
2
|
import { isAbsolute, join } from "node:path";
|
|
2
3
|
//#region ../git-cli/src/exec/commandClassifier.ts
|
|
3
4
|
/**
|
|
@@ -86,7 +87,11 @@ const readOnlySubcommands = {
|
|
|
86
87
|
"show",
|
|
87
88
|
"get-ref"
|
|
88
89
|
]),
|
|
89
|
-
stash: /* @__PURE__ */ new Set([
|
|
90
|
+
stash: /* @__PURE__ */ new Set([
|
|
91
|
+
"list",
|
|
92
|
+
"show",
|
|
93
|
+
"create"
|
|
94
|
+
]),
|
|
90
95
|
tag: /* @__PURE__ */ new Set([
|
|
91
96
|
"",
|
|
92
97
|
"-l",
|
|
@@ -187,6 +192,7 @@ const globalOptionsWithValue = /* @__PURE__ */ new Set([
|
|
|
187
192
|
*/
|
|
188
193
|
function leadingCommand(args) {
|
|
189
194
|
let pathOverride;
|
|
195
|
+
let configs;
|
|
190
196
|
let i = 0;
|
|
191
197
|
while (i < args.length) {
|
|
192
198
|
const arg = args[i];
|
|
@@ -197,6 +203,7 @@ function leadingCommand(args) {
|
|
|
197
203
|
continue;
|
|
198
204
|
}
|
|
199
205
|
if (globalOptionsWithValue.has(arg)) {
|
|
206
|
+
if (arg === "-c" && args[i + 1] != null) (configs ??= []).push(args[i + 1]);
|
|
200
207
|
i += 2;
|
|
201
208
|
continue;
|
|
202
209
|
}
|
|
@@ -213,7 +220,8 @@ function leadingCommand(args) {
|
|
|
213
220
|
readOnly: !hasWriteOption(verb, rest) && (readOnlyVerbs.has(verb) || (readOnlySubcommands[verb]?.has(rest[0] ?? "") ?? false) || verb === "config" && isConfigRead(rest) || verb === "reflog" && !reflogWriteSubcommands.has(rest[0] ?? "") || verb === "symbolic-ref" && !rest.includes("-d") && !rest.includes("--delete") && rest.filter((a) => !a.startsWith("-")).length < 2),
|
|
214
221
|
subcommand: rest[0],
|
|
215
222
|
rest,
|
|
216
|
-
pathOverride
|
|
223
|
+
pathOverride,
|
|
224
|
+
configs
|
|
217
225
|
};
|
|
218
226
|
}
|
|
219
227
|
/**
|
|
@@ -250,6 +258,9 @@ const objectOnlyVerbs = /* @__PURE__ */ new Set([
|
|
|
250
258
|
* table and {@link objectOnlyVerbs} announces `[]` — the conservative default for an unclassified
|
|
251
259
|
* mutation: the caller still learns something changed, just not what. An entry covers at least what the
|
|
252
260
|
* verb's typed mutator announces; `update-ref` and `symbolic-ref` name every kind their target ref could be.
|
|
261
|
+
* {@link getNamedChanges} narrows the forms whose argv proves what moved, which can announce less: the typed
|
|
262
|
+
* checkout announces `heads` for any target, but a `--detach` or full-SHA checkout moves only this tree's HEAD,
|
|
263
|
+
* no shared ref.
|
|
253
264
|
* The kinds decide which caches the announcement resets, so a verb that can start or end a paused operation
|
|
254
265
|
* names `pausedOp`: `commit` concludes a merge, cherry-pick or revert, `reset` aborts one, and `am` runs in
|
|
255
266
|
* `rebase-apply`.
|
|
@@ -339,11 +350,7 @@ const changesByVerb = {
|
|
|
339
350
|
"tags",
|
|
340
351
|
"stash"
|
|
341
352
|
],
|
|
342
|
-
branch: [
|
|
343
|
-
"heads",
|
|
344
|
-
"remotes",
|
|
345
|
-
"tags"
|
|
346
|
-
],
|
|
353
|
+
branch: ["heads", "remotes"],
|
|
347
354
|
"symbolic-ref": [
|
|
348
355
|
"head",
|
|
349
356
|
"heads",
|
|
@@ -396,8 +403,7 @@ const branchConfigOptions = [
|
|
|
396
403
|
const branchConfigChanges = [
|
|
397
404
|
"config",
|
|
398
405
|
"heads",
|
|
399
|
-
"remotes"
|
|
400
|
-
"tags"
|
|
406
|
+
"remotes"
|
|
401
407
|
];
|
|
402
408
|
/**
|
|
403
409
|
* Whether creating a branch from `startPoint` may write its tracking to `.git/config`, as git's default
|
|
@@ -412,20 +418,560 @@ function mayWriteTracking(startPoint, noTracking) {
|
|
|
412
418
|
}
|
|
413
419
|
const worktreeChanges = ["worktrees", "heads"];
|
|
414
420
|
const emptyChanges = [];
|
|
421
|
+
const configHeadsChanges = ["config", "heads"];
|
|
422
|
+
const headsChanges = ["heads"];
|
|
423
|
+
const remotesChanges = ["remotes"];
|
|
424
|
+
const tagsChanges = ["tags"];
|
|
425
|
+
const stashChanges = ["stash"];
|
|
426
|
+
const stashIndexChanges = ["stash", "index"];
|
|
427
|
+
const stashBranchChanges = [
|
|
428
|
+
"head",
|
|
429
|
+
"heads",
|
|
430
|
+
"index",
|
|
431
|
+
"stash"
|
|
432
|
+
];
|
|
433
|
+
const indexChanges = ["index"];
|
|
434
|
+
const headHeadsChanges = ["head", "heads"];
|
|
435
|
+
const headIndexChanges = ["head", "index"];
|
|
436
|
+
const checkoutChanges = [
|
|
437
|
+
"head",
|
|
438
|
+
"heads",
|
|
439
|
+
"index"
|
|
440
|
+
];
|
|
441
|
+
const checkoutTrackingChanges = [
|
|
442
|
+
"config",
|
|
443
|
+
"head",
|
|
444
|
+
"heads",
|
|
445
|
+
"index"
|
|
446
|
+
];
|
|
447
|
+
const worktreeOnlyChanges = ["worktrees"];
|
|
448
|
+
const worktreeTrackingChanges = [
|
|
449
|
+
"config",
|
|
450
|
+
"worktrees",
|
|
451
|
+
"heads"
|
|
452
|
+
];
|
|
453
|
+
const stashRefs = ["refs/stash"];
|
|
454
|
+
/** A full object name, which `checkout` and `worktree add` never DWIM into a new branch */
|
|
455
|
+
const fullShaRegex = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;
|
|
456
|
+
/**
|
|
457
|
+
* Whether a branch name in argv is the branch it names. Git expands `@{-<n>}` (and `@`) wherever it takes a branch
|
|
458
|
+
* name, `branch -d @{-1}` deleting the previous branch, and no ref name can hold `@{` or be `@`
|
|
459
|
+
*/
|
|
460
|
+
function isLiteralBranchName(name) {
|
|
461
|
+
return name !== "@" && !name.includes("@{");
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Splits a verb's arguments by its own option table, since one letter means different things to different verbs
|
|
465
|
+
* (`-m` moves, merges or sets a message). An option outside the table returns `undefined` rather than a guess at
|
|
466
|
+
* whether it takes a value. A valued option accepts `--opt=<v>`, `--opt <v>`, `-o <v>` and `-o<v>`.
|
|
467
|
+
*/
|
|
468
|
+
function parseArgs(args, flags, valued) {
|
|
469
|
+
const options = [];
|
|
470
|
+
const positionals = [];
|
|
471
|
+
let dashDash;
|
|
472
|
+
let optionsEnded = false;
|
|
473
|
+
for (let i = 0; i < args.length; i++) {
|
|
474
|
+
const arg = args[i];
|
|
475
|
+
if (arg === "--" && dashDash == null) {
|
|
476
|
+
dashDash = positionals.length;
|
|
477
|
+
optionsEnded = true;
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
if (optionsEnded || arg === "-" || !arg.startsWith("-")) {
|
|
481
|
+
positionals.push(arg);
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
if (arg === "--end-of-options") {
|
|
485
|
+
optionsEnded = true;
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
if (flags.has(arg)) {
|
|
489
|
+
options.push([arg, void 0]);
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
if (valued.has(arg)) {
|
|
493
|
+
const value = args[i + 1];
|
|
494
|
+
if (value == null) return void 0;
|
|
495
|
+
options.push([arg, value]);
|
|
496
|
+
i++;
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
const long = arg.startsWith("--");
|
|
500
|
+
const name = long ? arg.split("=", 1)[0] : arg.slice(0, 2);
|
|
501
|
+
if (name === arg || !valued.has(name)) return void 0;
|
|
502
|
+
options.push([name, arg.slice(long ? name.length + 1 : name.length)]);
|
|
503
|
+
}
|
|
504
|
+
return {
|
|
505
|
+
options,
|
|
506
|
+
positionals,
|
|
507
|
+
dashDash
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
function hasParsedOption(parsed, ...names) {
|
|
511
|
+
return parsed.options.some(([option]) => names.includes(option));
|
|
512
|
+
}
|
|
513
|
+
const updateRefFlags = /* @__PURE__ */ new Set([
|
|
514
|
+
"-d",
|
|
515
|
+
"--delete",
|
|
516
|
+
"--no-deref",
|
|
517
|
+
"--create-reflog"
|
|
518
|
+
]);
|
|
519
|
+
const updateRefValued = /* @__PURE__ */ new Set(["-m", "--message"]);
|
|
520
|
+
/**
|
|
521
|
+
* `update-ref` moves the one ref it names, or the ref a symbolic one points to without `--no-deref`, so `HEAD` and a
|
|
522
|
+
* remote's `HEAD` stay unnamed. `null` means a ref outside the namespaces core reads, which the typed
|
|
523
|
+
* `updateReference` doesn't announce either. `git log --all` (graph, search, contributors) does walk such a ref when
|
|
524
|
+
* it points at a commit, and argv can't tell that from a blob ref, so a row it adds or drops can stay stale until the
|
|
525
|
+
* next heads change: the trade the typed write makes not to refresh every worktree on each bookkeeping write. A packed
|
|
526
|
+
* ref's delete rewrites `packed-refs`, which the watcher still reports.
|
|
527
|
+
*/
|
|
528
|
+
function getUpdateRefChanges(args) {
|
|
529
|
+
const parsed = parseArgs(args, updateRefFlags, updateRefValued);
|
|
530
|
+
if (parsed == null) return void 0;
|
|
531
|
+
const [ref] = parsed.positionals;
|
|
532
|
+
if (!ref) return void 0;
|
|
533
|
+
if (ref === "HEAD") return { changes: headHeadsChanges };
|
|
534
|
+
if (!ref.startsWith("refs/")) return void 0;
|
|
535
|
+
if (ref.endsWith("/HEAD") && !hasParsedOption(parsed, "--no-deref")) return void 0;
|
|
536
|
+
const kind = getRefChangeKind(ref);
|
|
537
|
+
if (kind != null) return {
|
|
538
|
+
changes: [kind],
|
|
539
|
+
refs: [ref]
|
|
540
|
+
};
|
|
541
|
+
if (ref.startsWith("refs/replace/") || ref.startsWith("refs/notes/")) return { changes: headsChanges };
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
const branchFlags = /* @__PURE__ */ new Set([
|
|
545
|
+
"-f",
|
|
546
|
+
"--force",
|
|
547
|
+
"--no-track",
|
|
548
|
+
"-d",
|
|
549
|
+
"-D",
|
|
550
|
+
"--delete",
|
|
551
|
+
"-m",
|
|
552
|
+
"-M",
|
|
553
|
+
"--move",
|
|
554
|
+
"-c",
|
|
555
|
+
"-C",
|
|
556
|
+
"--copy",
|
|
557
|
+
"-r",
|
|
558
|
+
"--remotes",
|
|
559
|
+
"-q",
|
|
560
|
+
"--quiet",
|
|
561
|
+
"--create-reflog"
|
|
562
|
+
]);
|
|
563
|
+
const noValuedOptions = /* @__PURE__ */ new Set();
|
|
564
|
+
/**
|
|
565
|
+
* The create, delete, rename and copy forms of `branch` name every branch they write. A delete removes a local
|
|
566
|
+
* branch's `branch.<name>` section and a rename or copy moves it; a create writes it only for tracking.
|
|
567
|
+
*/
|
|
568
|
+
function getBranchChanges(args) {
|
|
569
|
+
const parsed = parseArgs(args, branchFlags, noValuedOptions);
|
|
570
|
+
if (parsed == null) return void 0;
|
|
571
|
+
let mode;
|
|
572
|
+
let remote = false;
|
|
573
|
+
let noTrack = false;
|
|
574
|
+
for (const [option] of parsed.options) switch (option) {
|
|
575
|
+
case "-d":
|
|
576
|
+
case "-D":
|
|
577
|
+
case "--delete":
|
|
578
|
+
if (mode === "move") return void 0;
|
|
579
|
+
mode = "delete";
|
|
580
|
+
break;
|
|
581
|
+
case "-m":
|
|
582
|
+
case "-M":
|
|
583
|
+
case "--move":
|
|
584
|
+
case "-c":
|
|
585
|
+
case "-C":
|
|
586
|
+
case "--copy":
|
|
587
|
+
if (mode === "delete") return void 0;
|
|
588
|
+
mode = "move";
|
|
589
|
+
break;
|
|
590
|
+
case "-r":
|
|
591
|
+
case "--remotes":
|
|
592
|
+
remote = true;
|
|
593
|
+
break;
|
|
594
|
+
case "--no-track": noTrack = true;
|
|
595
|
+
}
|
|
596
|
+
const names = parsed.positionals;
|
|
597
|
+
if (mode === "delete") {
|
|
598
|
+
if (!names.length || !names.every(isLiteralBranchName)) return void 0;
|
|
599
|
+
if (remote) return {
|
|
600
|
+
changes: remotesChanges,
|
|
601
|
+
refs: names.map((n) => `refs/remotes/${n}`)
|
|
602
|
+
};
|
|
603
|
+
return {
|
|
604
|
+
changes: configHeadsChanges,
|
|
605
|
+
refs: names.map((n) => `refs/heads/${n}`),
|
|
606
|
+
configConfinedToRefs: true
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
if (remote) return void 0;
|
|
610
|
+
if (mode === "move") {
|
|
611
|
+
if (names.length !== 2 || !names.every(isLiteralBranchName)) return void 0;
|
|
612
|
+
return {
|
|
613
|
+
changes: configHeadsChanges,
|
|
614
|
+
refs: names.map((n) => `refs/heads/${n}`),
|
|
615
|
+
configConfinedToRefs: true
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
if (names.length !== 1 && names.length !== 2 || !isLiteralBranchName(names[0])) return void 0;
|
|
619
|
+
const refs = [`refs/heads/${names[0]}`];
|
|
620
|
+
if (mayWriteTracking(names[1], noTrack)) return {
|
|
621
|
+
changes: configHeadsChanges,
|
|
622
|
+
refs,
|
|
623
|
+
configConfinedToRefs: true
|
|
624
|
+
};
|
|
625
|
+
return {
|
|
626
|
+
changes: headsChanges,
|
|
627
|
+
refs
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
const tagFlags = /* @__PURE__ */ new Set([
|
|
631
|
+
"-f",
|
|
632
|
+
"--force",
|
|
633
|
+
"-d",
|
|
634
|
+
"--delete",
|
|
635
|
+
"-a",
|
|
636
|
+
"--annotate",
|
|
637
|
+
"-s",
|
|
638
|
+
"--sign",
|
|
639
|
+
"--no-sign",
|
|
640
|
+
"-e",
|
|
641
|
+
"--edit",
|
|
642
|
+
"--create-reflog"
|
|
643
|
+
]);
|
|
644
|
+
const tagValued = /* @__PURE__ */ new Set([
|
|
645
|
+
"-m",
|
|
646
|
+
"--message",
|
|
647
|
+
"-F",
|
|
648
|
+
"--file",
|
|
649
|
+
"-u",
|
|
650
|
+
"--local-user",
|
|
651
|
+
"--cleanup"
|
|
652
|
+
]);
|
|
653
|
+
function getTagChanges(args) {
|
|
654
|
+
const parsed = parseArgs(args, tagFlags, tagValued);
|
|
655
|
+
if (parsed == null) return void 0;
|
|
656
|
+
const names = parsed.positionals;
|
|
657
|
+
if (hasParsedOption(parsed, "-d", "--delete")) {
|
|
658
|
+
if (!names.length) return void 0;
|
|
659
|
+
return {
|
|
660
|
+
changes: tagsChanges,
|
|
661
|
+
refs: names.map((n) => `refs/tags/${n}`)
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
if (names.length !== 1 && names.length !== 2) return void 0;
|
|
665
|
+
return {
|
|
666
|
+
changes: tagsChanges,
|
|
667
|
+
refs: [`refs/tags/${names[0]}`]
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
/** The `worktree` subcommands other than `add` and `list`; none writes a branch */
|
|
671
|
+
const worktreeEditSubcommands = /* @__PURE__ */ new Set([
|
|
672
|
+
"remove",
|
|
673
|
+
"prune",
|
|
674
|
+
"lock",
|
|
675
|
+
"unlock",
|
|
676
|
+
"move",
|
|
677
|
+
"repair"
|
|
678
|
+
]);
|
|
679
|
+
const worktreeAddFlags = /* @__PURE__ */ new Set([
|
|
680
|
+
"-f",
|
|
681
|
+
"--force",
|
|
682
|
+
"-d",
|
|
683
|
+
"--detach",
|
|
684
|
+
"--checkout",
|
|
685
|
+
"--no-checkout",
|
|
686
|
+
"--lock",
|
|
687
|
+
"-q",
|
|
688
|
+
"--quiet",
|
|
689
|
+
"--track",
|
|
690
|
+
"--no-track",
|
|
691
|
+
"--guess-remote",
|
|
692
|
+
"--no-guess-remote",
|
|
693
|
+
"--orphan"
|
|
694
|
+
]);
|
|
695
|
+
const worktreeAddValued = /* @__PURE__ */ new Set([
|
|
696
|
+
"-b",
|
|
697
|
+
"-B",
|
|
698
|
+
"--reason"
|
|
699
|
+
]);
|
|
700
|
+
/**
|
|
701
|
+
* `worktree add` is the only subcommand that can create a branch: the `-b`/`-B` one it names or, without either, one
|
|
702
|
+
* git DWIMs from a short commit-ish (or from the path, given none), which argv can't name. A detached or full-SHA add
|
|
703
|
+
* creates none.
|
|
704
|
+
*/
|
|
705
|
+
function getWorktreeChanges(args) {
|
|
706
|
+
const [subcommand, ...rest] = args;
|
|
707
|
+
if (subcommand != null && worktreeEditSubcommands.has(subcommand)) return { changes: worktreeOnlyChanges };
|
|
708
|
+
if (subcommand !== "add") return void 0;
|
|
709
|
+
const parsed = parseArgs(rest, worktreeAddFlags, worktreeAddValued);
|
|
710
|
+
if (parsed == null) return void 0;
|
|
711
|
+
let branch;
|
|
712
|
+
let detach = false;
|
|
713
|
+
let orphan = false;
|
|
714
|
+
let track;
|
|
715
|
+
for (const [option, value] of parsed.options) switch (option) {
|
|
716
|
+
case "-b":
|
|
717
|
+
case "-B":
|
|
718
|
+
branch = value;
|
|
719
|
+
break;
|
|
720
|
+
case "-d":
|
|
721
|
+
case "--detach":
|
|
722
|
+
detach = true;
|
|
723
|
+
break;
|
|
724
|
+
case "--orphan":
|
|
725
|
+
orphan = true;
|
|
726
|
+
break;
|
|
727
|
+
case "--track":
|
|
728
|
+
track = true;
|
|
729
|
+
break;
|
|
730
|
+
case "--no-track": track = false;
|
|
731
|
+
}
|
|
732
|
+
const [path, commitish, ...extra] = parsed.positionals;
|
|
733
|
+
if (orphan || path == null || extra.length) return void 0;
|
|
734
|
+
if (branch) {
|
|
735
|
+
if (!isLiteralBranchName(branch)) return void 0;
|
|
736
|
+
const refs = [`refs/heads/${branch}`];
|
|
737
|
+
if (track === true || mayWriteTracking(commitish, track === false)) return {
|
|
738
|
+
changes: worktreeTrackingChanges,
|
|
739
|
+
refs,
|
|
740
|
+
configConfinedToRefs: true
|
|
741
|
+
};
|
|
742
|
+
return {
|
|
743
|
+
changes: worktreeChanges,
|
|
744
|
+
refs
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
if (detach || commitish != null && fullShaRegex.test(commitish)) return { changes: worktreeOnlyChanges };
|
|
748
|
+
}
|
|
749
|
+
const checkoutFlags = /* @__PURE__ */ new Set([
|
|
750
|
+
"-q",
|
|
751
|
+
"--quiet",
|
|
752
|
+
"-f",
|
|
753
|
+
"--force",
|
|
754
|
+
"--detach",
|
|
755
|
+
"--no-track",
|
|
756
|
+
"-m",
|
|
757
|
+
"--merge",
|
|
758
|
+
"--progress",
|
|
759
|
+
"--no-progress",
|
|
760
|
+
"--ignore-other-worktrees",
|
|
761
|
+
"--overwrite-ignore",
|
|
762
|
+
"--no-overwrite-ignore",
|
|
763
|
+
"--recurse-submodules",
|
|
764
|
+
"--no-recurse-submodules"
|
|
765
|
+
]);
|
|
766
|
+
const checkoutValued = /* @__PURE__ */ new Set(["-b", "-B"]);
|
|
767
|
+
const switchFlags = /* @__PURE__ */ new Set([
|
|
768
|
+
...checkoutFlags,
|
|
769
|
+
"-d",
|
|
770
|
+
"--discard-changes"
|
|
771
|
+
]);
|
|
772
|
+
const switchValued = /* @__PURE__ */ new Set([
|
|
773
|
+
"-c",
|
|
774
|
+
"-C",
|
|
775
|
+
"--create",
|
|
776
|
+
"--force-create"
|
|
777
|
+
]);
|
|
778
|
+
/**
|
|
779
|
+
* `checkout`/`switch` moves no existing branch, so the only shared ref it writes is one it creates: the `-b`/`-c` one
|
|
780
|
+
* it names, or the one git DWIMs from a remote branch named like its lone argument. A detached or full-SHA checkout
|
|
781
|
+
* creates none, and a pathspec checkout writes only the index and working tree.
|
|
782
|
+
*/
|
|
783
|
+
function getCheckoutChanges(verb, args) {
|
|
784
|
+
const parsed = verb === "switch" ? parseArgs(args, switchFlags, switchValued) : parseArgs(args, checkoutFlags, checkoutValued);
|
|
785
|
+
if (parsed == null) return void 0;
|
|
786
|
+
let branch;
|
|
787
|
+
let detach = false;
|
|
788
|
+
let noTrack = false;
|
|
789
|
+
for (const [option, value] of parsed.options) if (value != null) branch = value;
|
|
790
|
+
else if (option === "-d" || option === "--detach") detach = true;
|
|
791
|
+
else if (option === "--no-track") noTrack = true;
|
|
792
|
+
const { positionals, dashDash } = parsed;
|
|
793
|
+
const revs = dashDash == null ? positionals : positionals.slice(0, dashDash);
|
|
794
|
+
const hasPaths = dashDash != null && dashDash < positionals.length;
|
|
795
|
+
if (branch != null) {
|
|
796
|
+
if (hasPaths || revs.length > 1 || !isLiteralBranchName(branch)) return void 0;
|
|
797
|
+
const refs = [`refs/heads/${branch}`];
|
|
798
|
+
if (mayWriteTracking(revs[0], noTrack)) return {
|
|
799
|
+
changes: checkoutTrackingChanges,
|
|
800
|
+
refs,
|
|
801
|
+
configConfinedToRefs: true
|
|
802
|
+
};
|
|
803
|
+
return {
|
|
804
|
+
changes: checkoutChanges,
|
|
805
|
+
refs
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
if (hasPaths) return verb === "checkout" ? { changes: indexChanges } : void 0;
|
|
809
|
+
if (revs.length > 1) return void 0;
|
|
810
|
+
if (detach) return { changes: headIndexChanges };
|
|
811
|
+
const [rev] = revs;
|
|
812
|
+
if (rev == null || rev === "-" || rev.startsWith("@{-")) return void 0;
|
|
813
|
+
if (fullShaRegex.test(rev)) return { changes: headIndexChanges };
|
|
814
|
+
return {
|
|
815
|
+
changes: checkoutTrackingChanges,
|
|
816
|
+
refs: [`refs/heads/${rev}`],
|
|
817
|
+
configConfinedToRefs: true
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* A stash write moves only `refs/stash` (or its reflog, the rest of the list), save `branch`, which also creates the
|
|
822
|
+
* branch it names from a commit and checks it out; `apply` moves no ref
|
|
823
|
+
*/
|
|
824
|
+
function getStashChanges(subcommand, rest) {
|
|
825
|
+
switch (subcommand) {
|
|
826
|
+
case "branch": {
|
|
827
|
+
const name = rest[1];
|
|
828
|
+
if (!name || name.startsWith("-") || !isLiteralBranchName(name)) return void 0;
|
|
829
|
+
return {
|
|
830
|
+
changes: stashBranchChanges,
|
|
831
|
+
refs: [`refs/heads/${name}`, ...stashRefs]
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
case void 0:
|
|
835
|
+
case "push":
|
|
836
|
+
case "save":
|
|
837
|
+
case "pop": return {
|
|
838
|
+
changes: stashIndexChanges,
|
|
839
|
+
refs: stashRefs
|
|
840
|
+
};
|
|
841
|
+
case "drop":
|
|
842
|
+
case "clear":
|
|
843
|
+
case "store": return {
|
|
844
|
+
changes: stashChanges,
|
|
845
|
+
refs: stashRefs
|
|
846
|
+
};
|
|
847
|
+
case "apply": return { changes: indexChanges };
|
|
848
|
+
default: return subcommand.startsWith("-") ? {
|
|
849
|
+
changes: stashIndexChanges,
|
|
850
|
+
refs: stashRefs
|
|
851
|
+
} : void 0;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
/** A `reset` given paths, or `-p`, restores index entries and moves no ref */
|
|
855
|
+
function isPathspecReset(args) {
|
|
856
|
+
const dashDash = args.indexOf("--");
|
|
857
|
+
if (dashDash >= 0 && dashDash < args.length - 1) return true;
|
|
858
|
+
return args.some((arg) => arg === "-p" || arg === "--patch" || arg === "--pathspec-from-file" || arg.startsWith("--pathspec-from-file="));
|
|
859
|
+
}
|
|
860
|
+
const falseConfigValues = /* @__PURE__ */ new Set([
|
|
861
|
+
"false",
|
|
862
|
+
"no",
|
|
863
|
+
"off",
|
|
864
|
+
"0",
|
|
865
|
+
""
|
|
866
|
+
]);
|
|
867
|
+
/**
|
|
868
|
+
* The `rebase` options that take the refs it writes out of argv's hands: the update-refs list was fixed when the
|
|
869
|
+
* rebase started, whatever this argv says, an interactive or `--exec` todo list can write any ref, and `--update-refs`
|
|
870
|
+
* spelled any other way than in full turns update-refs on
|
|
871
|
+
*/
|
|
872
|
+
const unprovableRebaseOptions = [
|
|
873
|
+
"--continue",
|
|
874
|
+
"--skip",
|
|
875
|
+
"--edit-todo",
|
|
876
|
+
"--interactive",
|
|
877
|
+
"--exec",
|
|
878
|
+
"--update-refs"
|
|
879
|
+
];
|
|
880
|
+
/**
|
|
881
|
+
* Whether `arg` is one of {@link unprovableRebaseOptions}, `-i` or `-x`, however git lets it be written: any unique
|
|
882
|
+
* prefix of a long option, before any `=<value>` (`--cont`, `--update-ref`, `--exe=make`), and short ones bundled
|
|
883
|
+
* (`-ri`, `-xmake`)
|
|
884
|
+
*/
|
|
885
|
+
function isUnprovableRebaseOption(arg) {
|
|
886
|
+
if (arg.startsWith("--")) {
|
|
887
|
+
const name = arg.split("=", 1)[0];
|
|
888
|
+
return name.length > 2 && unprovableRebaseOptions.some((o) => o.startsWith(name));
|
|
889
|
+
}
|
|
890
|
+
return arg.length > 1 && arg.startsWith("-") && /[ix]/.test(arg.slice(1));
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Whether a `rebase` moves only the branch being rebased. Update-refs would rewrite every branch in the rebased range,
|
|
894
|
+
* so it must be provably off: the last of `--update-refs`/`--no-update-refs`, else the last `-c rebase.updateRefs`
|
|
895
|
+
* before the verb. An interactive or `--exec` rebase can write any ref from its todo list. Git writes the listed refs
|
|
896
|
+
* only when the rebase finishes, so an abort or quit moves none.
|
|
897
|
+
*/
|
|
898
|
+
function isHeadBranchRebase(command) {
|
|
899
|
+
let off;
|
|
900
|
+
for (const arg of command.rest) {
|
|
901
|
+
if (arg === "--" || arg === "--end-of-options") break;
|
|
902
|
+
if (arg === "--abort" || arg === "--quit") return true;
|
|
903
|
+
if (arg === "--no-update-refs") off = true;
|
|
904
|
+
else if (arg === "--update-refs") off = false;
|
|
905
|
+
else if (isUnprovableRebaseOption(arg)) return false;
|
|
906
|
+
}
|
|
907
|
+
if (off != null) return off;
|
|
908
|
+
for (const config of command.configs ?? []) {
|
|
909
|
+
const separator = config.indexOf("=");
|
|
910
|
+
if ((separator < 0 ? config : config.slice(0, separator)).toLowerCase() !== "rebase.updaterefs") continue;
|
|
911
|
+
off = separator >= 0 && falseConfigValues.has(config.slice(separator + 1).toLowerCase());
|
|
912
|
+
}
|
|
913
|
+
return off ?? false;
|
|
914
|
+
}
|
|
915
|
+
/** Verbs that move only the branch HEAD is on, or none from a detached HEAD */
|
|
916
|
+
const headBranchVerbs = /* @__PURE__ */ new Set([
|
|
917
|
+
"commit",
|
|
918
|
+
"merge",
|
|
919
|
+
"cherry-pick",
|
|
920
|
+
"revert",
|
|
921
|
+
"am"
|
|
922
|
+
]);
|
|
923
|
+
/**
|
|
924
|
+
* What argv proves more precisely than {@link changesByVerb}: the refs a write moved, or fewer kinds. `undefined`
|
|
925
|
+
* keeps the verb's table entry; `null` means the write moved nothing cached.
|
|
926
|
+
*/
|
|
927
|
+
function getNamedChanges(command) {
|
|
928
|
+
switch (command.verb) {
|
|
929
|
+
case "update-ref": return getUpdateRefChanges(command.rest);
|
|
930
|
+
case "branch": return getBranchChanges(command.rest);
|
|
931
|
+
case "tag": return getTagChanges(command.rest);
|
|
932
|
+
case "worktree": return getWorktreeChanges(command.rest);
|
|
933
|
+
case "checkout":
|
|
934
|
+
case "switch": return getCheckoutChanges(command.verb, command.rest);
|
|
935
|
+
case "stash": return getStashChanges(command.subcommand, command.rest);
|
|
936
|
+
case "reset":
|
|
937
|
+
if (isPathspecReset(command.rest)) return { changes: indexChanges };
|
|
938
|
+
return {
|
|
939
|
+
changes: changesByVerb.reset,
|
|
940
|
+
movesHeadBranch: true
|
|
941
|
+
};
|
|
942
|
+
case "rebase":
|
|
943
|
+
if (!isHeadBranchRebase(command)) return void 0;
|
|
944
|
+
return {
|
|
945
|
+
changes: changesByVerb.rebase,
|
|
946
|
+
movesHeadBranch: true
|
|
947
|
+
};
|
|
948
|
+
default:
|
|
949
|
+
if (!headBranchVerbs.has(command.verb)) return void 0;
|
|
950
|
+
return {
|
|
951
|
+
changes: changesByVerb[command.verb],
|
|
952
|
+
movesHeadBranch: true
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
}
|
|
415
956
|
/**
|
|
416
957
|
* Classifies a raw git argv for the notification `git.run`'s `notify: 'infer'` applies after the run
|
|
417
|
-
* settles. Returns `undefined` for a read-only
|
|
418
|
-
* the {@link RepositoryChange} kinds
|
|
419
|
-
* those kinds every worktree sees.
|
|
958
|
+
* settles. Returns `undefined` for a command with nothing to announce — read-only, object-only, or a write to a
|
|
959
|
+
* ref nothing cached reads — otherwise the {@link RepositoryChange} kinds it announces, the refs it names when argv
|
|
960
|
+
* proves them, the `-C` re-scope target (if any), and which of those kinds every worktree sees.
|
|
420
961
|
*/
|
|
421
962
|
function getChangesForCommand(args) {
|
|
422
963
|
const command = leadingCommand(args);
|
|
423
964
|
if (command.readOnly || objectOnlyVerbs.has(command.verb)) return void 0;
|
|
424
|
-
|
|
425
|
-
if (
|
|
426
|
-
|
|
427
|
-
|
|
965
|
+
const named = getNamedChanges(command);
|
|
966
|
+
if (named === null) return void 0;
|
|
967
|
+
let changes = named?.changes;
|
|
968
|
+
if (changes == null) {
|
|
969
|
+
if (command.verb === "worktree") changes = worktreeChanges;
|
|
970
|
+
else if (command.verb === "branch" && hasOption(command.rest, branchConfigOptions)) changes = branchConfigChanges;
|
|
971
|
+
else changes = changesByVerb[command.verb] ?? emptyChanges;
|
|
972
|
+
}
|
|
428
973
|
return {
|
|
974
|
+
...named,
|
|
429
975
|
changes,
|
|
430
976
|
pathOverride: command.pathOverride,
|
|
431
977
|
sharedChanges: changes.filter((c) => worktreeSharedChanges.has(c))
|