mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 15:18:28 +00:00
Infra: unify git root discovery
This commit is contained in:
72
src/infra/git-root.ts
Normal file
72
src/infra/git-root.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
export const DEFAULT_GIT_DISCOVERY_MAX_DEPTH = 12;
|
||||
|
||||
function walkUpFrom<T>(
|
||||
startDir: string,
|
||||
opts: { maxDepth?: number },
|
||||
resolveAtDir: (dir: string) => T | null | undefined,
|
||||
): T | null {
|
||||
let current = path.resolve(startDir);
|
||||
const maxDepth = opts.maxDepth ?? DEFAULT_GIT_DISCOVERY_MAX_DEPTH;
|
||||
for (let i = 0; i < maxDepth; i += 1) {
|
||||
const resolved = resolveAtDir(current);
|
||||
if (resolved !== null && resolved !== undefined) {
|
||||
return resolved;
|
||||
}
|
||||
const parent = path.dirname(current);
|
||||
if (parent === current) {
|
||||
break;
|
||||
}
|
||||
current = parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function hasGitMarker(repoRoot: string): boolean {
|
||||
const gitPath = path.join(repoRoot, ".git");
|
||||
try {
|
||||
const stat = fs.statSync(gitPath);
|
||||
return stat.isDirectory() || stat.isFile();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function findGitRoot(startDir: string, opts: { maxDepth?: number } = {}): string | null {
|
||||
// A `.git` file counts as a repo marker even if it is not a valid gitdir pointer.
|
||||
return walkUpFrom(startDir, opts, (repoRoot) => (hasGitMarker(repoRoot) ? repoRoot : null));
|
||||
}
|
||||
|
||||
function resolveGitDirFromMarker(repoRoot: string): string | null {
|
||||
const gitPath = path.join(repoRoot, ".git");
|
||||
try {
|
||||
const stat = fs.statSync(gitPath);
|
||||
if (stat.isDirectory()) {
|
||||
return gitPath;
|
||||
}
|
||||
if (!stat.isFile()) {
|
||||
return null;
|
||||
}
|
||||
const raw = fs.readFileSync(gitPath, "utf-8");
|
||||
const match = raw.match(/gitdir:\s*(.+)/i);
|
||||
if (!match?.[1]) {
|
||||
return null;
|
||||
}
|
||||
return path.resolve(repoRoot, match[1].trim());
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveGitHeadPath(
|
||||
startDir: string,
|
||||
opts: { maxDepth?: number } = {},
|
||||
): string | null {
|
||||
// Stricter than findGitRoot: keep walking until a resolvable git dir is found.
|
||||
return walkUpFrom(startDir, opts, (repoRoot) => {
|
||||
const gitDir = resolveGitDirFromMarker(repoRoot);
|
||||
return gitDir ? path.join(gitDir, "HEAD") : null;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user