More implementation of the new type

fb82fedb228595143547ae3003ba3426a5e39bc9

Tucker McKnight <tucker@pangolin.lan> | Sun Nov 16 2025

More implementation of the new type

The new commit field and branch heads work now, and other
functions/filters/template pages work as expected with those
new fields.

Switch to the new Repository type is pretty much done now.

Also did some miscellaneous things, like adding basic string
types to some function arguments. Also started changing all uses
of "patch" to "commit," which is a holdover from when this project
started with darcs.
main.ts:5
Before
4
5
6
7
8
9
import branches from './src/branches.ts'
import flatFiles from './src/flatFiles.ts'
import flatPatches from './src/flatPatches.ts'
import paginatedPatches from './src/paginatedPatches.ts'
import {getLocation} from './src/helpers.ts'
import repoOperations from './src/vcses/operations.ts'
import {ReposConfiguration} from './src/configTypes.ts'
After
4
5
6
7
8
9
import branches from './src/branches.ts'
import flatFiles from './src/flatFiles.ts'
import flatPatches from './src/flatPatches.ts'
import paginatedPatches, {type PatchPage} from './src/paginatedPatches.ts'
import {getLocation} from './src/helpers.ts'
import repoOperations from './src/vcses/operations.ts'
import {ReposConfiguration} from './src/configTypes.ts'
main.ts:49
Before
48
49
50
51
52
53

  eleventyConfig.on(
    "eleventy.after",
    async ({ directories, results, runMode, outputMode }) => {
      const cwd = process.cwd()
      // Check to see if there is already a repo in all of the locations
      // that should have one.
After
48
49
50
51
52
53

  eleventyConfig.on(
    "eleventy.after",
    async ({ directories }) => {
      const cwd = process.cwd()
      // Check to see if there is already a repo in all of the locations
      // that should have one.
main.ts:74
Before
73
74
75
76
77
78
          const tempDirName = `temp_${Math.floor(Math.random() * 10000).toString()}`
          const tempDir = `${directories.output}${reposPath}${tempDirName}`
          const tempDirRepoPath = `${tempDir}/${eleventyConfig.getFilter("slugify")(repoName)}`
          const mkdirresult = await exec(`mkdir ${tempDir}`)
          await exec(`git clone -s ${directories.output}${eleventyConfig.getFilter("slugify")(repoName)}.git ${tempDirRepoPath}`)
          for (let branch of repoConfig.branchesToPull) {
            await exec(`(cd ${tempDirRepoPath} && git checkout ${branch})`)
After
73
74
75
76
77
78
          const tempDirName = `temp_${Math.floor(Math.random() * 10000).toString()}`
          const tempDir = `${directories.output}${reposPath}${tempDirName}`
          const tempDirRepoPath = `${tempDir}/${eleventyConfig.getFilter("slugify")(repoName)}`
          await exec(`mkdir ${tempDir}`)
          await exec(`git clone -s ${directories.output}${eleventyConfig.getFilter("slugify")(repoName)}.git ${tempDirRepoPath}`)
          for (let branch of repoConfig.branchesToPull) {
            await exec(`(cd ${tempDirRepoPath} && git checkout ${branch})`)
main.ts:92
Before
91
92
93
94
95
96
97
98
99
100
101
102
103
104
    }
  );

  eleventyConfig.addFilter("getDirectoryContents", (repo, branch, dirPath) => {
    return reposData.find(current => current.name === repo).branches[branch].fileList.filter(file => file.startsWith(dirPath) && file !== dirPath)
  })

  eleventyConfig.addFilter("getRelativePath", (currentDir, fullFilePath) => {
    return fullFilePath.replace(`${currentDir}/`, "")
  })

  eleventyConfig.addFilter("lineNumbers", (code) => {
    const numLines = code.split('\n').length
    const lineNumbers = []
    for (let i = 1; i <= numLines; i++) {
After
91
92
93
94
95
96
97
98
99
100
101
102
103
104
    }
  );

  eleventyConfig.addFilter("getDirectoryContents", (repo: string, branch: string, dirPath: string) => {
    return reposData.find(current => current.name === repo).branches.find(current => current.name === branch).fileList.filter(file => file.startsWith(dirPath) && file !== dirPath)
  })

  eleventyConfig.addFilter("getRelativePath", (currentDir: string, fullFilePath: string) => {
    return fullFilePath.replace(`${currentDir}/`, "")
  })

  eleventyConfig.addFilter("lineNumbers", (code: string) => {
    const numLines = code.split('\n').length
    const lineNumbers = []
    for (let i = 1; i <= numLines; i++) {
main.ts:120
Before
119
120
121
122
123
124
    }
  })

  eleventyConfig.addAsyncFilter("renderContentIfAvailable", async (contentString, contentType) => {
    const renderer = eleventyConfig?.javascript?.functions?.renderContent
    if (renderer) {
      return await renderer.bind({})(contentString, contentType)
After
119
120
121
122
123
124
    }
  })

  eleventyConfig.addAsyncFilter("renderContentIfAvailable", async (contentString: string, contentType: string) => {
    const renderer = eleventyConfig?.javascript?.functions?.renderContent
    if (renderer) {
      return await renderer.bind({})(contentString, contentType)
main.ts:130
Before
129
130
131
132
133
134
135
136
137
138
139
140
141
    }
  })

  eleventyConfig.addFilter("languageExtension", (filename, repoName) => {
    let extension = filename.split(".")
    extension = extension[extension.length - 1]
    const extensionsConfig = reposConfiguration.repos[repoName].languageExtensions
    return extensionsConfig && extensionsConfig[extension] ? extensionsConfig[extension] : extension
  })

  eleventyConfig.addFilter("topLevelFilesOnly", (files, currentLevel) => {
    const onlyUnique = (value, index, array) => {
      return array.findIndex(test => test.name === value.name) === index;
    }
After
129
130
131
132
133
134
135
136
137
138
139
140
141
    }
  })

  eleventyConfig.addFilter("languageExtension", (filename: string, repoName: string) => {
    let filenameParts = filename.split(".")
    let extension = filenameParts[filenameParts.length - 1]
    const extensionsConfig = reposConfiguration.repos[repoName].languageExtensions
    return extensionsConfig && extensionsConfig[extension] ? extensionsConfig[extension] : extension
  })

  eleventyConfig.addFilter("topLevelFilesOnly", (files: Array<string>, currentLevel: string) => {
    const onlyUnique = (value, index, array) => {
      return array.findIndex(test => test.name === value.name) === index;
    }
main.ts:175
Before
174
175
176
177
178
179
180
181
182
183
184
185
186
187
    return sortedByDirectory
  })

  eleventyConfig.addAsyncFilter("getFileLastTouchInfo", async (repo, branch, filename) => {
    const ignoreExtensions = ['.png', '.jpg']
    if (ignoreExtensions.some(extension => filename.endsWith(extension))) {
      return ""
    }

    const config = reposConfiguration.repos[repo]
    const location = getLocation(reposConfiguration, branch, repo)
    return repoOperations[config._type].getFileLastTouchInfo(repo, branch, filename, location)
  })

  eleventyConfig.addFilter("isDirectory", (filename: string, repoName: string, branchName: string) => {
After
174
175
176
177
178
179
180
181
182
183
184
185
186
187
    return sortedByDirectory
  })

  eleventyConfig.addAsyncFilter("getFileLastTouchInfo", async (repo: string, branch: string, filename: string) => {
    const ignoreExtensions = ['.png', '.jpg']
    if (ignoreExtensions.some(extension => filename.endsWith(extension))) {
      return ""
    }

    const config = reposConfiguration.repos[repo]
    const location = getLocation(reposConfiguration, repo)
    return repoOperations[config._type].getFileLastTouchInfo(branch, filename, location)
  })

  eleventyConfig.addFilter("isDirectory", (filename: string, repoName: string, branchName: string) => {
main.ts:196
Before
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
  })

  eleventyConfig.addAsyncFilter("getFileContents", async (repo: string, branch: string, filename: string) => {
    const location = getLocation(reposConfiguration, branch, repo)
    const command = `git show ${branch}:${filename}`
    const res = await exec(`(cd ${location} && ${command})`)
    return res.stdout
  })

  eleventyConfig.addFilter("pagesJustForBranch", (pages, repoName, branchName) => {
    return pages.filter(page => page.repoName === repoName && page.branchName === branchName)
  })

  eleventyConfig.addFilter("date", (dateString) => {
    return new Date(dateString).toDateString()
  })

  eleventyConfig.addFilter("toDateObj", (dateString) => {
    return new Date(dateString)
  })

  eleventyConfig.addAsyncFilter("getReadMe", async (repoName, branchName) => {
    const location = getLocation(reposConfiguration, branchName, repoName)
    const command = `git show ${branchName}:README.md`
    try {
      const res = await exec(`(cd ${location} && ${command})`)
After
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
  })

  eleventyConfig.addAsyncFilter("getFileContents", async (repo: string, branch: string, filename: string) => {
    const location = getLocation(reposConfiguration, repo)
    const command = `git show ${branch}:${filename}`
    const res = await exec(`(cd ${location} && ${command})`)
    return res.stdout
  })

  eleventyConfig.addFilter("pagesJustForBranch", (pages: Array<PatchPage>, repoName: string, branchName: string) => {
    return pages.filter(page => page.repoName === repoName && page.branchName === branchName)
  })

  eleventyConfig.addFilter("date", (dateString: string) => {
    return new Date(dateString).toDateString()
  })

  eleventyConfig.addFilter("toDateObj", (dateString: string) => {
    return new Date(dateString)
  })

  eleventyConfig.addAsyncFilter("getReadMe", async (repoName: string, branchName: string) => {
    const location = getLocation(reposConfiguration, repoName)
    const command = `git show ${branchName}:README.md`
    try {
      const res = await exec(`(cd ${location} && ${command})`)
main.ts:260
Before
259
260
261

262








263
264
        nav: {
          repoName: (data) => data.branchInfo.repoName,
          branchName: (data) => data.branchInfo.branchName,
⁣
        }
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
      },
      navTab: "branches",
    }
After
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
        nav: {
          repoName: (data) => data.branchInfo.repoName,
          branchName: (data) => data.branchInfo.branchName,
          path: "list"
        },
        currentRepo: (data) => reposData.find(repo => {
          return repo.name === data.branchInfo.repoName
        }),
        currentBranch: (data) => reposData.find(repo => {
          return repo.name === data.branchInfo.repoName
        }).branches.find(branch => {
          return branch.name === data.branchInfo.branchName
        }),
      },
      navTab: "branches",
    }
main.ts:288
Before
287
288
289

290








291
292
        nav: {
          repoName: (data) => data.fileInfo.repoName,
          branchName: (data) => data.fileInfo.branchName,
⁣
        }
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
      },
      navTab: "files",
    }
After
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
        nav: {
          repoName: (data) => data.fileInfo.repoName,
          branchName: (data) => data.fileInfo.branchName,
          path: "files",
        },
        currentRepo: (data) => reposData.find(repo => {
          return repo.name === data.fileInfo.repoName
        }),
        currentBranch: (data) => reposData.find(repo => {
          return repo.name === data.fileInfo.repoName
        }).branches.find(branch => {
          return branch.name === data.fileInfo.branchName
        }),
      },
      navTab: "files",
    }
main.ts:335
Before
334
335
336

337
338
        nav: {
          repoName: (data) => data.branchInfo.repoName,
          branchName: (data) => data.branchInfo.branchName,
⁣
        },
        currentRepo: (data) => reposData.find(repo => {
          return repo.name === data.branchInfo.repoName
After
334
335
336
337
338
339
        nav: {
          repoName: (data) => data.branchInfo.repoName,
          branchName: (data) => data.branchInfo.branchName,
          path: "files",
        },
        currentRepo: (data) => reposData.find(repo => {
          return repo.name === data.branchInfo.repoName
main.ts:369
Before
368
369
370

371
372
        nav: {
          repoName: (data) => data.branch.repoName,
          branchName: (data) => data.branch.branchName,
⁣
        },
        currentRepo: (data) => reposData.find(repo => {
          return repo.name === data.branch.repoName
After
368
369
370
371
372
373
        nav: {
          repoName: (data) => data.branch.repoName,
          branchName: (data) => data.branch.branchName,
          path: "",
        },
        currentRepo: (data) => reposData.find(repo => {
          return repo.name === data.branch.repoName
main.ts:405
Before
404
405
406

407








408
409
        nav: {
          repoName: (data) => data.patchPage.repoName,
          branchName: (data) => data.patchPage.branchName,
⁣
        }
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
      },
      navTab: "patches",
    }
After
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
        nav: {
          repoName: (data) => data.patchPage.repoName,
          branchName: (data) => data.patchPage.branchName,
          path: "patches/page1",
        },
        currentRepo: (data) => reposData.find(repo => {
          return repo.name === data.patchPage.repoName
        }),
        currentBranch: (data) => reposData.find(repo => {
          return repo.name === data.patchPage.repoName
        }).branches.find(branch => {
          return branch.name === data.patchPage.branchName
        }),
      },
      navTab: "patches",
    }
main.ts:427
Before
426
427
428
429
430
431
432
433
434

435








436
437
      permalink: (data) => {
        const repoName = data.patchInfo.repoName
        const branchName = data.patchInfo.branchName
        return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/patches/${data.patchInfo.patch.hash}/`
      },
      eleventyComputed: {
        nav: {
          repoName: (data) => data.patchInfo.repoName,
          branchName: (data) => data.patchInfo.branchName,
⁣
        }
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
      },
      width: "full",
      navTab: "patches",
After
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
      permalink: (data) => {
        const repoName = data.patchInfo.repoName
        const branchName = data.patchInfo.branchName
        return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/patches/${data.patchInfo.commit.hash}/`
      },
      eleventyComputed: {
        nav: {
          repoName: (data) => data.patchInfo.repoName,
          branchName: (data) => data.patchInfo.branchName,
          path: "patches/page1",
        },
        currentRepo: (data) => reposData.find(repo => {
          return repo.name === data.patchInfo.repoName
        }),
        currentBranch: (data) => reposData.find(repo => {
          return repo.name === data.patchInfo.repoName
        }).branches.find(branch => {
          return branch.name === data.patchInfo.branchName
        }),
      },
      width: "full",
      navTab: "patches",
partial_templates/main_top.njk:13
Before
12
13
14
15
16
17
        window.jsVars['cloneDiv'] = `<label class="form-label">HTTPS URL</label>
<div class="input-group d-flex flex-nowrap">
  <span class="clone overflow-hidden input-group-text">
    {% set url = repos[nav.repoName].cloneUrl %}
    {{ url }}
  </span>
  <button data-clone-url="{{url}}" class="btn btn-primary" id="clone-button">Copy</button>
After
12
13
14
15
16
17
        window.jsVars['cloneDiv'] = `<label class="form-label">HTTPS URL</label>
<div class="input-group d-flex flex-nowrap">
  <span class="clone overflow-hidden input-group-text">
    {% set url = currentRepo.cloneUrl %}
    {{ url }}
  </span>
  <button data-clone-url="{{url}}" class="btn btn-primary" id="clone-button">Copy</button>
partial_templates/main_top.njk:83
Before
82
83
84
85
86
87
88
                    <div class="input-group input-group-sm">
                      <span class="input-group-text">Branch</span>
                      <select class="form-select" onchange="selectBranch(this)" aria-label="Repository branch selector">
                        {% for branch in repos[nav.repoName].branches %}
                        <option value="{{branch.repoName | slugify}},{{branch.branchName | slugify}},{{nav.path}}" {% if branch.branchName == nav.branchName %}selected{% endif %}>{{branch.branchName}}</option>
                        {% endfor %}
                      </select>
                    </div>
After
82
83
84
85
86
87
88
                    <div class="input-group input-group-sm">
                      <span class="input-group-text">Branch</span>
                      <select class="form-select" onchange="selectBranch(this)" aria-label="Repository branch selector">
                        {% for branch in currentRepo.branches %}
                        <option value="{{currentRepo.name | slugify}},{{branch.name | slugify}},{{nav.path}}" {% if branch.name == nav.branchName %}selected{% endif %}>{{branch.name}}</option>
                        {% endfor %}
                      </select>
                    </div>
src/branches.ts:1
Before
0
1



2
3
4
import { type Repository } from "./dataTypes.ts"

⁣
⁣
⁣
let cachedBranches = null

export default (repos: Array<Repository>) => {
  if (cachedBranches !== null) { return cachedBranches }
After
0
1
2
3
4
5
6
7
import { type Repository } from "./dataTypes.ts"

let cachedBranches: Array<{
  branchName: string,
  repoName: string,
}> | null = null

export default (repos: Array<Repository>) => {
  if (cachedBranches !== null) { return cachedBranches }
src/dataTypes.ts:18
Before
17
18
19
20
21
22
    message: string,
    author: string,
    date: Date,
    parent: string,
    diffs: Array<{
      fileName: string,
      lineNumber: number,
After
17
18
19
20
21
22
    message: string,
    author: string,
    date: Date,
    parent: string | null,
    diffs: Array<{
      fileName: string,
      lineNumber: number,
src/flatPatches.ts:1
Before
0
1






2
3

4
5
6
7
8













9
10
11
import { type Repository } from "./dataTypes.ts"

⁣
⁣
⁣
⁣
⁣
⁣
let cachedFlatPatches = null

⁣
export default async (repos: Array<Repository>) => {
  if (cachedFlatPatches !== null) { return cachedFlatPatches }

  cachedFlatPatches = repos.flatMap((repo) => {
    return repo.branches.flatMap((branch) => {
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
      return [] // todo implement this with new commits format
    })
  })
After
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { type Repository } from "./dataTypes.ts"

type FlatPatchRecord = {
  commit: ReturnType<Repository['commits']['get']>,
  repoName: string,
  branchName: string,
}

let cachedFlatPatches: Array<FlatPatchRecord> | null = null

export default async (repos: Array<Repository>)
  : Promise<Array<FlatPatchRecord>> => {
  if (cachedFlatPatches !== null) { return cachedFlatPatches }

  cachedFlatPatches = repos.flatMap((repo) => {
    return repo.branches.flatMap((branch) => {
      const flatPatches: Array<FlatPatchRecord> = []
      let currentCommit: ReturnType<Repository['commits']['get']> | undefined = repo.commits.get(branch.head)

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

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

      return flatPatches
    })
  })
src/helpers.ts:1
Before
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import _ from 'lodash'
import * as Diff from 'diff'
import {type Repository} from './dataTypes.ts'

type NavValues = {
  repoName: string | ((data: any) => string),
  branchName: string | ((data: any) => string),
  path: string | ((data: any) => string),
  title: string | ((data: any) => string),
}
type Hunk = {
  file: string,
  lineNumber: number,
  previousText: string,
  afterText: string,
}

type Diffs = ReturnType<Repository['commits']['get']>['diffs']
After
0
1
2







3
4





5
import _ from 'lodash'
import * as Diff from 'diff'
import {type Repository} from './dataTypes.ts'
⁣
⁣
⁣
⁣
⁣
⁣
⁣
import { type ReposConfiguration } from './configTypes.ts'

⁣
⁣
⁣
⁣
⁣
type Diffs = ReturnType<Repository['commits']['get']>['diffs']
src/helpers.ts:82
Before
81
82
83
84
85
86
87
88
89
90
91
92
  return hunks
}

const getLocation = (reposConfig: any, branchName: string, repoName: string): string => {
  const config = reposConfig.repos[repoName]
  return config.location
}

export {
  NavValues,
  getGitDiffsFromPatchText,
  getLocation,
}
After
81
82
83
84
85
86
87
88
89

90
91
  return hunks
}

const getLocation = (reposConfig: ReposConfiguration, repoName: string): string => {
  const config = reposConfig.repos[repoName]
  return config.location
}

export {
⁣
  getGitDiffsFromPatchText,
  getLocation,
}
src/paginatedPatches.ts:1
Before
0
1






2
3
4
5
6
i⁣
mport flatPatchesFunc from './flatPatches.js'

⁣
⁣
⁣
⁣
⁣
⁣
let paginatedPatches = null

export default async (repos) => {
  if (paginatedPatches !== null) { return paginatedPatches }

  const flatPatches = await flatPatchesFunc(repos)
After
0
1
2
3
4
5
6
7
8
9
10
11
12
13
import { type Repository } from './dataTypes.ts'
import flatPatchesFunc from './flatPatches.js'

export type PatchPage = {
  repoName: string,
  branchName: string,
  commits: Array<ReturnType<Repository['commits']['get']>>,
  pageNumber: number,
}
let paginatedPatches: Array<PatchPage> | null = null

export default async (repos: Array<Repository>) => {
  if (paginatedPatches !== null) { return paginatedPatches }

  const flatPatches = await flatPatchesFunc(repos)
src/paginatedPatches.ts:15
Before
14
15
16
17
18
19
      return (
        page.repoName === patch.repoName
        && page.branchName == patch.branchName
        && page.patches.length <= patchesPerPage
      )
    })
After
14
15
16
17
18
19
      return (
        page.repoName === patch.repoName
        && page.branchName == patch.branchName
        && page.commits.length <= patchesPerPage
      )
    })
src/paginatedPatches.ts:24
Before
23
24
25
26
27
28
29
30
31
32
33
34
35
      paginatedPatches.push({
        repoName: patch.repoName,
        branchName: patch.branchName,
        patches: [patch.patch],
        // current page number is one more than "how many items in paginatedPatches already
        // have repoName as their repo name.
        pageNumber,
      })
    }
    else {
      paginatedPatches[index].patches.push(patch.patch)
    }
  })
After
23
24
25
26
27
28
29
30
31
32
33
34
35
      paginatedPatches.push({
        repoName: patch.repoName,
        branchName: patch.branchName,
        commits: [patch.commit],
        // current page number is one more than "how many items in paginatedPatches already
        // have repoName as their repo name.
        pageNumber,
      })
    }
    else {
      paginatedPatches[index].commits.push(patch.commit)
    }
  })
src/repos.ts:3
Before
2
3
4
5



6
7
8



9
  type GitConfig,
} from './configTypes.ts'
import { type Repository} from './dataTypes.ts'

⁣
⁣
⁣
import { addBranchToCommitsMap } from './vcses/git/operations.ts'
import { getLocation} from './helpers.ts'

⁣
⁣
⁣
const getBranchNames = (repoConfig: GitConfig): Array<string> => {
  return repoConfig.branchesToPull.map((branch) => {
After
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  type GitConfig,
} from './configTypes.ts'
import { type Repository} from './dataTypes.ts'
import util from 'util'
import childProcess from 'child_process'
import cloneUrl from './vcses/git/helpers.ts'

import { addBranchToCommitsMap } from './vcses/git/operations.ts'
import { getLocation} from './helpers.ts'
import { getFileList } from './vcses/git/operations.ts'

const exec = util.promisify(childProcess.exec)

const getBranchNames = (repoConfig: GitConfig): Array<string> => {
  return repoConfig.branchesToPull.map((branch) => {
src/repos.ts:30
Before
29
30
31
32
33
34
35
36
37

38
39
40
41
42
43



44

45
46
47
48
49
    const branchNames = getBranchNames(reposConfig.repos[repoName])
    const commits: Repository['commits'] = new Map()
    for (const branchName of branchNames) {
      const repoLocation = getLocation(reposConfig, branchName, repoName)
      await addBranchToCommitsMap(branchName, repoLocation, commits)
    }

    repos.push({
      name: repoName,
⁣
      branches: branchNames.map((branchName) => {
        return {
          name: branchName,
          head: "", // todo
          fileList: [], //todo
        }
⁣
⁣
⁣
      }),
⁣
      cloneUrl: "",
      defaultBranch: "main",
      tags: [],
      commits,
    })
  }
After
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    const branchNames = getBranchNames(reposConfig.repos[repoName])
    const commits: Repository['commits'] = new Map()
    for (const branchName of branchNames) {
      const repoLocation = getLocation(reposConfig, repoName)
      await addBranchToCommitsMap(branchName, repoLocation, commits)
    }

    const branches = await Promise.all(branchNames.map(async (branchName) => {
      const repoLocation = getLocation(reposConfig, repoName)
      const branchHeadRes = await exec(`(cd ${repoLocation} && git show-ref --branch ${branchName})`)
      const branchHead = branchHeadRes.stdout.split(" ")[0]
      return {
        name: branchName,
        head: branchHead,
        fileList: await getFileList(branchName, repoLocation)
      }
    }))

    repos.push({
      name: repoName,
      branches,
      cloneUrl: cloneUrl.cloneUrl(reposConfig.baseUrl, repoName),
      defaultBranch: "main",
      tags: [], // todo
      commits,
    })
  }
src/vcses/git/operations.ts:4
Before
3
4
5
6
7
8
import { getGitDiffsFromPatchText} from '../../helpers.ts'
import { type Repository } from '../../dataTypes.ts'

export const getFileList = async (repoName: string, branchName: string, repoLocation: string) => {
  const command = `git ls-tree -r --name-only ${branchName}`

  const result = await exec(`(cd ${repoLocation} && ${command})`)
After
3
4
5
6
7
8
import { getGitDiffsFromPatchText} from '../../helpers.ts'
import { type Repository } from '../../dataTypes.ts'

export const getFileList = async (branchName: string, repoLocation: string) => {
  const command = `git ls-tree -r --name-only ${branchName}`

  const result = await exec(`(cd ${repoLocation} && ${command})`)
src/vcses/git/operations.ts:41
Before
40
41
42

43
44
export const addBranchToCommitsMap = async(branchName: string, repoLocation: string, commits: Repository['commits']): Promise<void> => {
  const totalPatchesCountRes = await exec(`(cd ${repoLocation} && git rev-list --count ${branchName})`)
  const totalPatchesCount = parseInt(totalPatchesCountRes.stdout)
⁣
  for (let i = 0; i < totalPatchesCount; i = i + 10) {
    const gitLogSubsetRes = await exec(`(cd ${repoLocation} && git log ${branchName} -p -n 10 --skip ${i})`)
    let gitLogSubset = gitLogSubsetRes.stdout.split("\n")
After
40
41
42
43
44
45
export const addBranchToCommitsMap = async(branchName: string, repoLocation: string, commits: Repository['commits']): Promise<void> => {
  const totalPatchesCountRes = await exec(`(cd ${repoLocation} && git rev-list --count ${branchName})`)
  const totalPatchesCount = parseInt(totalPatchesCountRes.stdout)
  let previousHash: null | string = null
  for (let i = 0; i < totalPatchesCount; i = i + 10) {
    const gitLogSubsetRes = await exec(`(cd ${repoLocation} && git log ${branchName} -p -n 10 --skip ${i})`)
    let gitLogSubset = gitLogSubsetRes.stdout.split("\n")
src/vcses/git/operations.ts:54
Before
53
54
55






56
57
58
59
60

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

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

      let author: string, date: string
After
53
54
55
56
57
58
59
60
61
62
63
64
65
66

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

      // 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
src/vcses/git/operations.ts:83
Before
82
83
84
85
86
87
88
89
90
91
92
93
94
        message: commitMessage,
        author,
        date: new Date(date),
                  diffs,
                  parent: "todo",
      })
    } while (gitLogSubset.length > 1)
  }
}

export const getFileLastTouchInfo = async (repo: string, branch: string, filename: string, repoLocation: string) => {
  const regex = RegExp(".* [0-9]+ [0-9]+")
  const command = `git blame --porcelain ${branch} ${filename}`
  const res = await exec(`(cd ${repoLocation} && ${command})`)
After
82
83
84
85
86
87
88
89
90
91
92
93
94
        message: commitMessage,
        author,
        date: new Date(date),
        diffs,
        parent: null,
      })
    } while (gitLogSubset.length > 1)
  }
}

export const getFileLastTouchInfo = async (branch: string, filename: string, repoLocation: string) => {
  const regex = RegExp(".* [0-9]+ [0-9]+")
  const command = `git blame --porcelain ${branch} ${filename}`
  const res = await exec(`(cd ${repoLocation} && ${command})`)
src/vcses/operations.ts:6
Before
5
6
7
8
9
10
type RepoOperationsType = {
  [vcs: string]: {
    getFileList: (repoName: string, branchName: string, repoLocation: string) => Promise<Array<string>>,
    getFileLastTouchInfo: (repoName: string, branchName: string, filename: string, repoLocation: string) => Promise<Array<{sha: string, author: string}>>,
  }
}
After
5
6
7
8
9
10
type RepoOperationsType = {
  [vcs: string]: {
    getFileList: (repoName: string, branchName: string, repoLocation: string) => Promise<Array<string>>,
    getFileLastTouchInfo: (branchName: string, filename: string, repoLocation: string) => Promise<Array<{sha: string, author: string}>>,
  }
}
templates/branches.njk:1
Before
0
1
2
3
4
5
6
<ul>
{% for branch in branches %}
  {% set description = reposConfig.repos[branch.repoName].branches[branch.branchName].description %}
  {% if branch.repoName == branchInfo.repoName %}
  <li><a href="{{reposPath}}/{{branch.repoName | slugify}}/branches/{{branch.branchName | slugify}}">{{branch.branchName}}</a>{% if branch.branchName == branchInfo.branchName %} (current){% endif %}{% if description %} - {{ description }}{% endif %}</li>
  {% endif %}
{% endfor %}
</ul>
After
0
1

2
3
4
5
<ul>
{% for branch in branches %}
⁣
  {% if branch.repoName == branchInfo.repoName %}
  <li><a href="{{reposPath}}/{{branch.repoName | slugify}}/branches/{{branch.branchName | slugify}}">{{branch.branchName}}</a>{% if branch.branchName == branchInfo.branchName %} (current){% endif %}</li>
  {% endif %}
{% endfor %}
</ul>
templates/patch.njk:1
Before
0
1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="container-lg">
  <div class="row">
    <div class="col-auto">
      <h1>{{patchInfo.patch.name}}</h2>
      <p>{{patchInfo.patch.date | date }}</p>
      <p>{{patchInfo.patch.author }}</p>
      <pre>{{patchInfo.patch.description}}</pre>
    </div>
  </div>
  <div class="row">
    <div class="col-auto">
      <p class="font-monospace fw-bold text-secondary">{{patchInfo.patch.hash}}</p>
    </div>
  </div>
</div>
After
0
1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="container-lg">
  <div class="row">
    <div class="col-auto">
      <h1>commit first line goes here</h2>
      <p>{{patchInfo.commit.date | date }}</p>
      <p>{{patchInfo.commit.author }}</p>
      <pre>{{patchInfo.commit.message}}</pre>
    </div>
  </div>
  <div class="row">
    <div class="col-auto">
      <p class="font-monospace fw-bold text-secondary">{{patchInfo.commit.hash}}</p>
    </div>
  </div>
</div>
templates/patch.njk:24
Before
23
24
25
26
27
28
    </div>
  </div>
  <div class="row" id="diffs">
  {% set patchHunks = patchInfo.patch.diffs %}
  {% for hunk in patchHunks %}
    <div class=hunk>
      <span class="font-monospace fw-bold"><a href="{{reposPath}}/{{patchInfo.repoName | slugify}}/branches/{{patchInfo.branchName | slugify}}/files/{{ hunk.fileName | slugify}}.html">{{ hunk.fileName }}:{{ hunk.lineNumber }}</a></span>
After
23
24
25
26
27
28
    </div>
  </div>
  <div class="row" id="diffs">
  {% set patchHunks = patchInfo.commit.diffs %}
  {% for hunk in patchHunks %}
    <div class=hunk>
      <span class="font-monospace fw-bold"><a href="{{reposPath}}/{{patchInfo.repoName | slugify}}/branches/{{patchInfo.branchName | slugify}}/files/{{ hunk.fileName | slugify}}.html">{{ hunk.fileName }}:{{ hunk.lineNumber }}</a></span>
templates/patches.njk:9
Before
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</nav>

<ul>
  {% for patch in patchPage.patches %}
    <li class="patch">
      <div>
        <span class="patch-name"><a href="{{reposPath}}/{{patchPage.repoName | slugify}}/branches/{{patchPage.branchName | slugify}}/patches/{{patch.hash}}">{{patch.name}}</a></span>
        <br />
        <span>{{patch.date | date}}</span>
        <br />
        <span>{{patch.author}}</span>
        <pre class="patch-hash">{{patch.hash}}</pre>
      </div>
    </li>
  {% endfor %}
After
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</nav>

<ul>
  {% for commit in patchPage.commits %}
    <li class="patch">
      <div>
        <span class="patch-name"><a href="{{reposPath}}/{{patchPage.repoName | slugify}}/branches/{{patchPage.branchName | slugify}}/patches/{{commit.hash}}">{{commit.message}}</a></span>
        <br />
        <span>{{commit.date | date}}</span>
        <br />
        <span>{{commit.author}}</span>
        <pre class="patch-hash">{{commit.hash}}</pre>
      </div>
    </li>
  {% endfor %}