@ricsam/r5d-worker 0.0.86 → 0.0.88

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.
@@ -133,6 +133,29 @@ function git(cwd, args, action) {
133
133
  function tryGit(cwd, args) {
134
134
  return gitResult(cwd, args).exitCode === 0;
135
135
  }
136
+ async function gitResultAsync(cwd, args) {
137
+ const subprocess = Bun.spawn(gitCommandArgs(args), {
138
+ cwd,
139
+ stdin: "ignore",
140
+ stdout: "pipe",
141
+ stderr: "pipe",
142
+ env: (0, import_git_process_environment.workerGitProcessEnvironment)()
143
+ });
144
+ const [stdout, stderr, exitCode] = await Promise.all([
145
+ new Response(subprocess.stdout).text(),
146
+ new Response(subprocess.stderr).text(),
147
+ subprocess.exited
148
+ ]);
149
+ return { exitCode, stdout: stdout.trim(), stderr: stderr.trim() };
150
+ }
151
+ async function gitAsync(cwd, args, action) {
152
+ const result = await gitResultAsync(cwd, args);
153
+ if (result.exitCode !== 0) throw new Error(`${action}: ${result.stderr || result.stdout || `git exited ${result.exitCode}`}`);
154
+ return result.stdout;
155
+ }
156
+ async function tryGitAsync(cwd, args) {
157
+ return (await gitResultAsync(cwd, args)).exitCode === 0;
158
+ }
136
159
  function branchPath(projectRoot, branchName) {
137
160
  (0, import_managed_paths.validateManagedBranchName)(branchName);
138
161
  const result = import_node_path.default.join(projectRoot, ...branchName.split("/"));
@@ -192,6 +215,10 @@ function commonGitDirectory(checkoutPath) {
192
215
  const result = gitResult(checkoutPath, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
193
216
  return result.exitCode === 0 ? import_node_path.default.resolve(result.stdout) : null;
194
217
  }
218
+ async function commonGitDirectoryAsync(checkoutPath) {
219
+ const result = await gitResultAsync(checkoutPath, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
220
+ return result.exitCode === 0 ? import_node_path.default.resolve(result.stdout) : null;
221
+ }
195
222
  function hasLinkedProjectWorktreeLayout(input) {
196
223
  const primaryPath = branchPath(input.projectRoot, input.primaryBranchName);
197
224
  if (gitDirectoryKind(primaryPath) !== "directory") return false;
@@ -203,17 +230,19 @@ function hasLinkedProjectWorktreeLayout(input) {
203
230
  return gitDirectoryKind(checkoutPath) === "file" && commonGitDirectory(checkoutPath) === primaryCommonDir;
204
231
  });
205
232
  }
206
- function hasCompatibleLinkedProjectWorktreeLayout(input) {
233
+ async function hasCompatibleLinkedProjectWorktreeLayout(input) {
207
234
  const primaryPath = branchPath(input.projectRoot, input.primaryBranchName);
208
235
  if (gitDirectoryKind(primaryPath) !== "directory") return false;
209
- const primaryCommonDir = commonGitDirectory(primaryPath);
236
+ const primaryCommonDir = await commonGitDirectoryAsync(primaryPath);
210
237
  if (!primaryCommonDir) return false;
211
- return input.branches.every(({ branchName }) => {
212
- if (branchName === input.primaryBranchName) return true;
238
+ for (const { branchName } of input.branches) {
239
+ if (branchName === input.primaryBranchName) continue;
213
240
  const checkoutPath = branchPath(input.projectRoot, branchName);
214
241
  const kind = gitDirectoryKind(checkoutPath);
215
- return kind === null || kind === "file" && commonGitDirectory(checkoutPath) === primaryCommonDir;
216
- });
242
+ if (kind === null) continue;
243
+ if (kind !== "file" || await commonGitDirectoryAsync(checkoutPath) !== primaryCommonDir) return false;
244
+ }
245
+ return true;
217
246
  }
218
247
  function fsyncDirectory(directory) {
219
248
  const descriptor = import_node_fs.default.openSync(directory, "r");
@@ -627,24 +656,34 @@ function configureProjectBranchTracking(checkoutPath, branchName, originBranchNa
627
656
  `configure upstream branch for ${branchName}`
628
657
  );
629
658
  }
630
- function configureRepository(input) {
631
- if (tryGit(input.primaryPath, ["remote", "get-url", "origin"])) {
632
- git(input.primaryPath, ["remote", "set-url", "origin", input.originUrl], "configure project origin");
659
+ async function configureProjectBranchTrackingAsync(checkoutPath, branchName, originBranchName) {
660
+ (0, import_managed_paths.validateManagedBranchName)(branchName);
661
+ (0, import_managed_paths.validateManagedBranchName)(originBranchName);
662
+ await gitAsync(checkoutPath, ["config", "--local", `branch.${branchName}.remote`, "origin"], `configure origin for ${branchName}`);
663
+ await gitAsync(
664
+ checkoutPath,
665
+ ["config", "--local", `branch.${branchName}.merge`, `refs/heads/${originBranchName}`],
666
+ `configure upstream branch for ${branchName}`
667
+ );
668
+ }
669
+ async function configureRepository(input) {
670
+ if (await tryGitAsync(input.primaryPath, ["remote", "get-url", "origin"])) {
671
+ await gitAsync(input.primaryPath, ["remote", "set-url", "origin", input.originUrl], "configure project origin");
633
672
  } else {
634
- git(input.primaryPath, ["remote", "add", "origin", input.originUrl], "configure project origin");
673
+ await gitAsync(input.primaryPath, ["remote", "add", "origin", input.originUrl], "configure project origin");
635
674
  }
636
- tryGit(input.primaryPath, ["config", "--local", "--unset-all", "remote.origin.pushurl"]);
637
- git(input.primaryPath, ["config", "--local", "--replace-all", "credential.helper", ""], "reset project credential helpers");
638
- git(input.primaryPath, ["config", "--local", "credential.useHttpPath", "false"], "configure project credential path matching");
675
+ await tryGitAsync(input.primaryPath, ["config", "--local", "--unset-all", "remote.origin.pushurl"]);
676
+ await gitAsync(input.primaryPath, ["config", "--local", "--replace-all", "credential.helper", ""], "reset project credential helpers");
677
+ await gitAsync(input.primaryPath, ["config", "--local", "credential.useHttpPath", "false"], "configure project credential path matching");
639
678
  if (input.originCredentialUsername) {
640
- git(
679
+ await gitAsync(
641
680
  input.primaryPath,
642
681
  ["config", "--local", "--replace-all", (0, import_git_process_environment.gitCredentialUsernameConfigKey)(input.originUrl), input.originCredentialUsername],
643
682
  "configure project credential username"
644
683
  );
645
684
  }
646
685
  if (input.credentialHelper) {
647
- git(
686
+ await gitAsync(
648
687
  input.primaryPath,
649
688
  ["config", "--local", "--add", "credential.helper", input.credentialHelper],
650
689
  "configure project credential helper"
@@ -654,23 +693,23 @@ function configureRepository(input) {
654
693
  const name = input.gitIdentity.name.trim();
655
694
  const email = input.gitIdentity.email.trim();
656
695
  if (!name || !email) throw new Error("Git identity must include name and email");
657
- git(input.primaryPath, ["config", "--local", "user.name", name], "configure project Git user name");
658
- git(input.primaryPath, ["config", "--local", "user.email", email], "configure project Git user email");
696
+ await gitAsync(input.primaryPath, ["config", "--local", "user.name", name], "configure project Git user name");
697
+ await gitAsync(input.primaryPath, ["config", "--local", "user.email", email], "configure project Git user email");
659
698
  }
660
699
  if (input.primaryBranchName && input.defaultBranch && input.branches) {
661
- git(input.primaryPath, ["config", "--local", "push.default", "upstream"], "configure project push mapping");
700
+ await gitAsync(input.primaryPath, ["config", "--local", "push.default", "upstream"], "configure project push mapping");
662
701
  for (const { branchName } of input.branches) {
663
702
  const originBranchName = projectOriginBranchName({
664
703
  branchName,
665
704
  primaryBranchName: input.primaryBranchName,
666
705
  defaultBranch: input.defaultBranch
667
706
  });
668
- configureProjectBranchTracking(input.primaryPath, branchName, originBranchName);
707
+ await configureProjectBranchTrackingAsync(input.primaryPath, branchName, originBranchName);
669
708
  }
670
709
  }
671
710
  }
672
- function fetchProjectHeads(input) {
673
- const originFetch = gitResult(input.primaryPath, [
711
+ async function fetchProjectHeads(input) {
712
+ const originFetch = await gitResultAsync(input.primaryPath, [
674
713
  ...(0, import_git_process_environment.gitTransportSecurityArgs)(input.originUrl, input.credentialHelper, input.originCredentialUsername),
675
714
  "fetch",
676
715
  "--no-recurse-submodules",
@@ -681,7 +720,7 @@ function fetchProjectHeads(input) {
681
720
  if (originFetch.exitCode !== 0) {
682
721
  throw new Error(`Fetch project origin: ${originFetch.stderr || originFetch.stdout || `git exited ${originFetch.exitCode}`}`);
683
722
  }
684
- const mirrorFetch = gitResult(input.primaryPath, [
723
+ const mirrorFetch = await gitResultAsync(input.primaryPath, [
685
724
  ...(0, import_git_process_environment.gitTransportSecurityArgs)(input.mirrorUrl, input.credentialHelper, input.mirrorCredentialUsername),
686
725
  "fetch",
687
726
  "--no-recurse-submodules",
@@ -696,13 +735,13 @@ ${mirrorFetch.stdout}`
696
735
  throw new Error(`Fetch project head mirror: ${mirrorFetch.stderr || mirrorFetch.stdout || `git exited ${mirrorFetch.exitCode}`}`);
697
736
  }
698
737
  }
699
- function ensureCommitAvailable(input) {
700
- if (tryGit(input.primaryPath, ["cat-file", "-e", `${input.commitHash}^{commit}`])) return;
738
+ async function ensureCommitAvailable(input) {
739
+ if (await tryGitAsync(input.primaryPath, ["cat-file", "-e", `${input.commitHash}^{commit}`])) return;
701
740
  for (const [url, username] of [
702
741
  [input.mirrorUrl, input.mirrorCredentialUsername],
703
742
  [input.originUrl, input.originCredentialUsername]
704
743
  ]) {
705
- if (tryGit(input.primaryPath, [
744
+ if (await tryGitAsync(input.primaryPath, [
706
745
  ...(0, import_git_process_environment.gitTransportSecurityArgs)(url, input.credentialHelper, username),
707
746
  "fetch",
708
747
  "--no-recurse-submodules",
@@ -714,34 +753,34 @@ function ensureCommitAvailable(input) {
714
753
  }
715
754
  throw new Error(`Project base commit ${input.commitHash} is unavailable from origin and the canonical mirror`);
716
755
  }
717
- function seedForBranch(input) {
756
+ async function seedForBranch(input) {
718
757
  const mirrorRef = `refs/r5d/mirror/${input.branch.branchName}`;
719
- const mirrorHead = tryGit(input.primaryPath, ["show-ref", "--verify", "--quiet", mirrorRef]) ? git(input.primaryPath, ["rev-parse", mirrorRef], "resolve project mirror head") : null;
758
+ const mirrorHead = await tryGitAsync(input.primaryPath, ["show-ref", "--verify", "--quiet", mirrorRef]) ? await gitAsync(input.primaryPath, ["rev-parse", mirrorRef], "resolve project mirror head") : null;
720
759
  if (input.requireMirrorHead && !mirrorHead) {
721
760
  throw new Error(`Exact hidden project mirror head is missing for ${input.branch.branchName}`);
722
761
  }
723
762
  const seed = mirrorHead ?? input.branch.baseCommitHash;
724
- ensureCommitAvailable({ ...input, commitHash: seed });
763
+ await ensureCommitAvailable({ ...input, commitHash: seed });
725
764
  return { seed, mirrorHead };
726
765
  }
727
- function preserveHead(checkoutPath) {
728
- if (!tryGit(checkoutPath, ["rev-parse", "--verify", "HEAD"])) return;
729
- const head = git(checkoutPath, ["rev-parse", "HEAD"], "read pre-reconciliation project head");
730
- git(checkoutPath, ["update-ref", `refs/r5d/pre-connect/${head}`, head], "preserve pre-reconciliation project head");
766
+ async function preserveHead(checkoutPath) {
767
+ if (!await tryGitAsync(checkoutPath, ["rev-parse", "--verify", "HEAD"])) return;
768
+ const head = await gitAsync(checkoutPath, ["rev-parse", "HEAD"], "read pre-reconciliation project head");
769
+ await gitAsync(checkoutPath, ["update-ref", `refs/r5d/pre-connect/${head}`, head], "preserve pre-reconciliation project head");
731
770
  }
732
- function initializeLinkedLayout(input) {
771
+ async function initializeLinkedLayout(input) {
733
772
  for (const { branchName } of [...input.branches].sort((left, right) => right.branchName.length - left.branchName.length)) {
734
773
  import_node_fs.default.rmSync(branchPath(input.projectRoot, branchName), { recursive: true, force: true });
735
774
  }
736
775
  const primaryPath = branchPath(input.projectRoot, input.primaryBranchName);
737
776
  import_node_fs.default.mkdirSync(import_node_path.default.dirname(primaryPath), { recursive: true });
738
- git(void 0, ["init", `--initial-branch=${input.primaryBranchName}`, primaryPath], "initialize primary project clone");
739
- configureRepository({ ...input, primaryPath });
740
- fetchProjectHeads({ ...input, primaryPath });
777
+ await gitAsync(void 0, ["init", `--initial-branch=${input.primaryBranchName}`, primaryPath], "initialize primary project clone");
778
+ await configureRepository({ ...input, primaryPath });
779
+ await fetchProjectHeads({ ...input, primaryPath });
741
780
  const seeds = /* @__PURE__ */ new Map();
742
781
  const mirrorHeads = /* @__PURE__ */ new Map();
743
782
  for (const branch of input.branches) {
744
- const resolved = seedForBranch({
783
+ const resolved = await seedForBranch({
745
784
  ...input,
746
785
  primaryPath,
747
786
  branch,
@@ -750,63 +789,71 @@ function initializeLinkedLayout(input) {
750
789
  seeds.set(branch.branchName, resolved.seed);
751
790
  mirrorHeads.set(branch.branchName, resolved.mirrorHead);
752
791
  }
753
- git(
792
+ await gitAsync(
754
793
  primaryPath,
755
794
  ["checkout", "--no-recurse-submodules", "-B", input.primaryBranchName, seeds.get(input.primaryBranchName)],
756
795
  "anchor primary project worktree"
757
796
  );
758
797
  for (const branch of input.branches) {
759
798
  if (branch.branchName === input.primaryBranchName) continue;
760
- git(primaryPath, ["branch", "-f", branch.branchName, seeds.get(branch.branchName)], `anchor project branch ${branch.branchName}`);
799
+ await gitAsync(
800
+ primaryPath,
801
+ ["branch", "-f", branch.branchName, seeds.get(branch.branchName)],
802
+ `anchor project branch ${branch.branchName}`
803
+ );
761
804
  const checkoutPath = branchPath(input.projectRoot, branch.branchName);
762
805
  import_node_fs.default.mkdirSync(import_node_path.default.dirname(checkoutPath), { recursive: true });
763
- git(primaryPath, ["worktree", "add", "--force", checkoutPath, branch.branchName], `create linked worktree ${branch.branchName}`);
806
+ await gitAsync(primaryPath, ["worktree", "add", "--force", checkoutPath, branch.branchName], `create linked worktree ${branch.branchName}`);
764
807
  }
765
808
  return mirrorHeads;
766
809
  }
767
- function reconcileLinkedLayout(input) {
810
+ async function reconcileLinkedLayout(input) {
768
811
  const primaryPath = branchPath(input.projectRoot, input.primaryBranchName);
769
- configureRepository({ ...input, primaryPath });
770
- tryGit(primaryPath, ["worktree", "prune"]);
771
- fetchProjectHeads({ ...input, primaryPath });
812
+ await configureRepository({ ...input, primaryPath });
813
+ await tryGitAsync(primaryPath, ["worktree", "prune"]);
814
+ await fetchProjectHeads({ ...input, primaryPath });
772
815
  const mirrorHeads = /* @__PURE__ */ new Map();
773
816
  const mutatedBranches = /* @__PURE__ */ new Set();
774
817
  for (const branch of input.branches) {
775
818
  const checkoutPath = branchPath(input.projectRoot, branch.branchName);
776
- const resolved = seedForBranch({
819
+ const resolved = await seedForBranch({
777
820
  ...input,
778
821
  primaryPath,
779
822
  branch,
780
823
  requireMirrorHead: input.exactMirrorBranches?.has(branch.branchName)
781
824
  });
782
825
  mirrorHeads.set(branch.branchName, resolved.mirrorHead);
783
- if (branch.branchName === input.primaryBranchName || commonGitDirectory(checkoutPath) === commonGitDirectory(primaryPath)) {
784
- const currentHead = git(checkoutPath, ["rev-parse", "HEAD"], `resolve linked worktree head ${branch.branchName}`);
826
+ if (branch.branchName === input.primaryBranchName || await commonGitDirectoryAsync(checkoutPath) === await commonGitDirectoryAsync(primaryPath)) {
827
+ const currentHead = await gitAsync(checkoutPath, ["rev-parse", "HEAD"], `resolve linked worktree head ${branch.branchName}`);
785
828
  if (currentHead === resolved.seed) continue;
786
829
  input.beforeDestructiveMutation();
787
830
  mutatedBranches.add(branch.branchName);
788
- preserveHead(checkoutPath);
789
- git(checkoutPath, ["reset", "--hard", resolved.seed], `anchor linked worktree ${branch.branchName}`);
831
+ await preserveHead(checkoutPath);
832
+ await gitAsync(checkoutPath, ["reset", "--hard", resolved.seed], `anchor linked worktree ${branch.branchName}`);
790
833
  continue;
791
834
  }
792
835
  input.beforeDestructiveMutation();
793
836
  mutatedBranches.add(branch.branchName);
794
837
  import_node_fs.default.rmSync(checkoutPath, { recursive: true, force: true });
795
- git(primaryPath, ["branch", "-f", branch.branchName, resolved.seed], `anchor project branch ${branch.branchName}`);
838
+ await gitAsync(primaryPath, ["branch", "-f", branch.branchName, resolved.seed], `anchor project branch ${branch.branchName}`);
796
839
  import_node_fs.default.mkdirSync(import_node_path.default.dirname(checkoutPath), { recursive: true });
797
- git(primaryPath, ["worktree", "add", "--force", checkoutPath, branch.branchName], `create linked worktree ${branch.branchName}`);
840
+ await gitAsync(primaryPath, ["worktree", "add", "--force", checkoutPath, branch.branchName], `create linked worktree ${branch.branchName}`);
798
841
  }
799
842
  return { mirrorHeads, mutatedBranches };
800
843
  }
801
- function aheadBehind(checkoutPath, branchName, primaryBranchName, defaultBranch) {
844
+ async function aheadBehind(checkoutPath, branchName, primaryBranchName, defaultBranch) {
802
845
  const originBranchName = projectOriginBranchName({ branchName, primaryBranchName, defaultBranch });
803
846
  const originRef = `refs/remotes/origin/${originBranchName}`;
804
- if (!tryGit(checkoutPath, ["show-ref", "--verify", "--quiet", originRef])) return { ahead: null, behind: null };
805
- const output = git(checkoutPath, ["rev-list", "--left-right", "--count", `${originRef}...HEAD`], "measure project origin divergence");
847
+ if (!await tryGitAsync(checkoutPath, ["show-ref", "--verify", "--quiet", originRef])) return { ahead: null, behind: null };
848
+ const output = await gitAsync(
849
+ checkoutPath,
850
+ ["rev-list", "--left-right", "--count", `${originRef}...HEAD`],
851
+ "measure project origin divergence"
852
+ );
806
853
  const [behind, ahead] = output.split(/\s+/).map(Number);
807
854
  return Number.isFinite(ahead) && Number.isFinite(behind) ? { ahead, behind } : { ahead: null, behind: null };
808
855
  }
809
- function ensureProjectWorktrees(input) {
856
+ async function ensureProjectWorktrees(input) {
810
857
  const projectRoot = import_node_path.default.resolve(input.projectRoot);
811
858
  const defaultBranch = input.defaultBranch ?? input.primaryBranchName;
812
859
  assertNonOverlappingBranches(input.branches);
@@ -822,7 +869,7 @@ function ensureProjectWorktrees(input) {
822
869
  };
823
870
  try {
824
871
  import_node_fs.default.mkdirSync(projectRoot, { recursive: true });
825
- const linked = hasCompatibleLinkedProjectWorktreeLayout({
872
+ const linked = await hasCompatibleLinkedProjectWorktreeLayout({
826
873
  projectRoot,
827
874
  primaryBranchName: input.primaryBranchName,
828
875
  branches: input.branches
@@ -830,7 +877,7 @@ function ensureProjectWorktrees(input) {
830
877
  let mirrorHeads;
831
878
  let mutatedBranches;
832
879
  if (linked) {
833
- ({ mirrorHeads, mutatedBranches } = reconcileLinkedLayout({
880
+ ({ mirrorHeads, mutatedBranches } = await reconcileLinkedLayout({
834
881
  ...input,
835
882
  projectRoot,
836
883
  defaultBranch,
@@ -838,7 +885,7 @@ function ensureProjectWorktrees(input) {
838
885
  }));
839
886
  } else {
840
887
  ensureSnapshot();
841
- mirrorHeads = initializeLinkedLayout({ ...input, projectRoot, defaultBranch });
888
+ mirrorHeads = await initializeLinkedLayout({ ...input, projectRoot, defaultBranch });
842
889
  mutatedBranches = new Set(input.branches.map(({ branchName }) => branchName));
843
890
  }
844
891
  const preparedSnapshot = snapshot;
@@ -856,17 +903,19 @@ function ensureProjectWorktrees(input) {
856
903
  deletionMode: "git"
857
904
  });
858
905
  }
859
- const states = input.branches.map((branch) => {
906
+ const states = [];
907
+ for (const branch of input.branches) {
860
908
  const checkoutPath = branchPath(projectRoot, branch.branchName);
861
- const divergence = aheadBehind(checkoutPath, branch.branchName, input.primaryBranchName, defaultBranch);
862
- return {
909
+ const divergence = await aheadBehind(checkoutPath, branch.branchName, input.primaryBranchName, defaultBranch);
910
+ states.push({
863
911
  branchName: branch.branchName,
864
912
  branchPath: checkoutPath,
865
- head: git(checkoutPath, ["rev-parse", "HEAD"], `resolve project head ${branch.branchName}`),
913
+ head: await gitAsync(checkoutPath, ["rev-parse", "HEAD"], `resolve project head ${branch.branchName}`),
866
914
  mirrorHead: mirrorHeads.get(branch.branchName) ?? null,
867
915
  ...divergence
868
- };
869
- }).sort((left, right) => left.branchName.localeCompare(right.branchName));
916
+ });
917
+ }
918
+ states.sort((left, right) => left.branchName.localeCompare(right.branchName));
870
919
  if (snapshot) persistReconciledProjectAndConsumeSnapshot(snapshot);
871
920
  return states;
872
921
  } catch (error) {
@@ -882,14 +931,31 @@ function ensureProjectWorktrees(input) {
882
931
  throw error;
883
932
  }
884
933
  }
934
+ const WORKTREE_OPERATION_STATE_PATHS = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer"];
935
+ function perWorktreeGitDirectory(checkoutPath) {
936
+ const gitPath = import_node_path.default.join(checkoutPath, ".git");
937
+ let stat;
938
+ try {
939
+ stat = import_node_fs.default.lstatSync(gitPath);
940
+ } catch {
941
+ return null;
942
+ }
943
+ if (stat.isDirectory()) return gitPath;
944
+ if (!stat.isFile()) return null;
945
+ let pointer;
946
+ try {
947
+ pointer = import_node_fs.default.readFileSync(gitPath, "utf8");
948
+ } catch {
949
+ return null;
950
+ }
951
+ const match = /^gitdir:[ \t]*(.+?)[ \t\r\n]*$/m.exec(pointer);
952
+ if (!match?.[1]) return null;
953
+ return import_node_path.default.resolve(checkoutPath, match[1]);
954
+ }
885
955
  function projectWorktreeOperationInProgress(checkoutPath) {
886
- const gitPaths = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer"];
887
- return gitPaths.some((gitPath) => {
888
- const result = gitResult(checkoutPath, ["rev-parse", "--git-path", gitPath]);
889
- if (result.exitCode !== 0) return false;
890
- const resolved = import_node_path.default.isAbsolute(result.stdout) ? result.stdout : import_node_path.default.resolve(checkoutPath, result.stdout);
891
- return import_node_fs.default.existsSync(resolved);
892
- });
956
+ const gitDirectory = perWorktreeGitDirectory(checkoutPath);
957
+ if (!gitDirectory) return false;
958
+ return WORKTREE_OPERATION_STATE_PATHS.some((statePath) => import_node_fs.default.existsSync(import_node_path.default.join(gitDirectory, statePath)));
893
959
  }
894
960
  function createLinkedProjectBranch(input) {
895
961
  (0, import_managed_paths.validateManagedBranchName)(input.sourceBranchName);
@@ -1030,9 +1096,9 @@ function removeProjectWorktrees(input) {
1030
1096
  }
1031
1097
  import_node_fs.default.rmSync(projectRoot, { recursive: true, force: true });
1032
1098
  }
1033
- function deleteProjectMirrorBranch(input) {
1099
+ async function deleteProjectMirrorBranch(input) {
1034
1100
  (0, import_managed_paths.validateManagedBranchName)(input.branchName);
1035
- const deleted = gitResult(input.gitDirectory, [
1101
+ const deleted = await gitResultAsync(input.gitDirectory, [
1036
1102
  ...(0, import_git_process_environment.gitTransportSecurityArgs)(input.mirrorUrl, input.credentialHelper, input.credentialUsername),
1037
1103
  "push",
1038
1104
  "--no-recurse-submodules",
@@ -1046,23 +1112,23 @@ ${deleted.stdout}`)) {
1046
1112
  );
1047
1113
  }
1048
1114
  }
1049
- function pushProjectMirrorHeads(input) {
1115
+ async function pushProjectMirrorHeads(input) {
1050
1116
  const results = [];
1051
1117
  for (const branchName of [...input.branchNames].sort()) {
1052
1118
  if (input.onlyBranches && !input.onlyBranches.has(branchName)) continue;
1053
1119
  const checkoutPath = branchPath(input.projectRoot, branchName);
1054
- const head = input.publicationHeads ? input.publicationHeads.get(branchName) : git(checkoutPath, ["rev-parse", "HEAD"], `resolve mirror head for ${branchName}`);
1120
+ const head = input.publicationHeads ? input.publicationHeads.get(branchName) : await gitAsync(checkoutPath, ["rev-parse", "HEAD"], `resolve mirror head for ${branchName}`);
1055
1121
  if (!head || !/^[0-9a-f]{40}(?:[0-9a-f]{24})?$/.test(head)) {
1056
1122
  throw new Error(`Missing exact publication head for project branch ${branchName}`);
1057
1123
  }
1058
- if (!tryGit(checkoutPath, ["cat-file", "-e", `${head}^{commit}`])) {
1124
+ if (!await tryGitAsync(checkoutPath, ["cat-file", "-e", `${head}^{commit}`])) {
1059
1125
  throw new Error(`Exact publication head ${head} is unavailable for project branch ${branchName}`);
1060
1126
  }
1061
1127
  if (projectWorktreeOperationInProgress(checkoutPath)) {
1062
1128
  results.push({ branchName, head, pushed: false, reason: "git_operation_in_progress" });
1063
1129
  continue;
1064
1130
  }
1065
- git(
1131
+ await gitAsync(
1066
1132
  checkoutPath,
1067
1133
  [
1068
1134
  ...(0, import_git_process_environment.gitTransportSecurityArgs)(input.mirrorUrl, input.credentialHelper, input.credentialUsername),
@@ -1083,9 +1149,9 @@ function assertProjectMirrorHeadsPushed(results) {
1083
1149
  throw new Error(`Hidden project mirror publication skipped ${skipped.branchName}${skipped.reason ? ` (${skipped.reason})` : ""}`);
1084
1150
  }
1085
1151
  }
1086
- function observeProjectMirrorHeads(input) {
1152
+ async function observeProjectMirrorHeads(input) {
1087
1153
  const primaryPath = branchPath(input.projectRoot, input.primaryBranchName);
1088
- const fetch = gitResult(primaryPath, [
1154
+ const fetch = await gitResultAsync(primaryPath, [
1089
1155
  ...(0, import_git_process_environment.gitTransportSecurityArgs)(input.mirrorUrl, input.credentialHelper, input.credentialUsername),
1090
1156
  "fetch",
1091
1157
  "--no-recurse-submodules",
@@ -1095,50 +1161,63 @@ function observeProjectMirrorHeads(input) {
1095
1161
  ]);
1096
1162
  if (fetch.exitCode !== 0)
1097
1163
  throw new Error(`Refresh project head mirror: ${fetch.stderr || fetch.stdout || `git exited ${fetch.exitCode}`}`);
1098
- return [...input.branchNames].sort().map((branchName) => {
1164
+ const observations = [];
1165
+ for (const branchName of [...input.branchNames].sort()) {
1099
1166
  const checkoutPath = branchPath(input.projectRoot, branchName);
1100
- const previousHead = git(checkoutPath, ["rev-parse", "HEAD"], `resolve local project head ${branchName}`);
1167
+ const previousHead = await gitAsync(checkoutPath, ["rev-parse", "HEAD"], `resolve local project head ${branchName}`);
1101
1168
  const mirrorRef = `refs/r5d/mirror/${branchName}`;
1102
- const mirrorHead = tryGit(primaryPath, ["show-ref", "--verify", "--quiet", mirrorRef]) ? git(primaryPath, ["rev-parse", mirrorRef], `resolve refreshed mirror head ${branchName}`) : null;
1169
+ const mirrorHead = await tryGitAsync(primaryPath, ["show-ref", "--verify", "--quiet", mirrorRef]) ? await gitAsync(primaryPath, ["rev-parse", mirrorRef], `resolve refreshed mirror head ${branchName}`) : null;
1103
1170
  const shouldMove = Boolean(
1104
- mirrorHead && mirrorHead !== previousHead && (input.allowNonFastForward || tryGit(checkoutPath, ["merge-base", "--is-ancestor", previousHead, mirrorHead]))
1171
+ mirrorHead && mirrorHead !== previousHead && (input.allowNonFastForward || await tryGitAsync(checkoutPath, ["merge-base", "--is-ancestor", previousHead, mirrorHead]))
1105
1172
  );
1106
- return {
1173
+ observations.push({
1107
1174
  branchName,
1108
1175
  previousHead,
1109
1176
  mirrorHead,
1110
1177
  mode: input.allowNonFastForward ? "reseed" : "fast_forward",
1111
1178
  shouldMove
1112
- };
1113
- });
1179
+ });
1180
+ }
1181
+ return observations;
1114
1182
  }
1115
- function applyObservedProjectMirrorHeads(input) {
1116
- return [...input.observations].sort((left, right) => left.branchName.localeCompare(right.branchName)).map((observation) => {
1183
+ async function applyObservedProjectMirrorHeads(input) {
1184
+ const results = [];
1185
+ for (const observation of [...input.observations].sort((left, right) => left.branchName.localeCompare(right.branchName))) {
1117
1186
  const { branchName, mirrorHead } = observation;
1118
1187
  const checkoutPath = branchPath(input.projectRoot, branchName);
1119
- const currentHead = git(checkoutPath, ["rev-parse", "HEAD"], `resolve local project head ${branchName}`);
1188
+ const currentHead = await gitAsync(checkoutPath, ["rev-parse", "HEAD"], `resolve local project head ${branchName}`);
1120
1189
  if (!observation.shouldMove || !mirrorHead || currentHead === mirrorHead || currentHead !== observation.previousHead) {
1121
- return { branchName, previousHead: currentHead, mirrorHead, moved: false };
1190
+ results.push({ branchName, previousHead: currentHead, mirrorHead, moved: false });
1191
+ continue;
1122
1192
  }
1123
- if (!tryGit(checkoutPath, ["cat-file", "-e", `${mirrorHead}^{commit}`])) {
1193
+ if (!await tryGitAsync(checkoutPath, ["cat-file", "-e", `${mirrorHead}^{commit}`])) {
1124
1194
  throw new Error(`Observed hidden mirror head is no longer available for ${branchName}: ${mirrorHead}`);
1125
1195
  }
1126
1196
  const mutationAllowed = () => !projectWorktreeOperationInProgress(checkoutPath) && input.branchMutationAllowed?.(branchName, checkoutPath) !== false;
1127
1197
  if (!mutationAllowed()) {
1128
- return { branchName, previousHead: currentHead, mirrorHead, moved: false };
1198
+ results.push({ branchName, previousHead: currentHead, mirrorHead, moved: false });
1199
+ continue;
1129
1200
  }
1130
1201
  if (observation.mode === "reseed") {
1131
- preserveHead(checkoutPath);
1132
- if (!mutationAllowed()) return { branchName, previousHead: currentHead, mirrorHead, moved: false };
1202
+ await preserveHead(checkoutPath);
1203
+ if (!mutationAllowed()) {
1204
+ results.push({ branchName, previousHead: currentHead, mirrorHead, moved: false });
1205
+ continue;
1206
+ }
1133
1207
  }
1134
1208
  const action = observation.mode === "reseed" ? "reseed" : "fast-forward";
1135
- git(checkoutPath, ["read-tree", "--reset", mirrorHead], `${action} project index ${branchName}`);
1136
- git(checkoutPath, ["update-ref", `refs/heads/${branchName}`, mirrorHead, currentHead], `${action} project branch ${branchName}`);
1137
- return { branchName, previousHead: currentHead, mirrorHead, moved: true };
1138
- });
1209
+ await gitAsync(checkoutPath, ["read-tree", "--reset", mirrorHead], `${action} project index ${branchName}`);
1210
+ await gitAsync(
1211
+ checkoutPath,
1212
+ ["update-ref", `refs/heads/${branchName}`, mirrorHead, currentHead],
1213
+ `${action} project branch ${branchName}`
1214
+ );
1215
+ results.push({ branchName, previousHead: currentHead, mirrorHead, moved: true });
1216
+ }
1217
+ return results;
1139
1218
  }
1140
- function updateProjectHeadsFromMirror(input) {
1141
- const observations = observeProjectMirrorHeads(input);
1219
+ async function updateProjectHeadsFromMirror(input) {
1220
+ const observations = await observeProjectMirrorHeads(input);
1142
1221
  return applyObservedProjectMirrorHeads({
1143
1222
  projectRoot: input.projectRoot,
1144
1223
  primaryBranchName: input.primaryBranchName,
@@ -1146,14 +1225,14 @@ function updateProjectHeadsFromMirror(input) {
1146
1225
  branchMutationAllowed: input.branchMutationAllowed
1147
1226
  });
1148
1227
  }
1149
- function fastForwardProjectHeadsFromMirror(input) {
1150
- return updateProjectHeadsFromMirror({ ...input, allowNonFastForward: false }).map(({ moved, ...state }) => ({
1228
+ async function fastForwardProjectHeadsFromMirror(input) {
1229
+ return (await updateProjectHeadsFromMirror({ ...input, allowNonFastForward: false })).map(({ moved, ...state }) => ({
1151
1230
  ...state,
1152
1231
  fastForwarded: moved
1153
1232
  }));
1154
1233
  }
1155
- function reseedProjectHeadsFromMirror(input) {
1156
- return updateProjectHeadsFromMirror({ ...input, allowNonFastForward: true }).map(({ moved, ...state }) => ({
1234
+ async function reseedProjectHeadsFromMirror(input) {
1235
+ return (await updateProjectHeadsFromMirror({ ...input, allowNonFastForward: true })).map(({ moved, ...state }) => ({
1157
1236
  ...state,
1158
1237
  reseeded: moved
1159
1238
  }));
@@ -113,7 +113,8 @@ async function superviseWorkerRuntime(argv, env = process.env, spawnRuntime = de
113
113
  }
114
114
  return exitCode;
115
115
  } finally {
116
- for (const { signal, handler } of handlers) process.off(signal, handler);
116
+ const processEvents = process;
117
+ for (const { signal, handler } of handlers) processEvents.off(signal, handler);
117
118
  }
118
119
  }
119
120
  }