parent
1f676d6b68
commit
0eb0840f10
@ -18,6 +18,7 @@
|
|||||||
"columnify": "^1.5.4",
|
"columnify": "^1.5.4",
|
||||||
"inquirer": "^7.3.3",
|
"inquirer": "^7.3.3",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
"node-emoji": "^1.11.0",
|
||||||
"simple-git": "^2.47.0",
|
"simple-git": "^2.47.0",
|
||||||
"stringify-tree": "^1.1.1",
|
"stringify-tree": "^1.1.1",
|
||||||
"updeep": "^1.2.1",
|
"updeep": "^1.2.1",
|
||||||
|
122
src/branches.js
Normal file
122
src/branches.js
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
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,
|
||||||
|
};
|
@ -7,6 +7,9 @@ const u = require("updeep");
|
|||||||
const columnify = require("columnify");
|
const columnify = require("columnify");
|
||||||
const chalk = require("chalk");
|
const chalk = require("chalk");
|
||||||
|
|
||||||
|
const myGit = require('../branches');
|
||||||
|
const log = require('../log');
|
||||||
|
|
||||||
const iconFor = ( branch, branches ) => {
|
const iconFor = ( branch, branches ) => {
|
||||||
if(branch.done ) return "✅";
|
if(branch.done ) return "✅";
|
||||||
|
|
||||||
@ -18,60 +21,78 @@ const iconFor = ( branch, branches ) => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = async (yargs) => {
|
const decorateCurrent = async branch => {
|
||||||
const config = (await Git().listConfig("local")).all;
|
if( branch !== await myGit.currentBranch() ) return branch;
|
||||||
|
|
||||||
let tree = Object.entries(config).reduce(
|
return chalk.underline.bold(branch);
|
||||||
(accum, [key, value]) => u.updateIn(key, value, accum),
|
}
|
||||||
{}
|
|
||||||
|
async function printNeedsRebasing() {
|
||||||
|
const needsRebasing = await myGit.needsRebasing();
|
||||||
|
|
||||||
|
if( !needsRebasing.length ) return false;
|
||||||
|
|
||||||
|
log.subtitle(':arrow_heading_up: needs rebasing');
|
||||||
|
const b = await Promise.all(
|
||||||
|
needsRebasing.map( decorateCurrent )
|
||||||
);
|
);
|
||||||
|
log.info( "\t", b.join(', ') );
|
||||||
|
|
||||||
const branches = {};
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const normalizeArray = (value) =>
|
async function printNeedsMerging() {
|
||||||
Array.isArray(value) ? value : [value].filter((x) => x);
|
const needsMerging = await myGit.needsMerging();
|
||||||
|
|
||||||
const initBranch = (config) =>
|
if(!needsMerging.length) return false;
|
||||||
defaults(
|
|
||||||
{
|
log.subtitle(':thumbsup: ready to be merged');
|
||||||
upstream: [],
|
log.info( "\t", needsMerging.map( x => x.join('->') ).join(', ') );
|
||||||
dependencies: [],
|
|
||||||
},
|
return true;
|
||||||
config
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function printActionables() {
|
||||||
|
|
||||||
|
log.title(":point_right: What's next?");
|
||||||
|
|
||||||
|
let something = false;
|
||||||
|
const accumSomething = (value) => something ||= value;
|
||||||
|
|
||||||
|
accumSomething( await printNeedsRebasing() );
|
||||||
|
|
||||||
|
accumSomething( await printNeedsMerging() );
|
||||||
|
|
||||||
|
const canBeDeleted = await myGit.canBeDeleted();
|
||||||
|
|
||||||
|
if( canBeDeleted.length ) {
|
||||||
|
something = true;
|
||||||
|
|
||||||
|
log.subtitle(':recycle: branches that can be deleted');
|
||||||
|
log.info( "\t", canBeDeleted.join(', ') );
|
||||||
|
}
|
||||||
|
|
||||||
|
const canBeWorkedOn = await myGit.canBeWorkedOn();
|
||||||
|
|
||||||
|
if( canBeWorkedOn.length ) {
|
||||||
|
something = true;
|
||||||
|
|
||||||
|
log.subtitle(':hammer: ready to be worked on');
|
||||||
|
const b = await Promise.all(
|
||||||
|
canBeWorkedOn.map( decorateCurrent )
|
||||||
);
|
);
|
||||||
|
log.info( "\t", b.join(', ') );
|
||||||
const current = (await Git().raw("branch", "--show-current")).replace(
|
|
||||||
"\n",
|
|
||||||
""
|
|
||||||
);
|
|
||||||
|
|
||||||
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)) {
|
if(!something) {
|
||||||
for (const ups of branch["upstream"]) {
|
log.info("nothing? :shrug:");
|
||||||
if (!branches[ups])
|
|
||||||
branches[ups] = initBranch({
|
|
||||||
name: ups,
|
|
||||||
upstream: [],
|
|
||||||
dependencies: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
branches[ups].dependencies.push(branch.name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (branches[current]) branches[current].current = true;
|
}
|
||||||
|
|
||||||
|
module.exports = async () => {
|
||||||
|
const branches = await myGit.branches();
|
||||||
|
|
||||||
const depColor = (current) => async (dep) => {
|
const depColor = (current) => async (dep) => {
|
||||||
let color = branches[dep].done ? chalk.green : chalk.red;
|
let color = branches[dep].done ? chalk.green : chalk.red;
|
||||||
@ -99,7 +120,7 @@ module.exports = async (yargs) => {
|
|||||||
return color(up);
|
return color(up);
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("\n=== Mikado status ===\n");
|
log.title(":chopsticks: Mikado status\n");
|
||||||
|
|
||||||
const sorted = _.sortBy(Object.values(branches), ["current"]);
|
const sorted = _.sortBy(Object.values(branches), ["current"]);
|
||||||
|
|
||||||
@ -126,4 +147,7 @@ module.exports = async (yargs) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await printActionables();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
12
src/log.js
Normal file
12
src/log.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const emoji = require('node-emoji');
|
||||||
|
const chalk = require("chalk");
|
||||||
|
|
||||||
|
function groomLog(...entries) {
|
||||||
|
console.log( ...(entries.map(emoji.emojify)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
info: groomLog,
|
||||||
|
title: title => groomLog("\n",chalk.blue.bold(title)),
|
||||||
|
subtitle: title => groomLog("\n",chalk.blue(title)),
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user