From 3d60a008ffe333c39e6baa0f0c694129af650355 Mon Sep 17 00:00:00 2001 From: Wesley Hershberger Date: Sun, 9 Feb 2025 23:15:32 -0600 Subject: [PATCH 1/2] Support non-tag/head refs This also only draws HEAD when it points to a valid reference --- src/git_mappers.js | 8 ++++++++ src/index.js | 23 +++++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/git_mappers.js b/src/git_mappers.js index fdcd39b..128f07f 100644 --- a/src/git_mappers.js +++ b/src/git_mappers.js @@ -161,6 +161,13 @@ const mapTagToStatements = createRefMapper({ margin: 0.2, }); +const mapRefsToStatements = createRefMapper({ + refBasePath: "refs/", + shape: "cds", + fillColor: "orange", + margin: 0.1, +}); + module.exports = { mapLineToObject, mapLineToRef, @@ -170,6 +177,7 @@ module.exports = { mapAnnotatedTag, mapHeadToStatements, mapTagToStatements, + mapRefsToStatements, mapTreeToStatements, mapCommitToStatements, mapBlobToStatement, diff --git a/src/index.js b/src/index.js index 1d67fcc..9fc7621 100644 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,7 @@ const { flattenDepth, zipWith, compact } = require("lodash/array"); const { exec } = require("./utility"); const { splitLines, splitByWhitespace } = require("./string_utils"); const { isEmpty } = require("lodash/lang"); -const { map, filter } = require("lodash/collection"); +const { map, filter, some } = require("lodash/collection"); const { stmtList, digraph } = require("./dot_builders"); const { mapRawCommit, @@ -17,6 +17,7 @@ const { mapHeadToStatements, mapAnnotatedTagsToStatements, mapTagToStatements, + mapRefsToStatements, } = require("./git_mappers"); // strip off node executable and script path @@ -46,8 +47,7 @@ const git = `git --git-dir=${gitDir}`; const noBlobs = opts.includes("--no-blobs"); const noTrees = opts.includes("--no-trees"); -const showHeads = () => exec(`${git} show-ref --heads`); -const showTags = () => exec(`${git} show-ref --tags`); +const showRefs = () => exec(`${git} show-ref`); const catAllObjects = () => exec(`${git} cat-file --batch-check --batch-all-objects`); const catFile = hash => exec(`${git} cat-file -p ${hash}`); const catObject = gitObject => catFile(getHash(gitObject)); @@ -97,9 +97,15 @@ async function main() { const rawHEAD = fs.readFileSync(`${gitDir}/HEAD`).toString(); const HEAD = rawHEAD.startsWith("ref:") ? splitByWhitespace(rawHEAD)[1] : rawHEAD; - // there may be no tags at all and a repo with no commits will have a floating main branch - const tagData = map(compact(splitLines(await showTags().catch(() => ""))), mapLineToRef); - const headData = map(compact(splitLines(await showHeads().catch(() => ""))), mapLineToRef); + // perfectly alright to have no refs at all (empty repo) + const refData = map(compact(splitLines(await showRefs().catch(() => ""))), mapLineToRef); + + // Only draw HEAD if it points to a ref that exists + const validHead = some(refData, (ref) => HEAD === ref.name); + + const tagData = filter(refData, (ref) => ref.name.includes("/tags/")); + const headData = filter(refData, (ref) => ref.name.includes("/heads/")); + const miscRefData = filter(refData, (ref) => !ref.name.includes("/tags/") && !ref.name.includes("/heads/")); /** * TODO think more about what the command line options do, @@ -111,9 +117,10 @@ async function main() { map(commitData, data => mapCommitToStatements(data, noTrees)), !noTrees && map(treeData, data => mapTreeToStatements(data, noBlobs)), map(headData, mapHeadToStatements), - mapHeadToStatements({ hash: HEAD, name: "HEAD" }), - map(annotatedTagData, mapAnnotatedTagsToStatements), map(tagData, mapTagToStatements), + map(miscRefData, mapRefsToStatements), + validHead && mapHeadToStatements({ hash: HEAD, name: "HEAD" }), + map(annotatedTagData, mapAnnotatedTagsToStatements), ]; const statements = flattenDepth(compact(statementsByType), 2); From 18924b43b16051580f6e1898e3b492361a0ce1f5 Mon Sep 17 00:00:00 2001 From: Wesley Hershberger Date: Wed, 25 Jun 2025 21:19:17 -0500 Subject: [PATCH 2/2] Fix detached HEAD git 2.43.0 uses the commit hash in .git/HEAD instead of `ref: ` syntax when checking out tags, so this only needs to consider commits. --- src/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index 9fc7621..00b8d55 100644 --- a/src/index.js +++ b/src/index.js @@ -94,14 +94,15 @@ async function main() { ); // git HEAD could be detached, referring directly to a commit, not another ref - const rawHEAD = fs.readFileSync(`${gitDir}/HEAD`).toString(); + const rawHEAD = fs.readFileSync(`${gitDir}/HEAD`).toString().trim(); const HEAD = rawHEAD.startsWith("ref:") ? splitByWhitespace(rawHEAD)[1] : rawHEAD; // perfectly alright to have no refs at all (empty repo) const refData = map(compact(splitLines(await showRefs().catch(() => ""))), mapLineToRef); - // Only draw HEAD if it points to a ref that exists - const validHead = some(refData, (ref) => HEAD === ref.name); + // Only draw HEAD if it points to a ref or commit that exists + const refsAndCommits = refData.concat(commitData); + const validHead = some(refsAndCommits, (obj) => HEAD === obj.name || HEAD === obj.hash); const tagData = filter(refData, (ref) => ref.name.includes("/tags/")); const headData = filter(refData, (ref) => ref.name.includes("/heads/"));