UNPKG

workspace-tools

Version:

A collection of utilities that are useful in a git-controlled monorepo managed by one of these tools:

288 lines 13.5 kB
"use strict"; // // Assorted other git utilities // (could be split into separate files later if desired) // Object.defineProperty(exports, "__esModule", { value: true }); exports.listAllTrackedFiles = exports.getDefaultBranch = exports.parseRemoteBranch = exports.getRemoteBranch = exports.getParentBranch = exports.revertLocalChanges = exports.stageAndCommit = exports.commit = exports.stage = exports.init = exports.getFileAddedHash = exports.getCurrentHash = exports.getShortBranchName = exports.getFullBranchRef = exports.getBranchName = exports.getUserEmail = exports.getRecentCommitMessages = exports.getStagedChanges = exports.getChangesBetweenRefs = exports.getBranchChanges = exports.getChanges = exports.getUnstagedChanges = exports.fetchRemoteBranch = exports.fetchRemote = exports.getUntrackedChanges = void 0; const config_1 = require("./config"); const git_1 = require("./git"); const diffArgs = ["--no-pager", "diff", "--name-only", "--relative"]; function getUntrackedChanges(cwdOrOptions) { const options = typeof cwdOrOptions === "string" ? { cwd: cwdOrOptions } : cwdOrOptions; const results = (0, git_1.git)(["ls-files", "--others", "--exclude-standard"], { description: "Gathering information about untracked changes", throwOnError: true, ...options, }); return (0, git_1.processGitOutput)(results, { excludeNodeModules: true }); } exports.getUntrackedChanges = getUntrackedChanges; function fetchRemote(remoteOrOptions, cwd) { const { remote, remoteBranch, options, ...gitOptions } = typeof remoteOrOptions === "string" ? ({ remote: remoteOrOptions, cwd: cwd }) : remoteOrOptions; if (remoteBranch && !remote) { throw new Error('Must provide "remote" when using "remoteBranch" option'); } const fetchArgs = [ "fetch", "--", ...(remote ? [remote] : []), ...(remoteBranch ? [remoteBranch] : []), ...(options || []), ]; (0, git_1.git)(fetchArgs, { description: remote ? `Fetching ${remoteBranch ? `branch "${remoteBranch}" from ` : ""}remote "${remote}"` : "Fetching all remotes", throwOnError: true, ...gitOptions, }); } exports.fetchRemote = fetchRemote; /** * Fetch from the given remote and branch. Throws an error on failure. * @deprecated Use `fetchRemote({ remote, remoteBranch, cwd })` */ // TODO: move to fetch.ts function fetchRemoteBranch(remote, remoteBranch, cwd) { fetchRemote({ remote, remoteBranch, cwd, throwOnError: true }); } exports.fetchRemoteBranch = fetchRemoteBranch; function getUnstagedChanges(cwdOrOptions) { const options = typeof cwdOrOptions === "string" ? { cwd: cwdOrOptions } : cwdOrOptions; const results = (0, git_1.git)(diffArgs, { description: "Gathering information about unstaged changes", throwOnError: true, ...options, }); return (0, git_1.processGitOutput)(results, { excludeNodeModules: true }); } exports.getUnstagedChanges = getUnstagedChanges; /** * Gets file paths with changes between the current branch and the given branch. * Throws an error on failure. * * @returns An array of relative file paths that have changed * @deprecated Use `getBranchChanges({ branch, cwd })` */ // TODO: move to getChanges.ts function getChanges(branch, cwd) { return getChangesBetweenRefs({ fromRef: branch, cwd, throwOnError: true }); } exports.getChanges = getChanges; function getBranchChanges(branchOrOptions, cwd) { const { branch, ...options } = typeof branchOrOptions === "string" ? { branch: branchOrOptions, cwd: cwd } : branchOrOptions; return getChangesBetweenRefs({ fromRef: branch, throwOnError: true, ...options }); } exports.getBranchChanges = getBranchChanges; function getChangesBetweenRefs(fromRef, toRef, options, pattern, cwd) { let gitOptions; if (typeof fromRef === "string") { gitOptions = { cwd: cwd }; } else { ({ fromRef, toRef, options, pattern, ...gitOptions } = fromRef); } const range = `${fromRef}...${toRef || ""}`; const results = (0, git_1.git)([...diffArgs, ...(options || []), range, ...(pattern ? ["--", pattern] : [])], { description: `Gathering information about changes between refs (${range})`, throwOnError: true, ...gitOptions, }); return (0, git_1.processGitOutput)(results, { excludeNodeModules: true }); } exports.getChangesBetweenRefs = getChangesBetweenRefs; function getStagedChanges(cwdOrOptions) { const options = typeof cwdOrOptions === "string" ? { cwd: cwdOrOptions } : cwdOrOptions; const results = (0, git_1.git)([...diffArgs, "--staged"], { description: "Gathering information about staged changes", throwOnError: true, ...options, }); return (0, git_1.processGitOutput)(results, { excludeNodeModules: true }); } exports.getStagedChanges = getStagedChanges; function getRecentCommitMessages(branchOrOptions, cwd) { const { branch, ...options } = typeof branchOrOptions === "string" ? { branch: branchOrOptions, cwd: cwd } : branchOrOptions; const results = (0, git_1.git)(["log", "--decorate", "--pretty=format:%s", `${branch}..HEAD`], { description: `Getting recent commit messages for branch "${branch}"`, ...options, }); return (0, git_1.processGitOutput)(results); } exports.getRecentCommitMessages = getRecentCommitMessages; function getUserEmail(cwdOrOptions) { const options = typeof cwdOrOptions === "string" ? { cwd: cwdOrOptions } : cwdOrOptions; return (0, config_1.getConfigValue)({ key: "user.email", ...options }); } exports.getUserEmail = getUserEmail; function getBranchName(cwdOrOptions) { const options = typeof cwdOrOptions === "string" ? { cwd: cwdOrOptions } : cwdOrOptions; const results = (0, git_1.git)(["rev-parse", "--abbrev-ref", "HEAD"], { description: "Getting current branch name", ...options, }); return results.success ? results.stdout : null; } exports.getBranchName = getBranchName; function getFullBranchRef(branchOrOptions, cwd) { const { branch, ...options } = typeof branchOrOptions === "string" ? { branch: branchOrOptions, cwd: cwd } : branchOrOptions; const showRefResults = (0, git_1.git)(["show-ref", "--heads", branch], options); return showRefResults.success ? showRefResults.stdout.split(" ")[1] : null; } exports.getFullBranchRef = getFullBranchRef; function getShortBranchName(refOrOptions, cwd) { const { fullBranchRef, ...options } = typeof refOrOptions === "string" ? { fullBranchRef: refOrOptions, cwd: cwd } : refOrOptions; // The original command `git name-rev --name-only` returned unreliable results if multiple // named refs point to the same commit as the branch. const showRefResults = (0, git_1.git)(["rev-parse", "--abbrev-ref", fullBranchRef], options); return showRefResults.success ? showRefResults.stdout || null : null; } exports.getShortBranchName = getShortBranchName; function getCurrentHash(cwdOrOptions) { const options = typeof cwdOrOptions === "string" ? { cwd: cwdOrOptions } : cwdOrOptions; const results = (0, git_1.git)(["rev-parse", "HEAD"], { description: "Getting current git hash", ...options, }); return results.success ? results.stdout : null; } exports.getCurrentHash = getCurrentHash; function getFileAddedHash(filenameOrOptions, cwd) { const { filename, ...options } = typeof filenameOrOptions === "string" ? { filename: filenameOrOptions, cwd: cwd } : filenameOrOptions; const results = (0, git_1.git)(["rev-list", "--max-count=1", "HEAD", filename], options); return results.success ? results.stdout.trim() : undefined; } exports.getFileAddedHash = getFileAddedHash; function init(cwdOrOptions, _email, _username) { const { email, username, ...options } = typeof cwdOrOptions === "string" ? { cwd: cwdOrOptions, email: _email, username: _username } : cwdOrOptions; (0, git_1.git)(["init"], { ...options, throwOnError: true }); if (!(0, config_1.getConfigValue)({ key: "user.name", ...options })) { if (!username) { throw new Error("must include a username when initializing git repo"); } (0, git_1.git)(["config", "user.name", username], options); } if (!getUserEmail(options)) { if (!email) { throw new Error("must include a email when initializing git repo"); } (0, git_1.git)(["config", "user.email", email], options); } } exports.init = init; function stage(patternsOrOptions, cwd) { const { patterns, ...options } = Array.isArray(patternsOrOptions) ? { patterns: patternsOrOptions, cwd: cwd } : patternsOrOptions; for (const pattern of patterns) { (0, git_1.git)(["add", pattern], { ...options, description: `Staging changes (git add ${pattern})` }); } } exports.stage = stage; function commit(messageOrOptions, _cwd, _options) { const { message, options, ...gitOptions } = typeof messageOrOptions === "string" ? { message: messageOrOptions, cwd: _cwd, options: _options } : messageOrOptions; (0, git_1.git)(["commit", "-m", message, ...(options || [])], { throwOnError: true, description: "Committing changes", ...gitOptions, }); } exports.commit = commit; function stageAndCommit(patternsOrOptions, message, cwd, commitOptions) { const options = Array.isArray(patternsOrOptions) ? { patterns: patternsOrOptions, message: message, cwd: cwd, options: commitOptions } : patternsOrOptions; stage(options); commit(options); } exports.stageAndCommit = stageAndCommit; function revertLocalChanges(cwdOrOptions) { const options = typeof cwdOrOptions === "string" ? { cwd: cwdOrOptions } : cwdOrOptions; const stash = `workspace-tools_${new Date().getTime()}`; if (!(0, git_1.git)(["stash", "push", "-u", "-m", stash], options).success) { return false; } const results = (0, git_1.git)(["stash", "list"], options); if (results.success) { const matched = results.stdout .split(/\n/) .find((line) => line.includes(stash)) ?.match(/^[^:]+/); if (matched) { (0, git_1.git)(["stash", "drop", matched[0]], options); return true; } } return false; } exports.revertLocalChanges = revertLocalChanges; /** * Attempts to determine the parent branch of the current branch using `git show-branch`. * @returns The parent branch name if found, null otherwise * @deprecated Does not appear to be used */ function getParentBranch(cwd) { const branchName = getBranchName({ cwd }); if (!branchName || branchName === "HEAD") { return null; } const showBranchResult = (0, git_1.git)(["show-branch", "-a"], { cwd }); if (showBranchResult.success) { const showBranchLines = showBranchResult.stdout.split(/\n/); const parentLine = showBranchLines.find((line) => line.includes("*") && !line.includes(branchName) && !line.includes("publish_")); const matched = parentLine?.match(/\[(.*)\]/); return matched ? matched[1] : null; } return null; } exports.getParentBranch = getParentBranch; function getRemoteBranch(branchOrOptions, cwd) { const options = typeof branchOrOptions === "string" ? { branch: branchOrOptions, cwd: cwd } : branchOrOptions; const results = (0, git_1.git)(["rev-parse", "--abbrev-ref", "--symbolic-full-name", `${options.branch}@\{u\}`], options); return results.success ? results.stdout.trim() : null; } exports.getRemoteBranch = getRemoteBranch; function parseRemoteBranch(branchOrOptions) { if (typeof branchOrOptions === "string") { const branch = branchOrOptions; const firstSlashPos = branch.indexOf("/", 0); return { remote: branch.substring(0, firstSlashPos), remoteBranch: branch.substring(firstSlashPos + 1), }; } const { branch, knownRemotes = ["origin", "upstream"], ...options } = branchOrOptions; if (!branch.includes("/")) { return { remote: "", remoteBranch: branch }; } let remote = knownRemotes.find((remote) => branch.startsWith(`${remote}/`)); if (!remote) { const remotes = (0, git_1.git)(["remote"], options).stdout.trim().split(/\n/); remote = remotes.find((remote) => branch.startsWith(`${remote}/`)); } if (remote) { return { remote, remoteBranch: branch.slice(remote.length + 1) }; } return { remote: "", remoteBranch: branch }; } exports.parseRemoteBranch = parseRemoteBranch; function getDefaultBranch(cwdOrOptions) { const options = typeof cwdOrOptions === "string" ? { cwd: cwdOrOptions } : cwdOrOptions; // Default to the legacy 'master' for backwards compat and old git clients return (0, config_1.getConfigValue)({ key: "init.defaultBranch", ...options }) || "master"; } exports.getDefaultBranch = getDefaultBranch; function listAllTrackedFiles(patternsOrOptions, cwd) { const { patterns, ...options } = Array.isArray(patternsOrOptions) ? { patterns: patternsOrOptions, cwd: cwd } : patternsOrOptions; const results = (0, git_1.git)(["ls-files", ...patterns], { throwOnError: true, ...options }); return (0, git_1.processGitOutput)(results); } exports.listAllTrackedFiles = listAllTrackedFiles; //# sourceMappingURL=gitUtilities.js.map