Mon Mar 10 2025
tucker.mcknight@gmail.com
Also remove the filter for getting a patch's contents; instead, get that right away when the initial parsing of the log is happening. Doing it in a separate step (meaning you need to make N cli calls for N patches in the repo) takes forever on large repos (like darcs.net). Adding the -v flag to `darcs log` gives the diff output in that call, so we can get the diffs and save them in the `repos` global data at the same time. Also, this change makes `darcs log` run with the --index flag, which is used to check only a limited number of results in the log output. Again, large repos (darcs.net) were being a problem, exceeing the nodejs max string buffer size when trying to read the entire `darcs log` output in one go. So now it reads 100 at a time and loops through until the log is empty.
bd9ace5a2fc0c0c685e6f37a3b7938c4af982f6f
import { getDiffsFromPatchText } from '../helpers.js'
let repos = null
const repos =await Promise.all(Object.keys(darcsConfig.repos).map(async (repoName) => {
const repoLocation = darcsConfig.repos[repoName].location
const filesRes = await exec(`(cd ${repoLocation}; darcs show files)`)
const files = filesRes.stdout.split("\n").filter(item => item.length > 0 && item != ".")
const patchesRes = await exec(`(cd ${repoLocation}; darcs log --xml-output)`)
const xmlString = patchesRes.stdout
const jsonString = xmlJs.xml2json(xmlString)
const logs = JSON.parse(jsonString)
const patches = logs.elements[0].elements.map((element) => {
const name = element.elements.find(elem = > elem.name == "name").elements.find(elem => elem.type = ="text").text
return {
details: element.attributes, name: name,
if (repos === null) {
repos = await Promise.all(Object.keys(darcsConfig.repos).map(async (repoName) => {
const repoLocation = darcsConfig.repos[repoName].location
const filesRes = await exec(`(cd ${repoLocation}; darcs show files)`)
const files = filesRes.stdout.split("\n").filter(item => item.length > 0 && item != ".")
const patches = new Map()
const totalPatchesCountRes = await exec(`(cd ${repoLocation}; darcs log --count)`)
const totalPatchesCount = parseInt(totalPatchesCountRes.stdout)
let hunkRegex = RegExp(/^ *hunk /)
// Get 100 patches at a time and parse those
for (let i = 1; i <= totalPatchesCount; i = i + 100) {
let patchesSubset = await exec(`(cd ${repoLocation}; darcs log --index=${i}-${i+100} -v)`)
patchesSubset = patchesSubset.stdout.split("\n")
do {
const nextPatchStart = patchesSubset.findIndex((line, index) => {
return index > 0 && line.startsWith("patch ")
})
const currentPatch = patchesSubset.slice(0, nextPatchStart - 1)
patchesSubset = patchesSubset.slice(nextPatchStart)
const hash = currentPatch[0].replace("patch ", "")
const author = currentPatch[1].replace("Author: ", "")
const date = currentPatch[2].replace("Date: ", "").trim()
const name = currentPatch[3]
const diffStart = currentPatch.findIndex((line) => {
return line.match(hunkRegex)
})
const diffs = getDiffsFromPatchText(currentPatch.slice(diffStart).map(str => str.trimStart()).join("\n"))
patches.set(hash, {
name,
author,
date,
hash,
diffs,
})
} while (patchesSubset.length > 1)
})
const info = {
files,
patches,
}
return [repoName, info]
}))
const info = {
files,
patches: [...patches.values()],
}
return [repoName, info]
}))
}
import * as Diff from 'diff'
import _ from 'lodash'
import { getDiffsFromPatchText } from './helpers.js'
})
eleventyConfig.addAsyncFilter("getPatchContents", async( repo, patchHash) => {
const res = await exec(`(cd ${darcsConfig.repos[repo].location}; darcs log -h ${patchHash} -v --machine-readable)`)
// TODO: look for closing bracket more sensibly than this
const closingBracket = res.stdout.indexOf("]")
const justTheDiffs = res.stdout.slice(closingBracket + 2) // Start at closingBracket plus the whitespace after it
const lines = justTheDiffs.split("\n")
const hunks = []
let previousHunk = -1
lines.forEach((line, index) => {
if (line.startsWith("hunk") || index === lines.length - 1) {
if (previousHunk === -1) {
previousHunk = index
return
}
// get diff from previous hunk to this next one
const lastHunk = lines.slice(previousHunk, index)
let lastHunkBefore = lastHunk.filter(line => line.startsWith("-")).map(str => str.replace("-", "")).join("\n")
let lastHunkAfter = lastHunk.filter(line => line.startsWith("+")).map(str => str.replace("+", "")).join("\n")
lastHunkBefore = _.escape(lastHunkBefore)
lastHunkAfter = _.escape(lastHunkAfter)
const changeObject = Diff.diffWords(lastHunkBefore, lastHunkAfter)
changeObject.forEach((obj) => {
if (obj.added) {
lastHunkAfter = lastHunkAfter.replace(obj.value, `<span class=green>${obj.value}</span>`)
}
if (obj.removed) {
lastHunkBefore = lastHunkBefore.replace(obj.value, `<span class=red>${obj.value}</span>`)
}
})
let filename = lines[previousHunk].replace("hunk ./", "")
const regex = RegExp(/(.*) ([0-9]+)$/)
const matches = filename.match(regex)
hunks.push({
file: `${matches[1]}:${matches[2]}`,
before: lastHunkBefore,
after: lastHunkAfter,
changeObject,
})
previousHunk = index
}
})
return hunks
permalink: "repos/{{patchInfo.repoName | slugify}}/patches/{{patchInfo.patch.details.hash}}/"
permalink: "repos/{{patchInfo.repoName | slugify}}/patches/{{patchInfo.patch.hash}}/"
<p>{{patchInfo.patch.details.author}}</p>
<p>{{patchInfo.patch.details.hash}}</p>
{% set patchHunks = patchInfo.repoName | getPatchContents(patchInfo.patch.details.hash) %}
<p>{{patchInfo.patch.author}}</p>
<p>{{patchInfo.patch.hash}}</p>
{% set patchHunks = patchInfo.patch.diffs %}
<li><span><a href="/repos/{{repo | slugify}}/patches/{{patch.details.hash}}">{{patch.details.hash}}></a></span> - <span>{{patch.details.date | date}}</span> - <span>{{patch.details.author}}</span> - <span>{{patch.name}}</span></li>
<li><span><a href="/repos/{{repo | slugify}}/patches/{{patch.hash}}">{{patch.hash}}></a></span> - <span>{{patch.date | date}}</span> - <span>{{patch.author}}</span> - <span>{{patch.name}}</span></li>