SPB Git

spb/spbgit Public MIT

SPB Git — the platform hosting itself

JavaScript 73.9% CSS 11.7% Nunjucks 11.6% Shell 2.7%
2.6 KB · 62 lines javascript
Raw Blame History
1/**2 * ─────────────────────────────────────────────3 *  SPB Git — Personal Git Platform4 * ─────────────────────────────────────────────5 *  Author  : Simon-Pierre Boucher6 *  Contact : contact@spboucher.ai7 *  File    : cli/commands/status.mjs8 *  Purpose : `spbgit status` — aggregated status across workspace repos9 *  License : MIT © Simon-Pierre Boucher10 * ─────────────────────────────────────────────11 */1213import { basename, join } from 'node:path';14import { existsSync } from 'node:fs';15import pc from 'picocolors';16import { requireCliConfig } from '../lib/config.mjs';17import { workspaceRepos, repoStatus } from '../lib/git.mjs';18import { printTable, mapLimit } from '../lib/ui.mjs';19import { EXIT } from '../lib/api.mjs';2021export function registerStatus(program) {22  program23    .command('status [name]')24    .description('aggregated status of workspace repositories')25    .option('--all', 'include clean repositories (default)', false)26    .option('--json', 'machine-readable output')27    .action(async (name, opts) => {28      const config = requireCliConfig();29      let dirs = workspaceRepos(config.workspace);30      if (name) {31        const target = join(config.workspace, name);32        if (!existsSync(target)) {33          console.error(pc.red(`✗ No such workspace repo: ${name}`));34          process.exit(EXIT.USER);35        }36        dirs = [target];37      }38      if (dirs.length === 0) {39        console.log(pc.dim(`Workspace ${config.workspace} has no repositories. Try: spbgit clone --all`));40        return;41      }42      const statuses = await mapLimit(dirs, 4, async (dir) => ({ dir, name: basename(dir), ...(await repoStatus(dir)) }));43      if (opts.json) {44        console.log(JSON.stringify(statuses, null, 2));45        return;46      }47      const rows = [[pc.bold('REPO'), pc.bold('BRANCH'), pc.bold('AHEAD'), pc.bold('BEHIND'), pc.bold('DIRTY')]];48      for (const s of statuses) {49        const clean = s.ahead === 0 && s.behind === 0 && s.dirty === 0;50        const paint = clean ? pc.dim : (x) => x;51        rows.push([52          paint(s.name),53          paint(s.branch + (s.hasUpstream ? '' : ' (no upstream)')),54          s.ahead > 0 ? pc.cyan(`↑${s.ahead}`) : paint('0'),55          s.behind > 0 ? pc.yellow(`↓${s.behind}`) : paint('0'),56          s.dirty > 0 ? pc.red(String(s.dirty)) : paint('0'),57        ]);58      }59      printTable(rows);60    });61}62