import { readdir } from "node:fs/promises";
import { join } from "node:path";
import { spawnSync } from "node:child_process";
/**
* --- 配置开关 ---
* true: 强制覆盖 (git fetch + git reset --hard),用于处理远程 push -f
* false: 普通更新 (git pull)
*/
const FORCE_MODE = process.argv.includes("--force") || false;
// 需要忽略的目录
const IGNORE_DIRS = new Set(["node_modules", ".idea", ".git"]);
/**
* 获取当前 git 仓库的当前分支名
*/
function getCurrentBranch(dir: string): string {
const result = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
cwd: dir,
encoding: "utf-8",
});
return result.stdout.trim() || "main";
}
/**
* 执行 Git 更新逻辑
*/
function updateRepo(dir: string) {
if (FORCE_MODE) {
console.log(`\x1b[33m[FORCE]\x1b[0m 正在强制重置: ${dir}`);
// 1. 获取远程状态
spawnSync("git", ["fetch", "--all"], { cwd: dir });
// 2. 强制重置
const branch = getCurrentBranch(dir);
const result = spawnSync("git", ["reset", "--hard", `origin/${branch}`], {
cwd: dir,
encoding: "utf-8",
});
return result;
} else {
console.log(`\x1b[34m[PULL]\x1b[0m 正在常规更新: ${dir}`);
return spawnSync("git", ["pull"], {
cwd: dir,
encoding: "utf-8",
});
}
}
/**
* 递归扫描目录
*/
async function scanAndPull(dir: string) {
try {
const entries = await readdir(dir, { withFileTypes: true });
const isGitRepo = entries.some(entry => entry.isDirectory() && entry.name === ".git");
if (isGitRepo) {
const result = updateRepo(dir);
if (result.status === 0) {
console.log(`\x1b[32m[SUCCESS]\x1b[0m ${dir} 更新完成`);
} else {
console.error(`\x1b[31m[ERROR]\x1b[0m ${dir} 更新失败: ${result.stderr}`);
}
return;
}
const tasks = entries
.filter(entry => entry.isDirectory() && !IGNORE_DIRS.has(entry.name))
.map(entry => scanAndPull(join(dir, entry.name)));
await Promise.all(tasks);
} catch (err) {
console.error(`无法读取目录 ${dir}:`, err);
}
}
// --- 执行入口 ---
console.log(`\x1b[35m[START]\x1b[0m 模式: ${FORCE_MODE ? "强制覆盖" : "普通更新"}`);
scanAndPull(process.cwd()).then(() => {
console.log("\x1b[35m[DONE]\x1b[0m 所有操作已完成。");
});