Skip to content

Conversation

jvns
Copy link
Collaborator

@jvns jvns commented Sep 3, 2025

Someone on Mastodon suggested it would be nice to have a cheat sheet on the Git website, so I thought I'd put together a prototype to see if it's of interest, perhaps for a future "Learn" section of the site. You can see it in action here: Git cheat sheet

There are definitely lots of Git cheat sheets out there already (for example from github and from ndpsoftware), but it feels like it would be nice to have one that's lower tech and not GitHub branded.

The content and styling both definitely need work -- right now it's basically an import of this cheat sheet, which has some of my personal Git idiosyncracies in it (like insisting on using HEAD^^^^^^ instead of HEAD~5 🙈).

I got a bit carried away writing handwritten SVG Git graphs, and I'm not sure they'll display in all browsers as is. Here's what they're meant to look like. I think I'll figure out a better solution than the handwritten SVGs, maybe graphviz.

image

@dscho
Copy link
Member

dscho commented Sep 3, 2025

There are Mermaid diagrams... maybe they are Good Enough™️?

  gitGraph
    commit id: "A"
    commit id: "B"
    branch topic
    commit id: "D"
    commit id: "E"
    checkout main
    commit id: "C"
Loading

And while Hugo does not support Mermaid diagrams out of the box, it is relatively easy to integrate them, I did something similar yesterday.

@jvns
Copy link
Collaborator Author

jvns commented Sep 3, 2025

I didn't know you could integrate Mermaid into Hugo in that way, that's cool! Maybe we could integrate Graphviz in a similar way -- I find the Mermaid diagrams aren't information dense enough (those labels are so small!), but Graphviz can do it easily (preview link)

@dscho
Copy link
Member

dscho commented Sep 4, 2025

Maybe we could integrate Graphviz in a similar way

Apparently we could! I just found https://huanglei.rocks/posts/add-math-and-dot-for-hugo/ which talks precisely about that... Unfortunately, it was quite outdated, and it left a lot of gaps to figure out. And then I fell into the trap that I wanted to use codeblocks, but git-scm.com is maintained as .html source files, so no codeblocks for Dscho!

But I'm no giver-upper, so I battled it, and I won! You can see the proof-of-concept here: https://dscho.github.io/git-scm.com/about/small-and-fast (commits: gh-pages...dscho:git-scm.com:poc-diagrams). For posterity, here is a screenshot:

image

The upper diagram uses Mermaid, the lower one Graphviz dot and neato.

I agree that Graphviz is more powerful than Mermaid, but it comes at the price of 1.4MB vs 25kB...

@jvns
Copy link
Collaborator Author

jvns commented Sep 4, 2025

Nice, I hadn't even thought about the issues with using codeblocks in HTML so it's really cool to see the solution.

Re the bundle sizes: it does look like the CDN version of Mermaid is smaller than viz.js (it downloads a bunch of extra code after the 25K load: from adding up the numbers in my head it looks like it's about 500K of code over maybe 30 different HTTP requests). But I'm not sure if it's possible to actually get that 500K size without using the CDN, and I'm also concerned about how dynamically loading 30 different JS files would impact loading files. Here's a discussion about large Mermaid bundle sizes. https://github.com/orgs/mermaid-js/discussions/4314

I tried to get a single JS file that we could host ourselves and the sizes came out to 1.4MB for viz.js and 2.5MB for mermaid.js.

$ cat viz.js
import * as Viz from "@viz-js/viz";

Viz.instance().then(viz => {
  document.body.appendChild(viz.renderSVGElement("digraph { a -> b }"))
});
$ cat mermaid.js
  import mermaid from "mermaid";
  mermaid.initialize({ startOnLoad: true });
$ esbuild viz.js --bundle --minify --tree-shaking=true --outfile=bundle-viz.js
  bundle-viz.js  1.3mb ⚠️
$ esbuild mermaid.js --bundle --minify --tree-shaking=true --outfile=bundle-mermaid.js
  bundle-mermaid.js  2.5mb ⚠️

Here's a screenshot of the Mermaid CDN loading all of its JS chunks:
image

@dscho
Copy link
Member

dscho commented Sep 4, 2025

Oh wow, mermaid has transitive dependencies? That's horrible!

And yes, while 1.4MB is not exactly light-weight, it is at least possible.

Obviously, we could consider a server-side rendering approach that generates an file containing a list of files that had {{< graphviz >}} constructs in them, and then post-process those files via nokogiri and replace the <pre class="graphviz"> tags with the corresponding .svg files. This would require another manual step (and graphviz) in development setups, the deployment workflow could be adjusted accordingly.

I think I would prefer that approach to requiring a 1.4MB JavaScript library just to render the diagrams.

@jvns
Copy link
Collaborator Author

jvns commented Sep 4, 2025

I agree that 1.44MB feels like too much, especially since the SVG files are only 3K each. I'm a bit worried that the Nokogiri approach would result in a really long build time while tweaking the diagrams though. I think it might be faster to have a directory of .dot files which all get converted to SVG with a bash script, maybe the equivalent of something like this:

for i in *.dot
do
neato -Tsvg $i > assets/images/graphs/$i.svg
done

I've already been using completely separate version of the files (just a standalone cheat-sheet.html) to develop with because the Hugo build times are slow.

@dscho
Copy link
Member

dscho commented Sep 4, 2025

I've already been using completely separate version of the files (just a standalone cheat-sheet.html) to develop with because the Hugo build times are slow.

Oh, I hope you're using a sparse checkout? In such a checkout, even on Windows the Hugo build times are somewhat bearable. Here is a run in the worktree in which I developed the Graphviz patches:

$ time hugo
Start building sites …
hugo v0.148.2-40c3d8233d4b123eff74725e5766fc6272f0a84d+extended windows/amd64 BuildDate=2025-07-27T12:43:24Z VendorInfo=gohugoio


                  │ EN
──────────────────┼─────
 Pages            │  39
 Paginator pages  │   0
 Non-page files   │   0
 Static files     │ 338
 Processed images │   0
 Aliases          │  42
 Cleaned          │   0

Total in 2122 ms

real    0m3.107s
user    0m0.015s
sys     0m0.078s

It's this fast because I exclude all external/ stuff:

$ git sparse-checkout list
assets
content
data
hugo.yml
layouts
script
static
tests

I agree that 1.44MB feels like too much, especially since the SVG files are only 3K each. I'm a bit worried that the Nokogiri approach would result in a really long build time while tweaking the diagrams though.

Right. Maybe the best approach would be to use that 1.44MB file by default, but also write the list of .html files that use viz-global.js into a file, and then have a post-processing .rb script that only runs when deploying it to the site? At least after I thought about this for a while, I liked that approach best.

@jvns
Copy link
Collaborator Author

jvns commented Sep 4, 2025

@dscho I tried switching to a sparse checkout again (not sure why I couldn't get it to work last time) and your {{< graphviz engine="neato" >}} trick works so well now! I love it. these graphs still need work but here's where I am right now, you can see them here: https://jvns.github.io/git-scm.com/cheat-sheet

@dscho
Copy link
Member

dscho commented Sep 4, 2025

Very nice!

BTW I worked some more on the idea that I mentioned earlier, to pre-render the diagrams as a post-processing step after Hugo. Here is the result: gitgitgadget/gitgitgadget.github.io@266bccc. You can see it in action here: https://dscho.github.io/gitgitgadget.github.io/architecture (this has the diagrams I added in gitgitgadget/gitgitgadget.github.io@af226bb pre-rendered as inline SVGs).

@dscho
Copy link
Member

dscho commented Sep 5, 2025

I've updated my poc-diagrams branch with two fixups (to avoid unnecessary empty lines) and with two commits to pre-render the Graphviz diagrams: 0f15924...0b34d58.

And I am really glad that I made those changes. When I loaded https://dscho.github.io/git-scm.com/about/small-and-fast into my browser, there was a noticeable delay before the Mermaid diagram was shown (but the Graphviz-generated SVG was displayed immediately, as one would expect). Makes for a much nicer user experience.

@jvns jvns force-pushed the cheat-sheet branch 3 times, most recently from c051d8f to 008916c Compare September 5, 2025 12:04
@jvns
Copy link
Collaborator Author

jvns commented Sep 5, 2025

Awesome!

I've made a bunch of updates and now I'd love comments on the content or styling of the page. Here's the current version again: https://jvns.github.io/git-scm.com/cheat-sheet

I built the cheat sheet mostly from the commands I use in my personal Git workflow (other than switch and restore, which I put in because "switch" seems like a nicer interface than "checkout" even though I haven't adopted it personally), so there might be a some idiosyncrasies that still need to be removed. It's definitely a cheat sheet for using Git in a "github-style" way.

One of my goals for the cheat sheet is to be a little bit more of a "complete" cheat sheet than the one GitHub provides -- for example I find it hard to imagine living without something like git log -S when doing archaeology.

I'm also using a sans-serif font right now, should probably move it back to the site font.

I'll try to stop force pushing changes at the point to make the changes easier to track.

@schacon
Copy link
Member

schacon commented Sep 5, 2025

Very cool!

Might be nice to add a TOC into the sidebar, as it's a bit long:

CleanShot 2025-09-05 at 14 50 25@2x

@schacon
Copy link
Member

schacon commented Sep 5, 2025

Would also be nice to link your cheat sheet at the top as a downloadable PDF version of this.

@dscho
Copy link
Member

dscho commented Sep 5, 2025

now I'd love comments on the content or styling of the page.

Yes! I'll stop cluttering this ticket with Graphviz stuff.

One thing that I noticed is that the dark mode probably needs slight adjustments.

The content is awesome!

The "new" way to get and set config variables is git config set and git config get, by the way. Maybe we want to use that style? Or do you think that enough people will be stuck on ancient Git versions (cough Debian cough)?

Do we want to get a bit more into the weeds like using the power of Git aliases? I am thinking about things like

  • git config set alias.ps '-p status' to force pagination (i.e. you can pass options before the Git command name)
  • git config set alias.timestamped-commit '!git commit -m "$(date +%Y%m%d-%H%M%S)' (i.e. you can call shell commands)
  • git config set alias.hrmpf '!f() { git rev-list --count "${1:-..upstream/master}" -- vendor/; }; f' (i.e. you can use shell functions to insert command-line parameters to the alias even when they do not fit at the end of the called Git command's parameters)
You probably don't want _this_ alias, though
[alias]
	edit-alias = "!\ndie() {\n\techo \"$*\" >&2\n\texit 1\n}\n\nedit_alias() {\n\tl_opt=--global\n\tcase \"$1\" in\n\t-l|--local)\n\t\tl_opt=--local\n\t\tshift\n\t\t;;\n\t-g|--global)\n\t\tl_opt=--global\n\t\tshift\n\t\t;;\n\tesac\n\ttest $# = 1 || die \"Need the name of an alias\"\n\n\ttmpdir=$(mktemp -d) &&\n\ttrap \"rm -rf $tmpdir\" EXIT &&\n\ttmpscript=\"$tmpdir/$1.sh\" &&\n\tgit config $l_opt --default '!\n# This is a new shell alias; If you do anything complicated, consider\n# implementing one or more shell functions, and end in a function call\n# whose parameters will be the command-line parameters passed to that alias.\n\ndie() {\n\techo \"$*\" >&2\n\texit 1\n}\n\nhello() {\n\ttest $# = 1 || die \"Need exactly one parameter\"\n\techo \"Hello, $1!\"\n}\n\nhello' alias.\"$1\" >\"$tmpscript\" &&\n\t\"$(git var GIT_SEQUENCE_EDITOR)\" \"$tmpscript\" &&\n\tgit config set $l_opt alias.\"$1\" \"$(cat \"$tmpscript\")\"\n}\n\nedit_alias"

😆

I'm also using a sans-serif font right now, should probably move it back to the site font.

Oh, please consider keeping using the sans-serif font! For cheat-sheets, I prefer them, so I'd vote to keep using it.

@dscho
Copy link
Member

dscho commented Sep 5, 2025

Would also be nice to link your cheat sheet at the top as a downloadable PDF version of this.

Oh @schacon, why do you want to nerd-snipe me into derailing the discussion with technically challenging automation again? 🤣

@dscho
Copy link
Member

dscho commented Sep 5, 2025

I find it hard to imagine living without something like git log -S when doing archaeology.

You may be interested in git log -G<regex>. I used to be a power user of the -S option, often cursing it for not finding the term only because it appeared as many times on the - as on the + line. The -G option does not share that flaw, and it offers the full power of regular expressions (well, I think it cannot do negative look-behind, because it cannot be told to use Perl-style regular expressions, Git's notorious inconsistencies extend to ignoring --perl-regexp under some circumstances if I remember correctly).

tl;dr I would highly recommend showing off git log -G and omitting git log -S.

@dscho
Copy link
Member

dscho commented Sep 5, 2025

Might be nice to add a TOC into the sidebar

There you go @schacon, @jvns is fast!

image

@dscho
Copy link
Member

dscho commented Sep 5, 2025

@jvns the dark mode colors look pretty good, except of course the SVGs:

image

I could try to hack on that, in addition to the PDF...

@jvns
Copy link
Collaborator Author

jvns commented Sep 5, 2025

I like the idea of including a downloadable PDF version, and I'd also like to be able to keep it updated as the page evolves. Maybe with a print stylesheet somehow? I'm not sure how easy it would be to get something that looks good though.

I tried a more basic approach to displaying the SVG in dark mode.

Comment on lines 29 to 24
svg {
height: auto;
width: 100%;
border-radius: 5px;
padding: 5px;
box-sizing: border-box;
background: var(--svg-bg);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit concerned that this might affect other SVGs than the Graphviz ones we generate, e.g. the company logos on the front page (which seem to be designed to look good in both dark/light mode?).

Here is an alternative that I would like to offer: jvns#1

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of using invert! This selector is .cheat-sheet .item svg so it's already very specific, it won't affect other SVGs on the site.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I missed that it was nested. Great!

@To1ne
Copy link
Collaborator

To1ne commented Sep 6, 2025

@jvns I'm catching up on this PR and it's amazing. Exactly the type of content we need for "Learn"/"Getting started".

Obviously, we could consider a server-side rendering approach that generates an file containing a list of files that had {{< graphviz >}} constructs in them, and then post-process those files via nokogiri and replace the

 tags with the corresponding .svg files. This would require another manual step (and graphviz) in development setups, the deployment workflow could be adjusted accordingly.

I think I would prefer that approach to requiring a 1.4MB JavaScript library just to render the diagrams.

@dscho I guess this was already settled, but I prefer server-side rendering. Please don't render them on the client-side, it doesn't make sense to bloat the user with this load (even though it's only floppy of extra data to transmit). I would even consider to check-in the generated svgs (similar to what we do with the man pages). We could have a Action that checks if the svgs are up-to-date.

About Mermaid and Graphviz, I like Graphviz more, visually. They fit the aesthetic of the site better, and are more dense as Julia pointed out.


image

@jvns I love how you've put your signature into this. 💜


Would also be nice to link your cheat sheet at the top as a downloadable PDF version of this.

Oh @schacon, why do you want to nerd-snipe me into derailing the discussion with technically challenging automation again? 🤣

@dscho I'm happy you are taking the bait... 🤣

Anyhow, that can happen outside this PR.

@dscho
Copy link
Member

dscho commented Sep 7, 2025

Would also be nice to link your cheat sheet at the top as a downloadable PDF version of this.

https://dscho.github.io/git-scm.com/cheat-sheet.pdf (generated via jvns/git-scm.com@cheat-sheet...dscho:git-scm.com:printable-cheat-sheet).

@To1ne
Copy link
Collaborator

To1ne commented Sep 7, 2025

@dscho That looks amazing! And I love the git --print-out. That's pretty sweet you were able to achieve this with Playwright!

And it's great you've improved the printiness (or is it printability?) of any page. Although I've noticed man pages for example git-commit has 5 or more empty pages in it. By coincidence any idea why?

@dscho
Copy link
Member

dscho commented Sep 7, 2025

Although I've noticed man pages for example git-commit has 5 or more empty pages in it. By coincidence any idea why?

@To1ne not by coincidence, and when you asked, I did not have an idea. But now I do:

position: relative;

Overriding that (as I found out via straight-forward, if laborious bisecting) removes those relative pages.

Speculation as to why

I expect this to have something to do with the many layers of abstraction in the HTML: body .inner #content-wrapper #content #main is a fully valid CSS selector, and all it gets you is the <div> in which the actual content lives. Now, I avoid playing CSS layout games whenever I can, because I find this harder than playing chess at a decent level, seeing as it is all too easy to mess something up like not leaving enough space for some padding, or running into floating point rounding issues when some careful calculation of what fits barely within what goes awry, which I suspect to be the case here. In any case, the relative positioning is not even necessary, certainly not in print mode, so there you go.

I overrode it in a @media print rule and bam it works: https://dscho.github.io/git-scm.com/docs/git-commit. And I force-pushed the branch: jvns/git-scm.com@cheat-sheet...dscho:git-scm.com:printable-cheat-sheet.

Range-diff
  • 1: ca85c5b ! 1: d7971c6 Add a print-specific style sheet

    @@ assets/sass/application.scss: $baseurl: "{{ .Site.BaseURL }}{{ if (and (ne .Site
      ## assets/sass/print.scss (new) ##
     @@
     +@media print {
    ++  .inner {
    ++    // The `position` of the `inner` class is defined as `relative`, which
    ++    // causes funny issues when printing, for example tens of empty pages in
    ++    // the middle. Let's suppress that.
    ++    position: inherit;
    ++    margin-bottom: 0;
    ++  }
    ++
    ++  #main {
    ++    margin-bottom: 0;
    ++  }
    ++
    ++  footer {
    ++    margin-top: 0;
    ++  }
    ++
     +  aside, .sidebar-btn, #search-container, #reference-version, #dark-mode-button, .site-source {
     +    display: none;
     +  }
  • 2: e5b0932 = 2: 84bdc9f print: handle rounded borders correctly with multiple pages

  • 3: 9aac747 ! 3: 350ec93 Show a different tagline for print media

    @@ Commit message
     
         Signed-off-by: Johannes Schindelin <[email protected]>
     
    - ## layouts/partials/header.html ##
    -@@
    -      "distributed-is-the-new-centralized"
    -    ];
    -    var tagline = taglines[Math.floor(Math.random() * taglines.length)];
    --   document.getElementById('tagline').innerHTML = '--' + tagline;
    -+   const taglineElement = document.getElementById('tagline');
    -+   taglineElement.innerHTML = `--${tagline}`;
    -+   window.matchMedia('print').addListener((mediaQueryList) => {
    -+     if (mediaQueryList.matches) {
    -+         taglineElement.innerHTMLBackup = taglineElement.innerHTML;
    -+         taglineElement.innerHTML = '--print-out';
    -+     } else {
    -+         taglineElement.innerHTML = taglineElement.innerHTMLBackup || '--as-git-as-it-gets';
    -+     }
    -+   })
    -   </script>
    + ## assets/js/application.js ##
    +@@ assets/js/application.js: $(document).ready(function() {
    +   Downloads.init();
    +   DownloadBox.init();
    +   PostelizeAnchor.init();
    ++  Print.init();
    + });
      
    -   {{ if ne (.Scratch.Get "section") "search" }}
    + function onPopState(fn) {
    +@@ assets/js/application.js: var PostelizeAnchor = {
    +   },
    + }
    + 
    ++var Print = {
    ++  init: function() {
    ++    Print.tagline = $("#tagline");
    ++    Print.scrollToTop = $("#scrollToTop");
    ++    window.matchMedia("print").addListener((mediaQueryList) => {
    ++      Print.toggle(mediaQueryList.matches);
    ++    });
    ++  },
    ++  toggle: function(enable) {
    ++    if (enable) {
    ++      Print.taglineBackup = Print.tagline.html();
    ++      Print.tagline.html("--print-out");
    ++      Print.scrollToTopDisplay = Print.scrollToTop.attr("display");
    ++      Print.scrollToTop.attr("display", "none");
    ++    } else {
    ++      Print.tagline.html(Print.taglineBackup || "--as-git-as-it-gets");
    ++      Print.scrollToTop.attr("display", Print.scrollToTopDisplay);
    ++    }
    ++  }
    ++}
    ++
    + // Scroll to Top
    + $('#scrollToTop').removeClass('no-js');
    + $(window).scroll(function() {
  • 4: 8a7ebd0 = 4: eddeaf2 Hide the dark mode button when printing

  • 5: 0c4104a ! 5: 358a843 Add a script to convert HTML to PDF

    @@ script/html-to-pdf.js (new)
     +
     +const fs = require('fs')
     +const path = require('path')
    ++const url = require('url')
     +const { chromium } = require('playwright')
     +
     +const htmlToPDF = async (htmlPath, options) => {
    @@ script/html-to-pdf.js (new)
     +  const browser = await chromium.launch({ channel: 'chrome' })
     +  const page = await browser.newPage()
     +
    -+  await page.goto(
    -+    `file://${path.isAbsolute(htmlPath) ? htmlPath : path.join(process.cwd(), htmlPath)}`, {
    -+      waitUntil: 'load'
    -+    }
    -+  )
    ++  const htmlPathURL = url.pathToFileURL(htmlPath).toString()
    ++
    ++  console.log(`Processing ${htmlPathURL}...`)
    ++  await page.goto(htmlPathURL, { waitUntil: 'load' })
    ++
     +  await page.pdf({
     +    path: outputPath,
     +    format: 'A4',
  • 6: 6718a27 ! 6: 7e272b1 html-to-pdf: optionally insert PDF link

    @@ assets/sass/print.scss
      \ No newline at end of file
     
      ## script/html-to-pdf.js ##
    -@@ script/html-to-pdf.js: const fs = require('fs')
    - const path = require('path')
    +@@ script/html-to-pdf.js: const path = require('path')
    + const url = require('url')
      const { chromium } = require('playwright')
      
     +const insertPDFLink = (htmlPath) => {
  • 7: 6bf2aa4 ! 7: 82d221e html-to-pdf: deal with HUGO_RELATIVEURLS=false

    @@ Commit message
         loading the file into a browser via the file: protocol. Rewrite
         those URLs to point to the local filesystem.
     
    +    To do so, we use the very convenient `page.route()` functionality of
    +    Playwright. Unfortunately, we do not get the "URLs" that start with a
    +    slash, but instead we get bogus `file:///<wrong-path>` URLs. Luckily,
    +    those wrong paths do not start with the absolute path of the `public/`
    +    directory, that's what we can use as tell-tale that an incorrect URL
    +    needs rewriting.
    +
         Signed-off-by: Johannes Schindelin <[email protected]>
     
      ## script/html-to-pdf.js ##
     @@ script/html-to-pdf.js: const htmlToPDF = async (htmlPath, options) => {
    -   const browser = await chromium.launch({ channel: 'chrome' })
        const page = await browser.newPage()
      
    --  await page.goto(
    --    `file://${path.isAbsolute(htmlPath) ? htmlPath : path.join(process.cwd(), htmlPath)}`, {
    --      waitUntil: 'load'
    --    }
    --  )
    -+  const htmlPathURL = `file://${path.isAbsolute(htmlPath) ? htmlPath : path.join(process.cwd(), htmlPath)}`
    -+    .replace(/\\/g, '/')
    -+  await page.goto(htmlPathURL, /* { waitUntil: 'load' } */)
    +   const htmlPathURL = url.pathToFileURL(htmlPath).toString()
    +-
    +   console.log(`Processing ${htmlPathURL}...`)
     +
     +  // Work around HUGO_RELATIVEURLS=false by rewriting the absolute URLs
    -+  const replacements = await page.evaluate((baseURLPrefix) => {
    -+    const replacements = []
    -+    const links = document.querySelectorAll("link[href^='/']")
    -+    ;[...links].forEach((e) => {
    -+      e.removeAttribute('integrity')
    -+      const replacement = e.href.replace(/^file:\/\/\/([A-Za-z]:\/)?(git-scm\.com\/)?/, baseURLPrefix)
    -+      replacements.push([e.href, replacement])
    -+      e.setAttribute('href', replacement)
    -+    })
    -+    const images = document.querySelectorAll("img[src^='/']")
    -+    ;[...images].forEach((e) => {
    -+      e.removeAttribute('integrity')
    -+      const replacement = e.src.replace(/^file:\/\/\/([A-Za-z]:\/)?(git-scm\.com\/)?/, baseURLPrefix)
    -+      replacements.push([e.src, replacement])
    -+      e.setAttribute('src', replacement)
    -+    })
    -+    return replacements
    -+  }, htmlPathURL.substring(0, htmlPathURL.lastIndexOf('/public/') + 8))
    -+  if (replacements.length)
    -+    console.log(`Rewrote ${replacements.length} absolute URLs:\n${replacements.map(r => `  ${r[0]} -> ${r[1]}`).join('\n')}`)
    ++  const baseURLPrefix = htmlPathURL.substring(0, htmlPathURL.lastIndexOf('/public/') + 8)
    ++  await page.route(/^file:\/\//, async (route, req) => {
    ++    // This _will_ be a correct URL when deployed to https://whatevers/, but
    ++    // this script runs before deployment, on a file:/// URL, where we need to
    ++    // be a bit clever to give the browser the file it needs.
    ++    const original = req.url()
    ++    const url =
    ++      original.startsWith(baseURLPrefix)
    ++        ? original
    ++        : original.replace(/^file:\/\/\/([A-Za-z]:\/)?(git-scm\.com\/)?/, baseURLPrefix)
    ++    console.error(`::notice::Rewrote ${original} to ${url}`)
    ++    await route.continue({ url })
    ++  })
     +
    +   await page.goto(htmlPathURL, { waitUntil: 'load' })
    + 
        await page.pdf({
    -     path: outputPath,
    -     format: 'A4',
  • -: --------- > 8: 63d3ce9 html-to-pdf: work around minified application.{css,js}

  • 8: b11a609 = 9: 21ad6a0 deploy/ci: render the cheat sheet as PDF and offer a link to it

@dscho
Copy link
Member

dscho commented Sep 7, 2025

Hey @jvns I did it again, I only added distraction from the actual topic in this PR. To make amends for that, let me propose the following plan:

  1. Let's merge Add support for Graphviz diagrams #2052 (unless there are more suggestions how to improve it),
  2. Then let's rebase this her PR on top of that and merge it. Every good Cheat Sheet will be a constant work in progress, it won't ever be done. We might just as well start iterating "in production"?
  3. Then I'll rebase the printable-cheat-sheet topic branch and open a PR, and we can see how much work still needs to be done there.
  4. Iterate!

How does that sound?

@jvns jvns mentioned this pull request Sep 8, 2025
4 tasks
@jvns
Copy link
Collaborator Author

jvns commented Sep 9, 2025

@dscho I think the print version of the cheat sheet looks nice (love --print-out!). I'm going to try to make it denser so that it fits on 2 pages. Hopefully that should be possible because the original fit on 1 page.

dscho added a commit that referenced this pull request Sep 13, 2025
## Changes

- This adds support for Graphviz diagrams and replaces the `.png`s in
https://git-scm.com/about/distributed with scalable versions.

## Context

Over in #2049, we discussed various options how to add elegant diagrams,
and settled on Graphviz ones. I broke this out so that I don't have to
derail the discussion about the cheat sheet anymore.

The solution presented here uses [`viz.js`](https://viz-js.com/), a
WebAssembly version of Graphviz. Since it weighs a bit much (1.4MB),
this is used client-side only when developing the page locally; When the
site gets deployed, the SVGs are "pre-rendered", i.e. generated and
inserted in place of the `<pre class="graphviz">` blocks.

To demonstrate this new feature, I replaced the diagrams in
https://git-scm.com/about/distributed with Graphviz ones:

| before | after |
| - | - |
| ![workflow-a as
PNG](https://github.com/git/git-scm.com/blob/802cbb4d3b71960972e9a09a78bab439eb303c29/static/images/about/[email protected]?raw=true)
|
![workflow-A](https://github.com/user-attachments/assets/c6f28d06-b64d-46f7-b220-dcb241e818f0)
|
| ![workflow-b as
PNG](https://github.com/git/git-scm.com/blob/802cbb4d3b71960972e9a09a78bab439eb303c29/static/images/about/[email protected]?raw=true)
|
![workflow-B](https://github.com/user-attachments/assets/6de3510d-818a-4118-96cf-043ba3bf3ab8)
|
| ![workflow-c as
PNG](https://github.com/git/git-scm.com/blob/802cbb4d3b71960972e9a09a78bab439eb303c29/static/images/about/[email protected]?raw=true)
|
![workflow-C](https://github.com/user-attachments/assets/89fc9699-6374-497f-bef6-aea715f0bb4f)
|

The changes of this PR can also be seen in action in my fork:
https://dscho.github.io/git-scm.com/about/distributed
@jvns
Copy link
Collaborator Author

jvns commented Sep 13, 2025

Just rebased on the graphviz changes! How about we can merge this before the printable cheat sheet work is done and then discuss that in a separate PR?

(edit: rebased & force pushed the wrong thing by accident but should be fixed now)

@jvns jvns force-pushed the cheat-sheet branch 2 times, most recently from bcff0e8 to c368fd5 Compare September 13, 2025 12:31
@jvns
Copy link
Collaborator Author

jvns commented Sep 13, 2025

@dscho One thing I just realized I don't understand -- in development I still see <svg> elements, is that right? Or should I be seeing <img> elements? Not sure if I'm doing something wrong. I think I need to make some tweaks to the CSS now that it's an <img>.

@dscho
Copy link
Member

dscho commented Sep 13, 2025

Even in development, it should now be <img> (but with URL-encoded SVG, not base64 encoded, that's for production).

@jvns
Copy link
Collaborator Author

jvns commented Sep 13, 2025

Got it, made the CSS tweaks.

@To1ne To1ne self-requested a review September 15, 2025 09:00
@To1ne
Copy link
Collaborator

To1ne commented Sep 15, 2025

@jvns It seems it's no longer deployed to gh-pages on your fork. But that's no problem, I did run it locally. For me these changes are ready to merge.

Just rebased on the graphviz changes! How about we can merge this before the printable cheat sheet work is done and then discuss that in a separate PR?

Yes. Makes sense to me.

One nice addition would be to have clickable headlines, or a permalink icon next to it (like on the man pages). But that's not a change I consider blocking before this PR can be merged.

@dscho Shall we merge then?

Copy link
Collaborator

@To1ne To1ne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! I think current version is mergable!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants