diff --git a/plugins/status.js b/plugins/status.js index d5859a2..5c499be 100644 --- a/plugins/status.js +++ b/plugins/status.js @@ -11,6 +11,8 @@ var exec = require('child_process').exec; module.exports = function(req, res, next) { var cwd = req.path, + trackingRegex = /\[(?:ahead (\d+))?(?:, )?(?:behind (\d+))?\]/, + fileStates = ['M','A','D','R','C'], tags, dirname = path.dirname(cwd).replace(req.gr.homePath, '~') + path.sep, repos = (req.gr.directories ? req.gr.directories : []), @@ -18,11 +20,77 @@ module.exports = function(req, res, next) { return Math.max(prev, current.replace(req.gr.homePath, '~').length + 2); }, 0); + function cloneState(lines) { + var stage, workTree, + i = 0, + flags = 0; // 0b100: modified, 0b010: staged, 0b001: untracked + // stop parsing if all flags are set (0b111) + while (i < lines.length && lines[i].length > 1 && flags !== 7) { + stage = lines[i].charAt(0); + workTree = lines[i++].charAt(1); + if (stage === 'U' || workTree === 'U') { + return ['MERGE', 'red']; // early exit + } + if ((flags & 2) === 0 && fileStates.includes(stage)) { + flags += 2; + } + if ((flags & 4) === 0 && fileStates.includes(workTree)) { + flags += 4; + } + if ((flags & 1) === 0 && workTree === '?') { + flags += 1; + } + } + + switch (flags) { + case 0: + return ['clean', 'green']; + case 1: + return ['% ', 'green']; + case 2: + return ['+ ', 'red']; + case 3: + return ['+% ', 'red']; + case 4: + return ['* ', 'red']; + case 5: + return ['*% ', 'red']; + case 6: + return ['+* ', 'red']; + case 7: + return ['+*% ', 'red']; + } + } + function pad(s, len) { return (s.toString().length < len ? new Array(len - s.toString().length).join(' ') : ''); } + function remoteInfo(porcelain) { + var display = 'u', + parsed; + if (!porcelain) { + return style('no upstream', 'gray'); // early exit + } + + parsed = porcelain.match(trackingRegex) || ['', undefined, undefined]; + if (parsed[1]) { + display = display + '+' + parsed[1]; + } + if (parsed[2]) { + display = display + '-' + parsed[2]; + } + + if (!parsed[1] && !parsed[2]) { + return ' '; // neither ahead nor behind (directly padded) + } else if (parsed[1] && parsed[2]) { + return style(display, 'red') + pad(display, 12); // both + } else { + return display + pad(display, 12); // either ahead or behind + } + } + // force human format, makes commandRequirements print out when skipping if (!commandRequirements.git({ format: 'json', path: cwd })) { @@ -38,7 +106,7 @@ module.exports = function(req, res, next) { // search for matching tags tags = req.gr.getTagsByPath(cwd); - if (req.argv.length > 0 && req.argv[0] == '-v') { + if (req.argv.length > 0 && req.argv.includes('-v')) { console.log( style('\nin ' + dirname, 'gray') + style(path.basename(cwd), 'white') + '\n' @@ -50,30 +118,27 @@ module.exports = function(req, res, next) { cwd: cwd, maxBuffer: 1024 * 1024 // 1Mb }, function(err, stdout, stderr) { - var lines = stdout.split('\n').filter(function(line) { - return !!line.trim(); - }); + var lines = stdout.split('\n'); //remove the branch info so it isn't counted as a change - var branchInfo = lines.shift(); + var branchInfo = lines.shift().slice(3).split('...', 2); // parse - var behind = (branchInfo || '').match(/(\[.+\])/g) || '', - modified = (lines.length > 0 ? - lines.length + ' modified' : - 'Clean' - ); - - var branchName = branchInfo.slice(3).split('...', 1)[0]; - - console.log( - style(dirname, 'gray') + - style(path.basename(cwd), 'white') + pad(dirname + path.basename(cwd), pathMaxLen) + ' ' + - branchName + pad(branchName, 15) + ' ' + - style(modified, (lines.length > 0 ? 'red' : 'green')) + pad(modified, 14) + - behind + pad(behind, 14) + - tags.map(function(s) { return '@' + s; }).join(' ') - ); + var branchName = branchInfo[0], + behind = remoteInfo(branchInfo[1]), + modified = cloneState(lines), + pname = path.basename(cwd), + printed; + + printed = style(dirname, 'gray') + + style(pname, 'white') + pad(dirname + pname, pathMaxLen) + ' ' + + branchName + pad(branchName, 24) + ' ' + + style(modified[0], modified[1]) + ' ' + + behind; + if (req.argv.length === 0 || !req.argv.includes('-q')) { + printed += tags.map(function(s) { return '@' + s; }).join(' '); + } + console.log(printed); if (err !== null) { console.log('exec error: ' + err); if (stderr) {