@gitkraken/core-gitlens 0.8.0 → 0.9.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +43 -1
  2. package/dist/git/cache.d.ts +11 -0
  3. package/dist/git/cache.js +26 -3
  4. package/dist/git/cache.js.map +1 -1
  5. package/dist/git/context.d.ts +3 -2
  6. package/dist/git/context.js.map +1 -1
  7. package/dist/git/features.d.ts +1 -1
  8. package/dist/git/features.js +1 -0
  9. package/dist/git/features.js.map +1 -1
  10. package/dist/git/models/reflog.d.ts +6 -0
  11. package/dist/git/models/reflog.js.map +1 -1
  12. package/dist/git/models/revision.d.ts +0 -1
  13. package/dist/git/models/revision.js +1 -2
  14. package/dist/git/models/revision.js.map +1 -1
  15. package/dist/git/providers/commits.d.ts +20 -1
  16. package/dist/git/providers/operations.d.ts +13 -0
  17. package/dist/git/providers/provider.d.ts +14 -4
  18. package/dist/git/providers/refs.d.ts +10 -0
  19. package/dist/git/providers/revision.d.ts +2 -0
  20. package/dist/git/providers/staging.d.ts +2 -1
  21. package/dist/git/providers/stash.d.ts +3 -2
  22. package/dist/git/providers/status.d.ts +4 -0
  23. package/dist/git/run.types.d.ts +16 -0
  24. package/dist/git-cli/cliGitProvider.d.ts +24 -3
  25. package/dist/git-cli/cliGitProvider.js +79 -17
  26. package/dist/git-cli/cliGitProvider.js.map +1 -1
  27. package/dist/git-cli/exec/commandClassifier.d.ts +31 -0
  28. package/dist/git-cli/exec/commandClassifier.js +391 -0
  29. package/dist/git-cli/exec/commandClassifier.js.map +1 -0
  30. package/dist/git-cli/exec/git.d.ts +24 -3
  31. package/dist/git-cli/exec/git.js +116 -12
  32. package/dist/git-cli/exec/git.js.map +1 -1
  33. package/dist/git-cli/parsers/reflogParser.d.ts +7 -0
  34. package/dist/git-cli/parsers/reflogParser.js +11 -1
  35. package/dist/git-cli/parsers/reflogParser.js.map +1 -1
  36. package/dist/git-cli/providers/branches.js +16 -14
  37. package/dist/git-cli/providers/branches.js.map +1 -1
  38. package/dist/git-cli/providers/commits.d.ts +4 -2
  39. package/dist/git-cli/providers/commits.js +55 -4
  40. package/dist/git-cli/providers/commits.js.map +1 -1
  41. package/dist/git-cli/providers/diff.js +4 -4
  42. package/dist/git-cli/providers/diff.js.map +1 -1
  43. package/dist/git-cli/providers/operations.d.ts +12 -0
  44. package/dist/git-cli/providers/operations.js +26 -3
  45. package/dist/git-cli/providers/operations.js.map +1 -1
  46. package/dist/git-cli/providers/patch.js +2 -5
  47. package/dist/git-cli/providers/patch.js.map +1 -1
  48. package/dist/git-cli/providers/refs.d.ts +5 -0
  49. package/dist/git-cli/providers/refs.js +26 -1
  50. package/dist/git-cli/providers/refs.js.map +1 -1
  51. package/dist/git-cli/providers/remotes.js +3 -2
  52. package/dist/git-cli/providers/remotes.js.map +1 -1
  53. package/dist/git-cli/providers/revision.d.ts +2 -0
  54. package/dist/git-cli/providers/revision.js +11 -0
  55. package/dist/git-cli/providers/revision.js.map +1 -1
  56. package/dist/git-cli/providers/staging.d.ts +2 -1
  57. package/dist/git-cli/providers/staging.js +12 -7
  58. package/dist/git-cli/providers/staging.js.map +1 -1
  59. package/dist/git-cli/providers/stash.d.ts +3 -2
  60. package/dist/git-cli/providers/stash.js +34 -22
  61. package/dist/git-cli/providers/stash.js.map +1 -1
  62. package/dist/git-cli/providers/status.d.ts +2 -0
  63. package/dist/git-cli/providers/status.js +27 -9
  64. package/dist/git-cli/providers/status.js.map +1 -1
  65. package/dist/plus/git-github/api/github.d.ts +13 -0
  66. package/dist/plus/git-github/api/github.js +36 -6
  67. package/dist/plus/git-github/api/github.js.map +1 -1
  68. package/dist/plus/git-github/providers/github/commits.d.ts +4 -2
  69. package/dist/plus/git-github/providers/github/commits.js +3 -1
  70. package/dist/plus/git-github/providers/github/commits.js.map +1 -1
  71. package/dist/plus/git-github/providers/github/revision.d.ts +1 -0
  72. package/dist/plus/git-github/providers/github/revision.js +5 -0
  73. package/dist/plus/git-github/providers/github/revision.js.map +1 -1
  74. package/dist/plus/integrations/collectionMetadata.d.ts +6 -0
  75. package/dist/plus/integrations/collectionMetadata.js +7 -4
  76. package/dist/plus/integrations/collectionMetadata.js.map +1 -1
  77. package/dist/plus/integrations/models/gitHostIntegration.d.ts +13 -0
  78. package/dist/plus/integrations/models/gitHostIntegration.js +19 -0
  79. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  80. package/dist/plus/integrations/providers/github.d.ts +16 -1
  81. package/dist/plus/integrations/providers/github.js +39 -1
  82. package/dist/plus/integrations/providers/github.js.map +1 -1
  83. package/dist/plus/integrations/reads/resolveRepository.js +26 -5
  84. package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
  85. package/docs/integrations.md +22 -5
  86. package/docs/kepler-git-write-api-parity.md +140 -14
  87. package/package.json +1 -1
@@ -4,13 +4,15 @@ import { getDurationMilliseconds, hrtime } from "../../utils/hrtime.js";
4
4
  import { Logger as _logger } from "../../utils/logger.js";
5
5
  import { formatLoggableScopeBlock } from "../../utils/logger.scoped.js";
6
6
  import { defer } from "../../utils/promise.js";
7
- import { dirname, isAbsolute, joinPaths, normalizePath } from "../../utils/path.js";
7
+ import { arePathsEqual, dirname as dirname$1, isAbsolute as isAbsolute$1, isDescendant, joinPaths, normalizePath } from "../../utils/path.js";
8
8
  import { GitWarnings, WorkspaceUntrustedError } from "../../git/errors.js";
9
9
  import { gitFeaturesByVersion } from "../../git/features.js";
10
10
  import { CancelledRunError, RunError, fsExists, runSpawn } from "../../utils/env/node/exec.js";
11
11
  import { compare, fromString } from "../../utils/version.js";
12
+ import { getChangesForCommand, leadingCommand } from "./commandClassifier.js";
12
13
  import { EventLoopMonitor } from "./eventLoopMonitor.js";
13
14
  import { GitQueue, getPrimaryGitCommand, inferGitCommandPriority } from "./gitQueue.js";
15
+ import { resolve } from "path";
14
16
  import { spawn } from "child_process";
15
17
  import * as process from "process";
16
18
  //#region ../git-cli/src/exec/git.ts
@@ -364,6 +366,46 @@ function classifySearchError(ex) {
364
366
  };
365
367
  if (/unknown revision/i.test(stderr)) return { reason: "invalidRef" };
366
368
  }
369
+ /**
370
+ * Keys dropped from the INHERITED `process.env` before it seeds the base git environment. git hands these
371
+ * to anything it launches (a hook, `rebase -x`, an editor invoked as `GIT_EDITOR`) to describe the command
372
+ * it is running; a host started that way would otherwise run every command against THAT repository with
373
+ * THAT command's settings, whatever `cwd` we pass. None is worth keeping when set deliberately either: a
374
+ * `GIT_DIR` would point every repository the host opens at one. `options.env`, `options.getEnvironment()`
375
+ * and a per-call `env` are merged in afterwards, so any of them can still set these keys (e.g. a temporary
376
+ * `GIT_INDEX_FILE` for a call).
377
+ */
378
+ const inheritedEnvKeysToDrop = /* @__PURE__ */ new Set([
379
+ "GIT_DIR",
380
+ "GIT_WORK_TREE",
381
+ "GIT_INDEX_FILE",
382
+ "GIT_COMMON_DIR",
383
+ "GIT_OBJECT_DIRECTORY",
384
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
385
+ "GIT_NAMESPACE",
386
+ "GIT_CONFIG_PARAMETERS",
387
+ "GIT_EXEC_PATH",
388
+ "GIT_PREFIX",
389
+ "GIT_REFLOG_ACTION"
390
+ ]);
391
+ /**
392
+ * Dropped only when git launched the host, which it always marks by exporting `GIT_EXEC_PATH`: an editor
393
+ * opened for `commit --amend` gets that commit's author and date, while an identity set deliberately (e.g.
394
+ * by direnv) must still reach our commits.
395
+ */
396
+ const inheritedIdentityKeysToDrop = /* @__PURE__ */ new Set([
397
+ "GIT_AUTHOR_NAME",
398
+ "GIT_AUTHOR_EMAIL",
399
+ "GIT_AUTHOR_DATE",
400
+ "GIT_COMMITTER_NAME",
401
+ "GIT_COMMITTER_EMAIL",
402
+ "GIT_COMMITTER_DATE"
403
+ ]);
404
+ /** `process.env` with {@link inheritedEnvKeysToDrop} removed, and {@link inheritedIdentityKeysToDrop} when git launched us. */
405
+ function getInheritedEnv() {
406
+ const launchedByGit = process.env.GIT_EXEC_PATH != null;
407
+ return Object.fromEntries(Object.entries(process.env).filter(([key]) => !inheritedEnvKeysToDrop.has(key) && !(launchedByGit && inheritedIdentityKeysToDrop.has(key))));
408
+ }
367
409
  const emptyArray = Object.freeze([]);
368
410
  const emptyObj = Object.freeze({});
369
411
  const trailingNewlineRegex = /[\r|\n]+$/;
@@ -380,25 +422,37 @@ var Git = class {
380
422
  _eventLoopMonitor = new EventLoopMonitor();
381
423
  /** Count of git commands currently in flight, across both the exec and streaming paths */
382
424
  _activeCommandCount = 0;
383
- /** Cached base environment: process.env + static options.env + GCM/LC_ALL vars */
425
+ /** Cached base environment: process.env (minus {@link inheritedEnvKeysToDrop}) + static options.env + GCM/LC_ALL vars */
384
426
  _baseEnv;
385
427
  /** Cached full environment: base + dynamic getEnvironment() result */
386
428
  _fullEnv;
387
429
  /** Last dynamic env reference, used to detect when getEnvironment() returns a new object */
388
430
  _lastDynamicEnv;
431
+ /** Bound via {@link bindChangeNotifier}; announces a `notify`-flagged run's changes. No-op until bound. */
432
+ _changeNotifier;
389
433
  constructor(_locator, options = {}) {
390
434
  this._locator = _locator;
391
435
  this.options = options;
392
436
  this._queue = new GitQueue(options.queue, { onSlowQueue: options.hooks?.onSlowQueue });
393
437
  }
394
438
  /**
439
+ * Binds the callback a `notify`-flagged {@link run} calls once it settles. `CliGitProvider` wires this
440
+ * to its own `notifyChanged` from its constructor — including when it's handed a pre-built `Git` via
441
+ * `options.git`, so a host-supplied instance gets bound too. With nothing bound, `notify` is a no-op.
442
+ * `repoPaths` holds more than one path only for a `-C`-scoped shared-state verb: the `-C` target, then the
443
+ * caller's own `cwd`.
444
+ */
445
+ bindChangeNotifier(fn) {
446
+ this._changeNotifier = fn;
447
+ }
448
+ /**
395
449
  * Returns the base environment for git commands. Cached to avoid spreading
396
450
  * process.env (30-100+ keys) on every call. Includes static options.env and
397
451
  * GCM/LC_ALL vars but NOT dynamic getEnvironment() or per-call env overrides.
398
452
  */
399
453
  getBaseEnv() {
400
454
  return this._baseEnv ??= {
401
- ...process.env,
455
+ ...getInheritedEnv(),
402
456
  ...this.options.env ?? emptyObj,
403
457
  GCM_INTERACTIVE: "NEVER",
404
458
  GCM_PRESERVE_CREDS: "TRUE",
@@ -435,9 +489,20 @@ var Git = class {
435
489
  this._queue.dispose();
436
490
  this._eventLoopMonitor.dispose();
437
491
  }
438
- /** Clear pending commands (e.g. on cache reset) */
439
- clearPendingCommands() {
440
- this.pendingCommands.clear();
492
+ /**
493
+ * Drops in-flight commands so a later identical call spawns its own run instead of joining one that
494
+ * started before a change. With `repoPaths`, drops only runs whose `cwd` is one of them or inside one;
495
+ * without, drops every run.
496
+ */
497
+ clearPendingCommands(repoPaths) {
498
+ if (repoPaths == null) {
499
+ this.pendingCommands.clear();
500
+ return;
501
+ }
502
+ for (const [key, { cwd }] of this.pendingCommands) {
503
+ if (cwd == null) continue;
504
+ if (repoPaths.some((p) => arePathsEqual(cwd, p) || isDescendant(cwd, p))) this.pendingCommands.delete(key);
505
+ }
441
506
  }
442
507
  /** Marks one more git command as in flight; starts the event-loop monitor on the first. */
443
508
  beginActiveCommand() {
@@ -492,6 +557,14 @@ var Git = class {
492
557
  async run(options, ...args) {
493
558
  if (this.options.isTrusted?.() === false) throw new WorkspaceUntrustedError();
494
559
  const runArgs = args.filter((a) => a != null);
560
+ if (options.notify != null) try {
561
+ return await this.run({
562
+ ...options,
563
+ notify: void 0
564
+ }, ...args);
565
+ } finally {
566
+ this.applyNotify(options, runArgs);
567
+ }
495
568
  const gitCommand = `git ${runArgs.join(" ")}`;
496
569
  if (options.caching != null) {
497
570
  const cachePath = options.caching.commonPath ?? options.cwd;
@@ -511,6 +584,34 @@ var Git = class {
511
584
  }
512
585
  return this.runCore(options, runArgs, gitCommand);
513
586
  }
587
+ /**
588
+ * Applies `options.notify` once a {@link run} has settled — a no-op unless both `notify` and a
589
+ * {@link bindChangeNotifier bound} notifier are present, so an unflagged caller pays nothing.
590
+ */
591
+ applyNotify(options, args) {
592
+ const notify = options.notify;
593
+ if (notify == null || this._changeNotifier == null || options.cwd == null) return;
594
+ if (notify !== "infer") {
595
+ this._changeNotifier([this.resolveNotifyPath(options.cwd, leadingCommand(args).pathOverride)], notify);
596
+ return;
597
+ }
598
+ const command = getChangesForCommand(args);
599
+ if (command == null) return;
600
+ const ranIn = this.resolveNotifyPath(options.cwd, command.pathOverride);
601
+ const cwd = normalizePath(options.cwd);
602
+ if (command.pathOverride != null && cwd !== ranIn && (command.sharedChanges.length || !command.changes.length)) {
603
+ if (command.sharedChanges.length === command.changes.length) this._changeNotifier([ranIn, cwd], command.changes);
604
+ else {
605
+ this._changeNotifier([ranIn], command.changes);
606
+ this._changeNotifier([cwd], command.sharedChanges);
607
+ }
608
+ return;
609
+ }
610
+ this._changeNotifier([ranIn], command.changes);
611
+ }
612
+ resolveNotifyPath(cwd, pathOverride) {
613
+ return normalizePath(pathOverride != null ? resolve(cwd, pathOverride) : cwd);
614
+ }
514
615
  async runCore(options, args, gitCommand) {
515
616
  const start = hrtime();
516
617
  const startEpochMs = Date.now();
@@ -529,12 +630,15 @@ var Git = class {
529
630
  const cacheKey = `${correlationKey !== void 0 ? `${correlationKey}:` : ""}${unshared ? `${unsharedRunCounter.next()}:` : ""}${cancellation != null ? `${getAbortSignalId(cancellation)}:` : ""}${gitCommand}`;
530
631
  let execStart;
531
632
  let waiting;
532
- let promise = this.pendingCommands.get(cacheKey);
633
+ let promise = this.pendingCommands.get(cacheKey)?.promise;
533
634
  if (promise == null) {
534
635
  waiting = false;
535
636
  const deferred = defer();
536
637
  promise = deferred.promise;
537
- this.pendingCommands.set(cacheKey, promise);
638
+ this.pendingCommands.set(cacheKey, {
639
+ cwd: options.cwd,
640
+ promise
641
+ });
538
642
  args.unshift("-c", "core.quotepath=false", "-c", "color.ui=false", ...configs ?? emptyArray);
539
643
  if (process.platform === "win32") args.unshift("-c", "core.longpaths=true");
540
644
  if (cancellation != null) runOpts.cancellation = cancellation;
@@ -544,7 +648,7 @@ var Git = class {
544
648
  execStart = hrtime();
545
649
  return runSpawn(gitPath, args, encoding ?? "utf8", runOpts);
546
650
  }, cancellation).then(deferred.fulfill, (e) => deferred.cancel(e instanceof Error ? e : new Error(String(e)))).finally(() => {
547
- this.pendingCommands.delete(cacheKey);
651
+ if (this.pendingCommands.get(cacheKey)?.promise === deferred.promise) this.pendingCommands.delete(cacheKey);
548
652
  }).catch(() => {});
549
653
  } else {
550
654
  waiting = true;
@@ -750,10 +854,10 @@ var Git = class {
750
854
  }, "rev-parse", "--git-dir", "--git-common-dir");
751
855
  if (!result.stdout) return void 0;
752
856
  let [dotGitPath, commonDotGitPath] = result.stdout.split("\n").map((r) => r.trimStart());
753
- if (!isAbsolute(dotGitPath)) dotGitPath = joinPaths(cwd, dotGitPath);
857
+ if (!isAbsolute$1(dotGitPath)) dotGitPath = joinPaths(cwd, dotGitPath);
754
858
  dotGitPath = normalizePath(dotGitPath);
755
859
  if (commonDotGitPath) {
756
- if (!isAbsolute(commonDotGitPath)) commonDotGitPath = joinPaths(cwd, commonDotGitPath);
860
+ if (!isAbsolute$1(commonDotGitPath)) commonDotGitPath = joinPaths(cwd, commonDotGitPath);
757
861
  commonDotGitPath = normalizePath(commonDotGitPath);
758
862
  return {
759
863
  path: dotGitPath,
@@ -807,7 +911,7 @@ var Git = class {
807
911
  let exists = inDotGit ? false : await fsExists(cwd);
808
912
  if (!exists) {
809
913
  do {
810
- const parent = dirname(cwd);
914
+ const parent = dirname$1(cwd);
811
915
  if (parent === cwd || parent.length === 0) return emptyArray;
812
916
  cwd = parent;
813
917
  exists = await fsExists(cwd);