152 lines
5.0 KiB
JavaScript
152 lines
5.0 KiB
JavaScript
// 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();
|