Skip to content

Instantly share code, notes, and snippets.

@fsubal
Last active May 28, 2025 11:23
Show Gist options
  • Select an option

  • Save fsubal/4d663c9fad65724c57993d2da1873605 to your computer and use it in GitHub Desktop.

Select an option

Save fsubal/4d663c9fad65724c57993d2da1873605 to your computer and use it in GitHub Desktop.
import { resolve } from 'node:path'
import { readFile } from 'node:fs/promises'
import { exec } from 'node:child_process'
import { promisify } from 'node:util'
const execp = promisify(exec)
/**
* @see https://git-scm.com/docs/git-status#_porcelain_format_version_1
*/
const PATTERN = /( M| D|\?\?)\s*(.+)/
function parseGitStatus(line: string) {
const [, status, path] = line.match(PATTERN) ?? []
switch (status) {
case ' M': {
return [path, 'modified'] as const
}
case ' D': {
return [path, 'deleted'] as const
}
case '??': {
return [path, 'added'] as const
}
default: {
throw new TypeError(`Could not parse git status result: (${line})`)
}
}
}
async function loadGitStatus(dirname = process.cwd()) {
const lines = await execp('git status --porcelain').then(({ stdout }) => stdout.trim().split('\n'))
const parsedStatus = new Map(lines.map(parseGitStatus))
return {
async *[Symbol.asyncIterator]() {
for (const [relativePath, status] of parsedStatus) {
const fullpath = resolve(dirname, relativePath)
const content = await readFile(fullpath, { encoding: 'utf8' })
yield { fullpath, status, content }
}
}
}
}
const gitStatus = await loadGitStatus()
for await (const changedFile of gitStatus) {
console.log(changedFile.status, changedFile.fullpath, changedFile.content)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment