Switching some more things over to the new Repository type

7e40e5b6c81b4a222015b3d5586bc53eebd258c5

Tucker McKnight <tucker@pangolin.lan> | Mon Nov 10 2025

Switching some more things over to the new Repository type

Also removed anything that prints out commits in order, as
that needs to be re-thought for the new format. So I left some
todos in there.
main.ts:93
Before
92
93
94
95
96
97
  );

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

  eleventyConfig.addFilter("getRelativePath", (currentDir, fullFilePath) => {
After
92
93
94
95
96
97
  );

  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) => {
main.ts:110
Before
109
110
111
112
113
114
    return lineNumbers
  })

  eleventyConfig.addFilter("highlightCode", (code, language) => {
    const highlighter = eleventyConfig?.javascript?.functions?.highlight
    if (highlighter) {
      return highlighter(language, code)
After
109
110
111
112
113
114
    return lineNumbers
  })

  eleventyConfig.addFilter("highlightCode", (code: string, language: string) => {
    const highlighter = eleventyConfig?.javascript?.functions?.highlight
    if (highlighter) {
      return highlighter(language, code)
main.ts:186
Before
185
186
187
188

189
190
191
192
193
194
195
196
197
198
199
200
    return repoOperations[config._type].getFileLastTouchInfo(repo, branch, filename, location)
  })

  eleventyConfig.addFilter("isDirectory", (filename, repoName, branchName) => {
⁣
    const files = reposData[repoName].branches[branchName].files
    const isDirectory = files.some((testFile) => {
        return testFile.startsWith(filename + '/') && (testFile !== filename)
      })
    return isDirectory
  })

  eleventyConfig.addAsyncFilter("getFileContents", async (repo, branch, filename) => {
    const location = getLocation(reposConfiguration, branch, repo)
    const config = reposConfiguration.repos[repo]
    const command = `git show ${branch}:${filename}`
    const res = await exec(`(cd ${location} && ${command})`)
    return res.stdout
After
185
186
187
188
189
190
191
192
193
194
195
196
197
198

199
200
    return repoOperations[config._type].getFileLastTouchInfo(repo, branch, filename, location)
  })

  eleventyConfig.addFilter("isDirectory", (filename: string, repoName: string, branchName: string) => {
    const repo = reposData.find(repo => repo.name === repoName)
    const files = repo.branches.find(branch => branch.name === branchName).fileList
    const isDirectory = files.some((testFile) => {
        return testFile.startsWith(filename + '/') && (testFile !== filename)
      })
    return isDirectory
  })

  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
main.ts:216
Before
215
216
217
218
219
220

  eleventyConfig.addAsyncFilter("getReadMe", async (repoName, branchName) => {
    const location = getLocation(reposConfiguration, branchName, repoName)
    const config = reposConfiguration.repos[repoName]
    const command = `git show ${branchName}:README.md`
    try {
      const res = await exec(`(cd ${location} && ${command})`)
After
215
216
217

218
219

  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})`)
main.ts:336
Before
335
336
337
338








339
340
        nav: {
          repoName: (data) => data.branchInfo.repoName,
          branchName: (data) => data.branchInfo.branchName,
        }
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
      },
      navTab: "files",
    }
After
335
336
337
338
339
340
341
342
343
344
345
346
347
348
        nav: {
          repoName: (data) => data.branchInfo.repoName,
          branchName: (data) => data.branchInfo.branchName,
        },
        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: "files",
    }
main.ts:362
Before
361
362
363
364








365
366
        nav: {
          repoName: (data) => data.branch.repoName,
          branchName: (data) => data.branch.branchName,
        }
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
      },
      navTab: "landing",
    }
After
361
362
363
364
365
366
367
368
369
370
371
372
373
374
        nav: {
          repoName: (data) => data.branch.repoName,
          branchName: (data) => data.branch.branchName,
        },
        currentRepo: (data) => reposData.find(repo => {
          return repo.name === data.branch.repoName
        }),
        currentBranch: (data) => reposData.find(repo => {
          return repo.name === data.branch.repoName
        }).branches.find(branch => {
          return branch.name === data.branch.branchName
        }),
      },
      navTab: "landing",
    }
main.ts:398
Before
397
398
399
400
401
402

  // PATCH.NJK
  const patchTemplate = fsImport.readFileSync(`${import.meta.dirname}/templates/patch.njk`).toString()
  const flatPatchesData = await flatPatches(reposConfiguration)
  eleventyConfig.addTemplate(
    `repos/patch.njk`,
    topLayoutPartial + patchTemplate + bottomLayoutPartial,
After
397
398
399
400
401
402

  // PATCH.NJK
  const patchTemplate = fsImport.readFileSync(`${import.meta.dirname}/templates/patch.njk`).toString()
  const flatPatchesData = await flatPatches(reposData)
  eleventyConfig.addTemplate(
    `repos/patch.njk`,
    topLayoutPartial + patchTemplate + bottomLayoutPartial,
main.ts:429
Before
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447










448
449
450
451
452
453
  // htmlBaseUrl is a function defined by the 11ty RSS plugin.
  // Skip this virtual template if the 11ty RSS plugin is not being used.
  let rssAvailable = false
  if (eleventyConfig?.javascript?.functions?.htmlBaseUrl) {
    rssAvailable = true
    const feedTemplate = fsImport.readFileSync(`${import.meta.dirname}/templates/feed.njk`).toString()
    eleventyConfig.addTemplate(
      `repos/feed.njk`,
      feedTemplate,
      {
        pagination: {
          data: "branches",
          size: 1,
          alias: "branch",
        },
        permalink: (data) => {
          const repoName = data.branch.repoName
          const branchName = data.branch.branchName
          return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/patches.xml`
        },
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
        eleventyExcludeFromCollections: true,
      }
    )
  }

  // This is used to show/hide the RSS feed link on the landing page.
  eleventyConfig.addGlobalData('rssAvailable', rssAvailable)
After
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
  // htmlBaseUrl is a function defined by the 11ty RSS plugin.
  // Skip this virtual template if the 11ty RSS plugin is not being used.
  let rssAvailable = false
  // if (eleventyConfig?.javascript?.functions?.htmlBaseUrl) {
  //   rssAvailable = true
  //   const feedTemplate = fsImport.readFileSync(`${import.meta.dirname}/templates/feed.njk`).toString()
  //   eleventyConfig.addTemplate(
  //     `repos/feed.njk`,
  //     feedTemplate,
  //     {
  //       pagination: {
  //         data: "branches",
  //         size: 1,
  //         alias: "branch",
  //       },
  //       permalink: (data) => {
  //         const repoName = data.branch.repoName
  //         const branchName = data.branch.branchName
  //         return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/patches.xml`
  //       },
  //       eleventyComputed: {
  //         currentRepo: (data) => reposData.find(repo => {
  //           return repo.name === data.branch.repoName
  //         }),
  //         currentBranch: (data) => reposData.find(repo => {
  //           return repo.name === data.branch.repoName
  //         }).branches.find(branch => {
  //           return branch.name === data.branch.branchName
  //         }),
  //       },
  //       eleventyExcludeFromCollections: true,
  //     }
  //   )
  // }

  // This is used to show/hide the RSS feed link on the landing page.
  eleventyConfig.addGlobalData('rssAvailable', rssAvailable)
partial_templates/main_top.njk:83
Before
82
83
84
85
86
87
88
89
90
                    <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 branches %}
                        {% if branch.repoName == nav.repoName %}
                        <option value="{{branch.repoName | slugify}},{{branch.branchName | slugify}},{{nav.path}}" {% if branch.branchName == nav.branchName %}selected{% endif %}>{{branch.branchName}}</option>
                        {% endif %}
                        {% 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 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>
schemas/ReposConfiguration.json:34
Before
33
34
35


36


















37
38
        },
        "branchesToPull": {
          "items": {
⁣
⁣
            "type": "string"
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
          },
          "type": "array"
        },
After
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
        },
        "branchesToPull": {
          "items": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "additionalProperties": false,
                "properties": {
                  "max": {
                    "type": "number"
                  },
                  "regex": {
                    "type": "string"
                  }
                },
                "required": [
                  "regex",
                  "max"
                ],
                "type": "object"
              }
            ]
          },
          "type": "array"
        },
schemas/ReposConfiguration.json:52
Before
51
52
53
54


























55
        },
        "location": {
          "type": "string"
        }
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
      },
      "required": [
After
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
        },
        "location": {
          "type": "string"
        },
        "tags": {
          "items": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "additionalProperties": false,
                "properties": {
                  "max": {
                    "type": "number"
                  },
                  "regex": {
                    "type": "string"
                  }
                },
                "required": [
                  "regex",
                  "max"
                ],
                "type": "object"
              }
            ]
          },
          "type": "array"
        }
      },
      "required": [
src/branches.ts:1
Before

0
1
2
3
4
5
6
7
8
9
10
11
⁣
⁣
let cachedBranches = null

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

  cachedBranches = Object.keys(repos).flatMap((repoName) => {
    return Object.keys(repos[repoName].branches).map((branchName) => {
      return {
        branchName,
        repoName,
      }
    })
  })
After
0
1
2
3
4
5
6
7
8
9
10
11
12
13
import { type Repository } from "./dataTypes.ts"

let cachedBranches = null

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

  cachedBranches = repos.flatMap((repo) => {
    return repo.branches.map((branch) => {
      return {
        branchName: branch.name,
        repoName: repo.name,
      }
    })
  })
src/configTypes.ts:51
Before
50
51
52
53

54
55
  location: string,
  description?: string,
  defaultBranch: string,
  branchesToPull: Array<string>,
⁣
  languageExtensions?: {
    [fileExtension: string]: string
  },
After
50
51
52
53
54
55
56
  location: string,
  description?: string,
  defaultBranch: string,
  branchesToPull: Array<string | {regex: string, max: number}>,
  tags?: Array<string | {regex: string, max: number}>,
  languageExtensions?: {
    [fileExtension: string]: string
  },
src/dataTypes.ts:5
Before
4
5
6
7
8
9
10
11
12

13
14

15
16
  defaultBranch: string,
  branches: Array<{
    name: string,
    description?: string,
    head: string,
  }>,
  tags: Array<{
    name: string,
    sha: string,
⁣
  }>,
  commits: Map<string, {
⁣
    message: string,
    author: string,
    date: Date,
After
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  defaultBranch: string,
  branches: Array<{
    name: string,
    head: string,
    fileList: Array<string>,
  }>,
  tags: Array<{
    name: string,
    sha: string,
    fileList: Array<string>,
  }>,
  commits: Map<string, {
    hash: string,
    message: string,
    author: string,
    date: Date,
src/dataTypes.ts:25
Before
24
25
26
27
28
29
30
    }>
  }>,
}

export type BranchInfo = {
  files: Array<string>,
  patches: Array<any>
}
After
24
25




    }>
  }>,
⁣
⁣
⁣
⁣
⁣
}
src/flatFiles.ts:1
Before
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import repos from './repos.ts'

let cachedFlatFiles = null

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

  cachedFlatFiles = Object.keys(repos).flatMap((repoName) => {
    return Object.keys(repos[repoName].branches).flatMap((branchName) => {
      return repos[repoName].branches[branchName].files.map((file) => {
        return {
          file,
          branchName,
          repoName,
        }
      })
    })
After
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { type Repository } from "./dataTypes.ts"

let cachedFlatFiles = null

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

  cachedFlatFiles = repos.flatMap((repo) => {
    return repo.branches.flatMap((branch) => {
      return branch.fileList.map((file) => {
        return {
          file,
          branchName: branch.name,
          repoName: repo.name,
        }
      })
    })
src/flatPatches.ts:1
Before

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
⁣
⁣
let cachedFlatPatches = null

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

  cachedFlatPatches = Object.keys(repos).flatMap((repoName) => {
    return Object.keys(repos[repoName].branches).flatMap((branchName) => {
      return repos[repoName].branches[branchName].patches.map((patch) => {
        return {
          patch,
          branchName,
          repoName,
        }
      })
    })
  })
After
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
⁣
⁣
⁣
⁣
⁣
    })
  })
src/repos.ts:1
Before
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


32
33
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56


57

58

59
60
61
62
63
64
65
import {
⁣
  type GitConfig,
} from './configTypes.ts'
import {type BranchInfo} from './dataTypes.ts'

import repoOperations from './vcses/operations.ts'
import { getBranchInfo } from './vcses/git/operations.ts'
import { getLocation} from './helpers.ts'
import repoHelpers from './vcses/helpers.ts'

type BranchInfoTuple = [string, BranchInfo]
type BranchObject = {
  [key: string]: BranchInfo
}
type RepoObjectTuple = [string, BranchObject]
type RepoObject = {
  [key: string]: {
    branches: BranchObject,
    cloneUrl: string,
  }
}

const getBranchNames = (repoConfig: GitConfig): Array<string> => {return repoConfig.branchesToPull
}

let cachedRepos = null

const repos: (reposConfig: any) => Promise<RepoObject> = async (reposConfig) => {
  if (cachedRepos !== null) { return cachedRepos }

  const repoNames = Object.keys(reposConfig.repos)
⁣
⁣
  const reposTuples: RepoObjectTuple[] = await Promise.all(repoNames.map(async (repoName): Promise<RepoObjectTuple> => {
    const vcs = reposConfig.repos[repoName]._type
    const branchNames = getBranchNames(reposConfig.repos[repoName])
⁣
    const branchTuples: BranchInfoTuple[] = await Promise.all(branchNames.map(async (branchName): Promise<BranchInfoTuple> => {
      const repoLocation = getLocation(reposConfig, branchName, repoName)
      const files = await repoOperations[vcs].getFileList(repoName, branchName, repoLocation)
      const patches = await getBranchInfo(branchName, repoLocation)
      return [branchName, {
        files,
        patches,
      }]
    }))

    const branchesObject: BranchObject = {}
    for (let branchTuple of branchTuples) {
      branchesObject[branchTuple[0]] = branchTuple[1]
    }
    return [repoName, branchesObject]
  }))

  const reposObject: RepoObject = {}
  for (let repoTuple of reposTuples) {
    const repoName = repoTuple[0]
    const repoType = reposConfig.repos[repoName]._type
    reposObject[repoName] = {
⁣
⁣
      branches: repoTuple[1],
⁣
      cloneUrl: repoHelpers[repoType].cloneUrl(reposConfig.baseUrl + (reposConfig.path || ""), repoName)
⁣
    }
  }

  cachedRepos = reposObject
  return reposObject
}

export default repos
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
32

33




34
35

36


37


38
39
40
41
42
43
44
45
46
47
48
49
50
51

52
53
54
import {
  type ReposConfiguration,
  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) => {    if (typeof branch === "string") {
      return branch
⁣
    }
    else {      return "" // todo
    }
  })
}

let cachedRepos = null

const repos: (reposConfig: ReposConfiguration) => Promise<Array<Repository>> = async (reposConfig) => {
  if (cachedRepos !== null) { return cachedRepos }

  const repoNames = Object.keys(reposConfig.repos)
  const repos: Array<Repository> = []

  for (const repoName of repoNames) {
⁣
    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,
    })
  }

⁣
  return repos
}

export default repos
src/vcses/git/operations.ts:38
Before
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  return Array.from(fileSet)
}

export const getBranchInfo = async (branchName: string, repoLocation: string) => {
  const patches: Map<string, {
    name: string,
    description: string,
    author: string,
    date: string,
    hash: string,
    diffs: ReturnType<Repository['commits']['get']>['diffs'],
  }> = new Map()

  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) {
After
37
38
39








40

41
42
  return Array.from(fileSet)
}

⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
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) {
src/vcses/git/operations.ts:62
Before
61
62
63






64
65
      gitLogSubset = gitLogSubset.slice(nextPatchStart)

      const hash = currentPatch[0].replace("commit ", "").trim()
⁣
⁣
⁣
⁣
⁣
⁣
      let author: string, date: string
      [1, 2, 3].forEach((lineNumber) => {
        if (currentPatch[lineNumber].startsWith("Author")) {
After
61
62
63
64
65
66
67
68
69
70
71
      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)) {
        break
      }

      let author: string, date: string
      [1, 2, 3].forEach((lineNumber) => {
        if (currentPatch[lineNumber].startsWith("Author")) {
src/vcses/git/operations.ts:75
Before
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
        return line.startsWith("diff ")
      })
      // Git log is indent four spaces by default -- remove those.
      const commitMessage = currentPatch.slice(4, diffStart).map(str => str.replace("    ", ""))
      const name = commitMessage[0].trim()
      const description = commitMessage.slice(1, commitMessage.length - 1).filter((line) => {
        // git log --porcelain output adds these "Ignore-this:" lines and I'm not sure what they are
        return !line.startsWith('Ignore-this: ')
      }).join("\n").trim()

      const diffs = getGitDiffsFromPatchText(currentPatch.slice(diffStart).join("\n"))
      patches.set(hash, {
        name,
        description,
        author,
        date,
        hash,
        diffs,
      })
    } while (gitLogSubset.length > 1)
  }

  return Array.from(patches.values())
}

export const getFileLastTouchInfo = async (repo, branch, filename, repoLocation) => {
  const regex = RegExp(".* [0-9]+ [0-9]+")
  const command = `git blame --porcelain ${branch} ${filename}`
  const res = await exec(`(cd ${repoLocation} && ${command})`)
After
74
75
76


77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92


93
94
95
96
97
        return line.startsWith("diff ")
      })
      // Git log is indent four spaces by default -- remove those.
⁣
⁣
      const commitMessage = currentPatch.slice(4, diffStart).map(str => str.replace("    ", "")).filter((line) => {
        // git log --porcelain output adds these "Ignore-this:" lines and I'm not sure what they are
        return !line.startsWith('Ignore-this: ')
      }).join("\n").trim()

      const diffs = getGitDiffsFromPatchText(currentPatch.slice(diffStart).join("\n"))
      commits.set(hash, {
        hash,
        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})`)
src/vcses/operations.ts:2
Before
1
2
3
4
5
6
7
8
9
10
  getFileList as getGitFileList,
  getFileLastTouchInfo as getGitFileLastTouchInfo,
} from './git/operations.ts'
import {type BranchInfo} from '../dataTypes.ts'

type RepoOperationsType = {
  [vcs: string]: {
    getFileList: (repoName: string, branchName: string, repoLocation: string) => Promise<BranchInfo['files']>,
    getFileLastTouchInfo: (repoName: string, branchName: string, filename: string, repoLocation: string) => Promise<Array<{sha: string, author: string}>>,
  }
}
After
1
2
3
4

5
6
7
8
9
  getFileList as getGitFileList,
  getFileLastTouchInfo as getGitFileLastTouchInfo,
} from './git/operations.ts'

⁣
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}>>,
  }
}
templates/files.njk:1
Before
0
1
2
3
4
5
<div class="row">
  <div class="col">
    <ul class="list-group">
    {% set files = repos[branchInfo.repoName].branches[branchInfo.branchName].files | topLevelFilesOnly('') %}
    {% for file in files %}
      <li class="list-group-item">
        {% if file.isDirectory %}
After
0
1
2
3
4
5
<div class="row">
  <div class="col">
    <ul class="list-group">
    {% set files = currentBranch.fileList | topLevelFilesOnly('') %}
    {% for file in files %}
      <li class="list-group-item">
        {% if file.isDirectory %}
templates/index.njk:1
Before
0
1
2
3
<ul>
{% for repoName, options in repos %}
  <li><a href="{{reposPath}}/{{repoName | slugify}}/branches/{{reposConfig.repos[repoName].defaultBranch}}">{{repoName}}</a></li>
{% endfor %}
</ul>
After
0
1
2
3
<ul>
{% for repo in repos %}
  <li><a href="{{reposPath}}/{{repo.name | slugify}}/branches/{{reposConfig.repos[repo.name].defaultBranch}}">{{repo.name}}</a></li>
{% endfor %}
</ul>
templates/repo.njk:15
Before
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
            </div>
          {% endif %}
        </div>
        {% for patch in repos[branch.repoName].branches[branch.branchName].patches | batch(3) | first %}
        <div class="card mt-2 mb-4">
          <div class="card-body">
            <a href="{{reposPath}}/{{branch.repoName | slugify}}/branches/{{branch.branchName | slugify}}/patches/{{patch.hash}}" class="text-primary d-inline-block card-title fs-5">{{patch.name}}</a>
            <p class="card-subtitle fs-6 mb-2 text-body-secondary">{{patch.date}}</p>
            <p class="card-subtitle fs-6 mb-2 text-body-secondary">{{patch.author}}</p>
            <p class="card-text">{{patch.description | truncate(150)}}</p>
          </div>
          <div class="card-footer">
            <button data-hash="{{patch.hash}}" data-vcs="git" class="copy-btn btn btn-sm btn-outline-primary">
              {{patch.hash | truncate(8, true, "")}} <i class="bi-copy bi me-1"></i>
            </button>
          </div>
        </div>
        {% endfor %}
      </div>
    </div>
  </div>
After
14
15
16















17
18
            </div>
          {% endif %}
        </div>
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
⁣
      </div>
    </div>
  </div>