const Git = require("simple-git"); const u = require("updeep"); const _ = require("lodash"); const fp = require("lodash/fp"); const log = require("./log"); const normalizeArray = (value) => Array.isArray(value) ? value : [value].filter((x) => x); const initBranch = (config = {}) => ({ upstream: [], dependencies: [], ...config, }); const git = Git(); const currentBranch = _.once(() => git.raw("branch", "--show-current").then((r) => r.replace("\n", "")) ); const isClean = () => git.status().then(({ files }) => !files.length); const branches = _.once(async () => { const config = await git.listConfig("local").then(fp.get("all")); let tree = Object.entries(config).reduce( (accum, [key, value]) => u.updateIn(key, value, accum), {} ); let branches = {}; for (const branch in tree.branch) { const entry = tree.branch[branch]; if (!("mikado-upstream" in entry)) continue; branches[branch] = initBranch({ name: branch, upstream: normalizeArray(entry["mikado-upstream"]), base: entry["mikado-base"], done: !!entry["mikado-done"], }); } for (const branch of Object.values(branches)) { for (const ups of branch["upstream"]) { if (!branches[ups]) branches[ups] = initBranch({ name: ups, upstream: [], dependencies: [], }); branches[ups].dependencies.push(branch.name); } } // include the merged info for (const branch of Object.values(branches)) { branch.contains = _.compact( await git .raw("branch", "--merged", branch.name) .then((r) => r.replace(/[ *]/g, "")) .then((r) => r.split("\n")) ); } const current = await currentBranch(); if (branches[current]) branches[current].current = true; return branches; }); async function canBeDeleted() { const allBranches = await branches(); return Object.values(allBranches) .filter(fp.get("done")) .filter(({ name, upstream }) => upstream.every((u) => allBranches[u].contains.includes(name)) ) .map(fp.get("name")); } async function canBeWorkedOn() { const allBranches = await branches(); return Object.values(allBranches) .filter(({ done }) => !done) .filter(({ name, dependencies }) => dependencies.every((u) => allBranches[u].done) ) .map(fp.get("name")); } async function needsRebasing() { const allBranches = await branches(); return Object.values(allBranches) .filter(fp.get("base")) .filter(({ base, name }) => !allBranches[name].contains.includes(base)) .map(fp.get("name")); } async function needsMerging() { const allBranches = await branches(); return Object.values(allBranches) .filter(fp.get("done")) .flatMap(({ upstream, name }) => upstream.map((u) => [name, u])) .filter(([name, up]) => !allBranches[up].contains.includes(name)); } module.exports = { currentBranch, branches, canBeDeleted, canBeWorkedOn, needsRebasing, needsMerging, isClean, };