Mistaken idea about having each branch have a full list of commits

bcdc647c53c3fcec3b55781bd0a413a77cb9bac8

Tucker McKnight <tmcknight@instructure.com> | Mon May 18 2026

Mistaken idea about having each branch have a full list of commits

The previous commit, in the file-caching branch, also is based on this
idea. I'll need to undo that as well.

Putting this in a branch and saving it somewhere so that I can more easily
see what I was doing to undo it later.
src/flatPatches.ts:18
Before
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
      const flatPatches: Array<FlatPatchRecord> = []
      let currentCommit: ReturnType<Repository['commits']['get']> | undefined = repo.commits.get(branch.sha)

      while (currentCommit !== undefined) {
        flatPatches.push({
          type: "branch",
          commit: currentCommit,
          repoName: repo.name,
          refName: branch.name
        })

        currentCommit = repo.commits.get(currentCommit.parent)
      }

      return flatPatches
    })
After
17
18
19
20
21
22
23
24
25
26


27
28
29
      const flatPatches: Array<FlatPatchRecord> = []
      let currentCommit: ReturnType<Repository['commits']['get']> | undefined = repo.commits.get(branch.sha)

      flatPatches.concat(branch.commits.map((commit) => {
        return {
          type: "branch",
          commit,
          repoName: repo.name,
          refName: branch.name
        }
⁣
⁣
      })

      return flatPatches
    })
src/flatPatches.ts:35
Before
34
35
36










37
38
    const tags = repo.tags.flatMap((tag) => {
      const flatPatches: Array<FlatPatchRecord> = []
      let currentCommit: ReturnType<Repository['commits']['get']> = repo.commits.get(tag.sha)
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
      while (currentCommit !== undefined) {
        flatPatches.push({
          type: "tag",
After
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
    const tags = repo.tags.flatMap((tag) => {
      const flatPatches: Array<FlatPatchRecord> = []
      let currentCommit: ReturnType<Repository['commits']['get']> = repo.commits.get(tag.sha)

      flatPatches.concat(branch.commits.map((commit) => {
        return {
          type: "tag",
          commit,
          repoName: repo.name,
          refName: branch.name
        }
      })

      while (currentCommit !== undefined) {
        flatPatches.push({
          type: "tag",
src/repos.ts:6
Before
5
6
7
8
9
10
import childProcess from 'child_process'
import {minimatch} from 'minimatch'

import { type Repository} from './dataTypes.ts'
import cloneUrl from './vcses/git/helpers.ts'
import { addBranchToCommitsMap } from './vcses/git/operations.ts'
import { getLocation} from './helpers.ts'
After
5
6
7
8
9
10
import childProcess from 'child_process'
import {minimatch} from 'minimatch'

import { type Repository, type Commit } from './dataTypes.ts'
import cloneUrl from './vcses/git/helpers.ts'
import { addBranchToCommitsMap } from './vcses/git/operations.ts'
import { getLocation} from './helpers.ts'
src/repos.ts:149
Before
148
149
150

151
152

153
154


155
156
    const commits: Repository['commits'] = new Map()
    const branchesAndTags = await getBranchesAndTags(reposConfig.repos[repoName], repoName)
    const branchNames = branchesAndTags.branches.map(branch => branch.name)
⁣
    for (const branchName of branchNames) {
      await addBranchToCommitsMap(branchName, repoLocation, commits)
⁣
    }

⁣
⁣
    const branches = await Promise.all(branchesAndTags.branches.map(async (branch) => {
      const repoLocation = getLocation(reposConfig, outputDir, repoName, slugify)
      const branchHeadRes = await exec(`git -C ${repoLocation} show-ref refs/heads/${branch.name}`)
After
148
149
150
151
152
153
154
155
156
157
158
159
160
    const commits: Repository['commits'] = new Map()
    const branchesAndTags = await getBranchesAndTags(reposConfig.repos[repoName], repoName)
    const branchNames = branchesAndTags.branches.map(branch => branch.name)
    const branchCommits: Map<string, Array<Commit>> = new Map()
    for (const branchName of branchNames) {
      branchCommits.set(branchName, await addBranchToCommitsMap(branchName, repoLocation, commits))
      // once we've iterated through here, the commits map is fully filled in
    }

// TODO: don't have this happen in a second loop that waits for things -- can this all
// be done at once?
    const branches = await Promise.all(branchesAndTags.branches.map(async (branch) => {
      const repoLocation = getLocation(reposConfig, outputDir, repoName, slugify)
      const branchHeadRes = await exec(`git -C ${repoLocation} show-ref refs/heads/${branch.name}`)
src/repos.ts:160
Before
159
160
161
162

163
164
      const result = {
        name: branch.name,
        sha: branchHead,
        fileList: await getFileList(branch.name, repoLocation)
⁣
      }
      if (branch.description) { result['description'] = branch.description }
      if (branch.compareTo) { result['compareTo'] = branch.compareTo }
After
159
160
161
162
163
164
165
      const result = {
        name: branch.name,
        sha: branchHead,
        fileList: await getFileList(branch.name, repoLocation),
        commits: branchCommits.get(branch.name)
      }
      if (branch.description) { result['description'] = branch.description }
      if (branch.compareTo) { result['compareTo'] = branch.compareTo }
src/repos.ts:186
Before
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
      const compareTo = branchDescription.compareTo || reposConfig.repos[repoName].defaultBranch
      const compareToBranch = branches.find((test) => test.name === compareTo)

      const compareToBranchCommits = new Set<string>()
      let currentCommit = commits.get(compareToBranch.sha)
      while (currentCommit !== undefined) {
        compareToBranchCommits.add(currentCommit.hash)
        currentCommit = commits.get(currentCommit.parent)
      }

      const thisBranchCommits = new Set<string>()
      currentCommit = commits.get(branch.sha)
      while (currentCommit !== undefined) {
        thisBranchCommits.add(currentCommit.hash)
        currentCommit = commits.get(currentCommit.parent)
      }

      // At this point, we have all commits in the compareTo branch in one set, and
      // all commits from this branch in another set.
      const onlyInThisBranch = Array.from(thisBranchCommits).filter((thisBranchCommit) => {
        return !commits.get(thisBranchCommit).isMerge && !compareToBranchCommits.has(thisBranchCommit)
      }).length
      const onlyInCompareToBranch = Array.from(compareToBranchCommits).filter((compareToBranchCommit) => {
        return !commits.get(compareToBranchCommit).isMerge && !thisBranchCommits.has(compareToBranchCommit)
      }).length

      const compareToInfo = {
After
185
186
187
















188
189
190
191
192
193
194
      const compareTo = branchDescription.compareTo || reposConfig.repos[repoName].defaultBranch
      const compareToBranch = branches.find((test) => test.name === compareTo)

⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
      const onlyInThisBranch = branch.commits.filter((thisBranchCommit) => {
        return !thisBranchCommit.isMerge && !compareToBranch.commits.includes(thisBranchCommit)
      }).length
      const onlyInCompareToBranch = compareToBranch.commits.filter((compareToBranchCommit) => {
        return !compareToBranchCommit.isMerge && !branch.commits.includes(compareToBranchCommit)
      }).length

      const compareToInfo = {
src/vcses/git/operations.ts:2
Before
1
2
3
4
5
6
import childProcess from 'child_process'
const exec = util.promisify(childProcess.exec)
import { getGitDiffsFromPatchText} from '../../helpers.ts'
import { type Repository, type FileInfo } from '../../dataTypes.ts'

export const getFileList = async (
  branchName: string, repoLocation: string
After
1
2
3
4
5
6
import childProcess from 'child_process'
const exec = util.promisify(childProcess.exec)
import { getGitDiffsFromPatchText} from '../../helpers.ts'
import { type Repository, type FileInfo, type Commit } from '../../dataTypes.ts'

export const getFileList = async (
  branchName: string, repoLocation: string
src/vcses/git/operations.ts:48
Before
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

85

86
87
  return filesMap
}

export const addBranchToCommitsMap = async(branchName: string, repoLocation: string, commits: Repository['commits']): Promise<void> => {
  const totalPatchesCountRes = await exec(`git -C ${repoLocation} rev-list --count ${branchName}`)
  const totalPatchesCount = parseInt(totalPatchesCountRes.stdout)
  let previousHash: null | string = null
  let gitLogSubsetCmd = null
  let gitLogSubset = []
  return addBranchToCommitsMapWithSkip(branchName, repoLocation, commits, totalPatchesCount)
}

export const addBranchToCommitsMapWithSkip = async(branchName: string, repoLocation: string, commits: Repository['commits'], totalPatchesCount: number, skip = 0, previousHash = null): Promise<void> => {
  console.log(`addBranchToCommitsMap, skip: ${skip}, total: ${totalPatchesCount}`)
  return new Promise((resolve) => {
    if (skip >= totalPatchesCount) {
      return resolve()
    }

    // const gitLogSubsetRes = await exec(`git -C ${repoLocation} log ${branchName} -p -n 10 --skip ${i}`)
    const gitLogSubsetCmd = childProcess.spawn('git', ['-C', repoLocation, 'log', branchName, '-p', '-n', '10', '--skip', skip.toString()], {
      stdio: [0, "pipe", "inherit"],
    })
    // let gitLogSubset = gitLogSubsetRes.stdout.split("\n")
    let gitLogSubset = []
    gitLogSubsetCmd.stdout.on('data', (data) => {
      gitLogSubset.push(data)
    })

    gitLogSubsetCmd.on('close', () => {
      previousHash = addGitLogSubsetToMap(gitLogSubset.join('').split('\n'), commits, previousHash)
      return addBranchToCommitsMapWithSkip(branchName, repoLocation, commits, totalPatchesCount, skip + 10, previousHash).then(() => {
        return resolve()
      })
    })
  })
}

⁣
const addGitLogSubsetToMap = (gitLogSubset: string[], commits: Repository['commits'], previousHash: string | null) => {
⁣
  do {
    const nextPatchStart = gitLogSubset.findIndex((line, index) => {
      return (index > 0 && line.startsWith("commit ")) || index === gitLogSubset.length - 1
After
47
48
49
50
51
52

53
54
55
56
57
58

59
60
61
62
63

64
65
66

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  return filesMap
}

export const addBranchToCommitsMap = async(branchName: string, repoLocation: string, commits: Repository['commits']): Promise<Array<Commit>> => {
  const totalPatchesCountRes = await exec(`git -C ${repoLocation} rev-list --count ${branchName}`)
  const totalPatchesCount = parseInt(totalPatchesCountRes.stdout)
⁣
  let gitLogSubsetCmd = null
  let gitLogSubset = []
  return addBranchToCommitsMapWithSkip(branchName, repoLocation, commits, totalPatchesCount)
}

export const addBranchToCommitsMapWithSkip = async(branchName: string, repoLocation: string, commits: Repository['commits'], totalPatchesCount: number, skip = 0): Promise<Array<Commit>> => {
⁣
  return new Promise((resolve) => {
    if (skip >= totalPatchesCount) {
      return resolve([])
    }

⁣
    const gitLogSubsetCmd = childProcess.spawn('git', ['-C', repoLocation, 'log', branchName, '-p', '-n', '10', '--skip', skip.toString()], {
      stdio: [0, "pipe", "inherit"],
    })
⁣
    let gitLogSubset = []
    gitLogSubsetCmd.stdout.on('data', (data) => {
      gitLogSubset.push(data)
    })

    gitLogSubsetCmd.on('close', () => {
      const myChunkOfCommits = addGitLogSubsetToMap(gitLogSubset.join('').split('\n'), commits)
      return addBranchToCommitsMapWithSkip(branchName, repoLocation, commits, totalPatchesCount, skip + 10).then((laterChunksOfCommits) => {
        return resolve(myChunkOfCommits.concat(laterChunksOfCommits))
      })
    })
  })
}

const addGitLogSubsetToMap = (gitLogSubset: string[], commits: Repository['commits']) => {
  const orderedCommits: Array<Commit> = []

  do {
    const nextPatchStart = gitLogSubset.findIndex((line, index) => {
      return (index > 0 && line.startsWith("commit ")) || index === gitLogSubset.length - 1
src/vcses/git/operations.ts:93
Before
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
    gitLogSubset = gitLogSubset.slice(nextPatchStart)

    const hash = currentPatch[0].replace("commit ", "").trim()
    console.log(`doing ${hash}`)
    // set the parent hash of the previous commit, unless we're on the first commit
    if (previousHash !== null) {
      commits.get(previousHash)['parent'] = hash
    }
    previousHash = hash

    // exit early if the commits map already includes this hash
    if (commits.has(hash)) {
      return
    }

    let author: string, date: string, isMerge: boolean
After
92
93
94
95






96
97
98
99
100
    gitLogSubset = gitLogSubset.slice(nextPatchStart)

    const hash = currentPatch[0].replace("commit ", "").trim()

⁣
⁣
⁣
⁣
⁣
⁣
    // exit early if the commits map already includes this hash
    if (commits.has(hash)) {
      continue
    }

    let author: string, date: string, isMerge: boolean
src/vcses/git/operations.ts:136
Before
135
136
137

138
139
140
    }).join("\n").trim()

    const diffs = getGitDiffsFromPatchText(currentPatch.slice(diffStart).join("\n"))
⁣
    commits.set(hash, {
      hash,
      message: commitMessage,
      isMerge: isMerge || false,
After
135
136
137
138
139
140
141
    }).join("\n").trim()

    const diffs = getGitDiffsFromPatchText(currentPatch.slice(diffStart).join("\n"))

    let commit = {
      hash,
      message: commitMessage,
      isMerge: isMerge || false,
src/vcses/git/operations.ts:145
Before
144
145
146


147

148
149
150
151
152
      diffs,
      parent: null,
      cachedFiles: new Map(),
⁣
⁣
    })
⁣
  } while (gitLogSubset.length > 1)

  return previousHash
}

export type BlameInfo = {
After
144
145
146
147
148
149
150
151
152
153
154
155
      diffs,
      parent: null,
      cachedFiles: new Map(),
    }

    orderedCommits.push(commit)
    commits.set(hash, commit)
  } while (gitLogSubset.length > 1)

  return orderedCommits
}

export type BlameInfo = {