{"thread":{"id":"60533","subject":"Migration of git-scm.com to a static web site: ready for review/testing","startedAt":"2023-11-17T13:25:48Z","lastAt":"2024-09-14T18:42:05Z","messageCount":15,"participants":["Johannes Schindelin","Todd Zullinger","Johannes Sixt","Kaartic Sivaraam","Toon Claes"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"484992","messageId":"6f7d20b4-a725-0ef9-f6d3-ff2810da9e7a@gmx.de","threadId":"60533","inReplyTo":null,"subject":"Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2023-11-17T13:25:43Z","receivedAt":"2023-11-17T13:25:48Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nthe idea of migrating https://git-scm.com/ from a Rails app to a static\nsite has been discussed several times on this list in the past.\n\nThanks to the heroic, multi-year efforts of Matt Burke, Victoria Dye and\nMatthias Aßhauer, there is now a Pull Request, ready for review:\nhttps://github.com/git/git-scm.com/pull/1804\n\nThis Pull Request is not for the faint of heart, mainly because of the\nsheer amount of generated pages that are committed to the repository (such\nas the book, the manual pages, etc, a design decision necessary to run\nthis as a static website).\n\nThese pages are generated by GitHub workflows that are intended to run on\na schedule, and the scripts that generate them are part of the Pull\nRequest. For that reason, I do not consider it necessary to review those\ngenerated pages, those reviews have been done in the upstream sources from\nwhich the pages were generated.\n\nAt this point, the patches are fairly robust and I am mainly hoping for\nhelp with verifying that the static site works as intended, that existing\nlinks will continue to work with the new site (essentially, find obscure\nreferences to the existing website, then insert `git.github.io/` in the\nURL and verify that it works as intended).\n\nTo that end, I deployed this branch to GitHub Pages so that anyone\ninterested (hopefully many!) can have a look at\nhttps://git.github.io/git-scm.com/ and compare to the existing\nhttps://git-scm.com/.\n\nThank you,\nJohannes\n"},{"id":"484993","messageId":"ZVeUQEG5jIzKbvmT@pobox.com","threadId":"60533","inReplyTo":"6f7d20b4-a725-0ef9-f6d3-ff2810da9e7a@gmx.de","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Todd Zullinger","fromEmail":"tmz@pobox.com","sentAt":"2023-11-17T16:26:40Z","receivedAt":"2023-11-17T16:26:44Z","isPatch":false,"sender":{"key":"tmz@pobox.com","avatar":"https://avatars.githubusercontent.com/u/806319?v=4"},"body":"Hello,\n\nJohannes Schindelin wrote:\n> At this point, the patches are fairly robust and I am mainly hoping for\n> help with verifying that the static site works as intended, that existing\n> links will continue to work with the new site (essentially, find obscure\n> references to the existing website, then insert `git.github.io/` in the\n> URL and verify that it works as intended).\n> \n> To that end, I deployed this branch to GitHub Pages so that anyone\n> interested (hopefully many!) can have a look at\n> https://git.github.io/git-scm.com/ and compare to the existing\n> https://git-scm.com/.\n\nThis is nice.  Thanks to all for working on it!\n\nFor checking links, a tool like linkcheker[1] is very handy.\nThis is run against the local docs in the Fedora package\nbuilds to catch broken links.\n\nI ran it against the test site and it turned up _a lot_ of\nbroken links.  It's enough that saving and sharing the\noutput is probably more work than having someone familiar\nwith the migration give it a run directly.\n\nI ran `linkchecker https://git.github.io/git-scm.com/` and\nthe eventual result was:\n\n  That's it. 13459 links in 14126 URLs checked. 0 warnings found. 6763 errors found.\n  Stopped checking at 2023-11-17 11:11:17-004 (1 hour, 19 minutes)\n\nThe default output reports failures in a format like this:\n\n  URL        `ch00/ch10-git-internals'\n  Name       `Git Internals'\n  Parent URL https://git.github.io/git-scm.com/book/tr/v2/Ek-b%C3%B6l%C3%BCm-C:-Git-Commands-Plumbing-Commands/, line 106, col 1318\n  Real URL   https://git.github.io/git-scm.com/book/tr/v2/Ek-b%C3%B6l%C3%BCm-C:-Git-Commands-Plumbing-Commands/ch00/ch10-git-internals\n  Check time 3.303 seconds\n  Size       1KB\n  Result     Error: 404 Not Found\n\nLinkChecker can be run in a mode which directs the failures\nto a file.  That would be more like:\n\n  linkchecker -F text/utf_8//tmp/git-scm-check.txt https://git.github.io/git-scm.com/\n\nThe format of the -F option is TYPE[/ENCODING][/FILENAME]\nwhere TYPE can be text, html, sql, csv, gml, dot, xml,\nsitemap, none or failures.  The failures type is much more\nterse:\n\n  1 \"('https://git.github.io/git-scm.com/book/en/v2/Appendix-C:-Git-Commands-Plumbing-Commands/', 'https://git.github.io/git-scm.com/book/en/v2/Appendix-C:-Git-Commands-Plumbing-Commands/ch00/ch10-git-internals')\"\n\nI found the text type much more helpful in quickly spot\nchecking some of the failures since it includes the text\nstring used for the link.\n\nRunning it against a local directory of the content would be\na lot faster, if that's an option.  It's also worth bumping\nthe default number of threads from 10 to increase the speed\na bit.\n\n[1] https://linkchecker.github.io/linkchecker/\n\n-- \nTodd\n"},{"id":"484999","messageId":"4dd9b45d-b352-d8ba-3314-96ab48f7abf7@gmx.de","threadId":"60533","inReplyTo":"ZVeUQEG5jIzKbvmT@pobox.com","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2023-11-18T01:14:37Z","receivedAt":"2023-11-18T01:14:47Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Todd,\n\nOn Fri, 17 Nov 2023, Todd Zullinger wrote:\n\n> Johannes Schindelin wrote:\n> > At this point, the patches are fairly robust and I am mainly hoping for\n> > help with verifying that the static site works as intended, that existing\n> > links will continue to work with the new site (essentially, find obscure\n> > references to the existing website, then insert `git.github.io/` in the\n> > URL and verify that it works as intended).\n> >\n> > To that end, I deployed this branch to GitHub Pages so that anyone\n> > interested (hopefully many!) can have a look at\n> > https://git.github.io/git-scm.com/ and compare to the existing\n> > https://git-scm.com/.\n>\n> This is nice.  Thanks to all for working on it!\n\n😊\n\n> For checking links, a tool like linkcheker[1] is very handy.\n> This is run against the local docs in the Fedora package\n> builds to catch broken links.\n\nHmm, `linkchecker` is really slow for me, even locally.\n\n> I ran it against the test site and it turned up _a lot_ of\n> broken links.  [...]\n>\n>   URL        `ch00/ch10-git-internals'\n>   Name       `Git Internals'\n>   Parent URL https://git.github.io/git-scm.com/book/tr/v2/Ek-b%C3%B6l%C3%BCm-C:-Git-Commands-Plumbing-Commands/, line 106, col 1318\n>   Real URL   https://git.github.io/git-scm.com/book/tr/v2/Ek-b%C3%B6l%C3%BCm-C:-Git-Commands-Plumbing-Commands/ch00/ch10-git-internals\n>   Check time 3.303 seconds\n>   Size       1KB\n>   Result     Error: 404 Not Found\n\nGood catch. I totally forgot to take care of the cross-references!\n\nThis is now fixed, as of\nhttps://github.com/dscho/git-scm.com/commit/e599a57b2fadf8cb01e57af23fcb929b32e94bcb\n\nI kicked off the GitHub workflow to re-generate the books, and the updated\nGitHub Pages look fine (see e.g. the parent URL mentioned above and follow\nthe \"Pull Request Refs\" link).\n\n> Running it against a local directory of the content would be\n> a lot faster, if that's an option.  It's also worth bumping\n> the default number of threads from 10 to increase the speed\n> a bit.\n>\n> [1] https://linkchecker.github.io/linkchecker/\n\nUnfortunately it is actually quite slow.\n\nGranted, the added cross-references now increase the number of hyperlinks\nto check, but after I let the program run for a bit over an hour to look\nat https://git-scm.com/ (for comparison), it is now running on the local\nbuild (i.e. the `public/` folder generated by Hugo, not even an HTTP\nserver) for over 45 minutes and still not done:\n\n-- snip --\n[...]\n10 threads active, 112977 links queued, 206443 links in 100001 URLs checked, runtime 48 minutes, 46 seconds\n10 threads active, 113455 links queued, 206689 links in 100001 URLs checked, runtime 48 minutes, 52 seconds\n10 threads active, 113829 links queued, 206874 links in 100001 URLs checked, runtime 48 minutes, 57 seconds\n10 threads active, 114230 links queued, 207136 links in 100001 URLs checked, runtime 49 minutes, 3 seconds\n10 threads active, 114731 links queued, 207498 links in 100001 URLs checked, runtime 49 minutes, 9 seconds\n-- snap --\n\nMaybe something is going utterly wrong because the number of links seems\nto be dramatically larger than what the https://git-scm.com/ reported;\nMaybe linkchecker broke out of the `public/` directory and now indexes my\nentire harddrive ;-)\n\nCiao,\nJohannes\n"},{"id":"485000","messageId":"ZVgoKPAg6jKZk_M6@pobox.com","threadId":"60533","inReplyTo":"4dd9b45d-b352-d8ba-3314-96ab48f7abf7@gmx.de","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Todd Zullinger","fromEmail":"tmz@pobox.com","sentAt":"2023-11-18T02:57:44Z","receivedAt":"2023-11-18T02:57:54Z","isPatch":false,"sender":{"key":"tmz@pobox.com","avatar":"https://avatars.githubusercontent.com/u/806319?v=4"},"body":"Hi Johannes,\n\nJohannes Schindelin wrote:\n>> For checking links, a tool like linkcheker[1] is very handy.\n>> This is run against the local docs in the Fedora package\n>> builds to catch broken links.\n> \n> Hmm, `linkchecker` is really slow for me, even locally.\n\nYeah, it took an hour and a half to run for me, both on an\nold laptop and a fast server with plenty of threads,\nbandwidth, and memory.\n\nChecking the git HTML documentation takes under 30 seconds,\nwhich is largely the only place I've used it.  It has been\nvery helpful in catching broken links in the docs during the\nbuild and the time is short enough that I never minded.\n\n> Granted, the added cross-references now increase the number of hyperlinks\n> to check, but after I let the program run for a bit over an hour to look\n> at https://git-scm.com/ (for comparison), it is now running on the local\n> build (i.e. the `public/` folder generated by Hugo, not even an HTTP\n> server) for over 45 minutes and still not done:\n> \n> -- snip --\n> [...]\n> 10 threads active, 112977 links queued, 206443 links in 100001 URLs checked, runtime 48 minutes, 46 seconds\n> 10 threads active, 113455 links queued, 206689 links in 100001 URLs checked, runtime 48 minutes, 52 seconds\n> 10 threads active, 113829 links queued, 206874 links in 100001 URLs checked, runtime 48 minutes, 57 seconds\n> 10 threads active, 114230 links queued, 207136 links in 100001 URLs checked, runtime 49 minutes, 3 seconds\n> 10 threads active, 114731 links queued, 207498 links in 100001 URLs checked, runtime 49 minutes, 9 seconds\n> -- snap --\n\nI would have thought that bumping the number of threads a\nlot would really help, but I ran it on a dual Xeon system\nwith 40 threads and it took about the same time.  Perhaps I\nshould have increased to double or more the system processor\ncount.\n\n> Maybe something is going utterly wrong because the number\n> of links seems to be dramatically larger than what the\n> https://git-scm.com/ reported; Maybe linkchecker broke out\n> of the `public/` directory and now indexes my entire\n> harddrive ;-)\n\nHeh, hopefully not. :)\n\nI wondered if there were circular links that it was picking\nup and not de-duplicating.  I may try to run it with the\n--verbose option which logs all checked URLs.  Maybe that\nwill turn up something.  It sure seems like there's a _lot_\nof links here.\n\nThere is a --recursion-level option which might be helpful.\nThe --ignore-url and/or --no-follow-url may also be useful.\n\nThough even if it's (very) slow, it might be worth running\nto flush out some initial issues before making the site\nlive.  Letting it run in the background for a few hours is\nprobably less effort than fielding a number of big reports\nabout broken URL here and there. :)\n\nOf course, it would be even better if it were fast enough to\nrun as part of the site build process to catch broken links\nbefore each deployment, but that would need to be measured\nin some relatively small number of seconds instead of the\nhours it seems to take now. :/\n\n-- \nTodd\n"},{"id":"485001","messageId":"39f863b9-fc81-43ca-93f5-89e341e8615d@kdbg.org","threadId":"60533","inReplyTo":"6f7d20b4-a725-0ef9-f6d3-ff2810da9e7a@gmx.de","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2023-11-18T09:41:11Z","receivedAt":"2023-11-18T09:41:17Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 17.11.23 um 14:25 schrieb Johannes Schindelin:\n> Hi,\n> \n> the idea of migrating https://git-scm.com/ from a Rails app to a static\n> site has been discussed several times on this list in the past.\n> \n> Thanks to the heroic, multi-year efforts of Matt Burke, Victoria Dye and\n> Matthias Aßhauer, there is now a Pull Request, ready for review:\n> https://github.com/git/git-scm.com/pull/1804\n> \n> This Pull Request is not for the faint of heart, mainly because of the\n> sheer amount of generated pages that are committed to the repository (such\n> as the book, the manual pages, etc, a design decision necessary to run\n> this as a static website).\n> \n> These pages are generated by GitHub workflows that are intended to run on\n> a schedule, and the scripts that generate them are part of the Pull\n> Request. For that reason, I do not consider it necessary to review those\n> generated pages, those reviews have been done in the upstream sources from\n> which the pages were generated.\n> \n> At this point, the patches are fairly robust and I am mainly hoping for\n> help with verifying that the static site works as intended, that existing\n> links will continue to work with the new site (essentially, find obscure\n> references to the existing website, then insert `git.github.io/` in the\n> URL and verify that it works as intended).\n> \n> To that end, I deployed this branch to GitHub Pages so that anyone\n> interested (hopefully many!) can have a look at\n> https://git.github.io/git-scm.com/ and compare to the existing\n> https://git-scm.com/.\n\nWhen a transition to static pages happens, an important aspect is that\nexternal links that point into git-scm.com must not be invalidated.\nThere are many such links in Stackoverflow answers, for example.\n\nI checked one link:\n\nhttps://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt--r\nhttps://git.github.io/git-scm.com/docs/git-rebase#Documentation/git-rebase.txt--r\n\nand it is looking very good. Thank you! I assume that keeping links\nworking is not just a happy accident, but part of the plan.\n\n-- Hannes\n\n"},{"id":"485002","messageId":"70C3012B-95A2-411F-8DEF-B3B81E1F2DFD@gmx.de","threadId":"60533","inReplyTo":"39f863b9-fc81-43ca-93f5-89e341e8615d@kdbg.org","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2023-11-18T09:46:19Z","receivedAt":"2023-11-18T09:46:26Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Hannes,\n\nYes, keeping existing deep links working is very much a goal of this work.\n\nThank you,\nJohannes\n\n\n-------- Original Message --------\nFrom: Johannes Sixt <j6t@kdbg.org>\nSent: November 18, 2023 10:41:11 AM GMT+01:00\nTo: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nCc: git@vger.kernel.org, Matt Burke <spraints@gmail.com>, Victoria Dye <vdye@github.com>, \"Matthias Aßhauer\" <mha1993@live.de>\nSubject: Re: Migration of git-scm.com to a static web site: ready for review/testing\n\nAm 17.11.23 um 14:25 schrieb Johannes Schindelin:\n> Hi,\n> \n> the idea of migrating https://git-scm.com/ from a Rails app to a static\n> site has been discussed several times on this list in the past.\n> \n> Thanks to the heroic, multi-year efforts of Matt Burke, Victoria Dye and\n> Matthias Aßhauer, there is now a Pull Request, ready for review:\n> https://github.com/git/git-scm.com/pull/1804\n> \n> This Pull Request is not for the faint of heart, mainly because of the\n> sheer amount of generated pages that are committed to the repository (such\n> as the book, the manual pages, etc, a design decision necessary to run\n> this as a static website).\n> \n> These pages are generated by GitHub workflows that are intended to run on\n> a schedule, and the scripts that generate them are part of the Pull\n> Request. For that reason, I do not consider it necessary to review those\n> generated pages, those reviews have been done in the upstream sources from\n> which the pages were generated.\n> \n> At this point, the patches are fairly robust and I am mainly hoping for\n> help with verifying that the static site works as intended, that existing\n> links will continue to work with the new site (essentially, find obscure\n> references to the existing website, then insert `git.github.io/` in the\n> URL and verify that it works as intended).\n> \n> To that end, I deployed this branch to GitHub Pages so that anyone\n> interested (hopefully many!) can have a look at\n> https://git.github.io/git-scm.com/ and compare to the existing\n> https://git-scm.com/.\n\nWhen a transition to static pages happens, an important aspect is that\nexternal links that point into git-scm.com must not be invalidated.\nThere are many such links in Stackoverflow answers, for example.\n\nI checked one link:\n\nhttps://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt--r\nhttps://git.github.io/git-scm.com/docs/git-rebase#Documentation/git-rebase.txt--r\n\nand it is looking very good. Thank you! I assume that keeping links\nworking is not just a happy accident, but part of the plan.\n\n-- Hannes\n\n"},{"id":"485034","messageId":"1d83b8f8-b739-6277-ac4f-e7fe6ff2d2fe@gmx.de","threadId":"60533","inReplyTo":"ZVgoKPAg6jKZk_M6@pobox.com","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2023-11-21T14:25:18Z","receivedAt":"2023-11-21T14:25:26Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Todd,\n\nOn Fri, 17 Nov 2023, Todd Zullinger wrote:\n\n> Johannes Schindelin wrote:\n> >> For checking links, a tool like linkcheker[1] is very handy.\n> >> This is run against the local docs in the Fedora package\n> >> builds to catch broken links.\n> >\n> > Hmm, `linkchecker` is really slow for me, even locally.\n>\n> Yeah, it took an hour and a half to run for me, both on an\n> old laptop and a fast server with plenty of threads,\n> bandwidth, and memory.\n>\n> Checking the git HTML documentation takes under 30 seconds,\n> which is largely the only place I've used it.  It has been\n> very helpful in catching broken links in the docs during the\n> build and the time is short enough that I never minded.\n\nI found https://lychee.cli.rs/#/ in the meantime and figured out how to\nuse it in a local setup:\n\nFirst, I run:\n\n\tHUGO_TIMEOUT=777 HUGO_BASEURL= HUGO_UGLYURLS=false time hugo\n\nThe first `HUGO_*` setting is to make sure that even though I sometimes\nuse all of the cores of my laptop's CPU it should not fail. The other two\nare to override settings from `hugo.yml` so that `lychee` can handle the\noutput (`lychee` will not auto-append `.html`, unlike GitHub Pages, and\nwould therefore mis-detect tons of broken links, without\n`HUGO_UGLYURLS=false`).\n\nIn my setup, this command typically runs for something like half a minute,\nbut sometimes takes for as long as 1 minute. (I noticed that it is much\nslower when I open the directory in VS Code because I'm running this in\nWSL and the filesystem watcher kind of eats all resources.)\n\nAfter that, I run:\n\n\ttime lychee --offline --exclude-mail \\\n\t        --exclude file:///path/to/repo.git/ \\\n\t\t--exclude file:///caminho/para/o/reposit%C3%B3rio.git/ \\\n\t\t--exclude file:///ruta/a/repositorio.git/ \\\n\t\t--exclude file:///sl%C3%B3%C3%B0/til/hirsla.git/ \\\n\t\t--exclude file:///Pfad/zum/Repo.git/ \\\n\t\t--exclude file:///chemin/du/d%C3%A9p%C3%B4t.git/ \\\n\t\t--exclude file:///srv/git/project.git \\\n\t\t--exclude \"file://$PWD/public/pagefind/pagefind-ui.css\" \\\n\t\t--format markdown -o lychee-local.md public/\n\nWithout `--offline`, there would be a couple of broken links (the\nhttp://git.or.cz/gitwiki/InterfacesFrontendsAndTools link leads to\n\"Forbidden\", it needs to be changed to https://).\n\nThe `file:///` URLs are all examples that are not expected to be valid.\nAnd we do not want to check the emails (tons of `xyz@example.com` would be\n\"broken\").\n\nThis command typically takes another half minute, sometimes a bit longer.\n\nGiven those times and the configurability (and the lure of a GitHub\nAction that could be easily integrated into a GitHub workflow:\nhttps://github.com/marketplace/actions/lychee-broken-link-checker), I have\nup on linkchecker and focused exclusively on lychee.\n\nNow, when I started working on this on Friday, lychee reported about\n12,000 broken links.\n\nThere were a couple of legitimate mistakes I made (when feeding paths to\nHugo's `relURL` function, the path must not have a leading slash or it\nwill remain unchanged, for example). These are fixed.\n\nBut there were also many other issues such as some manual page translation\nbeing incomplete yet linking to not-yet-existing pages. In those cases, I\nchanged he code to generate redirects to the English version. For example,\nhttps://git.github.io/git-scm.com/docs/git-clone/fr#_git has a link to\n`git[1]` that _should_ lead to the French version of the `git` manual\npage. However, that does not exist. So both the Rails App as well as the\nstatic website redirect to the English variant of that page.\n\nMy most recent lychee run results in 0 broken links.\n\nAs a bonus, some of the links that are currently broken on\nhttps://git-scm.com/ are fixed in https://git.github.io/git-scm.com/.\nFor example, following the `Pull Request Referləri` link at the top of\nhttps://git-scm.com/book/az/v2/Appendix-C:-Git-%C6%8Fmrl%C9%99ri-Plumbing-%C6%8Fmrl%C9%99ri/\nleads to a 404. But following it in\nhttps://git.github.io/git-scm.com/book/az/v2/Appendix-C:-Git-%C6%8Fmrl%C9%99ri-Plumbing-%C6%8Fmrl%C9%99ri/\ndirects the browser to the correct URL:\nhttps://git.github.io/git-scm.com/book/az/v2/GitHub-Bir-Layih%C9%99nin-Saxlan%C4%B1lmas%C4%B1/#_pr_refs\n\nAnother thing that is broken on https://git-scm.com/ are the footnotes in\nthe Czech translation of the ProGit book. These were broken in the Hugo\nversion, too, but now they are fixed. See e.g.\nhttps://dscho.github.io/git-scm.com/book/cs/v2/Z%C3%A1klady-pr%C3%A1ce-se-syst%C3%A9mem-Git-Zobrazen%C3%AD-historie-reviz%C3%AD/#_footnotedef_7\nand note that the Rails App redirects to\nhttps://git-scm.com/book/cs/v2/Z%C3%A1klady-pr%C3%A1ce-se-syst%C3%A9mem-Git-Zobrazen%C3%AD-historie-reviz%C3%AD/ch00/_footnotedef_7\nwhen clicking on the `[7]`, which 404s.\n\nCould you double-check that the links in the current version?\n\nThank you,\nJohannes\n"},{"id":"485073","messageId":"807be06c-e70c-4ee4-8851-bb8d82b68ac2@gmail.com","threadId":"60533","inReplyTo":"6f7d20b4-a725-0ef9-f6d3-ff2810da9e7a@gmx.de","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Kaartic Sivaraam","fromEmail":"kaartic.sivaraam@gmail.com","sentAt":"2023-11-23T18:53:40Z","receivedAt":"2023-11-23T18:53:44Z","isPatch":false,"sender":{"key":"kaartic.sivaraam@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12448084?v=4"},"body":"Hi Johannes,\n\nOn 17/11/23 18:55, Johannes Schindelin wrote:\n> \n> To that end, I deployed this branch to GitHub Pages so that anyone\n> interested (hopefully many!) can have a look at\n> https://git.github.io/git-scm.com/ and compare to the existing\n> https://git-scm.com/.\n> \n\nThanks for hosting it to easily check things!\n\nI gave a quick try at the search and it seems to be behaving a bit \nstrangely.\n\nFor instance, I searched for 'commit' and 'log'. I was hoping to see the \ncorresponding reference page show up in the results but they don't seem \nto show up. At least they don't show up in the first few results. They \nshow up in the first few results in the existing site.\n\nHere are some screenshots:\n\nExisting site: https://ibb.co/pZHx9TM\n\nNew site:\nhttps://ibb.co/dMpNth3\nhttps://ibb.co/h26J5Rx\n\nThis not always the case, though. Some terms like 'checkout' seem to \nbring the relevant results properly in the top results.\n\n-- \nSivaraam\n"},{"id":"485185","messageId":"ZWVIRXodL9pQZbtj@pobox.com","threadId":"60533","inReplyTo":"1d83b8f8-b739-6277-ac4f-e7fe6ff2d2fe@gmx.de","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Todd Zullinger","fromEmail":"tmz@pobox.com","sentAt":"2023-11-28T01:54:13Z","receivedAt":"2023-11-28T01:54:22Z","isPatch":false,"sender":{"key":"tmz@pobox.com","avatar":"https://avatars.githubusercontent.com/u/806319?v=4"},"body":"Hi Johannes,\n\nJohannes Schindelin wrote:\n> I found https://lychee.cli.rs/#/ in the meantime and figured out how to\n> use it in a local setup:\n\nNice.  That's much faster.\n\n> My most recent lychee run results in 0 broken links.\n> \n> As a bonus, some of the links that are currently broken on\n> https://git-scm.com/ are fixed in https://git.github.io/git-scm.com/.\n> For example, following the `Pull Request Referləri` link at the top of\n> https://git-scm.com/book/az/v2/Appendix-C:-Git-%C6%8Fmrl%C9%99ri-Plumbing-%C6%8Fmrl%C9%99ri/\n> leads to a 404. But following it in\n> https://git.github.io/git-scm.com/book/az/v2/Appendix-C:-Git-%C6%8Fmrl%C9%99ri-Plumbing-%C6%8Fmrl%C9%99ri/\n> directs the browser to the correct URL:\n> https://git.github.io/git-scm.com/book/az/v2/GitHub-Bir-Layih%C9%99nin-Saxlan%C4%B1lmas%C4%B1/#_pr_refs\n> \n> Another thing that is broken on https://git-scm.com/ are the footnotes in\n> the Czech translation of the ProGit book. These were broken in the Hugo\n> version, too, but now they are fixed. See e.g.\n> https://dscho.github.io/git-scm.com/book/cs/v2/Z%C3%A1klady-pr%C3%A1ce-se-syst%C3%A9mem-Git-Zobrazen%C3%AD-historie-reviz%C3%AD/#_footnotedef_7\n> and note that the Rails App redirects to\n> https://git-scm.com/book/cs/v2/Z%C3%A1klady-pr%C3%A1ce-se-syst%C3%A9mem-Git-Zobrazen%C3%AD-historie-reviz%C3%AD/ch00/_footnotedef_7\n> when clicking on the `[7]`, which 404s.\n> \n> Could you double-check that the links in the current version?\n\nSince I had it already, I ran linkchecker again.  It found\n25 errors.  I'll attach the output, though I'm not sure if\nthe list will pass it along or not.\n\nIt looks like a number of errors are due to '?' characters\nin the generated links, e.g.:\n\nhttps://git.github.io/git-scm.com/book/en/v2/Getting-Started-What-is-Git?/\n\nCheers,\n\n-- \nTodd\n\n\nLinkChecker 10.2.1\nCopyright (C) 2000-2016 Bastian Kleineidam, 2010-2022 LinkChecker Authors\nLinkChecker comes with ABSOLUTELY NO WARRANTY!\nThis is free software, and you are welcome to redistribute it under\ncertain conditions. Look at the file `LICENSE' within this distribution.\nRead the documentation at https://linkchecker.github.io/linkchecker/\nWrite comments and bugs to https://github.com/linkchecker/linkchecker/issues\n\nStart checking at 2023-11-25 18:49:01+000\n\nURL        `/git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F'\nName       `What is Git?'\nParent URL https://git.github.io/git-scm.com/book/en/v2, line 9, col 1\nReal URL   https://git.github.io/git-scm.com/book/en/v2/Getting-Started-What-is-Git?/\nCheck time 2.388 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/en/v2/Getting-Started-What-is-Git?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/az/v2/Ba%c5%9flan%c4%9f%c4%b1c-Git-N%c9%99dir%3F'\nName       `Git Nədir?'\nParent URL https://git.github.io/git-scm.com/book/az/v2, line 8, col 1\nReal URL   https://git.github.io/git-scm.com/book/az/v2/Ba%C5%9Flan%C4%9F%C4%B1c-Git-N%C9%99dir?/\nCheck time 0.074 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/az/v2/Ba%C5%9Flan%C4%9F%C4%B1c-Git-N%C9%99dir?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/de/v2/Erste-Schritte-Was-ist-Versionsverwaltung%3F'\nName       `Erste Schritte'\nParent URL https://git.github.io/git-scm.com/book/de/v2, line 5, col 3159\nReal URL   https://git.github.io/git-scm.com/book/de/v2/Erste-Schritte-Was-ist-Versionsverwaltung?/\nCheck time 0.121 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/de/v2/Erste-Schritte-Was-ist-Versionsverwaltung?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/de/v2/Erste-Schritte-Was-ist-Git%3F'\nName       `Was ist Git?'\nParent URL https://git.github.io/git-scm.com/book/de/v2, line 8, col 1\nReal URL   https://git.github.io/git-scm.com/book/de/v2/Erste-Schritte-Was-ist-Git?/\nCheck time 0.068 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/de/v2/Erste-Schritte-Was-ist-Git?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/es/v2/Inicio---Sobre-el-Control-de-Versiones-%c2%bfC%c3%b3mo-obtener-ayuda%3F'\nName       `¿Cómo obtener ayuda?'\nParent URL https://git.github.io/git-scm.com/book/es/v2, line 12, col 1\nReal URL   https://git.github.io/git-scm.com/book/es/v2/Inicio---Sobre-el-Control-de-Versiones-%C2%BFC%C3%B3mo-obtener-ayuda?/\nCheck time 0.093 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/es/v2/Inicio---Sobre-el-Control-de-Versiones-%C2%BFC%C3%B3mo-obtener-ayuda?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/es/v2/Ramificaciones-en-Git-%c2%bfQu%c3%a9-es-una-rama%3F'\nName       `Ramificaciones en Git'\nParent URL https://git.github.io/git-scm.com/book/es/v2, line 21, col 111\nReal URL   https://git.github.io/git-scm.com/book/es/v2/Ramificaciones-en-Git-%C2%BFQu%C3%A9-es-una-rama?/\nCheck time 0.094 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/es/v2/Ramificaciones-en-Git-%C2%BFQu%C3%A9-es-una-rama?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/ko/v2/%ec%8b%9c%ec%9e%91%ed%95%98%ea%b8%b0-%eb%b2%84%ec%a0%84-%ea%b4%80%eb%a6%ac%eb%9e%80%3F'\nName       `시작하기'\nParent URL https://git.github.io/git-scm.com/book/ko/v2, line 5, col 3159\nReal URL   https://git.github.io/git-scm.com/book/ko/v2/%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC%EB%9E%80?/\nCheck time 0.094 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/ko/v2/%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC%EB%9E%80?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/nl/v2/Aan-de-slag-Wat-is-Git%3F'\nName       `Wat is Git?'\nParent URL https://git.github.io/git-scm.com/book/nl/v2, line 8, col 1\nReal URL   https://git.github.io/git-scm.com/book/nl/v2/Aan-de-slag-Wat-is-Git?/\nCheck time 0.070 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/nl/v2/Aan-de-slag-Wat-is-Git?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/ru/v2/%d0%92%d0%b2%d0%b5%d0%b4%d0%b5%d0%bd%d0%b8%d0%b5-%d0%a7%d1%82%d0%be-%d1%82%d0%b0%d0%ba%d0%be%d0%b5-Git%3F'\nName       `Что такое Git?'\nParent URL https://git.github.io/git-scm.com/book/ru/v2, line 8, col 1\nReal URL   https://git.github.io/git-scm.com/book/ru/v2/%D0%92%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5-%D0%A7%D1%82%D0%BE-%D1%82%D0%B0%D0%BA%D0%BE%D0%B5-Git?/\nCheck time 0.075 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/ru/v2/%D0%92%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5-%D0%A7%D1%82%D0%BE-%D1%82%D0%B0%D0%BA%D0%BE%D0%B5-Git?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/sl/v2/Za%c4%8detek-Kaj-je-Git%3F'\nName       `Kaj je Git?'\nParent URL https://git.github.io/git-scm.com/book/sl/v2, line 8, col 1\nReal URL   https://git.github.io/git-scm.com/book/sl/v2/Za%C4%8Detek-Kaj-je-Git?/\nCheck time 0.071 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/sl/v2/Za%C4%8Detek-Kaj-je-Git?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/ru/v2/%d0%92%d0%b2%d0%b5%d0%b4%d0%b5%d0%bd%d0%b8%d0%b5-%d0%9a%d0%b0%d0%ba-%d0%bf%d0%be%d0%bb%d1%83%d1%87%d0%b8%d1%82%d1%8c-%d0%bf%d0%be%d0%bc%d0%be%d1%89%d1%8c%3F'\nName       `Как получить помощь?'\nParent URL https://git.github.io/git-scm.com/book/ru/v2, line 12, col 1\nReal URL   https://git.github.io/git-scm.com/book/ru/v2/%D0%92%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5-%D0%9A%D0%B0%D0%BA-%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B8%D1%82%D1%8C-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C?/\nCheck time 0.077 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/ru/v2/%D0%92%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5-%D0%9A%D0%B0%D0%BA-%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B8%D1%82%D1%8C-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/sr/v2/%d0%9f%d0%be%d1%87%d0%b5%d1%82%d0%b0%d0%ba-%d0%a8%d1%82%d0%b0-%d1%98%d0%b5-%d0%93%d0%b8%d1%82%3F'\nName       `Шта је Гит?'\nParent URL https://git.github.io/git-scm.com/book/sr/v2, line 8, col 1\nReal URL   https://git.github.io/git-scm.com/book/sr/v2/%D0%9F%D0%BE%D1%87%D0%B5%D1%82%D0%B0%D0%BA-%D0%A8%D1%82%D0%B0-%D1%98%D0%B5-%D0%93%D0%B8%D1%82?/\nCheck time 0.084 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/sr/v2/%D0%9F%D0%BE%D1%87%D0%B5%D1%82%D0%B0%D0%BA-%D0%A8%D1%82%D0%B0-%D1%98%D0%B5-%D0%93%D0%B8%D1%82?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/uz/v2/%d0%98%d1%88-%d0%b1%d0%be%d1%88%d0%bb%d0%b0%d0%bd%d0%b8%d1%88%d0%b8-%d2%9a%d0%b0%d0%bd%d0%b4%d0%b0%d0%b9-%d1%91%d1%80%d0%b4%d0%b0%d0%bc-%d0%be%d0%bb%d0%b8%d1%88-%d0%bc%d1%83%d0%bc%d0%ba%d0%b8%d0%bd%3F'\nName       `Қандай ёрдам олиш мумкин?'\nParent URL https://git.github.io/git-scm.com/book/uz/v2, line 12, col 1\nReal URL   https://git.github.io/git-scm.com/book/uz/v2/%D0%98%D1%88-%D0%B1%D0%BE%D1%88%D0%BB%D0%B0%D0%BD%D0%B8%D1%88%D0%B8-%D2%9A%D0%B0%D0%BD%D0%B4%D0%B0%D0%B9-%D1%91%D1%80%D0%B4%D0%B0%D0%BC-%D0%BE%D0%BB%D0%B8%D1%88-%D0%BC%D1%83%D0%BC%D0%BA%D0%B8%D0%BD?/\nCheck time 0.074 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/uz/v2/%D0%98%D1%88-%D0%B1%D0%BE%D1%88%D0%BB%D0%B0%D0%BD%D0%B8%D1%88%D0%B8-%D2%9A%D0%B0%D0%BD%D0%B4%D0%B0%D0%B9-%D1%91%D1%80%D0%B4%D0%B0%D0%BC-%D0%BE%D0%BB%D0%B8%D1%88-%D0%BC%D1%83%D0%BC%D0%BA%D0%B8%D0%BD?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/be/v2/%d0%9f%d0%b5%d1%80%d1%88%d1%8b%d1%8f-%d0%ba%d1%80%d0%be%d0%ba%d1%96-What-is-Git%3F'\nName       `What is Git?'\nParent URL https://git.github.io/git-scm.com/book/be/v2, line 8, col 1\nReal URL   https://git.github.io/git-scm.com/book/be/v2/%D0%9F%D0%B5%D1%80%D1%88%D1%8B%D1%8F-%D0%BA%D1%80%D0%BE%D0%BA%D1%96-What-is-Git?/\nCheck time 0.105 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/be/v2/%D0%9F%D0%B5%D1%80%D1%88%D1%8B%D1%8F-%D0%BA%D1%80%D0%BE%D0%BA%D1%96-What-is-Git?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/it/v2/Per-Iniziare-Cos%e2%80%99%c3%a9-Git%3F'\nName       `Cos’é Git?'\nParent URL https://git.github.io/git-scm.com/book/it/v2, line 8, col 1\nReal URL   https://git.github.io/git-scm.com/book/it/v2/Per-Iniziare-Cos%E2%80%99%C3%A9-Git?/\nCheck time 0.068 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/it/v2/Per-Iniziare-Cos%E2%80%99%C3%A9-Git?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/ms/v2/Getting-Started-What-is-Git%3F'\nName       `What is Git?'\nParent URL https://git.github.io/git-scm.com/book/ms/v2, line 8, col 1\nReal URL   https://git.github.io/git-scm.com/book/ms/v2/Getting-Started-What-is-Git?/\nCheck time 0.078 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/ms/v2/Getting-Started-What-is-Git?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/sv/v2/Kom-ig%c3%a5ng-Vad-%c3%a4r-Git%3F'\nName       `Vad är Git?'\nParent URL https://git.github.io/git-scm.com/book/sv/v2, line 8, col 1\nReal URL   https://git.github.io/git-scm.com/book/sv/v2/Kom-ig%C3%A5ng-Vad-%C3%A4r-Git?/\nCheck time 0.086 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/sv/v2/Kom-ig%C3%A5ng-Vad-%C3%A4r-Git?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/tr/v2/Ba%c5%9flang%c4%b1%c3%a7-Git-Nedir%3F'\nName       `Git Nedir?'\nParent URL https://git.github.io/git-scm.com/book/tr/v2, line 8, col 1\nReal URL   https://git.github.io/git-scm.com/book/tr/v2/Ba%C5%9Flang%C4%B1%C3%A7-Git-Nedir?/\nCheck time 0.076 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/book/tr/v2/Ba%C5%9Flang%C4%B1%C3%A7-Git-Nedir?/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/book/fr/v2/Les-branches-avec-Git-Branches-et-fusions'\nName       `next'\nParent URL https://git.github.io/git-scm.com/book/fr/v2/Les-branches-avec-Git-Les-branches-en-bref, line 160, col 1436\nReal URL   https://git.github.io/git-scm.com/book/fr/v2/Les-branches-avec-Git-Branches-et-fusions\nCheck time 1.887 seconds\nSize       1KB\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/docs/git-submodules'\nParent URL https://git.github.io/git-scm.com/docs/git-submodules/fr, line 2, col 1\nReal URL   https://git.github.io/git-scm.com/docs/git-submodules/\nCheck time 5.581 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/docs/git-submodules/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/docs/git-maintainance'\nParent URL https://git.github.io/git-scm.com/docs/git-maintainance/is, line 2, col 1\nReal URL   https://git.github.io/git-scm.com/docs/git-maintainance/\nCheck time 5.867 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/docs/git-maintainance/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/docs/gitignorar'\nParent URL https://git.github.io/git-scm.com/docs/gitignorar/pt_BR, line 2, col 1\nReal URL   https://git.github.io/git-scm.com/docs/gitignorar/\nCheck time 5.546 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/docs/gitignorar/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/docs/git-fsmonitor----daemon'\nParent URL https://git.github.io/git-scm.com/docs/git-fsmonitor----daemon/pt_BR, line 2, col 1\nReal URL   https://git.github.io/git-scm.com/docs/git-fsmonitor----daemon/\nCheck time 5.616 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/docs/git-fsmonitor----daemon/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/docs/git-pack'\nParent URL https://git.github.io/git-scm.com/docs/git-pack/pt_BR, line 2, col 1\nReal URL   https://git.github.io/git-scm.com/docs/git-pack/\nCheck time 4.311 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/docs/git-pack/'.\nResult     Error: 404 Not Found\n\nURL        `/git-scm.com/docs/git-hash'\nParent URL https://git.github.io/git-scm.com/docs/git-hash/fr, line 2, col 1\nReal URL   https://git.github.io/git-scm.com/docs/git-hash/\nCheck time 1.156 seconds\nSize       1KB\nInfo       Redirected to\n           `https://git.github.io/git-scm.com/docs/git-hash/'.\nResult     Error: 404 Not Found\n\nStatistics:\nDownloaded: 302.2MB.\nContent types: 5656 image, 11838 text, 0 video, 0 audio, 15 application, 29 mail and 620 other.\nURL lengths: min=15, max=841, avg=72.\n\nThat's it. 18158 links in 19951 URLs checked. 0 warnings found. 25 errors found.\nStopped checking at 2023-11-25 19:34:52+000 (45 minutes, 51 seconds)\n"},{"id":"502646","messageId":"c36ce70c-59ad-c53e-8ef6-cce779e428eb@gmx.de","threadId":"60533","inReplyTo":"807be06c-e70c-4ee4-8851-bb8d82b68ac2@gmail.com","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2024-09-11T22:18:42Z","receivedAt":"2024-09-11T22:18:46Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Kaartic,\n\nOn Fri, 24 Nov 2023, Kaartic Sivaraam wrote:\n\n> On 17/11/23 18:55, Johannes Schindelin wrote:\n> >\n> > To that end, I deployed this branch to GitHub Pages so that anyone\n> > interested (hopefully many!) can have a look at\n> > https://git.github.io/git-scm.com/ and compare to the existing\n> > https://git-scm.com/.\n> >\n>\n> Thanks for hosting it to easily check things!\n>\n> I gave a quick try at the search and it seems to be behaving a bit strangely.\n>\n> For instance, I searched for 'commit' and 'log'. I was hoping to see the\n> corresponding reference page show up in the results but they don't seem to\n> show up. At least they don't show up in the first few results. They show up in\n> the first few results in the existing site.\n>\n> Here are some screenshots:\n>\n> Existing site: https://ibb.co/pZHx9TM\n>\n> New site:\n> https://ibb.co/dMpNth3\n> https://ibb.co/h26J5Rx\n>\n> This not always the case, though. Some terms like 'checkout' seem to bring the\n> relevant results properly in the top results.\n\nThis was quite the adventure to figure out. I worked with the maintainer\nof Pagefind in the meantime, to add better controls regarding how much\nimpact various dimensions should have on the search results. And I am\nhappy to report that the fruit of these labors is now available to all, in\nthe form of Pagefind v1.1.1.\n\nI have integrated this into https://github.com/git/git-scm.com/pull/1804.\n\nCiao,\nJohannes\n"},{"id":"502647","messageId":"db0aeda7-f266-8b1a-517f-c0bea4d4e02b@gmx.de","threadId":"60533","inReplyTo":"ZWVIRXodL9pQZbtj@pobox.com","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2024-09-11T22:18:47Z","receivedAt":"2024-09-11T22:18:53Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Todd,\n\nOn Mon, 27 Nov 2023, Todd Zullinger wrote:\n\n> Johannes Schindelin wrote:\n> > I found https://lychee.cli.rs/#/ in the meantime and figured out how to\n> > use it in a local setup:\n>\n> Nice.  That's much faster.\n>\n> > My most recent lychee run results in 0 broken links.\n> >\n> > As a bonus, some of the links that are currently broken on\n> > https://git-scm.com/ are fixed in https://git.github.io/git-scm.com/.\n> > For example, following the `Pull Request Referləri` link at the top of\n> > https://git-scm.com/book/az/v2/Appendix-C:-Git-%C6%8Fmrl%C9%99ri-Plumbing-%C6%8Fmrl%C9%99ri/\n> > leads to a 404. But following it in\n> > https://git.github.io/git-scm.com/book/az/v2/Appendix-C:-Git-%C6%8Fmrl%C9%99ri-Plumbing-%C6%8Fmrl%C9%99ri/\n> > directs the browser to the correct URL:\n> > https://git.github.io/git-scm.com/book/az/v2/GitHub-Bir-Layih%C9%99nin-Saxlan%C4%B1lmas%C4%B1/#_pr_refs\n> >\n> > Another thing that is broken on https://git-scm.com/ are the footnotes in\n> > the Czech translation of the ProGit book. These were broken in the Hugo\n> > version, too, but now they are fixed. See e.g.\n> > https://dscho.github.io/git-scm.com/book/cs/v2/Z%C3%A1klady-pr%C3%A1ce-se-syst%C3%A9mem-Git-Zobrazen%C3%AD-historie-reviz%C3%AD/#_footnotedef_7\n> > and note that the Rails App redirects to\n> > https://git-scm.com/book/cs/v2/Z%C3%A1klady-pr%C3%A1ce-se-syst%C3%A9mem-Git-Zobrazen%C3%AD-historie-reviz%C3%AD/ch00/_footnotedef_7\n> > when clicking on the `[7]`, which 404s.\n> >\n> > Could you double-check that the links in the current version?\n>\n> Since I had it already, I ran linkchecker again.  It found\n> 25 errors.  I'll attach the output, though I'm not sure if\n> the list will pass it along or not.\n>\n> It looks like a number of errors are due to '?' characters\n> in the generated links, e.g.:\n>\n> https://git.github.io/git-scm.com/book/en/v2/Getting-Started-What-is-Git?/\n\n_That_ was quite a quest to investigate and to fix. The problem here is,\nof course, the difference in how question marks are handled in file:// vs\nhttps:// URLs: in the latter, they separate the path part from the query.\nTherefore, they need to be URI-encoded in https:// URLs.\n\nI verified manually (and with a Playwright-based test that is now run as\npart of every deployment and of every PR build) that these URLs with\nquestion marks in them work.\n\nAs to using `lychee` to verify the links, I went down quite a rabbit hole\nto allow using it on the very same files that are being deployed, with\nrelative URLs and \"ugly\" ones at that, too (i.e. with the `.html` suffix\nthat you do not need to specify when accessing `.html` files on GitHub\nPages). This has not yet been released in an official version, therefore I\nneed to use the `nightly` version for the time being.\n\nThe good news is that the link checking is integrated into the deployment\nand PR builds in https://github.com/git/git-scm.com/pull/1804. That will\nallow us to catch broken links faster. One caveat, though: for robustness\n(and also for speed), only internal links are checked. Links pointing to\nother websites are not validated, but it is easy to do that manually by\nrunning Lychee without the `--offline` flag.\n\nCiao,\nJohannes\n"},{"id":"502648","messageId":"155bc141-a16a-9fd2-920b-7a8c398d5e36@gmx.de","threadId":"60533","inReplyTo":"6f7d20b4-a725-0ef9-f6d3-ff2810da9e7a@gmx.de","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2024-09-11T22:18:53Z","receivedAt":"2024-09-11T22:18:59Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Me again,\n\nOn Fri, 17 Nov 2023, Johannes Schindelin wrote:\n\n> the idea of migrating https://git-scm.com/ from a Rails app to a static\n> site has been discussed several times on this list in the past.\n>\n> Thanks to the heroic, multi-year efforts of Matt Burke, Victoria Dye and\n> Matthias Aßhauer, there is now a Pull Request, ready for review:\n> https://github.com/git/git-scm.com/pull/1804\n>\n> This Pull Request is not for the faint of heart, mainly because of the\n> sheer amount of generated pages that are committed to the repository (such\n> as the book, the manual pages, etc, a design decision necessary to run\n> this as a static website).\n>\n> These pages are generated by GitHub workflows that are intended to run on\n> a schedule, and the scripts that generate them are part of the Pull\n> Request. For that reason, I do not consider it necessary to review those\n> generated pages, those reviews have been done in the upstream sources from\n> which the pages were generated.\n>\n> At this point, the patches are fairly robust and I am mainly hoping for\n> help with verifying that the static site works as intended, that existing\n> links will continue to work with the new site (essentially, find obscure\n> references to the existing website, then insert `git.github.io/` in the\n> URL and verify that it works as intended).\n>\n> To that end, I deployed this branch to GitHub Pages so that anyone\n> interested (hopefully many!) can have a look at\n> https://git.github.io/git-scm.com/ and compare to the existing\n> https://git-scm.com/.\n\nIt's time for the next update, after working with Pagefind, lychee and\nPlaywright project members to improve on this effort. Here is a focused\nlist of changes since November 17th 2023 (when I sent the first RFC), in\ndescending, quite subjective order of importance:\n\n  - A couple of patches were upstreamed via\n    https://github.com/git/git-scm.com/pull/1855\n\n  - All other upstream changes were incorporated by rebasing the branch\n    (keeping the merge commits as structuring elements). A couple of\n    times. Okay, I won't lie, I must have rebased them about ten dozen\n    times, even if I \"only\" force-pushed 22 times since my first update on\n    this mailing list.\n\n  - A rather huge change is that the pre-rendered HTML files are no longer\n    stored directly in `content/`, but instead in `external/` (using Hugo\n    mounts), and each and every pre-rendered file is marked clearly as\n    such, to avoid accidental PRs that want to change those files.\n\n  - Another important change is that the link checker Lychee is now used\n    in CI builds to verify that there are no broken internal links (it\n    does not try to follow external links, that would be too fragile and\n    too expensive).\n\n  - Yet another important change is that there are now UI tests (using\n    Playwright) that verify e.g. that the current book section is marked\n    as active in the Chapters drop-down.\n\n  - The Playwright tests also pass successfully on the current Rails-backed\n    git-scm.com, with a few bugs of that Rails app clearly documented in\n    the tests.\n\n  - The `README.md` is now updated in logical steps, reflecting how the\n    commit history builds up the Hugo site, then adds Pagefind, etc.\n\n  - Upgraded to the newest Hugo and Pagefind versions.\n\n  - I've re-done the way git-scm.com/docs works, it is no longer a\n    complex `_index.html` file, and uses a much more powerful partial\n    template now instead of awkward shortcodes.\n\n  - The pages' sections are now determined more carefully (and correctly).\n\n  - Since my last update, a couple of GUIs were added and others modified,\n    therefore I scripted the migration from the single\n    `resources/guis.yml` file to the separate `data/guis/*.yml` files, and\n    documented it in the commit message.\n\n  - The code using Pagefind was fixed (there were missing `await`s as well\n    as racy search result order).\n\n  - Translated manual pages are now searchable (when on a page in the\n    corresponding language).\n\n  - The `.html` file extension was dropped from the search results' URLs.\n\n  - Book sections containing question marks are now handled correctly,\n    with a fall-back when those question marks are incorrectly specified\n    in the URL (which is then interpreted as separator of the path from\n    the query part of the URL).\n\n  - Missing files in the ProGit book or its translations no longer fail\n    the deployment (and yes, there was one update that resulted in such\n    missing files, and the speed with which this was fixed provided the\n    motivation for this change of behavior).\n\n  - The Hugo and Pagefind versions are no longer hard-coded in the\n    workflows, but instead parsed out from `hugo.yml` (which is now the\n    only location where these versions are specified).\n\n  - Some Pagefind changes that accidentally slipped into earlier commits\n    have been moved into their correct spots in the commit history.\n\n  - The CI builds now verify that the search works as intended, by\n    verifying that searching for a couple of subcommand names will find\n    the respective manual pages as top result.\n\n  - Commits that were created by automated GitHub workflows report in\n    their commit message which workflow signs responsible for the update.\n\n  - Obviously, the automation updated Git version, download URLs, manual\n    pages and book (including translations) many times, as designed.\n\n  - Workflows that can be triggered manually now really respect the\n    `force-rebuild` flag.\n\n  - The GitHub workflows were updated to use current GitHub Actions'\n    versions (e.g. `actions/checkout@v4`).\n\nI will try to reply to this mail with a range-diff that is slightly edited\nto leave out the changes made by GitHub workflows (i.e. book and manual\npages). It is still over 100kB, therefore it might well bounce. Wish me\nluck.\n\nCiao,\nJohannes\n"},{"id":"502649","messageId":"19f553dc-a778-3ba7-b037-0a08da7e9388@gmx.de","threadId":"60533","inReplyTo":"155bc141-a16a-9fd2-920b-7a8c398d5e36@gmx.de","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2024-09-11T22:20:20Z","receivedAt":"2024-09-11T22:20:29Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Once again, me,\n\nOn Thu, 12 Sep 2024, Johannes Schindelin wrote:\n\n> I will try to reply to this mail with a range-diff that is slightly edited\n> to leave out the changes made by GitHub workflows (i.e. book and manual\n> pages).\n\nHere goes:\n\n  1:  3420701f6 <   -:  --------- Remove a bogus end tag\n  2:  861766083 <   -:  --------- docs: fix a bogus <a/>\n  3:  5af3129c1 <   -:  --------- Remove obsolete banner\n  4:  82173f4f1 <   -:  --------- Revert \"book2.rake: drop progit2-ms translation\"\n  5:  32795076e =   1:  987e733c3 Drop the Travis CI configuration\n  6:  0c6f2b28b =   2:  247a0615a Add a Hugo-aware .gitignore file\n  7:  5c522f814 !   3:  593aa9af8 Adjust the Gemfile in preparation for generating a static site\n    @@ Gemfile\n\n      source \"https://rubygems.org\"\n     -ruby \"3.1.3\"\n    -\n    --gem \"rails\", \"~> 6.0\"\n    +-\n    +-gem \"rails\", \"~> 6.1\"\n     -\n     -# hacks for rails6 + ruby 3.1\n     -gem 'net-imap', require: false\n    @@ Gemfile\n     -gem 'net-smtp', require: false\n     -\n     -gem \"asciidoctor\", \"~> 2.0.0\"\n    --gem \"elasticsearch\", \"2.0.2\"\n    +-gem \"elasticsearch\", \"7.13\"\n     -gem \"iso8601\"\n     -gem \"octokit\"\n     -gem \"puma\"\n  8:  46233d1bb !   4:  5cddbb9a3 Add a Hugo configuration\n    @@ Commit message\n      ## hugo.yml (new) ##\n     @@\n     +---\n    -+baseURL: https://git-scm.com/\n     +languageCode: en\n     +title: Git\n     +disablePathToLower: true\n    @@ hugo.yml (new)\n     +    renderer:\n     +      unsafe: true\n     +params:\n    -+  latest_version: 2.43.0\n    -+  latest_relnote_url: https://raw.github.com/git/git/master/Documentation/RelNotes/2.43.0.txt\n    -+  latest_release_date: '2023-11-20'\n    ++  latest_version: 2.46.0\n    ++  latest_relnote_url: https://raw.github.com/git/git/master/Documentation/RelNotes/2.46.0.txt\n    ++  latest_release_date: '2024-07-29'\n  9:  ed4e92165 !   5:  8de703d8c Drop the Ruby app\n    @@ Gemfile.lock (deleted)\n     -GEM\n     -  remote: https://rubygems.org/\n     -  specs:\n    --    actioncable (6.1.7.3)\n    --      actionpack (= 6.1.7.3)\n    --      activesupport (= 6.1.7.3)\n    +-    actioncable (6.1.7.7)\n    +-      actionpack (= 6.1.7.7)\n    +-      activesupport (= 6.1.7.7)\n     -      nio4r (~> 2.0)\n     -      websocket-driver (>= 0.6.1)\n    --    actionmailbox (6.1.7.3)\n    --      actionpack (= 6.1.7.3)\n    --      activejob (= 6.1.7.3)\n    --      activerecord (= 6.1.7.3)\n    --      activestorage (= 6.1.7.3)\n    --      activesupport (= 6.1.7.3)\n    +-    actionmailbox (6.1.7.7)\n    +-      actionpack (= 6.1.7.7)\n    +-      activejob (= 6.1.7.7)\n    +-      activerecord (= 6.1.7.7)\n    +-      activestorage (= 6.1.7.7)\n    +-      activesupport (= 6.1.7.7)\n     -      mail (>= 2.7.1)\n    --    actionmailer (6.1.7.3)\n    --      actionpack (= 6.1.7.3)\n    --      actionview (= 6.1.7.3)\n    --      activejob (= 6.1.7.3)\n    --      activesupport (= 6.1.7.3)\n    +-    actionmailer (6.1.7.7)\n    +-      actionpack (= 6.1.7.7)\n    +-      actionview (= 6.1.7.7)\n    +-      activejob (= 6.1.7.7)\n    +-      activesupport (= 6.1.7.7)\n     -      mail (~> 2.5, >= 2.5.4)\n     -      rails-dom-testing (~> 2.0)\n    --    actionpack (6.1.7.3)\n    --      actionview (= 6.1.7.3)\n    --      activesupport (= 6.1.7.3)\n    +-    actionpack (6.1.7.7)\n    +-      actionview (= 6.1.7.7)\n    +-      activesupport (= 6.1.7.7)\n     -      rack (~> 2.0, >= 2.0.9)\n     -      rack-test (>= 0.6.3)\n     -      rails-dom-testing (~> 2.0)\n     -      rails-html-sanitizer (~> 1.0, >= 1.2.0)\n    --    actiontext (6.1.7.3)\n    --      actionpack (= 6.1.7.3)\n    --      activerecord (= 6.1.7.3)\n    --      activestorage (= 6.1.7.3)\n    --      activesupport (= 6.1.7.3)\n    +-    actiontext (6.1.7.7)\n    +-      actionpack (= 6.1.7.7)\n    +-      activerecord (= 6.1.7.7)\n    +-      activestorage (= 6.1.7.7)\n    +-      activesupport (= 6.1.7.7)\n     -      nokogiri (>= 1.8.5)\n    --    actionview (6.1.7.3)\n    --      activesupport (= 6.1.7.3)\n    +-    actionview (6.1.7.7)\n    +-      activesupport (= 6.1.7.7)\n     -      builder (~> 3.1)\n     -      erubi (~> 1.4)\n     -      rails-dom-testing (~> 2.0)\n     -      rails-html-sanitizer (~> 1.1, >= 1.2.0)\n    --    activejob (6.1.7.3)\n    --      activesupport (= 6.1.7.3)\n    +-    activejob (6.1.7.7)\n    +-      activesupport (= 6.1.7.7)\n     -      globalid (>= 0.3.6)\n    --    activemodel (6.1.7.3)\n    --      activesupport (= 6.1.7.3)\n    --    activerecord (6.1.7.3)\n    --      activemodel (= 6.1.7.3)\n    --      activesupport (= 6.1.7.3)\n    --    activestorage (6.1.7.3)\n    --      actionpack (= 6.1.7.3)\n    --      activejob (= 6.1.7.3)\n    --      activerecord (= 6.1.7.3)\n    --      activesupport (= 6.1.7.3)\n    +-    activemodel (6.1.7.7)\n    +-      activesupport (= 6.1.7.7)\n    +-    activerecord (6.1.7.7)\n    +-      activemodel (= 6.1.7.7)\n    +-      activesupport (= 6.1.7.7)\n    +-    activestorage (6.1.7.7)\n    +-      actionpack (= 6.1.7.7)\n    +-      activejob (= 6.1.7.7)\n    +-      activerecord (= 6.1.7.7)\n    +-      activesupport (= 6.1.7.7)\n     -      marcel (~> 1.0)\n     -      mini_mime (>= 1.1.0)\n    --    activesupport (6.1.7.3)\n    +-    activesupport (6.1.7.7)\n     -      concurrent-ruby (~> 1.0, >= 1.0.2)\n     -      i18n (>= 1.6, < 2)\n     -      minitest (>= 5.1)\n    @@ Gemfile.lock (deleted)\n     -    builder (3.2.4)\n     -    byebug (11.1.3)\n     -    coderay (1.1.3)\n    --    concurrent-ruby (1.2.2)\n    +-    concurrent-ruby (1.2.3)\n     -    crack (0.4.5)\n     -      rexml\n     -    crass (1.0.6)\n    @@ Gemfile.lock (deleted)\n     -    dotenv-rails (2.7.6)\n     -      dotenv (= 2.7.6)\n     -      railties (>= 3.2)\n    --    elasticsearch (2.0.2)\n    --      elasticsearch-api (= 2.0.2)\n    --      elasticsearch-transport (= 2.0.2)\n    --    elasticsearch-api (2.0.2)\n    +-    elasticsearch (7.13.0)\n    +-      elasticsearch-api (= 7.13.0)\n    +-      elasticsearch-transport (= 7.13.0)\n    +-    elasticsearch-api (7.13.0)\n     -      multi_json\n    --    elasticsearch-transport (2.0.2)\n    --      faraday\n    +-    elasticsearch-transport (7.13.0)\n    +-      faraday (~> 1)\n     -      multi_json\n     -    erubi (1.12.0)\n     -    execjs (2.8.1)\n    @@ Gemfile.lock (deleted)\n     -    factory_bot_rails (6.2.0)\n     -      factory_bot (~> 6.2.0)\n     -      railties (>= 5.0.0)\n    --    faraday (1.8.0)\n    +-    faraday (1.10.3)\n     -      faraday-em_http (~> 1.0)\n     -      faraday-em_synchrony (~> 1.0)\n     -      faraday-excon (~> 1.1)\n    --      faraday-httpclient (~> 1.0.1)\n    +-      faraday-httpclient (~> 1.0)\n    +-      faraday-multipart (~> 1.0)\n     -      faraday-net_http (~> 1.0)\n    --      faraday-net_http_persistent (~> 1.1)\n    +-      faraday-net_http_persistent (~> 1.0)\n     -      faraday-patron (~> 1.0)\n     -      faraday-rack (~> 1.0)\n    --      multipart-post (>= 1.2, < 3)\n    +-      faraday-retry (~> 1.0)\n     -      ruby2_keywords (>= 0.0.4)\n     -    faraday-em_http (1.0.0)\n     -    faraday-em_synchrony (1.0.0)\n     -    faraday-excon (1.1.0)\n     -    faraday-httpclient (1.0.1)\n    --    faraday-net_http (1.0.1)\n    +-    faraday-multipart (1.0.4)\n    +-      multipart-post (~> 2)\n    +-    faraday-net_http (1.0.2)\n     -    faraday-net_http_persistent (1.2.0)\n     -    faraday-patron (1.0.0)\n     -    faraday-rack (1.0.0)\n    +-    faraday-retry (1.0.3)\n     -    ffi (1.15.4)\n     -    foreman (0.87.2)\n    --    globalid (1.1.0)\n    --      activesupport (>= 5.0)\n    +-    globalid (1.2.1)\n    +-      activesupport (>= 6.1)\n     -    hashdiff (1.0.1)\n    --    i18n (1.12.0)\n    +-    i18n (1.14.4)\n     -      concurrent-ruby (~> 1.0)\n     -    iso8601 (0.13.0)\n     -    json (2.6.2)\n     -    listen (3.7.0)\n     -      rb-fsevent (~> 0.10, >= 0.10.3)\n     -      rb-inotify (~> 0.9, >= 0.9.10)\n    --    loofah (2.19.1)\n    +-    loofah (2.22.0)\n     -      crass (~> 1.0.2)\n    --      nokogiri (>= 1.5.9)\n    +-      nokogiri (>= 1.12.0)\n     -    mail (2.8.1)\n     -      mini_mime (>= 0.1.1)\n     -      net-imap\n     -      net-pop\n     -      net-smtp\n    --    marcel (1.0.2)\n    +-    marcel (1.0.4)\n     -    method_source (1.0.0)\n    --    mini_mime (1.1.2)\n    --    mini_portile2 (2.8.1)\n    --    minitest (5.18.0)\n    +-    mini_mime (1.1.5)\n    +-    mini_portile2 (2.8.6)\n    +-    minitest (5.22.3)\n     -    multi_json (1.15.0)\n    --    multipart-post (2.1.1)\n    +-    multipart-post (2.4.1)\n     -    net-imap (0.3.1)\n     -      net-protocol\n     -    net-pop (0.1.2)\n     -      net-protocol\n    --    net-protocol (0.1.3)\n    +-    net-protocol (0.2.2)\n     -      timeout\n     -    net-smtp (0.3.2)\n     -      net-protocol\n    --    nio4r (2.5.9)\n    --    nokogiri (1.14.3)\n    --      mini_portile2 (~> 2.8.0)\n    +-    nio4r (2.7.3)\n    +-    nokogiri (1.16.5)\n    +-      mini_portile2 (~> 2.8.2)\n     -      racc (~> 1.4)\n     -    octokit (4.21.0)\n     -      faraday (>= 0.9)\n    @@ Gemfile.lock (deleted)\n     -      byebug (~> 11.0)\n     -      pry (~> 0.13.0)\n     -    public_suffix (4.0.6)\n    --    puma (5.6.7)\n    +-    puma (5.6.8)\n     -      nio4r (~> 2.0)\n    --    racc (1.6.2)\n    --    rack (2.2.6.4)\n    +-    racc (1.7.3)\n    +-    rack (2.2.8.1)\n     -    rack-test (2.1.0)\n     -      rack (>= 1.3)\n     -    rack-timeout (0.6.0)\n    --    rails (6.1.7.3)\n    --      actioncable (= 6.1.7.3)\n    --      actionmailbox (= 6.1.7.3)\n    --      actionmailer (= 6.1.7.3)\n    --      actionpack (= 6.1.7.3)\n    --      actiontext (= 6.1.7.3)\n    --      actionview (= 6.1.7.3)\n    --      activejob (= 6.1.7.3)\n    --      activemodel (= 6.1.7.3)\n    --      activerecord (= 6.1.7.3)\n    --      activestorage (= 6.1.7.3)\n    --      activesupport (= 6.1.7.3)\n    +-    rails (6.1.7.7)\n    +-      actioncable (= 6.1.7.7)\n    +-      actionmailbox (= 6.1.7.7)\n    +-      actionmailer (= 6.1.7.7)\n    +-      actionpack (= 6.1.7.7)\n    +-      actiontext (= 6.1.7.7)\n    +-      actionview (= 6.1.7.7)\n    +-      activejob (= 6.1.7.7)\n    +-      activemodel (= 6.1.7.7)\n    +-      activerecord (= 6.1.7.7)\n    +-      activestorage (= 6.1.7.7)\n    +-      activesupport (= 6.1.7.7)\n     -      bundler (>= 1.15.0)\n    --      railties (= 6.1.7.3)\n    +-      railties (= 6.1.7.7)\n     -      sprockets-rails (>= 2.0.0)\n     -    rails-controller-testing (1.0.5)\n     -      actionpack (>= 5.0.1.rc1)\n     -      actionview (>= 5.0.1.rc1)\n     -      activesupport (>= 5.0.1.rc1)\n    --    rails-dom-testing (2.0.3)\n    --      activesupport (>= 4.2.0)\n    +-    rails-dom-testing (2.2.0)\n    +-      activesupport (>= 5.0.0)\n    +-      minitest\n     -      nokogiri (>= 1.6)\n    --    rails-html-sanitizer (1.5.0)\n    --      loofah (~> 2.19, >= 2.19.1)\n    +-    rails-html-sanitizer (1.6.0)\n    +-      loofah (~> 2.21)\n    +-      nokogiri (~> 1.14)\n     -    rails_12factor (0.0.3)\n     -      rails_serve_static_assets\n     -      rails_stdout_logging\n     -    rails_serve_static_assets (0.0.5)\n     -    rails_stdout_logging (0.0.5)\n    --    railties (6.1.7.3)\n    --      actionpack (= 6.1.7.3)\n    --      activesupport (= 6.1.7.3)\n    +-    railties (6.1.7.7)\n    +-      actionpack (= 6.1.7.7)\n    +-      activesupport (= 6.1.7.7)\n     -      method_source\n     -      rake (>= 12.2)\n     -      thor (~> 1.0)\n     -    rainbow (3.0.0)\n    --    rake (13.0.6)\n    +-    rake (13.1.0)\n     -    rb-fsevent (0.11.0)\n     -    rb-inotify (0.10.1)\n     -      ffi (~> 1.0)\n    @@ Gemfile.lock (deleted)\n     -    redis-store (1.9.0)\n     -      redis (>= 4, < 5)\n     -    regexp_parser (2.1.1)\n    --    rexml (3.2.5)\n    +-    rexml (3.2.8)\n    +-      strscan (>= 3.0.9)\n     -    rspec-core (3.10.1)\n     -      rspec-support (~> 3.10.0)\n     -    rspec-expectations (3.10.1)\n    @@ Gemfile.lock (deleted)\n     -    shoulda-context (2.0.0)\n     -    shoulda-matchers (4.5.1)\n     -      activesupport (>= 4.2.0)\n    --    sprockets (4.2.0)\n    +-    sprockets (4.2.1)\n     -      concurrent-ruby (~> 1.0)\n     -      rack (>= 2.2.4, < 4)\n     -    sprockets-rails (3.4.2)\n    @@ Gemfile.lock (deleted)\n     -      activesupport (>= 5.2)\n     -      sprockets (>= 3.0.0)\n     -    sqlite3 (1.4.2)\n    --    thor (1.2.1)\n    +-    strscan (3.1.0)\n    +-    thor (1.3.1)\n     -    tilt (2.0.10)\n    --    timeout (0.3.0)\n    +-    timeout (0.4.1)\n     -    tzinfo (2.0.6)\n     -      concurrent-ruby (~> 1.0)\n     -    uglifier (4.2.0)\n    @@ Gemfile.lock (deleted)\n     -      addressable (>= 2.8.0)\n     -      crack (>= 0.3.2)\n     -      hashdiff (>= 0.4.0, < 2.0.0)\n    --    websocket-driver (0.7.5)\n    +-    websocket-driver (0.7.6)\n     -      websocket-extensions (>= 0.1.0)\n     -    websocket-extensions (0.1.5)\n    --    zeitwerk (2.6.7)\n    +-    zeitwerk (2.6.13)\n     -\n     -PLATFORMS\n     -  ruby\n    @@ Gemfile.lock (deleted)\n     -  database_cleaner\n     -  diffy\n     -  dotenv-rails\n    --  elasticsearch (= 2.0.2)\n    +-  elasticsearch (= 7.13)\n     -  fabrication\n     -  factory_bot_rails\n     -  foreman\n    @@ Gemfile.lock (deleted)\n     -  pry-byebug\n     -  puma\n     -  rack-timeout\n    --  rails (~> 6.0)\n    +-  rails (~> 6.1)\n     -  rails-controller-testing\n     -  rails_12factor\n     -  redis-rails\n    @@ Rakefile (deleted)\n      ## app.json (deleted) ##\n     @@\n     -{\n    +-  \"stack\": \"heroku-24\",\n     -  \"scripts\": {\n     -  },\n     -  \"env\": {\n    @@ lib/searchable.rb (deleted)\n     -        \"query\" => {\n     -          \"bool\" => {\n     -            \"should\" => [],\n    --            \"minimum_number_should_match\" => 1\n    +-            \"minimum_should_match\" => 1\n     -          },\n     -        },\n     -        \"highlight\" => {\n 10:  7865f2051 =   6:  24844c72e In preparation for using Hugo, move the \"blog has moved\" page\n 11:  4effd55ad =   7:  5830b2c27 Hugo-ify the \"blog has moved\" page\n 12:  6a09add78 =   8:  34580a126 Move the favicon to the `static/` directory\n 13:  a79b9c844 =   9:  9f96eea1f Drop the robots.txt file\n 14:  32a4eb168 =  10:  47cbca3c8 Hugo'ify the 404 page\n 15:  906bb0235 =  11:  7a1f5bdac Drop the custom pages for HTML codes 422 and 500\n 16:  bff32d315 !  12:  340558bd1 ci: switch to building with Hugo\n    @@ .github/workflows/ci.yml\n      name: CI\n     -on: pull_request\n     +on: [pull_request]\n    -+\n    -+env:\n    -+  HUGO_VERSION: 0.120.3\n\n      jobs:\n        build:\n          runs-on: ubuntu-latest\n          steps:\n     -    - uses: actions/checkout@v2\n    -+    - uses: actions/checkout@v3\n    ++    - uses: actions/checkout@v4\n\n     -    - name: ruby setup\n     -      uses: ruby/setup-ruby@v1\n     -      with:\n     -        bundler-cache: true\n    ++    - name: configure Hugo version\n    ++      run: |\n    ++        set -x &&\n    ++        echo \"HUGO_VERSION=$(sed -n 's/^ *hugo_version: *//p' <hugo.yml)\" >>$GITHUB_ENV\n    ++\n     +    - name: install Hugo ${{ env.HUGO_VERSION }}\n     +      run: |\n     +        set -x &&\n    @@ .github/workflows/ci.yml\n\n     -    - name: rubocop\n     -      run: bundle exec rubocop -P\n    -+    - name: run Pagefind to build the search index\n    -+      run: npx -y pagefind --site public\n    ++    - name: build tar archive\n    ++      run: cd public && tar czvf ../pages.tar.gz *\n\n     -    - name: rspec\n     -      run: bundle exec rspec\n    -+    - name: build tar archive\n    -+      run: cd public && tar czvf ../pages.tar.gz *\n    -+\n     +    - name: Upload build artifact\n    -+      uses: actions/upload-artifact@v3\n    ++      uses: actions/upload-artifact@v4\n     +      with:\n     +        name: pages\n     +        path: pages.tar.gz\n    +\n    + ## hugo.yml ##\n    +@@ hugo.yml: markup:\n    +     renderer:\n    +       unsafe: true\n    + params:\n    ++  hugo_version: 0.134.1\n    +   latest_version: 2.46.0\n    +   latest_relnote_url: https://raw.github.com/git/git/master/Documentation/RelNotes/2.46.0.txt\n    +   latest_release_date: '2024-07-29'\n 17:  f685a732d !  13:  2b1735089 README: reflect that this is now a Hugo site\n    @@ README.md\n     +## Local development setup\n\n     -You'll need a Ruby environment to run Rails.  First do:\n    -+You'll need the extended version of [Hugo](https://gohugo.io/). On Windows, we recommend using the Windows Subsystem for Linux (WSL). You can serve the site locally via\n    ++It is highly recommended to clone this repository using [`scalar`](https://git-scm.com/docs/scalar); This allows to work only on the parts of the repository relevant to your interests. You can select which directories are checked out using the [`git sparse-checkout add <directory>...`](https://git-scm.com/docs/git-sparse-checkout) command. The relevant directories are:\n\n     -    $ rvm use .\n     -    $ bundle install\n    -+    $ hugo serve -w\n    ++- If you want to test any page rendering using Hugo:\n    ++  - layouts/\n    ++  - content/\n    ++  - static/\n    ++  - assets/\n\n     -Then you need to create the database structure:\n    -+The site should be running on http://127.0.0.1:1313. Note that it may be advisable to do this in a sparse checkout that excludes large parts of `content/`, to speed up the rendering time.\n    ++- To add new GUIs:\n    ++  - data/\n\n     -    $ rake db:migrate\n    -+## Update manual pages\n    ++- To work on pre-rendering pages that originate from other repositories (such as the ProGit book):\n    ++  - script/\n\n     -Alternatively you can run the script at `script/bootstrap` which will set up Ruby dependencies and the local SQLite database.\n    --\n    ++- To work on the GitHub workflows that perform the automated, scheduled pre-rendering:\n    ++  - .github/\n    +\n     -Now you'll want to populate the man pages.  You can do so from a local Git\n     -source clone like this:\n    ++- The pre-rendered pages (ProGit book, its translated versions, the manual pages, their translated versions):\n    ++  - external/book/\n    ++  - external/docs/\n    ++  You will want to avoid editing these directly, as they contain pages that are pre-rendered via GitHub workflows, sourcing content from other repositories.\n    ++\n    ++To render the site locally, you'll need the extended version of [Hugo](https://gohugo.io/). On Windows, we recommend using the Windows Subsystem for Linux (WSL). You can serve the site locally via\n    ++\n    ++    $ hugo serve -w\n    ++\n    ++The site should be running on http://127.0.0.1:1313. Note that it may be advisable to do this in a sparse checkout that excludes large parts of `content/`, to speed up the rendering time.\n    ++\n    ++## Update manual pages\n    ++\n     +(TODO!)\n     +You can do so using a local Git source clone like this:\n\n    @@ README.md: Alternatively, you can get the book content from a repository on your\n     -\n      ## Contributing\n\n    - If you wish to contribute to this website, please [fork it on GitHub](https://github.com/git/git-scm.com), push your\n    -@@ README.md: be accepted. If it involves code, please also write tests for it.\n    +-If you wish to contribute to this website, please [fork it on GitHub](https://github.com/git/git-scm.com), push your\n    +-change to a named branch, then send a pull request. If it is a big feature,\n    +-you might want to [start an issue](https://github.com/git/git-scm.com/issues/new) first to make sure it's something that will\n    +-be accepted. If it involves code, please also write tests for it.\n    +-\n    +-## Adding new GUI\n    ++If you wish to contribute to this website, please [fork it on GitHub](https://github.com/git/git-scm.com).\n\n    - The [list of GUI clients](https://git-scm.com/downloads/guis) has been constructed by the community for a long time. If you want to add another tool you'll need to follow a few steps:\n    +-The [list of GUI clients](https://git-scm.com/downloads/guis) has been constructed by the community for a long time. If you want to add another tool you'll need to follow a few steps:\n    ++Then, clone it using [`scalar`](https://git-scm.com/docs/scalar) (this avoids long clone times) and then use [`git sparse-checkout add <directory>`](https://git-scm.com/docs/git-sparse-checkout) to check out the files relevant to your work.\n\n     -1. Add the GUI client details at the YAML file: https://github.com/git/git-scm.com/blob/main/resources/guis.yml\n     -    1. The fields `name`, `url`, `price`, `license` should be very straightforward to fill.\n    @@ README.md: be accepted. If it involves code, please also write tests for it.\n     -    3. `platforms` is a list of at least 1 platform in which the tool is supported. The possibilities are: `Windows`, `Mac`, `Linux`, `Android`, and `iOS`\n     -    4. `order` can be filled with the biggest number already existing, plus 1 (Adding to the bottom - this will be covered in the following steps)\n     -    5. `trend_name` is an optional field that can be used for helping sorting the clients (also covered in the next steps)\n    --\n    ++After making the changes, commit and push to a named branch in your fork, then open a pull request. If it is a big feature, you might want to [start an issue](https://github.com/git/git-scm.com/issues/new) first to make sure it's something that will be accepted.\n    +\n     -2. Add the image to `public/images/guis/<GUI_CLIENT_NAME>@2x.png` and `public/images/guis/<GUI_CLIENT_NAME>.png` making sure the aspect ratio matches a 588:332 image.\n    --\n    ++## Adding a new GUI\n    +\n     -3. Sort the tools\n     -    1. From the root of the repository, run: `$ ./script/sort-gui`\n     -    2. A list of google trends url's will be displayed at the bottom if everything went well.\n    @@ README.md: be accepted. If it involves code, please also write tests for it.\n     -    7. The script makes some basic verifications. If there was some problem, it should be easily visible in the output\n     -      1. If you have more than 1 tool with the same name, a warning will appear: `======= WARNING: THERE ARE DUPLICATED GUIS =======`\n     -      2. If you are using the same `order` value for more than 1 tool, a warning will appear: `======= WARNING: THERE ARE DUPLICATED ORDERS (value: <VALUE>) =======`\n    --\n    ++The [list of GUI clients](https://git-scm.com/downloads/guis) has been constructed by the community for a long time. If you want to add another tool you'll need to follow a few steps:\n    +\n     -## FAQ\n    --\n    --While setting the repo if you find any error, check if it's a known issue and the corresponding solution bellow.\n    -+1. Add a new `.md` file with the GUI client details: https://github.com/git/git-scm.com/tree/main/content/data/guis\n    ++1. Add a new `.md` file with the GUI client details: data/guis\n     +    1. The fields need to be enclosed within `---` lines\n     +    2. The fields `name`, `project_url`, `price`, `license` should be very straightforward to fill.\n     +    3. The field `image_tag` corresponds to the path of the image of the tool (should start with `images/guis/`).\n     +    4. `platforms` is a list of at least 1 platform in which the tool is supported. The possibilities are: `Windows`, `Mac`, `Linux`, `Android`, and `iOS`\n    -+    5. `order` can be filled with the biggest number already existing, plus 1 (Adding to the bottom - this will be covered in the following steps). This is the only field whose value should _not_ be enclosed in double-quote characters.\n    -+    6. `trend_name` is an optional field that can be used for helping sorting the clients (also covered in the next steps)\n    ++    5. `order` can be filled with the biggest number already existing, plus 1 (this number determines the order in which the GUIs are rendered). This is the only field whose value should _not_ be enclosed in double-quote characters.\n    ++    6. `trend_name` is an optional field that can be used for helping sorting the clients.\n\n    --### An error occurred while installing pg (1.2.3), and Bundler cannot continue.\n    +-While setting the repo if you find any error, check if it's a known issue and the corresponding solution bellow.\n     +2. Add the image to `static/images/guis/<GUI_CLIENT_NAME>@2x.png` and `static/images/guis/<GUI_CLIENT_NAME>.png` making sure the aspect ratio matches a 588:332 image.\n\n    +-### An error occurred while installing pg (1.2.3), and Bundler cannot continue.\n    ++## Useful links\n    +\n     -If you got this error when running `bundle install`, then you need to install postgresql on your OS. Check [this stackoverflow topic](https://stackoverflow.com/questions/52339221/rails-gem-error-while-installing-pg-1-1-3-and-bundler-cannot-continue) for more details.\n    -+## Useful link regarding working with Hugo\n    ++### Hugo (static site generator)\n\n     +* https://gohugo.io/\n     +* https://gohugo.io/content-management/shortcodes/\n 18:  ae9a0d82e =  14:  e53e639b5 Move stylesheets to `assets/sass/`\n 19:  faab827f4 !  15:  11ab6559e Move the images to `static/images/`\n    @@ public/images/guis/git-glint@2x.png => static/images/guis/git-glint@2x.png\n\n      ## public/images/guis/git-kraken.png => static/images/guis/git-kraken.png ##\n\n    - ## public/images/guis/git-kraken@2x.png => static/images/guis/git-kraken@2x.png ##\n    -\n      ## public/images/guis/git2go.png => static/images/guis/git2go.png ##\n\n      ## public/images/guis/git2go@2x.png => static/images/guis/git2go@2x.png ##\n    @@ public/images/guis/gitklient.png => static/images/guis/gitklient.png\n\n      ## public/images/guis/gitklient@2x.png => static/images/guis/gitklient@2x.png ##\n\n    + ## public/images/guis/gitkraken-2024@2x.png => static/images/guis/gitkraken-2024@2x.png ##\n    +\n      ## public/images/guis/gitonic.png => static/images/guis/gitonic.png ##\n\n      ## public/images/guis/gitonic@2x.png => static/images/guis/gitonic@2x.png ##\n    @@ public/images/guis/gitx.png => static/images/guis/gitx.png\n\n      ## public/images/guis/gitx@2x.png => static/images/guis/gitx@2x.png ##\n\n    + ## public/images/guis/gk-cli-keifs@2x.png => static/images/guis/gk-cli-keifs@2x.png ##\n    +\n      ## public/images/guis/gmaster.png => static/images/guis/gmaster.png ##\n\n      ## public/images/guis/gmaster@2x.png => static/images/guis/gmaster@2x.png ##\n    @@ public/images/guis/pragmagit.png => static/images/guis/pragmagit.png\n\n      ## public/images/guis/pragmagit@2x.png => static/images/guis/pragmagit@2x.png ##\n\n    + ## public/images/guis/relagit.png => static/images/guis/relagit.png ##\n    +\n    + ## public/images/guis/relagit@2x.png => static/images/guis/relagit@2x.png ##\n    +\n      ## public/images/guis/repoz.png => static/images/guis/repoz.png ##\n\n      ## public/images/guis/repoz@2x.png => static/images/guis/repoz@2x.png ##\n    @@ public/images/guis/snailgit.png => static/images/guis/snailgit.png\n\n      ## public/images/guis/snailgit@2x.png => static/images/guis/snailgit@2x.png ##\n\n    + ## public/images/guis/sourcegit.png => static/images/guis/sourcegit.png ##\n    +\n    + ## public/images/guis/sourcegit@2x.png => static/images/guis/sourcegit@2x.png ##\n    +\n      ## public/images/guis/sourcetree.png => static/images/guis/sourcetree.png ##\n\n      ## public/images/guis/sourcetree@2x.png => static/images/guis/sourcetree@2x.png ##\n 20:  34a9aafae =  16:  b379241b1 Move Javascripts to the `static/` directory\n 21:  1e7d57b39 =  17:  add38c971 Remove the layout for the error pages\n 22:  3775916fa =  18:  0929494dc Migrate the primary layout to the Hugo world\n 23:  03da9c6ea !  19:  817c49726 Adjust the layouts\n    @@ layouts/_default/baseof.html\n\n     -  <%= stylesheet_link_tag \"application\" %>\n     -  <%= javascript_include_tag \"modernize\" %>\n    -+  {{ $style := resources.Get \"sass/application.scss\" | resources.ExecuteAsTemplate \"application.scss\" . | resources.ToCSS | resources.Minify }}\n    ++  {{ $style := resources.Get \"sass/application.scss\" | resources.ExecuteAsTemplate \"application.scss\" . | css.Sass | resources.Minify }}\n     +  <link rel=\"stylesheet\" href=\"{{ $style.RelPermalink }}\">\n     +  <script src=\"/js/modernizr.js\"></script>\n     +  <script src=\"/js/modernize.js\"></script>\n    @@ layouts/_default/baseof.html\n     +        {{ partial \"sidebar.html\" . }}\n              <div id=\"content\">\n     -          <%= yield %>\n    -+          {{ .Content }}\n    ++          {{ if (eq .Page.Path \"/docs\") }}\n    ++            {{ partial \"ref/index.html\" . }}\n    ++          {{ else }}\n    ++            {{ .Content }}\n    ++          {{ end }}\n              </div>\n            </div>\n     -      <%= render partial: \"shared/footer\" %>\n 24:  b721d0a20 =  20:  f2546f28f Remove some includable bits that won't be needed in the future\n 25:  25b0a2330 =  21:  e465a5ae6 Move some includable bits to `layouts/`\n 26:  c9fd7e391 =  22:  b4a98bee7 Make Javascript imports explicit\n 27:  3b33bf0de =  23:  68e705c49 Inline the taglines on the top\n 28:  79ffa61fc =  24:  3558e3570 Move the main index.html to the `content/` directory\n 29:  1991470d8 =  25:  d23fd0543 Migrate the top-level page of the site to Hugo\n 30:  e308db418 =  26:  4d5b58b58 CSS: quote URLs consistently\n 31:  f55cae009 =  27:  37497edb9 Explicitly include 'normalize' in application CSS\n 32:  61a203caf =  28:  4ac41f786 Use the base URL in partial .scss files\n 33:  c3730b407 !  29:  6b2d46049 Add a script to update the latest Git version\n    @@ Commit message\n\n      ## Gemfile ##\n     @@\n    + # frozen_string_literal: true\n\n      source \"https://rubygems.org\"\n    -\n    ++\n     +gem \"octokit\"\n\n      ## script/update-git-version.rb (new) ##\n 34:  79e745184 =  30:  02abe2c42 update-git-version: skip -rc versions\n 35:  0e8a57fab =  31:  115ebbebf Allow redirecting from `list` pages\n 36:  ac13d76df !  32:  1118b8eaf Define and use sections in a flexible way\n    @@ layouts/_default/baseof.html\n     +{{ $section := \"\" }}\n     +{{ if isset .Params \"section\" }}\n     +{{ $section = .Params.section }}\n    -+{{ else if isset .Page \"Section\" }}\n    ++{{ else if (eq .Page.Path \"/docs\") }}\n    ++{{ $section = \"documentation\" }}\n    ++{{ else if (isset .Page \"Section\") }}\n     +{{ $section = .Page.Section }}\n    -+{{ else if or (eq .Page.Type \"doc\") (eq .Page.Type \"docs\") (eq .Page.Type \"video\") }}\n    ++{{ else if (or (eq .Page.Type \"doc\") (eq .Page.Type \"docs\") (eq .Page.Type \"video\")) }}\n     +{{ $section = \"documentation\" }}\n    -+{{ else if isset .Page \"Type\" }}\n    ++{{ else if (isset .Page \"Type\") }}\n     +{{ $section = .Page.Type }}\n     +{{ else }}\n    -+{{ warnf \"No section found in %s\" (.File | jsonify) }}\n    ++{{ warnf \"No section found in %s\" (.Page.Path | jsonify) }}\n     +{{ end }}\n     +{{ .Scratch.Set \"section\" $section }}\n     +\n 37:  894f2cef3 =  33:  fffd7a142 Move the \"About\" pages to `content/about/`\n 38:  1f7ef75b7 !  34:  3f3dd9823 Convert 'About' pages to Hugo\n    @@ content/about/trademark.html\n\n      ## layouts/_default/baseof.html ##\n     @@\n    -       <div id=\"content-wrapper\">\n    -         {{ partial \"sidebar.html\" . }}\n              <div id=\"content\">\n    --          {{ .Content }}\n    -+          {{ if eq $section \"about\" }}\n    -+\t    <div id=\"main\">\n    +           {{ if (eq .Page.Path \"/docs\") }}\n    +             {{ partial \"ref/index.html\" . }}\n    ++          {{ else if eq $section \"about\" }}\n    ++            <div id=\"main\">\n     +              <h1>About</h1>\n     +\n     +              <ol id=\"about-nav\">\n    @@ layouts/_default/baseof.html\n     +              </ol>\n     +              {{ .Content }}\n     +            </div>\n    -+          {{ else }}\n    -+            {{ .Content }}\n    -+          {{ end }}\n    -         </div>\n    -       </div>\n    -       {{ partial \"footer.html\" . }}\n    +           {{ else }}\n    +             {{ .Content }}\n    +           {{ end }}\n\n      ## layouts/partials/sidebar.html ##\n     @@\n 39:  c139f5c1f =  35:  e743fb5cc about/trademark: add back old redirect\n 40:  ded625e52 =  36:  b68954145 Install redirects for two `About` pages\n 41:  14843ae22 =  37:  a5f2b8be5 Remove dynamic About navigation code\n 42:  20e420d65 =  38:  100808034 Migrate the community page from the web app to the static site\n 43:  dd4f0b599 !  39:  7a4f64d25 Adjust the community page\n    @@ content/community/_index.html\n      <div id=\"main\">\n        <h1> Community</h1>\n\n    -   <h2> Mailing List</h2>\n    -\n    -   <p>\n    --    General questions or comments for the Git community can be sent to the mailing list by using the email address <a href=\"mailto:git@vger.kernel.org\">git@vger.kernel.org</a>.\n    -+    General questions or comments for the Git community can be sent to the mailing list by using the email address <a href=\"mailto:git@vger.kernel.org\">git@vger.kernel.org</a>.\n    -   </p>\n    --\n    -+\n    -   <p>\n    -     <strong>If you wish to report any possible bug for Git, please use this mailing list as well.</strong>\n    -   </p>\n     @@\n        <p>\n          The <a href=\"https://git.github.io/\">Git Developer Pages</a> have a <a href=\"https://git.github.io/Hacking-Git/\">Hacking Git page</a> which lists useful development resources. They also have <a href=\"https://git.github.io/General-Application-Information/\">information</a> for people applying to work on Git as part of programs like <a href=\"https://www.outreachy.org/\">Outreachy</a> or the <a href=\"https://summerofcode.withgoogle.com/\">Google Summer of Code</a>.\n 44:  6625d81dd =  40:  6767a083e Adjust the GUI clients link in the sidebar\n 45:  293e57ba7 =  41:  4a2f13d4b Move the GUI Clients page into the location expected by Hugo\n 46:  45c7d59a6 !  42:  df1b23221 Adapt the GUI Clients page to Hugo\n    @@ Commit message\n         the JSON containing the list of GUI Clients into individual files inside\n         `data/`.\n\n    +    The files in `data/guis/` were written via:\n    +\n    +      git show HEAD:resources/guis.yml |\n    +      ruby -e '\n    +        # cannot use YAML.load because that would lose comments\n    +        $filename = nil\n    +        $content = \"\"\n    +        def write\n    +          return if $filename.nil?\n    +          File.open($filename, \"w\") { |f| f.write(\"---\\n#{$content}---\\n\") } unless $filename.nil?\n    +          $filename = nil\n    +          $content = \"\"\n    +        end\n    +        ARGF.each do |line|\n    +          if line.start_with?(\"-\") then\n    +            write\n    +            line[0] = \" \"\n    +          end\n    +          if line.start_with?(\"  \") then\n    +            $filename = \"data/guis/#{line[8..].chomp.gsub(/ *\\(.*\\)$/, \"\").gsub(/ /, \"-\").downcase}.yml\" if line.start_with?(\"  name: \")\n    +            $content += line[2..]\n    +              .gsub(/^image_tag: /, \"\\\\0images/\")\n    +              .gsub(/^url:/, \"project_url:\")\n    +              .gsub(/([-:] )([^\"]*[^\"0-9][^\"]*)\\n$/, \"\\\\1\\\"\\\\2\\\"\\n\")\n    +              .gsub(/^-/, \"  -\")\n    +            end\n    +        end\n    +        write\n    +      '\n    +\n         Signed-off-by: Victoria Dye <vdye@github.com>\n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n\n    @@ data/guis/anchorpoint.yml (new)\n      ## data/guis/aurees.yml (new) ##\n     @@\n     +---\n    -+name: \"Aurees\"\n    ++name: \"Aurees (no longer under active development)\"\n     +project_url: \"https://aurees.com/\"\n     +image_tag: \"images/guis/aurees@2x.png\"\n     +platforms:\n    @@ data/guis/gitahead.yml (new)\n     +  - \"Linux\"\n     +price: \"Free\"\n     +license: \"MIT\"\n    -+order: 13\n    ++order: 17\n     +---\n\n      ## data/guis/gitatomic.yml (new) ##\n    @@ data/guis/gitbreeze.yml (new)\n     +  - \"Linux\"\n     +price: \"Free\"\n     +license: \"Proprietary\"\n    -+trend_name: \"git breeze\"\n     +order: 53\n     +---\n\n    @@ data/guis/gitdrive.yml (new)\n     +image_tag: \"images/guis/gitdrive@2x.png\"\n     +platforms:\n     +  - \"iOS\"\n    -+# No details what the $6.99 in-app purchase does, all it says is 'Unlimited Version'\n    ++# No details what the $6.99 in-app purchase does, all it says is \"Unlimited Version\"\n     +price: \"Free / $6.99\"\n     +license: \"Proprietary\"\n     +order: 37\n    @@ data/guis/github-desktop.yml (new)\n     +order: 1\n     +---\n\n    - ## data/guis/gitkraken.yml (new) ##\n    + ## data/guis/gitkraken-cli.yml (new) ##\n     @@\n     +---\n    -+name: \"GitKraken\"\n    -+project_url: \"https://www.gitkraken.com/\"\n    -+image_tag: \"images/guis/git-kraken@2x.png\"\n    ++name: \"GitKraken CLI\"\n    ++project_url: \"https://www.gitkraken.com/cli\"\n    ++image_tag: \"images/guis/gk-cli-keifs@2x.png\"\n    ++platforms:\n    ++  - \"Windows\"\n    ++  - \"Mac\"\n    ++  - \"Linux\"\n    ++price: \"Free / $48.00+/user annually\"\n    ++license: \"Proprietary\"\n    ++order: 55\n    ++---\n    +\n    + ## data/guis/gitkraken-desktop.yml (new) ##\n    +@@\n    ++---\n    ++name: \"GitKraken Desktop\"\n    ++project_url: \"https://www.gitkraken.com/git-client\"\n    ++image_tag: \"images/guis/gitkraken-2024@2x.png\"\n     +platforms:\n     +  - \"Windows\"\n     +  - \"Mac\"\n     +  - \"Linux\"\n     +# Free tier only works with local and public repositories\n    -+price: \"Free / $59+/user annually\"\n    ++price: \"Free / $48+/user annually\"\n     +license: \"Proprietary\"\n     +order: 5\n     +---\n    @@ data/guis/gitnuro.yml (new)\n     @@\n     +---\n     +name: \"Gitnuro\"\n    -+project_url: \"https://gitnuro.jetpackduba.com/\"\n    ++project_url: \"https://gitnuro.com/\"\n     +image_tag: \"images/guis/Gitnuro@2x.png\"\n     +platforms:\n     +  - \"Windows\"\n    @@ data/guis/gitui.yml (new)\n     @@\n     +---\n     +name: \"GitUI\"\n    -+project_url: \"https://extrawurst.itch.io/gitui\"\n    ++project_url: \"http://gitui.org\"\n     +image_tag: \"images/guis/gitui@2x.png\"\n     +platforms:\n     +  - \"Windows\"\n    @@ data/guis/glint.yml (new)\n     +  - \"Windows\"\n     +  - \"Mac\"\n     +  - \"Linux\"\n    -+price: \"Free (can only open up to two repositories at once)/ $35/user annually\"\n    ++# Free tier can only open up to two repositories at once\n    ++price: \"Free / $35/user annually\"\n     +license: \"Proprietary\"\n     +trend_name: \"git glint\"\n    -+order: 52\n    ++order: 13\n     +---\n\n      ## data/guis/guitar.yml (new) ##\n    @@ data/guis/pragma-git.yml (new)\n     +license: \"MIT\"\n     +trend_name: \"pragma github\"\n     +order: 54\n    ++---\n    +\n    + ## data/guis/relagit.yml (new) ##\n    +@@\n    ++---\n    ++name: \"RelaGit\"\n    ++project_url: \"https://rela.dev\"\n    ++image_tag: \"images/guis/relagit@2x.png\"\n    ++platforms:\n    ++  - \"Windows\"\n    ++  - \"Mac\"\n    ++  - \"Linux\"\n    ++price: \"Free\"\n    ++license: \"LGPL-3.0-or-later\"\n    ++order: 57\n     +---\n\n      ## data/guis/repoz.yml (new) ##\n    @@ data/guis/snailgit.yml (new)\n     +price: \"Free (limited) / $9.99\"\n     +license: \"Proprietary\"\n     +order: 32\n    ++---\n    +\n    + ## data/guis/sourcegit.yml (new) ##\n    +@@\n    ++---\n    ++name: \"SourceGit\"\n    ++project_url: \"https://sourcegit-scm.github.io\"\n    ++image_tag: \"images/guis/sourcegit@2x.png\"\n    ++platforms:\n    ++  - \"Windows\"\n    ++  - \"Mac\"\n    ++  - \"Linux\"\n    ++price: \"Free\"\n    ++license: \"MIT\"\n    ++trend_name: \"SourceGit\"\n    ++order: 56\n     +---\n\n      ## data/guis/sourcetree.yml (new) ##\n    @@ resources/guis.yml (deleted)\n     -  price: Free\n     -  license: GNU GPL\n     -  order: 4\n    --- name: GitKraken\n    --  url: https://www.gitkraken.com/\n    --  image_tag: guis/git-kraken@2x.png\n    +-- name: GitKraken Desktop\n    +-  url: https://www.gitkraken.com/git-client\n    +-  image_tag: guis/gitkraken-2024@2x.png\n     -  platforms:\n     -  - Windows\n     -  - Mac\n     -  - Linux\n     -  # Free tier only works with local and public repositories\n    --  price: Free / $59+/user annually\n    +-  price: Free / $48+/user annually\n     -  license: Proprietary\n     -  order: 5\n     -- name: Magit\n    @@ resources/guis.yml (deleted)\n     -  license: GNU GPL\n     -  trend_name: Git giggle\n     -  order: 27\n    --- name: Aurees\n    +-- name: Aurees (no longer under active development)\n     -  url: https://aurees.com/\n     -  image_tag: guis/aurees@2x.png\n     -  platforms:\n    @@ resources/guis.yml (deleted)\n     -  - Linux\n     -  price: Free\n     -  license: MIT\n    --  order: 13\n    +-  order: 17\n     -- name: Gittyup\n     -  url: https://murmele.github.io/Gittyup/\n     -  image_tag: guis/gittyup@2x.png\n    @@ resources/guis.yml (deleted)\n     -  license: Proprietary\n     -  order: 42\n     -- name: GitUI\n    --  url: https://extrawurst.itch.io/gitui\n    +-  url: http://gitui.org\n     -  image_tag: guis/gitui@2x.png\n     -  platforms:\n     -  - Windows\n    @@ resources/guis.yml (deleted)\n     -  trend_name: gitonic\n     -  order: 50\n     -- name: Gitnuro\n    --  url: https://gitnuro.jetpackduba.com/\n    +-  url: https://gitnuro.com/\n     -  image_tag: guis/Gitnuro@2x.png\n     -  platforms:\n     -    - Windows\n    @@ resources/guis.yml (deleted)\n     -  price: Free / $35/user annually\n     -  license: Proprietary\n     -  trend_name: git glint\n    --  order: 52\n    +-  order: 13\n     -- name: GitBreeze\n     -  url: https://gitbreeze.dev/\n     -  image_tag: guis/gitbreeze@2x.png\n    @@ resources/guis.yml (deleted)\n     -  license: MIT\n     -  trend_name: pragma github\n     -  order: 54\n    --\n    +-- name: GitKraken CLI\n    +-  url: https://www.gitkraken.com/cli\n    +-  image_tag: guis/gk-cli-keifs@2x.png\n    +-  platforms:\n    +-  - Windows\n    +-  - Mac\n    +-  - Linux\n    +-  price: Free / $48.00+/user annually\n    +-  license: Proprietary\n    +-  order: 55\n    +-- name: SourceGit\n    +-  url: https://sourcegit-scm.github.io\n    +-  image_tag: guis/sourcegit@2x.png\n    +-  platforms:\n    +-    - Windows\n    +-    - Mac\n    +-    - Linux\n    +-  price: Free\n    +-  license: MIT\n    +-  trend_name: SourceGit\n    +-  order: 56\n    +-- name: RelaGit\n    +-  url: https://rela.dev\n    +-  image_tag: guis/relagit@2x.png\n    +-  platforms:\n    +-    - Windows\n    +-    - Mac\n    +-    - Linux\n    +-  price: Free\n    +-  license: LGPL-3.0-or-later\n    +-  order: 57\n 47:  5b1e9a6de =  43:  ad17fe7f0 downloads/guis: adjust the Javascript for filtering\n 48:  579cf46a3 =  44:  e6208d5ed Move logos page to the new location\n 49:  99766e6fd =  45:  ea70f19f1 Migrate logos page\n 50:  1a42a0157 =  46:  bc0d9409a Create base Downloads page\n 51:  07c392bd8 =  47:  3d22dc37c Move the OS-specific Downloads pages into the location needed for Hugo\n 52:  6d315ee76 =  48:  17c567019 Implement the `site-param` shortcode\n 53:  c2cbcaf82 =  49:  1812362c7 Hugo-ify Linux & Mac download pages\n 54:  d55dfbd55 !  50:  58a155e45 Add a script to store download data in `hugo.yml`\n    @@ README.md: Or you can do it from GitHub (much slower) like this:\n\n      ## hugo.yml ##\n     @@ hugo.yml: params:\n    -   latest_version: 2.43.0\n    -   latest_relnote_url: https://raw.github.com/git/git/master/Documentation/RelNotes/2.43.0.txt\n    -   latest_release_date: '2023-11-20'\n    +   latest_version: 2.46.0\n    +   latest_relnote_url: https://raw.github.com/git/git/master/Documentation/RelNotes/2.46.0.txt\n    +   latest_release_date: '2024-07-29'\n     +  macos_installer:\n     +    url: https://sourceforge.net/projects/git-osx-installer/files/git-2.33.0-intel-universal-mavericks.dmg/download?use_mirror=autoselect\n     +    version: 2.33.0\n    @@ hugo.yml: params:\n     +    filename: git-2.33.0-intel-universal-mavericks.dmg\n     +  windows_installer:\n     +    portable32:\n    -+      filename: PortableGit-2.43.0-32-bit.7z.exe\n    -+      release_date: '2023-11-20'\n    -+      version: 2.43.0\n    -+      url: https://github.com/git-for-windows/git/releases/download/v2.43.0.windows.1/PortableGit-2.43.0-32-bit.7z.exe\n    ++      filename: PortableGit-2.45.2-32-bit.7z.exe\n    ++      release_date: '2024-06-03'\n    ++      version: 2.45.2\n    ++      url: https://github.com/git-for-windows/git/releases/download/v2.45.2.windows.1/PortableGit-2.45.2-32-bit.7z.exe\n     +    portable64:\n    -+      filename: PortableGit-2.43.0-64-bit.7z.exe\n    -+      release_date: '2023-11-20'\n    -+      version: 2.43.0\n    -+      url: https://github.com/git-for-windows/git/releases/download/v2.43.0.windows.1/PortableGit-2.43.0-64-bit.7z.exe\n    ++      filename: PortableGit-2.45.2-64-bit.7z.exe\n    ++      release_date: '2024-06-03'\n    ++      version: 2.45.2\n    ++      url: https://github.com/git-for-windows/git/releases/download/v2.45.2.windows.1/PortableGit-2.45.2-64-bit.7z.exe\n     +    installer32:\n    -+      filename: Git-2.43.0-32-bit.exe\n    -+      release_date: '2023-11-20'\n    -+      version: 2.43.0\n    -+      url: https://github.com/git-for-windows/git/releases/download/v2.43.0.windows.1/Git-2.43.0-32-bit.exe\n    ++      filename: Git-2.45.2-32-bit.exe\n    ++      release_date: '2024-06-03'\n    ++      version: 2.45.2\n    ++      url: https://github.com/git-for-windows/git/releases/download/v2.45.2.windows.1/Git-2.45.2-32-bit.exe\n     +    installer64:\n    -+      filename: Git-2.43.0-64-bit.exe\n    -+      release_date: '2023-11-20'\n    -+      version: 2.43.0\n    -+      url: https://github.com/git-for-windows/git/releases/download/v2.43.0.windows.1/Git-2.43.0-64-bit.exe\n    ++      filename: Git-2.45.2-64-bit.exe\n    ++      release_date: '2024-06-03'\n    ++      version: 2.45.2\n    ++      url: https://github.com/git-for-windows/git/releases/download/v2.45.2.windows.1/Git-2.45.2-64-bit.exe\n\n      ## app/services/download_service.rb => script/update-download-data.rb ##\n     @@\n 55:  d58f20885 =  51:  cce79c1a0 Downloads (MacOS): show relative date\n 56:  85d109ff6 =  52:  c0340b223 Convert the Windows downloads page to Hugo\n 57:  5599b85a8 =  53:  5ae935edb download(win): white-space fix\n 58:  6d1b7c7f3 =  54:  0b69e8c0c Add 'Downloads' page aliases\n 59:  2f572e985 =  55:  8a9b19d16 Move the documentation landing page in place for Hugo\n 60:  1a96900f1 =  56:  fc28e03d2 Migrate the 'Documentation' landing page to Hugo\n 61:  2f53db947 <   -:  --------- Move the \"references\" landing page to `content/`\n  -:  --------- >  57:  49631dde3 Move the \"references\" landing page to `layouts/partials/`, in preparation for using it as a Hugo partial template.\n 62:  dd874d4c7 =  58:  59972c75f Move the files defining the documentation categories\n 63:  60f601394 !  59:  5ecc96a9a Adjust the \"references\" page to Hugo\n    @@ Commit message\n         Signed-off-by: Victoria Dye <vdye@github.com>\n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n\n    - ## content/docs/_index.html ##\n    -@@\n    --<%- @section    = 'documentation' %>\n    --<%- @subsection = 'reference' %>\n    --<%- @page_title = \"Git - Reference\" %>\n    -+---\n    -+section: \"documentation\"\n    -+subsection: \"reference\"\n    -+title: \"Git - Reference\"\n    -+url: /docs.html\n    -+aliases:\n    -+- /docs/index.html\n    -+---\n    -\n    - <div id='main'>\n    -   <h1>Reference</h1>\n    -   <div class='callout quickref'>\n    -     <p>\n    -       Quick reference guides:\n    --      <%= link_to \"GitHub Cheat Sheet\", \"https://github.github.com/training-kit/\" %>\n    -+      <a href=\"https://github.github.com/training-kit/\">GitHub Cheat Sheet</a>\n    -       |\n    --      <%= link_to \"Visual Git Cheat Sheet\", \"https://ndpsoftware.com/git-cheatsheet.html\" %>\n    -+      <a href=\"https://ndpsoftware.com/git-cheatsheet.html\">Visual Git Cheat Sheet</a>\n    -     </p>\n    -   </div>\n    -   <div class=\"callout all-commands\">\n    -@@\n    -   <div class='reference-menu'>\n    -     <div class='two-column'>\n    -       <div class='column-left'>\n    --        <%= render 'shared/ref/setup' %>\n    --        <%= render 'shared/ref/creating' %>\n    --        <%= render 'shared/ref/snapshot' %>\n    --        <%= render 'shared/ref/branching' %>\n    --        <%= render 'shared/ref/sharing' %>\n    --        <%= render 'shared/ref/inspection' %>\n    --        <%= render 'shared/ref/patching' %>\n    --        <%= render 'shared/ref/debugging' %>\n    -+        {{< category \"setup\" >}}\n    -+        {{< category \"projects\" >}}\n    -+        {{< category \"snapshotting\" >}}\n    -+        {{< category \"branching\" >}}\n    -+        {{< category \"sharing\" >}}\n    -+        {{< category \"inspection\" >}}\n    -+        {{< category \"patching\" >}}\n    -+        {{< category \"debugging\" >}}\n    -       </div>\n    -       <div class='column-right'>\n    --        <%= render 'shared/ref/guides' %>\n    --        <%= render 'shared/ref/email' %>\n    --        <%= render 'shared/ref/external' %>\n    --        <%= render 'shared/ref/admin' %>\n    --        <%= render 'shared/ref/server' %>\n    --        <%= render 'shared/ref/plumbing' %>\n    -+        {{< category \"guides\" >}}\n    -+        {{< category \"email\" >}}\n    -+        {{< category \"external\" >}}\n    -+        {{< category \"admin\" >}}\n    -+        {{< category \"server-admin\" >}}\n    -+        {{< category \"plumbing\" >}}\n    -       </div>\n    -     </div>\n    -   </div>\n    -\n      ## data/doc_categories/admin.yml ##\n     @@\n     -<h3 class='admin'>Administration</h3>\n    @@ data/doc_categories/setup.yml\n     -  <li><%= man('git-config') %></li>\n     -  <li><%= man('git-help') %></li>\n     -  <li><%= man('git-bugreport') %></li>\n    --  <%= link_to \"Credential helpers\", \"/doc/credential-helpers\", sidebar_link_options(\"credential-helpers\") %>\n    +-  <li><%= link_to \"Credential helpers\", \"/doc/credential-helpers\", sidebar_link_options(\"credential-helpers\") %></li>\n     -</ul>\n     +---\n     +category_id: \"setup\"\n    @@ layouts/partials/ref/category.html (new)\n     +<ul class='unstyled'>\n     +  {{ range $doc := $category.commands }}\n     +  <li>\n    -+    <a href=\"{{ print \"docs/\" $doc.doc (cond (and (ne .Params.lang nil) (ne $doc.no_append_lang true)) (print \"/\" .Params.lang) \"\") }}\">\n    ++    <a href=\"{{ print \"/docs/\" $doc.doc (cond (and (ne $.Page.Params.lang nil) (ne $doc.no_append_lang true) (isset (index $.Site.Data.docs.pages $doc.doc) \"languages\") (isset (index $.Site.Data.docs.pages $doc.doc \"languages\") $.Page.Params.lang)) (print \"/\" $.Page.Params.lang) \"\") }}\">\n     +      {{ if (eq $doc.title nil) }}\n     +        {{ replace $doc.doc \"git-\" \"\" }}\n     +      {{ else }}\n    @@ layouts/partials/ref/category.html (new)\n     +  {{ end }}\n     +</ul>\n\n    - ## layouts/shortcodes/category.html (new) ##\n    + ## layouts/partials/ref/index.html ##\n     @@\n    -+{{ .Scratch.Set \"category_id\" (.Get 0) }}\n    -+{{ partial \"ref/category.html\" . }}\n    +-<%- @section    = 'documentation' %>\n    +-<%- @subsection = 'reference' %>\n    +-<%- @page_title = \"Git - Reference\" %>\n    +-\n    ++{{ $context := . }}\n    + <div id='main'>\n    +   <h1>Reference</h1>\n    +   <div class='callout quickref'>\n    +     <p>\n    +       Quick reference guides:\n    +-      <%= link_to \"GitHub Cheat Sheet\", \"https://github.github.com/training-kit/\" %>\n    ++      <a href=\"https://github.github.com/training-kit/\">GitHub Cheat Sheet</a>\n    +       |\n    +-      <%= link_to \"Visual Git Cheat Sheet\", \"https://ndpsoftware.com/git-cheatsheet.html\" %>\n    ++      <a href=\"https://ndpsoftware.com/git-cheatsheet.html\">Visual Git Cheat Sheet</a>\n    +     </p>\n    +   </div>\n    +   <div class=\"callout all-commands\">\n    +@@\n    +   <div class='reference-menu'>\n    +     <div class='two-column'>\n    +       <div class='column-left'>\n    +-        <%= render 'shared/ref/setup' %>\n    +-        <%= render 'shared/ref/creating' %>\n    +-        <%= render 'shared/ref/snapshot' %>\n    +-        <%= render 'shared/ref/branching' %>\n    +-        <%= render 'shared/ref/sharing' %>\n    +-        <%= render 'shared/ref/inspection' %>\n    +-        <%= render 'shared/ref/patching' %>\n    +-        <%= render 'shared/ref/debugging' %>\n    ++        {{ range slice \"setup\" \"projects\" \"snapshotting\" \"branching\" \"sharing\" \"inspection\" \"patching\" \"debugging\" }}\n    ++        {{ $context.Scratch.Set \"category_id\" . }}\n    ++        {{ partial \"ref/category.html\" $context }}\n    ++        {{ end }}\n    +       </div>\n    +       <div class='column-right'>\n    +-        <%= render 'shared/ref/guides' %>\n    +-        <%= render 'shared/ref/email' %>\n    +-        <%= render 'shared/ref/external' %>\n    +-        <%= render 'shared/ref/admin' %>\n    +-        <%= render 'shared/ref/server' %>\n    +-        <%= render 'shared/ref/plumbing' %>\n    ++        {{ range slice \"guides\" \"email\" \"external\" \"admin\" \"server-admin\" \"plumbing\" }}\n    ++        {{ $context.Scratch.Set \"category_id\" . }}\n    ++        {{ partial \"ref/category.html\" $context }}\n    ++        {{ end }}\n    +       </div>\n    +     </div>\n    +   </div>\n 64:  a3b5464d2 =  60:  10c090f35 Migrate 'Videos' page\n 65:  ebbf2dc93 !  61:  9bc68f7dd Migrate individual video pages\n    @@ app/views/doc/watch.html.erb (deleted)\n\n      ## layouts/_default/baseof.html ##\n     @@\n    -       <div id=\"content-wrapper\">\n    -         {{ partial \"sidebar.html\" . }}\n              <div id=\"content\">\n    --          {{ if eq $section \"about\" }}\n    -+          {{ if isset .Params \"video_title\" }}\n    -+\t    <div id=\"main\">\n    +           {{ if (eq .Page.Path \"/docs\") }}\n    +             {{ partial \"ref/index.html\" . }}\n    ++          {{ else if isset .Params \"video_title\" }}\n    ++            <div id=\"main\">\n     +              <h1>{{ .Params.category }} Episode {{ .Params.episode }}</h1>\n     +              <h2>{{ .Params.video_title }}</h2>\n     +\n    @@ layouts/_default/baseof.html\n     +                <iframe src=\"https://player.vimeo.com/video/{{ .Params.ext_id }}?title=0&amp;byline=0&amp;portrait=0&amp;color=f14e32\" width=\"635\" height=\"360\" frameborder=\"0\" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>\n     +              </div>\n     +            </div>\n    -+          {{ else if eq $section \"about\" }}\n    - \t    <div id=\"main\">\n    +           {{ else if eq $section \"about\" }}\n    +             <div id=\"main\">\n                    <h1>About</h1>\n    -\n 66:  d97d6afea !  62:  f0e2dad52 Migrate external links page to Hugo\n    @@ app/views/doc/_ext_tutorials.erb => layouts/shortcodes/external/tutorials.html\n              </p>\n     -      </li>\n     +      </li>\n    -     </ul>\n    -   </div>\n    -   <div class='column-right'>\n    +       <li>\n    +         <h4><a href=\"https://www.python4data.science/en/latest/productive/git/index.html\">Git for Data Science</a></h4>\n    +         <p class='description'>\n     @@\n              <p class='description'>\n                Learn the basics of Git and Version Control through detailed and easy to follow steps.\n 67:  9549255ef =  63:  b4755928a Migrate the 'About this site' page to Hugo\n 68:  4114ef8f4 =  64:  593d99e18 Migrate the Software Freedom Conservancy page\n 69:  d05bb21d4 !  65:  976c2b5c1 Hugo'ify the \"credential helpers\" page\n    @@ layouts/partials/ref/category.html\n      <ul class='unstyled'>\n        {{ range $doc := $category.commands }}\n        <li>\n    --    <a href=\"{{ print \"docs/\" $doc.doc (cond (and (ne .Params.lang nil) (ne $doc.no_append_lang true)) (print \"/\" .Params.lang) \"\") }}\">\n    +-    <a href=\"{{ print \"/docs/\" $doc.doc (cond (and (ne $.Page.Params.lang nil) (ne $doc.no_append_lang true) (isset (index $.Site.Data.docs.pages $doc.doc) \"languages\") (isset (index $.Site.Data.docs.pages $doc.doc \"languages\") $.Page.Params.lang)) (print \"/\" $.Page.Params.lang) \"\") }}\">\n     +    {{ if (eq $doc.doc \"credential-helpers\") }}\n     +      <a href=\"doc/credential-helpers\">\n     +    {{ else }}\n    -+      <a href=\"{{ print \"docs/\" $doc.doc (cond (and (ne .Params.lang nil) (ne $doc.no_append_lang true)) (print \"/\" .Params.lang) \"\") }}\">\n    ++      <a href=\"{{ print \"/docs/\" $doc.doc (cond (and (ne $.Page.Params.lang nil) (ne $doc.no_append_lang true) (isset (index $.Site.Data.docs.pages $doc.doc) \"languages\") (isset (index $.Site.Data.docs.pages $doc.doc \"languages\") $.Page.Params.lang)) (print \"/\" $.Page.Params.lang) \"\") }}\">\n     +    {{ end }}\n            {{ if (eq $doc.title nil) }}\n              {{ replace $doc.doc \"git-\" \"\" }}\n 70:  00a42808e =  66:  ccecdf517 Move the search results page\n 71:  5a183e236 !  67:  00d2b544f Implement client-side search using Pagefind\n    @@ Commit message\n\n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n\n    + ## .github/workflows/ci.yml ##\n    +@@ .github/workflows/ci.yml: jobs:\n    +     steps:\n    +     - uses: actions/checkout@v4\n    +\n    +-    - name: configure Hugo version\n    ++    - name: configure Hugo and Pagefind version\n    +       run: |\n    +         set -x &&\n    +         echo \"HUGO_VERSION=$(sed -n 's/^ *hugo_version: *//p' <hugo.yml)\" >>$GITHUB_ENV\n    ++        echo \"PAGEFIND_VERSION=$(sed -n 's/^ *pagefind_version: *//p' <hugo.yml)\" >>$GITHUB_ENV\n    +\n    +     - name: install Hugo ${{ env.HUGO_VERSION }}\n    +       run: |\n    +@@ .github/workflows/ci.yml: jobs:\n    +     - name: run Hugo to build the pages\n    +       run: hugo\n    +\n    ++    - name: run Pagefind ${{ env.PAGEFIND_VERSION }} to build the search index\n    ++      run: npx -y pagefind@${{ env.PAGEFIND_VERSION }} --site public\n    ++\n    +     - name: build tar archive\n    +       run: cd public && tar czvf ../pages.tar.gz *\n    +\n    +\n      ## README.md ##\n    -@@ README.md: You'll need the extended version of [Hugo](https://gohugo.io/). On Windows, we r\n    +@@ README.md: To render the site locally, you'll need the extended version of [Hugo](https://g\n\n      The site should be running on http://127.0.0.1:1313. Note that it may be advisable to do this in a sparse checkout that excludes large parts of `content/`, to speed up the rendering time.\n\n    @@ README.md: You'll need the extended version of [Hugo](https://gohugo.io/). On Wi\n\n      (TODO!)\n     @@ README.md: The [list of GUI clients](https://git-scm.com/downloads/guis) has been construct\n    -\n    - 2. Add the image to `static/images/guis/<GUI_CLIENT_NAME>@2x.png` and `static/images/guis/<GUI_CLIENT_NAME>.png` making sure the aspect ratio matches a 588:332 image.\n    -\n    --## Useful link regarding working with Hugo\n    -+## Useful links regarding working with Hugo and Pagefind\n    -\n      * https://gohugo.io/\n      * https://gohugo.io/content-management/shortcodes/\n    -+* https://pagefind.app/\n\n    ++### Pagefind (client-side search)\n    ++\n    ++* https://pagefind.app/\n    ++\n      ## License\n\n    + The source code for the site is licensed under the MIT license, which you can find in\n\n      ## assets/sass/search.scss ##\n     @@\n    @@ content/search/results.html\n     +  <ol class=\"full-search-results\"></ol>\n      </div>\n\n    + ## hugo.yml ##\n    +@@ hugo.yml: markup:\n    +       unsafe: true\n    + params:\n    +   hugo_version: 0.134.1\n    ++  pagefind_version: 1.1.1\n    +   latest_version: 2.46.0\n    +   latest_relnote_url: https://raw.github.com/git/git/master/Documentation/RelNotes/2.46.0.txt\n    +   latest_release_date: '2024-07-29'\n    +\n      ## static/js/application.js ##\n     @@ static/js/application.js: var Search = {\n        selectedIndex: 0,\n 72:  1c55e8995 !  68:  fd440ba66 Implement \"live search\" using Pagefind\n    @@ static/js/application.js: var Search = {\n     -    })\n     +    (async () => {\n     +      Search.pagefind = await import(`${baseURLPrefix}pagefind/pagefind.js`);\n    -+      Search.pagefind.init();\n    ++      await Search.pagefind.init();\n     +      await callback();\n     +    })().catch(console.log);\n        },\n 73:  99046a762 =  69:  03d63b90d Mark pages with the correct language\n 74:  3d70c728b !  70:  a12108c7f Direct Pagefind what parts of the pages to index\n    @@ layouts/_default/baseof.html\n              {{ partial \"sidebar.html\" . }}\n     -        <div id=\"content\">\n     +        <div id=\"content\" data-pagefind-body>\n    -           {{ if isset .Params \"video_title\" }}\n    - \t    <div id=\"main\">\n    -               <h1>{{ .Params.category }} Episode {{ .Params.episode }}</h1>\n    +           {{ if (eq .Page.Path \"/docs\") }}\n    +             {{ partial \"ref/index.html\" . }}\n    +           {{ else if isset .Params \"video_title\" }}\n 75:  1b6bb3c10 =  71:  23433ad6d Live search: only load 10 results at a time\n 76:  cd28ebbe1 !  72:  a688de393 Respect the language in non-live search\n    @@ static/js/application.js: var Search = {\n          }\n          (async () => {\n            Search.pagefind = await import(`${baseURLPrefix}pagefind/pagefind.js`);\n    ++      const options = {}\n     +      const language = this.getQueryValue('language');\n    -+      if (language) Search.pagefind.options({language});\n    -       Search.pagefind.init();\n    ++      if (language) options.language = language;\n    ++      await Search.pagefind.options(options);\n    +       await Search.pagefind.init();\n            await callback();\n          })().catch(console.log);\n 77:  338a2e6c0 !  73:  2a357df65 Use Pagefind's UI for the full search\n    @@ layouts/_default/baseof.html\n        <link href='/favicon.ico' rel='shortcut icon' type='image/x-icon'>\n\n     +  {{ if eq $section \"search\" }}<link href=\"{{ relURL \"pagefind/pagefind-ui.css\" }}\" rel=\"stylesheet\">{{ end }}\n    -   {{ $style := resources.Get \"sass/application.scss\" | resources.ExecuteAsTemplate \"application.scss\" . | resources.ToCSS | resources.Minify }}\n    +   {{ $style := resources.Get \"sass/application.scss\" | resources.ExecuteAsTemplate \"application.scss\" . | css.Sass | resources.Minify }}\n        <link rel=\"stylesheet\" href=\"{{ $style.RelPermalink }}\">\n        <script src=\"/js/modernizr.js\"></script>\n\n    @@ layouts/partials/header.html\n     +\n     +  {{ if ne (.Scratch.Get \"section\") \"search\" }}\n        <form id=\"search\" action=\"/search/results\">\n    -     <input id=\"search-text\" name=\"search\" placeholder=\"Search entire site...\" autocomplete=\"off\" type=\"text\" />\n    +     <input id=\"search-text\" name=\"search\" placeholder=\"Type / to search entire site…\" autocomplete=\"off\" type=\"text\" />\n        </form>\n        <div id=\"search-results\"></div>\n     +  {{ end }}\n    @@ static/js/application.js: var Search = {\n     -    this.initializeSearchIndex(async () => {\n     -      const results = await Search.pagefind.search(searchTerm);\n     -      if (!results || !results.results || !results.results.length) return;\n    -+    new PagefindUI({ element: \"#search-div\", showSubResults: true, language });\n    -\n    +-\n     -      const list = (await Promise.all(results.results.map(async e => {\n     -        const result = await e.data();\n     -        const href = result.url;\n    @@ static/js/application.js: var Search = {\n     -          <a class=\"url\" href=\"${href}\">${href}</a>\n     -          <p>${result.excerpt}</p></li>`;\n     -      }))).join('');\n    --\n    ++    new PagefindUI({\n    ++      element: \"#search-div\",\n    ++      showSubResults: true,\n    ++      language\n    ++    });\n    +\n     -      searchResultsElements[0].innerHTML = list || '<li>No results found</li>';\n     -    })\n     +    const searchTerm = this.getQueryValue('search');\n  -:  --------- >  74:  c7ff8dad0 Add a helper script to run Pagefind\n  -:  --------- >  75:  c32fdf3dc ci: verify the order of the search results\n 78:  8147144aa =  76:  26ff8cb61 Implement the `relurl` shortcode\n 79:  61d1610e4 !  77:  c8a20dffc Use relative URLs\n    @@ content/community/_index.html: aliases:\n        <h2> Contributing to Git </h2>\n\n        <p>\n    --    The <a href=\"https://github.com/git/git/tree/master/Documentation\">Documentation directory</a> in the Git source code has several files of interest to developers who are looking to help contribute. After reading the <a href=\"https://github.com/git/git/blob/master/Documentation/CodingGuidelines\">coding guidelines</a>, you can learn <a href=\"/docs/SubmittingPatches\">how to submit patches</a>. If you are just starting out, you can read the <a href=\"/docs/MyFirstContribution\">My First Contribution tutorial</a>. For those looking to get more deeply involved, there is a <a href=\"https://github.com/git/git/blob/master/Documentation/howto/maintain-git.txt\">howto for Git maintainers</a>.\n    -+    The <a href=\"https://github.com/git/git/tree/master/Documentation\">Documentation directory</a> in the Git source code has several files of interest to developers who are looking to help contribute. After reading the <a href=\"https://github.com/git/git/blob/master/Documentation/CodingGuidelines\">coding guidelines</a>, you can learn <a href=\"{{< relurl \"docs/SubmittingPatches\" >}}\">how to submit patches</a>. If you are just starting out, you can read the <a href=\"{{< relurl \"docs/MyFirstContribution\" >}}\">My First Contribution tutorial</a>. For those looking to get more deeply involved, there is a <a href=\"https://github.com/git/git/blob/master/Documentation/howto/maintain-git.txt\">howto for Git maintainers</a>.\n    +-    The <a href=\"https://github.com/git/git/tree/master/Documentation\">Documentation directory</a> in the Git source code has several files of interest to developers who are looking to help contribute. After reading the <a href=\"https://github.com/git/git/blob/master/Documentation/CodingGuidelines\">coding guidelines</a> and <a href=\"https://github.com/git/git/blob/master/CODE_OF_CONDUCT.md\">code of conduct</a>, you can learn <a href=\"/docs/SubmittingPatches\">how to submit patches</a>. If you are just starting out, you can read the <a href=\"/docs/MyFirstContribution\">My First Contribution tutorial</a>. For those looking to get more deeply involved, there is a <a href=\"https://github.com/git/git/blob/master/Documentation/howto/maintain-git.txt\">howto for Git maintainers</a>.\n    ++    The <a href=\"https://github.com/git/git/tree/master/Documentation\">Documentation directory</a> in the Git source code has several files of interest to developers who are looking to help contribute. After reading the <a href=\"https://github.com/git/git/blob/master/Documentation/CodingGuidelines\">coding guidelines</a> and <a href=\"https://github.com/git/git/blob/master/CODE_OF_CONDUCT.md\">code of conduct</a>, you can learn <a href=\"{{< relurl \"docs/SubmittingPatches\" >}}\">how to submit patches</a>. If you are just starting out, you can read the <a href=\"{{< relurl \"docs/MyFirstContribution\" >}}\">My First Contribution tutorial</a>. For those looking to get more deeply involved, there is a <a href=\"https://github.com/git/git/blob/master/Documentation/howto/maintain-git.txt\">howto for Git maintainers</a>.\n        </p>\n\n        <p>\n    @@ content/doc/_index.html: aliases:\n\n        </div>\n\n    - ## content/docs/_index.html ##\n    -@@ content/docs/_index.html: aliases:\n    -     </p>\n    -   </div>\n    -   <div class=\"callout all-commands\">\n    --    <a href=\"/docs/git#_git_commands\">Complete list of all commands</a>\n    -+    <a href=\"{{< relurl \"docs/git#_git_commands\" >}}\">Complete list of all commands</a>\n    -   </div>\n    -   <div class='reference-menu'>\n    -     <div class='two-column'>\n    -\n      ## content/downloads/_index.html ##\n     @@ content/downloads/_index.html: aliases:\n              <table class=\"binaries\">\n    @@ layouts/_default/baseof.html\n     +  <link href=\"{{ relURL \"favicon.ico\" }}\" rel='shortcut icon' type='image/x-icon'>\n\n        {{ if eq $section \"search\" }}<link href=\"{{ relURL \"pagefind/pagefind-ui.css\" }}\" rel=\"stylesheet\">{{ end }}\n    -   {{ $style := resources.Get \"sass/application.scss\" | resources.ExecuteAsTemplate \"application.scss\" . | resources.ToCSS | resources.Minify }}\n    +   {{ $style := resources.Get \"sass/application.scss\" | resources.ExecuteAsTemplate \"application.scss\" . | css.Sass | resources.Minify }}\n        <link rel=\"stylesheet\" href=\"{{ $style.RelPermalink }}\">\n     -  <script src=\"/js/modernizr.js\"></script>\n     -  <script src=\"/js/modernize.js\"></script>\n    @@ layouts/partials/header.html\n        {{ if ne (.Scratch.Get \"section\") \"search\" }}\n     -  <form id=\"search\" action=\"/search/results\">\n     +  <form id=\"search\" action=\"{{ relURL \"search/results\" }}\">\n    -     <input id=\"search-text\" name=\"search\" placeholder=\"Search entire site...\" autocomplete=\"off\" type=\"text\" />\n    +     <input id=\"search-text\" name=\"search\" placeholder=\"Type / to search entire site…\" autocomplete=\"off\" type=\"text\" />\n        </form>\n        <div id=\"search-results\"></div>\n\n    @@ layouts/partials/ref/category.html\n     -      <a href=\"doc/credential-helpers\">\n     +      <a href=\"{{ relURL \"doc/credential-helpers\" }}\">\n          {{ else }}\n    --      <a href=\"{{ print \"docs/\" $doc.doc (cond (and (ne .Params.lang nil) (ne $doc.no_append_lang true)) (print \"/\" .Params.lang) \"\") }}\">\n    -+      <a href=\"{{ relURL (print \"docs/\" $doc.doc (cond (and (ne .Params.lang nil) (ne $doc.no_append_lang true)) (print \"/\" .Params.lang) \"\")) }}\">\n    +-      <a href=\"{{ print \"/docs/\" $doc.doc (cond (and (ne $.Page.Params.lang nil) (ne $doc.no_append_lang true) (isset (index $.Site.Data.docs.pages $doc.doc) \"languages\") (isset (index $.Site.Data.docs.pages $doc.doc \"languages\") $.Page.Params.lang)) (print \"/\" $.Page.Params.lang) \"\") }}\">\n    ++      <a href=\"{{ relURL (print \"docs/\" $doc.doc (cond (and (ne $.Page.Params.lang nil) (ne $doc.no_append_lang true) (isset (index $.Site.Data.docs.pages $doc.doc) \"languages\") (isset (index $.Site.Data.docs.pages $doc.doc \"languages\") $.Page.Params.lang)) (print \"/\" $.Page.Params.lang) \"\")) }}\">\n          {{ end }}\n            {{ if (eq $doc.title nil) }}\n              {{ replace $doc.doc \"git-\" \"\" }}\n\n    + ## layouts/partials/ref/index.html ##\n    +@@\n    +     </p>\n    +   </div>\n    +   <div class=\"callout all-commands\">\n    +-    <a href=\"/docs/git#_git_commands\">Complete list of all commands</a>\n    ++    <a href=\"{{ relURL \"docs/git#_git_commands\" }}\">Complete list of all commands</a>\n    +   </div>\n    +   <div class='reference-menu'>\n    +     <div class='two-column'>\n    +\n      ## layouts/partials/sidebar.html ##\n     @@\n        <nav>\n 80:  199ad72ad =  78:  943375d44 Accommodate for base URLs other than `/`\n 81:  905f3d1b1 !  79:  312d96605 ci: ensure that there are only relative URLs\n    @@ .github/workflows/ci.yml: jobs:\n     +          exit 1\n     +        fi\n     +\n    -     - name: run Pagefind to build the search index\n    -       run: npx -y pagefind --site public\n    +     - name: run Pagefind ${{ env.PAGEFIND_VERSION }} to build the search index\n    +       run: npx -y pagefind@${{ env.PAGEFIND_VERSION }} --site public\n\n 82:  efccaef38 =  80:  f54c9f6af Drop the Rails version of the ProGit book\n 83:  046ae69cf =  81:  05158a866 Move the ProGit book related Ruby code to a new location\n 84:  1b6c1ef2c !  82:  34b4c74b9 Transmogrify the Rake script to generate the Pro Git book sections\n    @@ README.md: Now you need to get the latest downloads for the downloads pages:\n     +If you have 2FA enabled, you'll need to create a [Personal Access Token](https://help.github.com/articles/creating-an-access-token-for-command-line-use/).\n\n     -Alternatively, you can get the book content from a repository on your computer by specifying the path in the `GENPATH` environment variable to the `local_genbook2` target:\n    -+If you want to build the book for all available languages, just skip the language code:\n    ++If you want to build the book for all available languages, just omit the language code parameter:\n\n     -    $ GENLANG=fr GENPATH=../progit2-fr rake local_genbook2\n     +    $ ruby ./script/update-book2.rb\n    @@ README.md: Now you need to get the latest downloads for the downloads pages:\n      ## Contributing\n\n\n    + ## hugo.yml ##\n    +@@ hugo.yml: markup:\n    +   goldmark:\n    +     renderer:\n    +       unsafe: true\n    ++module:\n    ++  mounts:\n    ++  - source: content\n    ++    target: content\n    ++  - source: static\n    ++    target: static\n    ++  - source: data\n    ++    target: data\n    ++  - source: external/book/data/book\n    ++    target: data/book\n    ++  - source: external/book/content/book\n    ++    target: content/book\n    ++  - source: external/book/static/book\n    ++    target: static/book\n    + params:\n    +   hugo_version: 0.134.1\n    +   pagefind_version: 1.1.1\n    +\n      ## layouts/_default/baseof.html ##\n     @@\n            </div> <!-- .inner -->\n    @@ script/book.rb: class Book < ApplicationRecord\n     +    }\n     +  end\n     +\n    ++  def content_note\n    ++    \"### DO NOT EDIT! Generated by script/update-book2.rb\"\n    ++  end\n    ++\n    ++  def wrap_front_matter(front_matter)\n    ++    \"#{front_matter.to_yaml.sub(\"---\\n\", \"---\\n#{self.content_note}\\n\")}---\\n\"\n    ++  end\n    ++\n     +  def absolute_path(path)\n    -+    File.absolute_path(File.join(File.dirname(__FILE__), \"..\", \"content\", \"book\", @language_code, \"v#{@edition}\", path))\n    ++    File.absolute_path(File.join(File.dirname(__FILE__), \"..\", \"external\", \"book\", \"content\", \"book\", @language_code, \"v#{@edition}\", path))\n     +  end\n     +\n     +  def removeAllFiles\n    @@ script/book.rb: class Book < ApplicationRecord\n     +    return front_matter\n     +  end\n     +\n    ++  def wrap_front_matter(front_matter)\n    ++    @book.wrap_front_matter(front_matter)\n    ++  end\n    ++\n     +  attr_accessor :title\n     +  attr_accessor :chapter_type\n     +  attr_accessor :chapter_number\n    @@ script/book.rb: class Book < ApplicationRecord\n     +  def save\n     +    return if self.slug.nil?\n     +\n    -+    front_matter = \"#{self.front_matter.to_yaml}\\n---\\n\"\n    -+\n     +    path = self.absolute_path(self.slug)\n     +    FileUtils.mkdir_p(File.dirname(path))\n     +    File.open(\"#{path}.html\", 'w') do |file|\n    -+      file.write(front_matter)\n    ++      file.write(@chapter.wrap_front_matter(self.front_matter))\n     +      file.write(self.html.strip)\n     +    end\n        end\n 85:  cdb7f4377 !  83:  803869e33 update-book2: adjust navigation\n    @@ layouts/shortcodes/previous-section.html (new)\n\n      ## script/book.rb ##\n     @@ script/book.rb: class Book\n    -     File.absolute_path(File.join(File.dirname(__FILE__), \"..\", \"content\", \"book\", @language_code, \"v#{@edition}\", path))\n    +     File.absolute_path(File.join(File.dirname(__FILE__), \"..\", \"external\", \"book\", \"content\", \"book\", @language_code, \"v#{@edition}\", path))\n        end\n\n     +  def relative_url(path)\n 86:  44c163eaa !  84:  f6734888d update-book2: also include the images\n    @@ Commit message\n\n      ## script/book.rb ##\n     @@ script/book.rb: class Book\n    -     }\n    +     \"#{front_matter.to_yaml.sub(\"---\\n\", \"---\\n#{self.content_note}\\n\")}---\\n\"\n        end\n\n     -  def absolute_path(path)\n    --    File.absolute_path(File.join(File.dirname(__FILE__), \"..\", \"content\", \"book\", @language_code, \"v#{@edition}\", path))\n    +-    File.absolute_path(File.join(File.dirname(__FILE__), \"..\", \"external\", \"book\", \"content\", \"book\", @language_code, \"v#{@edition}\", path))\n     +  def absolute_path(path, top_level = \"content\")\n    -+    File.absolute_path(File.join(File.dirname(__FILE__), \"..\", top_level, \"book\", @language_code, \"v#{@edition}\", path))\n    ++    File.absolute_path(File.join(File.dirname(__FILE__), \"..\", \"external\", \"book\", top_level, \"book\", @language_code, \"v#{@edition}\", path))\n        end\n\n        def relative_url(path)\n 87:  7d0edee4b =  85:  dcff0ad8c update-book2: show the chapter/section numbers in the titles\n 88:  eb4e84dfd !  86:  91531feee Generate the front page of the Pro Git book\n    @@ script/book.rb: class Book\n     +      front_matter[\"book\"][\"ebook_mobi\"] = self.ebook_mobi\n     +    end\n     +\n    -+    front_matter = \"#{front_matter.to_yaml}\\n---\"\n    -+\n     +    path = self.absolute_path(\"_index.html\")\n     +    FileUtils.mkdir_p(File.dirname(path))\n     +    File.open(path, 'w') do |file|\n    -+      file.write(front_matter)\n    ++      file.write(self.wrap_front_matter(front_matter))\n     +    end\n     +\n    ++    front_matter = { \"redirect_to\" => \"book/#{@language_code}/v#{@edition}\" }\n     +    File.open(self.absolute_path(\"../_index.html\"), 'w') do |file|\n    -+      file.write(\"---\\nredirect_to: \\\"book/#{@language_code}/v#{@edition}\\\"\\n---\\n\")\n    ++      file.write(self.wrap_front_matter(front_matter))\n     +    end\n    ++\n     +    if @language_code == \"en\"\n     +      File.open(self.absolute_path(\"../../_index.html\"), 'w') do |file|\n    -+        file.write(\"---\\nredirect_to: \\\"book/#{@language_code}/v#{@edition}\\\"\\n---\\n\")\n    ++        file.write(self.wrap_front_matter(front_matter))\n     +      end\n     +    end\n     +  end\n 89:  714b23ec6 !  87:  419be615f book: also generate the URLs for the downloadable formats of the book\n    @@ Commit message\n         ... but do this only for the English version, as the other versions are\n         not available in downloadable formats.\n\n    +    As the idea is to run this script in a GitHub workflow on a shallow\n    +    clone, let's be prepared for the situation where the tip commit of the\n    +    default branch is _not_ (yet?) tagged and therefore no tag is present in\n    +    the clone. In this situation, let's just go ahead and fetch all tags,\n    +    then move along and update the URLs accordingly.\n    +\n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n\n      ## script/update-book2.rb ##\n    @@ script/update-book2.rb: def local_genbook2(language_code, worktree_path)\n          end\n     +    if language_code == 'en'\n     +      latest_tag = `git -C \"#{worktree_path}\" for-each-ref --format '%(refname:short)' --sort=-committerdate --count=1 refs/tags/`.chomp\n    ++      if latest_tag.empty?\n    ++        puts \"No tag found in #{worktree_path}, trying to fetch tags\"\n    ++        latest_tag = `git -C \"#{worktree_path}\" fetch --tags origin && git -C \"#{worktree_path}\" for-each-ref --format '%(refname:short)' --sort=-committerdate --count=1 refs/tags/`.chomp\n    ++\traise \"Still no tags in #{worktree_path}?\" if latest_tag.empty?\n    ++      end\n     +      book.ebook_pdf = \"https://github.com/progit/progit2/releases/download/#{latest_tag}/progit.pdf\"\n     +      book.ebook_epub = \"https://github.com/progit/progit2/releases/download/#{latest_tag}/progit.epub\"\n     +      book.ebook_mobi = \"https://github.com/progit/progit2/releases/download/#{latest_tag}/progit.mobi\"\n 90:  68e96e524 <   -:  --------- Record SHA of the Pro Git book even when generating it locally\n  -:  --------- >  88:  406254667 update-book2.rb: gracefully warn about missing files, but continue\n  -:  --------- >  89:  9f7d416f5 Record SHA of the Pro Git book even when generating it locally\n 91:  915329bc2 !  90:  e6e83c127 Pro Git book: populate the `Chapters` drop-down\n    @@ layouts/_default/baseof.html\n            {{ partial \"footer.html\" . }}\n          </div> <!-- #content-wrapper -->\n        {{ else if (isset .Params \"book\") }}\n    -+    {{ .Scratch.Set \"book\" (index .Site.Data (print \"book-\" .Params.book.language_code)) }}\n    ++    {{ .Scratch.Set \"book\" (index .Site.Data.book .Params.book.language_code) }}\n          <div class=\"inner\">\n            <div id=\"content-wrapper\">\n              {{ partial \"sidebar.html\" . }}\n    @@ script/book.rb: class Book\n     +      \"language_code\" => @language_code,\n     +      \"chapters\" => chapters\n     +    }\n    -+    path = File.join(File.dirname(__FILE__), \"..\", \"data\", \"book-#{@language_code}.yml\")\n    ++    path = File.join(File.dirname(__FILE__), \"..\", \"external\", \"book\", \"data\", \"book\", \"#{@language_code}.yml\")\n     +    FileUtils.mkdir_p(File.dirname(path))\n     +    File.open(path, 'w') do |file|\n    ++      file.write(\"#{self.content_note}\\n\")\n     +      file.write(data.to_yaml.strip)\n     +    end\n     +\n 92:  6156ecfdd =  91:  1a51fcc94 book: keep colons and other special characters in URLs\n 93:  2dd98a59e =  92:  e492c9c63 book: fix translated labels\n 94:  289ee87b7 !  93:  7f3b191a3 book: generate cross-references correctly\n    @@ script/book.rb: class Book\n\n        def front_matter\n     @@ script/book.rb: class Book\n    -         file.write(\"---\\nredirect_to: \\\"book/#{@language_code}/v#{@edition}\\\"\\n---\\n\")\n    +         file.write(self.wrap_front_matter(front_matter))\n            end\n          end\n     +\n    @@ script/book.rb: class Book\n     +    @xrefs.each do |id_xref, section|\n     +      path = self.absolute_path(\"ch00/#{id_xref}.html\")\n     +      relurl = \"#{section.relative_url(nil)}##{id_xref}\"\n    ++      front_matter = { \"redirect_to\" => relurl }\n     +      File.open(path, 'w') do |file|\n    -+        file.write(\"---\\nredirect_to: \\\"#{relurl}\\\"\\n---\\n\")\n    ++        file.write(self.wrap_front_matter(front_matter))\n     +      end\n     +    end\n        end\n 95:  ded726a6c =  94:  4a31646bc book-tl: work around an incorrect image reference\n 96:  4078053ef =  95:  01b1dd71f book: work around lagging translations' image paths\n 97:  8f4d3d8a3 !  96:  731b6b686 book(xrefs): maintain question marks that are part of the URL path\n    @@ Commit message\n         sections' titles; These need to be (URL-)encoded in the URL and not be\n         mistaken for the separator between path and GET parameter(s).\n\n    +    While at it, also install redirects for URLs containing a question mark\n    +    so that incorrect URLs that contain a literal question mark (which is\n    +    interpreted as separator between the URL path and the `GET`\n    +    parameter(s)) redirect to the correct ones.\n    +\n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n\n      ## script/book.rb ##\n    @@ script/book.rb: class Section\n          }\n          if @slug =~ /:|[^-A-Za-z0-9_]/\n     -      front_matter[\"url\"] = self.relative_url(@slug)\n    -+      front_matter[\"url\"] = self.relative_url(@slug).gsub(/%3F/, '?')\n    ++      relative_url = self.relative_url(@slug)\n    ++      front_matter[\"url\"] = \"/#{relative_url.gsub(/%3F/, '?')}.html\"\n    ++      if relative_url =~ /%3F/\n    ++        front_matter[\"aliases\"] = [\n    ++\t  \"/#{relative_url.gsub(/%3F.*/, '')}.html\"\n    ++\t]\n    ++      end\n          end\n          return front_matter\n        end\n 98:  b99c5d1d5 !  97:  0343bf0ab book: redirect missing xrefs to the English book\n    @@ script/book.rb: class Book\n          @xrefs.each do |id_xref, section|\n            path = self.absolute_path(\"ch00/#{id_xref}.html\")\n     -      relurl = \"#{section.relative_url(nil)}##{id_xref}\"\n    +-      front_matter = { \"redirect_to\" => relurl }\n     +      if section == 'redirect-to-en'\n     +        url = \"book/en/v2/ch00/#{id_xref}\"\n     +      else\n     +        url = \"#{section.relative_url(nil)}##{id_xref}\"\n     +      end\n    ++      front_matter = { \"redirect_to\" => url }\n            File.open(path, 'w') do |file|\n    --        file.write(\"---\\nredirect_to: \\\"#{relurl}\\\"\\n---\\n\")\n    -+        file.write(\"---\\nredirect_to: \\\"#{url}\\\"\\n---\\n\")\n    +         file.write(self.wrap_front_matter(front_matter))\n            end\n    -     end\n    -   end\n\n      ## script/update-book2.rb ##\n     @@ script/update-book2.rb: def genbook(language_code, &get_content)\n 99:  c3f723764 =  98:  8b30c3748 book: handle footnotes gracefully\n100:  58dcc1d03 =  99:  bedb26d29 book: fix a couple of broken redirects\n101:  be1163d71 = 100:  f890fa149 docs: prepare for `index.rake` to become a proper Ruby script\n102:  73280317a ! 101:  990ac016c Turn what used to be the `index.rake` file into a proper Ruby script\n    @@ README.md: Note that this will take about 7 times as long, and the site will not\n      Similarly, you can also populate the localized man pages. From a local clone of https://github.com/jnavila/git-html-l10n :\n\n\n    + ## hugo.yml ##\n    +@@ hugo.yml: module:\n    +     target: content/book\n    +   - source: external/book/static/book\n    +     target: static/book\n    ++  - source: external/docs/data\n    ++    target: data\n    ++  - source: external/docs/content\n    ++    target: content\n    + params:\n    +   hugo_version: 0.134.1\n    +   pagefind_version: 1.1.1\n    +\n      ## layouts/_default/baseof.html ##\n     @@\n            </div>\n    @@ script/update-docs.rb\n     +require_relative \"version\"\n     +\n     +SITE_ROOT = File.join(File.expand_path(File.dirname(__FILE__)), '../')\n    -+DATA_FILE = \"#{SITE_ROOT}data/docs.yml\"\n    ++DOCS_INDEX_FILE = \"#{SITE_ROOT}external/docs/content/docs/_index.html\"\n    ++DATA_FILE = \"#{SITE_ROOT}external/docs/data/docs.yml\"\n     +\n     +def read_data\n     +  if File.exists?(DATA_FILE)\n    @@ script/update-docs.rb\n\n      def make_asciidoc(content)\n        Asciidoctor::Document.new(content,\n    +@@ script/update-docs.rb: def make_asciidoc(content)\n    +                             doctype: \"book\")\n    + end\n    +\n    ++def content_note\n    ++  \"### DO NOT EDIT! Generated by script/update-docs.rb\\n\"\n    ++end\n    ++\n    ++def wrap_front_matter(front_matter)\n    ++  \"#{front_matter.to_yaml.sub(\"---\\n\", \"---\\n#{content_note}\\n\")}---\\n\"\n    ++end\n    ++\n    + def expand_l10n(path, content, get_f_content, categories)\n    +   content.gsub!(/include::(\\S+)\\.txt/) do |line|\n    +     line.gsub!(\"include::\", \"\")\n     @@ script/update-docs.rb: def expand_l10n(path, content, get_f_content, categories)\n      end\n\n    @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content)\n              next if doc_limit && path !~ /#{doc_limit}/\n\n     -        file = DocFile.where(name: docname).first_or_create\n    -+        doc_path = \"#{SITE_ROOT}content/docs/#{docname}\"\n    ++        doc_path = \"#{SITE_ROOT}external/docs/content/docs/#{docname}\"\n\n              puts \"   build: #{docname}\"\n\n    @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content)\n     +\n     +        FileUtils.mkdir_p(doc_path)\n     +        File.open(\"#{doc_path}/#{tagname}.html\", \"w\") do |out|\n    -+          out.write(\"#{front_matter.to_yaml}\\n---\\n\", out)\n    ++          out.write(wrap_front_matter(front_matter))\n     +          out.write(html, out)\n     +        end\n\n    @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content)\n     +          FileUtils.mkdir_p(File.dirname(doc_path))\n     +          File.open(\"#{doc_path}.html\", \"w\") do |out|\n     +            front_matter[\"aliases\"] = [\"/docs/#{docname}/index.html\"]\n    -+            out.write(\"#{front_matter.to_yaml}\\n---\\n\")\n    ++            out.write(wrap_front_matter(front_matter))\n     +            out.write(html)\n     +          end\n     +        end\n    @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content)\n     -    Rails.cache.write(\"latest-version\", Version.latest_version.name)\n     +    data[\"latest-version\"] = tagname\n     +  end\n    ++\n    ++  front_matter = {\n    ++    \"section\" => \"documentation\",\n    ++    \"subsection\" => \"reference\",\n    ++    \"title\" => \"Git - Reference\",\n    ++    \"url\" => \"/docs.html\",\n    ++    \"aliases\" => [\"/docs/index.html\"]\n    ++  }\n    ++\n    ++  File.open(DOCS_INDEX_FILE, \"w\") do |out|\n    ++    out.write(wrap_front_matter(front_matter))\n    ++  end\n    ++\n     +  File.open(DATA_FILE, \"w\") do |out|\n     +    YAML.dump(data, out)\n        end\n103:  e34a4915b = 102:  c7eb81c73 Highlight the `Reference` nav item for manual pages\n104:  42a90c22a ! 103:  5d5bfe56a update-docs: install redirects for the various versions\n    @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content)\n\n              FileUtils.mkdir_p(doc_path)\n     -        File.open(\"#{doc_path}/#{tagname}.html\", \"w\") do |out|\n    --          out.write(\"#{front_matter.to_yaml}\\n---\\n\", out)\n    +-          out.write(wrap_front_matter(front_matter))\n     -          out.write(html, out)\n     +        front_matter_with_redirects = front_matter.clone\n     +        front_matter_with_redirects[\"aliases\"] =\n    @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content)\n     +            [\"/docs/#{docname}/#{v}/index.html\"]\n     +          end\n     +        File.open(\"#{doc_path}/#{doc_versions[changed_in]}.html\", \"w\") do |out|\n    -+          out.write(\"#{front_matter_with_redirects.to_yaml}\\n---\\n\")\n    ++          out.write(wrap_front_matter(front_matter_with_redirects))\n     +          out.write(html)\n              end\n\n105:  6a68ca809 ! 104:  1fc1b2d4b Show the different versions of the manual pages\n    @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content)\n     -            anchor += \"-1\" while ids.include?(anchor)\n     -            ids.add(anchor)\n     -            \"<dt class=\\\"hdlist1\\\" id=\\\"#{anchor}\\\"> <a class=\\\"anchor\\\" href=\\\"##{anchor}\\\"></a>#{$1} </dt>\"\n    -+        if !File.exists?(\"#{SITE_ROOT}_generated-asciidoc/#{asciidoc_sha}\")\n    -+          FileUtils.mkdir_p(\"#{SITE_ROOT}_generated-asciidoc\")\n    -+          File.open(\"#{SITE_ROOT}_generated-asciidoc/#{asciidoc_sha}\", \"w\") do |out|\n    ++        if !File.exists?(\"#{SITE_ROOT}external/docs/asciidoc/#{asciidoc_sha}\")\n    ++          FileUtils.mkdir_p(\"#{SITE_ROOT}external/docs/asciidoc\")\n    ++          File.open(\"#{SITE_ROOT}external/docs/asciidoc/#{asciidoc_sha}\", \"w\") do |out|\n     +            out.write(content)\n                end\n              end\n    @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content)\n     +            page_data[\"diff-cache\"] = {} if !page_data[\"diff-cache\"]\n     +            cached_diff = page_data[\"diff-cache\"][\"#{pre_sha}..#{post_sha}\"]\n     +            if !cached_diff\n    -+              pre_content = File.read(\"#{SITE_ROOT}_generated-asciidoc/#{pre_sha}\")\n    -+              post_content = File.read(\"#{SITE_ROOT}_generated-asciidoc/#{post_sha}\")\n    ++              pre_content = File.read(\"#{SITE_ROOT}external/docs/asciidoc/#{pre_sha}\")\n    ++              post_content = File.read(\"#{SITE_ROOT}external/docs/asciidoc/#{post_sha}\")\n     +              cached_diff = page_data[\"diff-cache\"][\"#{pre_sha}..#{post_sha}\"] = diff(pre_content, post_content)\n     +            end\n     +            page_versions.unshift({\n    @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content)\n     -    data[\"latest-version\"] = tagname\n     +    data[\"latest-version\"] = version if !data[\"latest-version\"] || Version.version_to_num(data[\"latest-version\"]) < Version.version_to_num(version)\n        end\n    -   File.open(DATA_FILE, \"w\") do |out|\n    -     YAML.dump(data, out)\n    +\n    +   front_matter = {\n106:  1ab0c772b ! 105:  9953afacd search: exclude older manual page versions\n    @@ layouts/_default/baseof.html\n\n     -          <div id=\"main\" data-pagefind-body>\n     +          <!-- older manual page versions are less interesting and need to be excluded from the search -->\n    -+          {{ $include_in_search := (or (not (isset .Params \"docname\")) (isset .Params \"latest-changes\")) }}\n    ++          {{ $include_in_search := (or (not (isset .Params \"docname\")) (isset .Params \"latest-changes\") (isset .Params \"lang\")) }}\n     +          <div id=\"main\"{{ if $include_in_search }} data-pagefind-body{{ end }}>\n                  {{ .Content }}\n                </div>\n    @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content)\n                File.open(\"#{doc_path}.html\", \"w\") do |out|\n     +            front_matter[\"latest-changes\"] = version\n                  front_matter[\"aliases\"] = [\"/docs/#{docname}/index.html\"]\n    -             out.write(\"#{front_matter.to_yaml}\\n---\\n\")\n    +             out.write(wrap_front_matter(front_matter))\n                  out.write(html)\n107:  c1948441b ! 106:  ec44e3010 search: offer the section as a filter\n    @@ layouts/_default/baseof.html\n     @@\n\n                <!-- older manual page versions are less interesting and need to be excluded from the search -->\n    -           {{ $include_in_search := (or (not (isset .Params \"docname\")) (isset .Params \"latest-changes\")) }}\n    +           {{ $include_in_search := (or (not (isset .Params \"docname\")) (isset .Params \"latest-changes\") (isset .Params \"lang\")) }}\n     -          <div id=\"main\"{{ if $include_in_search }} data-pagefind-body{{ end }}>\n     +          <div id=\"main\"{{ if $include_in_search }} data-pagefind-filter=\"category:{{ $section }}\" data-pagefind-body{{ end }}>\n                  {{ .Content }}\n    @@ layouts/_default/baseof.html\n              {{ partial \"sidebar.html\" . }}\n     -        <div id=\"content\" data-pagefind-body>\n     +        <div id=\"content\" data-pagefind-filter=\"category:{{ $section }}\" data-pagefind-body>\n    -           {{ if isset .Params \"video_title\" }}\n    - \t    <div id=\"main\">\n    -               <h1>{{ .Params.category }} Episode {{ .Params.episode }}</h1>\n    +           {{ if (eq .Page.Path \"/docs\") }}\n    +             {{ partial \"ref/index.html\" . }}\n    +           {{ else if isset .Params \"video_title\" }}\n108:  ad3768be5 <   -:  --------- search: give the manual pages' titles maximal weight\n  -:  --------- > 107:  5e6f328ce search: give the manual pages' titles maximal weight\n109:  05a15df77 ! 108:  25906cd41 search: reintroduce the categories in the live search\n    @@ Commit message\n         We do this by specifically adding metadata indicating the category of\n         manual pages as well as for the sections of the book (for details, see\n         https://pagefind.app/docs/metadata/). Then, we use that information to\n    -    sort the results as they arrive into the correct table row (and\n    -    displaying it when necessary, as the empty rows are initially hidden).\n    +    sort the results into the correct table row. Due to the `async` nature\n    +    of the search results coming in, we need to be\n    +    careful to process them in order when doing that.\n\n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n\n    @@ layouts/_default/baseof.html\n                    2nd Edition\n                  </span>\n                </div>\n    --          <div id=\"main\" data-pagefind-filter=\"category:{{ $section }}\" data-pagefind-body class=\"book edition2\">\n    -+          <div id=\"main\" data-pagefind-filter=\"category:{{ $section }}\" data-pagefind-meta=\"category:Book\" data-pagefind-body class=\"book edition2\">\n    +-          <div id=\"main\" data-pagefind-filter=\"category:{{ $section }}\" data-pagefind-weight=\"0.05\" data-pagefind-body class=\"book edition2\">\n    ++          <div id=\"main\" data-pagefind-filter=\"category:{{ $section }}\" data-pagefind-meta=\"category:Book\" data-pagefind-weight=\"0.05\" data-pagefind-body class=\"book edition2\">\n                  <h1>{{ .Params.book.section.cs_number }} {{ .Params.book.chapter.title }} - {{ .Params.book.section.title }}</h1>\n                  <div>\n                    {{ .Content }}\n     @@\n\n                <!-- older manual page versions are less interesting and need to be excluded from the search -->\n    -           {{ $include_in_search := (or (not (isset .Params \"docname\")) (isset .Params \"latest-changes\")) }}\n    --          <div id=\"main\"{{ if $include_in_search }} data-pagefind-filter=\"category:{{ $section }}\" data-pagefind-body{{ end }}>\n    -+          <div id=\"main\"{{ if $include_in_search }} data-pagefind-filter=\"category:{{ $section }}\" data-pagefind-meta=\"category:Reference\" data-pagefind-body{{ end }}>\n    -             {{ safe.HTML (replaceRE \"(?s:>NAME</h2>.*?<p)\" \"$0 data-pagefind-weight=\\\"7\\\"\" .Content) }}\n    -           </div>\n    -         </div>\n    +           {{ $include_in_search := (or (not (isset .Params \"docname\")) (isset .Params \"latest-changes\") (isset .Params \"lang\")) }}\n    +-          <div id=\"main\"{{ if $include_in_search }} data-pagefind-filter=\"category:{{ $section }}\" data-pagefind-weight=\"0.05\" data-pagefind-body{{ end }}>\n    ++          <div id=\"main\"{{ if $include_in_search }} data-pagefind-filter=\"category:{{ $section }}\" data-pagefind-meta=\"category:Reference\" data-pagefind-weight=\"0.05\" data-pagefind-body{{ end }}>\n    +             {{ .Content }}\n    +             {{ $match := findRESubmatch \"(?s)>NAME</h2>.*?<p[^>]*>(git-)?([^ ]+)\" .Content 1 }}\n    +             {{ if (eq ($match | len) 1) }}\n\n      ## static/js/application.js ##\n     @@ static/js/application.js: var Search = {\n    @@ static/js/application.js: var Search = {\n     +\n                const chunkLength = 10;\n                let displayCount = 0;\n    ++\n    ++          const categorizeResult = (i) => {\n    ++            while (i < displayCount && typeof results.results[i].data === 'object') {\n    ++              const result = results.results[i++];\n    ++              if (result.data.meta.category === 'Reference') {\n    ++                if (ulReference.children().length === 0) ulReference.parent().parent().css(\"display\", \"table-row\")\n    ++                ulReference.append(result.li)\n    ++              } else if (result.data.meta.category === 'Book') {\n    ++                if (ulBook.children().length === 0) ulBook.parent().parent().css(\"display\", \"table-row\")\n    ++                ulBook.append(result.li)\n    ++              }\n    ++            }\n    ++          };\n    ++\n                const loadResultsChunk = () => {\n    -@@ static/js/application.js: var Search = {\n    +             if (loadButton.loading || displayCount >= results.results.length) return;\n    +\n    +             loadButton.loading = true;\n    +             const n = displayCount + chunkLength;\n    +             while (displayCount < n) {\n    +-              const li = $(\"<li><a>&hellip;</a></li>\");\n    +-              li.insertBefore(loadButton);\n    ++              const result = results.results[displayCount]\n    ++              result.li = $(\"<li><a>&hellip;</a></li>\");\n    ++              result.li.insertBefore(loadButton);\n    +\n                    // load the result lazily\n    -               (async () => {\n    -                 const result = await results.results[displayCount].data();\n    -+                if (result.meta.category === 'Reference') {\n    -+                  if (ulReference.children().length === 0) ulReference.parent().parent().css(\"display\", \"table-row\")\n    -+                  ulReference.append(li)\n    -+                } else if (result.meta.category === 'Book') {\n    -+                  if (ulBook.children().length === 0) ulBook.parent().parent().css(\"display\", \"table-row\")\n    -+                  ulBook.append(li)\n    -+                }\n    -                 li.html(`<a href = \"${result.url}\">${result.meta.title}</a>`);\n    -               })().catch(console.log);\n    +-              (async () => {\n    +-                const result = await results.results[displayCount].data();\n    +-                li.html(`<a href = \"${result.url}\">${result.meta.title}</a>`);\n    +-              })().catch(console.log);\n    ++              (async (i) => {\n    ++                result.data = await results.results[displayCount].data();\n    ++                if (!i || typeof results.results[i - 1].data === 'object') categorizeResult(i);\n    ++                result.li.html(`<a href = \"${result.data.url}\">${result.data.meta.title}</a>`);\n    ++              })(displayCount).catch((err) => {\n    ++                console.log(err);\n    ++                result.li.html(`<i>Error loading result</i>`);\n    ++              });\n\n    +               if (++displayCount >= results.results.length) {\n    +                 loadButton.remove();\n110:  49f58f182 <   -:  --------- search: show manual pages' short title in the live search\n  -:  --------- > 109:  4f239bc08 search: show manual pages' short title in the live search\n  -:  --------- > 110:  347830d09 search: link to \"pretty\" URLs\n111:  6fc0f8e9e = 111:  cb68f3611 docs: handle multiple `linkgit:` in the same line\n112:  1f9dc5079 = 112:  687183039 docs: correctly handle `{litdd}` in `linkgit` values\n113:  44dca56fe = 113:  2b061bf40 docs: handle `gitlink:` gracfully\n114:  702ce1711 = 114:  149872c37 docs: handle double colon in `linkgit::` gracefully\n115:  26a84723f = 115:  25d0d5463 docs: handle `linkgit:curl[1]` gracefully\n116:  9cb3ff53a = 116:  187843aba Allow `redirect_to` with fully-qualified URLs\n117:  412391840 ! 117:  f20b1c76d docs: add fall-back redirects for unrendered pages\n    @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content)\n     +      # that are not populated. Let's redirect to the source files in the\n     +      # git/git repository.\n     +      check_paths.each do |path|\n    -+        doc_path = \"#{SITE_ROOT}content/#{path}.html\"\n    ++        doc_path = \"#{SITE_ROOT}external/docs/content/#{path}.html\"\n     +        if !File.exists?(doc_path)\n     +          type = 'blob'\n     +          target = path.sub(/^docs\\//, '')\n    @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content)\n     +          front_matter = { \"redirect_to\" => \"https://github.com/git/git/#{type}/HEAD/Documentation/#{target}\" } # ltrim `docs/`\n     +          FileUtils.mkdir_p(File.dirname(doc_path))\n     +          File.open(doc_path, \"w\") do |out|\n    -+            out.write(\"#{front_matter.to_yaml}\\n---\\n\")\n    ++            out.write(wrap_front_matter(front_matter))\n     +          end\n     +        end\n     +      end\n118:  55c7b5f48 ! 118:  fcbfb1f1d docs: work around a `link:` bug in older versions\n    @@ Metadata\n     Author: Johannes Schindelin <johannes.schindelin@gmx.de>\n\n      ## Commit message ##\n    -    docs: work around a `link:` bug in older versions\n    +    docs: work around `link:`/`linkgit:` bugs in older versions\n\n         Since we want to build older Git versions' manual pages, too, we have to\n         work around bugs since fixed.\n    @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content)\n     +        # Handle erroneous `link:api-trace2.txt`, see 4945f046c7f5 (api docs:\n     +        # link to html version of api-trace2, 2022-09-16)\n     +        content.gsub!(/link:api-trace2.txt/, 'link:api-trace2.html')\n    ++\t# Handle `linkgit:git-config.txt` mistake, fixed in ad52148a7d0\n    ++\t# (Documentation: fix broken linkgit to git-config, 2016-03-21)\n    ++        content.gsub!(/linkgit:git-config.txt/, 'linkgit:git-config')\n              content.gsub!(/link:(?:technical\\/)?(\\S*?)\\.html(\\#\\S*?)?\\[(.*?)\\]/m, \"link:/docs/\\\\1\\\\2[\\\\3]\")\n\n              asciidoc = make_asciidoc(content)\n119:  a7344a9d2 ! 119:  5b1d692eb Migrate the translated manual pages to the Hugo world\n    @@ layouts/_default/baseof.html\n              <div id=\"content\">\n                <div id='reference-version'>\n     +            {{ partial \"ref/languages.html\" . }}\n    -+            {{ partialCached \"ref/topics.html\" . }}\n    ++            {{ partial \"ref/topics.html\" . }}\n                  {{ partial \"ref/versions.html\" . }}\n                </div>\n\n    @@ script/update-docs.rb: def index_l10n_doc(filter_tags, doc_list, get_content)\n            path = File.basename(full_path, \".txt\")\n\n     -      file = DocFile.where(name: path).first_or_create\n    -+      doc_path = \"#{SITE_ROOT}content/docs/#{path}\"\n    ++      doc_path = \"#{SITE_ROOT}external/docs/content/docs/#{path}\"\n\n            puts \"   build: #{path} for #{lang}\"\n\n    @@ script/update-docs.rb: def index_l10n_doc(filter_tags, doc_list, get_content)\n     -        html.gsub!(/linkgit:(\\S+?)\\[(\\d+)\\]/) do |line|\n     -          x = /^linkgit:(\\S+?)\\[(\\d+)\\]/.match(line)\n     -          \"<a href='/docs/#{x[1].gsub(/&#x2d;/, '-')}/#{lang}'>#{x[1]}[#{x[2]}]</a>\"\n    -+      if !File.exists?(\"#{SITE_ROOT}_generated-asciidoc/#{asciidoc_sha}\")\n    -+        FileUtils.mkdir_p(\"#{SITE_ROOT}_generated-asciidoc\")\n    -+        File.open(\"#{SITE_ROOT}_generated-asciidoc/#{asciidoc_sha}\", \"w\") do |out|\n    ++      if !File.exists?(\"#{SITE_ROOT}external/docs/asciidoc/#{asciidoc_sha}\")\n    ++        FileUtils.mkdir_p(\"#{SITE_ROOT}external/docs/asciidoc\")\n    ++        File.open(\"#{SITE_ROOT}external/docs/asciidoc/#{asciidoc_sha}\", \"w\") do |out|\n     +          out.write(content)\n              end\n     -        # HTML anchor on hdlist1 (i.e. command options)\n    @@ script/update-docs.rb: def index_l10n_doc(filter_tags, doc_list, get_content)\n     +\n     +      FileUtils.mkdir_p(doc_path)\n     +      File.open(\"#{doc_path}/#{lang}.html\", \"w\") do |out|\n    -+        out.write(\"#{front_matter.to_yaml}\\n---\\n\")\n    ++        out.write(wrap_front_matter(front_matter))\n     +        out.write(html)\n     +      end\n     +\n120:  dd2d5016c = 120:  b88d2bef6 docs(zh_HANS-CN): a trailing \"full stop\" character is not part of a URL\n121:  9c93bfdec <   -:  --------- redirect_to: keep the anchor, if any was specified\n  -:  --------- > 121:  968e6e25b redirect_to/aliases: keep the anchor, if any was specified\n122:  6ef6dbe2f ! 122:  f2e7a5ffa docs(translated): add redirects for missing files\n    @@ script/update-docs.rb: def index_l10n_doc(filter_tags, doc_list, get_content)\n     +      end\n            asciidoc = make_asciidoc(content)\n            asciidoc_sha = Digest::SHA1.hexdigest(asciidoc.source)\n    -       if !File.exists?(\"#{SITE_ROOT}_generated-asciidoc/#{asciidoc_sha}\")\n    +       if !File.exists?(\"#{SITE_ROOT}external/docs/asciidoc/#{asciidoc_sha}\")\n     @@ script/update-docs.rb: def index_l10n_doc(filter_tags, doc_list, get_content)\n            html.gsub!(/linkgit:(\\S+?)\\[(\\d+)\\]/) do |line|\n              x = /^linkgit:(\\S+?)\\[(\\d+)\\]/.match(line)\n    @@ script/update-docs.rb: def index_l10n_doc(filter_tags, doc_list, get_content)\n     +    # translated manual pages may point to other translated manual pages that do\n     +    # not exist. In these cases, redirect to the English version.\n     +    check_paths.each do |path|\n    -+      doc_path = \"#{SITE_ROOT}content/#{path}.html\"\n    ++      doc_path = \"#{SITE_ROOT}external/docs/content/#{path}.html\"\n     +      if !File.exists?(doc_path)\n     +        front_matter = { \"redirect_to\" => \"#{path.sub(/\\/[^\\/]*$/, '')}\" } # rtrim `/<lang>`\n     +        FileUtils.mkdir_p(File.dirname(doc_path))\n     +        File.open(doc_path, \"w\") do |out|\n    -+          out.write(\"#{front_matter.to_yaml}\\n---\\n\")\n    ++          out.write(wrap_front_matter(front_matter))\n     +        end\n     +      end\n     +    end\n123:  e7e038a32 ! 123:  19d453f94 ci: update the books via a GitHub workflow\n    @@ Commit message\n         This information is then used by a scheduled workflow to determine what\n         needs to be updated (if anything) and then performing that task.\n\n    +    When GitHub workflows push new changes, they cannot trigger other GitHub\n    +    workflows (to avoid infinite loops). Therefore, this new GitHub workflow\n    +    not only synchronizes the books, but also builds the site and deploys\n    +    it.\n    +\n    +    Note: The code to build the site and to deploy it is provided in a\n    +    custom Action, to make it reusable. It will come in handy over the next\n    +    commits, where other GitHub workflows are added that likewise need\n    +    to synchronize changes that desire a site rebuild & deployment.\n    +\n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n\n      ## .github/actions/deploy-to-github-pages/action.yml (new) ##\n    @@ .github/actions/deploy-to-github-pages/action.yml (new)\n     +\n     +    - name: setup GitHub Pages\n     +      id: pages\n    -+      uses: actions/configure-pages@v3\n    ++      uses: actions/configure-pages@v5\n     +\n    -+    - name: install Hugo\n    -+      env:\n    -+        HUGO_VERSION: 0.120.3\n    ++    - name: configure Hugo and Pagefind version\n    ++      shell: bash\n    ++      run: |\n    ++        set -x &&\n    ++        echo \"HUGO_VERSION=$(sed -n 's/^ *hugo_version: *//p' <hugo.yml)\" >>$GITHUB_ENV\n    ++        echo \"PAGEFIND_VERSION=$(sed -n 's/^ *pagefind_version: *//p' <hugo.yml)\" >>$GITHUB_ENV\n    ++\n    ++    - name: install Hugo ${{ env.HUGO_VERSION }}\n     +      shell: bash\n     +      run: |\n     +        set -x &&\n    @@ .github/actions/deploy-to-github-pages/action.yml (new)\n     +      env:\n     +        HUGO_RELATIVEURLS: false\n     +      shell: bash\n    -+      run: hugo --minify --baseURL \"${{ steps.pages.outputs.base_url }}/\"\n    ++      run: hugo config && hugo --minify --baseURL \"${{ steps.pages.outputs.base_url }}/\"\n     +\n    -+    - name: run Pagefind to build the search index\n    ++    - name: run Pagefind ${{ env.PAGEFIND_VERSION }} to build the search index\n     +      shell: bash\n    -+      run: npx -y pagefind --site public\n    ++      run: npx -y pagefind@${{ env.PAGEFIND_VERSION }} --site public\n     +\n     +    - name: upload GitHub Pages artifact\n    -+      uses: actions/upload-pages-artifact@v2\n    ++      uses: actions/upload-pages-artifact@v3\n     +      with:\n     +        path: ./public\n     +\n     +    - name: deploy\n     +      id: deploy\n    -+      uses: actions/deploy-pages@v2\n    ++      uses: actions/deploy-pages@v4\n\n      ## .github/workflows/update-book.yml (new) ##\n     @@\n    @@ .github/workflows/update-book.yml (new)\n     +  check-for-updates:\n     +    runs-on: ubuntu-latest\n     +    steps:\n    -+      - uses: actions/checkout@v3\n    ++      - uses: actions/checkout@v4\n     +        with:\n     +          sparse-checkout: |\n    -+            _sync_state\n    ++            external/book/sync\n     +            script\n    -+      - uses: actions/github-script@v6\n    ++      - uses: actions/github-script@v7\n     +        id: get-pending\n     +        with:\n     +          script: |\n    @@ .github/workflows/update-book.yml (new)\n     +        language: ${{ fromJson(needs.check-for-updates.outputs.matrix) }}\n     +      fail-fast: false\n     +    steps:\n    -+      - uses: actions/checkout@v3\n    ++      - uses: actions/checkout@v4\n     +        with:\n     +          sparse-checkout: |\n    -+            _sync_state\n     +            script\n    -+            data\n    -+            content/book/${{ matrix.language.lang }}\n    -+            static/book/${{ matrix.language.lang }}\n    ++            external/book/sync\n    ++            external/book/data\n    ++            external/book/content/book/${{ matrix.language.lang }}\n    ++            external/book/static/book/${{ matrix.language.lang }}\n     +      - name: clone ${{ matrix.language.repository }}\n     +        run: |\n     +          printf '%s\\n' /progit-clone/ /vendor >>.git/info/exclude &&\n    @@ .github/workflows/update-book.yml (new)\n     +      - name: commit changes\n     +        run: |\n     +          # record the commit hash\n    -+          mkdir -p _sync_state &&\n    -+          git -C progit-clone rev-parse HEAD >_sync_state/book-${{ matrix.language.lang }}.sha &&\n    ++          mkdir -p external/book/sync &&\n    ++          git -C progit-clone rev-parse HEAD >external/book/sync/book-${{ matrix.language.lang }}.sha &&\n     +\n     +          # commit it all\n    -+          git add -A \\\n    -+            _sync_state \\\n    -+            data/book-${{ matrix.language.lang }}.yml \\\n    -+            content/book &&\n    -+          # there might be images\n    -+          if test -d static/book\n    -+          then\n    -+            git add -A static/book\n    -+          fi &&\n    ++          git add -A external/book &&\n     +          git -c user.name=${{ github.actor }} \\\n     +            -c user.email=${{ github.actor }}@noreply.github.com \\\n     +            commit -m 'book: update ${{ matrix.language.lang }}' \\\n    @@ .github/workflows/update-book.yml (new)\n     +        run: |\n     +          git branch -m book-${{ matrix.language.lang }}\n     +          git bundle create ${{ matrix.language.lang }}.bundle refs/remotes/origin/${{ github.ref_name }}..book-${{ matrix.language.lang }}\n    -+      - uses: actions/upload-artifact@v3\n    ++      - uses: actions/upload-artifact@v4\n     +        with:\n     +          name: bundle-${{ matrix.language.lang }}\n     +          path: ${{ matrix.language.lang }}.bundle\n    @@ .github/workflows/update-book.yml (new)\n     +      url: ${{ steps.deploy.outputs.url }}\n     +    runs-on: ubuntu-latest\n     +    steps:\n    -+      - uses: actions/checkout@v3\n    -+      - uses: actions/download-artifact@v3\n    ++      - uses: actions/checkout@v4\n    ++      - uses: actions/download-artifact@v4\n     +      - name: apply updates\n     +        id: apply\n     +        run: |\n    @@ .github/workflows/update-book.yml (new)\n     +        id: deploy\n     +        uses: ./.github/actions/deploy-to-github-pages\n\n    - ## _sync_state/.gitignore (new) ##\n    -\n      ## script/ci-helper.js (new) ##\n     @@\n     +const fs = require('fs')\n    @@ script/ci-helper.js (new)\n     +  const result = []\n     +  for (const lang of Object.keys(books)) {\n     +    try {\n    -+      const localSha = await getFileContents(`_sync_state/book-${lang}.sha`)\n    ++      const localSha = await getFileContents(`external/book/sync/book-${lang}.sha`)\n     +\n     +      const [owner, repo] = books[lang].split('/')\n     +      const { data: { default_branch: remoteDefaultBranch } } =\n124:  c7c4d347d ! 124:  1ace765fc Update the download data via a GitHub workflow\n    @@ .github/workflows/update-download-data.yml (new)\n     +      name: github-pages\n     +      url: ${{ steps.deploy.outputs.url }}\n     +    steps:\n    -+      - uses: actions/checkout@v3\n    ++      - uses: actions/checkout@v4\n     +        with:\n     +          sparse-checkout: |\n     +            .github/actions\n    -+            _sync_state\n     +            script\n     +      - name: ruby setup\n     +        uses: ruby/setup-ruby@v1\n    @@ .github/workflows/update-download-data.yml (new)\n     +          git \\\n     +            -c user.name=${{ github.actor }} \\\n     +            -c user.email=${{ github.actor }}@noreply.github.com \\\n    -+            commit -m \"$commit_message\" -- hugo.yml\n    ++            commit -m \"$commit_message\" \\\n    ++              -m 'Updated via the `update-download-data.yml` GitHub workflow.' \\\n    ++              -- hugo.yml\n     +      - name: verify that there are no uncommitted changes\n     +        run: |\n     +          git update-index --refresh &&\n125:  3bd0e53a3 ! 125:  775c1d220 update-book: allow force-rebuilding\n    @@ .github/workflows/update-book.yml: jobs:\n                  const { getPendingBookUpdates } = require('./script/ci-helper.js')\n\n     -            const pending = await getPendingBookUpdates(github)\n    -+            const pending = await getPendingBookUpdates(github, ${{ inputs.force-rebuild }})\n    ++            const pending = await getPendingBookUpdates(github, ${{ inputs.force-rebuild == true }})\n                  // an empty matrix is invalid and makes the workflow run fail, unfortunately\n                  return pending.length ? pending : ['']\n            - name: ruby setup\n    @@ script/ci-helper.js: const getAllBooks = async () => {\n        const result = []\n        for (const lang of Object.keys(books)) {\n     -    try {\n    --      const localSha = await getFileContents(`_sync_state/book-${lang}.sha`)\n    +-      const localSha = await getFileContents(`external/book/sync/book-${lang}.sha`)\n     +    if (!forceRebuild) {\n     +      try {\n    -+        const localSha = await getFileContents(`_sync_state/book-${lang}.sha`)\n    ++        const localSha = await getFileContents(`external/book/sync/book-${lang}.sha`)\n\n     -      const [owner, repo] = books[lang].split('/')\n     -      const { data: { default_branch: remoteDefaultBranch } } =\n126:  835105745 ! 126:  2193295a0 Add a workflow to update the Git version and manual pages\n    @@ .github/workflows/update-git-version-and-manual-pages.yml (new)\n     +      name: github-pages\n     +      url: ${{ steps.deploy.outputs.url }}\n     +    steps:\n    -+      - uses: actions/checkout@v3\n    ++      - uses: actions/checkout@v4\n     +        with:\n     +          sparse-checkout: |\n     +            .github/actions\n    -+            _sync_state\n     +            script\n     +      - name: ruby setup\n     +        uses: ruby/setup-ruby@v1\n    @@ .github/workflows/update-git-version-and-manual-pages.yml (new)\n     +          git \\\n     +            -c user.name=${{ github.actor }} \\\n     +            -c user.email=${{ github.actor }}@noreply.github.com \\\n    -+            commit -m \"Update git-version ($version)\" -- hugo.yml\n    ++            commit -m \"Update git-version ($version)\" \\\n    ++              -m 'Updated via the `update-git-version-and-manual-pages.yml` GitHub workflow.' \\\n    ++              -- hugo.yml\n     +      - name: prepare worktree\n     +        if: steps.commit.outputs.result != ''\n     +        run: git sparse-checkout disable\n    @@ .github/workflows/update-git-version-and-manual-pages.yml (new)\n     +      - name: commit manual pages\n     +        if: steps.commit.outputs.result != ''\n     +        run: |\n    -+          git add -A _generated-asciidoc data/docs.yml content/docs &&\n    ++          git add -A external/docs &&\n     +          git \\\n     +            -c user.name=${{ github.actor }} \\\n     +            -c user.email=${{ github.actor }}@noreply.github.com \\\n    -+            commit -m \"Update manual pages (${{ steps.commit.outputs.result }})\"\n    ++            commit -m \"Update manual pages (${{ steps.commit.outputs.result }})\" \\\n    ++              -m 'Updated via the `update-git-version-and-manual-pages.yml` GitHub workflow.'\n     +      - name: verify that there are no uncommitted changes\n     +        run: |\n     +          git update-index --refresh &&\n127:  15a7705ad ! 127:  93af22a96 update-manual-pages: optionally force a complete rebuild\n    @@ .github/workflows/update-git-version-and-manual-pages.yml: name: Synchronize wit\n          # check daily for updates\n          - cron: '37 17 * * *'\n     @@ .github/workflows/update-git-version-and-manual-pages.yml: jobs:\n    -             -c user.email=${{ github.actor }}@noreply.github.com \\\n    -             commit -m \"Update git-version ($version)\" -- hugo.yml\n    +               -m 'Updated via the `update-git-version-and-manual-pages.yml` GitHub workflow.' \\\n    +               -- hugo.yml\n            - name: prepare worktree\n     -        if: steps.commit.outputs.result != ''\n    -+        if: steps.commit.outputs.result != '' || inputs.force-rebuild\n    ++        if: steps.commit.outputs.result != '' || inputs.force-rebuild == true\n              run: git sparse-checkout disable\n            - name: clone git.git\n     -        if: steps.commit.outputs.result != ''\n    -+        if: steps.commit.outputs.result != '' || inputs.force-rebuild\n    ++        if: steps.commit.outputs.result != '' || inputs.force-rebuild == true\n              run: git clone --bare https://github.com/git/git '${{ runner.temp }}/git'\n            - name: update manual pages\n     -        if: steps.commit.outputs.result != ''\n     -        run: bundle exec ruby script/update-docs.rb '${{ runner.temp }}/git' en\n    -+        if: steps.commit.outputs.result != '' || inputs.force-rebuild\n    ++        if: steps.commit.outputs.result != '' || inputs.force-rebuild == true\n     +        run: |\n    -+          test false = '${{ inputs.force-rebuild }}' || export RERUN=true\n    ++          if test true = '${{ inputs.force-rebuild }}'\n    ++          then\n    ++            export RERUN=true\n    ++          fi\n     +          bundle exec ruby script/update-docs.rb '${{ runner.temp }}/git' en\n            - name: commit manual pages\n     -        if: steps.commit.outputs.result != ''\n     +        id: manual-pages\n    -+        if: steps.commit.outputs.result != '' || inputs.force-rebuild\n    ++        if: steps.commit.outputs.result != '' || inputs.force-rebuild == true\n              run: |\n    -           git add -A _generated-asciidoc data/docs.yml content/docs &&\n    -+          if test false != '${{ inputs.force-rebuild }}' && git diff-index --cached --quiet HEAD --\n    +           git add -A external/docs &&\n    ++          if test true = '${{ inputs.force-rebuild }}' && git diff-index --cached --quiet HEAD --\n     +          then\n     +            echo '::notice::A manual pages rebuild was requested but resulted in no changes' >&2\n     +            exit 0\n    @@ .github/workflows/update-git-version-and-manual-pages.yml: jobs:\n                git \\\n                  -c user.name=${{ github.actor }} \\\n                  -c user.email=${{ github.actor }}@noreply.github.com \\\n    --            commit -m \"Update manual pages (${{ steps.commit.outputs.result }})\"\n    -+            commit -m \"Update manual pages (${version:-manually forced rebuild})\" &&\n    +-            commit -m \"Update manual pages (${{ steps.commit.outputs.result }})\" \\\n    +-              -m 'Updated via the `update-git-version-and-manual-pages.yml` GitHub workflow.'\n    ++            commit -m \"Update manual pages (${version:-manually forced rebuild})\" \\\n    ++              -m 'Updated via the `update-git-version-and-manual-pages.yml` GitHub workflow.' &&\n     +          echo \"result=modified\" >>$GITHUB_OUTPUT\n            - name: verify that there are no uncommitted changes\n              run: |\n128:  21f13a6ef ! 128:  9ccb08bb2 Add a GitHub workflow to deploy `gh-pages` to GitHub Pages\n    @@ .github/workflows/deploy.yml (new)\n     +      name: github-pages\n     +      url: ${{ steps.deploy.outputs.url }}\n     +    steps:\n    -+      - uses: actions/checkout@v3\n    ++      - uses: actions/checkout@v4\n     +      - name: deploy to GitHub Pages\n     +        id: deploy\n     +        uses: ./.github/actions/deploy-to-github-pages\n129:  6a53852f0 ! 129:  ac51259a9 Add a GitHub workflow to update the translated manual pages regularly\n    @@ .github/workflows/update-translated-manual-pages.yml (new)\n     +  check-for-updates:\n     +    runs-on: ubuntu-latest\n     +    steps:\n    -+      - uses: actions/checkout@v3\n    ++      - uses: actions/checkout@v4\n     +        with:\n     +          sparse-checkout: |\n    -+            _sync_state\n    ++            external/docs/sync\n     +            script\n    -+      - uses: actions/github-script@v6\n    ++      - uses: actions/github-script@v7\n     +        id: get-pending\n     +        with:\n     +          script: |\n    @@ .github/workflows/update-translated-manual-pages.yml (new)\n     +      up-to-date: ${{ steps.get-pending.outputs.result }}\n     +  update-translated-manual-pages:\n     +    needs: [check-for-updates]\n    -+    if: inputs.force-rebuild || needs.check-for-updates.outputs.up-to-date == 'false'\n    ++    if: inputs.force-rebuild == true || needs.check-for-updates.outputs.up-to-date == 'false'\n     +    runs-on: ubuntu-latest\n     +    permissions:\n     +      contents: write # to push changes (if any)\n    @@ .github/workflows/update-translated-manual-pages.yml (new)\n     +      name: github-pages\n     +      url: ${{ steps.deploy.outputs.url }}\n     +    steps:\n    -+      - uses: actions/checkout@v3\n    ++      - uses: actions/checkout@v4\n     +      - name: ruby setup\n     +        uses: ruby/setup-ruby@v1\n     +        with:\n    @@ .github/workflows/update-translated-manual-pages.yml (new)\n     +        run: git clone --bare https://github.com/jnavila/git-html-l10n '${{ runner.temp }}/git-html-l10n'\n     +      - name: update translated manual pages\n     +        run: |\n    -+          test false = '${{ inputs.force-rebuild }}' || export RERUN=true\n    ++          if test true = '${{ inputs.force-rebuild }}'\n    ++          then\n    ++            export RERUN=true\n    ++          fi\n     +          bundle exec ruby script/update-docs.rb '${{ runner.temp }}/git-html-l10n' l10n\n     +      - name: commit translated manual pages\n     +        id: manual-pages\n     +        run: |\n    -+          git -C '${{ runner.temp }}/git-html-l10n' rev-parse HEAD >_sync_state/git-html-l10n.sha &&\n    -+          git add _sync_state/git-html-l10n.sha &&\n    ++          mkdir -p external/docs/sync &&\n    ++          git -C '${{ runner.temp }}/git-html-l10n' rev-parse HEAD >external/docs/sync/git-html-l10n.sha &&\n    ++          git add external/docs/sync/git-html-l10n.sha &&\n     +\n    -+          git add -A _generated-asciidoc/ data/docs.yml content/docs &&\n    -+          if test false != '${{ inputs.force-rebuild }}' && git diff-index --cached --quiet HEAD --\n    ++          git add -A external/docs &&\n    ++          if test true = '${{ inputs.force-rebuild }}' && git diff-index --cached --quiet HEAD --\n     +          then\n     +            echo '::notice::Rebuild of the translated manual pages was requested but resulted in no changes' >&2\n     +            exit 0\n    @@ .github/workflows/update-translated-manual-pages.yml (new)\n     +          git \\\n     +            -c user.name=${{ github.actor }} \\\n     +            -c user.email=${{ github.actor }}@noreply.github.com \\\n    -+            commit -m \"Update translated manual pages\" &&\n    ++            commit -m \"Update translated manual pages\" \\\n    ++              -m 'Updated via the `update-translated-manual-pages.yml` GitHub workflow.' &&\n     +          echo \"result=modified\" >>$GITHUB_OUTPUT\n     +      - name: verify that there are no uncommitted changes\n     +        run: |\n    @@ script/ci-helper.js: const getPendingBookUpdates = async (octokit, forceRebuild)\n\n     +const areTranslatedManualPagesUpToDate = async (octokit) => {\n     +  try {\n    -+    const localSha = await getFileContents(`_sync_state/git-html-l10n.sha`)\n    ++    const localSha = await getFileContents(`external/docs/sync/git-html-l10n.sha`)\n     +\n     +    const [owner, repo] = 'jnavila/git-html-l10n'.split('/')\n     +    const { data: { default_branch: remoteDefaultBranch } } =\n  -:  --------- > 130:  28b147af1 deploy: check for broken links\n  -:  --------- > 131:  c9dae6afc deploy(linkcheck): deal with stray \"secondary rate limits\"\n130:  bff0ab61c ! 132:  2b63bbb71 Adjust `ARCHITECTURE.md` to the Hugo reality\n    @@ Commit message\n         https://github.com/git/git-scm.com/issues/942.\n\n         However, I aborted that migration when it turned out that Jekyll\n    -    required 20 minutes to process the files while Hugo spent less than half\n    -    a minute on them.\n    +    required 20 minutes to process the files while Hugo spent less than\n    +    half a minute on them.\n\n         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n\n    @@ ARCHITECTURE.md\n     +  - original content from this repository\n\n        - community book content brought in from https://github.com/progit;\n    -     see the `lib/tasks/book2.rake` file.\n    -@@ ARCHITECTURE.md: The content is a mix of:\n    -   - manpages from releases of the git project, imported and formatted\n    -     via asciidoctor; see the `lib/tasks/index.rake` task.\n    +-    see the `lib/tasks/book2.rake` file.\n    ++    see the `script/update-book2.rb` and `script/book.rb` files.\n\n    -+To deploy to GitHub Pages, it is necessary to turn off the default setting to\n    -+\"publish from a branch\" and instead change the setting to \"publish with a\n    -+custom GitHub Actions workflow\":\n    -+https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow\n    +-  - manpages from releases of the git project, imported and formatted\n    +-    via asciidoctor; see the `lib/tasks/index.rake` task.\n    ++    The content is pre-rendered and tracked in the `external/book/` directory\n    ++    tree.\n    +\n    ++  - manual pages from releases of the git project, imported and formatted via\n    ++    AsciiDoctor, and translated versions of the manual pages from\n    ++    https://github.com/jnavila/git-manpages-l10n/ (which itself contains\n    ++    pre-rendered pages from https://github.com/jnavila/git-manpages-l10n/); see\n    ++    the `script/update-docs.rb` file.\n\n     -## Heroku\n    -+## Non-static parts\n    ++    The pre-rendered pages are tracked in the `external/docs/` directory tree.\n\n     -The app itself is served by Heroku. The app name is `git-scm` (so you\n     -can visit it directly as https://git-scm.herokuapp.com). The site is\n     -owned by the git-scm.com team. If you want to be involved in managing\n     -uptime/deploys/etc, you'll need a Heroku account and request to be added\n     -to that team.\n    ++To deploy to GitHub Pages, it is necessary to turn off the default setting to\n    ++\"publish from a branch\" and instead change the setting to \"publish with a\n    ++custom GitHub Actions workflow\":\n    ++https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow\n    ++With this change, the site can be tested in the fork by pushing to the\n    ++`gh-pages` branch (which will trigger the `deploy.yml` workflow) and then\n    ++navigating to https://git-scm.<user>.github.io/.\n    +\n    +-We use a few Heroku add-ons:\n    ++## Non-static parts\n    +\n    +-  - Bonsai elasticsearch (see below)\n     +While the site consists mostly of static content, there are a couple of\n     +parts that are sort of dynamic.\n\n    --We use a few Heroku add-ons:\n    +-  - Heroku Postgres as the database\n     +The search is implemented client-side, via [Pagefind](https://pagefind.app/).\n\n    --  - Bonsai elasticsearch (see below)\n    +-  - Heroku Redis for rails caching\n     +A few scheduled GitHub workflows keep the content up to date:\n\n    --  - Heroku Postgres as the database\n    +-  - Heroku scheduler for cron jobs\n     +  - `update-git-version-and-manual-pages` and `update-download-data` (pick\n     +    up newly released git versions)\n\n    --  - Heroku Redis for rails caching\n    +-The nightly scheduled jobs are:\n     +  - `update-translated-manual-pages` (fetch and format translated manual\n     +    pages from the jnavila/git-html-l10n repository)\n\n    --  - Heroku scheduler for cron jobs\n    --\n    --The nightly scheduled jobs are:\n    --\n     -  - `rake downloads` (pick up newly released git versions)\n     -\n     -  - `rake preindex` (pull in and format manpages for released git\n    @@ ARCHITECTURE.md: The content is a mix of:\n     -dyno. So we have Cloudflare sitting in front of it, aggressively caching\n     -everything. That also should make the site faster to serve to regions\n     -far away from Heroku's servers.\n    +-\n    +-The Cloudflare setup is mostly pretty simple:\n     +These workflows are also marked as `workflow_dispatch`, i.e. they can be run\n     +manually (e.g. to update the download links just after Git for Windows\n     +published a new release).\n\n    --The Cloudflare setup is mostly pretty simple:\n    --\n     - - they serve DNS for the whole domain (that's where they insert the CDN\n     -   magic)\n     -\n    @@ ARCHITECTURE.md: The content is a mix of:\n     +`deploy` GitHub workflow.\n\n     +Note that some of the formatting of manual pages and book content happens\n    -+when they are imported by the GitHub workflows. Therefore, after fixing some\n    -+formatting, these workflows may need the force-rebuild flag to be toggled (see\n    -+the individual workflows for details).\n    ++when they are imported by the GitHub workflows. Therefore, whenever there are\n    ++changes to the scripts/workflows/automation that affect formatting, these\n    ++workflows may need to be triggered using the force-rebuild flag to be toggled\n    ++(see the individual workflows for details).\n\n      ## DNS\n\n    @@ ARCHITECTURE.md: The content is a mix of:\n\n      Note that we own both git-scm.com and git-scm.org; the latter redirects\n      to the former.\n    -@@ ARCHITECTURE.md: The site mostly just runs without intervention:\n\n    -   - code merged to `main` is auto-deployed\n    +-\n    + ## Manual Intervention\n    +\n    + The site mostly just runs without intervention:\n    +\n    +-  - code merged to `main` is auto-deployed\n    ++  - code merged to `gh-pages` is auto-deployed\n\n     -  - new git versions are detected daily and manpages and download links\n     +  - new git versions are detected daily and manual pages and download links\n    @@ ARCHITECTURE.md: The site mostly just runs without intervention:\n     -    `lib/tasks/book2.rake`\n     +    `script/book.rb`\n\n    -   - forced re-imports of content (e.g., a formatting fix to imported\n    +-  - forced re-imports of content (e.g., a formatting fix to imported\n     -    manpages) must be triggered manually\n    -+    manual pages) must be triggered manually with `force-rebuild` toggled\n    ++  - forced re-imports of content (e.g., when fixing formatting in the\n    ++    imported manual pages) must be triggered manually with `force-rebuild`\n    ++    toggled\n  -:  --------- > 133:  e91c406fa Add Playwright-based UI tests\n  -:  --------- > 134:  a43cdff2c playwright: add a few platform-specific tests\n  -:  --------- > 135:  f6b7ccd21 playwright: let the UI tests pass with the Rails app\n  -:  --------- > 136:  e7db6d766 playwright: add a regression test for the `git remote renom` issue\n  -:  --------- > 137:  87609ac77 playwright: add a GitHub workflow to run the tests\n  -:  --------- > 138:  5facfdaee playwright: auto-start a web server for the special URL localhost:5000\n  -:  --------- > 139:  b54046816 playwright: verify that URLs with question marks work\n  -:  --------- > 140:  4210b9bba ci: run the Playwright tests in every Pull Request\n  -:  --------- > 141:  ffb957b5f ci: run the Playwright tests on every deployment\n\nCiao,\nJohannes\n"},{"id":"502653","messageId":"87h6al9ujv.fsf@iotcl.com","threadId":"60533","inReplyTo":"155bc141-a16a-9fd2-920b-7a8c398d5e36@gmx.de","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2024-09-12T07:53:56Z","receivedAt":"2024-09-12T07:54:08Z","isPatch":false,"sender":{"key":"toon@iotcl.com","avatar":"https://avatars.githubusercontent.com/u/121621?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Me again,\n>\n> It's time for the next update, after working with Pagefind, lychee and\n> Playwright project members to improve on this effort. Here is a focused\n> list of changes since November 17th 2023 (when I sent the first RFC), in\n> descending, quite subjective order of importance\n>\n> [snip]\n\nHi Johannes,\n\nI just discovered this effort. I didn't realize this was happening, but\nI'm super excited to see this going (and being picked up again).\n\nI've been doing some testing locally, and I've been posting some\ncomments on the GitHub PR, with small patches.\n\nThe biggest thing I've noticed is there is still some work to be done on\nthe search results page, for example:\nhttps://git.github.io/git-scm.com/search/results?search=bundle&language=en\n\nI assume we'd need to inject some of our CSS into the pagefind results.\nI'm happy to contribute to this if you like? It would give me the\nopportunity to play with some CSS again.\n\n--\nToon\n"},{"id":"502793","messageId":"702cec20-a1a2-40b4-de8e-305b63a92b26@gmx.de","threadId":"60533","inReplyTo":"87h6al9ujv.fsf@iotcl.com","subject":"Re: Migration of git-scm.com to a static web site: ready for review/testing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2024-09-14T18:41:57Z","receivedAt":"2024-09-14T18:42:05Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Toon,\n\nOn Thu, 12 Sep 2024, Toon Claes wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n> > Me again,\n> >\n> > It's time for the next update, after working with Pagefind, lychee and\n> > Playwright project members to improve on this effort. Here is a focused\n> > list of changes since November 17th 2023 (when I sent the first RFC), in\n> > descending, quite subjective order of importance\n> >\n> > [snip]\n>\n> Hi Johannes,\n>\n> I just discovered this effort. I didn't realize this was happening, but\n> I'm super excited to see this going (and being picked up again).\n>\n> I've been doing some testing locally, and I've been posting some\n> comments on the GitHub PR, with small patches.\n\nThank you, I integrated them!\n\n> The biggest thing I've noticed is there is still some work to be done on\n> the search results page, for example:\n> https://git.github.io/git-scm.com/search/results?search=bundle&language=en\n>\n> I assume we'd need to inject some of our CSS into the pagefind results.\n> I'm happy to contribute to this if you like? It would give me the\n> opportunity to play with some CSS again.\n\nI would love it if you would make those changes, and would be happy to\nintegrate them into that PR!\n\nThank you,\nJohannes\n"}]}