import { execSync } from 'node:child_process' import { basename } from 'node:path' import type { UnixTimestamp } from '@naturalcycles/js-lib/types' import { exec2 } from '../exec2/exec2.js' /** * Set of utility functions to work with git. */ class Git2 { getLastGitCommitMsg(): string { return exec2.exec('git log -1 --pretty=%B') } commitMessageToTitleMessage(msg: string): string { const firstLine = msg.split('\n')[0]! const [preTitle, title] = firstLine.split(': ') return title || preTitle! } hasUncommittedChanges(): boolean { // git diff-index --quiet HEAD -- || echo "untracked" try { execSync('git diff-index --quiet HEAD --', { encoding: 'utf8', }) return false } catch { return true } } /** * Returns true if there were changes */ commitAll(msg: string): boolean { // git commit -a -m "style(lint-all): $GIT_MSG" || true const cmd = `git commit -a --no-verify -m "${msg}"` // const cmd = `git` // const args = ['commit', '-a', '--no-verify', '-m', msg] console.log(cmd) try { execSync(cmd, { stdio: 'inherit', }) return true } catch { return false } } /** * @returns true if there are not pushed commits. */ isAhead(): boolean { // ahead=`git rev-list HEAD --not --remotes | wc -l | awk '{print $1}'` const cmd = `git rev-list HEAD --not --remotes | wc -l | awk '{print $1}'` const stdout = exec2.exec(cmd) // console.log(`gitIsAhead: ${stdout}`) return Number(stdout) > 0 } fetch(): void { const cmd = 'git fetch' try { execSync(cmd, { stdio: 'inherit', }) } catch {} } pull(): void { const cmd = 'git pull' try { execSync(cmd, { stdio: 'inherit', }) } catch {} } push(): void { // git push --set-upstream origin $CIRCLE_BRANCH && echo "pushed, exiting" && exit 0 let cmd = 'git push' const branchName = this.getCurrentBranchName() if (branchName) { cmd += ` --set-upstream origin ${branchName}` } exec2.spawn(cmd, { logStart: true }) } getCurrentCommitSha(full = false): string { const sha = exec2.exec('git rev-parse HEAD') return full ? sha : sha.slice(0, 7) } getCurrentCommitTimestamp(): UnixTimestamp { return Number(exec2.exec('git log -1 --format=%ct')) as UnixTimestamp } getCurrentBranchName(): string { return exec2.exec('git rev-parse --abbrev-ref HEAD') } getCurrentRepoName(): string { const originUrl = exec2.exec('git config --get remote.origin.url') return basename(originUrl, '.git') } getAllBranchesNames(): string[] { /** * Raw output example from this repository: * $ git branch -r * origin/DEV-22569-zod-isoDate-params * origin/DEV-23052-git2-add-method * origin/HEAD -> origin/main * origin/add-requiredpick-type-helper * origin/better-object-keys * origin/feat/immutable-j-1 * origin/feat/immutable-j-2 * origin/feat/random-string-util * origin/fix-vsc-red-line * origin/generic-cachekey-function-2 * origin/gh-pages * origin/main * origin/stringify-or-undefined */ return exec2 .exec('git branch -r') .split('\n') .map(s => s.trim()) .filter(s => !s.includes(' -> ')) .map(s => s.split('/')[1]!) } } export const git2 = new Git2()