
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="9d9c3c2a-8688-589e-9942-06d39e441fca")}catch(e){}}();
import { readFile, stat } from "node:fs/promises";
import { join } from "node:path";
import { runGit } from "./git-utils.js";
function countLines(text) {
    if (!text) {
        return 0;
    }
    return text.split("\n").length;
}
function parseNumstatTotals(output) {
    let additions = 0;
    let deletions = 0;
    for (const rawLine of output.split("\n")) {
        const line = rawLine.trim();
        if (!line) {
            continue;
        }
        const [addedRaw, deletedRaw] = line.split("\t");
        const added = Number.parseInt(addedRaw ?? "", 10);
        const deleted = Number.parseInt(deletedRaw ?? "", 10);
        if (Number.isFinite(added)) {
            additions += added;
        }
        if (Number.isFinite(deleted)) {
            deletions += deleted;
        }
    }
    return { additions, deletions };
}
function parseAheadBehindCounts(output) {
    const [aheadRaw, behindRaw] = output.trim().split(/\s+/, 2);
    const ahead = Math.abs(Number.parseInt(aheadRaw ?? "", 10));
    const behind = Math.abs(Number.parseInt(behindRaw ?? "", 10));
    return {
        aheadCount: Number.isFinite(ahead) ? ahead : 0,
        behindCount: Number.isFinite(behind) ? behind : 0,
    };
}
function buildFingerprintToken(fingerprints) {
    return fingerprints
        .map((entry) => `${entry.path}\t${entry.size ?? "null"}\t${entry.mtimeMs ?? "null"}\t${entry.ctimeMs ?? "null"}`)
        .join("\n");
}
async function buildPathFingerprints(repoRoot, paths) {
    const uniqueSortedPaths = Array.from(new Set(paths)).sort((left, right) => left.localeCompare(right));
    return await Promise.all(uniqueSortedPaths.map(async (path) => {
        try {
            const fileStat = await stat(join(repoRoot, path));
            return {
                path,
                size: fileStat.size,
                mtimeMs: fileStat.mtimeMs,
                ctimeMs: fileStat.ctimeMs,
            };
        }
        catch {
            return {
                path,
                size: null,
                mtimeMs: null,
                ctimeMs: null,
            };
        }
    }));
}
function parseStatusPath(line) {
    const trimmed = line.trim();
    if (!trimmed) {
        return null;
    }
    const parts = trimmed.split("\t");
    const metadata = parts[0]?.trim() ?? "";
    const tokens = metadata.split(/\s+/);
    return tokens[tokens.length - 1] ?? null;
}
export async function probeGitWorkspaceState(cwd) {
    const repoRoot = await resolveRepoRoot(cwd);
    const [statusResult, headCommitResult] = await Promise.all([
        runGit(repoRoot, ["status", "--porcelain=v2", "--branch", "--untracked-files=all"]),
        runGit(repoRoot, ["rev-parse", "--verify", "HEAD"]),
    ]);
    if (!statusResult.ok) {
        throw new Error(statusResult.error ?? "Git status command failed.");
    }
    let currentBranch = null;
    let upstreamBranch = null;
    let aheadCount = 0;
    let behindCount = 0;
    const fingerprintPaths = [];
    const untrackedPaths = [];
    let changedFiles = 0;
    for (const rawLine of statusResult.stdout.split("\n")) {
        const line = rawLine.trim();
        if (!line) {
            continue;
        }
        if (line.startsWith("# branch.head ")) {
            const branchName = line.slice("# branch.head ".length).trim();
            currentBranch = branchName && branchName !== "(detached)" ? branchName : null;
            continue;
        }
        if (line.startsWith("# branch.upstream ")) {
            upstreamBranch = line.slice("# branch.upstream ".length).trim() || null;
            continue;
        }
        if (line.startsWith("# branch.ab ")) {
            const counts = parseAheadBehindCounts(line.slice("# branch.ab ".length));
            aheadCount = counts.aheadCount;
            behindCount = counts.behindCount;
            continue;
        }
        if (line.startsWith("? ")) {
            const path = line.slice(2).trim();
            if (!path) {
                continue;
            }
            changedFiles += 1;
            untrackedPaths.push(path);
            fingerprintPaths.push(path);
            continue;
        }
        if (line.startsWith("1 ") || line.startsWith("2 ") || line.startsWith("u ")) {
            const path = parseStatusPath(line);
            if (!path) {
                continue;
            }
            changedFiles += 1;
            fingerprintPaths.push(path);
            const renameParts = line.split("\t");
            const previousPath = renameParts[1]?.trim();
            if (previousPath) {
                fingerprintPaths.push(previousPath);
            }
        }
    }
    const headCommit = headCommitResult.ok && headCommitResult.stdout ? headCommitResult.stdout : null;
    const fingerprints = await buildPathFingerprints(repoRoot, fingerprintPaths);
    return {
        repoRoot,
        headCommit,
        currentBranch,
        upstreamBranch,
        aheadCount,
        behindCount,
        changedFiles,
        untrackedPaths,
        stateToken: [
            repoRoot,
            headCommit ?? "no-head",
            currentBranch ?? "detached",
            upstreamBranch ?? "no-upstream",
            String(aheadCount),
            String(behindCount),
            statusResult.stdout,
            buildFingerprintToken(fingerprints),
        ].join("\n--\n"),
    };
}
async function resolveRepoRoot(cwd) {
    const result = await runGit(cwd, ["rev-parse", "--show-toplevel"]);
    if (!result.ok || !result.stdout) {
        throw new Error("No git repository detected for this workspace.");
    }
    return result.stdout;
}
async function countUntrackedAdditions(repoRoot, untrackedPaths) {
    const counts = await Promise.all(untrackedPaths.map(async (relativePath) => {
        try {
            const contents = await readFile(join(repoRoot, relativePath), "utf8");
            return countLines(contents);
        }
        catch {
            return 0;
        }
    }));
    return counts.reduce((total, value) => total + value, 0);
}
async function hasGitRef(repoRoot, ref) {
    const result = await runGit(repoRoot, ["show-ref", "--verify", "--quiet", ref]);
    return result.ok;
}
export async function getGitSyncSummary(cwd, options) {
    const probe = options?.probe ?? (await probeGitWorkspaceState(cwd));
    const diffResult = await runGit(probe.repoRoot, ["diff", "--numstat", "HEAD", "--"]);
    const trackedTotals = diffResult.ok ? parseNumstatTotals(diffResult.stdout) : { additions: 0, deletions: 0 };
    const untrackedAdditions = await countUntrackedAdditions(probe.repoRoot, probe.untrackedPaths);
    return {
        currentBranch: probe.currentBranch,
        upstreamBranch: probe.upstreamBranch,
        changedFiles: probe.changedFiles,
        additions: trackedTotals.additions + untrackedAdditions,
        deletions: trackedTotals.deletions,
        aheadCount: probe.aheadCount,
        behindCount: probe.behindCount,
    };
}
export async function runGitSyncAction(options) {
    const initialSummary = await getGitSyncSummary(options.cwd);
    if (options.action === "pull" && initialSummary.changedFiles > 0) {
        return {
            ok: false,
            action: options.action,
            summary: initialSummary,
            output: "",
            error: "Pull failed: working tree has local changes. Commit, stash, or discard changes first.",
        };
    }
    const argsByAction = {
        fetch: ["fetch", "--all", "--prune"],
        pull: ["pull", "--ff-only"],
        push: ["push"],
    };
    const commandResult = await runGit(options.cwd, argsByAction[options.action]);
    const nextSummary = await getGitSyncSummary(options.cwd);
    if (!commandResult.ok) {
        return {
            ok: false,
            action: options.action,
            summary: nextSummary,
            output: commandResult.output,
            error: commandResult.error ?? "Git command failed.",
        };
    }
    return {
        ok: true,
        action: options.action,
        summary: nextSummary,
        output: commandResult.output,
    };
}
export async function runGitCheckoutAction(options) {
    const requestedBranch = options.branch.trim();
    const initialSummary = await getGitSyncSummary(options.cwd);
    if (!requestedBranch) {
        return {
            ok: false,
            branch: requestedBranch,
            summary: initialSummary,
            output: "",
            error: "Branch name cannot be empty.",
        };
    }
    if (initialSummary.currentBranch === requestedBranch) {
        return {
            ok: true,
            branch: requestedBranch,
            summary: initialSummary,
            output: `Already on '${requestedBranch}'.`,
        };
    }
    const repoRoot = await resolveRepoRoot(options.cwd);
    const hasLocalBranch = await hasGitRef(repoRoot, `refs/heads/${requestedBranch}`);
    const commandResult = hasLocalBranch
        ? await runGit(repoRoot, ["switch", requestedBranch])
        : (await hasGitRef(repoRoot, `refs/remotes/origin/${requestedBranch}`))
            ? await runGit(repoRoot, ["switch", "--track", `origin/${requestedBranch}`])
            : await runGit(repoRoot, ["switch", requestedBranch]);
    const nextSummary = await getGitSyncSummary(repoRoot);
    if (!commandResult.ok) {
        return {
            ok: false,
            branch: requestedBranch,
            summary: nextSummary,
            output: commandResult.output,
            error: commandResult.error ?? "Git branch switch failed.",
        };
    }
    return {
        ok: true,
        branch: requestedBranch,
        summary: nextSummary,
        output: commandResult.output,
    };
}
export async function discardGitChanges(options) {
    const repoRoot = await resolveRepoRoot(options.cwd);
    const initialSummary = await getGitSyncSummary(repoRoot);
    if (initialSummary.changedFiles === 0) {
        return {
            ok: true,
            summary: initialSummary,
            output: "Working tree is already clean.",
        };
    }
    const restoreResult = await runGit(repoRoot, [
        "restore",
        "--source=HEAD",
        "--staged",
        "--worktree",
        "--",
        ".",
    ]);
    const cleanResult = restoreResult.ok ? await runGit(repoRoot, ["clean", "-fd", "--", "."]) : null;
    const nextSummary = await getGitSyncSummary(repoRoot);
    const output = [restoreResult.output, cleanResult?.output ?? ""].filter(Boolean).join("\n");
    if (!restoreResult.ok) {
        return {
            ok: false,
            summary: nextSummary,
            output,
            error: restoreResult.error ?? "Discard failed.",
        };
    }
    if (cleanResult && !cleanResult.ok) {
        return {
            ok: false,
            summary: nextSummary,
            output,
            error: cleanResult.error ?? "Discard failed while cleaning untracked files.",
        };
    }
    return {
        ok: true,
        summary: nextSummary,
        output,
    };
}
//# sourceMappingURL=git-sync.js.map
//# debugId=9d9c3c2a-8688-589e-9942-06d39e441fca
