// app.ts var import_child_process = require("child_process"); var IGNORED_BRANCHES = ["master", "main", "dev", "release"]; var mainBranch = "dev"; var runGitCommand = (args, options) => new Promise((resolve, reject) => { const git = (0, import_child_process.spawn)("git", args, { stdio: "pipe", ...options }); let stdout = ""; let stderr = ""; git.stdout.on("data", (data) => stdout += data.toString()); git.stderr.on("data", (data) => stderr += data.toString()); git.on("close", (code) => code === 0 ? resolve(stdout) : reject(new Error(`Git command failed with code ${code}: ${stderr}`))); }); var fetchBranches = async () => { console.log("Fetching all branches..."); await runGitCommand(["fetch", "--all"]); const branches = await runGitCommand(["branch", "-r"]); return branches.split("\n").map((branch) => branch.trim().replace("origin/", "")).filter((branch) => !IGNORED_BRANCHES.includes(branch) && branch !== ""); }; var getCommitsForBranch = async (branch) => { const commits = await runGitCommand(["rev-list", branch]); return new Set(commits.split("\n").filter(Boolean)); }; var removeIgnoredBranches = (branchesWithDependencies) => { let withoutIgnoredBranches = {}; for (const [branch, value] of Object.entries(branchesWithDependencies)) { if (!value.ignore) { withoutIgnoredBranches[branch] = value; } } return withoutIgnoredBranches; }; var buildRebaseDependencyGraph = async (branches) => { const commitHistories = {}; for (const branch of branches) { commitHistories[branch] = await getCommitsForBranch(branch); } let finalBranches = {}; for (const branchA of branches) { for (const branchB of branches) { if (branchA !== branchB) { const infos = { superset: commitHistories[branchA].isSupersetOf(commitHistories[branchB]), difference: commitHistories[branchA].difference(commitHistories[branchB]) }; if (infos.superset) { if (branchB === mainBranch) { finalBranches[branchA] = { ignore: true }; } if (infos.difference.size === 0) { const prevBranches = finalBranches[branchA]?.equalBranches ?? []; finalBranches[branchA] = { rebaseBranch: mainBranch, ...finalBranches[branchA], equalBranches: [...prevBranches, branchB] }; } else { if (branchA !== mainBranch && (!finalBranches[branchA] || finalBranches[branchA].differenceWithRebase > infos.difference.size)) { finalBranches[branchA] = { ...finalBranches[branchA], rebaseBranch: branchB, differenceWithRebase: infos.difference.size }; } } } } } } for (const branch of branches) { if (branch !== mainBranch) { finalBranches[branch] = finalBranches[branch] ?? { rebaseBranch: mainBranch, differenceWithRebase: 0 }; } } return removeIgnoredBranches(finalBranches); }; var rebaseOrder = (branchesWithDependencies) => { console.log("Order everything"); let orderedActions = []; for (const branch of Object.keys(branchesWithDependencies)) { const alreadyRebasedEqualBranch = orderedActions.find((action) => branchesWithDependencies[branch].equalBranches?.some((otherBranch) => otherBranch === action.branch)); if (alreadyRebasedEqualBranch) { orderedActions.push({ branch, onBranch: alreadyRebasedEqualBranch.branch, action: 1 /* Reset */ }); } else { orderedActions.push({ branch, onBranch: branchesWithDependencies[branch].rebaseBranch, action: 0 /* Rebase */ }); } } orderedActions = orderedActions.sort((a, b) => { const diffA = branchesWithDependencies[a.branch].differenceWithRebase; const diffB = branchesWithDependencies[b.branch].differenceWithRebase; return diffA - diffB; }); orderedActions = orderedActions.sort((a, b) => a.action - b.action); return orderedActions; }; var rebaseBranch = async ({ branch, onBranch, action }) => { console.log(`${action === 0 /* Rebase */ ? "Rebasing" : "Resetting"} ${branch} on ${onBranch}`); await runGitCommand([ "checkout", branch ]); if (action === 0 /* Rebase */) { await runGitCommand([ "rebase", onBranch ]); } else if (action === 1 /* Reset */) { await runGitCommand([ "reset", "--hard", onBranch ]); } await runGitCommand([ "push", "--force-with-lease" ]); }; var main = async () => { try { const branches = await fetchBranches(); branches.push(mainBranch); console.log("Branches:", branches); const dependencies = await buildRebaseDependencyGraph(branches); console.log("Dependencies:", dependencies); const order = rebaseOrder(dependencies); console.log("Rebase order:", order); for (const rebaseAction of order) { await rebaseBranch(rebaseAction); } } catch (error) { console.error("Error during workflow execution:", error.message); } }; main();