Chunk command output when reading git show and git blame

330625f37de2c1aec7240ae32780199d4e6aa01c

Tucker McKnight <tmcknight@instructure.com> | Thu Jun 18 2026

Chunk command output when reading git show and git blame

Previously, large files were causing it to crash with an stdout
buffer overflow error.
main.ts:195
Before
194
195
196

197
198
199
200
  })

  eleventyConfig.addAsyncFilter("renderContentIfAvailable", async (contentString: string, branchName: string) => {
⁣
    const renderer = eleventyConfig?.javascript?.functions?.renderContent
    if (renderer) {
      return await renderer.bind({
        data: {
          branch: branchName
After
194
195
196
197
198
199
200
201
  })

  eleventyConfig.addAsyncFilter("renderContentIfAvailable", async (contentString: string, branchName: string) => {
    // TODO: give the user an option to skip this. E.g. when doing the 11ty repo, complains about missing tags.
    const renderer = eleventyConfig?.javascript?.functions?.renderContent
    if (false && renderer) {
      return await renderer.bind({
        data: {
          branch: branchName
src/helpers.ts:1
Before
0
1
i⁣
mport escape from 'escape-html'
import * as Diff from 'diff'
import {type Repository} from './dataTypes.ts'
After
0
1
2
import childProcess from 'child_process'
import escape from 'escape-html'
import * as Diff from 'diff'
import {type Repository} from './dataTypes.ts'
src/helpers.ts:50
Before
49
50
51


52
53
54
        if (str.startsWith(' ')) { return str.replace(" ", "") }
      }).join("\n")

⁣
⁣
      const changeObject = Diff.diffWordsWithSpace(lastHunkBefore, lastHunkAfter)
      let beforeText = ""
      let afterText = ""
After
49
50
51
52
53
54
55
56
        if (str.startsWith(' ')) { return str.replace(" ", "") }
      }).join("\n")

      const changeObject = (lastHunkBefore.length > 1000 || lastHunkAfter.length > 1000)
        ? []
        : Diff.diffWordsWithSpace(lastHunkBefore, lastHunkAfter)
      let beforeText = ""
      let afterText = ""
src/helpers.ts:99
Before
98
99
100

















101
102
103
  return outputDir + (reposConfig.path || "") + "/" + slugify(repoName) + ".git"
}

⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
export {
  getGitDiffsFromPatchText,
  getLocation,
⁣
}
After
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
  return outputDir + (reposConfig.path || "") + "/" + slugify(repoName) + ".git"
}

const readChunkedCommandOutput = async (executable, args): Promise<string> => {
  return new Promise((resolve) => {
    const cmd = childProcess.spawn(executable, args, {
      stdio: [0, "pipe", "inherit"],
    })
    let output = ""

    cmd.stdout.on('data', (data) => {
      output = output + data
    })

    cmd.on('close', () => {
      resolve(output)
    })
  })
}

export {
  getGitDiffsFromPatchText,
  getLocation,
  readChunkedCommandOutput,
}
src/repos.ts:9
Before
8
9
10
11
12
13
import { type Repository, type FileInfo } from './dataTypes.ts'
import cloneUrl from './vcses/git/helpers.ts'
import { addBranchToCommitsMap } from './vcses/git/operations.ts'
import { getLocation} from './helpers.ts'
import { getFileList, getFileLastTouchInfo } from './vcses/git/operations.ts'

const exec = util.promisify(childProcess.exec)
After
8
9
10
11
12
13
import { type Repository, type FileInfo } from './dataTypes.ts'
import cloneUrl from './vcses/git/helpers.ts'
import { addBranchToCommitsMap } from './vcses/git/operations.ts'
import { getLocation, readChunkedCommandOutput } from './helpers.ts'
import { getFileList, getFileLastTouchInfo } from './vcses/git/operations.ts'

const exec = util.promisify(childProcess.exec)
src/repos.ts:251
Before
250
251
252
253
254
255
256
            currentCommit = commits.get(currentCommit.parent)
          }
        }
        const gitShowCmd = `git -C ${repoLocation} show ${sha}:${filename}`
        const fileContents = (await exec(gitShowCmd)).stdout
        const blameLines = await getFileLastTouchInfo(filename, sha, repoLocation)
        const commit = {
          contents: fileContents,
After
250
251
252

253
254
255
            currentCommit = commits.get(currentCommit.parent)
          }
        }
⁣
        const fileContents = await readChunkedCommandOutput('git', ['-C',  repoLocation, 'show', `${sha}:${filename}`])
        const blameLines = await getFileLastTouchInfo(filename, sha, repoLocation)
        const commit = {
          contents: fileContents,
src/vcses/git/operations.ts:1
Before
0
1
2
3
4
5
import util from 'util'
import childProcess from 'child_process'
const exec = util.promisify(childProcess.exec)
import { getGitDiffsFromPatchText} from '../../helpers.ts'
import { type Repository } from '../../dataTypes.ts'

export const getFileList = async (
After
0
1
2
3
4
5
import util from 'util'
import childProcess from 'child_process'
const exec = util.promisify(childProcess.exec)
import { getGitDiffsFromPatchText, readChunkedCommandOutput } from '../../helpers.ts'
import { type Repository } from '../../dataTypes.ts'

export const getFileList = async (
src/vcses/git/operations.ts:100
Before
99
100
101

102
103
      return !line.startsWith('Ignore-this: ')
    }).join("\n").trim()

⁣
    const diffs = getGitDiffsFromPatchText(currentPatch.slice(diffStart).join("\n"))
    commits.set(hash, {
      hash,
After
99
100
101
102
103
104
      return !line.startsWith('Ignore-this: ')
    }).join("\n").trim()

    // TODO: this is very slow on commits that have a huge 20k line package-lock.json change
    const diffs = getGitDiffsFromPatchText(currentPatch.slice(diffStart).join("\n"))
    commits.set(hash, {
      hash,
src/vcses/git/operations.ts:123
Before
122
123
124
125
126
127
128
129
  filename: string, sha: string, repoLocation: string
) : Promise<Array<BlameInfo>> => {
  const regex = RegExp(".* [0-9]+ [0-9]+")
  const command = `git -C ${repoLocation} blame --porcelain ${sha} ${filename}`
  const res = await exec(command)
  const output = res.stdout
  const outputLines = output.split("\n")
  const initialValue: Array<Array<string>> = [[outputLines[0]]]
  const chunked = outputLines.reduce((accumulator, currentLine, currentIndex) => {
After
122
123
124

125

126
127
  filename: string, sha: string, repoLocation: string
) : Promise<Array<BlameInfo>> => {
  const regex = RegExp(".* [0-9]+ [0-9]+")
⁣
  const output = await readChunkedCommandOutput('git', ['-C', repoLocation, 'blame', '--porcelain', sha, filename])
⁣
  const outputLines = output.split("\n")
  const initialValue: Array<Array<string>> = [[outputLines[0]]]
  const chunked = outputLines.reduce((accumulator, currentLine, currentIndex) => {