git-mikado/src/branches.js

123 lines
3.2 KiB
JavaScript

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 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,
};