import { isBoolean, isNumber, isString } from 'jsc-utils/type'; import shell from 'shelljs'; import { sync, Commit } from 'conventional-commits-parser'; /** * 判断当前状态是否干净 * @param {string} cwd * @returns {boolean} */ export const isGitStatusClean = (cwd = process.cwd()): boolean => shell.exec('git status --short', { cwd, silent: true }).trim().length === 0; export interface GitStausChange { x: string; y: string; origPath: string; path: string; } // XY PATH // XY ORIG_PATH -> PATH // X Y Meaning // ------------------------------------------------- // [AMD] not updated // M [ MD] updated in index // A [ MD] added to index // D deleted from index // R [ MD] renamed in index // C [ MD] copied in index // [MARC] index and work tree matches // [ MARC] M work tree changed since index // [ MARC] D deleted in work tree // [ D] R renamed in work tree // [ D] C copied in work tree // ------------------------------------------------- // D D unmerged, both deleted // A U unmerged, added by us // U D unmerged, deleted by them // U A unmerged, added by them // D U unmerged, deleted by us // A A unmerged, both added // U U unmerged, both modified // ------------------------------------------------- // ? ? untracked // ! ! ignored // ------------------------------------------------- /** * 解析 git 状态 * @param {string} cwd * @returns {GitStausChange[]} */ export function parseGitStatus(cwd = process.cwd()): GitStausChange[] { return shell .exec('git status --short', { cwd, silent: true }) .toString() .trimEnd() .split(/\n/) .map((change) => { // XY PATH // XY ORIG_PATH -> PATH const x = change.slice(0, 1); const y = change.slice(1, 2); let origPath = ''; let path = change.slice(3); if (/\s->\s/.test(path)) { const index = path.indexOf(' -> '); origPath = path.slice(0, index); path = path.slice(index + 4); } return { x, y, origPath, path }; }); } /** * 获取最后一次提交消息 * @param {string | boolean} cwd * @param {boolean} noMerges * @returns {string} */ export const getLastGitCommitMessage = (cwd?: string | boolean, noMerges?: boolean): string => { let finalCwd = process.cwd(); let finalNoMerges = false; // cwd, noMerges if (isString(cwd) && isBoolean(noMerges)) { finalCwd = cwd; finalNoMerges = noMerges; } // cwd else if (isString(cwd)) { finalCwd = cwd; } // noMerges else if (isBoolean(cwd)) { finalNoMerges = cwd; } const noMergesOption = finalNoMerges ? '--no-merges' : ''; return shell.exec(`git log -1 ${noMergesOption} --pretty=%B`, { cwd: finalCwd, silent: true }).toString().trim(); }; /** * 解析提交消息 * @param {string} message * @returns {conventionalCommitsParser.Commit} */ export const parseGitCommitMessage = (message: string): Commit => sync(message); /** * 获取当前分支的名称 * @param {string} cwd * @returns {string} */ export function getCurrentGitBranch(cwd = process.cwd()): string { return shell.exec('git symbolic-ref --short HEAD', { cwd, silent: true }).toString().trim(); } /** * 获取当前的标签列表 * @param {string} pattern * @param {string} cwd * @returns {string[]} */ export function getCurrentGitTags(pattern: string, cwd = process.cwd()): string[] { const tagStr = shell.exec(`git tag --points-at HEAD --list "${pattern}"`, { cwd, silent: true }).trim(); return tagStr.length > 0 ? tagStr.split(/\n/) : []; } /** * 获取所有的标签列表 * @param {string} pattern * @param {string} cwd * @returns {string[]} */ export function getAllGitTags(pattern: string, cwd = process.cwd()): string[] { const tagStr = shell.exec(`git tag --list "${pattern}"`, { cwd, silent: true }).trim(); return tagStr.length > 0 ? tagStr.split(/\n/) : []; }