Once again, me, On Thu, 12 Sep 2024, Johannes Schindelin wrote: > I will try to reply to this mail with a range-diff that is slightly edited > to leave out the changes made by GitHub workflows (i.e. book and manual > pages). Here goes: 1: 3420701f6 < -: --------- Remove a bogus end tag 2: 861766083 < -: --------- docs: fix a bogus <a/> 3: 5af3129c1 < -: --------- Remove obsolete banner 4: 82173f4f1 < -: --------- Revert "book2.rake: drop progit2-ms translation" 5: 32795076e = 1: 987e733c3 Drop the Travis CI configuration 6: 0c6f2b28b = 2: 247a0615a Add a Hugo-aware .gitignore file 7: 5c522f814 ! 3: 593aa9af8 Adjust the Gemfile in preparation for generating a static site @@ Gemfile source "https://rubygems.org" -ruby "3.1.3" - --gem "rails", "~> 6.0" +- +-gem "rails", "~> 6.1" - -# hacks for rails6 + ruby 3.1 -gem 'net-imap', require: false @@ Gemfile -gem 'net-smtp', require: false - -gem "asciidoctor", "~> 2.0.0" --gem "elasticsearch", "2.0.2" +-gem "elasticsearch", "7.13" -gem "iso8601" -gem "octokit" -gem "puma" 8: 46233d1bb ! 4: 5cddbb9a3 Add a Hugo configuration @@ Commit message ## hugo.yml (new) ## @@ +--- -+baseURL: https://git-scm.com/ +languageCode: en +title: Git +disablePathToLower: true @@ hugo.yml (new) + renderer: + unsafe: true +params: -+ latest_version: 2.43.0 -+ latest_relnote_url: https://raw.github.com/git/git/master/Documentation/RelNotes/2.43.0.txt -+ latest_release_date: '2023-11-20' ++ latest_version: 2.46.0 ++ latest_relnote_url: https://raw.github.com/git/git/master/Documentation/RelNotes/2.46.0.txt ++ latest_release_date: '2024-07-29' 9: ed4e92165 ! 5: 8de703d8c Drop the Ruby app @@ Gemfile.lock (deleted) -GEM - remote: https://rubygems.org/ - specs: -- actioncable (6.1.7.3) -- actionpack (= 6.1.7.3) -- activesupport (= 6.1.7.3) +- actioncable (6.1.7.7) +- actionpack (= 6.1.7.7) +- activesupport (= 6.1.7.7) - nio4r (~> 2.0) - websocket-driver (>= 0.6.1) -- actionmailbox (6.1.7.3) -- actionpack (= 6.1.7.3) -- activejob (= 6.1.7.3) -- activerecord (= 6.1.7.3) -- activestorage (= 6.1.7.3) -- activesupport (= 6.1.7.3) +- actionmailbox (6.1.7.7) +- actionpack (= 6.1.7.7) +- activejob (= 6.1.7.7) +- activerecord (= 6.1.7.7) +- activestorage (= 6.1.7.7) +- activesupport (= 6.1.7.7) - mail (>= 2.7.1) -- actionmailer (6.1.7.3) -- actionpack (= 6.1.7.3) -- actionview (= 6.1.7.3) -- activejob (= 6.1.7.3) -- activesupport (= 6.1.7.3) +- actionmailer (6.1.7.7) +- actionpack (= 6.1.7.7) +- actionview (= 6.1.7.7) +- activejob (= 6.1.7.7) +- activesupport (= 6.1.7.7) - mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 2.0) -- actionpack (6.1.7.3) -- actionview (= 6.1.7.3) -- activesupport (= 6.1.7.3) +- actionpack (6.1.7.7) +- actionview (= 6.1.7.7) +- activesupport (= 6.1.7.7) - rack (~> 2.0, >= 2.0.9) - rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) -- actiontext (6.1.7.3) -- actionpack (= 6.1.7.3) -- activerecord (= 6.1.7.3) -- activestorage (= 6.1.7.3) -- activesupport (= 6.1.7.3) +- actiontext (6.1.7.7) +- actionpack (= 6.1.7.7) +- activerecord (= 6.1.7.7) +- activestorage (= 6.1.7.7) +- activesupport (= 6.1.7.7) - nokogiri (>= 1.8.5) -- actionview (6.1.7.3) -- activesupport (= 6.1.7.3) +- actionview (6.1.7.7) +- activesupport (= 6.1.7.7) - builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) -- activejob (6.1.7.3) -- activesupport (= 6.1.7.3) +- activejob (6.1.7.7) +- activesupport (= 6.1.7.7) - globalid (>= 0.3.6) -- activemodel (6.1.7.3) -- activesupport (= 6.1.7.3) -- activerecord (6.1.7.3) -- activemodel (= 6.1.7.3) -- activesupport (= 6.1.7.3) -- activestorage (6.1.7.3) -- actionpack (= 6.1.7.3) -- activejob (= 6.1.7.3) -- activerecord (= 6.1.7.3) -- activesupport (= 6.1.7.3) +- activemodel (6.1.7.7) +- activesupport (= 6.1.7.7) +- activerecord (6.1.7.7) +- activemodel (= 6.1.7.7) +- activesupport (= 6.1.7.7) +- activestorage (6.1.7.7) +- actionpack (= 6.1.7.7) +- activejob (= 6.1.7.7) +- activerecord (= 6.1.7.7) +- activesupport (= 6.1.7.7) - marcel (~> 1.0) - mini_mime (>= 1.1.0) -- activesupport (6.1.7.3) +- activesupport (6.1.7.7) - concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 1.6, < 2) - minitest (>= 5.1) @@ Gemfile.lock (deleted) - builder (3.2.4) - byebug (11.1.3) - coderay (1.1.3) -- concurrent-ruby (1.2.2) +- concurrent-ruby (1.2.3) - crack (0.4.5) - rexml - crass (1.0.6) @@ Gemfile.lock (deleted) - dotenv-rails (2.7.6) - dotenv (= 2.7.6) - railties (>= 3.2) -- elasticsearch (2.0.2) -- elasticsearch-api (= 2.0.2) -- elasticsearch-transport (= 2.0.2) -- elasticsearch-api (2.0.2) +- elasticsearch (7.13.0) +- elasticsearch-api (= 7.13.0) +- elasticsearch-transport (= 7.13.0) +- elasticsearch-api (7.13.0) - multi_json -- elasticsearch-transport (2.0.2) -- faraday +- elasticsearch-transport (7.13.0) +- faraday (~> 1) - multi_json - erubi (1.12.0) - execjs (2.8.1) @@ Gemfile.lock (deleted) - factory_bot_rails (6.2.0) - factory_bot (~> 6.2.0) - railties (>= 5.0.0) -- faraday (1.8.0) +- faraday (1.10.3) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) -- faraday-httpclient (~> 1.0.1) +- faraday-httpclient (~> 1.0) +- faraday-multipart (~> 1.0) - faraday-net_http (~> 1.0) -- faraday-net_http_persistent (~> 1.1) +- faraday-net_http_persistent (~> 1.0) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) -- multipart-post (>= 1.2, < 3) +- faraday-retry (~> 1.0) - ruby2_keywords (>= 0.0.4) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) -- faraday-net_http (1.0.1) +- faraday-multipart (1.0.4) +- multipart-post (~> 2) +- faraday-net_http (1.0.2) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) +- faraday-retry (1.0.3) - ffi (1.15.4) - foreman (0.87.2) -- globalid (1.1.0) -- activesupport (>= 5.0) +- globalid (1.2.1) +- activesupport (>= 6.1) - hashdiff (1.0.1) -- i18n (1.12.0) +- i18n (1.14.4) - concurrent-ruby (~> 1.0) - iso8601 (0.13.0) - json (2.6.2) - listen (3.7.0) - rb-fsevent (~> 0.10, >= 0.10.3) - rb-inotify (~> 0.9, >= 0.9.10) -- loofah (2.19.1) +- loofah (2.22.0) - crass (~> 1.0.2) -- nokogiri (>= 1.5.9) +- nokogiri (>= 1.12.0) - mail (2.8.1) - mini_mime (>= 0.1.1) - net-imap - net-pop - net-smtp -- marcel (1.0.2) +- marcel (1.0.4) - method_source (1.0.0) -- mini_mime (1.1.2) -- mini_portile2 (2.8.1) -- minitest (5.18.0) +- mini_mime (1.1.5) +- mini_portile2 (2.8.6) +- minitest (5.22.3) - multi_json (1.15.0) -- multipart-post (2.1.1) +- multipart-post (2.4.1) - net-imap (0.3.1) - net-protocol - net-pop (0.1.2) - net-protocol -- net-protocol (0.1.3) +- net-protocol (0.2.2) - timeout - net-smtp (0.3.2) - net-protocol -- nio4r (2.5.9) -- nokogiri (1.14.3) -- mini_portile2 (~> 2.8.0) +- nio4r (2.7.3) +- nokogiri (1.16.5) +- mini_portile2 (~> 2.8.2) - racc (~> 1.4) - octokit (4.21.0) - faraday (>= 0.9) @@ Gemfile.lock (deleted) - byebug (~> 11.0) - pry (~> 0.13.0) - public_suffix (4.0.6) -- puma (5.6.7) +- puma (5.6.8) - nio4r (~> 2.0) -- racc (1.6.2) -- rack (2.2.6.4) +- racc (1.7.3) +- rack (2.2.8.1) - rack-test (2.1.0) - rack (>= 1.3) - rack-timeout (0.6.0) -- rails (6.1.7.3) -- actioncable (= 6.1.7.3) -- actionmailbox (= 6.1.7.3) -- actionmailer (= 6.1.7.3) -- actionpack (= 6.1.7.3) -- actiontext (= 6.1.7.3) -- actionview (= 6.1.7.3) -- activejob (= 6.1.7.3) -- activemodel (= 6.1.7.3) -- activerecord (= 6.1.7.3) -- activestorage (= 6.1.7.3) -- activesupport (= 6.1.7.3) +- rails (6.1.7.7) +- actioncable (= 6.1.7.7) +- actionmailbox (= 6.1.7.7) +- actionmailer (= 6.1.7.7) +- actionpack (= 6.1.7.7) +- actiontext (= 6.1.7.7) +- actionview (= 6.1.7.7) +- activejob (= 6.1.7.7) +- activemodel (= 6.1.7.7) +- activerecord (= 6.1.7.7) +- activestorage (= 6.1.7.7) +- activesupport (= 6.1.7.7) - bundler (>= 1.15.0) -- railties (= 6.1.7.3) +- railties (= 6.1.7.7) - sprockets-rails (>= 2.0.0) - rails-controller-testing (1.0.5) - actionpack (>= 5.0.1.rc1) - actionview (>= 5.0.1.rc1) - activesupport (>= 5.0.1.rc1) -- rails-dom-testing (2.0.3) -- activesupport (>= 4.2.0) +- rails-dom-testing (2.2.0) +- activesupport (>= 5.0.0) +- minitest - nokogiri (>= 1.6) -- rails-html-sanitizer (1.5.0) -- loofah (~> 2.19, >= 2.19.1) +- rails-html-sanitizer (1.6.0) +- loofah (~> 2.21) +- nokogiri (~> 1.14) - rails_12factor (0.0.3) - rails_serve_static_assets - rails_stdout_logging - rails_serve_static_assets (0.0.5) - rails_stdout_logging (0.0.5) -- railties (6.1.7.3) -- actionpack (= 6.1.7.3) -- activesupport (= 6.1.7.3) +- railties (6.1.7.7) +- actionpack (= 6.1.7.7) +- activesupport (= 6.1.7.7) - method_source - rake (>= 12.2) - thor (~> 1.0) - rainbow (3.0.0) -- rake (13.0.6) +- rake (13.1.0) - rb-fsevent (0.11.0) - rb-inotify (0.10.1) - ffi (~> 1.0) @@ Gemfile.lock (deleted) - redis-store (1.9.0) - redis (>= 4, < 5) - regexp_parser (2.1.1) -- rexml (3.2.5) +- rexml (3.2.8) +- strscan (>= 3.0.9) - rspec-core (3.10.1) - rspec-support (~> 3.10.0) - rspec-expectations (3.10.1) @@ Gemfile.lock (deleted) - shoulda-context (2.0.0) - shoulda-matchers (4.5.1) - activesupport (>= 4.2.0) -- sprockets (4.2.0) +- sprockets (4.2.1) - concurrent-ruby (~> 1.0) - rack (>= 2.2.4, < 4) - sprockets-rails (3.4.2) @@ Gemfile.lock (deleted) - activesupport (>= 5.2) - sprockets (>= 3.0.0) - sqlite3 (1.4.2) -- thor (1.2.1) +- strscan (3.1.0) +- thor (1.3.1) - tilt (2.0.10) -- timeout (0.3.0) +- timeout (0.4.1) - tzinfo (2.0.6) - concurrent-ruby (~> 1.0) - uglifier (4.2.0) @@ Gemfile.lock (deleted) - addressable (>= 2.8.0) - crack (>= 0.3.2) - hashdiff (>= 0.4.0, < 2.0.0) -- websocket-driver (0.7.5) +- websocket-driver (0.7.6) - websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.5) -- zeitwerk (2.6.7) +- zeitwerk (2.6.13) - -PLATFORMS - ruby @@ Gemfile.lock (deleted) - database_cleaner - diffy - dotenv-rails -- elasticsearch (= 2.0.2) +- elasticsearch (= 7.13) - fabrication - factory_bot_rails - foreman @@ Gemfile.lock (deleted) - pry-byebug - puma - rack-timeout -- rails (~> 6.0) +- rails (~> 6.1) - rails-controller-testing - rails_12factor - redis-rails @@ Rakefile (deleted) ## app.json (deleted) ## @@ -{ +- "stack": "heroku-24", - "scripts": { - }, - "env": { @@ lib/searchable.rb (deleted) - "query" => { - "bool" => { - "should" => [], -- "minimum_number_should_match" => 1 +- "minimum_should_match" => 1 - }, - }, - "highlight" => { 10: 7865f2051 = 6: 24844c72e In preparation for using Hugo, move the "blog has moved" page 11: 4effd55ad = 7: 5830b2c27 Hugo-ify the "blog has moved" page 12: 6a09add78 = 8: 34580a126 Move the favicon to the `static/` directory 13: a79b9c844 = 9: 9f96eea1f Drop the robots.txt file 14: 32a4eb168 = 10: 47cbca3c8 Hugo'ify the 404 page 15: 906bb0235 = 11: 7a1f5bdac Drop the custom pages for HTML codes 422 and 500 16: bff32d315 ! 12: 340558bd1 ci: switch to building with Hugo @@ .github/workflows/ci.yml name: CI -on: pull_request +on: [pull_request] -+ -+env: -+ HUGO_VERSION: 0.120.3 jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 -+ - uses: actions/checkout@v3 ++ - uses: actions/checkout@v4 - - name: ruby setup - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true ++ - name: configure Hugo version ++ run: | ++ set -x && ++ echo "HUGO_VERSION=$(sed -n 's/^ *hugo_version: *//p' <hugo.yml)" >>$GITHUB_ENV ++ + - name: install Hugo ${{ env.HUGO_VERSION }} + run: | + set -x && @@ .github/workflows/ci.yml - - name: rubocop - run: bundle exec rubocop -P -+ - name: run Pagefind to build the search index -+ run: npx -y pagefind --site public ++ - name: build tar archive ++ run: cd public && tar czvf ../pages.tar.gz * - - name: rspec - run: bundle exec rspec -+ - name: build tar archive -+ run: cd public && tar czvf ../pages.tar.gz * -+ + - name: Upload build artifact -+ uses: actions/upload-artifact@v3 ++ uses: actions/upload-artifact@v4 + with: + name: pages + path: pages.tar.gz + + ## hugo.yml ## +@@ hugo.yml: markup: + renderer: + unsafe: true + params: ++ hugo_version: 0.134.1 + latest_version: 2.46.0 + latest_relnote_url: https://raw.github.com/git/git/master/Documentation/RelNotes/2.46.0.txt + latest_release_date: '2024-07-29' 17: f685a732d ! 13: 2b1735089 README: reflect that this is now a Hugo site @@ README.md +## Local development setup -You'll need a Ruby environment to run Rails. First do: -+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 ++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: - $ rvm use . - $ bundle install -+ $ hugo serve -w ++- If you want to test any page rendering using Hugo: ++ - layouts/ ++ - content/ ++ - static/ ++ - assets/ -Then you need to create the database structure: -+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. ++- To add new GUIs: ++ - data/ - $ rake db:migrate -+## Update manual pages ++- To work on pre-rendering pages that originate from other repositories (such as the ProGit book): ++ - script/ -Alternatively you can run the script at `script/bootstrap` which will set up Ruby dependencies and the local SQLite database. -- ++- To work on the GitHub workflows that perform the automated, scheduled pre-rendering: ++ - .github/ + -Now you'll want to populate the man pages. You can do so from a local Git -source clone like this: ++- The pre-rendered pages (ProGit book, its translated versions, the manual pages, their translated versions): ++ - external/book/ ++ - external/docs/ ++ You will want to avoid editing these directly, as they contain pages that are pre-rendered via GitHub workflows, sourcing content from other repositories. ++ ++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 ++ ++ $ hugo serve -w ++ ++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. ++ ++## Update manual pages ++ +(TODO!) +You can do so using a local Git source clone like this: @@ README.md: Alternatively, you can get the book content from a repository on your - ## Contributing - If you wish to contribute to this website, please [fork it on GitHub](https://github.com/git/git-scm.com), push your -@@ README.md: be accepted. If it involves code, please also write tests for it. +-If you wish to contribute to this website, please [fork it on GitHub](https://github.com/git/git-scm.com), push your +-change to a named branch, then send 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. If it involves code, please also write tests for it. +- +-## Adding new GUI ++If you wish to contribute to this website, please [fork it on GitHub](https://github.com/git/git-scm.com). - 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: +-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: ++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. -1. Add the GUI client details at the YAML file: https://github.com/git/git-scm.com/blob/main/resources/guis.yml - 1. The fields `name`, `url`, `price`, `license` should be very straightforward to fill. @@ README.md: be accepted. If it involves code, please also write tests for it. - 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` - 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) - 5. `trend_name` is an optional field that can be used for helping sorting the clients (also covered in the next steps) -- ++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. + -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. -- ++## Adding a new GUI + -3. Sort the tools - 1. From the root of the repository, run: `$ ./script/sort-gui` - 2. A list of google trends url's will be displayed at the bottom if everything went well. @@ README.md: be accepted. If it involves code, please also write tests for it. - 7. The script makes some basic verifications. If there was some problem, it should be easily visible in the output - 1. If you have more than 1 tool with the same name, a warning will appear: `======= WARNING: THERE ARE DUPLICATED GUIS =======` - 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>) =======` -- ++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: + -## FAQ -- --While setting the repo if you find any error, check if it's a known issue and the corresponding solution bellow. -+1. Add a new `.md` file with the GUI client details: https://github.com/git/git-scm.com/tree/main/content/data/guis ++1. Add a new `.md` file with the GUI client details: data/guis + 1. The fields need to be enclosed within `---` lines + 2. The fields `name`, `project_url`, `price`, `license` should be very straightforward to fill. + 3. The field `image_tag` corresponds to the path of the image of the tool (should start with `images/guis/`). + 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` -+ 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. -+ 6. `trend_name` is an optional field that can be used for helping sorting the clients (also covered in the next steps) ++ 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. ++ 6. `trend_name` is an optional field that can be used for helping sorting the clients. --### An error occurred while installing pg (1.2.3), and Bundler cannot continue. +-While setting the repo if you find any error, check if it's a known issue and the corresponding solution bellow. +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. +-### An error occurred while installing pg (1.2.3), and Bundler cannot continue. ++## Useful links + -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. -+## Useful link regarding working with Hugo ++### Hugo (static site generator) +* https://gohugo.io/ +* https://gohugo.io/content-management/shortcodes/ 18: ae9a0d82e = 14: e53e639b5 Move stylesheets to `assets/sass/` 19: faab827f4 ! 15: 11ab6559e Move the images to `static/images/` @@ public/images/guis/git-glint@xxxxxx => static/images/guis/git-glint@xxxxxx ## public/images/guis/git-kraken.png => static/images/guis/git-kraken.png ## - ## public/images/guis/git-kraken@xxxxxx => static/images/guis/git-kraken@xxxxxx ## - ## public/images/guis/git2go.png => static/images/guis/git2go.png ## ## public/images/guis/git2go@xxxxxx => static/images/guis/git2go@xxxxxx ## @@ public/images/guis/gitklient.png => static/images/guis/gitklient.png ## public/images/guis/gitklient@xxxxxx => static/images/guis/gitklient@xxxxxx ## + ## public/images/guis/gitkraken-2024@xxxxxx => static/images/guis/gitkraken-2024@xxxxxx ## + ## public/images/guis/gitonic.png => static/images/guis/gitonic.png ## ## public/images/guis/gitonic@xxxxxx => static/images/guis/gitonic@xxxxxx ## @@ public/images/guis/gitx.png => static/images/guis/gitx.png ## public/images/guis/gitx@xxxxxx => static/images/guis/gitx@xxxxxx ## + ## public/images/guis/gk-cli-keifs@xxxxxx => static/images/guis/gk-cli-keifs@xxxxxx ## + ## public/images/guis/gmaster.png => static/images/guis/gmaster.png ## ## public/images/guis/gmaster@xxxxxx => static/images/guis/gmaster@xxxxxx ## @@ public/images/guis/pragmagit.png => static/images/guis/pragmagit.png ## public/images/guis/pragmagit@xxxxxx => static/images/guis/pragmagit@xxxxxx ## + ## public/images/guis/relagit.png => static/images/guis/relagit.png ## + + ## public/images/guis/relagit@xxxxxx => static/images/guis/relagit@xxxxxx ## + ## public/images/guis/repoz.png => static/images/guis/repoz.png ## ## public/images/guis/repoz@xxxxxx => static/images/guis/repoz@xxxxxx ## @@ public/images/guis/snailgit.png => static/images/guis/snailgit.png ## public/images/guis/snailgit@xxxxxx => static/images/guis/snailgit@xxxxxx ## + ## public/images/guis/sourcegit.png => static/images/guis/sourcegit.png ## + + ## public/images/guis/sourcegit@xxxxxx => static/images/guis/sourcegit@xxxxxx ## + ## public/images/guis/sourcetree.png => static/images/guis/sourcetree.png ## ## public/images/guis/sourcetree@xxxxxx => static/images/guis/sourcetree@xxxxxx ## 20: 34a9aafae = 16: b379241b1 Move Javascripts to the `static/` directory 21: 1e7d57b39 = 17: add38c971 Remove the layout for the error pages 22: 3775916fa = 18: 0929494dc Migrate the primary layout to the Hugo world 23: 03da9c6ea ! 19: 817c49726 Adjust the layouts @@ layouts/_default/baseof.html - <%= stylesheet_link_tag "application" %> - <%= javascript_include_tag "modernize" %> -+ {{ $style := resources.Get "sass/application.scss" | resources.ExecuteAsTemplate "application.scss" . | resources.ToCSS | resources.Minify }} ++ {{ $style := resources.Get "sass/application.scss" | resources.ExecuteAsTemplate "application.scss" . | css.Sass | resources.Minify }} + <link rel="stylesheet" href="{{ $style.RelPermalink }}"> + <script src="/js/modernizr.js"></script> + <script src="/js/modernize.js"></script> @@ layouts/_default/baseof.html + {{ partial "sidebar.html" . }} <div id="content"> - <%= yield %> -+ {{ .Content }} ++ {{ if (eq .Page.Path "/docs") }} ++ {{ partial "ref/index.html" . }} ++ {{ else }} ++ {{ .Content }} ++ {{ end }} </div> </div> - <%= render partial: "shared/footer" %> 24: b721d0a20 = 20: f2546f28f Remove some includable bits that won't be needed in the future 25: 25b0a2330 = 21: e465a5ae6 Move some includable bits to `layouts/` 26: c9fd7e391 = 22: b4a98bee7 Make Javascript imports explicit 27: 3b33bf0de = 23: 68e705c49 Inline the taglines on the top 28: 79ffa61fc = 24: 3558e3570 Move the main index.html to the `content/` directory 29: 1991470d8 = 25: d23fd0543 Migrate the top-level page of the site to Hugo 30: e308db418 = 26: 4d5b58b58 CSS: quote URLs consistently 31: f55cae009 = 27: 37497edb9 Explicitly include 'normalize' in application CSS 32: 61a203caf = 28: 4ac41f786 Use the base URL in partial .scss files 33: c3730b407 ! 29: 6b2d46049 Add a script to update the latest Git version @@ Commit message ## Gemfile ## @@ + # frozen_string_literal: true source "https://rubygems.org" - ++ +gem "octokit" ## script/update-git-version.rb (new) ## 34: 79e745184 = 30: 02abe2c42 update-git-version: skip -rc versions 35: 0e8a57fab = 31: 115ebbebf Allow redirecting from `list` pages 36: ac13d76df ! 32: 1118b8eaf Define and use sections in a flexible way @@ layouts/_default/baseof.html +{{ $section := "" }} +{{ if isset .Params "section" }} +{{ $section = .Params.section }} -+{{ else if isset .Page "Section" }} ++{{ else if (eq .Page.Path "/docs") }} ++{{ $section = "documentation" }} ++{{ else if (isset .Page "Section") }} +{{ $section = .Page.Section }} -+{{ else if or (eq .Page.Type "doc") (eq .Page.Type "docs") (eq .Page.Type "video") }} ++{{ else if (or (eq .Page.Type "doc") (eq .Page.Type "docs") (eq .Page.Type "video")) }} +{{ $section = "documentation" }} -+{{ else if isset .Page "Type" }} ++{{ else if (isset .Page "Type") }} +{{ $section = .Page.Type }} +{{ else }} -+{{ warnf "No section found in %s" (.File | jsonify) }} ++{{ warnf "No section found in %s" (.Page.Path | jsonify) }} +{{ end }} +{{ .Scratch.Set "section" $section }} + 37: 894f2cef3 = 33: fffd7a142 Move the "About" pages to `content/about/` 38: 1f7ef75b7 ! 34: 3f3dd9823 Convert 'About' pages to Hugo @@ content/about/trademark.html ## layouts/_default/baseof.html ## @@ - <div id="content-wrapper"> - {{ partial "sidebar.html" . }} <div id="content"> -- {{ .Content }} -+ {{ if eq $section "about" }} -+ <div id="main"> + {{ if (eq .Page.Path "/docs") }} + {{ partial "ref/index.html" . }} ++ {{ else if eq $section "about" }} ++ <div id="main"> + <h1>About</h1> + + <ol id="about-nav"> @@ layouts/_default/baseof.html + </ol> + {{ .Content }} + </div> -+ {{ else }} -+ {{ .Content }} -+ {{ end }} - </div> - </div> - {{ partial "footer.html" . }} + {{ else }} + {{ .Content }} + {{ end }} ## layouts/partials/sidebar.html ## @@ 39: c139f5c1f = 35: e743fb5cc about/trademark: add back old redirect 40: ded625e52 = 36: b68954145 Install redirects for two `About` pages 41: 14843ae22 = 37: a5f2b8be5 Remove dynamic About navigation code 42: 20e420d65 = 38: 100808034 Migrate the community page from the web app to the static site 43: dd4f0b599 ! 39: 7a4f64d25 Adjust the community page @@ content/community/_index.html <div id="main"> <h1> Community</h1> - <h2> Mailing List</h2> - - <p> -- General questions or comments for the Git community can be sent to the mailing list by using the email address <a href="mailto:git@xxxxxxxxxxxxxxx">git@xxxxxxxxxxxxxxx</a>. -+ General questions or comments for the Git community can be sent to the mailing list by using the email address <a href="mailto:git@xxxxxxxxxxxxxxx">git@xxxxxxxxxxxxxxx</a>. - </p> -- -+ - <p> - <strong>If you wish to report any possible bug for Git, please use this mailing list as well.</strong> - </p> @@ <p> 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>. 44: 6625d81dd = 40: 6767a083e Adjust the GUI clients link in the sidebar 45: 293e57ba7 = 41: 4a2f13d4b Move the GUI Clients page into the location expected by Hugo 46: 45c7d59a6 ! 42: df1b23221 Adapt the GUI Clients page to Hugo @@ Commit message the JSON containing the list of GUI Clients into individual files inside `data/`. + The files in `data/guis/` were written via: + + git show HEAD:resources/guis.yml | + ruby -e ' + # cannot use YAML.load because that would lose comments + $filename = nil + $content = "" + def write + return if $filename.nil? + File.open($filename, "w") { |f| f.write("---\n#{$content}---\n") } unless $filename.nil? + $filename = nil + $content = "" + end + ARGF.each do |line| + if line.start_with?("-") then + write + line[0] = " " + end + if line.start_with?(" ") then + $filename = "data/guis/#{line[8..].chomp.gsub(/ *\(.*\)$/, "").gsub(/ /, "-").downcase}.yml" if line.start_with?(" name: ") + $content += line[2..] + .gsub(/^image_tag: /, "\\0images/") + .gsub(/^url:/, "project_url:") + .gsub(/([-:] )([^"]*[^"0-9][^"]*)\n$/, "\\1\"\\2\"\n") + .gsub(/^-/, " -") + end + end + write + ' + Signed-off-by: Victoria Dye <vdye@xxxxxxxxxx> Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> @@ data/guis/anchorpoint.yml (new) ## data/guis/aurees.yml (new) ## @@ +--- -+name: "Aurees" ++name: "Aurees (no longer under active development)" +project_url: "https://aurees.com/" +image_tag: "images/guis/aurees@xxxxxx" +platforms: @@ data/guis/gitahead.yml (new) + - "Linux" +price: "Free" +license: "MIT" -+order: 13 ++order: 17 +--- ## data/guis/gitatomic.yml (new) ## @@ data/guis/gitbreeze.yml (new) + - "Linux" +price: "Free" +license: "Proprietary" -+trend_name: "git breeze" +order: 53 +--- @@ data/guis/gitdrive.yml (new) +image_tag: "images/guis/gitdrive@xxxxxx" +platforms: + - "iOS" -+# No details what the $6.99 in-app purchase does, all it says is 'Unlimited Version' ++# No details what the $6.99 in-app purchase does, all it says is "Unlimited Version" +price: "Free / $6.99" +license: "Proprietary" +order: 37 @@ data/guis/github-desktop.yml (new) +order: 1 +--- - ## data/guis/gitkraken.yml (new) ## + ## data/guis/gitkraken-cli.yml (new) ## @@ +--- -+name: "GitKraken" -+project_url: "https://www.gitkraken.com/" -+image_tag: "images/guis/git-kraken@xxxxxx" ++name: "GitKraken CLI" ++project_url: "https://www.gitkraken.com/cli" ++image_tag: "images/guis/gk-cli-keifs@xxxxxx" ++platforms: ++ - "Windows" ++ - "Mac" ++ - "Linux" ++price: "Free / $48.00+/user annually" ++license: "Proprietary" ++order: 55 ++--- + + ## data/guis/gitkraken-desktop.yml (new) ## +@@ ++--- ++name: "GitKraken Desktop" ++project_url: "https://www.gitkraken.com/git-client" ++image_tag: "images/guis/gitkraken-2024@xxxxxx" +platforms: + - "Windows" + - "Mac" + - "Linux" +# Free tier only works with local and public repositories -+price: "Free / $59+/user annually" ++price: "Free / $48+/user annually" +license: "Proprietary" +order: 5 +--- @@ data/guis/gitnuro.yml (new) @@ +--- +name: "Gitnuro" -+project_url: "https://gitnuro.jetpackduba.com/" ++project_url: "https://gitnuro.com/" +image_tag: "images/guis/Gitnuro@xxxxxx" +platforms: + - "Windows" @@ data/guis/gitui.yml (new) @@ +--- +name: "GitUI" -+project_url: "https://extrawurst.itch.io/gitui" ++project_url: "http://gitui.org" +image_tag: "images/guis/gitui@xxxxxx" +platforms: + - "Windows" @@ data/guis/glint.yml (new) + - "Windows" + - "Mac" + - "Linux" -+price: "Free (can only open up to two repositories at once)/ $35/user annually" ++# Free tier can only open up to two repositories at once ++price: "Free / $35/user annually" +license: "Proprietary" +trend_name: "git glint" -+order: 52 ++order: 13 +--- ## data/guis/guitar.yml (new) ## @@ data/guis/pragma-git.yml (new) +license: "MIT" +trend_name: "pragma github" +order: 54 ++--- + + ## data/guis/relagit.yml (new) ## +@@ ++--- ++name: "RelaGit" ++project_url: "https://rela.dev" ++image_tag: "images/guis/relagit@xxxxxx" ++platforms: ++ - "Windows" ++ - "Mac" ++ - "Linux" ++price: "Free" ++license: "LGPL-3.0-or-later" ++order: 57 +--- ## data/guis/repoz.yml (new) ## @@ data/guis/snailgit.yml (new) +price: "Free (limited) / $9.99" +license: "Proprietary" +order: 32 ++--- + + ## data/guis/sourcegit.yml (new) ## +@@ ++--- ++name: "SourceGit" ++project_url: "https://sourcegit-scm.github.io" ++image_tag: "images/guis/sourcegit@xxxxxx" ++platforms: ++ - "Windows" ++ - "Mac" ++ - "Linux" ++price: "Free" ++license: "MIT" ++trend_name: "SourceGit" ++order: 56 +--- ## data/guis/sourcetree.yml (new) ## @@ resources/guis.yml (deleted) - price: Free - license: GNU GPL - order: 4 --- name: GitKraken -- url: https://www.gitkraken.com/ -- image_tag: guis/git-kraken@xxxxxx +-- name: GitKraken Desktop +- url: https://www.gitkraken.com/git-client +- image_tag: guis/gitkraken-2024@xxxxxx - platforms: - - Windows - - Mac - - Linux - # Free tier only works with local and public repositories -- price: Free / $59+/user annually +- price: Free / $48+/user annually - license: Proprietary - order: 5 -- name: Magit @@ resources/guis.yml (deleted) - license: GNU GPL - trend_name: Git giggle - order: 27 --- name: Aurees +-- name: Aurees (no longer under active development) - url: https://aurees.com/ - image_tag: guis/aurees@xxxxxx - platforms: @@ resources/guis.yml (deleted) - - Linux - price: Free - license: MIT -- order: 13 +- order: 17 -- name: Gittyup - url: https://murmele.github.io/Gittyup/ - image_tag: guis/gittyup@xxxxxx @@ resources/guis.yml (deleted) - license: Proprietary - order: 42 -- name: GitUI -- url: https://extrawurst.itch.io/gitui +- url: http://gitui.org - image_tag: guis/gitui@xxxxxx - platforms: - - Windows @@ resources/guis.yml (deleted) - trend_name: gitonic - order: 50 -- name: Gitnuro -- url: https://gitnuro.jetpackduba.com/ +- url: https://gitnuro.com/ - image_tag: guis/Gitnuro@xxxxxx - platforms: - - Windows @@ resources/guis.yml (deleted) - price: Free / $35/user annually - license: Proprietary - trend_name: git glint -- order: 52 +- order: 13 -- name: GitBreeze - url: https://gitbreeze.dev/ - image_tag: guis/gitbreeze@xxxxxx @@ resources/guis.yml (deleted) - license: MIT - trend_name: pragma github - order: 54 -- +-- name: GitKraken CLI +- url: https://www.gitkraken.com/cli +- image_tag: guis/gk-cli-keifs@xxxxxx +- platforms: +- - Windows +- - Mac +- - Linux +- price: Free / $48.00+/user annually +- license: Proprietary +- order: 55 +-- name: SourceGit +- url: https://sourcegit-scm.github.io +- image_tag: guis/sourcegit@xxxxxx +- platforms: +- - Windows +- - Mac +- - Linux +- price: Free +- license: MIT +- trend_name: SourceGit +- order: 56 +-- name: RelaGit +- url: https://rela.dev +- image_tag: guis/relagit@xxxxxx +- platforms: +- - Windows +- - Mac +- - Linux +- price: Free +- license: LGPL-3.0-or-later +- order: 57 47: 5b1e9a6de = 43: ad17fe7f0 downloads/guis: adjust the Javascript for filtering 48: 579cf46a3 = 44: e6208d5ed Move logos page to the new location 49: 99766e6fd = 45: ea70f19f1 Migrate logos page 50: 1a42a0157 = 46: bc0d9409a Create base Downloads page 51: 07c392bd8 = 47: 3d22dc37c Move the OS-specific Downloads pages into the location needed for Hugo 52: 6d315ee76 = 48: 17c567019 Implement the `site-param` shortcode 53: c2cbcaf82 = 49: 1812362c7 Hugo-ify Linux & Mac download pages 54: d55dfbd55 ! 50: 58a155e45 Add a script to store download data in `hugo.yml` @@ README.md: Or you can do it from GitHub (much slower) like this: ## hugo.yml ## @@ hugo.yml: params: - latest_version: 2.43.0 - latest_relnote_url: https://raw.github.com/git/git/master/Documentation/RelNotes/2.43.0.txt - latest_release_date: '2023-11-20' + latest_version: 2.46.0 + latest_relnote_url: https://raw.github.com/git/git/master/Documentation/RelNotes/2.46.0.txt + latest_release_date: '2024-07-29' + macos_installer: + url: https://sourceforge.net/projects/git-osx-installer/files/git-2.33.0-intel-universal-mavericks.dmg/download?use_mirror=autoselect + version: 2.33.0 @@ hugo.yml: params: + filename: git-2.33.0-intel-universal-mavericks.dmg + windows_installer: + portable32: -+ filename: PortableGit-2.43.0-32-bit.7z.exe -+ release_date: '2023-11-20' -+ version: 2.43.0 -+ url: https://github.com/git-for-windows/git/releases/download/v2.43.0.windows.1/PortableGit-2.43.0-32-bit.7z.exe ++ filename: PortableGit-2.45.2-32-bit.7z.exe ++ release_date: '2024-06-03' ++ version: 2.45.2 ++ url: https://github.com/git-for-windows/git/releases/download/v2.45.2.windows.1/PortableGit-2.45.2-32-bit.7z.exe + portable64: -+ filename: PortableGit-2.43.0-64-bit.7z.exe -+ release_date: '2023-11-20' -+ version: 2.43.0 -+ url: https://github.com/git-for-windows/git/releases/download/v2.43.0.windows.1/PortableGit-2.43.0-64-bit.7z.exe ++ filename: PortableGit-2.45.2-64-bit.7z.exe ++ release_date: '2024-06-03' ++ version: 2.45.2 ++ url: https://github.com/git-for-windows/git/releases/download/v2.45.2.windows.1/PortableGit-2.45.2-64-bit.7z.exe + installer32: -+ filename: Git-2.43.0-32-bit.exe -+ release_date: '2023-11-20' -+ version: 2.43.0 -+ url: https://github.com/git-for-windows/git/releases/download/v2.43.0.windows.1/Git-2.43.0-32-bit.exe ++ filename: Git-2.45.2-32-bit.exe ++ release_date: '2024-06-03' ++ version: 2.45.2 ++ url: https://github.com/git-for-windows/git/releases/download/v2.45.2.windows.1/Git-2.45.2-32-bit.exe + installer64: -+ filename: Git-2.43.0-64-bit.exe -+ release_date: '2023-11-20' -+ version: 2.43.0 -+ url: https://github.com/git-for-windows/git/releases/download/v2.43.0.windows.1/Git-2.43.0-64-bit.exe ++ filename: Git-2.45.2-64-bit.exe ++ release_date: '2024-06-03' ++ version: 2.45.2 ++ url: https://github.com/git-for-windows/git/releases/download/v2.45.2.windows.1/Git-2.45.2-64-bit.exe ## app/services/download_service.rb => script/update-download-data.rb ## @@ 55: d58f20885 = 51: cce79c1a0 Downloads (MacOS): show relative date 56: 85d109ff6 = 52: c0340b223 Convert the Windows downloads page to Hugo 57: 5599b85a8 = 53: 5ae935edb download(win): white-space fix 58: 6d1b7c7f3 = 54: 0b69e8c0c Add 'Downloads' page aliases 59: 2f572e985 = 55: 8a9b19d16 Move the documentation landing page in place for Hugo 60: 1a96900f1 = 56: fc28e03d2 Migrate the 'Documentation' landing page to Hugo 61: 2f53db947 < -: --------- Move the "references" landing page to `content/` -: --------- > 57: 49631dde3 Move the "references" landing page to `layouts/partials/`, in preparation for using it as a Hugo partial template. 62: dd874d4c7 = 58: 59972c75f Move the files defining the documentation categories 63: 60f601394 ! 59: 5ecc96a9a Adjust the "references" page to Hugo @@ Commit message Signed-off-by: Victoria Dye <vdye@xxxxxxxxxx> Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> - ## content/docs/_index.html ## -@@ --<%- @section = 'documentation' %> --<%- @subsection = 'reference' %> --<%- @page_title = "Git - Reference" %> -+--- -+section: "documentation" -+subsection: "reference" -+title: "Git - Reference" -+url: /docs.html -+aliases: -+- /docs/index.html -+--- - - <div id='main'> - <h1>Reference</h1> - <div class='callout quickref'> - <p> - Quick reference guides: -- <%= link_to "GitHub Cheat Sheet", "https://github.github.com/training-kit/" %> -+ <a href="https://github.github.com/training-kit/">GitHub Cheat Sheet</a> - | -- <%= link_to "Visual Git Cheat Sheet", "https://ndpsoftware.com/git-cheatsheet.html" %> -+ <a href="https://ndpsoftware.com/git-cheatsheet.html">Visual Git Cheat Sheet</a> - </p> - </div> - <div class="callout all-commands"> -@@ - <div class='reference-menu'> - <div class='two-column'> - <div class='column-left'> -- <%= render 'shared/ref/setup' %> -- <%= render 'shared/ref/creating' %> -- <%= render 'shared/ref/snapshot' %> -- <%= render 'shared/ref/branching' %> -- <%= render 'shared/ref/sharing' %> -- <%= render 'shared/ref/inspection' %> -- <%= render 'shared/ref/patching' %> -- <%= render 'shared/ref/debugging' %> -+ {{< category "setup" >}} -+ {{< category "projects" >}} -+ {{< category "snapshotting" >}} -+ {{< category "branching" >}} -+ {{< category "sharing" >}} -+ {{< category "inspection" >}} -+ {{< category "patching" >}} -+ {{< category "debugging" >}} - </div> - <div class='column-right'> -- <%= render 'shared/ref/guides' %> -- <%= render 'shared/ref/email' %> -- <%= render 'shared/ref/external' %> -- <%= render 'shared/ref/admin' %> -- <%= render 'shared/ref/server' %> -- <%= render 'shared/ref/plumbing' %> -+ {{< category "guides" >}} -+ {{< category "email" >}} -+ {{< category "external" >}} -+ {{< category "admin" >}} -+ {{< category "server-admin" >}} -+ {{< category "plumbing" >}} - </div> - </div> - </div> - ## data/doc_categories/admin.yml ## @@ -<h3 class='admin'>Administration</h3> @@ data/doc_categories/setup.yml - <li><%= man('git-config') %></li> - <li><%= man('git-help') %></li> - <li><%= man('git-bugreport') %></li> -- <%= link_to "Credential helpers", "/doc/credential-helpers", sidebar_link_options("credential-helpers") %> +- <li><%= link_to "Credential helpers", "/doc/credential-helpers", sidebar_link_options("credential-helpers") %></li> -</ul> +--- +category_id: "setup" @@ layouts/partials/ref/category.html (new) +<ul class='unstyled'> + {{ range $doc := $category.commands }} + <li> -+ <a href="{{ print "docs/" $doc.doc (cond (and (ne .Params.lang nil) (ne $doc.no_append_lang true)) (print "/" .Params.lang) "") }}"> ++ <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) "") }}"> + {{ if (eq $doc.title nil) }} + {{ replace $doc.doc "git-" "" }} + {{ else }} @@ layouts/partials/ref/category.html (new) + {{ end }} +</ul> - ## layouts/shortcodes/category.html (new) ## + ## layouts/partials/ref/index.html ## @@ -+{{ .Scratch.Set "category_id" (.Get 0) }} -+{{ partial "ref/category.html" . }} +-<%- @section = 'documentation' %> +-<%- @subsection = 'reference' %> +-<%- @page_title = "Git - Reference" %> +- ++{{ $context := . }} + <div id='main'> + <h1>Reference</h1> + <div class='callout quickref'> + <p> + Quick reference guides: +- <%= link_to "GitHub Cheat Sheet", "https://github.github.com/training-kit/" %> ++ <a href="https://github.github.com/training-kit/">GitHub Cheat Sheet</a> + | +- <%= link_to "Visual Git Cheat Sheet", "https://ndpsoftware.com/git-cheatsheet.html" %> ++ <a href="https://ndpsoftware.com/git-cheatsheet.html">Visual Git Cheat Sheet</a> + </p> + </div> + <div class="callout all-commands"> +@@ + <div class='reference-menu'> + <div class='two-column'> + <div class='column-left'> +- <%= render 'shared/ref/setup' %> +- <%= render 'shared/ref/creating' %> +- <%= render 'shared/ref/snapshot' %> +- <%= render 'shared/ref/branching' %> +- <%= render 'shared/ref/sharing' %> +- <%= render 'shared/ref/inspection' %> +- <%= render 'shared/ref/patching' %> +- <%= render 'shared/ref/debugging' %> ++ {{ range slice "setup" "projects" "snapshotting" "branching" "sharing" "inspection" "patching" "debugging" }} ++ {{ $context.Scratch.Set "category_id" . }} ++ {{ partial "ref/category.html" $context }} ++ {{ end }} + </div> + <div class='column-right'> +- <%= render 'shared/ref/guides' %> +- <%= render 'shared/ref/email' %> +- <%= render 'shared/ref/external' %> +- <%= render 'shared/ref/admin' %> +- <%= render 'shared/ref/server' %> +- <%= render 'shared/ref/plumbing' %> ++ {{ range slice "guides" "email" "external" "admin" "server-admin" "plumbing" }} ++ {{ $context.Scratch.Set "category_id" . }} ++ {{ partial "ref/category.html" $context }} ++ {{ end }} + </div> + </div> + </div> 64: a3b5464d2 = 60: 10c090f35 Migrate 'Videos' page 65: ebbf2dc93 ! 61: 9bc68f7dd Migrate individual video pages @@ app/views/doc/watch.html.erb (deleted) ## layouts/_default/baseof.html ## @@ - <div id="content-wrapper"> - {{ partial "sidebar.html" . }} <div id="content"> -- {{ if eq $section "about" }} -+ {{ if isset .Params "video_title" }} -+ <div id="main"> + {{ if (eq .Page.Path "/docs") }} + {{ partial "ref/index.html" . }} ++ {{ else if isset .Params "video_title" }} ++ <div id="main"> + <h1>{{ .Params.category }} Episode {{ .Params.episode }}</h1> + <h2>{{ .Params.video_title }}</h2> + @@ layouts/_default/baseof.html + <iframe src="https://player.vimeo.com/video/{{ .Params.ext_id }}?title=0&byline=0&portrait=0&color=f14e32" width="635" height="360" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe> + </div> + </div> -+ {{ else if eq $section "about" }} - <div id="main"> + {{ else if eq $section "about" }} + <div id="main"> <h1>About</h1> - 66: d97d6afea ! 62: f0e2dad52 Migrate external links page to Hugo @@ app/views/doc/_ext_tutorials.erb => layouts/shortcodes/external/tutorials.html </p> - </li> + </li> - </ul> - </div> - <div class='column-right'> + <li> + <h4><a href="https://www.python4data.science/en/latest/productive/git/index.html">Git for Data Science</a></h4> + <p class='description'> @@ <p class='description'> Learn the basics of Git and Version Control through detailed and easy to follow steps. 67: 9549255ef = 63: b4755928a Migrate the 'About this site' page to Hugo 68: 4114ef8f4 = 64: 593d99e18 Migrate the Software Freedom Conservancy page 69: d05bb21d4 ! 65: 976c2b5c1 Hugo'ify the "credential helpers" page @@ layouts/partials/ref/category.html <ul class='unstyled'> {{ range $doc := $category.commands }} <li> -- <a href="{{ print "docs/" $doc.doc (cond (and (ne .Params.lang nil) (ne $doc.no_append_lang true)) (print "/" .Params.lang) "") }}"> +- <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) "") }}"> + {{ if (eq $doc.doc "credential-helpers") }} + <a href="doc/credential-helpers"> + {{ else }} -+ <a href="{{ print "docs/" $doc.doc (cond (and (ne .Params.lang nil) (ne $doc.no_append_lang true)) (print "/" .Params.lang) "") }}"> ++ <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) "") }}"> + {{ end }} {{ if (eq $doc.title nil) }} {{ replace $doc.doc "git-" "" }} 70: 00a42808e = 66: ccecdf517 Move the search results page 71: 5a183e236 ! 67: 00d2b544f Implement client-side search using Pagefind @@ Commit message Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> + ## .github/workflows/ci.yml ## +@@ .github/workflows/ci.yml: jobs: + steps: + - uses: actions/checkout@v4 + +- - name: configure Hugo version ++ - name: configure Hugo and Pagefind version + run: | + set -x && + echo "HUGO_VERSION=$(sed -n 's/^ *hugo_version: *//p' <hugo.yml)" >>$GITHUB_ENV ++ echo "PAGEFIND_VERSION=$(sed -n 's/^ *pagefind_version: *//p' <hugo.yml)" >>$GITHUB_ENV + + - name: install Hugo ${{ env.HUGO_VERSION }} + run: | +@@ .github/workflows/ci.yml: jobs: + - name: run Hugo to build the pages + run: hugo + ++ - name: run Pagefind ${{ env.PAGEFIND_VERSION }} to build the search index ++ run: npx -y pagefind@${{ env.PAGEFIND_VERSION }} --site public ++ + - name: build tar archive + run: cd public && tar czvf ../pages.tar.gz * + + ## README.md ## -@@ README.md: You'll need the extended version of [Hugo](https://gohugo.io/). On Windows, we r +@@ README.md: To render the site locally, you'll need the extended version of [Hugo](https://g 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. @@ README.md: You'll need the extended version of [Hugo](https://gohugo.io/). On Wi (TODO!) @@ README.md: The [list of GUI clients](https://git-scm.com/downloads/guis) has been construct - - 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. - --## Useful link regarding working with Hugo -+## Useful links regarding working with Hugo and Pagefind - * https://gohugo.io/ * https://gohugo.io/content-management/shortcodes/ -+* https://pagefind.app/ ++### Pagefind (client-side search) ++ ++* https://pagefind.app/ ++ ## License + The source code for the site is licensed under the MIT license, which you can find in ## assets/sass/search.scss ## @@ @@ content/search/results.html + <ol class="full-search-results"></ol> </div> + ## hugo.yml ## +@@ hugo.yml: markup: + unsafe: true + params: + hugo_version: 0.134.1 ++ pagefind_version: 1.1.1 + latest_version: 2.46.0 + latest_relnote_url: https://raw.github.com/git/git/master/Documentation/RelNotes/2.46.0.txt + latest_release_date: '2024-07-29' + ## static/js/application.js ## @@ static/js/application.js: var Search = { selectedIndex: 0, 72: 1c55e8995 ! 68: fd440ba66 Implement "live search" using Pagefind @@ static/js/application.js: var Search = { - }) + (async () => { + Search.pagefind = await import(`${baseURLPrefix}pagefind/pagefind.js`); -+ Search.pagefind.init(); ++ await Search.pagefind.init(); + await callback(); + })().catch(console.log); }, 73: 99046a762 = 69: 03d63b90d Mark pages with the correct language 74: 3d70c728b ! 70: a12108c7f Direct Pagefind what parts of the pages to index @@ layouts/_default/baseof.html {{ partial "sidebar.html" . }} - <div id="content"> + <div id="content" data-pagefind-body> - {{ if isset .Params "video_title" }} - <div id="main"> - <h1>{{ .Params.category }} Episode {{ .Params.episode }}</h1> + {{ if (eq .Page.Path "/docs") }} + {{ partial "ref/index.html" . }} + {{ else if isset .Params "video_title" }} 75: 1b6bb3c10 = 71: 23433ad6d Live search: only load 10 results at a time 76: cd28ebbe1 ! 72: a688de393 Respect the language in non-live search @@ static/js/application.js: var Search = { } (async () => { Search.pagefind = await import(`${baseURLPrefix}pagefind/pagefind.js`); ++ const options = {} + const language = this.getQueryValue('language'); -+ if (language) Search.pagefind.options({language}); - Search.pagefind.init(); ++ if (language) options.language = language; ++ await Search.pagefind.options(options); + await Search.pagefind.init(); await callback(); })().catch(console.log); 77: 338a2e6c0 ! 73: 2a357df65 Use Pagefind's UI for the full search @@ layouts/_default/baseof.html <link href='/favicon.ico' rel='shortcut icon' type='image/x-icon'> + {{ if eq $section "search" }}<link href="{{ relURL "pagefind/pagefind-ui.css" }}" rel="stylesheet">{{ end }} - {{ $style := resources.Get "sass/application.scss" | resources.ExecuteAsTemplate "application.scss" . | resources.ToCSS | resources.Minify }} + {{ $style := resources.Get "sass/application.scss" | resources.ExecuteAsTemplate "application.scss" . | css.Sass | resources.Minify }} <link rel="stylesheet" href="{{ $style.RelPermalink }}"> <script src="/js/modernizr.js"></script> @@ layouts/partials/header.html + + {{ if ne (.Scratch.Get "section") "search" }} <form id="search" action="/search/results"> - <input id="search-text" name="search" placeholder="Search entire site..." autocomplete="off" type="text" /> + <input id="search-text" name="search" placeholder="Type / to search entire site…" autocomplete="off" type="text" /> </form> <div id="search-results"></div> + {{ end }} @@ static/js/application.js: var Search = { - this.initializeSearchIndex(async () => { - const results = await Search.pagefind.search(searchTerm); - if (!results || !results.results || !results.results.length) return; -+ new PagefindUI({ element: "#search-div", showSubResults: true, language }); - +- - const list = (await Promise.all(results.results.map(async e => { - const result = await e.data(); - const href = result.url; @@ static/js/application.js: var Search = { - <a class="url" href="${href}">${href}</a> - <p>${result.excerpt}</p></li>`; - }))).join(''); -- ++ new PagefindUI({ ++ element: "#search-div", ++ showSubResults: true, ++ language ++ }); + - searchResultsElements[0].innerHTML = list || '<li>No results found</li>'; - }) + const searchTerm = this.getQueryValue('search'); -: --------- > 74: c7ff8dad0 Add a helper script to run Pagefind -: --------- > 75: c32fdf3dc ci: verify the order of the search results 78: 8147144aa = 76: 26ff8cb61 Implement the `relurl` shortcode 79: 61d1610e4 ! 77: c8a20dffc Use relative URLs @@ content/community/_index.html: aliases: <h2> Contributing to Git </h2> <p> -- 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>. -+ 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>. +- 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>. ++ 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>. </p> <p> @@ content/doc/_index.html: aliases: </div> - ## content/docs/_index.html ## -@@ content/docs/_index.html: aliases: - </p> - </div> - <div class="callout all-commands"> -- <a href="/docs/git#_git_commands">Complete list of all commands</a> -+ <a href="{{< relurl "docs/git#_git_commands" >}}">Complete list of all commands</a> - </div> - <div class='reference-menu'> - <div class='two-column'> - ## content/downloads/_index.html ## @@ content/downloads/_index.html: aliases: <table class="binaries"> @@ layouts/_default/baseof.html + <link href="{{ relURL "favicon.ico" }}" rel='shortcut icon' type='image/x-icon'> {{ if eq $section "search" }}<link href="{{ relURL "pagefind/pagefind-ui.css" }}" rel="stylesheet">{{ end }} - {{ $style := resources.Get "sass/application.scss" | resources.ExecuteAsTemplate "application.scss" . | resources.ToCSS | resources.Minify }} + {{ $style := resources.Get "sass/application.scss" | resources.ExecuteAsTemplate "application.scss" . | css.Sass | resources.Minify }} <link rel="stylesheet" href="{{ $style.RelPermalink }}"> - <script src="/js/modernizr.js"></script> - <script src="/js/modernize.js"></script> @@ layouts/partials/header.html {{ if ne (.Scratch.Get "section") "search" }} - <form id="search" action="/search/results"> + <form id="search" action="{{ relURL "search/results" }}"> - <input id="search-text" name="search" placeholder="Search entire site..." autocomplete="off" type="text" /> + <input id="search-text" name="search" placeholder="Type / to search entire site…" autocomplete="off" type="text" /> </form> <div id="search-results"></div> @@ layouts/partials/ref/category.html - <a href="doc/credential-helpers"> + <a href="{{ relURL "doc/credential-helpers" }}"> {{ else }} -- <a href="{{ print "docs/" $doc.doc (cond (and (ne .Params.lang nil) (ne $doc.no_append_lang true)) (print "/" .Params.lang) "") }}"> -+ <a href="{{ relURL (print "docs/" $doc.doc (cond (and (ne .Params.lang nil) (ne $doc.no_append_lang true)) (print "/" .Params.lang) "")) }}"> +- <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) "") }}"> ++ <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) "")) }}"> {{ end }} {{ if (eq $doc.title nil) }} {{ replace $doc.doc "git-" "" }} + ## layouts/partials/ref/index.html ## +@@ + </p> + </div> + <div class="callout all-commands"> +- <a href="/docs/git#_git_commands">Complete list of all commands</a> ++ <a href="{{ relURL "docs/git#_git_commands" }}">Complete list of all commands</a> + </div> + <div class='reference-menu'> + <div class='two-column'> + ## layouts/partials/sidebar.html ## @@ <nav> 80: 199ad72ad = 78: 943375d44 Accommodate for base URLs other than `/` 81: 905f3d1b1 ! 79: 312d96605 ci: ensure that there are only relative URLs @@ .github/workflows/ci.yml: jobs: + exit 1 + fi + - - name: run Pagefind to build the search index - run: npx -y pagefind --site public + - name: run Pagefind ${{ env.PAGEFIND_VERSION }} to build the search index + run: npx -y pagefind@${{ env.PAGEFIND_VERSION }} --site public 82: efccaef38 = 80: f54c9f6af Drop the Rails version of the ProGit book 83: 046ae69cf = 81: 05158a866 Move the ProGit book related Ruby code to a new location 84: 1b6c1ef2c ! 82: 34b4c74b9 Transmogrify the Rake script to generate the Pro Git book sections @@ README.md: Now you need to get the latest downloads for the downloads pages: +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/). -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: -+If you want to build the book for all available languages, just skip the language code: ++If you want to build the book for all available languages, just omit the language code parameter: - $ GENLANG=fr GENPATH=../progit2-fr rake local_genbook2 + $ ruby ./script/update-book2.rb @@ README.md: Now you need to get the latest downloads for the downloads pages: ## Contributing + ## hugo.yml ## +@@ hugo.yml: markup: + goldmark: + renderer: + unsafe: true ++module: ++ mounts: ++ - source: content ++ target: content ++ - source: static ++ target: static ++ - source: data ++ target: data ++ - source: external/book/data/book ++ target: data/book ++ - source: external/book/content/book ++ target: content/book ++ - source: external/book/static/book ++ target: static/book + params: + hugo_version: 0.134.1 + pagefind_version: 1.1.1 + ## layouts/_default/baseof.html ## @@ </div> <!-- .inner --> @@ script/book.rb: class Book < ApplicationRecord + } + end + ++ def content_note ++ "### DO NOT EDIT! Generated by script/update-book2.rb" ++ end ++ ++ def wrap_front_matter(front_matter) ++ "#{front_matter.to_yaml.sub("---\n", "---\n#{self.content_note}\n")}---\n" ++ end ++ + def absolute_path(path) -+ File.absolute_path(File.join(File.dirname(__FILE__), "..", "content", "book", @language_code, "v#{@edition}", path)) ++ File.absolute_path(File.join(File.dirname(__FILE__), "..", "external", "book", "content", "book", @language_code, "v#{@edition}", path)) + end + + def removeAllFiles @@ script/book.rb: class Book < ApplicationRecord + return front_matter + end + ++ def wrap_front_matter(front_matter) ++ @book.wrap_front_matter(front_matter) ++ end ++ + attr_accessor :title + attr_accessor :chapter_type + attr_accessor :chapter_number @@ script/book.rb: class Book < ApplicationRecord + def save + return if self.slug.nil? + -+ front_matter = "#{self.front_matter.to_yaml}\n---\n" -+ + path = self.absolute_path(self.slug) + FileUtils.mkdir_p(File.dirname(path)) + File.open("#{path}.html", 'w') do |file| -+ file.write(front_matter) ++ file.write(@chapter.wrap_front_matter(self.front_matter)) + file.write(self.html.strip) + end end 85: cdb7f4377 ! 83: 803869e33 update-book2: adjust navigation @@ layouts/shortcodes/previous-section.html (new) ## script/book.rb ## @@ script/book.rb: class Book - File.absolute_path(File.join(File.dirname(__FILE__), "..", "content", "book", @language_code, "v#{@edition}", path)) + File.absolute_path(File.join(File.dirname(__FILE__), "..", "external", "book", "content", "book", @language_code, "v#{@edition}", path)) end + def relative_url(path) 86: 44c163eaa ! 84: f6734888d update-book2: also include the images @@ Commit message ## script/book.rb ## @@ script/book.rb: class Book - } + "#{front_matter.to_yaml.sub("---\n", "---\n#{self.content_note}\n")}---\n" end - def absolute_path(path) -- File.absolute_path(File.join(File.dirname(__FILE__), "..", "content", "book", @language_code, "v#{@edition}", path)) +- File.absolute_path(File.join(File.dirname(__FILE__), "..", "external", "book", "content", "book", @language_code, "v#{@edition}", path)) + def absolute_path(path, top_level = "content") -+ File.absolute_path(File.join(File.dirname(__FILE__), "..", top_level, "book", @language_code, "v#{@edition}", path)) ++ File.absolute_path(File.join(File.dirname(__FILE__), "..", "external", "book", top_level, "book", @language_code, "v#{@edition}", path)) end def relative_url(path) 87: 7d0edee4b = 85: dcff0ad8c update-book2: show the chapter/section numbers in the titles 88: eb4e84dfd ! 86: 91531feee Generate the front page of the Pro Git book @@ script/book.rb: class Book + front_matter["book"]["ebook_mobi"] = self.ebook_mobi + end + -+ front_matter = "#{front_matter.to_yaml}\n---" -+ + path = self.absolute_path("_index.html") + FileUtils.mkdir_p(File.dirname(path)) + File.open(path, 'w') do |file| -+ file.write(front_matter) ++ file.write(self.wrap_front_matter(front_matter)) + end + ++ front_matter = { "redirect_to" => "book/#{@language_code}/v#{@edition}" } + File.open(self.absolute_path("../_index.html"), 'w') do |file| -+ file.write("---\nredirect_to: \"book/#{@language_code}/v#{@edition}\"\n---\n") ++ file.write(self.wrap_front_matter(front_matter)) + end ++ + if @language_code == "en" + File.open(self.absolute_path("../../_index.html"), 'w') do |file| -+ file.write("---\nredirect_to: \"book/#{@language_code}/v#{@edition}\"\n---\n") ++ file.write(self.wrap_front_matter(front_matter)) + end + end + end 89: 714b23ec6 ! 87: 419be615f book: also generate the URLs for the downloadable formats of the book @@ Commit message ... but do this only for the English version, as the other versions are not available in downloadable formats. + As the idea is to run this script in a GitHub workflow on a shallow + clone, let's be prepared for the situation where the tip commit of the + default branch is _not_ (yet?) tagged and therefore no tag is present in + the clone. In this situation, let's just go ahead and fetch all tags, + then move along and update the URLs accordingly. + Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> ## script/update-book2.rb ## @@ script/update-book2.rb: def local_genbook2(language_code, worktree_path) end + if language_code == 'en' + latest_tag = `git -C "#{worktree_path}" for-each-ref --format '%(refname:short)' --sort=-committerdate --count=1 refs/tags/`.chomp ++ if latest_tag.empty? ++ puts "No tag found in #{worktree_path}, trying to fetch tags" ++ 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 ++ raise "Still no tags in #{worktree_path}?" if latest_tag.empty? ++ end + book.ebook_pdf = "https://github.com/progit/progit2/releases/download/#{latest_tag}/progit.pdf" + book.ebook_epub = "https://github.com/progit/progit2/releases/download/#{latest_tag}/progit.epub" + book.ebook_mobi = "https://github.com/progit/progit2/releases/download/#{latest_tag}/progit.mobi" 90: 68e96e524 < -: --------- Record SHA of the Pro Git book even when generating it locally -: --------- > 88: 406254667 update-book2.rb: gracefully warn about missing files, but continue -: --------- > 89: 9f7d416f5 Record SHA of the Pro Git book even when generating it locally 91: 915329bc2 ! 90: e6e83c127 Pro Git book: populate the `Chapters` drop-down @@ layouts/_default/baseof.html {{ partial "footer.html" . }} </div> <!-- #content-wrapper --> {{ else if (isset .Params "book") }} -+ {{ .Scratch.Set "book" (index .Site.Data (print "book-" .Params.book.language_code)) }} ++ {{ .Scratch.Set "book" (index .Site.Data.book .Params.book.language_code) }} <div class="inner"> <div id="content-wrapper"> {{ partial "sidebar.html" . }} @@ script/book.rb: class Book + "language_code" => @language_code, + "chapters" => chapters + } -+ path = File.join(File.dirname(__FILE__), "..", "data", "book-#{@language_code}.yml") ++ path = File.join(File.dirname(__FILE__), "..", "external", "book", "data", "book", "#{@language_code}.yml") + FileUtils.mkdir_p(File.dirname(path)) + File.open(path, 'w') do |file| ++ file.write("#{self.content_note}\n") + file.write(data.to_yaml.strip) + end + 92: 6156ecfdd = 91: 1a51fcc94 book: keep colons and other special characters in URLs 93: 2dd98a59e = 92: e492c9c63 book: fix translated labels 94: 289ee87b7 ! 93: 7f3b191a3 book: generate cross-references correctly @@ script/book.rb: class Book def front_matter @@ script/book.rb: class Book - file.write("---\nredirect_to: \"book/#{@language_code}/v#{@edition}\"\n---\n") + file.write(self.wrap_front_matter(front_matter)) end end + @@ script/book.rb: class Book + @xrefs.each do |id_xref, section| + path = self.absolute_path("ch00/#{id_xref}.html") + relurl = "#{section.relative_url(nil)}##{id_xref}" ++ front_matter = { "redirect_to" => relurl } + File.open(path, 'w') do |file| -+ file.write("---\nredirect_to: \"#{relurl}\"\n---\n") ++ file.write(self.wrap_front_matter(front_matter)) + end + end end 95: ded726a6c = 94: 4a31646bc book-tl: work around an incorrect image reference 96: 4078053ef = 95: 01b1dd71f book: work around lagging translations' image paths 97: 8f4d3d8a3 ! 96: 731b6b686 book(xrefs): maintain question marks that are part of the URL path @@ Commit message sections' titles; These need to be (URL-)encoded in the URL and not be mistaken for the separator between path and GET parameter(s). + While at it, also install redirects for URLs containing a question mark + so that incorrect URLs that contain a literal question mark (which is + interpreted as separator between the URL path and the `GET` + parameter(s)) redirect to the correct ones. + Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> ## script/book.rb ## @@ script/book.rb: class Section } if @slug =~ /:|[^-A-Za-z0-9_]/ - front_matter["url"] = self.relative_url(@slug) -+ front_matter["url"] = self.relative_url(@slug).gsub(/%3F/, '?') ++ relative_url = self.relative_url(@slug) ++ front_matter["url"] = "/#{relative_url.gsub(/%3F/, '?')}.html" ++ if relative_url =~ /%3F/ ++ front_matter["aliases"] = [ ++ "/#{relative_url.gsub(/%3F.*/, '')}.html" ++ ] ++ end end return front_matter end 98: b99c5d1d5 ! 97: 0343bf0ab book: redirect missing xrefs to the English book @@ script/book.rb: class Book @xrefs.each do |id_xref, section| path = self.absolute_path("ch00/#{id_xref}.html") - relurl = "#{section.relative_url(nil)}##{id_xref}" +- front_matter = { "redirect_to" => relurl } + if section == 'redirect-to-en' + url = "book/en/v2/ch00/#{id_xref}" + else + url = "#{section.relative_url(nil)}##{id_xref}" + end ++ front_matter = { "redirect_to" => url } File.open(path, 'w') do |file| -- file.write("---\nredirect_to: \"#{relurl}\"\n---\n") -+ file.write("---\nredirect_to: \"#{url}\"\n---\n") + file.write(self.wrap_front_matter(front_matter)) end - end - end ## script/update-book2.rb ## @@ script/update-book2.rb: def genbook(language_code, &get_content) 99: c3f723764 = 98: 8b30c3748 book: handle footnotes gracefully 100: 58dcc1d03 = 99: bedb26d29 book: fix a couple of broken redirects 101: be1163d71 = 100: f890fa149 docs: prepare for `index.rake` to become a proper Ruby script 102: 73280317a ! 101: 990ac016c Turn what used to be the `index.rake` file into a proper Ruby script @@ README.md: Note that this will take about 7 times as long, and the site will not Similarly, you can also populate the localized man pages. From a local clone of https://github.com/jnavila/git-html-l10n : + ## hugo.yml ## +@@ hugo.yml: module: + target: content/book + - source: external/book/static/book + target: static/book ++ - source: external/docs/data ++ target: data ++ - source: external/docs/content ++ target: content + params: + hugo_version: 0.134.1 + pagefind_version: 1.1.1 + ## layouts/_default/baseof.html ## @@ </div> @@ script/update-docs.rb +require_relative "version" + +SITE_ROOT = File.join(File.expand_path(File.dirname(__FILE__)), '../') -+DATA_FILE = "#{SITE_ROOT}data/docs.yml" ++DOCS_INDEX_FILE = "#{SITE_ROOT}external/docs/content/docs/_index.html" ++DATA_FILE = "#{SITE_ROOT}external/docs/data/docs.yml" + +def read_data + if File.exists?(DATA_FILE) @@ script/update-docs.rb def make_asciidoc(content) Asciidoctor::Document.new(content, +@@ script/update-docs.rb: def make_asciidoc(content) + doctype: "book") + end + ++def content_note ++ "### DO NOT EDIT! Generated by script/update-docs.rb\n" ++end ++ ++def wrap_front_matter(front_matter) ++ "#{front_matter.to_yaml.sub("---\n", "---\n#{content_note}\n")}---\n" ++end ++ + def expand_l10n(path, content, get_f_content, categories) + content.gsub!(/include::(\S+)\.txt/) do |line| + line.gsub!("include::", "") @@ script/update-docs.rb: def expand_l10n(path, content, get_f_content, categories) end @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content) next if doc_limit && path !~ /#{doc_limit}/ - file = DocFile.where(name: docname).first_or_create -+ doc_path = "#{SITE_ROOT}content/docs/#{docname}" ++ doc_path = "#{SITE_ROOT}external/docs/content/docs/#{docname}" puts " build: #{docname}" @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content) + + FileUtils.mkdir_p(doc_path) + File.open("#{doc_path}/#{tagname}.html", "w") do |out| -+ out.write("#{front_matter.to_yaml}\n---\n", out) ++ out.write(wrap_front_matter(front_matter)) + out.write(html, out) + end @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content) + FileUtils.mkdir_p(File.dirname(doc_path)) + File.open("#{doc_path}.html", "w") do |out| + front_matter["aliases"] = ["/docs/#{docname}/index.html"] -+ out.write("#{front_matter.to_yaml}\n---\n") ++ out.write(wrap_front_matter(front_matter)) + out.write(html) + end + end @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content) - Rails.cache.write("latest-version", Version.latest_version.name) + data["latest-version"] = tagname + end ++ ++ front_matter = { ++ "section" => "documentation", ++ "subsection" => "reference", ++ "title" => "Git - Reference", ++ "url" => "/docs.html", ++ "aliases" => ["/docs/index.html"] ++ } ++ ++ File.open(DOCS_INDEX_FILE, "w") do |out| ++ out.write(wrap_front_matter(front_matter)) ++ end ++ + File.open(DATA_FILE, "w") do |out| + YAML.dump(data, out) end 103: e34a4915b = 102: c7eb81c73 Highlight the `Reference` nav item for manual pages 104: 42a90c22a ! 103: 5d5bfe56a update-docs: install redirects for the various versions @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content) FileUtils.mkdir_p(doc_path) - File.open("#{doc_path}/#{tagname}.html", "w") do |out| -- out.write("#{front_matter.to_yaml}\n---\n", out) +- out.write(wrap_front_matter(front_matter)) - out.write(html, out) + front_matter_with_redirects = front_matter.clone + front_matter_with_redirects["aliases"] = @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content) + ["/docs/#{docname}/#{v}/index.html"] + end + File.open("#{doc_path}/#{doc_versions[changed_in]}.html", "w") do |out| -+ out.write("#{front_matter_with_redirects.to_yaml}\n---\n") ++ out.write(wrap_front_matter(front_matter_with_redirects)) + out.write(html) end 105: 6a68ca809 ! 104: 1fc1b2d4b Show the different versions of the manual pages @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content) - anchor += "-1" while ids.include?(anchor) - ids.add(anchor) - "<dt class=\"hdlist1\" id=\"#{anchor}\"> <a class=\"anchor\" href=\"##{anchor}\"></a>#{$1} </dt>" -+ if !File.exists?("#{SITE_ROOT}_generated-asciidoc/#{asciidoc_sha}") -+ FileUtils.mkdir_p("#{SITE_ROOT}_generated-asciidoc") -+ File.open("#{SITE_ROOT}_generated-asciidoc/#{asciidoc_sha}", "w") do |out| ++ if !File.exists?("#{SITE_ROOT}external/docs/asciidoc/#{asciidoc_sha}") ++ FileUtils.mkdir_p("#{SITE_ROOT}external/docs/asciidoc") ++ File.open("#{SITE_ROOT}external/docs/asciidoc/#{asciidoc_sha}", "w") do |out| + out.write(content) end end @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content) + page_data["diff-cache"] = {} if !page_data["diff-cache"] + cached_diff = page_data["diff-cache"]["#{pre_sha}..#{post_sha}"] + if !cached_diff -+ pre_content = File.read("#{SITE_ROOT}_generated-asciidoc/#{pre_sha}") -+ post_content = File.read("#{SITE_ROOT}_generated-asciidoc/#{post_sha}") ++ pre_content = File.read("#{SITE_ROOT}external/docs/asciidoc/#{pre_sha}") ++ post_content = File.read("#{SITE_ROOT}external/docs/asciidoc/#{post_sha}") + cached_diff = page_data["diff-cache"]["#{pre_sha}..#{post_sha}"] = diff(pre_content, post_content) + end + page_versions.unshift({ @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content) - data["latest-version"] = tagname + data["latest-version"] = version if !data["latest-version"] || Version.version_to_num(data["latest-version"]) < Version.version_to_num(version) end - File.open(DATA_FILE, "w") do |out| - YAML.dump(data, out) + + front_matter = { 106: 1ab0c772b ! 105: 9953afacd search: exclude older manual page versions @@ layouts/_default/baseof.html - <div id="main" data-pagefind-body> + <!-- older manual page versions are less interesting and need to be excluded from the search --> -+ {{ $include_in_search := (or (not (isset .Params "docname")) (isset .Params "latest-changes")) }} ++ {{ $include_in_search := (or (not (isset .Params "docname")) (isset .Params "latest-changes") (isset .Params "lang")) }} + <div id="main"{{ if $include_in_search }} data-pagefind-body{{ end }}> {{ .Content }} </div> @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content) File.open("#{doc_path}.html", "w") do |out| + front_matter["latest-changes"] = version front_matter["aliases"] = ["/docs/#{docname}/index.html"] - out.write("#{front_matter.to_yaml}\n---\n") + out.write(wrap_front_matter(front_matter)) out.write(html) 107: c1948441b ! 106: ec44e3010 search: offer the section as a filter @@ layouts/_default/baseof.html @@ <!-- older manual page versions are less interesting and need to be excluded from the search --> - {{ $include_in_search := (or (not (isset .Params "docname")) (isset .Params "latest-changes")) }} + {{ $include_in_search := (or (not (isset .Params "docname")) (isset .Params "latest-changes") (isset .Params "lang")) }} - <div id="main"{{ if $include_in_search }} data-pagefind-body{{ end }}> + <div id="main"{{ if $include_in_search }} data-pagefind-filter="category:{{ $section }}" data-pagefind-body{{ end }}> {{ .Content }} @@ layouts/_default/baseof.html {{ partial "sidebar.html" . }} - <div id="content" data-pagefind-body> + <div id="content" data-pagefind-filter="category:{{ $section }}" data-pagefind-body> - {{ if isset .Params "video_title" }} - <div id="main"> - <h1>{{ .Params.category }} Episode {{ .Params.episode }}</h1> + {{ if (eq .Page.Path "/docs") }} + {{ partial "ref/index.html" . }} + {{ else if isset .Params "video_title" }} 108: ad3768be5 < -: --------- search: give the manual pages' titles maximal weight -: --------- > 107: 5e6f328ce search: give the manual pages' titles maximal weight 109: 05a15df77 ! 108: 25906cd41 search: reintroduce the categories in the live search @@ Commit message We do this by specifically adding metadata indicating the category of manual pages as well as for the sections of the book (for details, see https://pagefind.app/docs/metadata/). Then, we use that information to - sort the results as they arrive into the correct table row (and - displaying it when necessary, as the empty rows are initially hidden). + sort the results into the correct table row. Due to the `async` nature + of the search results coming in, we need to be + careful to process them in order when doing that. Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> @@ layouts/_default/baseof.html 2nd Edition </span> </div> -- <div id="main" data-pagefind-filter="category:{{ $section }}" data-pagefind-body class="book edition2"> -+ <div id="main" data-pagefind-filter="category:{{ $section }}" data-pagefind-meta="category:Book" data-pagefind-body class="book edition2"> +- <div id="main" data-pagefind-filter="category:{{ $section }}" data-pagefind-weight="0.05" data-pagefind-body class="book edition2"> ++ <div id="main" data-pagefind-filter="category:{{ $section }}" data-pagefind-meta="category:Book" data-pagefind-weight="0.05" data-pagefind-body class="book edition2"> <h1>{{ .Params.book.section.cs_number }} {{ .Params.book.chapter.title }} - {{ .Params.book.section.title }}</h1> <div> {{ .Content }} @@ <!-- older manual page versions are less interesting and need to be excluded from the search --> - {{ $include_in_search := (or (not (isset .Params "docname")) (isset .Params "latest-changes")) }} -- <div id="main"{{ if $include_in_search }} data-pagefind-filter="category:{{ $section }}" data-pagefind-body{{ end }}> -+ <div id="main"{{ if $include_in_search }} data-pagefind-filter="category:{{ $section }}" data-pagefind-meta="category:Reference" data-pagefind-body{{ end }}> - {{ safe.HTML (replaceRE "(?s:>NAME</h2>.*?<p)" "$0 data-pagefind-weight=\"7\"" .Content) }} - </div> - </div> + {{ $include_in_search := (or (not (isset .Params "docname")) (isset .Params "latest-changes") (isset .Params "lang")) }} +- <div id="main"{{ if $include_in_search }} data-pagefind-filter="category:{{ $section }}" data-pagefind-weight="0.05" data-pagefind-body{{ end }}> ++ <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 }}> + {{ .Content }} + {{ $match := findRESubmatch "(?s)>NAME</h2>.*?<p[^>]*>(git-)?([^ ]+)" .Content 1 }} + {{ if (eq ($match | len) 1) }} ## static/js/application.js ## @@ static/js/application.js: var Search = { @@ static/js/application.js: var Search = { + const chunkLength = 10; let displayCount = 0; ++ ++ const categorizeResult = (i) => { ++ while (i < displayCount && typeof results.results[i].data === 'object') { ++ const result = results.results[i++]; ++ if (result.data.meta.category === 'Reference') { ++ if (ulReference.children().length === 0) ulReference.parent().parent().css("display", "table-row") ++ ulReference.append(result.li) ++ } else if (result.data.meta.category === 'Book') { ++ if (ulBook.children().length === 0) ulBook.parent().parent().css("display", "table-row") ++ ulBook.append(result.li) ++ } ++ } ++ }; ++ const loadResultsChunk = () => { -@@ static/js/application.js: var Search = { + if (loadButton.loading || displayCount >= results.results.length) return; + + loadButton.loading = true; + const n = displayCount + chunkLength; + while (displayCount < n) { +- const li = $("<li><a>…</a></li>"); +- li.insertBefore(loadButton); ++ const result = results.results[displayCount] ++ result.li = $("<li><a>…</a></li>"); ++ result.li.insertBefore(loadButton); + // load the result lazily - (async () => { - const result = await results.results[displayCount].data(); -+ if (result.meta.category === 'Reference') { -+ if (ulReference.children().length === 0) ulReference.parent().parent().css("display", "table-row") -+ ulReference.append(li) -+ } else if (result.meta.category === 'Book') { -+ if (ulBook.children().length === 0) ulBook.parent().parent().css("display", "table-row") -+ ulBook.append(li) -+ } - li.html(`<a href = "${result.url}">${result.meta.title}</a>`); - })().catch(console.log); +- (async () => { +- const result = await results.results[displayCount].data(); +- li.html(`<a href = "${result.url}">${result.meta.title}</a>`); +- })().catch(console.log); ++ (async (i) => { ++ result.data = await results.results[displayCount].data(); ++ if (!i || typeof results.results[i - 1].data === 'object') categorizeResult(i); ++ result.li.html(`<a href = "${result.data.url}">${result.data.meta.title}</a>`); ++ })(displayCount).catch((err) => { ++ console.log(err); ++ result.li.html(`<i>Error loading result</i>`); ++ }); + if (++displayCount >= results.results.length) { + loadButton.remove(); 110: 49f58f182 < -: --------- search: show manual pages' short title in the live search -: --------- > 109: 4f239bc08 search: show manual pages' short title in the live search -: --------- > 110: 347830d09 search: link to "pretty" URLs 111: 6fc0f8e9e = 111: cb68f3611 docs: handle multiple `linkgit:` in the same line 112: 1f9dc5079 = 112: 687183039 docs: correctly handle `{litdd}` in `linkgit` values 113: 44dca56fe = 113: 2b061bf40 docs: handle `gitlink:` gracfully 114: 702ce1711 = 114: 149872c37 docs: handle double colon in `linkgit::` gracefully 115: 26a84723f = 115: 25d0d5463 docs: handle `linkgit:curl[1]` gracefully 116: 9cb3ff53a = 116: 187843aba Allow `redirect_to` with fully-qualified URLs 117: 412391840 ! 117: f20b1c76d docs: add fall-back redirects for unrendered pages @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content) + # that are not populated. Let's redirect to the source files in the + # git/git repository. + check_paths.each do |path| -+ doc_path = "#{SITE_ROOT}content/#{path}.html" ++ doc_path = "#{SITE_ROOT}external/docs/content/#{path}.html" + if !File.exists?(doc_path) + type = 'blob' + target = path.sub(/^docs\//, '') @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content) + front_matter = { "redirect_to" => "https://github.com/git/git/#{type}/HEAD/Documentation/#{target}" } # ltrim `docs/` + FileUtils.mkdir_p(File.dirname(doc_path)) + File.open(doc_path, "w") do |out| -+ out.write("#{front_matter.to_yaml}\n---\n") ++ out.write(wrap_front_matter(front_matter)) + end + end + end 118: 55c7b5f48 ! 118: fcbfb1f1d docs: work around a `link:` bug in older versions @@ Metadata Author: Johannes Schindelin <johannes.schindelin@xxxxxx> ## Commit message ## - docs: work around a `link:` bug in older versions + docs: work around `link:`/`linkgit:` bugs in older versions Since we want to build older Git versions' manual pages, too, we have to work around bugs since fixed. @@ script/update-docs.rb: def index_doc(filter_tags, doc_list, get_content) + # Handle erroneous `link:api-trace2.txt`, see 4945f046c7f5 (api docs: + # link to html version of api-trace2, 2022-09-16) + content.gsub!(/link:api-trace2.txt/, 'link:api-trace2.html') ++ # Handle `linkgit:git-config.txt` mistake, fixed in ad52148a7d0 ++ # (Documentation: fix broken linkgit to git-config, 2016-03-21) ++ content.gsub!(/linkgit:git-config.txt/, 'linkgit:git-config') content.gsub!(/link:(?:technical\/)?(\S*?)\.html(\#\S*?)?\[(.*?)\]/m, "link:/docs/\\1\\2[\\3]") asciidoc = make_asciidoc(content) 119: a7344a9d2 ! 119: 5b1d692eb Migrate the translated manual pages to the Hugo world @@ layouts/_default/baseof.html <div id="content"> <div id='reference-version'> + {{ partial "ref/languages.html" . }} -+ {{ partialCached "ref/topics.html" . }} ++ {{ partial "ref/topics.html" . }} {{ partial "ref/versions.html" . }} </div> @@ script/update-docs.rb: def index_l10n_doc(filter_tags, doc_list, get_content) path = File.basename(full_path, ".txt") - file = DocFile.where(name: path).first_or_create -+ doc_path = "#{SITE_ROOT}content/docs/#{path}" ++ doc_path = "#{SITE_ROOT}external/docs/content/docs/#{path}" puts " build: #{path} for #{lang}" @@ script/update-docs.rb: def index_l10n_doc(filter_tags, doc_list, get_content) - html.gsub!(/linkgit:(\S+?)\[(\d+)\]/) do |line| - x = /^linkgit:(\S+?)\[(\d+)\]/.match(line) - "<a href='/docs/#{x[1].gsub(/-/, '-')}/#{lang}'>#{x[1]}[#{x[2]}]</a>" -+ if !File.exists?("#{SITE_ROOT}_generated-asciidoc/#{asciidoc_sha}") -+ FileUtils.mkdir_p("#{SITE_ROOT}_generated-asciidoc") -+ File.open("#{SITE_ROOT}_generated-asciidoc/#{asciidoc_sha}", "w") do |out| ++ if !File.exists?("#{SITE_ROOT}external/docs/asciidoc/#{asciidoc_sha}") ++ FileUtils.mkdir_p("#{SITE_ROOT}external/docs/asciidoc") ++ File.open("#{SITE_ROOT}external/docs/asciidoc/#{asciidoc_sha}", "w") do |out| + out.write(content) end - # HTML anchor on hdlist1 (i.e. command options) @@ script/update-docs.rb: def index_l10n_doc(filter_tags, doc_list, get_content) + + FileUtils.mkdir_p(doc_path) + File.open("#{doc_path}/#{lang}.html", "w") do |out| -+ out.write("#{front_matter.to_yaml}\n---\n") ++ out.write(wrap_front_matter(front_matter)) + out.write(html) + end + 120: dd2d5016c = 120: b88d2bef6 docs(zh_HANS-CN): a trailing "full stop" character is not part of a URL 121: 9c93bfdec < -: --------- redirect_to: keep the anchor, if any was specified -: --------- > 121: 968e6e25b redirect_to/aliases: keep the anchor, if any was specified 122: 6ef6dbe2f ! 122: f2e7a5ffa docs(translated): add redirects for missing files @@ script/update-docs.rb: def index_l10n_doc(filter_tags, doc_list, get_content) + end asciidoc = make_asciidoc(content) asciidoc_sha = Digest::SHA1.hexdigest(asciidoc.source) - if !File.exists?("#{SITE_ROOT}_generated-asciidoc/#{asciidoc_sha}") + if !File.exists?("#{SITE_ROOT}external/docs/asciidoc/#{asciidoc_sha}") @@ script/update-docs.rb: def index_l10n_doc(filter_tags, doc_list, get_content) html.gsub!(/linkgit:(\S+?)\[(\d+)\]/) do |line| x = /^linkgit:(\S+?)\[(\d+)\]/.match(line) @@ script/update-docs.rb: def index_l10n_doc(filter_tags, doc_list, get_content) + # translated manual pages may point to other translated manual pages that do + # not exist. In these cases, redirect to the English version. + check_paths.each do |path| -+ doc_path = "#{SITE_ROOT}content/#{path}.html" ++ doc_path = "#{SITE_ROOT}external/docs/content/#{path}.html" + if !File.exists?(doc_path) + front_matter = { "redirect_to" => "#{path.sub(/\/[^\/]*$/, '')}" } # rtrim `/<lang>` + FileUtils.mkdir_p(File.dirname(doc_path)) + File.open(doc_path, "w") do |out| -+ out.write("#{front_matter.to_yaml}\n---\n") ++ out.write(wrap_front_matter(front_matter)) + end + end + end 123: e7e038a32 ! 123: 19d453f94 ci: update the books via a GitHub workflow @@ Commit message This information is then used by a scheduled workflow to determine what needs to be updated (if anything) and then performing that task. + When GitHub workflows push new changes, they cannot trigger other GitHub + workflows (to avoid infinite loops). Therefore, this new GitHub workflow + not only synchronizes the books, but also builds the site and deploys + it. + + Note: The code to build the site and to deploy it is provided in a + custom Action, to make it reusable. It will come in handy over the next + commits, where other GitHub workflows are added that likewise need + to synchronize changes that desire a site rebuild & deployment. + Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> ## .github/actions/deploy-to-github-pages/action.yml (new) ## @@ .github/actions/deploy-to-github-pages/action.yml (new) + + - name: setup GitHub Pages + id: pages -+ uses: actions/configure-pages@v3 ++ uses: actions/configure-pages@v5 + -+ - name: install Hugo -+ env: -+ HUGO_VERSION: 0.120.3 ++ - name: configure Hugo and Pagefind version ++ shell: bash ++ run: | ++ set -x && ++ echo "HUGO_VERSION=$(sed -n 's/^ *hugo_version: *//p' <hugo.yml)" >>$GITHUB_ENV ++ echo "PAGEFIND_VERSION=$(sed -n 's/^ *pagefind_version: *//p' <hugo.yml)" >>$GITHUB_ENV ++ ++ - name: install Hugo ${{ env.HUGO_VERSION }} + shell: bash + run: | + set -x && @@ .github/actions/deploy-to-github-pages/action.yml (new) + env: + HUGO_RELATIVEURLS: false + shell: bash -+ run: hugo --minify --baseURL "${{ steps.pages.outputs.base_url }}/" ++ run: hugo config && hugo --minify --baseURL "${{ steps.pages.outputs.base_url }}/" + -+ - name: run Pagefind to build the search index ++ - name: run Pagefind ${{ env.PAGEFIND_VERSION }} to build the search index + shell: bash -+ run: npx -y pagefind --site public ++ run: npx -y pagefind@${{ env.PAGEFIND_VERSION }} --site public + + - name: upload GitHub Pages artifact -+ uses: actions/upload-pages-artifact@v2 ++ uses: actions/upload-pages-artifact@v3 + with: + path: ./public + + - name: deploy + id: deploy -+ uses: actions/deploy-pages@v2 ++ uses: actions/deploy-pages@v4 ## .github/workflows/update-book.yml (new) ## @@ @@ .github/workflows/update-book.yml (new) + check-for-updates: + runs-on: ubuntu-latest + steps: -+ - uses: actions/checkout@v3 ++ - uses: actions/checkout@v4 + with: + sparse-checkout: | -+ _sync_state ++ external/book/sync + script -+ - uses: actions/github-script@v6 ++ - uses: actions/github-script@v7 + id: get-pending + with: + script: | @@ .github/workflows/update-book.yml (new) + language: ${{ fromJson(needs.check-for-updates.outputs.matrix) }} + fail-fast: false + steps: -+ - uses: actions/checkout@v3 ++ - uses: actions/checkout@v4 + with: + sparse-checkout: | -+ _sync_state + script -+ data -+ content/book/${{ matrix.language.lang }} -+ static/book/${{ matrix.language.lang }} ++ external/book/sync ++ external/book/data ++ external/book/content/book/${{ matrix.language.lang }} ++ external/book/static/book/${{ matrix.language.lang }} + - name: clone ${{ matrix.language.repository }} + run: | + printf '%s\n' /progit-clone/ /vendor >>.git/info/exclude && @@ .github/workflows/update-book.yml (new) + - name: commit changes + run: | + # record the commit hash -+ mkdir -p _sync_state && -+ git -C progit-clone rev-parse HEAD >_sync_state/book-${{ matrix.language.lang }}.sha && ++ mkdir -p external/book/sync && ++ git -C progit-clone rev-parse HEAD >external/book/sync/book-${{ matrix.language.lang }}.sha && + + # commit it all -+ git add -A \ -+ _sync_state \ -+ data/book-${{ matrix.language.lang }}.yml \ -+ content/book && -+ # there might be images -+ if test -d static/book -+ then -+ git add -A static/book -+ fi && ++ git add -A external/book && + git -c user.name=${{ github.actor }} \ + -c user.email=${{ github.actor }}@noreply.github.com \ + commit -m 'book: update ${{ matrix.language.lang }}' \ @@ .github/workflows/update-book.yml (new) + run: | + git branch -m book-${{ matrix.language.lang }} + git bundle create ${{ matrix.language.lang }}.bundle refs/remotes/origin/${{ github.ref_name }}..book-${{ matrix.language.lang }} -+ - uses: actions/upload-artifact@v3 ++ - uses: actions/upload-artifact@v4 + with: + name: bundle-${{ matrix.language.lang }} + path: ${{ matrix.language.lang }}.bundle @@ .github/workflows/update-book.yml (new) + url: ${{ steps.deploy.outputs.url }} + runs-on: ubuntu-latest + steps: -+ - uses: actions/checkout@v3 -+ - uses: actions/download-artifact@v3 ++ - uses: actions/checkout@v4 ++ - uses: actions/download-artifact@v4 + - name: apply updates + id: apply + run: | @@ .github/workflows/update-book.yml (new) + id: deploy + uses: ./.github/actions/deploy-to-github-pages - ## _sync_state/.gitignore (new) ## - ## script/ci-helper.js (new) ## @@ +const fs = require('fs') @@ script/ci-helper.js (new) + const result = [] + for (const lang of Object.keys(books)) { + try { -+ const localSha = await getFileContents(`_sync_state/book-${lang}.sha`) ++ const localSha = await getFileContents(`external/book/sync/book-${lang}.sha`) + + const [owner, repo] = books[lang].split('/') + const { data: { default_branch: remoteDefaultBranch } } = 124: c7c4d347d ! 124: 1ace765fc Update the download data via a GitHub workflow @@ .github/workflows/update-download-data.yml (new) + name: github-pages + url: ${{ steps.deploy.outputs.url }} + steps: -+ - uses: actions/checkout@v3 ++ - uses: actions/checkout@v4 + with: + sparse-checkout: | + .github/actions -+ _sync_state + script + - name: ruby setup + uses: ruby/setup-ruby@v1 @@ .github/workflows/update-download-data.yml (new) + git \ + -c user.name=${{ github.actor }} \ + -c user.email=${{ github.actor }}@noreply.github.com \ -+ commit -m "$commit_message" -- hugo.yml ++ commit -m "$commit_message" \ ++ -m 'Updated via the `update-download-data.yml` GitHub workflow.' \ ++ -- hugo.yml + - name: verify that there are no uncommitted changes + run: | + git update-index --refresh && 125: 3bd0e53a3 ! 125: 775c1d220 update-book: allow force-rebuilding @@ .github/workflows/update-book.yml: jobs: const { getPendingBookUpdates } = require('./script/ci-helper.js') - const pending = await getPendingBookUpdates(github) -+ const pending = await getPendingBookUpdates(github, ${{ inputs.force-rebuild }}) ++ const pending = await getPendingBookUpdates(github, ${{ inputs.force-rebuild == true }}) // an empty matrix is invalid and makes the workflow run fail, unfortunately return pending.length ? pending : [''] - name: ruby setup @@ script/ci-helper.js: const getAllBooks = async () => { const result = [] for (const lang of Object.keys(books)) { - try { -- const localSha = await getFileContents(`_sync_state/book-${lang}.sha`) +- const localSha = await getFileContents(`external/book/sync/book-${lang}.sha`) + if (!forceRebuild) { + try { -+ const localSha = await getFileContents(`_sync_state/book-${lang}.sha`) ++ const localSha = await getFileContents(`external/book/sync/book-${lang}.sha`) - const [owner, repo] = books[lang].split('/') - const { data: { default_branch: remoteDefaultBranch } } = 126: 835105745 ! 126: 2193295a0 Add a workflow to update the Git version and manual pages @@ .github/workflows/update-git-version-and-manual-pages.yml (new) + name: github-pages + url: ${{ steps.deploy.outputs.url }} + steps: -+ - uses: actions/checkout@v3 ++ - uses: actions/checkout@v4 + with: + sparse-checkout: | + .github/actions -+ _sync_state + script + - name: ruby setup + uses: ruby/setup-ruby@v1 @@ .github/workflows/update-git-version-and-manual-pages.yml (new) + git \ + -c user.name=${{ github.actor }} \ + -c user.email=${{ github.actor }}@noreply.github.com \ -+ commit -m "Update git-version ($version)" -- hugo.yml ++ commit -m "Update git-version ($version)" \ ++ -m 'Updated via the `update-git-version-and-manual-pages.yml` GitHub workflow.' \ ++ -- hugo.yml + - name: prepare worktree + if: steps.commit.outputs.result != '' + run: git sparse-checkout disable @@ .github/workflows/update-git-version-and-manual-pages.yml (new) + - name: commit manual pages + if: steps.commit.outputs.result != '' + run: | -+ git add -A _generated-asciidoc data/docs.yml content/docs && ++ git add -A external/docs && + git \ + -c user.name=${{ github.actor }} \ + -c user.email=${{ github.actor }}@noreply.github.com \ -+ commit -m "Update manual pages (${{ steps.commit.outputs.result }})" ++ commit -m "Update manual pages (${{ steps.commit.outputs.result }})" \ ++ -m 'Updated via the `update-git-version-and-manual-pages.yml` GitHub workflow.' + - name: verify that there are no uncommitted changes + run: | + git update-index --refresh && 127: 15a7705ad ! 127: 93af22a96 update-manual-pages: optionally force a complete rebuild @@ .github/workflows/update-git-version-and-manual-pages.yml: name: Synchronize wit # check daily for updates - cron: '37 17 * * *' @@ .github/workflows/update-git-version-and-manual-pages.yml: jobs: - -c user.email=${{ github.actor }}@noreply.github.com \ - commit -m "Update git-version ($version)" -- hugo.yml + -m 'Updated via the `update-git-version-and-manual-pages.yml` GitHub workflow.' \ + -- hugo.yml - name: prepare worktree - if: steps.commit.outputs.result != '' -+ if: steps.commit.outputs.result != '' || inputs.force-rebuild ++ if: steps.commit.outputs.result != '' || inputs.force-rebuild == true run: git sparse-checkout disable - name: clone git.git - if: steps.commit.outputs.result != '' -+ if: steps.commit.outputs.result != '' || inputs.force-rebuild ++ if: steps.commit.outputs.result != '' || inputs.force-rebuild == true run: git clone --bare https://github.com/git/git '${{ runner.temp }}/git' - name: update manual pages - if: steps.commit.outputs.result != '' - run: bundle exec ruby script/update-docs.rb '${{ runner.temp }}/git' en -+ if: steps.commit.outputs.result != '' || inputs.force-rebuild ++ if: steps.commit.outputs.result != '' || inputs.force-rebuild == true + run: | -+ test false = '${{ inputs.force-rebuild }}' || export RERUN=true ++ if test true = '${{ inputs.force-rebuild }}' ++ then ++ export RERUN=true ++ fi + bundle exec ruby script/update-docs.rb '${{ runner.temp }}/git' en - name: commit manual pages - if: steps.commit.outputs.result != '' + id: manual-pages -+ if: steps.commit.outputs.result != '' || inputs.force-rebuild ++ if: steps.commit.outputs.result != '' || inputs.force-rebuild == true run: | - git add -A _generated-asciidoc data/docs.yml content/docs && -+ if test false != '${{ inputs.force-rebuild }}' && git diff-index --cached --quiet HEAD -- + git add -A external/docs && ++ if test true = '${{ inputs.force-rebuild }}' && git diff-index --cached --quiet HEAD -- + then + echo '::notice::A manual pages rebuild was requested but resulted in no changes' >&2 + exit 0 @@ .github/workflows/update-git-version-and-manual-pages.yml: jobs: git \ -c user.name=${{ github.actor }} \ -c user.email=${{ github.actor }}@noreply.github.com \ -- commit -m "Update manual pages (${{ steps.commit.outputs.result }})" -+ commit -m "Update manual pages (${version:-manually forced rebuild})" && +- commit -m "Update manual pages (${{ steps.commit.outputs.result }})" \ +- -m 'Updated via the `update-git-version-and-manual-pages.yml` GitHub workflow.' ++ commit -m "Update manual pages (${version:-manually forced rebuild})" \ ++ -m 'Updated via the `update-git-version-and-manual-pages.yml` GitHub workflow.' && + echo "result=modified" >>$GITHUB_OUTPUT - name: verify that there are no uncommitted changes run: | 128: 21f13a6ef ! 128: 9ccb08bb2 Add a GitHub workflow to deploy `gh-pages` to GitHub Pages @@ .github/workflows/deploy.yml (new) + name: github-pages + url: ${{ steps.deploy.outputs.url }} + steps: -+ - uses: actions/checkout@v3 ++ - uses: actions/checkout@v4 + - name: deploy to GitHub Pages + id: deploy + uses: ./.github/actions/deploy-to-github-pages 129: 6a53852f0 ! 129: ac51259a9 Add a GitHub workflow to update the translated manual pages regularly @@ .github/workflows/update-translated-manual-pages.yml (new) + check-for-updates: + runs-on: ubuntu-latest + steps: -+ - uses: actions/checkout@v3 ++ - uses: actions/checkout@v4 + with: + sparse-checkout: | -+ _sync_state ++ external/docs/sync + script -+ - uses: actions/github-script@v6 ++ - uses: actions/github-script@v7 + id: get-pending + with: + script: | @@ .github/workflows/update-translated-manual-pages.yml (new) + up-to-date: ${{ steps.get-pending.outputs.result }} + update-translated-manual-pages: + needs: [check-for-updates] -+ if: inputs.force-rebuild || needs.check-for-updates.outputs.up-to-date == 'false' ++ if: inputs.force-rebuild == true || needs.check-for-updates.outputs.up-to-date == 'false' + runs-on: ubuntu-latest + permissions: + contents: write # to push changes (if any) @@ .github/workflows/update-translated-manual-pages.yml (new) + name: github-pages + url: ${{ steps.deploy.outputs.url }} + steps: -+ - uses: actions/checkout@v3 ++ - uses: actions/checkout@v4 + - name: ruby setup + uses: ruby/setup-ruby@v1 + with: @@ .github/workflows/update-translated-manual-pages.yml (new) + run: git clone --bare https://github.com/jnavila/git-html-l10n '${{ runner.temp }}/git-html-l10n' + - name: update translated manual pages + run: | -+ test false = '${{ inputs.force-rebuild }}' || export RERUN=true ++ if test true = '${{ inputs.force-rebuild }}' ++ then ++ export RERUN=true ++ fi + bundle exec ruby script/update-docs.rb '${{ runner.temp }}/git-html-l10n' l10n + - name: commit translated manual pages + id: manual-pages + run: | -+ git -C '${{ runner.temp }}/git-html-l10n' rev-parse HEAD >_sync_state/git-html-l10n.sha && -+ git add _sync_state/git-html-l10n.sha && ++ mkdir -p external/docs/sync && ++ git -C '${{ runner.temp }}/git-html-l10n' rev-parse HEAD >external/docs/sync/git-html-l10n.sha && ++ git add external/docs/sync/git-html-l10n.sha && + -+ git add -A _generated-asciidoc/ data/docs.yml content/docs && -+ if test false != '${{ inputs.force-rebuild }}' && git diff-index --cached --quiet HEAD -- ++ git add -A external/docs && ++ if test true = '${{ inputs.force-rebuild }}' && git diff-index --cached --quiet HEAD -- + then + echo '::notice::Rebuild of the translated manual pages was requested but resulted in no changes' >&2 + exit 0 @@ .github/workflows/update-translated-manual-pages.yml (new) + git \ + -c user.name=${{ github.actor }} \ + -c user.email=${{ github.actor }}@noreply.github.com \ -+ commit -m "Update translated manual pages" && ++ commit -m "Update translated manual pages" \ ++ -m 'Updated via the `update-translated-manual-pages.yml` GitHub workflow.' && + echo "result=modified" >>$GITHUB_OUTPUT + - name: verify that there are no uncommitted changes + run: | @@ script/ci-helper.js: const getPendingBookUpdates = async (octokit, forceRebuild) +const areTranslatedManualPagesUpToDate = async (octokit) => { + try { -+ const localSha = await getFileContents(`_sync_state/git-html-l10n.sha`) ++ const localSha = await getFileContents(`external/docs/sync/git-html-l10n.sha`) + + const [owner, repo] = 'jnavila/git-html-l10n'.split('/') + const { data: { default_branch: remoteDefaultBranch } } = -: --------- > 130: 28b147af1 deploy: check for broken links -: --------- > 131: c9dae6afc deploy(linkcheck): deal with stray "secondary rate limits" 130: bff0ab61c ! 132: 2b63bbb71 Adjust `ARCHITECTURE.md` to the Hugo reality @@ Commit message https://github.com/git/git-scm.com/issues/942. However, I aborted that migration when it turned out that Jekyll - required 20 minutes to process the files while Hugo spent less than half - a minute on them. + required 20 minutes to process the files while Hugo spent less than + half a minute on them. Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> @@ ARCHITECTURE.md + - original content from this repository - community book content brought in from https://github.com/progit; - see the `lib/tasks/book2.rake` file. -@@ ARCHITECTURE.md: The content is a mix of: - - manpages from releases of the git project, imported and formatted - via asciidoctor; see the `lib/tasks/index.rake` task. +- see the `lib/tasks/book2.rake` file. ++ see the `script/update-book2.rb` and `script/book.rb` files. -+To deploy to GitHub Pages, it is necessary to turn off the default setting to -+"publish from a branch" and instead change the setting to "publish with a -+custom GitHub Actions workflow": -+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 +- - manpages from releases of the git project, imported and formatted +- via asciidoctor; see the `lib/tasks/index.rake` task. ++ The content is pre-rendered and tracked in the `external/book/` directory ++ tree. + ++ - manual pages from releases of the git project, imported and formatted via ++ AsciiDoctor, and translated versions of the manual pages from ++ https://github.com/jnavila/git-manpages-l10n/ (which itself contains ++ pre-rendered pages from https://github.com/jnavila/git-manpages-l10n/); see ++ the `script/update-docs.rb` file. -## Heroku -+## Non-static parts ++ The pre-rendered pages are tracked in the `external/docs/` directory tree. -The app itself is served by Heroku. The app name is `git-scm` (so you -can visit it directly as https://git-scm.herokuapp.com). The site is -owned by the git-scm.com team. If you want to be involved in managing -uptime/deploys/etc, you'll need a Heroku account and request to be added -to that team. ++To deploy to GitHub Pages, it is necessary to turn off the default setting to ++"publish from a branch" and instead change the setting to "publish with a ++custom GitHub Actions workflow": ++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 ++With this change, the site can be tested in the fork by pushing to the ++`gh-pages` branch (which will trigger the `deploy.yml` workflow) and then ++navigating to https://git-scm.<user>.github.io/. + +-We use a few Heroku add-ons: ++## Non-static parts + +- - Bonsai elasticsearch (see below) +While the site consists mostly of static content, there are a couple of +parts that are sort of dynamic. --We use a few Heroku add-ons: +- - Heroku Postgres as the database +The search is implemented client-side, via [Pagefind](https://pagefind.app/). -- - Bonsai elasticsearch (see below) +- - Heroku Redis for rails caching +A few scheduled GitHub workflows keep the content up to date: -- - Heroku Postgres as the database +- - Heroku scheduler for cron jobs + - `update-git-version-and-manual-pages` and `update-download-data` (pick + up newly released git versions) -- - Heroku Redis for rails caching +-The nightly scheduled jobs are: + - `update-translated-manual-pages` (fetch and format translated manual + pages from the jnavila/git-html-l10n repository) -- - Heroku scheduler for cron jobs -- --The nightly scheduled jobs are: -- - - `rake downloads` (pick up newly released git versions) - - - `rake preindex` (pull in and format manpages for released git @@ ARCHITECTURE.md: The content is a mix of: -dyno. So we have Cloudflare sitting in front of it, aggressively caching -everything. That also should make the site faster to serve to regions -far away from Heroku's servers. +- +-The Cloudflare setup is mostly pretty simple: +These workflows are also marked as `workflow_dispatch`, i.e. they can be run +manually (e.g. to update the download links just after Git for Windows +published a new release). --The Cloudflare setup is mostly pretty simple: -- - - they serve DNS for the whole domain (that's where they insert the CDN - magic) - @@ ARCHITECTURE.md: The content is a mix of: +`deploy` GitHub workflow. +Note that some of the formatting of manual pages and book content happens -+when they are imported by the GitHub workflows. Therefore, after fixing some -+formatting, these workflows may need the force-rebuild flag to be toggled (see -+the individual workflows for details). ++when they are imported by the GitHub workflows. Therefore, whenever there are ++changes to the scripts/workflows/automation that affect formatting, these ++workflows may need to be triggered using the force-rebuild flag to be toggled ++(see the individual workflows for details). ## DNS @@ ARCHITECTURE.md: The content is a mix of: Note that we own both git-scm.com and git-scm.org; the latter redirects to the former. -@@ ARCHITECTURE.md: The site mostly just runs without intervention: - - code merged to `main` is auto-deployed +- + ## Manual Intervention + + The site mostly just runs without intervention: + +- - code merged to `main` is auto-deployed ++ - code merged to `gh-pages` is auto-deployed - - new git versions are detected daily and manpages and download links + - new git versions are detected daily and manual pages and download links @@ ARCHITECTURE.md: The site mostly just runs without intervention: - `lib/tasks/book2.rake` + `script/book.rb` - - forced re-imports of content (e.g., a formatting fix to imported +- - forced re-imports of content (e.g., a formatting fix to imported - manpages) must be triggered manually -+ manual pages) must be triggered manually with `force-rebuild` toggled ++ - forced re-imports of content (e.g., when fixing formatting in the ++ imported manual pages) must be triggered manually with `force-rebuild` ++ toggled -: --------- > 133: e91c406fa Add Playwright-based UI tests -: --------- > 134: a43cdff2c playwright: add a few platform-specific tests -: --------- > 135: f6b7ccd21 playwright: let the UI tests pass with the Rails app -: --------- > 136: e7db6d766 playwright: add a regression test for the `git remote renom` issue -: --------- > 137: 87609ac77 playwright: add a GitHub workflow to run the tests -: --------- > 138: 5facfdaee playwright: auto-start a web server for the special URL localhost:5000 -: --------- > 139: b54046816 playwright: verify that URLs with question marks work -: --------- > 140: 4210b9bba ci: run the Playwright tests in every Pull Request -: --------- > 141: ffb957b5f ci: run the Playwright tests on every deployment Ciao, Johannes