oh-my-space 0.11.4 → 0.11.5

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.
@@ -1 +1 @@
1
- {"commit":"60bbfdfcc5f72dce2b2b74508715ddbc723aa3ff"}
1
+ {"commit":"022ade8884c62e549770e73407066671e8caa50a"}
package/dist/oms.js CHANGED
@@ -14548,7 +14548,7 @@ function loadForSubmodules() {
14548
14548
  __name(loadForSubmodules, "loadForSubmodules");
14549
14549
 
14550
14550
  // scripts/lib/status.ts
14551
- import { existsSync as existsSync5 } from "node:fs";
14551
+ import { existsSync as existsSync5, lstatSync, readdirSync } from "node:fs";
14552
14552
  import { relative, resolve as resolve4 } from "node:path";
14553
14553
  function pinState(repoRoot, alias) {
14554
14554
  const r = runGit(repoRoot, ["submodule", "status", "--", submodulePath(alias)]);
@@ -14701,6 +14701,55 @@ function partialRemovalTopology(s) {
14701
14701
  return s.headOid !== null && !s.pathExists !== !s.gitmodulesEntry;
14702
14702
  }
14703
14703
  __name(partialRemovalTopology, "partialRemovalTopology");
14704
+ function readAliasDirEntries(repoRoot, alias) {
14705
+ const dir = aliasDir(repoRoot, alias);
14706
+ try {
14707
+ if (!lstatSync(dir).isDirectory()) return { exists: true, kind: "file" };
14708
+ return { exists: true, kind: "dir", entries: readdirSync(dir) };
14709
+ } catch (error) {
14710
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
14711
+ return { exists: false };
14712
+ }
14713
+ return { exists: true, kind: "unreadable" };
14714
+ }
14715
+ }
14716
+ __name(readAliasDirEntries, "readAliasDirEntries");
14717
+ function unreadablePathReason(alias) {
14718
+ return `oms/${alias} could not be read (permission or I/O error). Resolve access to it, then retry.`;
14719
+ }
14720
+ __name(unreadablePathReason, "unreadablePathReason");
14721
+ var TOPOLOGY_REASON = {
14722
+ conflict: "the root gitlink is conflicted. Resolve the root repository conflict first.",
14723
+ inProgressOp: /* @__PURE__ */ __name((op) => `the root repository has a ${op} in progress. Resolve, continue, or abort it first.`, "inProgressOp"),
14724
+ occupiedPath: /* @__PURE__ */ __name((alias) => `oms/${alias} is occupied by a non-submodule path. Move or remove it manually, then retry.`, "occupiedPath"),
14725
+ unreadable: /* @__PURE__ */ __name((alias) => unreadablePathReason(alias), "unreadable")
14726
+ };
14727
+ function occupiedByNonSubmodule(repoRoot, alias) {
14728
+ if (isRegisteredSubmodule(repoRoot, submodulePath(alias))) return "clear";
14729
+ const dirState = readAliasDirEntries(repoRoot, alias);
14730
+ if (!dirState.exists) return "clear";
14731
+ if (dirState.kind === "unreadable") return "unreadable";
14732
+ if (dirState.kind === "file") return "occupied";
14733
+ return dirState.entries.length > 0 ? "occupied" : "clear";
14734
+ }
14735
+ __name(occupiedByNonSubmodule, "occupiedByNonSubmodule");
14736
+ function assertRootTopologySafe(repoRoot, alias, checks = ["conflict", "inProgressOp", "occupiedPath"]) {
14737
+ const applies = new Set(checks);
14738
+ if (applies.has("conflict") && gitlinkState(repoRoot, alias).conflict) {
14739
+ return { safe: false, reason: TOPOLOGY_REASON.conflict };
14740
+ }
14741
+ if (applies.has("inProgressOp")) {
14742
+ const op = gitOperationInProgress(repoRoot);
14743
+ if (op) return { safe: false, reason: TOPOLOGY_REASON.inProgressOp(op) };
14744
+ }
14745
+ if (applies.has("occupiedPath")) {
14746
+ const occupied = occupiedByNonSubmodule(repoRoot, alias);
14747
+ if (occupied === "unreadable") return { safe: false, reason: TOPOLOGY_REASON.unreadable(alias) };
14748
+ if (occupied === "occupied") return { safe: false, reason: TOPOLOGY_REASON.occupiedPath(alias) };
14749
+ }
14750
+ return { safe: true };
14751
+ }
14752
+ __name(assertRootTopologySafe, "assertRootTopologySafe");
14704
14753
  function rootFollowupHint(alias, s) {
14705
14754
  if (s.headOid !== null && s.pathExists && !s.conflict && s.moved) {
14706
14755
  return `Run "oms record ${alias}" to record the root pointer update.`;
@@ -15154,13 +15203,9 @@ async function runRecord(alias) {
15154
15203
  const selected = resolution.alias;
15155
15204
  const path = submodulePath(selected);
15156
15205
  const state = gitlinkState(repoRoot, selected);
15157
- if (state.conflict) {
15158
- R2.error(`${selected}: the root gitlink is conflicted. Resolve the root repository conflict first.`);
15159
- return 1;
15160
- }
15161
- const rootOp = gitOperationInProgress(repoRoot);
15162
- if (rootOp) {
15163
- R2.error(`Root repository has a ${rootOp} in progress. Resolve, continue, or abort it before recording.`);
15206
+ const safety = assertRootTopologySafe(repoRoot, selected, ["conflict", "inProgressOp"]);
15207
+ if (!safety.safe) {
15208
+ R2.error(`${selected}: ${safety.reason}`);
15164
15209
  return 1;
15165
15210
  }
15166
15211
  if (currentBranch(repoRoot) === null) {
@@ -15766,7 +15811,7 @@ async function runInit(options) {
15766
15811
  __name(runInit, "runInit");
15767
15812
 
15768
15813
  // scripts/lib/repo-ops.ts
15769
- import { existsSync as existsSync8, lstatSync, readFileSync as readFileSync4, readdirSync, rmSync as rmSync2, rmdirSync, writeFileSync as writeFileSync4 } from "node:fs";
15814
+ import { existsSync as existsSync8, readFileSync as readFileSync4, readdirSync as readdirSync2, rmSync as rmSync2, rmdirSync, writeFileSync as writeFileSync4 } from "node:fs";
15770
15815
  import { join as join7 } from "node:path";
15771
15816
  function cleanupFailedAdd(repoRoot, alias) {
15772
15817
  const path = submodulePath(alias);
@@ -15865,32 +15910,20 @@ function reconcileGitmodulesMetadata(repoRoot, repo) {
15865
15910
  return changed;
15866
15911
  }
15867
15912
  __name(reconcileGitmodulesMetadata, "reconcileGitmodulesMetadata");
15868
- function readAliasDirEntries(repoRoot, alias) {
15869
- const dir = aliasDir(repoRoot, alias);
15870
- try {
15871
- if (!lstatSync(dir).isDirectory()) return { exists: true, entries: null };
15872
- return { exists: true, entries: readdirSync(dir) };
15873
- } catch (error) {
15874
- if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
15875
- return { exists: false };
15876
- }
15877
- return { exists: true, entries: null };
15878
- }
15879
- }
15880
- __name(readAliasDirEntries, "readAliasDirEntries");
15881
15913
  function cleanupRestorableAliasDir(repoRoot, alias) {
15882
- if (submoduleInitialized(repoRoot, alias)) return true;
15914
+ if (submoduleInitialized(repoRoot, alias)) return "ok";
15883
15915
  const dirState = readAliasDirEntries(repoRoot, alias);
15884
- if (!dirState.exists) return true;
15885
- if (dirState.entries === null) return false;
15916
+ if (!dirState.exists) return "ok";
15917
+ if (dirState.kind === "unreadable") return "unreadable";
15918
+ if (dirState.kind === "file") return "occupied";
15886
15919
  const entries = dirState.entries;
15887
- if (entries.length > 0 && entries.some((entry) => entry !== ".DS_Store")) return false;
15920
+ if (entries.length > 0 && entries.some((entry) => entry !== ".DS_Store")) return "occupied";
15888
15921
  try {
15889
15922
  rmSync2(aliasDir(repoRoot, alias), { recursive: true, force: true });
15890
15923
  } catch {
15891
- return false;
15924
+ return "unremovable";
15892
15925
  }
15893
- return true;
15926
+ return "ok";
15894
15927
  }
15895
15928
  __name(cleanupRestorableAliasDir, "cleanupRestorableAliasDir");
15896
15929
  function restorePendingRemoval(repo, repoRoot) {
@@ -15911,9 +15944,10 @@ function restorePendingRemoval(repo, repoRoot) {
15911
15944
  }
15912
15945
  const section = headGitmodulesSection(repoRoot, alias);
15913
15946
  if (!section) return unsafe(".gitmodules metadata is not recoverable from HEAD");
15914
- if (!cleanupRestorableAliasDir(repoRoot, alias)) {
15915
- return unsafe(`${path} is occupied by a non-submodule path`);
15916
- }
15947
+ const cleanup = cleanupRestorableAliasDir(repoRoot, alias);
15948
+ if (cleanup === "unreadable") return unsafe(`${path} could not be read (permission or I/O error)`);
15949
+ if (cleanup === "unremovable") return unsafe(`${path} could not be removed (permission or I/O error)`);
15950
+ if (cleanup === "occupied") return unsafe(`${path} is occupied by a non-submodule path`);
15917
15951
  runGit(repoRoot, ["restore", "--source=HEAD", "--staged", "--", ".gitmodules"]);
15918
15952
  if (!state.gitmodulesEntry) insertGitmodulesSection(repoRoot, alias, section);
15919
15953
  const metadataUpdated = reconcileGitmodulesMetadata(repoRoot, repo);
@@ -15944,7 +15978,11 @@ function syncRepo(repo, repoRoot) {
15944
15978
  }
15945
15979
  if (!registered) {
15946
15980
  const dirState = readAliasDirEntries(repoRoot, alias);
15947
- if (dirState.exists && (dirState.entries === null || dirState.entries.length > 0)) {
15981
+ if (dirState.exists && dirState.kind === "unreadable") {
15982
+ R2.error(`${alias}: ${unreadablePathReason(alias)}`);
15983
+ return "failed";
15984
+ }
15985
+ if (dirState.exists && (dirState.kind === "file" || dirState.entries.length > 0)) {
15948
15986
  R2.error(
15949
15987
  `${alias}: ${path}/ already exists but is not a registered submodule. Move or remove it manually, then retry.`
15950
15988
  );
@@ -16018,6 +16056,11 @@ function unsyncRepo(repo, repoRoot, force) {
16018
16056
  const registered = isRegisteredSubmodule(repoRoot, path);
16019
16057
  const exists = existsSync8(aliasDir(repoRoot, alias));
16020
16058
  if (!registered && !exists) return "nothing-to-remove";
16059
+ const safety = assertRootTopologySafe(repoRoot, alias);
16060
+ if (!safety.safe) {
16061
+ R2.error(`${alias}: ${safety.reason}`);
16062
+ return "failed";
16063
+ }
16021
16064
  if (!force && submoduleInitialized(repoRoot, alias) && isDirty(aliasDir(repoRoot, alias))) {
16022
16065
  R2.error(
16023
16066
  `${alias}: ${path} has uncommitted or untracked changes. Commit, stash, remove them, or pass --force.`
@@ -16038,7 +16081,7 @@ function unsyncRepo(repo, repoRoot, force) {
16038
16081
  }
16039
16082
  try {
16040
16083
  const modulesParent = join7(repoRoot, ".git", "modules", DATA_DIRNAME);
16041
- if (existsSync8(modulesParent) && readdirSync(modulesParent).length === 0) rmdirSync(modulesParent);
16084
+ if (existsSync8(modulesParent) && readdirSync2(modulesParent).length === 0) rmdirSync(modulesParent);
16042
16085
  } catch {
16043
16086
  }
16044
16087
  if (existsSync8(aliasDir(repoRoot, alias))) {
@@ -16046,7 +16089,7 @@ function unsyncRepo(repo, repoRoot, force) {
16046
16089
  }
16047
16090
  try {
16048
16091
  const parent = join7(repoRoot, DATA_DIRNAME);
16049
- if (existsSync8(parent) && readdirSync(parent).length === 0) rmdirSync(parent);
16092
+ if (existsSync8(parent) && readdirSync2(parent).length === 0) rmdirSync(parent);
16050
16093
  } catch {
16051
16094
  }
16052
16095
  const gitmodules = join7(repoRoot, ".gitmodules");
@@ -16245,9 +16288,7 @@ async function runUnsync(aliases, options) {
16245
16288
  if (results.length > 1 || options.all) printSummary(results);
16246
16289
  const failed = picked.filter((_, i) => results[i] === "failed").map((r) => r.alias);
16247
16290
  if (failed.length > 0) {
16248
- R2.error(
16249
- `Not unsynced: ${failed.join(", ")}. The submodule had uncommitted or untracked changes \u2014 commit/stash/remove them, or re-run with --force.`
16250
- );
16291
+ R2.error(`Not unsynced: ${failed.join(", ")}. See the per-alias error above for each.`);
16251
16292
  }
16252
16293
  return exitFromResults(results) || topoExit;
16253
16294
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-space",
3
- "version": "0.11.4",
3
+ "version": "0.11.5",
4
4
  "description": "Manage multi-repo workspaces with Git submodules.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/divlook/oh-my-space",