mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-01 02:01:24 +00:00
refactor(storage): reuse helpers across the shared-store code (#3352)
One graph-clone helper replaces two copy-then-rename blocks; clean and status reuse formatSlotSize; leaving a store reuses removeSharedStorePointer; withStoreLock is imported from its own module instead of a re-export. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
990ee1b121
commit
f9e988ebce
7 changed files with 30 additions and 29 deletions
|
|
@ -17,7 +17,7 @@ import {
|
|||
type RegistryEntry,
|
||||
} from '../storage/repo-manager.js';
|
||||
import { requireDeletableStoragePath, StorageDeletionError } from '../storage/storage-resolver.js';
|
||||
import { formatStaleSlotLine } from './stale-branch-format.js';
|
||||
import { formatSlotSize, formatStaleSlotLine } from './stale-branch-format.js';
|
||||
import { listLocalHeads } from '../storage/git.js';
|
||||
import {
|
||||
isContainedBranchDir,
|
||||
|
|
@ -157,11 +157,6 @@ const cleanStaleBranchSlots = async (force: boolean): Promise<void> => {
|
|||
}
|
||||
};
|
||||
|
||||
const formatBytes = (bytes: number): string =>
|
||||
bytes >= 1024 * 1024
|
||||
? `${(bytes / (1024 * 1024)).toFixed(1)} MB`
|
||||
: `${Math.ceil(bytes / 1024)} KB`;
|
||||
|
||||
const reportReclaim = (result: ReclaimResult | null): void => {
|
||||
if (!result) return;
|
||||
if (result.removed.length > 0) {
|
||||
|
|
@ -222,14 +217,14 @@ export const cleanCommand = async (options?: {
|
|||
}
|
||||
if (!options.force) {
|
||||
console.log(
|
||||
t('clean.localIndex.preview', { path: legacy.dir, size: formatBytes(legacy.bytes) }),
|
||||
t('clean.localIndex.preview', { path: legacy.dir, size: formatSlotSize(legacy.bytes) }),
|
||||
);
|
||||
console.log(`\n${t('common.runForceConfirm')}`);
|
||||
return;
|
||||
}
|
||||
await removeLegacyLocalIndex(repo.repoPath, repo.storagePath);
|
||||
console.log(
|
||||
t('clean.localIndex.deleted', { path: legacy.dir, size: formatBytes(legacy.bytes) }),
|
||||
t('clean.localIndex.deleted', { path: legacy.dir, size: formatSlotSize(legacy.bytes) }),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { resolveGraphPath, storeRootOfCheckoutSlot } from '../storage/shared-store.js';
|
||||
import { describeSharedGraph, findLegacyLocalIndex } from '../storage/shared-store-lifecycle.js';
|
||||
import { formatSlotSize } from './stale-branch-format.js';
|
||||
import path from 'path';
|
||||
import {
|
||||
getStoragePaths,
|
||||
|
|
@ -413,7 +414,7 @@ export const statusCommand = async (options: StatusOptions = {}) => {
|
|||
console.log(
|
||||
t('status.legacyLocalIndex', {
|
||||
path: legacyLocalIndex.dir,
|
||||
size: `${Math.ceil(legacyLocalIndex.bytes / 1024)} KB`,
|
||||
size: formatSlotSize(legacyLocalIndex.bytes),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,8 +182,8 @@ import {
|
|||
publishSharedGraph,
|
||||
resolveOptedInStore,
|
||||
seedSharedSlot,
|
||||
withStoreLock,
|
||||
} from './shared-store-analyze.js';
|
||||
import { withStoreLock } from '../storage/shared-store-lifecycle.js';
|
||||
import { DEFAULT_PDG_MAX_FUNCTION_LINES } from './ingestion/cfg/collect.js';
|
||||
import {
|
||||
DEFAULT_MAX_CFG_EDGES_PER_FUNCTION,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import { constants as fsConstants } from 'fs';
|
|||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { acquireIndexLock } from '../storage/index-lock.js';
|
||||
import { withStoreLock } from '../storage/shared-store-lifecycle.js';
|
||||
import { commitDistanceToHead, getRemoteUrl, isWorkingTreeDirty } from '../storage/git.js';
|
||||
import {
|
||||
readRegistry,
|
||||
|
|
@ -33,13 +32,14 @@ import {
|
|||
commitGraphDir,
|
||||
resolveGraphPath,
|
||||
resolveSharedStore,
|
||||
SHARED_STORE_POINTER,
|
||||
sharedStoreLayout,
|
||||
storeRootOfCheckoutSlot,
|
||||
type SharedStoreLayout,
|
||||
} from '../storage/shared-store.js';
|
||||
import {
|
||||
reclaimAfterSlotRemoval,
|
||||
removeSharedStorePointer,
|
||||
withStoreLock,
|
||||
writeSharedStorePointer,
|
||||
} from '../storage/shared-store-lifecycle.js';
|
||||
import { GITNEXUS_DIR, INDEX_METADATA_FILE, LBUG_DIRECTORY } from '../storage/storage-constants.js';
|
||||
|
|
@ -101,6 +101,22 @@ const exists = (p: string): Promise<boolean> =>
|
|||
() => false,
|
||||
);
|
||||
|
||||
/**
|
||||
* Copy a graph file to `dest` via a unique `lbug.new.<id>` temp (swept by the
|
||||
* slot lock if this process dies mid-copy), cloning copy-on-write where the
|
||||
* filesystem supports it. On failure the temp is removed and the error thrown.
|
||||
*/
|
||||
const cloneGraphFile = async (source: string, dest: string): Promise<void> => {
|
||||
const tmp = `${dest}.new.${randomUUID()}`;
|
||||
try {
|
||||
await fs.copyFile(source, tmp, fsConstants.COPYFILE_FICLONE);
|
||||
await fs.rename(tmp, dest);
|
||||
} catch (err) {
|
||||
await fs.rm(tmp, { force: true }).catch(() => {});
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
interface CommitGraph {
|
||||
dir: string;
|
||||
commit: string;
|
||||
|
|
@ -175,13 +191,9 @@ const seedFromLocalIndex = async (
|
|||
try {
|
||||
if (lock.lockFree || (await inspectLbugSidecars(sourceGraph)).kind !== 'clean') return false;
|
||||
await fs.mkdir(slot, { recursive: true });
|
||||
const own = path.join(slot, LBUG_DIRECTORY);
|
||||
const tmp = `${own}.new.${randomUUID()}`;
|
||||
try {
|
||||
await fs.copyFile(sourceGraph, tmp, fsConstants.COPYFILE_FICLONE);
|
||||
await fs.rename(tmp, own);
|
||||
await cloneGraphFile(sourceGraph, path.join(slot, LBUG_DIRECTORY));
|
||||
} catch (err) {
|
||||
await fs.rm(tmp, { force: true }).catch(() => {});
|
||||
log(`Shared store: could not copy ${sourceGraph} (${(err as Error).message}).`);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -245,14 +257,10 @@ export const ensurePrivateSharedGraph = async (slot: string, log: Log): Promise<
|
|||
const meta = await loadMeta(slot);
|
||||
if (!meta) return true;
|
||||
if (!(await exists(own))) {
|
||||
// `lbug.new.<id>` is swept by the slot lock if this process dies mid-copy.
|
||||
const tmp = `${own}.new.${randomUUID()}`;
|
||||
const started = Date.now();
|
||||
try {
|
||||
await fs.copyFile(pointed, tmp, fsConstants.COPYFILE_FICLONE);
|
||||
await fs.rename(tmp, own);
|
||||
await cloneGraphFile(pointed, own);
|
||||
} catch (err) {
|
||||
await fs.rm(tmp, { force: true }).catch(() => {});
|
||||
const reason = (err as NodeJS.ErrnoException).code ?? (err as Error).message;
|
||||
log(`Shared store: shared graph unavailable (${reason}); doing a full build.`);
|
||||
return false;
|
||||
|
|
@ -346,8 +354,6 @@ export const publishSharedGraph = async (
|
|||
await writeSharedStorePointer(repoPath, layout);
|
||||
};
|
||||
|
||||
export { withStoreLock };
|
||||
|
||||
/**
|
||||
* Store for a checkout that is not a linked worktree: the store named by
|
||||
* `--share-with`, or the one its registry entry already points into. Clones
|
||||
|
|
@ -414,7 +420,7 @@ export const leaveSharedStore = async (
|
|||
log: Log,
|
||||
): Promise<void> => {
|
||||
await fs.rm(previousSlot, { recursive: true, force: true });
|
||||
await fs.rm(path.join(repoPath, GITNEXUS_DIR, SHARED_STORE_POINTER), { force: true });
|
||||
await removeSharedStorePointer(repoPath);
|
||||
await reclaimAfterSlotRemoval(previousSlot);
|
||||
log(`Shared store: left ${previousSlot}.`);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ export const reclaimAfterSlotRemoval = async (
|
|||
}
|
||||
};
|
||||
|
||||
/** Graph directory a checkout slot reads, for reporting. */
|
||||
/** Whether a checkout slot reads a shared commit graph or its own private graph. */
|
||||
export const describeSharedGraph = (
|
||||
graphPath: string,
|
||||
storagePath: string,
|
||||
|
|
|
|||
|
|
@ -405,9 +405,8 @@ const inspectCodeIndexDB = async (
|
|||
// owning one; `resolveGraphPath` only returns a path inside the same store's
|
||||
// commit graphs, else the slot's own graph.
|
||||
const graphPath = resolveGraphPath(resolved);
|
||||
const target = graphPath === lbugPath ? lbugPath : graphPath;
|
||||
try {
|
||||
await fsp.access(target);
|
||||
await fsp.access(graphPath);
|
||||
return { present: true };
|
||||
} catch (error) {
|
||||
const code = (error as NodeJS.ErrnoException)?.code;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { existsSync } from 'fs';
|
|||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
import { withStoreLock } from '../../src/core/shared-store-analyze.js';
|
||||
import { withStoreLock } from '../../src/storage/shared-store-lifecycle.js';
|
||||
import { loadMeta } from '../../src/storage/repo-manager.js';
|
||||
import {
|
||||
resolveSharedStore,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue