{"thread":{"id":"63406","subject":"[PATCH 02/10] contrib: remove \"examples\" directory","startedAt":"2025-05-06T14:12:43Z","lastAt":"2025-06-03T22:12:41Z","messageCount":62,"participants":["Patrick Steinhardt","Junio C Hamano","Eric Sunshine","Todd Zullinger","Matthieu Moy","Kristoffer Haugsbakk","Elijah Newren","Peter Krefting","D. Ben Knoble","Collin Funk","Phillip Wood"],"isPatch":true,"patchVersion":1,"patchTotal":10},"messages":[{"id":"517345","messageId":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","threadId":"63406","inReplyTo":null,"subject":"[PATCH 00/10] Spring cleanup of \"contrib/\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-06T14:12:33Z","receivedAt":"2025-05-06T14:12:43Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nas I have lamented multiple times multiple times already (e.g. [1]), the\n\"contrib/\" directory is a bit of a mess containing many bits and pieces\nthat just sit there gathering dust, without getting any maintenance and\nsometimes even in a clearly-broken state. So I decided to finally bite\nthe bullet and do a spring cleanup of \"contrib/\", which resulted in this\npatch series here.\n\nI have used the following reasons for removal:\n\n  - The tool is clearly broken, e.g. it doesn't even compile.\n\n  - The tool hasn't received any updates for at least the last 5 years.\n\n  - The tool has a clear alternative or just isn't useful anymore.\n\nThe next step for me would be to spell out a policy around \"contrib/\" to\nmatch what Junio has said [2]:\n\n    Things in contrib/ should either move up (to become a part of the\n    core), move out (to become an independent project), or disappear.\n\nWith this model, \"contrib/\" would be closer to Linux' staging drivers\nwith the expectation that a tool should eventually be part of proper Git\nin case it proves to be useful, or booted out when it doesn't seem to be\ngetting there.\n\nAnother subsequent step would be to split out some parts of \"contrib/\"\nto be hosted in their own hierarchy. CMake, Coccinelle, Unicode updates,\nVScode and the like are all tools that are used during development, so\nthey should probably not be part of \"contrib/\" but rather of a new\n\"tools/\" hierarchy (we can bikeshed the name at a later point, I'm not\nyet doing that in this series).\n\nThere's also other bits and pieces that serve as examples. I think we\nshould move these into our documentation instead of having those in\n\"contrib/\".\n\nAnway, this series here is just the first step.\n\nThanks!\n\nPatrick\n\n[1]: <aBhZHA7av8bWH9Ac@pks.im>\n[2]: <xmqq5xieq3fs.fsf@gitster.g>\n\n---\nPatrick Steinhardt (10):\n      contrib: remove \"remotes2config.sh\"\n      contrib: remove \"examples\" directory\n      contrib: remove remote-helper stubs\n      contrib: remove \"thunderbird-patch-inline\"\n      contrib: remove \"hooks\" directory\n      contrib: remove \"mw-to-git\"\n      contrib: remove \"persistent-https\" remote helper\n      contrib: remove \"git-resurrect.sh\"\n      contrib: remove \"emacs\" directory\n      contrib: remove \"git-new-workdir\"\n\n contrib/emacs/README                               |   33 -\n contrib/emacs/git-blame.el                         |    6 -\n contrib/emacs/git.el                               |    6 -\n contrib/examples/README                            |   20 -\n contrib/git-resurrect.sh                           |  181 ---\n contrib/hooks/multimail/README.Git                 |    7 -\n contrib/hooks/post-receive-email                   |  759 -----------\n contrib/hooks/pre-auto-gc-battery                  |   42 -\n contrib/hooks/setgitperms.perl                     |  214 ---\n contrib/hooks/update-paranoid                      |  421 ------\n contrib/mw-to-git/.gitignore                       |    2 -\n contrib/mw-to-git/.perlcriticrc                    |   28 -\n contrib/mw-to-git/Git/Mediawiki.pm                 |  101 --\n contrib/mw-to-git/Makefile                         |   61 -\n contrib/mw-to-git/bin-wrapper/git                  |   14 -\n contrib/mw-to-git/git-mw.perl                      |  368 ------\n contrib/mw-to-git/git-remote-mediawiki.perl        | 1390 --------------------\n contrib/mw-to-git/git-remote-mediawiki.txt         |    7 -\n contrib/mw-to-git/t/.gitignore                     |    4 -\n contrib/mw-to-git/t/Makefile                       |   32 -\n contrib/mw-to-git/t/README                         |  124 --\n contrib/mw-to-git/t/install-wiki.sh                |   55 -\n contrib/mw-to-git/t/push-pull-tests.sh             |  144 --\n contrib/mw-to-git/t/t9360-mw-to-git-clone.sh       |  257 ----\n contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh   |   24 -\n contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh        |  347 -----\n .../mw-to-git/t/t9363-mw-to-git-export-import.sh   |  218 ---\n contrib/mw-to-git/t/t9364-pull-by-rev.sh           |   17 -\n contrib/mw-to-git/t/t9365-continuing-queries.sh    |   23 -\n contrib/mw-to-git/t/test-gitmw-lib.sh              |  432 ------\n contrib/mw-to-git/t/test-gitmw.pl                  |  223 ----\n contrib/mw-to-git/t/test.config                    |   40 -\n contrib/persistent-https/LICENSE                   |  202 ---\n contrib/persistent-https/Makefile                  |   43 -\n contrib/persistent-https/README                    |   72 -\n contrib/persistent-https/client.go                 |  189 ---\n contrib/persistent-https/main.go                   |   82 --\n contrib/persistent-https/proxy.go                  |  190 ---\n contrib/persistent-https/socket.go                 |   97 --\n contrib/remote-helpers/README                      |   15 -\n contrib/remote-helpers/git-remote-bzr              |   11 -\n contrib/remote-helpers/git-remote-hg               |   11 -\n contrib/remotes2config.sh                          |   33 -\n contrib/thunderbird-patch-inline/README            |   20 -\n contrib/thunderbird-patch-inline/appp.sh           |   55 -\n contrib/workdir/.gitattributes                     |    1 -\n contrib/workdir/git-new-workdir                    |  105 --\n t/meson.build                                      |    1 -\n t/t1021-rerere-in-workdir.sh                       |   58 -\n t/t3000-ls-files-others.sh                         |   19 -\n 50 files changed, 6804 deletions(-)\n\n\n---\nbase-commit: 6f84262c44a89851c3ae5a6e4c1a9d06b2068d75\nchange-id: 20250506-pks-contrib-spring-cleanup-bb23b15d6df4\n\n"},{"id":"517344","messageId":"20250506-pks-contrib-spring-cleanup-v1-2-e6d5ddd79a72@pks.im","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","subject":"[PATCH 02/10] contrib: remove \"examples\" directory","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-06T14:12:35Z","receivedAt":"2025-05-06T14:12:44Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"examples\" directory used to contain scripted versions of some of\nour builtins. These have all been removed in 49eb8d39c78 (Remove\ncontrib/examples/*, 2018-03-25), but we left a note in the directory to\nmake it discoverable that there used to be examples.\n\nIt is unlikely that anybody still looks at these examples more than 7\nyears after they have been removed. Remove the note and its directory.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/examples/README | 20 --------------------\n 1 file changed, 20 deletions(-)\n\ndiff --git a/contrib/examples/README b/contrib/examples/README\ndeleted file mode 100644\nindex 18bc60b021b..00000000000\n--- a/contrib/examples/README\n+++ /dev/null\n@@ -1,20 +0,0 @@\n-This directory used to contain scripted implementations of builtins\n-that have since been rewritten in C.\n-\n-They have now been removed, but can be retrieved from an older commit\n-that removed them from this directory.\n-\n-They're interesting for their reference value to any aspiring plumbing\n-users who want to learn how pieces can be fit together, but in many\n-cases have drifted enough from the actual implementations Git uses to\n-be instructive.\n-\n-Other things that can be useful:\n-\n- * Some commands such as git-gc wrap other commands, and what they're\n-   doing behind the scenes can be seen by running them under\n-   GIT_TRACE=1\n-\n- * Doing `git log` on paths matching '*--helper.c' will show\n-   incremental effort in the direction of moving existing shell\n-   scripts to C.\n\n-- \n2.49.0.1045.g170613ef41.dirty\n\n"},{"id":"517347","messageId":"20250506-pks-contrib-spring-cleanup-v1-1-e6d5ddd79a72@pks.im","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","subject":"[PATCH 01/10] contrib: remove \"remotes2config.sh\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-06T14:12:34Z","receivedAt":"2025-05-06T14:12:44Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Remotes can be configured either via a repository's config or by using\nthe \".git/branches/\" or \".git/remotes/\" directories. Back when the new\nconfig-based mechanism has been introduced we also introduced a helper\nscript that migrates from the old-style remote configuration to the new\nconfig-based mechanism.\n\nWith the recent removal announcement for the two directories we also\nstarted to instruct users to migrate repositories that still use these\nmechanism to use config-based remotes. Notably though, the migration\npath doesn't even use the migration script. Instead, git-remote(1)\nitself knows how to migrate any such remote via `git remote rename`.\nFurthermore, the script hasn't been touched since afa75bc8aa1 (contrib:\nMake remotes2config.sh script more robust, 2007-12-02).\n\nGiven that there is a migration path without this script it is very\nunlikely that anyone still uses the script. Remove it.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/remotes2config.sh | 33 ---------------------------------\n 1 file changed, 33 deletions(-)\n\ndiff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh\ndeleted file mode 100755\nindex 1cda19f66af..00000000000\n--- a/contrib/remotes2config.sh\n+++ /dev/null\n@@ -1,33 +0,0 @@\n-#!/bin/sh\n-\n-# Use this tool to rewrite your .git/remotes/ files into the config.\n-\n-. git-sh-setup\n-\n-if [ -d \"$GIT_DIR\"/remotes ]; then\n-\techo \"Rewriting $GIT_DIR/remotes\" >&2\n-\terror=0\n-\t# rewrite into config\n-\t{\n-\t\tcd \"$GIT_DIR\"/remotes\n-\t\tls | while read f; do\n-\t\t\tname=$(printf \"$f\" | tr -c \"A-Za-z0-9-\" \".\")\n-\t\t\tsed -n \\\n-\t\t\t-e \"s/^URL:[ \t]*\\(.*\\)$/remote.$name.url \\1 ./p\" \\\n-\t\t\t-e \"s/^Pull:[ \t]*\\(.*\\)$/remote.$name.fetch \\1 ^$ /p\" \\\n-\t\t\t-e \"s/^Push:[ \t]*\\(.*\\)$/remote.$name.push \\1 ^$ /p\" \\\n-\t\t\t< \"$f\"\n-\t\tdone\n-\t\techo done\n-\t} | while read key value regex; do\n-\t\tcase $key in\n-\t\tdone)\n-\t\t\tif [ $error = 0 ]; then\n-\t\t\t\tmv \"$GIT_DIR\"/remotes \"$GIT_DIR\"/remotes.old\n-\t\t\tfi ;;\n-\t\t*)\n-\t\t\techo \"git config $key \"$value\" $regex\"\n-\t\t\tgit config $key \"$value\" $regex || error=1 ;;\n-\t\tesac\n-\tdone\n-fi\n\n-- \n2.49.0.1045.g170613ef41.dirty\n\n"},{"id":"517346","messageId":"20250506-pks-contrib-spring-cleanup-v1-3-e6d5ddd79a72@pks.im","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","subject":"[PATCH 03/10] contrib: remove remote-helper stubs","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-06T14:12:36Z","receivedAt":"2025-05-06T14:12:45Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"remote-helpers\" directory contains two remote helper scripts for\nMercurial and Bazaar. These scripts have since been converted into stubs\nin b2c851a8e67 (Revert \"Merge branch 'jc/graduate-remote-hg-bzr' (early\npart)\", 2014-05-20) as the helpers have been moved into their own\nupstream projects [1][2].\n\nGiven that these stubs have been created more than a decade ago it is\nvery unlikely that anybody still tries to use them. Remove them.\n\n[1]: https://github.com/felipec/git-remote-bzr\n[1]: https://github.com/felipec/git-remote-hg\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/remote-helpers/README         | 15 ---------------\n contrib/remote-helpers/git-remote-bzr | 11 -----------\n contrib/remote-helpers/git-remote-hg  | 11 -----------\n 3 files changed, 37 deletions(-)\n\ndiff --git a/contrib/remote-helpers/README b/contrib/remote-helpers/README\ndeleted file mode 100644\nindex ac72332517a..00000000000\n--- a/contrib/remote-helpers/README\n+++ /dev/null\n@@ -1,15 +0,0 @@\n-The remote-helper bridges to access data stored in Mercurial and\n-Bazaar are maintained outside the git.git tree in the repositories\n-of their primary author:\n-\n-    https://github.com/felipec/git-remote-hg (for Mercurial)\n-    https://github.com/felipec/git-remote-bzr (for Bazaar)\n-\n-You can pick a directory on your $PATH and download them from these\n-repositories, e.g.:\n-\n-  $ wget -O $HOME/bin/git-remote-hg \\\n-    https://raw.github.com/felipec/git-remote-hg/master/git-remote-hg\n-  $ wget -O $HOME/bin/git-remote-bzr \\\n-    https://raw.github.com/felipec/git-remote-bzr/master/git-remote-bzr\n-  $ chmod +x $HOME/bin/git-remote-hg $HOME/bin/git-remote-bzr\ndiff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr\ndeleted file mode 100755\nindex 1c3d87f8619..00000000000\n--- a/contrib/remote-helpers/git-remote-bzr\n+++ /dev/null\n@@ -1,11 +0,0 @@\n-#!/bin/sh\n-\n-cat >&2 <<'EOT'\n-WARNING: git-remote-bzr is now maintained independently.\n-WARNING: For more information visit https://github.com/felipec/git-remote-bzr\n-WARNING:\n-WARNING: You can pick a directory on your $PATH and download it, e.g.:\n-WARNING:   $ wget -O $HOME/bin/git-remote-bzr \\\n-WARNING:     https://raw.github.com/felipec/git-remote-bzr/master/git-remote-bzr\n-WARNING:   $ chmod +x $HOME/bin/git-remote-bzr\n-EOT\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\ndeleted file mode 100755\nindex 8e9188364c6..00000000000\n--- a/contrib/remote-helpers/git-remote-hg\n+++ /dev/null\n@@ -1,11 +0,0 @@\n-#!/bin/sh\n-\n-cat >&2 <<'EOT'\n-WARNING: git-remote-hg is now maintained independently.\n-WARNING: For more information visit https://github.com/felipec/git-remote-hg\n-WARNING:\n-WARNING: You can pick a directory on your $PATH and download it, e.g.:\n-WARNING:   $ wget -O $HOME/bin/git-remote-hg \\\n-WARNING:     https://raw.github.com/felipec/git-remote-hg/master/git-remote-hg\n-WARNING:   $ chmod +x $HOME/bin/git-remote-hg\n-EOT\n\n-- \n2.49.0.1045.g170613ef41.dirty\n\n"},{"id":"517348","messageId":"20250506-pks-contrib-spring-cleanup-v1-4-e6d5ddd79a72@pks.im","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","subject":"[PATCH 04/10] contrib: remove \"thunderbird-patch-inline\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-06T14:12:37Z","receivedAt":"2025-05-06T14:12:45Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"thunderbird-patch-inline\" directory in \"contrib/\" contains a script\nto send patch files via Thunderbird. This script depends on the\nExternalEditor extension [1], which seems to be effectively unmaintained\nwith the last update being in 2008. While the extension has eventually\nbeen maintained in [2], that fork hasn't received any updates since\n2020, either.\n\nIn addition, the script itself hasn't really seen a lot of maintenance\noutside of a couple of global cleanups. It is quite unlikely that this\nsetup still works, and if it did it's even less likely that somebody\nuses this script.\n\nRemove it.\n\n[1]: https://globs.org/articles.php?lng=en&pg=2\n[2]: https://github.com/exteditor/exteditor/releases\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/thunderbird-patch-inline/README  | 20 ------------\n contrib/thunderbird-patch-inline/appp.sh | 55 --------------------------------\n 2 files changed, 75 deletions(-)\n\ndiff --git a/contrib/thunderbird-patch-inline/README b/contrib/thunderbird-patch-inline/README\ndeleted file mode 100644\nindex 000147bbe4a..00000000000\n--- a/contrib/thunderbird-patch-inline/README\n+++ /dev/null\n@@ -1,20 +0,0 @@\n-appp.sh is a script that is supposed to be used together with ExternalEditor\n-for Mozilla Thunderbird. It will let you include patches inline in e-mails\n-in an easy way.\n-\n-Usage:\n-- Generate the patch with git format-patch.\n-- Start writing a new e-mail in Thunderbird.\n-- Press the external editor button (or Ctrl-E) to run appp.sh\n-- Select the previously generated patch file.\n-- Finish editing the e-mail.\n-\n-Any text that is entered into the message editor before appp.sh is called\n-will be moved to the section between the --- and the diffstat.\n-\n-All S-O-B:s and Cc:s in the patch will be added to the CC list.\n-\n-To set it up, just install External Editor and tell it to use appp.sh as the\n-editor.\n-\n-Zenity is a required dependency.\ndiff --git a/contrib/thunderbird-patch-inline/appp.sh b/contrib/thunderbird-patch-inline/appp.sh\ndeleted file mode 100755\nindex fdcc9483520..00000000000\n--- a/contrib/thunderbird-patch-inline/appp.sh\n+++ /dev/null\n@@ -1,55 +0,0 @@\n-#!/bin/sh\n-# Copyright 2008 Lukas Sandström <luksan@gmail.com>\n-#\n-# AppendPatch - A script to be used together with ExternalEditor\n-# for Mozilla Thunderbird to properly include patches inline in e-mails.\n-\n-# ExternalEditor can be downloaded at http://globs.org/articles.php?lng=en&pg=2\n-\n-CONFFILE=~/.appprc\n-\n-SEP=\"-=-=-=-=-=-=-=-=-=# Don't remove this line #=-=-=-=-=-=-=-=-=-\"\n-if [ -e \"$CONFFILE\" ] ; then\n-\tLAST_DIR=$(grep -m 1 \"^LAST_DIR=\" \"${CONFFILE}\"|sed -e 's/^LAST_DIR=//')\n-\tcd \"${LAST_DIR}\"\n-else\n-\tcd > /dev/null\n-fi\n-\n-PATCH=$(zenity --file-selection)\n-\n-if [ \"$?\" != \"0\" ] ; then\n-\t#zenity --error --text \"No patchfile given.\"\n-\texit 1\n-fi\n-\n-cd - > /dev/null\n-\n-SUBJECT=$(sed -n -e '/^Subject: /p' \"${PATCH}\")\n-HEADERS=$(sed -e '/^'\"${SEP}\"'$/,$d' $1)\n-BODY=$(sed -e \"1,/${SEP}/d\" $1)\n-CMT_MSG=$(sed -e '1,/^$/d' -e '/^---$/,$d' \"${PATCH}\")\n-DIFF=$(sed -e '1,/^---$/d' \"${PATCH}\")\n-\n-CCS=$(printf '%s\\n%s\\n' \"$CMT_MSG\" \"$HEADERS\" | sed -n -e 's/^Cc: \\(.*\\)$/\\1,/gp' \\\n-\t-e 's/^Signed-off-by: \\(.*\\)/\\1,/gp')\n-\n-echo \"$SUBJECT\" > $1\n-echo \"Cc: $CCS\" >> $1\n-echo \"$HEADERS\" | sed -e '/^Subject: /d' -e '/^Cc: /d' >> $1\n-echo \"$SEP\" >> $1\n-\n-echo \"$CMT_MSG\" >> $1\n-echo \"---\" >> $1\n-if [ \"x${BODY}x\" != \"xx\" ] ; then\n-\techo >> $1\n-\techo \"$BODY\" >> $1\n-\techo >> $1\n-fi\n-echo \"$DIFF\" >> $1\n-\n-LAST_DIR=$(dirname \"${PATCH}\")\n-\n-grep -v \"^LAST_DIR=\" \"${CONFFILE}\" > \"${CONFFILE}_\"\n-echo \"LAST_DIR=${LAST_DIR}\" >> \"${CONFFILE}_\"\n-mv \"${CONFFILE}_\" \"${CONFFILE}\"\n\n-- \n2.49.0.1045.g170613ef41.dirty\n\n"},{"id":"517349","messageId":"20250506-pks-contrib-spring-cleanup-v1-5-e6d5ddd79a72@pks.im","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","subject":"[PATCH 05/10] contrib: remove \"hooks\" directory","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-06T14:12:38Z","receivedAt":"2025-05-06T14:12:46Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"hooks\" directory contains a handful of example hooks. Most of these\nhooks are highly specific and haven't really received any updates over\nthe last couple of years, except for some global cleanups. The multimail\nhook has also been removed in f74d11471fa (multimail: stop shipping a\ncopy, 2021-06-10) in favor of its upstream project [1].\n\nRemove those hooks. If we want to provide examples for how to use Git\nhooks we should do that as part of our documentation, for example in\ngithooks(5).\n\n[1]: https://github.com/git-multimail/git-multimail\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/hooks/multimail/README.Git |   7 -\n contrib/hooks/post-receive-email   | 759 -------------------------------------\n contrib/hooks/pre-auto-gc-battery  |  42 --\n contrib/hooks/setgitperms.perl     | 214 -----------\n contrib/hooks/update-paranoid      | 421 --------------------\n 5 files changed, 1443 deletions(-)\n\ndiff --git a/contrib/hooks/multimail/README.Git b/contrib/hooks/multimail/README.Git\ndeleted file mode 100644\nindex c427efc7bd8..00000000000\n--- a/contrib/hooks/multimail/README.Git\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-git-multimail is developed as an independent project at the following\n-website:\n-\n-    https://github.com/git-multimail/git-multimail\n-\n-Please refer to that project page for information about how to report\n-bugs or contribute to git-multimail.\ndiff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email\ndeleted file mode 100755\nindex ff565eb3d88..00000000000\n--- a/contrib/hooks/post-receive-email\n+++ /dev/null\n@@ -1,759 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (c) 2007 Andy Parkins\n-#\n-# An example hook script to mail out commit update information.\n-#\n-# NOTE: This script is no longer under active development.  There\n-# is another script, git-multimail, which is more capable and\n-# configurable and is largely backwards-compatible with this script;\n-# please see \"contrib/hooks/multimail/\".  For instructions on how to\n-# migrate from post-receive-email to git-multimail, please see\n-# \"README.migrate-from-post-receive-email\" in that directory.\n-#\n-# This hook sends emails listing new revisions to the repository\n-# introduced by the change being reported.  The rule is that (for\n-# branch updates) each commit will appear on one email and one email\n-# only.\n-#\n-# This hook is stored in the contrib/hooks directory.  Your distribution\n-# will have put this somewhere standard.  You should make this script\n-# executable then link to it in the repository you would like to use it in.\n-# For example, on debian the hook is stored in\n-# /usr/share/git-core/contrib/hooks/post-receive-email:\n-#\n-#  cd /path/to/your/repository.git\n-#  ln -sf /usr/share/git-core/contrib/hooks/post-receive-email hooks/post-receive\n-#\n-# This hook script assumes it is enabled on the central repository of a\n-# project, with all users pushing only to it and not between each other.  It\n-# will still work if you don't operate in that style, but it would become\n-# possible for the email to be from someone other than the person doing the\n-# push.\n-#\n-# To help with debugging and use on pre-v1.5.1 git servers, this script will\n-# also obey the interface of hooks/update, taking its arguments on the\n-# command line.  Unfortunately, hooks/update is called once for each ref.\n-# To avoid firing one email per ref, this script just prints its output to\n-# the screen when used in this mode.  The output can then be redirected if\n-# wanted.\n-#\n-# Config\n-# ------\n-# hooks.mailinglist\n-#   This is the list that all pushes will go to; leave it blank to not send\n-#   emails for every ref update.\n-# hooks.announcelist\n-#   This is the list that all pushes of annotated tags will go to.  Leave it\n-#   blank to default to the mailinglist field.  The announce emails lists\n-#   the short log summary of the changes since the last annotated tag.\n-# hooks.envelopesender\n-#   If set then the -f option is passed to sendmail to allow the envelope\n-#   sender address to be set\n-# hooks.emailprefix\n-#   All emails have their subjects prefixed with this prefix, or \"[SCM]\"\n-#   if emailprefix is unset, to aid filtering\n-# hooks.showrev\n-#   The shell command used to format each revision in the email, with\n-#   \"%s\" replaced with the commit id.  Defaults to \"git rev-list -1\n-#   --pretty %s\", displaying the commit id, author, date and log\n-#   message.  To list full patches separated by a blank line, you\n-#   could set this to \"git show -C %s; echo\".\n-#   To list a gitweb/cgit URL *and* a full patch for each change set, use this:\n-#     \"t=%s; printf 'http://.../?id=%%s' \\$t; echo;echo; git show -C \\$t; echo\"\n-#   Be careful if \"...\" contains things that will be expanded by shell \"eval\"\n-#   or printf.\n-# hooks.emailmaxlines\n-#   The maximum number of lines that should be included in the generated\n-#   email body. If not specified, there is no limit.\n-#   Lines beyond the limit are suppressed and counted, and a final\n-#   line is added indicating the number of suppressed lines.\n-# hooks.diffopts\n-#   Alternate options for the git diff-tree invocation that shows changes.\n-#   Default is \"--stat --summary --find-copies-harder\". Add -p to those\n-#   options to include a unified diff of changes in addition to the usual\n-#   summary output.\n-#\n-# Notes\n-# -----\n-# All emails include the headers \"X-Git-Refname\", \"X-Git-Oldrev\",\n-# \"X-Git-Newrev\", and \"X-Git-Reftype\" to enable fine tuned filtering and\n-# give information for debugging.\n-#\n-\n-# ---------------------------- Functions\n-\n-#\n-# Function to prepare for email generation. This decides what type\n-# of update this is and whether an email should even be generated.\n-#\n-prep_for_email()\n-{\n-\t# --- Arguments\n-\toldrev=$(git rev-parse $1)\n-\tnewrev=$(git rev-parse $2)\n-\trefname=\"$3\"\n-\n-\t# --- Interpret\n-\t# 0000->1234 (create)\n-\t# 1234->2345 (update)\n-\t# 2345->0000 (delete)\n-\tif expr \"$oldrev\" : '0*$' >/dev/null\n-\tthen\n-\t\tchange_type=\"create\"\n-\telse\n-\t\tif expr \"$newrev\" : '0*$' >/dev/null\n-\t\tthen\n-\t\t\tchange_type=\"delete\"\n-\t\telse\n-\t\t\tchange_type=\"update\"\n-\t\tfi\n-\tfi\n-\n-\t# --- Get the revision types\n-\tnewrev_type=$(git cat-file -t $newrev 2> /dev/null)\n-\toldrev_type=$(git cat-file -t \"$oldrev\" 2> /dev/null)\n-\tcase \"$change_type\" in\n-\tcreate|update)\n-\t\trev=\"$newrev\"\n-\t\trev_type=\"$newrev_type\"\n-\t\t;;\n-\tdelete)\n-\t\trev=\"$oldrev\"\n-\t\trev_type=\"$oldrev_type\"\n-\t\t;;\n-\tesac\n-\n-\t# The revision type tells us what type the commit is, combined with\n-\t# the location of the ref we can decide between\n-\t#  - working branch\n-\t#  - tracking branch\n-\t#  - unannoted tag\n-\t#  - annotated tag\n-\tcase \"$refname\",\"$rev_type\" in\n-\t\trefs/tags/*,commit)\n-\t\t\t# un-annotated tag\n-\t\t\trefname_type=\"tag\"\n-\t\t\tshort_refname=${refname##refs/tags/}\n-\t\t\t;;\n-\t\trefs/tags/*,tag)\n-\t\t\t# annotated tag\n-\t\t\trefname_type=\"annotated tag\"\n-\t\t\tshort_refname=${refname##refs/tags/}\n-\t\t\t# change recipients\n-\t\t\tif [ -n \"$announcerecipients\" ]; then\n-\t\t\t\trecipients=\"$announcerecipients\"\n-\t\t\tfi\n-\t\t\t;;\n-\t\trefs/heads/*,commit)\n-\t\t\t# branch\n-\t\t\trefname_type=\"branch\"\n-\t\t\tshort_refname=${refname##refs/heads/}\n-\t\t\t;;\n-\t\trefs/remotes/*,commit)\n-\t\t\t# tracking branch\n-\t\t\trefname_type=\"tracking branch\"\n-\t\t\tshort_refname=${refname##refs/remotes/}\n-\t\t\techo >&2 \"*** Push-update of tracking branch, $refname\"\n-\t\t\techo >&2 \"***  - no email generated.\"\n-\t\t\treturn 1\n-\t\t\t;;\n-\t\t*)\n-\t\t\t# Anything else (is there anything else?)\n-\t\t\techo >&2 \"*** Unknown type of update to $refname ($rev_type)\"\n-\t\t\techo >&2 \"***  - no email generated\"\n-\t\t\treturn 1\n-\t\t\t;;\n-\tesac\n-\n-\t# Check if we've got anyone to send to\n-\tif [ -z \"$recipients\" ]; then\n-\t\tcase \"$refname_type\" in\n-\t\t\t\"annotated tag\")\n-\t\t\t\tconfig_name=\"hooks.announcelist\"\n-\t\t\t\t;;\n-\t\t\t*)\n-\t\t\t\tconfig_name=\"hooks.mailinglist\"\n-\t\t\t\t;;\n-\t\tesac\n-\t\techo >&2 \"*** $config_name is not set so no email will be sent\"\n-\t\techo >&2 \"*** for $refname update $oldrev->$newrev\"\n-\t\treturn 1\n-\tfi\n-\n-\treturn 0\n-}\n-\n-#\n-# Top level email generation function.  This calls the appropriate\n-# body-generation routine after outputting the common header.\n-#\n-# Note this function doesn't actually generate any email output, that is\n-# taken care of by the functions it calls:\n-#  - generate_email_header\n-#  - generate_create_XXXX_email\n-#  - generate_update_XXXX_email\n-#  - generate_delete_XXXX_email\n-#  - generate_email_footer\n-#\n-# Note also that this function cannot 'exit' from the script; when this\n-# function is running (in hook script mode), the send_mail() function\n-# is already executing in another process, connected via a pipe, and\n-# if this function exits without, whatever has been generated to that\n-# point will be sent as an email... even if nothing has been generated.\n-#\n-generate_email()\n-{\n-\t# Email parameters\n-\t# The email subject will contain the best description of the ref\n-\t# that we can build from the parameters\n-\tdescribe=$(git describe $rev 2>/dev/null)\n-\tif [ -z \"$describe\" ]; then\n-\t\tdescribe=$rev\n-\tfi\n-\n-\tgenerate_email_header\n-\n-\t# Call the correct body generation function\n-\tfn_name=general\n-\tcase \"$refname_type\" in\n-\t\"tracking branch\"|branch)\n-\t\tfn_name=branch\n-\t\t;;\n-\t\"annotated tag\")\n-\t\tfn_name=atag\n-\t\t;;\n-\tesac\n-\n-\tif [ -z \"$maxlines\" ]; then\n-\t\tgenerate_${change_type}_${fn_name}_email\n-\telse\n-\t\tgenerate_${change_type}_${fn_name}_email | limit_lines $maxlines\n-\tfi\n-\n-\tgenerate_email_footer\n-}\n-\n-generate_email_header()\n-{\n-\t# --- Email (all stdout will be the email)\n-\t# Generate header\n-\tcat <<-EOF\n-\tTo: $recipients\n-\tSubject: ${emailprefix}$projectdesc $refname_type $short_refname ${change_type}d. $describe\n-\tMIME-Version: 1.0\n-\tContent-Type: text/plain; charset=utf-8\n-\tContent-Transfer-Encoding: 8bit\n-\tX-Git-Refname: $refname\n-\tX-Git-Reftype: $refname_type\n-\tX-Git-Oldrev: $oldrev\n-\tX-Git-Newrev: $newrev\n-\tAuto-Submitted: auto-generated\n-\n-\tThis is an automated email from the git hooks/post-receive script. It was\n-\tgenerated because a ref change was pushed to the repository containing\n-\tthe project \"$projectdesc\".\n-\n-\tThe $refname_type, $short_refname has been ${change_type}d\n-\tEOF\n-}\n-\n-generate_email_footer()\n-{\n-\tSPACE=\" \"\n-\tcat <<-EOF\n-\n-\n-\thooks/post-receive\n-\t--${SPACE}\n-\t$projectdesc\n-\tEOF\n-}\n-\n-# --------------- Branches\n-\n-#\n-# Called for the creation of a branch\n-#\n-generate_create_branch_email()\n-{\n-\t# This is a new branch and so oldrev is not valid\n-\techo \"        at  $newrev ($newrev_type)\"\n-\techo \"\"\n-\n-\techo $LOGBEGIN\n-\tshow_new_revisions\n-\techo $LOGEND\n-}\n-\n-#\n-# Called for the change of a pre-existing branch\n-#\n-generate_update_branch_email()\n-{\n-\t# Consider this:\n-\t#   1 --- 2 --- O --- X --- 3 --- 4 --- N\n-\t#\n-\t# O is $oldrev for $refname\n-\t# N is $newrev for $refname\n-\t# X is a revision pointed to by some other ref, for which we may\n-\t#   assume that an email has already been generated.\n-\t# In this case we want to issue an email containing only revisions\n-\t# 3, 4, and N.  Given (almost) by\n-\t#\n-\t#  git rev-list N ^O --not --all\n-\t#\n-\t# The reason for the \"almost\", is that the \"--not --all\" will take\n-\t# precedence over the \"N\", and effectively will translate to\n-\t#\n-\t#  git rev-list N ^O ^X ^N\n-\t#\n-\t# So, we need to build up the list more carefully.  git rev-parse\n-\t# will generate a list of revs that may be fed into git rev-list.\n-\t# We can get it to make the \"--not --all\" part and then filter out\n-\t# the \"^N\" with:\n-\t#\n-\t#  git rev-parse --not --all | grep -v N\n-\t#\n-\t# Then, using the --stdin switch to git rev-list we have effectively\n-\t# manufactured\n-\t#\n-\t#  git rev-list N ^O ^X\n-\t#\n-\t# This leaves a problem when someone else updates the repository\n-\t# while this script is running.  Their new value of the ref we're\n-\t# working on would be included in the \"--not --all\" output; and as\n-\t# our $newrev would be an ancestor of that commit, it would exclude\n-\t# all of our commits.  What we really want is to exclude the current\n-\t# value of $refname from the --not list, rather than N itself.  So:\n-\t#\n-\t#  git rev-parse --not --all | grep -v $(git rev-parse $refname)\n-\t#\n-\t# Gets us to something pretty safe (apart from the small time\n-\t# between refname being read, and git rev-parse running - for that,\n-\t# I give up)\n-\t#\n-\t#\n-\t# Next problem, consider this:\n-\t#   * --- B --- * --- O ($oldrev)\n-\t#          \\\n-\t#           * --- X --- * --- N ($newrev)\n-\t#\n-\t# That is to say, there is no guarantee that oldrev is a strict\n-\t# subset of newrev (it would have required a --force, but that's\n-\t# allowed).  So, we can't simply say rev-list $oldrev..$newrev.\n-\t# Instead we find the common base of the two revs and list from\n-\t# there.\n-\t#\n-\t# As above, we need to take into account the presence of X; if\n-\t# another branch is already in the repository and points at some of\n-\t# the revisions that we are about to output - we don't want them.\n-\t# The solution is as before: git rev-parse output filtered.\n-\t#\n-\t# Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N\n-\t#\n-\t# Tags pushed into the repository generate nice shortlog emails that\n-\t# summarise the commits between them and the previous tag.  However,\n-\t# those emails don't include the full commit messages that we output\n-\t# for a branch update.  Therefore we still want to output revisions\n-\t# that have been output on a tag email.\n-\t#\n-\t# Luckily, git rev-parse includes just the tool.  Instead of using\n-\t# \"--all\" we use \"--branches\"; this has the added benefit that\n-\t# \"remotes/\" will be ignored as well.\n-\n-\t# List all of the revisions that were removed by this update, in a\n-\t# fast-forward update, this list will be empty, because rev-list O\n-\t# ^N is empty.  For a non-fast-forward, O ^N is the list of removed\n-\t# revisions\n-\tfast_forward=\"\"\n-\trev=\"\"\n-\tfor rev in $(git rev-list $newrev..$oldrev)\n-\tdo\n-\t\trevtype=$(git cat-file -t \"$rev\")\n-\t\techo \"  discards  $rev ($revtype)\"\n-\tdone\n-\tif [ -z \"$rev\" ]; then\n-\t\tfast_forward=1\n-\tfi\n-\n-\t# List all the revisions from baserev to newrev in a kind of\n-\t# \"table-of-contents\"; note this list can include revisions that\n-\t# have already had notification emails and is present to show the\n-\t# full detail of the change from rolling back the old revision to\n-\t# the base revision and then forward to the new revision\n-\tfor rev in $(git rev-list $oldrev..$newrev)\n-\tdo\n-\t\trevtype=$(git cat-file -t \"$rev\")\n-\t\techo \"       via  $rev ($revtype)\"\n-\tdone\n-\n-\tif [ \"$fast_forward\" ]; then\n-\t\techo \"      from  $oldrev ($oldrev_type)\"\n-\telse\n-\t\t#  1. Existing revisions were removed.  In this case newrev\n-\t\t#     is a subset of oldrev - this is the reverse of a\n-\t\t#     fast-forward, a rewind\n-\t\t#  2. New revisions were added on top of an old revision,\n-\t\t#     this is a rewind and addition.\n-\n-\t\t# (1) certainly happened, (2) possibly.  When (2) hasn't\n-\t\t# happened, we set a flag to indicate that no log printout\n-\t\t# is required.\n-\n-\t\techo \"\"\n-\n-\t\t# Find the common ancestor of the old and new revisions and\n-\t\t# compare it with newrev\n-\t\tbaserev=$(git merge-base $oldrev $newrev)\n-\t\trewind_only=\"\"\n-\t\tif [ \"$baserev\" = \"$newrev\" ]; then\n-\t\t\techo \"This update discarded existing revisions and left the branch pointing at\"\n-\t\t\techo \"a previous point in the repository history.\"\n-\t\t\techo \"\"\n-\t\t\techo \" * -- * -- N ($newrev)\"\n-\t\t\techo \"            \\\\\"\n-\t\t\techo \"             O -- O -- O ($oldrev)\"\n-\t\t\techo \"\"\n-\t\t\techo \"The removed revisions are not necessarily gone - if another reference\"\n-\t\t\techo \"still refers to them they will stay in the repository.\"\n-\t\t\trewind_only=1\n-\t\telse\n-\t\t\techo \"This update added new revisions after undoing existing revisions.  That is\"\n-\t\t\techo \"to say, the old revision is not a strict subset of the new revision.  This\"\n-\t\t\techo \"situation occurs when you --force push a change and generate a repository\"\n-\t\t\techo \"containing something like this:\"\n-\t\t\techo \"\"\n-\t\t\techo \" * -- * -- B -- O -- O -- O ($oldrev)\"\n-\t\t\techo \"            \\\\\"\n-\t\t\techo \"             N -- N -- N ($newrev)\"\n-\t\t\techo \"\"\n-\t\t\techo \"When this happens we assume that you've already had alert emails for all\"\n-\t\t\techo \"of the O revisions, and so we here report only the revisions in the N\"\n-\t\t\techo \"branch from the common base, B.\"\n-\t\tfi\n-\tfi\n-\n-\techo \"\"\n-\tif [ -z \"$rewind_only\" ]; then\n-\t\techo \"Those revisions listed above that are new to this repository have\"\n-\t\techo \"not appeared on any other notification email; so we list those\"\n-\t\techo \"revisions in full, below.\"\n-\n-\t\techo \"\"\n-\t\techo $LOGBEGIN\n-\t\tshow_new_revisions\n-\n-\t\t# XXX: Need a way of detecting whether git rev-list actually\n-\t\t# outputted anything, so that we can issue a \"no new\n-\t\t# revisions added by this update\" message\n-\n-\t\techo $LOGEND\n-\telse\n-\t\techo \"No new revisions were added by this update.\"\n-\tfi\n-\n-\t# The diffstat is shown from the old revision to the new revision.\n-\t# This is to show the truth of what happened in this change.\n-\t# There's no point showing the stat from the base to the new\n-\t# revision because the base is effectively a random revision at this\n-\t# point - the user will be interested in what this revision changed\n-\t# - including the undoing of previous revisions in the case of\n-\t# non-fast-forward updates.\n-\techo \"\"\n-\techo \"Summary of changes:\"\n-\tgit diff-tree $diffopts $oldrev..$newrev\n-}\n-\n-#\n-# Called for the deletion of a branch\n-#\n-generate_delete_branch_email()\n-{\n-\techo \"       was  $oldrev\"\n-\techo \"\"\n-\techo $LOGBEGIN\n-\tgit diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev\n-\techo $LOGEND\n-}\n-\n-# --------------- Annotated tags\n-\n-#\n-# Called for the creation of an annotated tag\n-#\n-generate_create_atag_email()\n-{\n-\techo \"        at  $newrev ($newrev_type)\"\n-\n-\tgenerate_atag_email\n-}\n-\n-#\n-# Called for the update of an annotated tag (this is probably a rare event\n-# and may not even be allowed)\n-#\n-generate_update_atag_email()\n-{\n-\techo \"        to  $newrev ($newrev_type)\"\n-\techo \"      from  $oldrev (which is now obsolete)\"\n-\n-\tgenerate_atag_email\n-}\n-\n-#\n-# Called when an annotated tag is created or changed\n-#\n-generate_atag_email()\n-{\n-\t# Use git for-each-ref to pull out the individual fields from the\n-\t# tag\n-\teval $(git for-each-ref --shell --format='\n-\ttagobject=%(*objectname)\n-\ttagtype=%(*objecttype)\n-\ttagger=%(taggername)\n-\ttagged=%(taggerdate)' $refname\n-\t)\n-\n-\techo \"   tagging  $tagobject ($tagtype)\"\n-\tcase \"$tagtype\" in\n-\tcommit)\n-\n-\t\t# If the tagged object is a commit, then we assume this is a\n-\t\t# release, and so we calculate which tag this tag is\n-\t\t# replacing\n-\t\tprevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)\n-\n-\t\tif [ -n \"$prevtag\" ]; then\n-\t\t\techo \"  replaces  $prevtag\"\n-\t\tfi\n-\t\t;;\n-\t*)\n-\t\techo \"    length  $(git cat-file -s $tagobject) bytes\"\n-\t\t;;\n-\tesac\n-\techo \" tagged by  $tagger\"\n-\techo \"        on  $tagged\"\n-\n-\techo \"\"\n-\techo $LOGBEGIN\n-\n-\t# Show the content of the tag message; this might contain a change\n-\t# log or release notes so is worth displaying.\n-\tgit cat-file tag $newrev | sed -e '1,/^$/d'\n-\n-\techo \"\"\n-\tcase \"$tagtype\" in\n-\tcommit)\n-\t\t# Only commit tags make sense to have rev-list operations\n-\t\t# performed on them\n-\t\tif [ -n \"$prevtag\" ]; then\n-\t\t\t# Show changes since the previous release\n-\t\t\tgit shortlog \"$prevtag..$newrev\"\n-\t\telse\n-\t\t\t# No previous tag, show all the changes since time\n-\t\t\t# began\n-\t\t\tgit shortlog $newrev\n-\t\tfi\n-\t\t;;\n-\t*)\n-\t\t# XXX: Is there anything useful we can do for non-commit\n-\t\t# objects?\n-\t\t;;\n-\tesac\n-\n-\techo $LOGEND\n-}\n-\n-#\n-# Called for the deletion of an annotated tag\n-#\n-generate_delete_atag_email()\n-{\n-\techo \"       was  $oldrev\"\n-\techo \"\"\n-\techo $LOGBEGIN\n-\tgit diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev\n-\techo $LOGEND\n-}\n-\n-# --------------- General references\n-\n-#\n-# Called when any other type of reference is created (most likely a\n-# non-annotated tag)\n-#\n-generate_create_general_email()\n-{\n-\techo \"        at  $newrev ($newrev_type)\"\n-\n-\tgenerate_general_email\n-}\n-\n-#\n-# Called when any other type of reference is updated (most likely a\n-# non-annotated tag)\n-#\n-generate_update_general_email()\n-{\n-\techo \"        to  $newrev ($newrev_type)\"\n-\techo \"      from  $oldrev\"\n-\n-\tgenerate_general_email\n-}\n-\n-#\n-# Called for creation or update of any other type of reference\n-#\n-generate_general_email()\n-{\n-\t# Unannotated tags are more about marking a point than releasing a\n-\t# version; therefore we don't do the shortlog summary that we do for\n-\t# annotated tags above - we simply show that the point has been\n-\t# marked, and print the log message for the marked point for\n-\t# reference purposes\n-\t#\n-\t# Note this section also catches any other reference type (although\n-\t# there aren't any) and deals with them in the same way.\n-\n-\techo \"\"\n-\tif [ \"$newrev_type\" = \"commit\" ]; then\n-\t\techo $LOGBEGIN\n-\t\tgit diff-tree -s --always --encoding=UTF-8 --pretty=medium $newrev\n-\t\techo $LOGEND\n-\telse\n-\t\t# What can we do here?  The tag marks an object that is not\n-\t\t# a commit, so there is no log for us to display.  It's\n-\t\t# probably not wise to output git cat-file as it could be a\n-\t\t# binary blob.  We'll just say how big it is\n-\t\techo \"$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long.\"\n-\tfi\n-}\n-\n-#\n-# Called for the deletion of any other type of reference\n-#\n-generate_delete_general_email()\n-{\n-\techo \"       was  $oldrev\"\n-\techo \"\"\n-\techo $LOGBEGIN\n-\tgit diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev\n-\techo $LOGEND\n-}\n-\n-\n-# --------------- Miscellaneous utilities\n-\n-#\n-# Show new revisions as the user would like to see them in the email.\n-#\n-show_new_revisions()\n-{\n-\t# This shows all log entries that are not already covered by\n-\t# another ref - i.e. commits that are now accessible from this\n-\t# ref that were previously not accessible\n-\t# (see generate_update_branch_email for the explanation of this\n-\t# command)\n-\n-\t# Revision range passed to rev-list differs for new vs. updated\n-\t# branches.\n-\tif [ \"$change_type\" = create ]\n-\tthen\n-\t\t# Show all revisions exclusive to this (new) branch.\n-\t\trevspec=$newrev\n-\telse\n-\t\t# Branch update; show revisions not part of $oldrev.\n-\t\trevspec=$oldrev..$newrev\n-\tfi\n-\n-\tother_branches=$(git for-each-ref --format='%(refname)' refs/heads/ |\n-\t    grep -F -v $refname)\n-\tgit rev-parse --not $other_branches |\n-\tif [ -z \"$custom_showrev\" ]\n-\tthen\n-\t\tgit rev-list --pretty --stdin $revspec\n-\telse\n-\t\tgit rev-list --stdin $revspec |\n-\t\twhile read onerev\n-\t\tdo\n-\t\t\teval $(printf \"$custom_showrev\" $onerev)\n-\t\tdone\n-\tfi\n-}\n-\n-\n-limit_lines()\n-{\n-\tlines=0\n-\tskipped=0\n-\twhile IFS=\"\" read -r line; do\n-\t\tlines=$((lines + 1))\n-\t\tif [ $lines -gt $1 ]; then\n-\t\t\tskipped=$((skipped + 1))\n-\t\telse\n-\t\t\tprintf \"%s\\n\" \"$line\"\n-\t\tfi\n-\tdone\n-\tif [ $skipped -ne 0 ]; then\n-\t\techo \"... $skipped lines suppressed ...\"\n-\tfi\n-}\n-\n-\n-send_mail()\n-{\n-\tif [ -n \"$envelopesender\" ]; then\n-\t\t/usr/sbin/sendmail -t -f \"$envelopesender\"\n-\telse\n-\t\t/usr/sbin/sendmail -t\n-\tfi\n-}\n-\n-# ---------------------------- main()\n-\n-# --- Constants\n-LOGBEGIN=\"- Log -----------------------------------------------------------------\"\n-LOGEND=\"-----------------------------------------------------------------------\"\n-\n-# --- Config\n-# Set GIT_DIR either from the working directory, or from the environment\n-# variable.\n-GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)\n-if [ -z \"$GIT_DIR\" ]; then\n-\techo >&2 \"fatal: post-receive: GIT_DIR not set\"\n-\texit 1\n-fi\n-\n-projectdesc=$(sed -ne '1p' \"$GIT_DIR/description\" 2>/dev/null)\n-# Check if the description is unchanged from it's default, and shorten it to\n-# a more manageable length if it is\n-if expr \"$projectdesc\" : \"Unnamed repository.*$\" >/dev/null\n-then\n-\tprojectdesc=\"UNNAMED PROJECT\"\n-fi\n-\n-recipients=$(git config hooks.mailinglist)\n-announcerecipients=$(git config hooks.announcelist)\n-envelopesender=$(git config hooks.envelopesender)\n-emailprefix=$(git config hooks.emailprefix || echo '[SCM] ')\n-custom_showrev=$(git config hooks.showrev)\n-maxlines=$(git config hooks.emailmaxlines)\n-diffopts=$(git config hooks.diffopts)\n-: ${diffopts:=\"--stat --summary --find-copies-harder\"}\n-\n-# --- Main loop\n-# Allow dual mode: run from the command line just like the update hook, or\n-# if no arguments are given then run as a hook script\n-if [ -n \"$1\" -a -n \"$2\" -a -n \"$3\" ]; then\n-\t# Output to the terminal in command line mode - if someone wanted to\n-\t# resend an email; they could redirect the output to sendmail\n-\t# themselves\n-\tprep_for_email $2 $3 $1 && PAGER= generate_email\n-else\n-\twhile read oldrev newrev refname\n-\tdo\n-\t\tprep_for_email $oldrev $newrev $refname || continue\n-\t\tgenerate_email $maxlines | send_mail\n-\tdone\n-fi\ndiff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery\ndeleted file mode 100755\nindex 7ba78c4dff6..00000000000\n--- a/contrib/hooks/pre-auto-gc-battery\n+++ /dev/null\n@@ -1,42 +0,0 @@\n-#!/bin/sh\n-#\n-# An example hook script to verify if you are on battery, in case you\n-# are running Linux or OS X. Called by git-gc --auto with no arguments.\n-# The hook should exit with non-zero status after issuing an appropriate\n-# message if it wants to stop the auto repacking.\n-#\n-# This hook is stored in the contrib/hooks directory. Your distribution\n-# may have put this somewhere else. If you want to use this hook, you\n-# should make this script executable then link to it in the repository\n-# you would like to use it in.\n-#\n-# For example, if the hook is stored in\n-# /usr/share/git-core/contrib/hooks/pre-auto-gc-battery:\n-#\n-# cd /path/to/your/repository.git\n-# ln -sf /usr/share/git-core/contrib/hooks/pre-auto-gc-battery \\\n-#\thooks/pre-auto-gc\n-\n-if test -x /sbin/on_ac_power && (/sbin/on_ac_power;test $? -ne 1)\n-then\n-\texit 0\n-elif test \"$(cat /sys/class/power_supply/AC/online 2>/dev/null)\" = 1\n-then\n-\texit 0\n-elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null\n-then\n-\texit 0\n-elif grep -q '0x01$' /proc/apm 2>/dev/null\n-then\n-\texit 0\n-elif grep -q \"AC Power \\+: 1\" /proc/pmu/info 2>/dev/null\n-then\n-\texit 0\n-elif test -x /usr/bin/pmset && /usr/bin/pmset -g batt |\n-\tgrep -q \"drawing from 'AC Power'\"\n-then\n-\texit 0\n-fi\n-\n-echo \"Auto packing deferred; not on AC\"\n-exit 1\ndiff --git a/contrib/hooks/setgitperms.perl b/contrib/hooks/setgitperms.perl\ndeleted file mode 100755\nindex 2770a1b1d20..00000000000\n--- a/contrib/hooks/setgitperms.perl\n+++ /dev/null\n@@ -1,214 +0,0 @@\n-#!/usr/bin/perl\n-#\n-# Copyright (c) 2006 Josh England\n-#\n-# This script can be used to save/restore full permissions and ownership data\n-# within a git working tree.\n-#\n-# To save permissions/ownership data, place this script in your .git/hooks\n-# directory and enable a `pre-commit` hook with the following lines:\n-#      #!/bin/sh\n-#     SUBDIRECTORY_OK=1 . git-sh-setup\n-#     $GIT_DIR/hooks/setgitperms.perl -r\n-#\n-# To restore permissions/ownership data, place this script in your .git/hooks\n-# directory and enable a `post-merge` and `post-checkout` hook with the\n-# following lines:\n-#      #!/bin/sh\n-#     SUBDIRECTORY_OK=1 . git-sh-setup\n-#     $GIT_DIR/hooks/setgitperms.perl -w\n-#\n-use strict;\n-use Getopt::Long;\n-use File::Find;\n-use File::Basename;\n-\n-my $usage =\n-\"usage: setgitperms.perl [OPTION]... <--read|--write>\n-This program uses a file `.gitmeta` to store/restore permissions and uid/gid\n-info for all files/dirs tracked by git in the repository.\n-\n----------------------------------Read Mode-------------------------------------\n--r,  --read         Reads perms/etc from working dir into a .gitmeta file\n--s,  --stdout       Output to stdout instead of .gitmeta\n--d,  --diff         Show unified diff of perms file (XOR with --stdout)\n-\n----------------------------------Write Mode------------------------------------\n--w,  --write        Modify perms/etc in working dir to match the .gitmeta file\n--v,  --verbose      Be verbose\n-\n-\\n\";\n-\n-my ($stdout, $showdiff, $verbose, $read_mode, $write_mode);\n-\n-if ((@ARGV < 0) || !GetOptions(\n-\t\t\t       \"stdout\",         \\$stdout,\n-\t\t\t       \"diff\",           \\$showdiff,\n-\t\t\t       \"read\",           \\$read_mode,\n-\t\t\t       \"write\",          \\$write_mode,\n-\t\t\t       \"verbose\",        \\$verbose,\n-\t\t\t      )) { die $usage; }\n-die $usage unless ($read_mode xor $write_mode);\n-\n-my $topdir = `git rev-parse --show-cdup` or die \"\\n\"; chomp $topdir;\n-my $gitdir = $topdir . '.git';\n-my $gitmeta = $topdir . '.gitmeta';\n-\n-if ($write_mode) {\n-    # Update the working dir permissions/ownership based on data from .gitmeta\n-    open (IN, \"<$gitmeta\") or die \"Could not open $gitmeta for reading: $!\\n\";\n-    while (defined ($_ = <IN>)) {\n-\tchomp;\n-\tif (/^(.*)  mode=(\\S+)\\s+uid=(\\d+)\\s+gid=(\\d+)/) {\n-\t    # Compare recorded perms to actual perms in the working dir\n-\t    my ($path, $mode, $uid, $gid) = ($1, $2, $3, $4);\n-\t    my $fullpath = $topdir . $path;\n-\t    my (undef,undef,$wmode,undef,$wuid,$wgid) = lstat($fullpath);\n-\t    $wmode = sprintf \"%04o\", $wmode & 07777;\n-\t    if ($mode ne $wmode) {\n-\t\t$verbose && print \"Updating permissions on $path: old=$wmode, new=$mode\\n\";\n-\t\tchmod oct($mode), $fullpath;\n-\t    }\n-\t    if ($uid != $wuid || $gid != $wgid) {\n-\t\tif ($verbose) {\n-\t\t    # Print out user/group names instead of uid/gid\n-\t\t    my $pwname  = getpwuid($uid);\n-\t\t    my $grpname  = getgrgid($gid);\n-\t\t    my $wpwname  = getpwuid($wuid);\n-\t\t    my $wgrpname  = getgrgid($wgid);\n-\t\t    $pwname = $uid if !defined $pwname;\n-\t\t    $grpname = $gid if !defined $grpname;\n-\t\t    $wpwname = $wuid if !defined $wpwname;\n-\t\t    $wgrpname = $wgid if !defined $wgrpname;\n-\n-\t\t    print \"Updating uid/gid on $path: old=$wpwname/$wgrpname, new=$pwname/$grpname\\n\";\n-\t\t}\n-\t\tchown $uid, $gid, $fullpath;\n-\t    }\n-\t}\n-\telse {\n-\t    warn \"Invalid input format in $gitmeta:\\n\\t$_\\n\";\n-\t}\n-    }\n-    close IN;\n-}\n-elsif ($read_mode) {\n-    # Handle merge conflicts in the .gitperms file\n-    if (-e \"$gitdir/MERGE_MSG\") {\n-\tif (`grep ====== $gitmeta`) {\n-\t    # Conflict not resolved -- abort the commit\n-\t    print \"PERMISSIONS/OWNERSHIP CONFLICT\\n\";\n-\t    print \"    Resolve the conflict in the $gitmeta file and then run\\n\";\n-\t    print \"    `.git/hooks/setgitperms.perl --write` to reconcile.\\n\";\n-\t    exit 1;\n-\t}\n-\telsif (`grep $gitmeta $gitdir/MERGE_MSG`) {\n-\t    # A conflict in .gitmeta has been manually resolved. Verify that\n-\t    # the working dir perms matches the current .gitmeta perms for\n-\t    # each file/dir that conflicted.\n-\t    # This is here because a `setgitperms.perl --write` was not\n-\t    # performed due to a merge conflict, so permissions/ownership\n-\t    # may not be consistent with the manually merged .gitmeta file.\n-\t    my @conflict_diff = `git show \\$(cat $gitdir/MERGE_HEAD)`;\n-\t    my @conflict_files;\n-\t    my $metadiff = 0;\n-\n-\t    # Build a list of files that conflicted from the .gitmeta diff\n-\t    foreach my $line (@conflict_diff) {\n-\t\tif ($line =~ m|^diff --git a/$gitmeta b/$gitmeta|) {\n-\t\t    $metadiff = 1;\n-\t\t}\n-\t\telsif ($line =~ /^diff --git/) {\n-\t\t    $metadiff = 0;\n-\t\t}\n-\t\telsif ($metadiff && $line =~ /^\\+(.*)  mode=/) {\n-\t\t    push @conflict_files, $1;\n-\t\t}\n-\t    }\n-\n-\t    # Verify that each conflict file now has permissions consistent\n-\t    # with the .gitmeta file\n-\t    foreach my $file (@conflict_files) {\n-\t\tmy $absfile = $topdir . $file;\n-\t\tmy $gm_entry = `grep \"^$file  mode=\" $gitmeta`;\n-\t\tif ($gm_entry =~ /mode=(\\d+)  uid=(\\d+)  gid=(\\d+)/) {\n-\t\t    my ($gm_mode, $gm_uid, $gm_gid) = ($1, $2, $3);\n-\t\t    my (undef,undef,$mode,undef,$uid,$gid) = lstat(\"$absfile\");\n-\t\t    $mode = sprintf(\"%04o\", $mode & 07777);\n-\t\t    if (($gm_mode ne $mode) || ($gm_uid != $uid)\n-\t\t\t|| ($gm_gid != $gid)) {\n-\t\t\tprint \"PERMISSIONS/OWNERSHIP CONFLICT\\n\";\n-\t\t\tprint \"    Mismatch found for file: $file\\n\";\n-\t\t\tprint \"    Run `.git/hooks/setgitperms.perl --write` to reconcile.\\n\";\n-\t\t\texit 1;\n-\t\t    }\n-\t\t}\n-\t\telse {\n-\t\t    print \"Warning! Permissions/ownership no longer being tracked for file: $file\\n\";\n-\t\t}\n-\t    }\n-\t}\n-    }\n-\n-    # No merge conflicts -- write out perms/ownership data to .gitmeta file\n-    unless ($stdout) {\n-\topen (OUT, \">$gitmeta.tmp\") or die \"Could not open $gitmeta.tmp for writing: $!\\n\";\n-    }\n-\n-    my @files = `git ls-files`;\n-    my %dirs;\n-\n-    foreach my $path (@files) {\n-\tchomp $path;\n-\t# We have to manually add stats for parent directories\n-\tmy $parent = dirname($path);\n-\twhile (!exists $dirs{$parent}) {\n-\t    $dirs{$parent} = 1;\n-\t    next if $parent eq '.';\n-\t    printstats($parent);\n-\t    $parent = dirname($parent);\n-\t}\n-\t# Now the git-tracked file\n-\tprintstats($path);\n-    }\n-\n-    # diff the temporary metadata file to see if anything has changed\n-    # If no metadata has changed, don't overwrite the real file\n-    # This is just so `git commit -a` doesn't try to commit a bogus update\n-    unless ($stdout) {\n-\tif (! -e $gitmeta) {\n-\t    rename \"$gitmeta.tmp\", $gitmeta;\n-\t}\n-\telse {\n-\t    my $diff = `diff -U 0 $gitmeta $gitmeta.tmp`;\n-\t    if ($diff ne '') {\n-\t\trename \"$gitmeta.tmp\", $gitmeta;\n-\t    }\n-\t    else {\n-\t\tunlink \"$gitmeta.tmp\";\n-\t    }\n-\t    if ($showdiff) {\n-\t\tprint $diff;\n-\t    }\n-\t}\n-\tclose OUT;\n-    }\n-    # Make sure the .gitmeta file is tracked\n-    system(\"git add $gitmeta\");\n-}\n-\n-\n-sub printstats {\n-    my $path = $_[0];\n-    $path =~ s/@/\\@/g;\n-    my (undef,undef,$mode,undef,$uid,$gid) = lstat($path);\n-    $path =~ s/%/\\%/g;\n-    if ($stdout) {\n-\tprint $path;\n-\tprintf \"  mode=%04o  uid=$uid  gid=$gid\\n\", $mode & 07777;\n-    }\n-    else {\n-\tprint OUT $path;\n-\tprintf OUT \"  mode=%04o  uid=$uid  gid=$gid\\n\", $mode & 07777;\n-    }\n-}\ndiff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid\ndeleted file mode 100755\nindex 0092d67b8a4..00000000000\n--- a/contrib/hooks/update-paranoid\n+++ /dev/null\n@@ -1,421 +0,0 @@\n-#!/usr/bin/perl\n-\n-use strict;\n-use File::Spec;\n-\n-$ENV{PATH}     = '/opt/git/bin';\n-my $acl_git    = '/vcs/acls.git';\n-my $acl_branch = 'refs/heads/master';\n-my $debug      = 0;\n-\n-=doc\n-Invoked as: update refname old-sha1 new-sha1\n-\n-This script is run by git-receive-pack once for each ref that the\n-client is trying to modify.  If we exit with a non-zero exit value\n-then the update for that particular ref is denied, but updates for\n-other refs in the same run of receive-pack may still be allowed.\n-\n-We are run after the objects have been uploaded, but before the\n-ref is actually modified.  We take advantage of that fact when we\n-look for \"new\" commits and tags (the new objects won't show up in\n-`rev-list --all`).\n-\n-This script loads and parses the content of the config file\n-\"users/$this_user.acl\" from the $acl_branch commit of $acl_git ODB.\n-The acl file is a git-config style file, but uses a slightly more\n-restricted syntax as the Perl parser contained within this script\n-is not nearly as permissive as git-config.\n-\n-Example:\n-\n-  [user]\n-    committer = John Doe <john.doe@example.com>\n-    committer = John R. Doe <john.doe@example.com>\n-\n-  [repository \"acls\"]\n-    allow = heads/master\n-    allow = CDUR for heads/jd/\n-    allow = C    for ^tags/v\\\\d+$\n-\n-For all new commit or tag objects the committer (or tagger) line\n-within the object must exactly match one of the user.committer\n-values listed in the acl file (\"HEAD:users/$this_user.acl\").\n-\n-For a branch to be modified an allow line within the matching\n-repository section must be matched for both the refname and the\n-opcode.\n-\n-Repository sections are matched on the basename of the repository\n-(after removing the .git suffix).\n-\n-The opcode abbreviations are:\n-\n-  C: create new ref\n-  D: delete existing ref\n-  U: fast-forward existing ref (no commit loss)\n-  R: rewind/rebase existing ref (commit loss)\n-\n-if no opcodes are listed before the \"for\" keyword then \"U\" (for\n-fast-forward update only) is assumed as this is the most common\n-usage.\n-\n-Refnames are matched by always assuming a prefix of \"refs/\".\n-This hook forbids pushing or deleting anything not under \"refs/\".\n-\n-Refnames that start with ^ are Perl regular expressions, and the ^\n-is kept as part of the regexp.  \\\\ is needed to get just one \\, so\n-\\\\d expands to \\d in Perl.  The 3rd allow line above is an example.\n-\n-Refnames that don't start with ^ but that end with / are prefix\n-matches (2nd allow line above); all other refnames are strict\n-equality matches (1st allow line).\n-\n-Anything pushed to \"heads/\" (ok, really \"refs/heads/\") must be\n-a commit.  Tags are not permitted here.\n-\n-Anything pushed to \"tags/\" (err, really \"refs/tags/\") must be an\n-annotated tag.  Commits, blobs, trees, etc. are not permitted here.\n-Annotated tag signatures aren't checked, nor are they required.\n-\n-The special subrepository of 'info/new-commit-check' can\n-be created and used to allow users to push new commits and\n-tags from another local repository to this one, even if they\n-aren't the committer/tagger of those objects.  In a nut shell\n-the info/new-commit-check directory is a Git repository whose\n-objects/info/alternates file lists this repository and all other\n-possible sources, and whose refs subdirectory contains symlinks\n-to this repository's refs subdirectory, and to all other possible\n-sources refs subdirectories.  Yes, this means that you cannot\n-use packed-refs in those repositories as they won't be resolved\n-correctly.\n-\n-=cut\n-\n-my $git_dir = $ENV{GIT_DIR};\n-my $new_commit_check = \"$git_dir/info/new-commit-check\";\n-my $ref = $ARGV[0];\n-my $old = $ARGV[1];\n-my $new = $ARGV[2];\n-my $new_type;\n-my ($this_user) = getpwuid $<; # REAL_USER_ID\n-my $repository_name;\n-my %user_committer;\n-my @allow_rules;\n-my @path_rules;\n-my %diff_cache;\n-\n-sub deny ($) {\n-\tprint STDERR \"-Deny-    $_[0]\\n\" if $debug;\n-\tprint STDERR \"\\ndenied: $_[0]\\n\\n\";\n-\texit 1;\n-}\n-\n-sub grant ($) {\n-\tprint STDERR \"-Grant-   $_[0]\\n\" if $debug;\n-\texit 0;\n-}\n-\n-sub info ($) {\n-\tprint STDERR \"-Info-    $_[0]\\n\" if $debug;\n-}\n-\n-sub git_value (@) {\n-\topen(T,'-|','git',@_); local $_ = <T>; chop; close T; $_;\n-}\n-\n-sub match_string ($$) {\n-\tmy ($acl_n, $ref) = @_;\n-\t   ($acl_n eq $ref)\n-\t|| ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n)\n-\t|| ($acl_n =~ m,^\\^, && $ref =~ m:$acl_n:);\n-}\n-\n-sub parse_config ($$$$) {\n-\tmy $data = shift;\n-\tlocal $ENV{GIT_DIR} = shift;\n-\tmy $br = shift;\n-\tmy $fn = shift;\n-\treturn unless git_value('rev-list','--max-count=1',$br,'--',$fn);\n-\tinfo \"Loading $br:$fn\";\n-\topen(I,'-|','git','cat-file','blob',\"$br:$fn\");\n-\tmy $section = '';\n-\twhile (<I>) {\n-\t\tchomp;\n-\t\tif (/^\\s*$/ || /^\\s*#/) {\n-\t\t} elsif (/^\\[([a-z]+)\\]$/i) {\n-\t\t\t$section = lc $1;\n-\t\t} elsif (/^\\[([a-z]+)\\s+\"(.*)\"\\]$/i) {\n-\t\t\t$section = join('.',lc $1,$2);\n-\t\t} elsif (/^\\s*([a-z][a-z0-9]+)\\s*=\\s*(.*?)\\s*$/i) {\n-\t\t\tpush @{$data->{join('.',$section,lc $1)}}, $2;\n-\t\t} else {\n-\t\t\tdeny \"bad config file line $. in $br:$fn\";\n-\t\t}\n-\t}\n-\tclose I;\n-}\n-\n-sub all_new_committers () {\n-\tlocal $ENV{GIT_DIR} = $git_dir;\n-\t$ENV{GIT_DIR} = $new_commit_check if -d $new_commit_check;\n-\n-\tinfo \"Getting committers of new commits.\";\n-\tmy %used;\n-\topen(T,'-|','git','rev-list','--pretty=raw',$new,'--not','--all');\n-\twhile (<T>) {\n-\t\tnext unless s/^committer //;\n-\t\tchop;\n-\t\ts/>.*$/>/;\n-\t\tinfo \"Found $_.\" unless $used{$_}++;\n-\t}\n-\tclose T;\n-\tinfo \"No new commits.\" unless %used;\n-\tkeys %used;\n-}\n-\n-sub all_new_taggers () {\n-\tmy %exists;\n-\topen(T,'-|','git','for-each-ref','--format=%(objectname)','refs/tags');\n-\twhile (<T>) {\n-\t\tchop;\n-\t\t$exists{$_} = 1;\n-\t}\n-\tclose T;\n-\n-\tinfo \"Getting taggers of new tags.\";\n-\tmy %used;\n-\tmy $obj = $new;\n-\tmy $obj_type = $new_type;\n-\twhile ($obj_type eq 'tag') {\n-\t\tlast if $exists{$obj};\n-\t\t$obj_type = '';\n-\t\topen(T,'-|','git','cat-file','tag',$obj);\n-\t\twhile (<T>) {\n-\t\t\tchop;\n-\t\t\tif (/^object ([a-z0-9]{40})$/) {\n-\t\t\t\t$obj = $1;\n-\t\t\t} elsif (/^type (.+)$/) {\n-\t\t\t\t$obj_type = $1;\n-\t\t\t} elsif (s/^tagger //) {\n-\t\t\t\ts/>.*$/>/;\n-\t\t\t\tinfo \"Found $_.\" unless $used{$_}++;\n-\t\t\t\tlast;\n-\t\t\t}\n-\t\t}\n-\t\tclose T;\n-\t}\n-\tinfo \"No new tags.\" unless %used;\n-\tkeys %used;\n-}\n-\n-sub check_committers (@) {\n-\tmy @bad;\n-\tforeach (@_) { push @bad, $_ unless $user_committer{$_}; }\n-\tif (@bad) {\n-\t\tprint STDERR \"\\n\";\n-\t\tprint STDERR \"You are not $_.\\n\" foreach (sort @bad);\n-\t\tdeny \"You cannot push changes not committed by you.\";\n-\t}\n-}\n-\n-sub load_diff ($) {\n-\tmy $base = shift;\n-\tmy $d = $diff_cache{$base};\n-\tunless ($d) {\n-\t\tlocal $/ = \"\\0\";\n-\t\tmy %this_diff;\n-\t\tif ($base =~ /^0{40}$/) {\n-\t\t\t# Don't load the diff at all; we are making the\n-\t\t\t# branch and have no base to compare to in this\n-\t\t\t# case.  A file level ACL makes no sense in this\n-\t\t\t# context.  Having an empty diff will allow the\n-\t\t\t# branch creation.\n-\t\t\t#\n-\t\t} else {\n-\t\t\topen(T,'-|','git','diff-tree',\n-\t\t\t\t'-r','--name-status','-z',\n-\t\t\t\t$base,$new) or return undef;\n-\t\t\twhile (<T>) {\n-\t\t\t\tmy $op = $_;\n-\t\t\t\tchop $op;\n-\n-\t\t\t\tmy $path = <T>;\n-\t\t\t\tchop $path;\n-\n-\t\t\t\t$this_diff{$path} = $op;\n-\t\t\t}\n-\t\t\tclose T or return undef;\n-\t\t}\n-\t\t$d = \\%this_diff;\n-\t\t$diff_cache{$base} = $d;\n-\t}\n-\treturn $d;\n-}\n-\n-deny \"No GIT_DIR inherited from caller\" unless $git_dir;\n-deny \"Need a ref name\" unless $ref;\n-deny \"Refusing funny ref $ref\" unless $ref =~ s,^refs/,,;\n-deny \"Bad old value $old\" unless $old =~ /^[a-z0-9]{40}$/;\n-deny \"Bad new value $new\" unless $new =~ /^[a-z0-9]{40}$/;\n-deny \"Cannot determine who you are.\" unless $this_user;\n-grant \"No change requested.\" if $old eq $new;\n-\n-$repository_name = File::Spec->rel2abs($git_dir);\n-$repository_name =~ m,/([^/]+)(?:\\.git|/\\.git)$,;\n-$repository_name = $1;\n-info \"Updating in '$repository_name'.\";\n-\n-my $op;\n-if    ($old =~ /^0{40}$/) { $op = 'C'; }\n-elsif ($new =~ /^0{40}$/) { $op = 'D'; }\n-else                      { $op = 'R'; }\n-\n-# This is really an update (fast-forward) if the\n-# merge base of $old and $new is $old.\n-#\n-$op = 'U' if ($op eq 'R'\n-\t&& $ref =~ m,^heads/,\n-\t&& $old eq git_value('merge-base',$old,$new));\n-\n-# Load the user's ACL file. Expand groups (user.memberof) one level.\n-{\n-\tmy %data = ('user.committer' => []);\n-\tparse_config(\\%data,$acl_git,$acl_branch,\"external/$repository_name.acl\");\n-\n-\t%data = (\n-\t\t'user.committer' => $data{'user.committer'},\n-\t\t'user.memberof' => [],\n-\t);\n-\tparse_config(\\%data,$acl_git,$acl_branch,\"users/$this_user.acl\");\n-\n-\t%user_committer = map {$_ => $_} @{$data{'user.committer'}};\n-\tmy $rule_key = \"repository.$repository_name.allow\";\n-\tmy $rules = $data{$rule_key} || [];\n-\n-\tforeach my $group (@{$data{'user.memberof'}}) {\n-\t\tmy %g;\n-\t\tparse_config(\\%g,$acl_git,$acl_branch,\"groups/$group.acl\");\n-\t\tmy $group_rules = $g{$rule_key};\n-\t\tpush @$rules, @$group_rules if $group_rules;\n-\t}\n-\n-RULE:\n-\tforeach (@$rules) {\n-\t\twhile (/\\${user\\.([a-z][a-zA-Z0-9]+)}/) {\n-\t\t\tmy $k = lc $1;\n-\t\t\tmy $v = $data{\"user.$k\"};\n-\t\t\tnext RULE unless defined $v;\n-\t\t\tnext RULE if @$v != 1;\n-\t\t\tnext RULE unless defined $v->[0];\n-\t\t\ts/\\${user\\.$k}/$v->[0]/g;\n-\t\t}\n-\n-\t\tif (/^([AMD ]+)\\s+of\\s+([^\\s]+)\\s+for\\s+([^\\s]+)\\s+diff\\s+([^\\s]+)$/) {\n-\t\t\tmy ($ops, $pth, $ref, $bst) = ($1, $2, $3, $4);\n-\t\t\t$ops =~ s/ //g;\n-\t\t\t$pth =~ s/\\\\\\\\/\\\\/g;\n-\t\t\t$ref =~ s/\\\\\\\\/\\\\/g;\n-\t\t\tpush @path_rules, [$ops, $pth, $ref, $bst];\n-\t\t} elsif (/^([AMD ]+)\\s+of\\s+([^\\s]+)\\s+for\\s+([^\\s]+)$/) {\n-\t\t\tmy ($ops, $pth, $ref) = ($1, $2, $3);\n-\t\t\t$ops =~ s/ //g;\n-\t\t\t$pth =~ s/\\\\\\\\/\\\\/g;\n-\t\t\t$ref =~ s/\\\\\\\\/\\\\/g;\n-\t\t\tpush @path_rules, [$ops, $pth, $ref, $old];\n-\t\t} elsif (/^([CDRU ]+)\\s+for\\s+([^\\s]+)$/) {\n-\t\t\tmy $ops = $1;\n-\t\t\tmy $ref = $2;\n-\t\t\t$ops =~ s/ //g;\n-\t\t\t$ref =~ s/\\\\\\\\/\\\\/g;\n-\t\t\tpush @allow_rules, [$ops, $ref];\n-\t\t} elsif (/^for\\s+([^\\s]+)$/) {\n-\t\t\t# Mentioned, but nothing granted?\n-\t\t} elsif (/^[^\\s]+$/) {\n-\t\t\ts/\\\\\\\\/\\\\/g;\n-\t\t\tpush @allow_rules, ['U', $_];\n-\t\t}\n-\t}\n-}\n-\n-if ($op ne 'D') {\n-\t$new_type = git_value('cat-file','-t',$new);\n-\n-\tif ($ref =~ m,^heads/,) {\n-\t\tdeny \"$ref must be a commit.\" unless $new_type eq 'commit';\n-\t} elsif ($ref =~ m,^tags/,) {\n-\t\tdeny \"$ref must be an annotated tag.\" unless $new_type eq 'tag';\n-\t}\n-\n-\tcheck_committers (all_new_committers);\n-\tcheck_committers (all_new_taggers) if $new_type eq 'tag';\n-}\n-\n-info \"$this_user wants $op for $ref\";\n-foreach my $acl_entry (@allow_rules) {\n-\tmy ($acl_ops, $acl_n) = @$acl_entry;\n-\tnext unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen.\n-\tnext unless $acl_n;\n-\tnext unless $op =~ /^[$acl_ops]$/;\n-\tnext unless match_string $acl_n, $ref;\n-\n-\t# Don't test path rules on branch deletes.\n-\t#\n-\tgrant \"Allowed by: $acl_ops for $acl_n\" if $op eq 'D';\n-\n-\t# Aggregate matching path rules; allow if there aren't\n-\t# any matching this ref.\n-\t#\n-\tmy %pr;\n-\tforeach my $p_entry (@path_rules) {\n-\t\tmy ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry;\n-\t\tnext unless $p_ref;\n-\t\tpush @{$pr{$p_bst}}, $p_entry if match_string $p_ref, $ref;\n-\t}\n-\tgrant \"Allowed by: $acl_ops for $acl_n\" unless %pr;\n-\n-\t# Allow only if all changes against a single base are\n-\t# allowed by file path rules.\n-\t#\n-\tmy @bad;\n-\tforeach my $p_bst (keys %pr) {\n-\t\tmy $diff_ref = load_diff $p_bst;\n-\t\tdeny \"Cannot difference trees.\" unless ref $diff_ref;\n-\n-\t\tmy %fd = %$diff_ref;\n-\t\tforeach my $p_entry (@{$pr{$p_bst}}) {\n-\t\t\tmy ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry;\n-\t\t\tnext unless $p_ops =~ /^[AMD]+$/;\n-\t\t\tnext unless $p_n;\n-\n-\t\t\tforeach my $f_n (keys %fd) {\n-\t\t\t\tmy $f_op = $fd{$f_n};\n-\t\t\t\tnext unless $f_op;\n-\t\t\t\tnext unless $f_op =~ /^[$p_ops]$/;\n-\t\t\t\tdelete $fd{$f_n} if match_string $p_n, $f_n;\n-\t\t\t}\n-\t\t\tlast unless %fd;\n-\t\t}\n-\n-\t\tif (%fd) {\n-\t\t\tpush @bad, [$p_bst, \\%fd];\n-\t\t} else {\n-\t\t\t# All changes relative to $p_bst were allowed.\n-\t\t\t#\n-\t\t\tgrant \"Allowed by: $acl_ops for $acl_n diff $p_bst\";\n-\t\t}\n-\t}\n-\n-\tforeach my $bad_ref (@bad) {\n-\t\tmy ($p_bst, $fd) = @$bad_ref;\n-\t\tprint STDERR \"\\n\";\n-\t\tprint STDERR \"Not allowed to make the following changes:\\n\";\n-\t\tprint STDERR \"(base: $p_bst)\\n\";\n-\t\tforeach my $f_n (sort keys %$fd) {\n-\t\t\tprint STDERR \"  $fd->{$f_n} $f_n\\n\";\n-\t\t}\n-\t}\n-\tdeny \"You are not permitted to $op $ref\";\n-}\n-close A;\n-deny \"You are not permitted to $op $ref\";\n\n-- \n2.49.0.1045.g170613ef41.dirty\n\n"},{"id":"517354","messageId":"20250506-pks-contrib-spring-cleanup-v1-6-e6d5ddd79a72@pks.im","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","subject":"[PATCH 06/10] contrib: remove \"mw-to-git\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-06T14:12:39Z","receivedAt":"2025-05-06T14:12:47Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"mw-to-git\" directory contains tools for accessing MediaWiki via\nGit. The scripts are essentially unmaintained in Git: despite a couple\nof global cleanups, the last changes were a couple of security-related\nissues part of 9a8606465e8 (remote-mediawiki: use \"sh\" to eliminate\nunquoted commands, 2020-09-21) and its parents. We don't ever run any of\nthe tests so it is more likely than not that many of the tests have been\nbitrotting, like e.g. documented in f8ab018dafc (remote-mediawiki tests:\nannotate failing tests, 2020-09-21).\n\nFurthermore, the code has been spun out into a separate project at [2].\nRemove the directory in favor of this new home.\n\n[2]: https://github.com/Git-Mediawiki/Git-Mediawiki\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/mw-to-git/.gitignore                       |    2 -\n contrib/mw-to-git/.perlcriticrc                    |   28 -\n contrib/mw-to-git/Git/Mediawiki.pm                 |  101 --\n contrib/mw-to-git/Makefile                         |   61 -\n contrib/mw-to-git/bin-wrapper/git                  |   14 -\n contrib/mw-to-git/git-mw.perl                      |  368 ------\n contrib/mw-to-git/git-remote-mediawiki.perl        | 1390 --------------------\n contrib/mw-to-git/git-remote-mediawiki.txt         |    7 -\n contrib/mw-to-git/t/.gitignore                     |    4 -\n contrib/mw-to-git/t/Makefile                       |   32 -\n contrib/mw-to-git/t/README                         |  124 --\n contrib/mw-to-git/t/install-wiki.sh                |   55 -\n contrib/mw-to-git/t/push-pull-tests.sh             |  144 --\n contrib/mw-to-git/t/t9360-mw-to-git-clone.sh       |  257 ----\n contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh   |   24 -\n contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh        |  347 -----\n .../mw-to-git/t/t9363-mw-to-git-export-import.sh   |  218 ---\n contrib/mw-to-git/t/t9364-pull-by-rev.sh           |   17 -\n contrib/mw-to-git/t/t9365-continuing-queries.sh    |   23 -\n contrib/mw-to-git/t/test-gitmw-lib.sh              |  432 ------\n contrib/mw-to-git/t/test-gitmw.pl                  |  223 ----\n contrib/mw-to-git/t/test.config                    |   40 -\n 22 files changed, 3911 deletions(-)\n\ndiff --git a/contrib/mw-to-git/.gitignore b/contrib/mw-to-git/.gitignore\ndeleted file mode 100644\nindex ae545b013dc..00000000000\n--- a/contrib/mw-to-git/.gitignore\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-git-remote-mediawiki\n-git-mw\ndiff --git a/contrib/mw-to-git/.perlcriticrc b/contrib/mw-to-git/.perlcriticrc\ndeleted file mode 100644\nindex b7333267ada..00000000000\n--- a/contrib/mw-to-git/.perlcriticrc\n+++ /dev/null\n@@ -1,28 +0,0 @@\n-# These 3 rules demand to add the s, m and x flag to *every* regexp. This is\n-# overkill and would be harmful for readability.\n-[-RegularExpressions::RequireExtendedFormatting]\n-[-RegularExpressions::RequireDotMatchAnything]\n-[-RegularExpressions::RequireLineBoundaryMatching]\n-\n-# This rule says that builtin functions should not be called with parentheses\n-# e.g.: (taken from CPAN's documentation)\n-# open($handle, '>', $filename); #not ok\n-# open $handle, '>', $filename;  #ok\n-# Applying such a rule would mean modifying a huge number of lines for a\n-# question of style.\n-[-CodeLayout::ProhibitParensWithBuiltins]\n-\n-# This rule states that each system call should have its return value checked\n-# The problem is that it includes the print call. Checking every print call's\n-# return value would be harmful to the code readability.\n-# This configuration keeps all default function but print.\n-[InputOutput::RequireCheckedSyscalls]\n-functions = open say close\n-\n-# This rule demands to add a dependency for the Readonly module. This is not\n-# wished.\n-[-ValuesAndExpressions::ProhibitConstantPragma]\n-\n-# This rule is not really useful (rather a question of style) and produces many\n-# warnings among the code.\n-[-ValuesAndExpressions::ProhibitNoisyQuotes]\ndiff --git a/contrib/mw-to-git/Git/Mediawiki.pm b/contrib/mw-to-git/Git/Mediawiki.pm\ndeleted file mode 100644\nindex 629c0cea442..00000000000\n--- a/contrib/mw-to-git/Git/Mediawiki.pm\n+++ /dev/null\n@@ -1,101 +0,0 @@\n-package Git::Mediawiki;\n-\n-require v5.26;\n-use strict;\n-use POSIX;\n-use Git;\n-\n-BEGIN {\n-\n-our ($VERSION, @ISA, @EXPORT, @EXPORT_OK);\n-\n-# Totally unstable API.\n-$VERSION = '0.01';\n-\n-require Exporter;\n-\n-@ISA = qw(Exporter);\n-\n-@EXPORT = ();\n-\n-# Methods which can be called as standalone functions as well:\n-@EXPORT_OK = qw(clean_filename smudge_filename connect_maybe\n-\t\t\t\tEMPTY HTTP_CODE_OK HTTP_CODE_PAGE_NOT_FOUND);\n-}\n-\n-# Mediawiki filenames can contain forward slashes. This variable decides by which pattern they should be replaced\n-use constant SLASH_REPLACEMENT => '%2F';\n-\n-# Used to test for empty strings\n-use constant EMPTY => q{};\n-\n-# HTTP codes\n-use constant HTTP_CODE_OK => 200;\n-use constant HTTP_CODE_PAGE_NOT_FOUND => 404;\n-\n-sub clean_filename {\n-\tmy $filename = shift;\n-\t$filename =~ s{@{[SLASH_REPLACEMENT]}}{/}g;\n-\t# [, ], |, {, and } are forbidden by MediaWiki, even URL-encoded.\n-\t# Do a variant of URL-encoding, i.e. looks like URL-encoding,\n-\t# but with _ added to prevent MediaWiki from thinking this is\n-\t# an actual special character.\n-\t$filename =~ s/[\\[\\]\\{\\}\\|]/sprintf(\"_%%_%x\", ord($&))/ge;\n-\t# If we use the uri escape before\n-\t# we should unescape here, before anything\n-\n-\treturn $filename;\n-}\n-\n-sub smudge_filename {\n-\tmy $filename = shift;\n-\t$filename =~ s{/}{@{[SLASH_REPLACEMENT]}}g;\n-\t$filename =~ s/ /_/g;\n-\t# Decode forbidden characters encoded in clean_filename\n-\t$filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf('%c', hex($1))/ge;\n-\treturn substr($filename, 0, NAME_MAX-length('.mw'));\n-}\n-\n-sub connect_maybe {\n-\tmy $wiki = shift;\n-\tif ($wiki) {\n-\t\treturn $wiki;\n-\t}\n-\n-\tmy $remote_name = shift;\n-\tmy $remote_url = shift;\n-\tmy ($wiki_login, $wiki_password, $wiki_domain);\n-\n-\t$wiki_login = Git::config(\"remote.${remote_name}.mwLogin\");\n-\t$wiki_password = Git::config(\"remote.${remote_name}.mwPassword\");\n-\t$wiki_domain = Git::config(\"remote.${remote_name}.mwDomain\");\n-\n-\t$wiki = MediaWiki::API->new;\n-\t$wiki->{config}->{api_url} = \"${remote_url}/api.php\";\n-\tif ($wiki_login) {\n-\t\tmy %credential = (\n-\t\t\t'url' => $remote_url,\n-\t\t\t'username' => $wiki_login,\n-\t\t\t'password' => $wiki_password\n-\t\t);\n-\t\tGit::credential(\\%credential);\n-\t\tmy $request = {lgname => $credential{username},\n-\t\t\t       lgpassword => $credential{password},\n-\t\t\t       lgdomain => $wiki_domain};\n-\t\tif ($wiki->login($request)) {\n-\t\t\tGit::credential(\\%credential, 'approve');\n-\t\t\tprint {*STDERR} qq(Logged in mediawiki user \"$credential{username}\".\\n);\n-\t\t} else {\n-\t\t\tprint {*STDERR} qq(Failed to log in mediawiki user \"$credential{username}\" on ${remote_url}\\n);\n-\t\t\tprint {*STDERR} '  (error ' .\n-\t\t\t\t$wiki->{error}->{code} . ': ' .\n-\t\t\t\t$wiki->{error}->{details} . \")\\n\";\n-\t\t\tGit::credential(\\%credential, 'reject');\n-\t\t\texit 1;\n-\t\t}\n-\t}\n-\n-\treturn $wiki;\n-}\n-\n-1; # Famous last words\ndiff --git a/contrib/mw-to-git/Makefile b/contrib/mw-to-git/Makefile\ndeleted file mode 100644\nindex 497ac434d60..00000000000\n--- a/contrib/mw-to-git/Makefile\n+++ /dev/null\n@@ -1,61 +0,0 @@\n-#\n-# Copyright (C) 2013\n-#     Matthieu Moy <Matthieu.Moy@imag.fr>\n-#\n-# To build and test:\n-#\n-#   make\n-#   bin-wrapper/git mw preview Some_page.mw\n-#   bin-wrapper/git clone mediawiki::http://example.com/wiki/\n-#\n-# To install, run Git's toplevel 'make install' then run:\n-#\n-#   make install\n-\n-# The default target of this Makefile is...\n-all::\n-\n-GIT_MEDIAWIKI_PM=Git/Mediawiki.pm\n-SCRIPT_PERL=git-remote-mediawiki.perl\n-SCRIPT_PERL+=git-mw.perl\n-GIT_ROOT_DIR=../..\n-HERE=contrib/mw-to-git/\n-\n-INSTALL = install\n-\n-SCRIPT_PERL_FULL=$(patsubst %,$(HERE)/%,$(SCRIPT_PERL))\n-INSTLIBDIR=$(shell $(MAKE) -C $(GIT_ROOT_DIR)/ \\\n-                -s --no-print-directory prefix=$(prefix) \\\n-                perllibdir=$(perllibdir) perllibdir)\n-DESTDIR_SQ = $(subst ','\\'',$(DESTDIR))\n-INSTLIBDIR_SQ = $(subst ','\\'',$(INSTLIBDIR))\n-\n-all:: build\n-\n-test: all\n-\t$(MAKE) -C t\n-\n-check: perlcritic test\n-\n-install_pm:\n-\t$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(INSTLIBDIR_SQ)/Git'\n-\t$(INSTALL) -m 644 $(GIT_MEDIAWIKI_PM) \\\n-\t\t'$(DESTDIR_SQ)$(INSTLIBDIR_SQ)/$(GIT_MEDIAWIKI_PM)'\n-\n-build:\n-\t$(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL=\"$(SCRIPT_PERL_FULL)\" \\\n-                build-perl-script\n-\n-install: install_pm\n-\t$(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL=\"$(SCRIPT_PERL_FULL)\" \\\n-                install-perl-script\n-\n-clean:\n-\t$(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL=\"$(SCRIPT_PERL_FULL)\" \\\n-                clean-perl-script\n-\n-perlcritic:\n-\tperlcritic -5 $(SCRIPT_PERL)\n-\t-perlcritic -2 $(SCRIPT_PERL)\n-\n-.PHONY: all test check install_pm install clean perlcritic\ndiff --git a/contrib/mw-to-git/bin-wrapper/git b/contrib/mw-to-git/bin-wrapper/git\ndeleted file mode 100755\nindex 6663ae57e86..00000000000\n--- a/contrib/mw-to-git/bin-wrapper/git\n+++ /dev/null\n@@ -1,14 +0,0 @@\n-#!/bin/sh\n-\n-# git executable wrapper script for Git-Mediawiki to run tests without\n-# installing all the scripts and perl packages.\n-\n-GIT_ROOT_DIR=../../..\n-GIT_EXEC_PATH=$(cd \"$(dirname \"$0\")\" && cd ${GIT_ROOT_DIR} && pwd)\n-\n-GITPERLLIB=\"$GIT_EXEC_PATH\"'/contrib/mw-to-git'\"${GITPERLLIB:+:$GITPERLLIB}\"\n-PATH=\"$GIT_EXEC_PATH\"'/contrib/mw-to-git:'\"$PATH\"\n-\n-export GITPERLLIB PATH\n-\n-exec \"${GIT_EXEC_PATH}/bin-wrappers/git\" \"$@\"\ndiff --git a/contrib/mw-to-git/git-mw.perl b/contrib/mw-to-git/git-mw.perl\ndeleted file mode 100755\nindex eb52a53d320..00000000000\n--- a/contrib/mw-to-git/git-mw.perl\n+++ /dev/null\n@@ -1,368 +0,0 @@\n-#!/usr/bin/perl\n-\n-# Copyright (C) 2013\n-#     Benoit Person <benoit.person@ensimag.imag.fr>\n-#     Celestin Matte <celestin.matte@ensimag.imag.fr>\n-# License: GPL v2 or later\n-\n-# Set of tools for git repo with a mediawiki remote.\n-# Documentation & bugtracker: https://github.com/Git-Mediawiki/Git-Mediawiki\n-\n-use strict;\n-use warnings;\n-\n-use Getopt::Long;\n-use URI::URL qw(url);\n-use LWP::UserAgent;\n-use HTML::TreeBuilder;\n-\n-use Git;\n-use MediaWiki::API;\n-use Git::Mediawiki qw(clean_filename connect_maybe\n-\t\t\t\t\tEMPTY HTTP_CODE_PAGE_NOT_FOUND);\n-\n-# By default, use UTF-8 to communicate with Git and the user\n-binmode STDERR, ':encoding(UTF-8)';\n-binmode STDOUT, ':encoding(UTF-8)';\n-\n-# Global parameters\n-my $verbose = 0;\n-sub v_print {\n-\tif ($verbose) {\n-\t\treturn print {*STDERR} @_;\n-\t}\n-\treturn;\n-}\n-\n-# Preview parameters\n-my $file_name = EMPTY;\n-my $remote_name = EMPTY;\n-my $preview_file_name = EMPTY;\n-my $autoload = 0;\n-sub file {\n-\t$file_name = shift;\n-\treturn $file_name;\n-}\n-\n-my %commands = (\n-\t'help' =>\n-\t\t[\\&help, {}, \\&help],\n-\t'preview' =>\n-\t\t[\\&preview, {\n-\t\t\t'<>' => \\&file,\n-\t\t\t'output|o=s' => \\$preview_file_name,\n-\t\t\t'remote|r=s' => \\$remote_name,\n-\t\t\t'autoload|a' => \\$autoload\n-\t\t}, \\&preview_help]\n-);\n-\n-# Search for sub-command\n-my $cmd = $commands{'help'};\n-for (0..@ARGV-1) {\n-\tif (defined $commands{$ARGV[$_]}) {\n-\t\t$cmd = $commands{$ARGV[$_]};\n-\t\tsplice @ARGV, $_, 1;\n-\t\tlast;\n-\t}\n-};\n-GetOptions( %{$cmd->[1]},\n-\t'help|h' => \\&{$cmd->[2]},\n-\t'verbose|v'  => \\$verbose);\n-\n-# Launch command\n-&{$cmd->[0]};\n-\n-############################# Preview Functions ################################\n-\n-sub preview_help {\n-\tprint {*STDOUT} <<'END';\n-USAGE: git mw preview [--remote|-r <remote name>] [--autoload|-a]\n-                      [--output|-o <output filename>] [--verbose|-v]\n-                      <blob> | <filename>\n-\n-DESCRIPTION:\n-Preview is an utiliy to preview local content of a mediawiki repo as if it was\n-pushed on the remote.\n-\n-For that, preview searches for the remote name of the current branch's\n-upstream if --remote is not set. If that remote is not found or if it\n-is not a mediawiki, it lists all mediawiki remotes configured and asks\n-you to replay your command with the --remote option set properly.\n-\n-Then, it searches for a file named 'filename'. If it's not found in\n-the current dir, it will assume it's a blob.\n-\n-The content retrieved in the file (or in the blob) will then be parsed\n-by the remote mediawiki and combined with a template retrieved from\n-the mediawiki.\n-\n-Finally, preview will save the HTML result in a file. and autoload it\n-in your default web browser if the option --autoload is present.\n-\n-OPTIONS:\n-    -r <remote name>, --remote <remote name>\n-        If the remote is a mediawiki, the template and the parse engine\n-        used for the preview will be those of that remote.\n-        If not, a list of valid remotes will be shown.\n-\n-    -a, --autoload\n-        Try to load the HTML output in a new tab (or new window) of your\n-        default web browser.\n-\n-    -o <output filename>, --output <output filename>\n-        Change the HTML output filename. Default filename is based on the\n-        input filename with its extension replaced by '.html'.\n-\n-    -v, --verbose\n-        Show more information on what's going on under the hood.\n-END\n-\texit;\n-}\n-\n-sub preview {\n-\tmy $wiki;\n-\tmy ($remote_url, $wiki_page_name);\n-\tmy ($new_content, $template);\n-\tmy $file_content;\n-\n-\tif ($file_name eq EMPTY) {\n-\t\tdie \"Missing file argument, see `git mw help`\\n\";\n-\t}\n-\n-\tv_print(\"### Selecting remote\\n\");\n-\tif ($remote_name eq EMPTY) {\n-\t\t$remote_name = find_upstream_remote_name();\n-\t\tif ($remote_name) {\n-\t\t\t$remote_url = mediawiki_remote_url_maybe($remote_name);\n-\t\t}\n-\n-\t\tif (! $remote_url) {\n-\t\t\tmy @valid_remotes = find_mediawiki_remotes();\n-\n-\t\t\tif ($#valid_remotes == 0) {\n-\t\t\t\tprint {*STDERR} \"No mediawiki remote in this repo. \\n\";\n-\t\t\t\texit 1;\n-\t\t\t} else {\n-\t\t\t\tmy $remotes_list = join(\"\\n\\t\", @valid_remotes);\n-\t\t\t\tprint {*STDERR} <<\"MESSAGE\";\n-There are multiple mediawiki remotes, which of:\n-\t${remotes_list}\n-do you want ? Use the -r option to specify the remote.\n-MESSAGE\n-\t\t\t}\n-\n-\t\t\texit 1;\n-\t\t}\n-\t} else {\n-\t\tif (!is_valid_remote($remote_name)) {\n-\t\t\tdie \"${remote_name} is not a remote\\n\";\n-\t\t}\n-\n-\t\t$remote_url = mediawiki_remote_url_maybe($remote_name);\n-\t\tif (! $remote_url) {\n-\t\t\tdie \"${remote_name} is not a mediawiki remote\\n\";\n-\t\t}\n-\t}\n-\tv_print(\"selected remote:\\n\\tname: ${remote_name}\\n\\turl: ${remote_url}\\n\");\n-\n-\t$wiki = connect_maybe($wiki, $remote_name, $remote_url);\n-\n-\t# Read file content\n-\tif (! -e $file_name) {\n-\t\t$file_content = git_cmd_try {\n-\t\t\tGit::command('cat-file', 'blob', $file_name); }\n-\t\t\t\"%s failed w/ code %d\";\n-\n-\t\tif ($file_name =~ /(.+):(.+)/) {\n-\t\t\t$file_name = $2;\n-\t\t}\n-\t} else {\n-\t\topen my $read_fh, \"<\", $file_name\n-\t\t\tor die \"could not open ${file_name}: $!\\n\";\n-\t\t$file_content = do { local $/ = undef; <$read_fh> };\n-\t\tclose $read_fh\n-\t\t\tor die \"unable to close: $!\\n\";\n-\t}\n-\n-\tv_print(\"### Retrieving template\\n\");\n-\t($wiki_page_name = clean_filename($file_name)) =~ s/\\.[^.]+$//;\n-\t$template = get_template($remote_url, $wiki_page_name);\n-\n-\tv_print(\"### Parsing local content\\n\");\n-\t$new_content = $wiki->api({\n-\t\taction => 'parse',\n-\t\ttext => $file_content,\n-\t\ttitle => $wiki_page_name\n-\t}, {\n-\t\tskip_encoding => 1\n-\t}) or die \"No response from remote mediawiki\\n\";\n-\t$new_content = $new_content->{'parse'}->{'text'}->{'*'};\n-\n-\tv_print(\"### Merging contents\\n\");\n-\tif ($preview_file_name eq EMPTY) {\n-\t\t($preview_file_name = $file_name) =~ s/\\.[^.]+$/.html/;\n-\t}\n-\topen(my $save_fh, '>:encoding(UTF-8)', $preview_file_name)\n-\t\tor die \"Could not open: $!\\n\";\n-\tprint {$save_fh} merge_contents($template, $new_content, $remote_url);\n-\tclose($save_fh)\n-\t\tor die \"Could not close: $!\\n\";\n-\n-\tv_print(\"### Results\\n\");\n-\tif ($autoload) {\n-\t\tv_print(\"Launching browser w/ file: ${preview_file_name}\");\n-\t\tsystem('git', 'web--browse', $preview_file_name);\n-\t} else {\n-\t\tprint {*STDERR} \"Preview file saved as: ${preview_file_name}\\n\";\n-\t}\n-\n-\texit;\n-}\n-\n-# uses global scope variable: $remote_name\n-sub merge_contents {\n-\tmy $template = shift;\n-\tmy $content = shift;\n-\tmy $remote_url = shift;\n-\tmy ($content_tree, $html_tree, $mw_content_text);\n-\tmy $template_content_id = 'bodyContent';\n-\n-\t$html_tree = HTML::TreeBuilder->new;\n-\t$html_tree->parse($template);\n-\n-\t$content_tree = HTML::TreeBuilder->new;\n-\t$content_tree->parse($content);\n-\n-\t$template_content_id = Git::config(\"remote.${remote_name}.mwIDcontent\")\n-\t\t|| $template_content_id;\n-\tv_print(\"Using '${template_content_id}' as the content ID\\n\");\n-\n-\t$mw_content_text = $html_tree->look_down('id', $template_content_id);\n-\tif (!defined $mw_content_text) {\n-\t\tprint {*STDERR} <<\"CONFIG\";\n-Could not combine the new content with the template. You might want to\n-configure `mediawiki.IDContent` in your config:\n-\tgit config --add remote.${remote_name}.mwIDcontent <id>\n-and re-run the command afterward.\n-CONFIG\n-\t\texit 1;\n-\t}\n-\t$mw_content_text->delete_content();\n-\t$mw_content_text->push_content($content_tree);\n-\n-\tmake_links_absolute($html_tree, $remote_url);\n-\n-\treturn $html_tree->as_HTML;\n-}\n-\n-sub make_links_absolute {\n-\tmy $html_tree = shift;\n-\tmy $remote_url = shift;\n-\tfor (@{ $html_tree->extract_links() }) {\n-\t\tmy ($link, $element, $attr) = @{ $_ };\n-\t\tmy $url = url($link)->canonical;\n-\t\tif ($url !~ /#/) {\n-\t\t\t$element->attr($attr, URI->new_abs($url, $remote_url));\n-\t\t}\n-\t}\n-\treturn $html_tree;\n-}\n-\n-sub is_valid_remote {\n-\tmy $remote = shift;\n-\tmy @remotes = git_cmd_try {\n-\t\tGit::command('remote') }\n-\t\t\"%s failed w/ code %d\";\n-\tmy $found_remote = 0;\n-\tforeach my $remote (@remotes) {\n-\t\tif ($remote eq $remote) {\n-\t\t\t$found_remote = 1;\n-\t\t\tlast;\n-\t\t}\n-\t}\n-\treturn $found_remote;\n-}\n-\n-sub find_mediawiki_remotes {\n-\tmy @remotes = git_cmd_try {\n-\t\tGit::command('remote'); }\n-\t\t\"%s failed w/ code %d\";\n-\tmy $remote_url;\n-\tmy @valid_remotes = ();\n-\tforeach my $remote (@remotes) {\n-\t\t$remote_url = mediawiki_remote_url_maybe($remote);\n-\t\tif ($remote_url) {\n-\t\t\tpush(@valid_remotes, $remote);\n-\t\t}\n-\t}\n-\treturn @valid_remotes;\n-}\n-\n-sub find_upstream_remote_name {\n-\tmy $current_branch = git_cmd_try {\n-\t\tGit::command_oneline('symbolic-ref', '--short', 'HEAD') }\n-\t\t\"%s failed w/ code %d\";\n-\treturn Git::config(\"branch.${current_branch}.remote\");\n-}\n-\n-sub mediawiki_remote_url_maybe {\n-\tmy $remote = shift;\n-\n-\t# Find remote url\n-\tmy $remote_url = Git::config(\"remote.${remote}.url\");\n-\tif ($remote_url =~ s/mediawiki::(.*)/$1/) {\n-\t\treturn url($remote_url)->canonical;\n-\t}\n-\n-\treturn;\n-}\n-\n-sub get_template {\n-\tmy $url = shift;\n-\tmy $page_name = shift;\n-\tmy ($req, $res, $code, $url_after);\n-\n-\t$req = LWP::UserAgent->new;\n-\tif ($verbose) {\n-\t\t$req->show_progress(1);\n-\t}\n-\n-\t$res = $req->get(\"${url}/index.php?title=${page_name}\");\n-\tif (!$res->is_success) {\n-\t\t$code = $res->code;\n-\t\t$url_after = $res->request()->uri(); # resolve all redirections\n-\t\tif ($code == HTTP_CODE_PAGE_NOT_FOUND) {\n-\t\t\tif ($verbose) {\n-\t\t\t\tprint {*STDERR} <<\"WARNING\";\n-Warning: Failed to retrieve '$page_name'. Create it on the mediawiki if you want\n-all the links to work properly.\n-Trying to use the mediawiki homepage as a fallback template ...\n-WARNING\n-\t\t\t}\n-\n-\t\t\t# LWP automatically redirects GET request\n-\t\t\t$res = $req->get(\"${url}/index.php\");\n-\t\t\tif (!$res->is_success) {\n-\t\t\t\t$url_after = $res->request()->uri(); # resolve all redirections\n-\t\t\t\tdie \"Failed to get homepage @ ${url_after} w/ code ${code}\\n\";\n-\t\t\t}\n-\t\t} else {\n-\t\t\tdie \"Failed to get '${page_name}' @ ${url_after} w/ code ${code}\\n\";\n-\t\t}\n-\t}\n-\n-\treturn $res->decoded_content;\n-}\n-\n-############################## Help Functions ##################################\n-\n-sub help {\n-\tprint {*STDOUT} <<'END';\n-usage: git mw <command> <args>\n-\n-git mw commands are:\n-    help        Display help information about git mw\n-    preview     Parse and render local file into HTML\n-END\n-\texit;\n-}\ndiff --git a/contrib/mw-to-git/git-remote-mediawiki.perl b/contrib/mw-to-git/git-remote-mediawiki.perl\ndeleted file mode 100755\nindex a5624413dc1..00000000000\n--- a/contrib/mw-to-git/git-remote-mediawiki.perl\n+++ /dev/null\n@@ -1,1390 +0,0 @@\n-#! /usr/bin/perl\n-\n-# Copyright (C) 2011\n-#     Jérémie Nikaes <jeremie.nikaes@ensimag.imag.fr>\n-#     Arnaud Lacurie <arnaud.lacurie@ensimag.imag.fr>\n-#     Claire Fousse <claire.fousse@ensimag.imag.fr>\n-#     David Amouyal <david.amouyal@ensimag.imag.fr>\n-#     Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n-# License: GPL v2 or later\n-\n-# Gateway between Git and MediaWiki.\n-# Documentation & bugtracker: https://github.com/Git-Mediawiki/Git-Mediawiki\n-\n-use strict;\n-use MediaWiki::API;\n-use Git;\n-use Git::Mediawiki qw(clean_filename smudge_filename connect_maybe\n-\t\t\t\t\tEMPTY HTTP_CODE_OK);\n-use DateTime::Format::ISO8601;\n-use warnings;\n-\n-# By default, use UTF-8 to communicate with Git and the user\n-binmode STDERR, ':encoding(UTF-8)';\n-binmode STDOUT, ':encoding(UTF-8)';\n-\n-use URI::Escape;\n-\n-# It's not always possible to delete pages (may require some\n-# privileges). Deleted pages are replaced with this content.\n-use constant DELETED_CONTENT => \"[[Category:Deleted]]\\n\";\n-\n-# It's not possible to create empty pages. New empty files in Git are\n-# sent with this content instead.\n-use constant EMPTY_CONTENT => \"<!-- empty page -->\\n\";\n-\n-# used to reflect file creation or deletion in diff.\n-use constant NULL_SHA1 => '0000000000000000000000000000000000000000';\n-\n-# Used on Git's side to reflect empty edit messages on the wiki\n-use constant EMPTY_MESSAGE => '*Empty MediaWiki Message*';\n-\n-# Number of pages taken into account at once in submodule get_mw_page_list\n-use constant SLICE_SIZE => 50;\n-\n-# Number of linked mediafile to get at once in get_linked_mediafiles\n-# The query is split in small batches because of the MW API limit of\n-# the number of links to be returned (500 links max).\n-use constant BATCH_SIZE => 10;\n-\n-if (@ARGV != 2) {\n-\texit_error_usage();\n-}\n-\n-my $remotename = $ARGV[0];\n-my $url = $ARGV[1];\n-\n-# Accept both space-separated and multiple keys in config file.\n-# Spaces should be written as _ anyway because we'll use chomp.\n-my @tracked_pages = split(/[ \\n]/, run_git_quoted([\"config\", \"--get-all\", \"remote.${remotename}.pages\"]));\n-chomp(@tracked_pages);\n-\n-# Just like @tracked_pages, but for MediaWiki categories.\n-my @tracked_categories = split(/[ \\n]/, run_git_quoted([\"config\", \"--get-all\", \"remote.${remotename}.categories\"]));\n-chomp(@tracked_categories);\n-\n-# Just like @tracked_categories, but for MediaWiki namespaces.\n-my @tracked_namespaces = split(/[ \\n]/, run_git_quoted([\"config\", \"--get-all\", \"remote.${remotename}.namespaces\"]));\n-for (@tracked_namespaces) { s/_/ /g; }\n-chomp(@tracked_namespaces);\n-\n-# Import media files on pull\n-my $import_media = run_git_quoted([\"config\", \"--get\", \"--bool\", \"remote.${remotename}.mediaimport\"]);\n-chomp($import_media);\n-$import_media = ($import_media eq 'true');\n-\n-# Export media files on push\n-my $export_media = run_git_quoted([\"config\", \"--get\", \"--bool\", \"remote.${remotename}.mediaexport\"]);\n-chomp($export_media);\n-$export_media = !($export_media eq 'false');\n-\n-my $wiki_login = run_git_quoted([\"config\", \"--get\", \"remote.${remotename}.mwLogin\"]);\n-# Note: mwPassword is discouraged. Use the credential system instead.\n-my $wiki_passwd = run_git_quoted([\"config\", \"--get\", \"remote.${remotename}.mwPassword\"]);\n-my $wiki_domain = run_git_quoted([\"config\", \"--get\", \"remote.${remotename}.mwDomain\"]);\n-chomp($wiki_login);\n-chomp($wiki_passwd);\n-chomp($wiki_domain);\n-\n-# Import only last revisions (both for clone and fetch)\n-my $shallow_import = run_git_quoted([\"config\", \"--get\", \"--bool\", \"remote.${remotename}.shallow\"]);\n-chomp($shallow_import);\n-$shallow_import = ($shallow_import eq 'true');\n-\n-# Fetch (clone and pull) by revisions instead of by pages. This behavior\n-# is more efficient when we have a wiki with lots of pages and we fetch\n-# the revisions quite often so that they concern only few pages.\n-# Possible values:\n-# - by_rev: perform one query per new revision on the remote wiki\n-# - by_page: query each tracked page for new revision\n-my $fetch_strategy = run_git_quoted([\"config\", \"--get\", \"remote.${remotename}.fetchStrategy\"]);\n-if (!$fetch_strategy) {\n-\t$fetch_strategy = run_git_quoted([\"config\", \"--get\", \"mediawiki.fetchStrategy\"]);\n-}\n-chomp($fetch_strategy);\n-if (!$fetch_strategy) {\n-\t$fetch_strategy = 'by_page';\n-}\n-\n-# Remember the timestamp corresponding to a revision id.\n-my %basetimestamps;\n-\n-# Dumb push: don't update notes and mediawiki ref to reflect the last push.\n-#\n-# Configurable with mediawiki.dumbPush, or per-remote with\n-# remote.<remotename>.dumbPush.\n-#\n-# This means the user will have to re-import the just-pushed\n-# revisions. On the other hand, this means that the Git revisions\n-# corresponding to MediaWiki revisions are all imported from the wiki,\n-# regardless of whether they were initially created in Git or from the\n-# web interface, hence all users will get the same history (i.e. if\n-# the push from Git to MediaWiki loses some information, everybody\n-# will get the history with information lost). If the import is\n-# deterministic, this means everybody gets the same sha1 for each\n-# MediaWiki revision.\n-my $dumb_push = run_git_quoted([\"config\", \"--get\", \"--bool\", \"remote.${remotename}.dumbPush\"]);\n-if (!$dumb_push) {\n-\t$dumb_push = run_git_quoted([\"config\", \"--get\", \"--bool\", \"mediawiki.dumbPush\"]);\n-}\n-chomp($dumb_push);\n-$dumb_push = ($dumb_push eq 'true');\n-\n-my $wiki_name = $url;\n-$wiki_name =~ s{[^/]*://}{};\n-# If URL is like http://user:password@example.com/, we clearly don't\n-# want the password in $wiki_name. While we're there, also remove user\n-# and '@' sign, to avoid author like MWUser@HTTPUser@host.com\n-$wiki_name =~ s/^.*@//;\n-\n-# Commands parser\n-while (<STDIN>) {\n-\tchomp;\n-\n-\tif (!parse_command($_)) {\n-\t\tlast;\n-\t}\n-\n-\tBEGIN { $| = 1 } # flush STDOUT, to make sure the previous\n-\t\t\t # command is fully processed.\n-}\n-\n-########################## Functions ##############################\n-\n-## error handling\n-sub exit_error_usage {\n-\tdie \"ERROR: git-remote-mediawiki module was not called with a correct number of\\n\" .\n-\t    \"parameters\\n\" .\n-\t    \"You may obtain this error because you attempted to run the git-remote-mediawiki\\n\" .\n-            \"module directly.\\n\" .\n-\t    \"This module can be used the following way:\\n\" .\n-\t    \"\\tgit clone mediawiki://<address of a mediawiki>\\n\" .\n-\t    \"Then, use git commit, push and pull as with every normal git repository.\\n\";\n-}\n-\n-sub parse_command {\n-\tmy ($line) = @_;\n-\tmy @cmd = split(/ /, $line);\n-\tif (!defined $cmd[0]) {\n-\t\treturn 0;\n-\t}\n-\tif ($cmd[0] eq 'capabilities') {\n-\t\tdie(\"Too many arguments for capabilities\\n\")\n-\t\t    if (defined($cmd[1]));\n-\t\tmw_capabilities();\n-\t} elsif ($cmd[0] eq 'list') {\n-\t\tdie(\"Too many arguments for list\\n\") if (defined($cmd[2]));\n-\t\tmw_list($cmd[1]);\n-\t} elsif ($cmd[0] eq 'import') {\n-\t\tdie(\"Invalid argument for import\\n\")\n-\t\t    if ($cmd[1] eq EMPTY);\n-\t\tdie(\"Too many arguments for import\\n\")\n-\t\t    if (defined($cmd[2]));\n-\t\tmw_import($cmd[1]);\n-\t} elsif ($cmd[0] eq 'option') {\n-\t\tdie(\"Invalid arguments for option\\n\")\n-\t\t    if ($cmd[1] eq EMPTY || $cmd[2] eq EMPTY);\n-\t\tdie(\"Too many arguments for option\\n\")\n-\t\t    if (defined($cmd[3]));\n-\t\tmw_option($cmd[1],$cmd[2]);\n-\t} elsif ($cmd[0] eq 'push') {\n-\t\tmw_push($cmd[1]);\n-\t} else {\n-\t\tprint {*STDERR} \"Unknown command. Aborting...\\n\";\n-\t\treturn 0;\n-\t}\n-\treturn 1;\n-}\n-\n-# MediaWiki API instance, created lazily.\n-my $mediawiki;\n-\n-sub fatal_mw_error {\n-\tmy $action = shift;\n-\tprint STDERR \"fatal: could not $action.\\n\";\n-\tprint STDERR \"fatal: '$url' does not appear to be a mediawiki\\n\";\n-\tif ($url =~ /^https/) {\n-\t\tprint STDERR \"fatal: make sure '$url/api.php' is a valid page\\n\";\n-\t\tprint STDERR \"fatal: and the SSL certificate is correct.\\n\";\n-\t} else {\n-\t\tprint STDERR \"fatal: make sure '$url/api.php' is a valid page.\\n\";\n-\t}\n-\tprint STDERR \"fatal: (error \" .\n-\t    $mediawiki->{error}->{code} . ': ' .\n-\t    $mediawiki->{error}->{details} . \")\\n\";\n-\texit 1;\n-}\n-\n-## Functions for listing pages on the remote wiki\n-sub get_mw_tracked_pages {\n-\tmy $pages = shift;\n-\tget_mw_page_list(\\@tracked_pages, $pages);\n-\treturn;\n-}\n-\n-sub get_mw_page_list {\n-\tmy $page_list = shift;\n-\tmy $pages = shift;\n-\tmy @some_pages = @{$page_list};\n-\twhile (@some_pages) {\n-\t\tmy $last_page = SLICE_SIZE;\n-\t\tif ($#some_pages < $last_page) {\n-\t\t\t$last_page = $#some_pages;\n-\t\t}\n-\t\tmy @slice = @some_pages[0..$last_page];\n-\t\tget_mw_first_pages(\\@slice, $pages);\n-\t\t@some_pages = @some_pages[(SLICE_SIZE + 1)..$#some_pages];\n-\t}\n-\treturn;\n-}\n-\n-sub get_mw_tracked_categories {\n-\tmy $pages = shift;\n-\tforeach my $category (@tracked_categories) {\n-\t\tif (index($category, ':') < 0) {\n-\t\t\t# Mediawiki requires the Category\n-\t\t\t# prefix, but let's not force the user\n-\t\t\t# to specify it.\n-\t\t\t$category = \"Category:${category}\";\n-\t\t}\n-\t\tmy $mw_pages = $mediawiki->list( {\n-\t\t\taction => 'query',\n-\t\t\tlist => 'categorymembers',\n-\t\t\tcmtitle => $category,\n-\t\t\tcmlimit => 'max' } )\n-\t\t\t|| die $mediawiki->{error}->{code} . ': '\n-\t\t\t\t. $mediawiki->{error}->{details} . \"\\n\";\n-\t\tforeach my $page (@{$mw_pages}) {\n-\t\t\t$pages->{$page->{title}} = $page;\n-\t\t}\n-\t}\n-\treturn;\n-}\n-\n-sub get_mw_tracked_namespaces {\n-    my $pages = shift;\n-    foreach my $local_namespace (sort @tracked_namespaces) {\n-        my $namespace_id;\n-        if ($local_namespace eq \"(Main)\") {\n-            $namespace_id = 0;\n-        } else {\n-            $namespace_id = get_mw_namespace_id($local_namespace);\n-        }\n-        # virtual namespaces don't support allpages\n-        next if !defined($namespace_id) || $namespace_id < 0;\n-        my $mw_pages = $mediawiki->list( {\n-            action => 'query',\n-            list => 'allpages',\n-            apnamespace => $namespace_id,\n-            aplimit => 'max' } )\n-            || die $mediawiki->{error}->{code} . ': '\n-                . $mediawiki->{error}->{details} . \"\\n\";\n-        print {*STDERR} \"$#{$mw_pages} found in namespace $local_namespace ($namespace_id)\\n\";\n-        foreach my $page (@{$mw_pages}) {\n-            $pages->{$page->{title}} = $page;\n-        }\n-    }\n-    return;\n-}\n-\n-sub get_mw_all_pages {\n-\tmy $pages = shift;\n-\t# No user-provided list, get the list of pages from the API.\n-\tmy $mw_pages = $mediawiki->list({\n-\t\taction => 'query',\n-\t\tlist => 'allpages',\n-\t\taplimit => 'max'\n-\t});\n-\tif (!defined($mw_pages)) {\n-\t\tfatal_mw_error(\"get the list of wiki pages\");\n-\t}\n-\tforeach my $page (@{$mw_pages}) {\n-\t\t$pages->{$page->{title}} = $page;\n-\t}\n-\treturn;\n-}\n-\n-# queries the wiki for a set of pages. Meant to be used within a loop\n-# querying the wiki for slices of page list.\n-sub get_mw_first_pages {\n-\tmy $some_pages = shift;\n-\tmy @some_pages = @{$some_pages};\n-\n-\tmy $pages = shift;\n-\n-\t# pattern 'page1|page2|...' required by the API\n-\tmy $titles = join('|', @some_pages);\n-\n-\tmy $mw_pages = $mediawiki->api({\n-\t\taction => 'query',\n-\t\ttitles => $titles,\n-\t});\n-\tif (!defined($mw_pages)) {\n-\t\tfatal_mw_error(\"query the list of wiki pages\");\n-\t}\n-\twhile (my ($id, $page) = each(%{$mw_pages->{query}->{pages}})) {\n-\t\tif ($id < 0) {\n-\t\t\tprint {*STDERR} \"Warning: page $page->{title} not found on wiki\\n\";\n-\t\t} else {\n-\t\t\t$pages->{$page->{title}} = $page;\n-\t\t}\n-\t}\n-\treturn;\n-}\n-\n-# Get the list of pages to be fetched according to configuration.\n-sub get_mw_pages {\n-\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\n-\tprint {*STDERR} \"Listing pages on remote wiki...\\n\";\n-\n-\tmy %pages; # hash on page titles to avoid duplicates\n-\tmy $user_defined;\n-\tif (@tracked_pages) {\n-\t\t$user_defined = 1;\n-\t\t# The user provided a list of pages titles, but we\n-\t\t# still need to query the API to get the page IDs.\n-\t\tget_mw_tracked_pages(\\%pages);\n-\t}\n-\tif (@tracked_categories) {\n-\t\t$user_defined = 1;\n-\t\tget_mw_tracked_categories(\\%pages);\n-\t}\n-\tif (@tracked_namespaces) {\n-\t\t$user_defined = 1;\n-\t\tget_mw_tracked_namespaces(\\%pages);\n-\t}\n-\tif (!$user_defined) {\n-\t\tget_mw_all_pages(\\%pages);\n-\t}\n-\tif ($import_media) {\n-\t\tprint {*STDERR} \"Getting media files for selected pages...\\n\";\n-\t\tif ($user_defined) {\n-\t\t\tget_linked_mediafiles(\\%pages);\n-\t\t} else {\n-\t\t\tget_all_mediafiles(\\%pages);\n-\t\t}\n-\t}\n-\tprint {*STDERR} (scalar keys %pages) . \" pages found.\\n\";\n-\treturn %pages;\n-}\n-\n-# usage: $out = run_git_quoted([\"command\", \"args\", ...]);\n-#        $out = run_git_quoted([\"command\", \"args\", ...], \"raw\"); # don't interpret output as UTF-8.\n-#        $out = run_git_quoted_nostderr([\"command\", \"args\", ...]); # discard stderr\n-#        $out = run_git_quoted_nostderr([\"command\", \"args\", ...], \"raw\"); # ditto but raw instead of UTF-8 as above\n-sub _run_git {\n-\tmy $args = shift;\n-\tmy $encoding = (shift || 'encoding(UTF-8)');\n-\topen(my $git, \"-|:${encoding}\", @$args)\n-\t    or die \"Unable to fork: $!\\n\";\n-\tmy $res = do {\n-\t\tlocal $/ = undef;\n-\t\t<$git>\n-\t};\n-\tclose($git);\n-\n-\treturn $res;\n-}\n-\n-sub run_git_quoted {\n-    _run_git([\"git\", @{$_[0]}], $_[1]);\n-}\n-\n-sub run_git_quoted_nostderr {\n-    _run_git(['sh', '-c', 'git \"$@\" 2>/dev/null', '--', @{$_[0]}], $_[1]);\n-}\n-\n-sub get_all_mediafiles {\n-\tmy $pages = shift;\n-\t# Attach list of all pages for media files from the API,\n-\t# they are in a different namespace, only one namespace\n-\t# can be queried at the same moment\n-\tmy $mw_pages = $mediawiki->list({\n-\t\taction => 'query',\n-\t\tlist => 'allpages',\n-\t\tapnamespace => get_mw_namespace_id('File'),\n-\t\taplimit => 'max'\n-\t});\n-\tif (!defined($mw_pages)) {\n-\t\tprint {*STDERR} \"fatal: could not get the list of pages for media files.\\n\";\n-\t\tprint {*STDERR} \"fatal: '$url' does not appear to be a mediawiki\\n\";\n-\t\tprint {*STDERR} \"fatal: make sure '$url/api.php' is a valid page.\\n\";\n-\t\texit 1;\n-\t}\n-\tforeach my $page (@{$mw_pages}) {\n-\t\t$pages->{$page->{title}} = $page;\n-\t}\n-\treturn;\n-}\n-\n-sub get_linked_mediafiles {\n-\tmy $pages = shift;\n-\tmy @titles = map { $_->{title} } values(%{$pages});\n-\n-\tmy $batch = BATCH_SIZE;\n-\twhile (@titles) {\n-\t\tif ($#titles < $batch) {\n-\t\t\t$batch = $#titles;\n-\t\t}\n-\t\tmy @slice = @titles[0..$batch];\n-\n-\t\t# pattern 'page1|page2|...' required by the API\n-\t\tmy $mw_titles = join('|', @slice);\n-\n-\t\t# Media files could be included or linked from\n-\t\t# a page, get all related\n-\t\tmy $query = {\n-\t\t\taction => 'query',\n-\t\t\tprop => 'links|images',\n-\t\t\ttitles => $mw_titles,\n-\t\t\tplnamespace => get_mw_namespace_id('File'),\n-\t\t\tpllimit => 'max'\n-\t\t};\n-\t\tmy $result = $mediawiki->api($query);\n-\n-\t\twhile (my ($id, $page) = each(%{$result->{query}->{pages}})) {\n-\t\t\tmy @media_titles;\n-\t\t\tif (defined($page->{links})) {\n-\t\t\t\tmy @link_titles\n-\t\t\t\t    = map { $_->{title} } @{$page->{links}};\n-\t\t\t\tpush(@media_titles, @link_titles);\n-\t\t\t}\n-\t\t\tif (defined($page->{images})) {\n-\t\t\t\tmy @image_titles\n-\t\t\t\t    = map { $_->{title} } @{$page->{images}};\n-\t\t\t\tpush(@media_titles, @image_titles);\n-\t\t\t}\n-\t\t\tif (@media_titles) {\n-\t\t\t\tget_mw_page_list(\\@media_titles, $pages);\n-\t\t\t}\n-\t\t}\n-\n-\t\t@titles = @titles[($batch+1)..$#titles];\n-\t}\n-\treturn;\n-}\n-\n-sub get_mw_mediafile_for_page_revision {\n-\t# Name of the file on Wiki, with the prefix.\n-\tmy $filename = shift;\n-\tmy $timestamp = shift;\n-\tmy %mediafile;\n-\n-\t# Search if on a media file with given timestamp exists on\n-\t# MediaWiki. In that case download the file.\n-\tmy $query = {\n-\t\taction => 'query',\n-\t\tprop => 'imageinfo',\n-\t\ttitles => \"File:${filename}\",\n-\t\tiistart => $timestamp,\n-\t\tiiend => $timestamp,\n-\t\tiiprop => 'timestamp|archivename|url',\n-\t\tiilimit => 1\n-\t};\n-\tmy $result = $mediawiki->api($query);\n-\n-\tmy ($fileid, $file) = each( %{$result->{query}->{pages}} );\n-\t# If not defined it means there is no revision of the file for\n-\t# given timestamp.\n-\tif (defined($file->{imageinfo})) {\n-\t\t$mediafile{title} = $filename;\n-\n-\t\tmy $fileinfo = pop(@{$file->{imageinfo}});\n-\t\t$mediafile{timestamp} = $fileinfo->{timestamp};\n-\t\t# Mediawiki::API's download function doesn't support https URLs\n-\t\t# and can't download old versions of files.\n-\t\tprint {*STDERR} \"\\tDownloading file $mediafile{title}, version $mediafile{timestamp}\\n\";\n-\t\t$mediafile{content} = download_mw_mediafile($fileinfo->{url});\n-\t}\n-\treturn %mediafile;\n-}\n-\n-sub download_mw_mediafile {\n-\tmy $download_url = shift;\n-\n-\tmy $response = $mediawiki->{ua}->get($download_url);\n-\tif ($response->code == HTTP_CODE_OK) {\n-\t\t# It is tempting to return\n-\t\t# $response->decoded_content({charset => \"none\"}), but\n-\t\t# when doing so, utf8::downgrade($content) fails with\n-\t\t# \"Wide character in subroutine entry\".\n-\t\t$response->decode();\n-\t\treturn $response->content();\n-\t} else {\n-\t\tprint {*STDERR} \"Error downloading mediafile from :\\n\";\n-\t\tprint {*STDERR} \"URL: ${download_url}\\n\";\n-\t\tprint {*STDERR} 'Server response: ' . $response->code . q{ } . $response->message . \"\\n\";\n-\t\texit 1;\n-\t}\n-}\n-\n-sub get_last_local_revision {\n-\t# Get note regarding last mediawiki revision.\n-\tmy $note = run_git_quoted_nostderr([\"notes\", \"--ref=${remotename}/mediawiki\",\n-\t\t\t\t\t    \"show\", \"refs/mediawiki/${remotename}/master\"]);\n-\tmy @note_info = split(/ /, $note);\n-\n-\tmy $lastrevision_number;\n-\tif (!(defined($note_info[0]) && $note_info[0] eq 'mediawiki_revision:')) {\n-\t\tprint {*STDERR} 'No previous mediawiki revision found';\n-\t\t$lastrevision_number = 0;\n-\t} else {\n-\t\t# Notes are formatted : mediawiki_revision: #number\n-\t\t$lastrevision_number = $note_info[1];\n-\t\tchomp($lastrevision_number);\n-\t\tprint {*STDERR} \"Last local mediawiki revision found is ${lastrevision_number}\";\n-\t}\n-\treturn $lastrevision_number;\n-}\n-\n-# Get the last remote revision without taking in account which pages are\n-# tracked or not. This function makes a single request to the wiki thus\n-# avoid a loop onto all tracked pages. This is useful for the fetch-by-rev\n-# option.\n-sub get_last_global_remote_rev {\n-\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\n-\tmy $query = {\n-\t\taction => 'query',\n-\t\tlist => 'recentchanges',\n-\t\tprop => 'revisions',\n-\t\trclimit => '1',\n-\t\trcdir => 'older',\n-\t};\n-\tmy $result = $mediawiki->api($query);\n-\treturn $result->{query}->{recentchanges}[0]->{revid};\n-}\n-\n-# Get the last remote revision concerning the tracked pages and the tracked\n-# categories.\n-sub get_last_remote_revision {\n-\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\n-\tmy %pages_hash = get_mw_pages();\n-\tmy @pages = values(%pages_hash);\n-\n-\tmy $max_rev_num = 0;\n-\n-\tprint {*STDERR} \"Getting last revision id on tracked pages...\\n\";\n-\n-\tforeach my $page (@pages) {\n-\t\tmy $id = $page->{pageid};\n-\n-\t\tmy $query = {\n-\t\t\taction => 'query',\n-\t\t\tprop => 'revisions',\n-\t\t\trvprop => 'ids|timestamp',\n-\t\t\tpageids => $id,\n-\t\t};\n-\n-\t\tmy $result = $mediawiki->api($query);\n-\n-\t\tmy $lastrev = pop(@{$result->{query}->{pages}->{$id}->{revisions}});\n-\n-\t\t$basetimestamps{$lastrev->{revid}} = $lastrev->{timestamp};\n-\n-\t\t$max_rev_num = ($lastrev->{revid} > $max_rev_num ? $lastrev->{revid} : $max_rev_num);\n-\t}\n-\n-\tprint {*STDERR} \"Last remote revision found is $max_rev_num.\\n\";\n-\treturn $max_rev_num;\n-}\n-\n-# Clean content before sending it to MediaWiki\n-sub mediawiki_clean {\n-\tmy $string = shift;\n-\tmy $page_created = shift;\n-\t# Mediawiki does not allow blank space at the end of a page and ends with a single \\n.\n-\t# This function right trims a string and adds a \\n at the end to follow this rule\n-\t$string =~ s/\\s+$//;\n-\tif ($string eq EMPTY && $page_created) {\n-\t\t# Creating empty pages is forbidden.\n-\t\t$string = EMPTY_CONTENT;\n-\t}\n-\treturn $string.\"\\n\";\n-}\n-\n-# Filter applied on MediaWiki data before adding them to Git\n-sub mediawiki_smudge {\n-\tmy $string = shift;\n-\tif ($string eq EMPTY_CONTENT) {\n-\t\t$string = EMPTY;\n-\t}\n-\t# This \\n is important. This is due to mediawiki's way to handle end of files.\n-\treturn \"${string}\\n\";\n-}\n-\n-sub literal_data {\n-\tmy ($content) = @_;\n-\tprint {*STDOUT} 'data ', bytes::length($content), \"\\n\", $content;\n-\treturn;\n-}\n-\n-sub literal_data_raw {\n-\t# Output possibly binary content.\n-\tmy ($content) = @_;\n-\t# Avoid confusion between size in bytes and in characters\n-\tutf8::downgrade($content);\n-\tbinmode STDOUT, ':raw';\n-\tprint {*STDOUT} 'data ', bytes::length($content), \"\\n\", $content;\n-\tbinmode STDOUT, ':encoding(UTF-8)';\n-\treturn;\n-}\n-\n-sub mw_capabilities {\n-\t# Revisions are imported to the private namespace\n-\t# refs/mediawiki/$remotename/ by the helper and fetched into\n-\t# refs/remotes/$remotename later by fetch.\n-\tprint {*STDOUT} \"refspec refs/heads/*:refs/mediawiki/${remotename}/*\\n\";\n-\tprint {*STDOUT} \"import\\n\";\n-\tprint {*STDOUT} \"list\\n\";\n-\tprint {*STDOUT} \"push\\n\";\n-\tif ($dumb_push) {\n-\t\tprint {*STDOUT} \"no-private-update\\n\";\n-\t}\n-\tprint {*STDOUT} \"\\n\";\n-\treturn;\n-}\n-\n-sub mw_list {\n-\t# MediaWiki do not have branches, we consider one branch arbitrarily\n-\t# called master, and HEAD pointing to it.\n-\tprint {*STDOUT} \"? refs/heads/master\\n\";\n-\tprint {*STDOUT} \"\\@refs/heads/master HEAD\\n\";\n-\tprint {*STDOUT} \"\\n\";\n-\treturn;\n-}\n-\n-sub mw_option {\n-\tprint {*STDERR} \"remote-helper command 'option $_[0]' not yet implemented\\n\";\n-\tprint {*STDOUT} \"unsupported\\n\";\n-\treturn;\n-}\n-\n-sub fetch_mw_revisions_for_page {\n-\tmy $page = shift;\n-\tmy $id = shift;\n-\tmy $fetch_from = shift;\n-\tmy @page_revs = ();\n-\tmy $query = {\n-\t\taction => 'query',\n-\t\tprop => 'revisions',\n-\t\trvprop => 'ids',\n-\t\trvdir => 'newer',\n-\t\trvstartid => $fetch_from,\n-\t\trvlimit => 500,\n-\t\tpageids => $id,\n-\n-\t\t# Let MediaWiki know that we support the latest API.\n-\t\tcontinue => '',\n-\t};\n-\n-\tmy $revnum = 0;\n-\t# Get 500 revisions at a time due to the mediawiki api limit\n-\twhile (1) {\n-\t\tmy $result = $mediawiki->api($query);\n-\n-\t\t# Parse each of those 500 revisions\n-\t\tforeach my $revision (@{$result->{query}->{pages}->{$id}->{revisions}}) {\n-\t\t\tmy $page_rev_ids;\n-\t\t\t$page_rev_ids->{pageid} = $page->{pageid};\n-\t\t\t$page_rev_ids->{revid} = $revision->{revid};\n-\t\t\tpush(@page_revs, $page_rev_ids);\n-\t\t\t$revnum++;\n-\t\t}\n-\n-\t\tif ($result->{'query-continue'}) { # For legacy APIs\n-\t\t\t$query->{rvstartid} = $result->{'query-continue'}->{revisions}->{rvstartid};\n-\t\t} elsif ($result->{continue}) { # For newer APIs\n-\t\t\t$query->{rvstartid} = $result->{continue}->{rvcontinue};\n-\t\t\t$query->{continue} = $result->{continue}->{continue};\n-\t\t} else {\n-\t\t\tlast;\n-\t\t}\n-\t}\n-\tif ($shallow_import && @page_revs) {\n-\t\tprint {*STDERR} \"  Found 1 revision (shallow import).\\n\";\n-\t\t@page_revs = sort {$b->{revid} <=> $a->{revid}} (@page_revs);\n-\t\treturn $page_revs[0];\n-\t}\n-\tprint {*STDERR} \"  Found ${revnum} revision(s).\\n\";\n-\treturn @page_revs;\n-}\n-\n-sub fetch_mw_revisions {\n-\tmy $pages = shift; my @pages = @{$pages};\n-\tmy $fetch_from = shift;\n-\n-\tmy @revisions = ();\n-\tmy $n = 1;\n-\tforeach my $page (@pages) {\n-\t\tmy $id = $page->{pageid};\n-\t\tprint {*STDERR} \"page ${n}/\", scalar(@pages), ': ', $page->{title}, \"\\n\";\n-\t\t$n++;\n-\t\tmy @page_revs = fetch_mw_revisions_for_page($page, $id, $fetch_from);\n-\t\t@revisions = (@page_revs, @revisions);\n-\t}\n-\n-\treturn ($n, @revisions);\n-}\n-\n-sub fe_escape_path {\n-    my $path = shift;\n-    $path =~ s/\\\\/\\\\\\\\/g;\n-    $path =~ s/\"/\\\\\"/g;\n-    $path =~ s/\\n/\\\\n/g;\n-    return qq(\"${path}\");\n-}\n-\n-sub import_file_revision {\n-\tmy $commit = shift;\n-\tmy %commit = %{$commit};\n-\tmy $full_import = shift;\n-\tmy $n = shift;\n-\tmy $mediafile = shift;\n-\tmy %mediafile;\n-\tif ($mediafile) {\n-\t\t%mediafile = %{$mediafile};\n-\t}\n-\n-\tmy $title = $commit{title};\n-\tmy $comment = $commit{comment};\n-\tmy $content = $commit{content};\n-\tmy $author = $commit{author};\n-\tmy $date = $commit{date};\n-\n-\tprint {*STDOUT} \"commit refs/mediawiki/${remotename}/master\\n\";\n-\tprint {*STDOUT} \"mark :${n}\\n\";\n-\tprint {*STDOUT} \"committer ${author} <${author}\\@${wiki_name}> \" . $date->epoch . \" +0000\\n\";\n-\tliteral_data($comment);\n-\n-\t# If it's not a clone, we need to know where to start from\n-\tif (!$full_import && $n == 1) {\n-\t\tprint {*STDOUT} \"from refs/mediawiki/${remotename}/master^0\\n\";\n-\t}\n-\tif ($content ne DELETED_CONTENT) {\n-\t\tprint {*STDOUT} 'M 644 inline ' .\n-\t\t    fe_escape_path(\"${title}.mw\") . \"\\n\";\n-\t\tliteral_data($content);\n-\t\tif (%mediafile) {\n-\t\t\tprint {*STDOUT} 'M 644 inline '\n-\t\t\t    . fe_escape_path($mediafile{title}) . \"\\n\";\n-\t\t\tliteral_data_raw($mediafile{content});\n-\t\t}\n-\t\tprint {*STDOUT} \"\\n\\n\";\n-\t} else {\n-\t\tprint {*STDOUT} 'D ' . fe_escape_path(\"${title}.mw\") . \"\\n\";\n-\t}\n-\n-\t# mediawiki revision number in the git note\n-\tif ($full_import && $n == 1) {\n-\t\tprint {*STDOUT} \"reset refs/notes/${remotename}/mediawiki\\n\";\n-\t}\n-\tprint {*STDOUT} \"commit refs/notes/${remotename}/mediawiki\\n\";\n-\tprint {*STDOUT} \"committer ${author} <${author}\\@${wiki_name}> \" . $date->epoch . \" +0000\\n\";\n-\tliteral_data('Note added by git-mediawiki during import');\n-\tif (!$full_import && $n == 1) {\n-\t\tprint {*STDOUT} \"from refs/notes/${remotename}/mediawiki^0\\n\";\n-\t}\n-\tprint {*STDOUT} \"N inline :${n}\\n\";\n-\tliteral_data(\"mediawiki_revision: $commit{mw_revision}\");\n-\tprint {*STDOUT} \"\\n\\n\";\n-\treturn;\n-}\n-\n-# parse a sequence of\n-# <cmd> <arg1>\n-# <cmd> <arg2>\n-# \\n\n-# (like batch sequence of import and sequence of push statements)\n-sub get_more_refs {\n-\tmy $cmd = shift;\n-\tmy @refs;\n-\twhile (1) {\n-\t\tmy $line = <STDIN>;\n-\t\tif ($line =~ /^$cmd (.*)$/) {\n-\t\t\tpush(@refs, $1);\n-\t\t} elsif ($line eq \"\\n\") {\n-\t\t\treturn @refs;\n-\t\t} else {\n-\t\t\tdie(\"Invalid command in a '$cmd' batch: $_\\n\");\n-\t\t}\n-\t}\n-\treturn;\n-}\n-\n-sub mw_import {\n-\t# multiple import commands can follow each other.\n-\tmy @refs = (shift, get_more_refs('import'));\n-\tmy $processedRefs;\n-\tforeach my $ref (@refs) {\n-\t\tnext if $processedRefs->{$ref}; # skip duplicates: \"import refs/heads/master\" being issued twice; TODO: why?\n-\t\t$processedRefs->{$ref} = 1;\n-\t\tmw_import_ref($ref);\n-\t}\n-\tprint {*STDOUT} \"done\\n\";\n-\treturn;\n-}\n-\n-sub mw_import_ref {\n-\tmy $ref = shift;\n-\t# The remote helper will call \"import HEAD\" and\n-\t# \"import refs/heads/master\".\n-\t# Since HEAD is a symbolic ref to master (by convention,\n-\t# followed by the output of the command \"list\" that we gave),\n-\t# we don't need to do anything in this case.\n-\tif ($ref eq 'HEAD') {\n-\t\treturn;\n-\t}\n-\n-\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\n-\tprint {*STDERR} \"Searching revisions...\\n\";\n-\tmy $last_local = get_last_local_revision();\n-\tmy $fetch_from = $last_local + 1;\n-\tif ($fetch_from == 1) {\n-\t\tprint {*STDERR} \", fetching from beginning.\\n\";\n-\t} else {\n-\t\tprint {*STDERR} \", fetching from here.\\n\";\n-\t}\n-\n-\tmy $n = 0;\n-\tif ($fetch_strategy eq 'by_rev') {\n-\t\tprint {*STDERR} \"Fetching & writing export data by revs...\\n\";\n-\t\t$n = mw_import_ref_by_revs($fetch_from);\n-\t} elsif ($fetch_strategy eq 'by_page') {\n-\t\tprint {*STDERR} \"Fetching & writing export data by pages...\\n\";\n-\t\t$n = mw_import_ref_by_pages($fetch_from);\n-\t} else {\n-\t\tprint {*STDERR} qq(fatal: invalid fetch strategy \"${fetch_strategy}\".\\n);\n-\t\tprint {*STDERR} \"Check your configuration variables remote.${remotename}.fetchStrategy and mediawiki.fetchStrategy\\n\";\n-\t\texit 1;\n-\t}\n-\n-\tif ($fetch_from == 1 && $n == 0) {\n-\t\tprint {*STDERR} \"You appear to have cloned an empty MediaWiki.\\n\";\n-\t\t# Something has to be done remote-helper side. If nothing is done, an error is\n-\t\t# thrown saying that HEAD is referring to unknown object 0000000000000000000\n-\t\t# and the clone fails.\n-\t}\n-\treturn;\n-}\n-\n-sub mw_import_ref_by_pages {\n-\n-\tmy $fetch_from = shift;\n-\tmy %pages_hash = get_mw_pages();\n-\tmy @pages = values(%pages_hash);\n-\n-\tmy ($n, @revisions) = fetch_mw_revisions(\\@pages, $fetch_from);\n-\n-\t@revisions = sort {$a->{revid} <=> $b->{revid}} @revisions;\n-\tmy @revision_ids = map { $_->{revid} } @revisions;\n-\n-\treturn mw_import_revids($fetch_from, \\@revision_ids, \\%pages_hash);\n-}\n-\n-sub mw_import_ref_by_revs {\n-\n-\tmy $fetch_from = shift;\n-\tmy %pages_hash = get_mw_pages();\n-\n-\tmy $last_remote = get_last_global_remote_rev();\n-\tmy @revision_ids = $fetch_from..$last_remote;\n-\treturn mw_import_revids($fetch_from, \\@revision_ids, \\%pages_hash);\n-}\n-\n-# Import revisions given in second argument (array of integers).\n-# Only pages appearing in the third argument (hash indexed by page titles)\n-# will be imported.\n-sub mw_import_revids {\n-\tmy $fetch_from = shift;\n-\tmy $revision_ids = shift;\n-\tmy $pages = shift;\n-\n-\tmy $n = 0;\n-\tmy $n_actual = 0;\n-\tmy $last_timestamp = 0; # Placeholder in case $rev->timestamp is undefined\n-\n-\tforeach my $pagerevid (@{$revision_ids}) {\n-\t        # Count page even if we skip it, since we display\n-\t\t# $n/$total and $total includes skipped pages.\n-\t\t$n++;\n-\n-\t\t# fetch the content of the pages\n-\t\tmy $query = {\n-\t\t\taction => 'query',\n-\t\t\tprop => 'revisions',\n-\t\t\trvprop => 'content|timestamp|comment|user|ids',\n-\t\t\trevids => $pagerevid,\n-\t\t};\n-\n-\t\tmy $result = $mediawiki->api($query);\n-\n-\t\tif (!$result) {\n-\t\t\tdie \"Failed to retrieve modified page for revision $pagerevid\\n\";\n-\t\t}\n-\n-\t\tif (defined($result->{query}->{badrevids}->{$pagerevid})) {\n-\t\t\t# The revision id does not exist on the remote wiki.\n-\t\t\tnext;\n-\t\t}\n-\n-\t\tif (!defined($result->{query}->{pages})) {\n-\t\t\tdie \"Invalid revision ${pagerevid}.\\n\";\n-\t\t}\n-\n-\t\tmy @result_pages = values(%{$result->{query}->{pages}});\n-\t\tmy $result_page = $result_pages[0];\n-\t\tmy $rev = $result_pages[0]->{revisions}->[0];\n-\n-\t\tmy $page_title = $result_page->{title};\n-\n-\t\tif (!exists($pages->{$page_title})) {\n-\t\t\tprint {*STDERR} \"${n}/\", scalar(@{$revision_ids}),\n-\t\t\t\t\": Skipping revision #$rev->{revid} of ${page_title}\\n\";\n-\t\t\tnext;\n-\t\t}\n-\n-\t\t$n_actual++;\n-\n-\t\tmy %commit;\n-\t\t$commit{author} = $rev->{user} || 'Anonymous';\n-\t\t$commit{comment} = $rev->{comment} || EMPTY_MESSAGE;\n-\t\t$commit{title} = smudge_filename($page_title);\n-\t\t$commit{mw_revision} = $rev->{revid};\n-\t\t$commit{content} = mediawiki_smudge($rev->{'*'});\n-\n-\t\tif (!defined($rev->{timestamp})) {\n-\t\t\t$last_timestamp++;\n-\t\t} else {\n-\t\t\t$last_timestamp = $rev->{timestamp};\n-\t\t}\n-\t\t$commit{date} = DateTime::Format::ISO8601->parse_datetime($last_timestamp);\n-\n-\t\t# Differentiates classic pages and media files.\n-\t\tmy ($namespace, $filename) = $page_title =~ /^([^:]*):(.*)$/;\n-\t\tmy %mediafile;\n-\t\tif ($namespace) {\n-\t\t\tmy $id = get_mw_namespace_id($namespace);\n-\t\t\tif ($id && $id == get_mw_namespace_id('File')) {\n-\t\t\t\t%mediafile = get_mw_mediafile_for_page_revision($filename, $rev->{timestamp});\n-\t\t\t}\n-\t\t}\n-\t\t# If this is a revision of the media page for new version\n-\t\t# of a file do one common commit for both file and media page.\n-\t\t# Else do commit only for that page.\n-\t\tprint {*STDERR} \"${n}/\", scalar(@{$revision_ids}), \": Revision #$rev->{revid} of $commit{title}\\n\";\n-\t\timport_file_revision(\\%commit, ($fetch_from == 1), $n_actual, \\%mediafile);\n-\t}\n-\n-\treturn $n_actual;\n-}\n-\n-sub error_non_fast_forward {\n-\tmy $advice = run_git_quoted([\"config\", \"--bool\", \"advice.pushNonFastForward\"]);\n-\tchomp($advice);\n-\tif ($advice ne 'false') {\n-\t\t# Native git-push would show this after the summary.\n-\t\t# We can't ask it to display it cleanly, so print it\n-\t\t# ourselves before.\n-\t\tprint {*STDERR} \"To prevent you from losing history, non-fast-forward updates were rejected\\n\";\n-\t\tprint {*STDERR} \"Merge the remote changes (e.g. 'git pull') before pushing again. See the\\n\";\n-\t\tprint {*STDERR} \"'Note about fast-forwards' section of 'git push --help' for details.\\n\";\n-\t}\n-\tprint {*STDOUT} qq(error $_[0] \"non-fast-forward\"\\n);\n-\treturn 0;\n-}\n-\n-sub mw_upload_file {\n-\tmy $complete_file_name = shift;\n-\tmy $new_sha1 = shift;\n-\tmy $extension = shift;\n-\tmy $file_deleted = shift;\n-\tmy $summary = shift;\n-\tmy $newrevid;\n-\tmy $path = \"File:${complete_file_name}\";\n-\tmy %hashFiles = get_allowed_file_extensions();\n-\tif (!exists($hashFiles{$extension})) {\n-\t\tprint {*STDERR} \"${complete_file_name} is not a permitted file on this wiki.\\n\";\n-\t\tprint {*STDERR} \"Check the configuration of file uploads in your mediawiki.\\n\";\n-\t\treturn $newrevid;\n-\t}\n-\t# Deleting and uploading a file requires a privileged user\n-\tif ($file_deleted) {\n-\t\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\t\tmy $query = {\n-\t\t\taction => 'delete',\n-\t\t\ttitle => $path,\n-\t\t\treason => $summary\n-\t\t};\n-\t\tif (!$mediawiki->edit($query)) {\n-\t\t\tprint {*STDERR} \"Failed to delete file on remote wiki\\n\";\n-\t\t\tprint {*STDERR} \"Check your permissions on the remote site. Error code:\\n\";\n-\t\t\tprint {*STDERR} $mediawiki->{error}->{code} . ':' . $mediawiki->{error}->{details};\n-\t\t\texit 1;\n-\t\t}\n-\t} else {\n-\t\t# Don't let perl try to interpret file content as UTF-8 => use \"raw\"\n-\t\tmy $content = run_git_quoted([\"cat-file\", \"blob\", $new_sha1], 'raw');\n-\t\tif ($content ne EMPTY) {\n-\t\t\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\t\t\t$mediawiki->{config}->{upload_url} =\n-\t\t\t\t\"${url}/index.php/Special:Upload\";\n-\t\t\t$mediawiki->edit({\n-\t\t\t\taction => 'upload',\n-\t\t\t\tfilename => $complete_file_name,\n-\t\t\t\tcomment => $summary,\n-\t\t\t\tfile => [undef,\n-\t\t\t\t\t $complete_file_name,\n-\t\t\t\t\t Content => $content],\n-\t\t\t\tignorewarnings => 1,\n-\t\t\t}, {\n-\t\t\t\tskip_encoding => 1\n-\t\t\t} ) || die $mediawiki->{error}->{code} . ':'\n-\t\t\t\t . $mediawiki->{error}->{details} . \"\\n\";\n-\t\t\tmy $last_file_page = $mediawiki->get_page({title => $path});\n-\t\t\t$newrevid = $last_file_page->{revid};\n-\t\t\tprint {*STDERR} \"Pushed file: ${new_sha1} - ${complete_file_name}.\\n\";\n-\t\t} else {\n-\t\t\tprint {*STDERR} \"Empty file ${complete_file_name} not pushed.\\n\";\n-\t\t}\n-\t}\n-\treturn $newrevid;\n-}\n-\n-sub mw_push_file {\n-\tmy $diff_info = shift;\n-\t# $diff_info contains a string in this format:\n-\t# 100644 100644 <sha1_of_blob_before_commit> <sha1_of_blob_now> <status>\n-\tmy @diff_info_split = split(/[ \\t]/, $diff_info);\n-\n-\t# Filename, including .mw extension\n-\tmy $complete_file_name = shift;\n-\t# Commit message\n-\tmy $summary = shift;\n-\t# MediaWiki revision number. Keep the previous one by default,\n-\t# in case there's no edit to perform.\n-\tmy $oldrevid = shift;\n-\tmy $newrevid;\n-\n-\tif ($summary eq EMPTY_MESSAGE) {\n-\t\t$summary = EMPTY;\n-\t}\n-\n-\tmy $new_sha1 = $diff_info_split[3];\n-\tmy $old_sha1 = $diff_info_split[2];\n-\tmy $page_created = ($old_sha1 eq NULL_SHA1);\n-\tmy $page_deleted = ($new_sha1 eq NULL_SHA1);\n-\t$complete_file_name = clean_filename($complete_file_name);\n-\n-\tmy ($title, $extension) = $complete_file_name =~ /^(.*)\\.([^\\.]*)$/;\n-\tif (!defined($extension)) {\n-\t\t$extension = EMPTY;\n-\t}\n-\tif ($extension eq 'mw') {\n-\t\tmy $ns = get_mw_namespace_id_for_page($complete_file_name);\n-\t\tif ($ns && $ns == get_mw_namespace_id('File') && (!$export_media)) {\n-\t\t\tprint {*STDERR} \"Ignoring media file related page: ${complete_file_name}\\n\";\n-\t\t\treturn ($oldrevid, 'ok');\n-\t\t}\n-\t\tmy $file_content;\n-\t\tif ($page_deleted) {\n-\t\t\t# Deleting a page usually requires\n-\t\t\t# special privileges. A common\n-\t\t\t# convention is to replace the page\n-\t\t\t# with this content instead:\n-\t\t\t$file_content = DELETED_CONTENT;\n-\t\t} else {\n-\t\t\t$file_content = run_git_quoted([\"cat-file\", \"blob\", $new_sha1]);\n-\t\t}\n-\n-\t\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\n-\t\tmy $result = $mediawiki->edit( {\n-\t\t\taction => 'edit',\n-\t\t\tsummary => $summary,\n-\t\t\ttitle => $title,\n-\t\t\tbasetimestamp => $basetimestamps{$oldrevid},\n-\t\t\ttext => mediawiki_clean($file_content, $page_created),\n-\t\t\t\t  }, {\n-\t\t\t\t\t  skip_encoding => 1 # Helps with names with accentuated characters\n-\t\t\t\t  });\n-\t\tif (!$result) {\n-\t\t\tif ($mediawiki->{error}->{code} == 3) {\n-\t\t\t\t# edit conflicts, considered as non-fast-forward\n-\t\t\t\tprint {*STDERR} 'Warning: Error ' .\n-\t\t\t\t    $mediawiki->{error}->{code} .\n-\t\t\t\t    ' from mediawiki: ' . $mediawiki->{error}->{details} .\n-\t\t\t\t    \".\\n\";\n-\t\t\t\treturn ($oldrevid, 'non-fast-forward');\n-\t\t\t} else {\n-\t\t\t\t# Other errors. Shouldn't happen => just die()\n-\t\t\t\tdie 'Fatal: Error ' .\n-\t\t\t\t    $mediawiki->{error}->{code} .\n-\t\t\t\t    ' from mediawiki: ' . $mediawiki->{error}->{details} . \"\\n\";\n-\t\t\t}\n-\t\t}\n-\t\t$newrevid = $result->{edit}->{newrevid};\n-\t\tprint {*STDERR} \"Pushed file: ${new_sha1} - ${title}\\n\";\n-\t} elsif ($export_media) {\n-\t\t$newrevid = mw_upload_file($complete_file_name, $new_sha1,\n-\t\t\t\t\t   $extension, $page_deleted,\n-\t\t\t\t\t   $summary);\n-\t} else {\n-\t\tprint {*STDERR} \"Ignoring media file ${title}\\n\";\n-\t}\n-\t$newrevid = ($newrevid or $oldrevid);\n-\treturn ($newrevid, 'ok');\n-}\n-\n-sub mw_push {\n-\t# multiple push statements can follow each other\n-\tmy @refsspecs = (shift, get_more_refs('push'));\n-\tmy $pushed;\n-\tfor my $refspec (@refsspecs) {\n-\t\tmy ($force, $local, $remote) = $refspec =~ /^(\\+)?([^:]*):([^:]*)$/\n-\t\t    or die(\"Invalid refspec for push. Expected <src>:<dst> or +<src>:<dst>\\n\");\n-\t\tif ($force) {\n-\t\t\tprint {*STDERR} \"Warning: forced push not allowed on a MediaWiki.\\n\";\n-\t\t}\n-\t\tif ($local eq EMPTY) {\n-\t\t\tprint {*STDERR} \"Cannot delete remote branch on a MediaWiki\\n\";\n-\t\t\tprint {*STDOUT} \"error ${remote} cannot delete\\n\";\n-\t\t\tnext;\n-\t\t}\n-\t\tif ($remote ne 'refs/heads/master') {\n-\t\t\tprint {*STDERR} \"Only push to the branch 'master' is supported on a MediaWiki\\n\";\n-\t\t\tprint {*STDOUT} \"error ${remote} only master allowed\\n\";\n-\t\t\tnext;\n-\t\t}\n-\t\tif (mw_push_revision($local, $remote)) {\n-\t\t\t$pushed = 1;\n-\t\t}\n-\t}\n-\n-\t# Notify Git that the push is done\n-\tprint {*STDOUT} \"\\n\";\n-\n-\tif ($pushed && $dumb_push) {\n-\t\tprint {*STDERR} \"Just pushed some revisions to MediaWiki.\\n\";\n-\t\tprint {*STDERR} \"The pushed revisions now have to be re-imported, and your current branch\\n\";\n-\t\tprint {*STDERR} \"needs to be updated with these re-imported commits. You can do this with\\n\";\n-\t\tprint {*STDERR} \"\\n\";\n-\t\tprint {*STDERR} \"  git pull --rebase\\n\";\n-\t\tprint {*STDERR} \"\\n\";\n-\t}\n-\treturn;\n-}\n-\n-sub mw_push_revision {\n-\tmy $local = shift;\n-\tmy $remote = shift; # actually, this has to be \"refs/heads/master\" at this point.\n-\tmy $last_local_revid = get_last_local_revision();\n-\tprint {*STDERR} \".\\n\"; # Finish sentence started by get_last_local_revision()\n-\tmy $last_remote_revid = get_last_remote_revision();\n-\tmy $mw_revision = $last_remote_revid;\n-\n-\t# Get sha1 of commit pointed by local HEAD\n-\tmy $HEAD_sha1 = run_git_quoted_nostderr([\"rev-parse\", $local]);\n-\tchomp($HEAD_sha1);\n-\t# Get sha1 of commit pointed by remotes/$remotename/master\n-\tmy $remoteorigin_sha1 = run_git_quoted_nostderr([\"rev-parse\", \"refs/remotes/${remotename}/master\"]);\n-\tchomp($remoteorigin_sha1);\n-\n-\tif ($last_local_revid > 0 &&\n-\t    $last_local_revid < $last_remote_revid) {\n-\t\treturn error_non_fast_forward($remote);\n-\t}\n-\n-\tif ($HEAD_sha1 eq $remoteorigin_sha1) {\n-\t\t# nothing to push\n-\t\treturn 0;\n-\t}\n-\n-\t# Get every commit in between HEAD and refs/remotes/origin/master,\n-\t# including HEAD and refs/remotes/origin/master\n-\tmy @commit_pairs = ();\n-\tif ($last_local_revid > 0) {\n-\t\tmy $parsed_sha1 = $remoteorigin_sha1;\n-\t\t# Find a path from last MediaWiki commit to pushed commit\n-\t\tprint {*STDERR} \"Computing path from local to remote ...\\n\";\n-\t\tmy @local_ancestry = split(/\\n/, run_git_quoted([\"rev-list\", \"--boundary\", \"--parents\", $local, \"^${parsed_sha1}\"]));\n-\t\tmy %local_ancestry;\n-\t\tforeach my $line (@local_ancestry) {\n-\t\t\tif (my ($child, $parents) = $line =~ /^-?([a-f0-9]+) ([a-f0-9 ]+)/) {\n-\t\t\t\tforeach my $parent (split(/ /, $parents)) {\n-\t\t\t\t\t$local_ancestry{$parent} = $child;\n-\t\t\t\t}\n-\t\t\t} elsif (!$line =~ /^([a-f0-9]+)/) {\n-\t\t\t\tdie \"Unexpected output from git rev-list: ${line}\\n\";\n-\t\t\t}\n-\t\t}\n-\t\twhile ($parsed_sha1 ne $HEAD_sha1) {\n-\t\t\tmy $child = $local_ancestry{$parsed_sha1};\n-\t\t\tif (!$child) {\n-\t\t\t\tprint {*STDERR} \"Cannot find a path in history from remote commit to last commit\\n\";\n-\t\t\t\treturn error_non_fast_forward($remote);\n-\t\t\t}\n-\t\t\tpush(@commit_pairs, [$parsed_sha1, $child]);\n-\t\t\t$parsed_sha1 = $child;\n-\t\t}\n-\t} else {\n-\t\t# No remote mediawiki revision. Export the whole\n-\t\t# history (linearized with --first-parent)\n-\t\tprint {*STDERR} \"Warning: no common ancestor, pushing complete history\\n\";\n-\t\tmy $history = run_git_quoted([\"rev-list\", \"--first-parent\", \"--children\", $local]);\n-\t\tmy @history = split(/\\n/, $history);\n-\t\t@history = @history[1..$#history];\n-\t\tforeach my $line (reverse @history) {\n-\t\t\tmy @commit_info_split = split(/[ \\n]/, $line);\n-\t\t\tpush(@commit_pairs, \\@commit_info_split);\n-\t\t}\n-\t}\n-\n-\tforeach my $commit_info_split (@commit_pairs) {\n-\t\tmy $sha1_child = @{$commit_info_split}[0];\n-\t\tmy $sha1_commit = @{$commit_info_split}[1];\n-\t\tmy $diff_infos = run_git_quoted([\"diff-tree\", \"-r\", \"--raw\", \"-z\", $sha1_child, $sha1_commit]);\n-\t\t# TODO: we could detect rename, and encode them with a #redirect on the wiki.\n-\t\t# TODO: for now, it's just a delete+add\n-\t\tmy @diff_info_list = split(/\\0/, $diff_infos);\n-\t\t# Keep the subject line of the commit message as mediawiki comment for the revision\n-\t\tmy $commit_msg = run_git_quoted([\"log\", \"--no-walk\", '--format=\"%s\"', $sha1_commit]);\n-\t\tchomp($commit_msg);\n-\t\t# Push every blob\n-\t\twhile (@diff_info_list) {\n-\t\t\tmy $status;\n-\t\t\t# git diff-tree -z gives an output like\n-\t\t\t# <metadata>\\0<filename1>\\0\n-\t\t\t# <metadata>\\0<filename2>\\0\n-\t\t\t# and we've split on \\0.\n-\t\t\tmy $info = shift(@diff_info_list);\n-\t\t\tmy $file = shift(@diff_info_list);\n-\t\t\t($mw_revision, $status) = mw_push_file($info, $file, $commit_msg, $mw_revision);\n-\t\t\tif ($status eq 'non-fast-forward') {\n-\t\t\t\t# we may already have sent part of the\n-\t\t\t\t# commit to MediaWiki, but it's too\n-\t\t\t\t# late to cancel it. Stop the push in\n-\t\t\t\t# the middle, but still give an\n-\t\t\t\t# accurate error message.\n-\t\t\t\treturn error_non_fast_forward($remote);\n-\t\t\t}\n-\t\t\tif ($status ne 'ok') {\n-\t\t\t\tdie(\"Unknown error from mw_push_file()\\n\");\n-\t\t\t}\n-\t\t}\n-\t\tif (!$dumb_push) {\n-\t\t\trun_git_quoted([\"notes\", \"--ref=${remotename}/mediawiki\",\n-\t\t\t\t\t\"add\", \"-f\", \"-m\",\n-\t\t\t\t\t\"mediawiki_revision: ${mw_revision}\",\n-\t\t\t\t\t$sha1_commit]);\n-\t\t}\n-\t}\n-\n-\tprint {*STDOUT} \"ok ${remote}\\n\";\n-\treturn 1;\n-}\n-\n-sub get_allowed_file_extensions {\n-\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\n-\tmy $query = {\n-\t\taction => 'query',\n-\t\tmeta => 'siteinfo',\n-\t\tsiprop => 'fileextensions'\n-\t\t};\n-\tmy $result = $mediawiki->api($query);\n-\tmy @file_extensions = map { $_->{ext}} @{$result->{query}->{fileextensions}};\n-\tmy %hashFile = map { $_ => 1 } @file_extensions;\n-\n-\treturn %hashFile;\n-}\n-\n-# In memory cache for MediaWiki namespace ids.\n-my %namespace_id;\n-\n-# Namespaces whose id is cached in the configuration file\n-# (to avoid duplicates)\n-my %cached_mw_namespace_id;\n-\n-# Return MediaWiki id for a canonical namespace name.\n-# Ex.: \"File\", \"Project\".\n-sub get_mw_namespace_id {\n-\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\tmy $name = shift;\n-\n-\tif (!exists $namespace_id{$name}) {\n-\t\t# Look at configuration file, if the record for that namespace is\n-\t\t# already cached. Namespaces are stored in form:\n-\t\t# \"Name_of_namespace:Id_namespace\", ex.: \"File:6\".\n-\t\tmy @temp = split(/\\n/,\n-\t\t\t\t run_git_quoted([\"config\", \"--get-all\", \"remote.${remotename}.namespaceCache\"]));\n-\t\tchomp(@temp);\n-\t\tforeach my $ns (@temp) {\n-\t\t\tmy ($n, $id) = split(/:/, $ns);\n-\t\t\tif ($id eq 'notANameSpace') {\n-\t\t\t\t$namespace_id{$n} = {is_namespace => 0};\n-\t\t\t} else {\n-\t\t\t\t$namespace_id{$n} = {is_namespace => 1, id => $id};\n-\t\t\t}\n-\t\t\t$cached_mw_namespace_id{$n} = 1;\n-\t\t}\n-\t}\n-\n-\tif (!exists $namespace_id{$name}) {\n-\t\tprint {*STDERR} \"Namespace ${name} not found in cache, querying the wiki ...\\n\";\n-\t\t# NS not found => get namespace id from MW and store it in\n-\t        # configuration file.\n-\t        my $query = {\n-\t                action => 'query',\n-\t                meta => 'siteinfo',\n-\t                siprop => 'namespaces'\n-\t        };\n-\t        my $result = $mediawiki->api($query);\n-\n-\t        while (my ($id, $ns) = each(%{$result->{query}->{namespaces}})) {\n-\t                if (defined($ns->{id}) && defined($ns->{canonical})) {\n-\t\t\t\t$namespace_id{$ns->{canonical}} = {is_namespace => 1, id => $ns->{id}};\n-\t\t\t\tif ($ns->{'*'}) {\n-\t\t\t\t\t# alias (e.g. french Fichier: as alias for canonical File:)\n-\t\t\t\t\t$namespace_id{$ns->{'*'}} = {is_namespace => 1, id => $ns->{id}};\n-\t\t\t\t}\n-\t\t\t}\n-\t        }\n-\t}\n-\n-\tmy $ns = $namespace_id{$name};\n-\tmy $id;\n-\n-\tif (!defined $ns) {\n-\t\tmy @namespaces = map { s/ /_/g; $_; } sort keys %namespace_id;\n-\t\tprint {*STDERR} \"No such namespace ${name} on MediaWiki, known namespaces: @namespaces\\n\";\n-\t\t$ns = {is_namespace => 0};\n-\t\t$namespace_id{$name} = $ns;\n-\t}\n-\n-\tif ($ns->{is_namespace}) {\n-\t\t$id = $ns->{id};\n-\t}\n-\n-\t# Store \"notANameSpace\" as special value for inexisting namespaces\n-\tmy $store_id = ($id || 'notANameSpace');\n-\n-\t# Store explicitly requested namespaces on disk\n-\tif (!exists $cached_mw_namespace_id{$name}) {\n-\t\trun_git_quoted([\"config\", \"--add\", \"remote.${remotename}.namespaceCache\", \"${name}:${store_id}\"]);\n-\t\t$cached_mw_namespace_id{$name} = 1;\n-\t}\n-\treturn $id;\n-}\n-\n-sub get_mw_namespace_id_for_page {\n-\tmy $namespace = shift;\n-\tif ($namespace =~ /^([^:]*):/) {\n-\t\treturn get_mw_namespace_id($namespace);\n-\t} else {\n-\t\treturn;\n-\t}\n-}\ndiff --git a/contrib/mw-to-git/git-remote-mediawiki.txt b/contrib/mw-to-git/git-remote-mediawiki.txt\ndeleted file mode 100644\nindex 5da825f61e2..00000000000\n--- a/contrib/mw-to-git/git-remote-mediawiki.txt\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-Git-Mediawiki is a project which aims the creation of a gate\n-between git and mediawiki, allowing git users to push and pull\n-objects from mediawiki just as one would do with a classic git\n-repository thanks to remote-helpers.\n-\n-For more information, visit the wiki at\n-https://github.com/Git-Mediawiki/Git-Mediawiki\ndiff --git a/contrib/mw-to-git/t/.gitignore b/contrib/mw-to-git/t/.gitignore\ndeleted file mode 100644\nindex 2b8dc30c6db..00000000000\n--- a/contrib/mw-to-git/t/.gitignore\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-WEB/\n-mediawiki/\n-trash directory.t*/\n-test-results/\ndiff --git a/contrib/mw-to-git/t/Makefile b/contrib/mw-to-git/t/Makefile\ndeleted file mode 100644\nindex 6c9f377caac..00000000000\n--- a/contrib/mw-to-git/t/Makefile\n+++ /dev/null\n@@ -1,32 +0,0 @@\n-#\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-#\n-## Test git-remote-mediawiki\n-\n-# The default target of this Makefile is...\n-all:: test\n-\n--include ../../../config.mak.autogen\n--include ../../../config.mak\n-\n-T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)\n-\n-.PHONY: help test clean all\n-\n-help:\n-\t@echo 'Run \"$(MAKE) test\" to launch test scripts'\n-\t@echo 'Run \"$(MAKE) clean\" to remove trash folders'\n-\n-test:\n-\t@for t in $(T); do \\\n-\t\techo \"$$t\"; \\\n-\t\t\"./$$t\" || exit 1; \\\n-\tdone\n-\n-clean:\n-\t$(RM) -r 'trash directory'.*\ndiff --git a/contrib/mw-to-git/t/README b/contrib/mw-to-git/t/README\ndeleted file mode 100644\nindex 72c4889db7a..00000000000\n--- a/contrib/mw-to-git/t/README\n+++ /dev/null\n@@ -1,124 +0,0 @@\n-Tests for Mediawiki-to-Git\n-==========================\n-\n-Introduction\n-------------\n-This manual describes how to install the git-remote-mediawiki test\n-environment on a machine with git installed on it.\n-\n-Prerequisite\n-------------\n-\n-In order to run this test environment correctly, you will need to\n-install the following packages (Debian/Ubuntu names, may need to be\n-adapted for another distribution):\n-\n-* lighttpd\n-* php\n-* php-cgi\n-* php-cli\n-* php-curl\n-* php-sqlite\n-\n-Principles and Technical Choices\n---------------------------------\n-\n-The test environment makes it easy to install and manipulate one or\n-several MediaWiki instances. To allow developers to run the testsuite\n-easily, the environment does not require root privilege (except to\n-install the required packages if needed). It starts a webserver\n-instance on the user's account (using lighttpd greatly helps for\n-that), and does not need a separate database daemon (thanks to the use\n-of sqlite).\n-\n-Run the test environment\n-------------------------\n-\n-Install a new wiki\n-~~~~~~~~~~~~~~~~~~\n-\n-Once you have all the prerequisite, you need to install a MediaWiki\n-instance on your machine. If you already have one, it is still\n-strongly recommended to install one with the script provided. Here's\n-how to work it:\n-\n-a. change directory to contrib/mw-to-git/t/\n-b. if needed, edit test.config to choose your installation parameters\n-c. run `./install-wiki.sh install`\n-d. check on your favourite web browser if your wiki is correctly\n-   installed.\n-\n-Remove an existing wiki\n-~~~~~~~~~~~~~~~~~~~~~~~\n-\n-Edit the file test.config to fit the wiki you want to delete, and then\n-execute the command `./install-wiki.sh delete` from the\n-contrib/mw-to-git/t directory.\n-\n-Run the existing tests\n-~~~~~~~~~~~~~~~~~~~~~~\n-\n-The provided tests are currently in the `contrib/mw-to-git/t` directory.\n-The files are all the t936[0-9]-*.sh shell scripts.\n-\n-a. Run all tests:\n-To do so, run \"make test\" from the contrib/mw-to-git/ directory.\n-\n-b. Run a specific test:\n-To run a given test <test_name>, run ./<test_name> from the\n-contrib/mw-to-git/t directory.\n-\n-How to create new tests\n------------------------\n-\n-Available functions\n-~~~~~~~~~~~~~~~~~~~\n-\n-The test environment of git-remote-mediawiki provides some functions\n-useful to test its behaviour. for more details about the functions'\n-parameters, please refer to the `test-gitmw-lib.sh` and\n-`test-gitmw.pl` files.\n-\n-** `test_check_wiki_precond`:\n-Check if the tests must be skipped or not. Please use this function\n-at the beginning of each new test file.\n-\n-** `wiki_getpage`:\n-Fetch a given page from the wiki and puts its content in the\n-directory in parameter.\n-\n-** `wiki_delete_page`:\n-Delete a given page from the wiki.\n-\n-** `wiki_edit_page`:\n-Create or modify a given page in the wiki. You can specify several\n-parameters like a summary for the page edition, or add the page to a\n-given category.\n-See test-gitmw.pl for more details.\n-\n-** `wiki_getallpage`:\n-Fetch all pages from the wiki into a given directory. The directory\n-is created if it does not exists.\n-\n-** `test_diff_directories`:\n-Compare the content of two directories. The content must be the same.\n-Use this function to compare the content of a git directory and a wiki\n-one created by wiki_getallpage.\n-\n-** `test_contains_N_files`:\n-Check if the given directory contains a given number of file.\n-\n-** `wiki_page_exists`:\n-Tests if a given page exists on the wiki.\n-\n-** `wiki_reset`:\n-Reset the wiki, i.e. flush the database. Use this function at the\n-beginning of each new test, except if the test re-uses the same wiki\n-(and history) as the previous test.\n-\n-How to write a new test\n-~~~~~~~~~~~~~~~~~~~~~~~\n-\n-Please, follow the standards given by git. See git/t/README.\n-New file should be named as t936[0-9]-*.sh.\n-Be sure to reset your wiki regularly with the function `wiki_reset`.\ndiff --git a/contrib/mw-to-git/t/install-wiki.sh b/contrib/mw-to-git/t/install-wiki.sh\ndeleted file mode 100755\nindex c215213c4bf..00000000000\n--- a/contrib/mw-to-git/t/install-wiki.sh\n+++ /dev/null\n@@ -1,55 +0,0 @@\n-#!/bin/sh\n-\n-# This script installs or deletes a MediaWiki on your computer.\n-# It requires a web server with PHP and SQLite running. In addition, if you\n-# do not have MediaWiki sources on your computer, the option 'install'\n-# downloads them for you.\n-# Please set the CONFIGURATION VARIABLES in ./test-gitmw-lib.sh\n-\n-WIKI_TEST_DIR=$(cd \"$(dirname \"$0\")\" && pwd)\n-\n-if test -z \"$WIKI_TEST_DIR\"\n-then\n-\tWIKI_TEST_DIR=.\n-fi\n-\n-. \"$WIKI_TEST_DIR\"/test-gitmw-lib.sh\n-usage () {\n-\techo \"usage: \"\n-\techo \"\t./install-wiki.sh <install | delete | --help>\"\n-\techo \"\t\tinstall | -i :\tInstall a wiki on your computer.\"\n-\techo \"\t\tdelete | -d : Delete the wiki and all its pages and \"\n-\techo \"\t\t\tcontent.\"\n-\techo \"\t\tstart  | -s : Start the previously configured lighttpd daemon\"\n-\techo \"\t\tstop        : Stop lighttpd daemon.\"\n-}\n-\n-\n-# Argument: install, delete, --help | -h\n-case \"$1\" in\n-\t\"install\" | \"-i\")\n-\t\twiki_install\n-\t\texit 0\n-\t\t;;\n-\t\"delete\" | \"-d\")\n-\t\twiki_delete\n-\t\texit 0\n-\t\t;;\n-\t\"start\" | \"-s\")\n-\t\tstart_lighttpd\n-\t\texit\n-\t\t;;\n-\t\"stop\")\n-\t\tstop_lighttpd\n-\t\texit\n-\t\t;;\n-\t\"--help\" | \"-h\")\n-\t\tusage\n-\t\texit 0\n-\t\t;;\n-\t*)\n-\t\techo \"Invalid argument: $1\"\n-\t\tusage\n-\t\texit 1\n-\t\t;;\n-esac\ndiff --git a/contrib/mw-to-git/t/push-pull-tests.sh b/contrib/mw-to-git/t/push-pull-tests.sh\ndeleted file mode 100644\nindex 9da2dc5ff03..00000000000\n--- a/contrib/mw-to-git/t/push-pull-tests.sh\n+++ /dev/null\n@@ -1,144 +0,0 @@\n-test_push_pull () {\n-\n-\ttest_expect_success 'Git pull works after adding a new wiki page' '\n-\t\twiki_reset &&\n-\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_1 &&\n-\t\twiki_editpage Foo \"page created after the git clone\" false &&\n-\n-\t\t(\n-\t\t\tcd mw_dir_1 &&\n-\t\t\tgit pull\n-\t\t) &&\n-\n-\t\twiki_getallpage ref_page_1 &&\n-\t\ttest_diff_directories mw_dir_1 ref_page_1\n-\t'\n-\n-\ttest_expect_success 'Git pull works after editing a wiki page' '\n-\t\twiki_reset &&\n-\n-\t\twiki_editpage Foo \"page created before the git clone\" false &&\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_2 &&\n-\t\twiki_editpage Foo \"new line added on the wiki\" true &&\n-\n-\t\t(\n-\t\t\tcd mw_dir_2 &&\n-\t\t\tgit pull\n-\t\t) &&\n-\n-\t\twiki_getallpage ref_page_2 &&\n-\t\ttest_diff_directories mw_dir_2 ref_page_2\n-\t'\n-\n-\ttest_expect_success 'git pull works on conflict handled by auto-merge' '\n-\t\twiki_reset &&\n-\n-\t\twiki_editpage Foo \"1 init\n-3\n-5\n-\t\" false &&\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_3 &&\n-\n-\t\twiki_editpage Foo \"1 init\n-2 content added on wiki after clone\n-3\n-5\n-\t\" false &&\n-\n-\t\t(\n-\t\t\tcd mw_dir_3 &&\n-\t\techo \"1 init\n-3\n-4 content added on git after clone\n-5\n-\" >Foo.mw &&\n-\t\t\tgit commit -am \"conflicting change on foo\" &&\n-\t\t\tgit pull &&\n-\t\t\tgit push\n-\t\t)\n-\t'\n-\n-\ttest_expect_success 'Git push works after adding a file .mw' '\n-\t\twiki_reset &&\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_4 &&\n-\t\twiki_getallpage ref_page_4 &&\n-\t\t(\n-\t\t\tcd mw_dir_4 &&\n-\t\t\ttest_path_is_missing Foo.mw &&\n-\t\t\ttouch Foo.mw &&\n-\t\t\techo \"hello world\" >>Foo.mw &&\n-\t\t\tgit add Foo.mw &&\n-\t\t\tgit commit -m \"Foo\" &&\n-\t\t\tgit push\n-\t\t) &&\n-\t\twiki_getallpage ref_page_4 &&\n-\t\ttest_diff_directories mw_dir_4 ref_page_4\n-\t'\n-\n-\ttest_expect_success 'Git push works after editing a file .mw' '\n-\t\twiki_reset &&\n-\t\twiki_editpage \"Foo\" \"page created before the git clone\" false &&\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_5 &&\n-\n-\t\t(\n-\t\t\tcd mw_dir_5 &&\n-\t\t\techo \"new line added in the file Foo.mw\" >>Foo.mw &&\n-\t\t\tgit commit -am \"edit file Foo.mw\" &&\n-\t\t\tgit push\n-\t\t) &&\n-\n-\t\twiki_getallpage ref_page_5 &&\n-\t\ttest_diff_directories mw_dir_5 ref_page_5\n-\t'\n-\n-\ttest_expect_failure 'Git push works after deleting a file' '\n-\t\twiki_reset &&\n-\t\twiki_editpage Foo \"wiki page added before git clone\" false &&\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_6 &&\n-\n-\t\t(\n-\t\t\tcd mw_dir_6 &&\n-\t\t\tgit rm Foo.mw &&\n-\t\t\tgit commit -am \"page Foo.mw deleted\" &&\n-\t\t\tgit push\n-\t\t) &&\n-\n-\t\ttest_must_fail wiki_page_exist Foo\n-\t'\n-\n-\ttest_expect_success 'Merge conflict expected and solving it' '\n-\t\twiki_reset &&\n-\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_7 &&\n-\t\twiki_editpage Foo \"1 conflict\n-3 wiki\n-4\" false &&\n-\n-\t\t(\n-\t\t\tcd mw_dir_7 &&\n-\t\techo \"1 conflict\n-2 git\n-4\" >Foo.mw &&\n-\t\t\tgit add Foo.mw &&\n-\t\t\tgit commit -m \"conflict created\" &&\n-\t\t\ttest_must_fail git pull &&\n-\t\t\t\"$PERL_PATH\" -pi -e \"s/[<=>].*//g\" Foo.mw &&\n-\t\t\tgit commit -am \"merge conflict solved\" &&\n-\t\t\tgit push\n-\t\t)\n-\t'\n-\n-\ttest_expect_failure 'git pull works after deleting a wiki page' '\n-\t\twiki_reset &&\n-\t\twiki_editpage Foo \"wiki page added before the git clone\" false &&\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_8 &&\n-\n-\t\twiki_delete_page Foo &&\n-\t\t(\n-\t\t\tcd mw_dir_8 &&\n-\t\t\tgit pull &&\n-\t\t\ttest_path_is_missing Foo.mw\n-\t\t)\n-\t'\n-}\ndiff --git a/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh b/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh\ndeleted file mode 100755\nindex f08890d9e7a..00000000000\n--- a/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh\n+++ /dev/null\n@@ -1,257 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-#\n-# License: GPL v2 or later\n-\n-\n-test_description='Test the Git Mediawiki remote helper: git clone'\n-\n-. ./test-gitmw-lib.sh\n-. $TEST_DIRECTORY/test-lib.sh\n-\n-\n-test_check_precond\n-\n-\n-test_expect_success 'Git clone creates the expected git log with one file' '\n-\twiki_reset &&\n-\twiki_editpage foo \"this is not important\" false -c cat -s \"this must be the same\" &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_1 &&\n-\t(\n-\t\tcd mw_dir_1 &&\n-\t\tgit log --format=%s HEAD^..HEAD >log.tmp\n-\t) &&\n-\techo \"this must be the same\" >msg.tmp &&\n-\ttest_cmp msg.tmp mw_dir_1/log.tmp\n-'\n-\n-\n-test_expect_success 'Git clone creates the expected git log with multiple files' '\n-\twiki_reset &&\n-\twiki_editpage daddy \"this is not important\" false -s=\"this must be the same\" &&\n-\twiki_editpage daddy \"neither is this\" true -s=\"this must also be the same\" &&\n-\twiki_editpage daddy \"neither is this\" true -s=\"same same same\" &&\n-\twiki_editpage dj \"dont care\" false -s=\"identical\" &&\n-\twiki_editpage dj \"dont care either\" true -s=\"identical too\" &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_2 &&\n-\t(\n-\t\tcd mw_dir_2 &&\n-\t\tgit log --format=%s Daddy.mw  >logDaddy.tmp &&\n-\t\tgit log --format=%s Dj.mw >logDj.tmp\n-\t) &&\n-\techo \"same same same\" >msgDaddy.tmp &&\n-\techo \"this must also be the same\" >>msgDaddy.tmp &&\n-\techo \"this must be the same\" >>msgDaddy.tmp &&\n-\techo \"identical too\" >msgDj.tmp &&\n-\techo \"identical\" >>msgDj.tmp &&\n-\ttest_cmp msgDaddy.tmp mw_dir_2/logDaddy.tmp &&\n-\ttest_cmp msgDj.tmp mw_dir_2/logDj.tmp\n-'\n-\n-\n-test_expect_success 'Git clone creates only Main_Page.mw with an empty wiki' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_3 &&\n-\ttest_contains_N_files mw_dir_3 1 &&\n-\ttest_path_is_file mw_dir_3/Main_Page.mw\n-'\n-\n-test_expect_success 'Git clone does not fetch a deleted page' '\n-\twiki_reset &&\n-\twiki_editpage foo \"this page must be deleted before the clone\" false &&\n-\twiki_delete_page foo &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_4 &&\n-\ttest_contains_N_files mw_dir_4 1 &&\n-\ttest_path_is_file mw_dir_4/Main_Page.mw &&\n-\ttest_path_is_missing mw_dir_4/Foo.mw\n-'\n-\n-test_expect_success 'Git clone works with page added' '\n-\twiki_reset &&\n-\twiki_editpage foo \" I will be cloned\" false &&\n-\twiki_editpage bar \"I will be cloned\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_5 &&\n-\twiki_getallpage ref_page_5 &&\n-\ttest_diff_directories mw_dir_5 ref_page_5 &&\n-\twiki_delete_page foo &&\n-\twiki_delete_page bar\n-'\n-\n-test_expect_success 'Git clone works with an edited page ' '\n-\twiki_reset &&\n-\twiki_editpage foo \"this page will be edited\" \\\n-\t\tfalse -s \"first edition of page foo\" &&\n-\twiki_editpage foo \"this page has been edited and must be on the clone \" true &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_6 &&\n-\ttest_path_is_file mw_dir_6/Foo.mw &&\n-\ttest_path_is_file mw_dir_6/Main_Page.mw &&\n-\twiki_getallpage mw_dir_6/page_ref_6 &&\n-\ttest_diff_directories mw_dir_6 mw_dir_6/page_ref_6 &&\n-\t(\n-\t\tcd mw_dir_6 &&\n-\t\tgit log --format=%s HEAD^ Foo.mw > ../Foo.log\n-\t) &&\n-\techo \"first edition of page foo\" > FooExpect.log &&\n-\tdiff FooExpect.log Foo.log\n-'\n-\n-\n-test_expect_success 'Git clone works with several pages and some deleted ' '\n-\twiki_reset &&\n-\twiki_editpage foo \"this page will not be deleted\" false &&\n-\twiki_editpage bar \"I must not be erased\" false &&\n-\twiki_editpage namnam \"I will not be there at the end\" false &&\n-\twiki_editpage nyancat \"nyan nyan nyan delete me\" false &&\n-\twiki_delete_page namnam &&\n-\twiki_delete_page nyancat &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_7 &&\n-\ttest_path_is_file mw_dir_7/Foo.mw &&\n-\ttest_path_is_file mw_dir_7/Bar.mw &&\n-\ttest_path_is_missing mw_dir_7/Namnam.mw &&\n-\ttest_path_is_missing mw_dir_7/Nyancat.mw &&\n-\twiki_getallpage mw_dir_7/page_ref_7 &&\n-\ttest_diff_directories mw_dir_7 mw_dir_7/page_ref_7\n-'\n-\n-\n-test_expect_success 'Git clone works with one specific page cloned ' '\n-\twiki_reset &&\n-\twiki_editpage foo \"I will not be cloned\" false &&\n-\twiki_editpage bar \"Do not clone me\" false &&\n-\twiki_editpage namnam \"I will be cloned :)\" false  -s=\"this log must stay\" &&\n-\twiki_editpage nyancat \"nyan nyan nyan you cant clone me\" false &&\n-\tgit clone -c remote.origin.pages=namnam \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_8 &&\n-\ttest_contains_N_files mw_dir_8 1 &&\n-\ttest_path_is_file mw_dir_8/Namnam.mw &&\n-\ttest_path_is_missing mw_dir_8/Main_Page.mw &&\n-\t(\n-\t\tcd mw_dir_8 &&\n-\t\techo \"this log must stay\" >msg.tmp &&\n-\t\tgit log --format=%s >log.tmp &&\n-\t\ttest_cmp msg.tmp log.tmp\n-\t) &&\n-\twiki_check_content mw_dir_8/Namnam.mw Namnam\n-'\n-\n-test_expect_success 'Git clone works with multiple specific page cloned ' '\n-\twiki_reset &&\n-\twiki_editpage foo \"I will be there\" false &&\n-\twiki_editpage bar \"I will not disappear\" false &&\n-\twiki_editpage namnam \"I be erased\" false &&\n-\twiki_editpage nyancat \"nyan nyan nyan you will not erase me\" false &&\n-\twiki_delete_page namnam &&\n-\tgit clone -c remote.origin.pages=\"foo bar nyancat namnam\" \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_9 &&\n-\ttest_contains_N_files mw_dir_9 3 &&\n-\ttest_path_is_missing mw_dir_9/Namnam.mw &&\n-\ttest_path_is_file mw_dir_9/Foo.mw &&\n-\ttest_path_is_file mw_dir_9/Nyancat.mw &&\n-\ttest_path_is_file mw_dir_9/Bar.mw &&\n-\twiki_check_content mw_dir_9/Foo.mw Foo &&\n-\twiki_check_content mw_dir_9/Bar.mw Bar &&\n-\twiki_check_content mw_dir_9/Nyancat.mw Nyancat\n-'\n-\n-test_expect_success 'Mediawiki-clone of several specific pages on wiki' '\n-\twiki_reset &&\n-\twiki_editpage foo \"foo 1\" false &&\n-\twiki_editpage bar \"bar 1\" false &&\n-\twiki_editpage dummy \"dummy 1\" false &&\n-\twiki_editpage cloned_1 \"cloned_1 1\" false &&\n-\twiki_editpage cloned_2 \"cloned_2 2\" false &&\n-\twiki_editpage cloned_3 \"cloned_3 3\" false &&\n-\tmkdir -p ref_page_10 &&\n-\twiki_getpage cloned_1 ref_page_10 &&\n-\twiki_getpage cloned_2 ref_page_10 &&\n-\twiki_getpage cloned_3 ref_page_10 &&\n-\tgit clone -c remote.origin.pages=\"cloned_1 cloned_2 cloned_3\" \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_10 &&\n-\ttest_diff_directories mw_dir_10 ref_page_10\n-'\n-\n-test_expect_success 'Git clone works with the shallow option' '\n-\twiki_reset &&\n-\twiki_editpage foo \"1st revision, should be cloned\" false &&\n-\twiki_editpage bar \"1st revision, should be cloned\" false &&\n-\twiki_editpage nyan \"1st revision, should not be cloned\" false &&\n-\twiki_editpage nyan \"2nd revision, should be cloned\" false &&\n-\tgit -c remote.origin.shallow=true clone \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_11 &&\n-\ttest_contains_N_files mw_dir_11 4 &&\n-\ttest_path_is_file mw_dir_11/Nyan.mw &&\n-\ttest_path_is_file mw_dir_11/Foo.mw &&\n-\ttest_path_is_file mw_dir_11/Bar.mw &&\n-\ttest_path_is_file mw_dir_11/Main_Page.mw &&\n-\t(\n-\t\tcd mw_dir_11 &&\n-\t\ttest $(git log --oneline Nyan.mw | wc -l) -eq 1 &&\n-\t\ttest $(git log --oneline Foo.mw | wc -l) -eq 1 &&\n-\t\ttest $(git log --oneline Bar.mw | wc -l) -eq 1 &&\n-\t\ttest $(git log --oneline Main_Page.mw | wc -l ) -eq 1\n-\t) &&\n-\twiki_check_content mw_dir_11/Nyan.mw Nyan &&\n-\twiki_check_content mw_dir_11/Foo.mw Foo &&\n-\twiki_check_content mw_dir_11/Bar.mw Bar &&\n-\twiki_check_content mw_dir_11/Main_Page.mw Main_Page\n-'\n-\n-test_expect_success 'Git clone works with the shallow option with a delete page' '\n-\twiki_reset &&\n-\twiki_editpage foo \"1st revision, will be deleted\" false &&\n-\twiki_editpage bar \"1st revision, should be cloned\" false &&\n-\twiki_editpage nyan \"1st revision, should not be cloned\" false &&\n-\twiki_editpage nyan \"2nd revision, should be cloned\" false &&\n-\twiki_delete_page foo &&\n-\tgit -c remote.origin.shallow=true clone \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_12 &&\n-\ttest_contains_N_files mw_dir_12 3 &&\n-\ttest_path_is_file mw_dir_12/Nyan.mw &&\n-\ttest_path_is_missing mw_dir_12/Foo.mw &&\n-\ttest_path_is_file mw_dir_12/Bar.mw &&\n-\ttest_path_is_file mw_dir_12/Main_Page.mw &&\n-\t(\n-\t\tcd mw_dir_12 &&\n-\t\ttest $(git log --oneline Nyan.mw | wc -l) -eq 1 &&\n-\t\ttest $(git log --oneline Bar.mw | wc -l) -eq 1 &&\n-\t\ttest $(git log --oneline Main_Page.mw | wc -l ) -eq 1\n-\t) &&\n-\twiki_check_content mw_dir_12/Nyan.mw Nyan &&\n-\twiki_check_content mw_dir_12/Bar.mw Bar &&\n-\twiki_check_content mw_dir_12/Main_Page.mw Main_Page\n-'\n-\n-test_expect_success 'Test of fetching a category' '\n-\twiki_reset &&\n-\twiki_editpage Foo \"I will be cloned\" false -c=Category &&\n-\twiki_editpage Bar \"Meet me on the repository\" false -c=Category &&\n-\twiki_editpage Dummy \"I will not come\" false &&\n-\twiki_editpage BarWrong \"I will stay online only\" false -c=NotCategory &&\n-\tgit clone -c remote.origin.categories=\"Category\" \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_13 &&\n-\twiki_getallpage ref_page_13 Category &&\n-\ttest_diff_directories mw_dir_13 ref_page_13\n-'\n-\n-test_expect_success 'Test of resistance to modification of category on wiki for clone' '\n-\twiki_reset &&\n-\twiki_editpage Tobedeleted \"this page will be deleted\" false -c=Catone &&\n-\twiki_editpage Tobeedited \"this page will be modified\" false -c=Catone &&\n-\twiki_editpage Normalone \"this page wont be modified and will be on git\" false -c=Catone &&\n-\twiki_editpage Notconsidered \"this page will not appear on local\" false &&\n-\twiki_editpage Othercategory \"this page will not appear on local\" false -c=Cattwo &&\n-\twiki_editpage Tobeedited \"this page have been modified\" true -c=Catone &&\n-\twiki_delete_page Tobedeleted &&\n-\tgit clone -c remote.origin.categories=\"Catone\" \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_14 &&\n-\twiki_getallpage ref_page_14 Catone &&\n-\ttest_diff_directories mw_dir_14 ref_page_14\n-'\n-\n-test_done\ndiff --git a/contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh b/contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh\ndeleted file mode 100755\nindex 9ea201459b5..00000000000\n--- a/contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh\n+++ /dev/null\n@@ -1,24 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-#\n-# License: GPL v2 or later\n-\n-# tests for git-remote-mediawiki\n-\n-test_description='Test the Git Mediawiki remote helper: git push and git pull simple test cases'\n-\n-. ./test-gitmw-lib.sh\n-. ./push-pull-tests.sh\n-. $TEST_DIRECTORY/test-lib.sh\n-\n-test_check_precond\n-\n-test_push_pull\n-\n-test_done\ndiff --git a/contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh b/contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh\ndeleted file mode 100755\nindex 526d92850ff..00000000000\n--- a/contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh\n+++ /dev/null\n@@ -1,347 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-#\n-# License: GPL v2 or later\n-\n-# tests for git-remote-mediawiki\n-\n-test_description='Test git-mediawiki with special characters in filenames'\n-\n-. ./test-gitmw-lib.sh\n-. $TEST_DIRECTORY/test-lib.sh\n-\n-\n-test_check_precond\n-\n-\n-test_expect_success 'Git clone works for a wiki with accents in the page names' '\n-\twiki_reset &&\n-\twiki_editpage féé \"This page must be délétéd before clone\" false &&\n-\twiki_editpage kèè \"This page must be deleted before clone\" false &&\n-\twiki_editpage hàà \"This page must be deleted before clone\" false &&\n-\twiki_editpage kîî \"This page must be deleted before clone\" false &&\n-\twiki_editpage foo \"This page must be deleted before clone\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_1 &&\n-\twiki_getallpage ref_page_1 &&\n-\ttest_diff_directories mw_dir_1 ref_page_1\n-'\n-\n-\n-test_expect_success 'Git pull works with a wiki with accents in the pages names' '\n-\twiki_reset &&\n-\twiki_editpage kîî \"this page must be cloned\" false &&\n-\twiki_editpage foo \"this page must be cloned\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_2 &&\n-\twiki_editpage éàîôû \"This page must be pulled\" false &&\n-\t(\n-\t\tcd mw_dir_2 &&\n-\t\tgit pull\n-\t) &&\n-\twiki_getallpage ref_page_2 &&\n-\ttest_diff_directories mw_dir_2 ref_page_2\n-'\n-\n-\n-test_expect_success 'Cloning a chosen page works with accents' '\n-\twiki_reset &&\n-\twiki_editpage kîî \"this page must be cloned\" false &&\n-\tgit clone -c remote.origin.pages=kîî \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_3 &&\n-\twiki_check_content mw_dir_3/Kîî.mw Kîî &&\n-\ttest_path_is_file mw_dir_3/Kîî.mw &&\n-\trm -rf mw_dir_3\n-'\n-\n-\n-test_expect_success 'The shallow option works with accents' '\n-\twiki_reset &&\n-\twiki_editpage néoà \"1st revision, should not be cloned\" false &&\n-\twiki_editpage néoà \"2nd revision, should be cloned\" false &&\n-\tgit -c remote.origin.shallow=true clone \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_4 &&\n-\ttest_contains_N_files mw_dir_4 2 &&\n-\ttest_path_is_file mw_dir_4/Néoà.mw &&\n-\ttest_path_is_file mw_dir_4/Main_Page.mw &&\n-\t(\n-\t\tcd mw_dir_4 &&\n-\t\ttest $(git log --oneline Néoà.mw | wc -l) -eq 1 &&\n-\t\ttest $(git log --oneline Main_Page.mw | wc -l ) -eq 1\n-\t) &&\n-\twiki_check_content mw_dir_4/Néoà.mw Néoà &&\n-\twiki_check_content mw_dir_4/Main_Page.mw Main_Page\n-'\n-\n-\n-test_expect_success 'Cloning works when page name first letter has an accent' '\n-\twiki_reset &&\n-\twiki_editpage îî \"this page must be cloned\" false &&\n-\tgit clone -c remote.origin.pages=îî \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_5 &&\n-\ttest_path_is_file mw_dir_5/Îî.mw &&\n-\twiki_check_content mw_dir_5/Îî.mw Îî\n-'\n-\n-\n-test_expect_success 'Git push works with a wiki with accents' '\n-\twiki_reset &&\n-\twiki_editpage féé \"lots of accents : éèàÖ\" false &&\n-\twiki_editpage foo \"this page must be cloned\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_6 &&\n-\t(\n-\t\tcd mw_dir_6 &&\n-\t\techo \"A wild Pîkächû appears on the wiki\" >Pîkächû.mw &&\n-\t\tgit add Pîkächû.mw &&\n-\t\tgit commit -m \"A new page appears\" &&\n-\t\tgit push\n-\t) &&\n-\twiki_getallpage ref_page_6 &&\n-\ttest_diff_directories mw_dir_6 ref_page_6\n-'\n-\n-test_expect_success 'Git clone works with accentsand spaces' '\n-\twiki_reset &&\n-\twiki_editpage \"é à î\" \"this page must be délété before the clone\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_7 &&\n-\twiki_getallpage ref_page_7 &&\n-\ttest_diff_directories mw_dir_7 ref_page_7\n-'\n-\n-test_expect_success 'character $ in page name (mw -> git)' '\n-\twiki_reset &&\n-\twiki_editpage file_\\$_foo \"expect to be called file_$_foo\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_8 &&\n-\ttest_path_is_file mw_dir_8/File_\\$_foo.mw &&\n-\twiki_getallpage ref_page_8 &&\n-\ttest_diff_directories mw_dir_8 ref_page_8\n-'\n-\n-\n-\n-test_expect_success 'character $ in file name (git -> mw) ' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_9 &&\n-\t(\n-\t\tcd mw_dir_9 &&\n-\t\techo \"this file is called File_\\$_foo.mw\" >File_\\$_foo.mw &&\n-\t\tgit add . &&\n-\t\tgit commit -am \"file File_\\$_foo.mw\" &&\n-\t\tgit pull &&\n-\t\tgit push\n-\t) &&\n-\twiki_getallpage ref_page_9 &&\n-\ttest_diff_directories mw_dir_9 ref_page_9\n-'\n-\n-\n-test_expect_failure 'capital at the beginning of file names' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_10 &&\n-\t(\n-\t\tcd mw_dir_10 &&\n-\t\techo \"my new file foo\" >foo.mw &&\n-\t\techo \"my new file Foo... Finger crossed\" >Foo.mw &&\n-\t\tgit add . &&\n-\t\tgit commit -am \"file foo.mw\" &&\n-\t\tgit pull &&\n-\t\tgit push\n-\t) &&\n-\twiki_getallpage ref_page_10 &&\n-\ttest_diff_directories mw_dir_10 ref_page_10\n-'\n-\n-\n-test_expect_failure 'special character at the beginning of file name from mw to git' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_11 &&\n-\twiki_editpage {char_1 \"expect to be renamed {char_1\" false &&\n-\twiki_editpage [char_2 \"expect to be renamed [char_2\" false &&\n-\t(\n-\t\tcd mw_dir_11 &&\n-\t\tgit pull\n-\t) &&\n-\ttest_path_is_file mw_dir_11/{char_1 &&\n-\ttest_path_is_file mw_dir_11/[char_2\n-'\n-\n-test_expect_success 'Pull page with title containing \":\" other than namespace separator' '\n-\twiki_editpage Foo:Bar content false &&\n-\t(\n-\t\tcd mw_dir_11 &&\n-\t\tgit pull\n-\t) &&\n-\ttest_path_is_file mw_dir_11/Foo:Bar.mw\n-'\n-\n-test_expect_success 'Push page with title containing \":\" other than namespace separator' '\n-\t(\n-\t\tcd mw_dir_11 &&\n-\t\techo content >NotANameSpace:Page.mw &&\n-\t\tgit add NotANameSpace:Page.mw &&\n-\t\tgit commit -m \"add page with colon\" &&\n-\t\tgit push\n-\t) &&\n-\twiki_page_exist NotANameSpace:Page\n-'\n-\n-test_expect_success 'test of correct formatting for file name from mw to git' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_12 &&\n-\twiki_editpage char_%_7b_1 \"expect to be renamed char{_1\" false &&\n-\twiki_editpage char_%_5b_2 \"expect to be renamed char{_2\" false &&\n-\t(\n-\t\tcd mw_dir_12 &&\n-\t\tgit pull\n-\t) &&\n-\ttest_path_is_file mw_dir_12/Char\\{_1.mw &&\n-\ttest_path_is_file mw_dir_12/Char\\[_2.mw &&\n-\twiki_getallpage ref_page_12 &&\n-\tmv ref_page_12/Char_%_7b_1.mw ref_page_12/Char\\{_1.mw &&\n-\tmv ref_page_12/Char_%_5b_2.mw ref_page_12/Char\\[_2.mw &&\n-\ttest_diff_directories mw_dir_12 ref_page_12\n-'\n-\n-\n-test_expect_failure 'test of correct formatting for file name beginning with special character' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_13 &&\n-\t(\n-\t\tcd mw_dir_13 &&\n-\t\techo \"my new file {char_1\" >\\{char_1.mw &&\n-\t\techo \"my new file [char_2\" >\\[char_2.mw &&\n-\t\tgit add . &&\n-\t\tgit commit -am \"committing some exotic file name...\" &&\n-\t\tgit push &&\n-\t\tgit pull\n-\t) &&\n-\twiki_getallpage ref_page_13 &&\n-\ttest_path_is_file ref_page_13/{char_1.mw &&\n-\ttest_path_is_file ref_page_13/[char_2.mw &&\n-\ttest_diff_directories mw_dir_13 ref_page_13\n-'\n-\n-\n-test_expect_success 'test of correct formatting for file name from git to mw' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_14 &&\n-\t(\n-\t\tcd mw_dir_14 &&\n-\t\techo \"my new file char{_1\" >Char\\{_1.mw &&\n-\t\techo \"my new file char[_2\" >Char\\[_2.mw &&\n-\t\tgit add . &&\n-\t\tgit commit -m \"committing some exotic file name...\" &&\n-\t\tgit push\n-\t) &&\n-\twiki_getallpage ref_page_14 &&\n-\tmv mw_dir_14/Char\\{_1.mw mw_dir_14/Char_%_7b_1.mw &&\n-\tmv mw_dir_14/Char\\[_2.mw mw_dir_14/Char_%_5b_2.mw &&\n-\ttest_diff_directories mw_dir_14 ref_page_14\n-'\n-\n-\n-test_expect_success 'git clone with /' '\n-\twiki_reset &&\n-\twiki_editpage \\/fo\\/o \"this is not important\" false -c=Deleted &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_15 &&\n-\ttest_path_is_file mw_dir_15/%2Ffo%2Fo.mw &&\n-\twiki_check_content mw_dir_15/%2Ffo%2Fo.mw \\/fo\\/o\n-'\n-\n-\n-test_expect_success 'git push with /' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_16 &&\n-\techo \"I will be on the wiki\" >mw_dir_16/%2Ffo%2Fo.mw &&\n-\t(\n-\t\tcd mw_dir_16 &&\n-\t\tgit add %2Ffo%2Fo.mw &&\n-\t\tgit commit -m \" %2Ffo%2Fo added\" &&\n-\t\tgit push\n-\t) &&\n-\twiki_page_exist \\/fo\\/o &&\n-\twiki_check_content mw_dir_16/%2Ffo%2Fo.mw \\/fo\\/o\n-\n-'\n-\n-\n-test_expect_success 'git clone with \\' '\n-\twiki_reset &&\n-\twiki_editpage \\\\ko\\\\o \"this is not important\" false -c=Deleted &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_17 &&\n-\ttest_path_is_file mw_dir_17/\\\\ko\\\\o.mw &&\n-\twiki_check_content mw_dir_17/\\\\ko\\\\o.mw \\\\ko\\\\o\n-'\n-\n-\n-test_expect_success 'git push with \\' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_18 &&\n-\techo \"I will be on the wiki\" >mw_dir_18/\\\\ko\\\\o.mw &&\n-\t(\n-\t\tcd mw_dir_18 &&\n-\t\tgit add \\\\ko\\\\o.mw &&\n-\t\tgit commit -m \" \\\\ko\\\\o added\" &&\n-\t\tgit push\n-\t) &&\n-\twiki_page_exist \\\\ko\\\\o &&\n-\twiki_check_content mw_dir_18/\\\\ko\\\\o.mw \\\\ko\\\\o\n-\n-'\n-\n-test_expect_success 'git clone with \\ in format control' '\n-\twiki_reset &&\n-\twiki_editpage \\\\no\\\\o \"this is not important\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_19 &&\n-\ttest_path_is_file mw_dir_19/\\\\no\\\\o.mw &&\n-\twiki_check_content mw_dir_19/\\\\no\\\\o.mw \\\\no\\\\o\n-'\n-\n-\n-test_expect_success 'git push with \\ in format control' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_20 &&\n-\techo \"I will be on the wiki\" >mw_dir_20/\\\\fo\\\\o.mw &&\n-\t(\n-\t\tcd mw_dir_20 &&\n-\t\tgit add \\\\fo\\\\o.mw &&\n-\t\tgit commit -m \" \\\\fo\\\\o added\" &&\n-\t\tgit push\n-\t) &&\n-\twiki_page_exist \\\\fo\\\\o &&\n-\twiki_check_content mw_dir_20/\\\\fo\\\\o.mw \\\\fo\\\\o\n-\n-'\n-\n-\n-test_expect_success 'fast-import meta-characters in page name (mw -> git)' '\n-\twiki_reset &&\n-\twiki_editpage \\\"file\\\"_\\\\_foo \"expect to be called \\\"file\\\"_\\\\_foo\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_21 &&\n-\ttest_path_is_file mw_dir_21/\\\"file\\\"_\\\\_foo.mw &&\n-\twiki_getallpage ref_page_21 &&\n-\ttest_diff_directories mw_dir_21 ref_page_21\n-'\n-\n-\n-test_expect_success 'fast-import meta-characters in page name (git -> mw) ' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_22 &&\n-\t(\n-\t\tcd mw_dir_22 &&\n-\t\techo \"this file is called \\\"file\\\"_\\\\_foo.mw\" >\\\"file\\\"_\\\\_foo &&\n-\t\tgit add . &&\n-\t\tgit commit -am \"file \\\"file\\\"_\\\\_foo\" &&\n-\t\tgit pull &&\n-\t\tgit push\n-\t) &&\n-\twiki_getallpage ref_page_22 &&\n-\ttest_diff_directories mw_dir_22 ref_page_22\n-'\n-\n-\n-test_done\ndiff --git a/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh b/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh\ndeleted file mode 100755\nindex 7139995a405..00000000000\n--- a/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh\n+++ /dev/null\n@@ -1,218 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-#\n-# License: GPL v2 or later\n-\n-# tests for git-remote-mediawiki\n-\n-test_description='Test the Git Mediawiki remote helper: git push and git pull simple test cases'\n-\n-. ./test-gitmw-lib.sh\n-. $TEST_DIRECTORY/test-lib.sh\n-\n-\n-test_check_precond\n-\n-\n-test_git_reimport () {\n-\tgit -c remote.origin.dumbPush=true push &&\n-\tgit -c remote.origin.mediaImport=true pull --rebase\n-}\n-\n-# Don't bother with permissions, be administrator by default\n-test_expect_success 'setup config' '\n-\tgit config --global remote.origin.mwLogin \"$WIKI_ADMIN\" &&\n-\tgit config --global remote.origin.mwPassword \"$WIKI_PASSW\" &&\n-\ttest_might_fail git config --global --unset remote.origin.mediaImport\n-'\n-\n-test_expect_failure 'git push can upload media (File:) files' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\techo \"hello world\" >Foo.txt &&\n-\t\tgit add Foo.txt &&\n-\t\tgit commit -m \"add a text file\" &&\n-\t\tgit push &&\n-\t\t\"$PERL_PATH\" -e \"print STDOUT \\\"binary content: \\\".chr(255);\" >Foo.txt &&\n-\t\tgit add Foo.txt &&\n-\t\tgit commit -m \"add a text file with binary content\" &&\n-\t\tgit push\n-\t)\n-'\n-\n-test_expect_failure 'git clone works on previously created wiki with media files' '\n-\ttest_when_finished \"rm -rf mw_dir mw_dir_clone\" &&\n-\tgit clone -c remote.origin.mediaimport=true \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_clone &&\n-\ttest_cmp mw_dir_clone/Foo.txt mw_dir/Foo.txt &&\n-\t(cd mw_dir_clone && git checkout HEAD^) &&\n-\t(cd mw_dir && git checkout HEAD^) &&\n-\ttest_path_is_file mw_dir_clone/Foo.txt &&\n-\ttest_cmp mw_dir_clone/Foo.txt mw_dir/Foo.txt\n-'\n-\n-test_expect_success 'git push can upload media (File:) files containing valid UTF-8' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\t\"$PERL_PATH\" -e \"print STDOUT \\\"UTF-8 content: éèàéê€.\\\";\" >Bar.txt &&\n-\t\tgit add Bar.txt &&\n-\t\tgit commit -m \"add a text file with UTF-8 content\" &&\n-\t\tgit push\n-\t)\n-'\n-\n-test_expect_success 'git clone works on previously created wiki with media files containing valid UTF-8' '\n-\ttest_when_finished \"rm -rf mw_dir mw_dir_clone\" &&\n-\tgit clone -c remote.origin.mediaimport=true \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_clone &&\n-\ttest_cmp mw_dir_clone/Bar.txt mw_dir/Bar.txt\n-'\n-\n-test_expect_success 'git push & pull work with locally renamed media files' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\ttest_when_finished \"rm -fr mw_dir\" &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\techo \"A File\" >Foo.txt &&\n-\t\tgit add Foo.txt &&\n-\t\tgit commit -m \"add a file\" &&\n-\t\tgit mv Foo.txt Bar.txt &&\n-\t\tgit commit -m \"Rename a file\" &&\n-\t\ttest_git_reimport &&\n-\t\techo \"A File\" >expect &&\n-\t\ttest_cmp expect Bar.txt &&\n-\t\ttest_path_is_missing Foo.txt\n-\t)\n-'\n-\n-test_expect_success 'git push can propagate local page deletion' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\ttest_when_finished \"rm -fr mw_dir\" &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\ttest_path_is_missing Foo.mw &&\n-\t\techo \"hello world\" >Foo.mw &&\n-\t\tgit add Foo.mw &&\n-\t\tgit commit -m \"Add the page Foo\" &&\n-\t\tgit push &&\n-\t\trm -f Foo.mw &&\n-\t\tgit commit -am \"Delete the page Foo\" &&\n-\t\ttest_git_reimport &&\n-\t\ttest_path_is_missing Foo.mw\n-\t)\n-'\n-\n-test_expect_success 'git push can propagate local media file deletion' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\ttest_when_finished \"rm -fr mw_dir\" &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\techo \"hello world\" >Foo.txt &&\n-\t\tgit add Foo.txt &&\n-\t\tgit commit -m \"Add the text file Foo\" &&\n-\t\tgit rm Foo.txt &&\n-\t\tgit commit -m \"Delete the file Foo\" &&\n-\t\ttest_git_reimport &&\n-\t\ttest_path_is_missing Foo.txt\n-\t)\n-'\n-\n-# test failure: the file is correctly uploaded, and then deleted but\n-# as no page link to it, the import (which looks at page revisions)\n-# doesn't notice the file deletion on the wiki. We fetch the list of\n-# files from the wiki, but as the file is deleted, it doesn't appear.\n-test_expect_failure 'git pull correctly imports media file deletion when no page link to it' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\ttest_when_finished \"rm -fr mw_dir\" &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\techo \"hello world\" >Foo.txt &&\n-\t\tgit add Foo.txt &&\n-\t\tgit commit -m \"Add the text file Foo\" &&\n-\t\tgit push &&\n-\t\tgit rm Foo.txt &&\n-\t\tgit commit -m \"Delete the file Foo\" &&\n-\t\ttest_git_reimport &&\n-\t\ttest_path_is_missing Foo.txt\n-\t)\n-'\n-\n-test_expect_success 'git push properly warns about insufficient permissions' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\ttest_when_finished \"rm -fr mw_dir\" &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\techo \"A File\" >foo.forbidden &&\n-\t\tgit add foo.forbidden &&\n-\t\tgit commit -m \"add a file\" &&\n-\t\tgit push 2>actual &&\n-\t\ttest_grep \"foo.forbidden is not a permitted file\" actual\n-\t)\n-'\n-\n-test_expect_success 'setup a repository with media files' '\n-\twiki_reset &&\n-\twiki_editpage testpage \"I am linking a file [[File:File.txt]]\" false &&\n-\techo \"File content\" >File.txt &&\n-\twiki_upload_file File.txt &&\n-\techo \"Another file content\" >AnotherFile.txt &&\n-\twiki_upload_file AnotherFile.txt\n-'\n-\n-test_expect_success 'git clone works with one specific page cloned and mediaimport=true' '\n-\tgit clone -c remote.origin.pages=testpage \\\n-\t\t  -c remote.origin.mediaimport=true \\\n-\t\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_15 &&\n-\ttest_when_finished \"rm -rf mw_dir_15\" &&\n-\ttest_contains_N_files mw_dir_15 3 &&\n-\ttest_path_is_file mw_dir_15/Testpage.mw &&\n-\ttest_path_is_file mw_dir_15/File:File.txt.mw &&\n-\ttest_path_is_file mw_dir_15/File.txt &&\n-\ttest_path_is_missing mw_dir_15/Main_Page.mw &&\n-\ttest_path_is_missing mw_dir_15/File:AnotherFile.txt.mw &&\n-\ttest_path_is_missing mw_dir_15/AnothetFile.txt &&\n-\twiki_check_content mw_dir_15/Testpage.mw Testpage &&\n-\ttest_cmp mw_dir_15/File.txt File.txt\n-'\n-\n-test_expect_success 'git clone works with one specific page cloned and mediaimport=false' '\n-\ttest_when_finished \"rm -rf mw_dir_16\" &&\n-\tgit clone -c remote.origin.pages=testpage \\\n-\t\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_16 &&\n-\ttest_contains_N_files mw_dir_16 1 &&\n-\ttest_path_is_file mw_dir_16/Testpage.mw &&\n-\ttest_path_is_missing mw_dir_16/File:File.txt.mw &&\n-\ttest_path_is_missing mw_dir_16/File.txt &&\n-\ttest_path_is_missing mw_dir_16/Main_Page.mw &&\n-\twiki_check_content mw_dir_16/Testpage.mw Testpage\n-'\n-\n-# should behave like mediaimport=false\n-test_expect_success 'git clone works with one specific page cloned and mediaimport unset' '\n-\ttest_when_finished \"rm -fr mw_dir_17\" &&\n-\tgit clone -c remote.origin.pages=testpage \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_17 &&\n-\ttest_contains_N_files mw_dir_17 1 &&\n-\ttest_path_is_file mw_dir_17/Testpage.mw &&\n-\ttest_path_is_missing mw_dir_17/File:File.txt.mw &&\n-\ttest_path_is_missing mw_dir_17/File.txt &&\n-\ttest_path_is_missing mw_dir_17/Main_Page.mw &&\n-\twiki_check_content mw_dir_17/Testpage.mw Testpage\n-'\n-\n-test_done\ndiff --git a/contrib/mw-to-git/t/t9364-pull-by-rev.sh b/contrib/mw-to-git/t/t9364-pull-by-rev.sh\ndeleted file mode 100755\nindex 5c22457a0b6..00000000000\n--- a/contrib/mw-to-git/t/t9364-pull-by-rev.sh\n+++ /dev/null\n@@ -1,17 +0,0 @@\n-#!/bin/sh\n-\n-test_description='Test the Git Mediawiki remote helper: git pull by revision'\n-\n-. ./test-gitmw-lib.sh\n-. ./push-pull-tests.sh\n-. $TEST_DIRECTORY/test-lib.sh\n-\n-test_check_precond\n-\n-test_expect_success 'configuration' '\n-\tgit config --global mediawiki.fetchStrategy by_rev\n-'\n-\n-test_push_pull\n-\n-test_done\ndiff --git a/contrib/mw-to-git/t/t9365-continuing-queries.sh b/contrib/mw-to-git/t/t9365-continuing-queries.sh\ndeleted file mode 100755\nindex d3e73126595..00000000000\n--- a/contrib/mw-to-git/t/t9365-continuing-queries.sh\n+++ /dev/null\n@@ -1,23 +0,0 @@\n-#!/bin/sh\n-\n-test_description='Test the Git Mediawiki remote helper: queries w/ more than 500 results'\n-\n-. ./test-gitmw-lib.sh\n-. $TEST_DIRECTORY/test-lib.sh\n-\n-test_check_precond\n-\n-test_expect_success 'creating page w/ >500 revisions' '\n-\twiki_reset &&\n-\tfor i in $(test_seq 501)\n-\tdo\n-\t\techo \"creating revision $i\" &&\n-\t\twiki_editpage foo \"revision $i<br/>\" true || return 1\n-\tdone\n-'\n-\n-test_expect_success 'cloning page w/ >500 revisions' '\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir\n-'\n-\n-test_done\ndiff --git a/contrib/mw-to-git/t/test-gitmw-lib.sh b/contrib/mw-to-git/t/test-gitmw-lib.sh\ndeleted file mode 100755\nindex 64e46c16716..00000000000\n--- a/contrib/mw-to-git/t/test-gitmw-lib.sh\n+++ /dev/null\n@@ -1,432 +0,0 @@\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-# License: GPL v2 or later\n-\n-#\n-# CONFIGURATION VARIABLES\n-# You might want to change these ones\n-#\n-\n-. ./test.config\n-\n-WIKI_BASE_URL=http://$SERVER_ADDR:$PORT\n-WIKI_URL=$WIKI_BASE_URL/$WIKI_DIR_NAME\n-CURR_DIR=$(pwd)\n-TEST_OUTPUT_DIRECTORY=$(pwd)\n-TEST_DIRECTORY=\"$CURR_DIR\"/../../../t\n-\n-export TEST_OUTPUT_DIRECTORY TEST_DIRECTORY CURR_DIR\n-\n-if test \"$LIGHTTPD\" = \"false\" ; then\n-\tPORT=80\n-else\n-\tWIKI_DIR_INST=\"$CURR_DIR/$WEB_WWW\"\n-fi\n-\n-wiki_upload_file () {\n-\t\"$CURR_DIR\"/test-gitmw.pl upload_file \"$@\"\n-}\n-\n-wiki_getpage () {\n-\t\"$CURR_DIR\"/test-gitmw.pl get_page \"$@\"\n-}\n-\n-wiki_delete_page () {\n-\t\"$CURR_DIR\"/test-gitmw.pl delete_page \"$@\"\n-}\n-\n-wiki_editpage () {\n-\t\"$CURR_DIR\"/test-gitmw.pl edit_page \"$@\"\n-}\n-\n-die () {\n-\tdie_with_status 1 \"$@\"\n-}\n-\n-die_with_status () {\n-\tstatus=$1\n-\tshift\n-\techo >&2 \"$*\"\n-\texit \"$status\"\n-}\n-\n-\n-# Check the preconditions to run git-remote-mediawiki's tests\n-test_check_precond () {\n-\tif ! test_have_prereq PERL\n-\tthen\n-\t\tskip_all='skipping gateway git-mw tests, perl not available'\n-\t\ttest_done\n-\tfi\n-\n-\tGIT_EXEC_PATH=$(cd \"$(dirname \"$0\")\" && cd \"../..\" && pwd)\n-\tPATH=\"$GIT_EXEC_PATH\"'/bin-wrapper:'\"$PATH\"\n-\n-\tif ! test -d \"$WIKI_DIR_INST/$WIKI_DIR_NAME\"\n-\tthen\n-\t\tskip_all='skipping gateway git-mw tests, no mediawiki found'\n-\t\ttest_done\n-\tfi\n-}\n-\n-# test_diff_directories <dir_git> <dir_wiki>\n-#\n-# Compare the contents of directories <dir_git> and <dir_wiki> with diff\n-# and errors if they do not match. The program will\n-# not look into .git in the process.\n-# Warning: the first argument MUST be the directory containing the git data\n-test_diff_directories () {\n-\trm -rf \"$1_tmp\"\n-\tmkdir -p \"$1_tmp\"\n-\tcp \"$1\"/*.mw \"$1_tmp\"\n-\tdiff -r -b \"$1_tmp\" \"$2\"\n-}\n-\n-# $1=<dir>\n-# $2=<N>\n-#\n-# Check that <dir> contains exactly <N> files\n-test_contains_N_files () {\n-\tif test $(ls -- \"$1\" | wc -l) -ne \"$2\"; then\n-\t\techo \"directory $1 should contain $2 files\"\n-\t\techo \"it contains these files:\"\n-\t\tls \"$1\"\n-\t\tfalse\n-\tfi\n-}\n-\n-\n-# wiki_check_content <file_name> <page_name>\n-#\n-# Compares the contents of the file <file_name> and the wiki page\n-# <page_name> and exits with error 1 if they do not match.\n-wiki_check_content () {\n-\tmkdir -p wiki_tmp\n-\twiki_getpage \"$2\" wiki_tmp\n-\t# replacement of forbidden character in file name\n-\tpage_name=$(printf \"%s\\n\" \"$2\" | sed -e \"s/\\//%2F/g\")\n-\n-\tdiff -b \"$1\" wiki_tmp/\"$page_name\".mw\n-\tif test $? -ne 0\n-\tthen\n-\t\trm -rf wiki_tmp\n-\t\terror \"ERROR: file $2 not found on wiki\"\n-\tfi\n-\trm -rf wiki_tmp\n-}\n-\n-# wiki_page_exist <page_name>\n-#\n-# Check the existence of the page <page_name> on the wiki and exits\n-# with error if it is absent from it.\n-wiki_page_exist () {\n-\tmkdir -p wiki_tmp\n-\twiki_getpage \"$1\" wiki_tmp\n-\tpage_name=$(printf \"%s\\n\" \"$1\" | sed \"s/\\//%2F/g\")\n-\tif test -f wiki_tmp/\"$page_name\".mw ; then\n-\t\trm -rf wiki_tmp\n-\telse\n-\t\trm -rf wiki_tmp\n-\t\terror \"test failed: file $1 not found on wiki\"\n-\tfi\n-}\n-\n-# wiki_getallpagename\n-#\n-# Fetch the name of each page on the wiki.\n-wiki_getallpagename () {\n-\t\"$CURR_DIR\"/test-gitmw.pl getallpagename\n-}\n-\n-# wiki_getallpagecategory <category>\n-#\n-# Fetch the name of each page belonging to <category> on the wiki.\n-wiki_getallpagecategory () {\n-\t\"$CURR_DIR\"/test-gitmw.pl getallpagename \"$@\"\n-}\n-\n-# wiki_getallpage <dest_dir> [<category>]\n-#\n-# Fetch all the pages from the wiki and place them in the directory\n-# <dest_dir>.\n-# If <category> is define, then wiki_getallpage fetch the pages included\n-# in <category>.\n-wiki_getallpage () {\n-\tif test -z \"$2\";\n-\tthen\n-\t\twiki_getallpagename\n-\telse\n-\t\twiki_getallpagecategory \"$2\"\n-\tfi\n-\tmkdir -p \"$1\"\n-\twhile read -r line; do\n-\t\twiki_getpage \"$line\" $1;\n-\tdone < all.txt\n-}\n-\n-# ================= Install part =================\n-\n-error () {\n-\techo \"$@\" >&2\n-\texit 1\n-}\n-\n-# config_lighttpd\n-#\n-# Create the configuration files and the folders necessary to start lighttpd.\n-# Overwrite any existing file.\n-config_lighttpd () {\n-\tmkdir -p $WEB\n-\tmkdir -p $WEB_TMP\n-\tmkdir -p $WEB_WWW\n-\tcat > $WEB/lighttpd.conf <<EOF\n-\tserver.document-root = \"$CURR_DIR/$WEB_WWW\"\n-\tserver.port = $PORT\n-\tserver.pid-file = \"$CURR_DIR/$WEB_TMP/pid\"\n-\n-\tserver.modules = (\n-\t\"mod_rewrite\",\n-\t\"mod_redirect\",\n-\t\"mod_access\",\n-\t\"mod_accesslog\",\n-\t\"mod_fastcgi\"\n-\t)\n-\n-\tindex-file.names = (\"index.php\" , \"index.html\")\n-\n-\tmimetype.assign\t\t    = (\n-\t\".pdf\"\t\t=>\t\"application/pdf\",\n-\t\".sig\"\t\t=>\t\"application/pgp-signature\",\n-\t\".spl\"\t\t=>\t\"application/futuresplash\",\n-\t\".class\"\t=>\t\"application/octet-stream\",\n-\t\".ps\"\t\t=>\t\"application/postscript\",\n-\t\".torrent\"\t=>\t\"application/x-bittorrent\",\n-\t\".dvi\"\t\t=>\t\"application/x-dvi\",\n-\t\".gz\"\t\t=>\t\"application/x-gzip\",\n-\t\".pac\"\t\t=>\t\"application/x-ns-proxy-autoconfig\",\n-\t\".swf\"\t\t=>\t\"application/x-shockwave-flash\",\n-\t\".tar.gz\"\t=>\t\"application/x-tgz\",\n-\t\".tgz\"\t\t=>\t\"application/x-tgz\",\n-\t\".tar\"\t\t=>\t\"application/x-tar\",\n-\t\".zip\"\t\t=>\t\"application/zip\",\n-\t\".mp3\"\t\t=>\t\"audio/mpeg\",\n-\t\".m3u\"\t\t=>\t\"audio/x-mpegurl\",\n-\t\".wma\"\t\t=>\t\"audio/x-ms-wma\",\n-\t\".wax\"\t\t=>\t\"audio/x-ms-wax\",\n-\t\".ogg\"\t\t=>\t\"application/ogg\",\n-\t\".wav\"\t\t=>\t\"audio/x-wav\",\n-\t\".gif\"\t\t=>\t\"image/gif\",\n-\t\".jpg\"\t\t=>\t\"image/jpeg\",\n-\t\".jpeg\"\t\t=>\t\"image/jpeg\",\n-\t\".png\"\t\t=>\t\"image/png\",\n-\t\".xbm\"\t\t=>\t\"image/x-xbitmap\",\n-\t\".xpm\"\t\t=>\t\"image/x-xpixmap\",\n-\t\".xwd\"\t\t=>\t\"image/x-xwindowdump\",\n-\t\".css\"\t\t=>\t\"text/css\",\n-\t\".html\"\t\t=>\t\"text/html\",\n-\t\".htm\"\t\t=>\t\"text/html\",\n-\t\".js\"\t\t=>\t\"text/javascript\",\n-\t\".asc\"\t\t=>\t\"text/plain\",\n-\t\".c\"\t\t=>\t\"text/plain\",\n-\t\".cpp\"\t\t=>\t\"text/plain\",\n-\t\".log\"\t\t=>\t\"text/plain\",\n-\t\".conf\"\t\t=>\t\"text/plain\",\n-\t\".text\"\t\t=>\t\"text/plain\",\n-\t\".txt\"\t\t=>\t\"text/plain\",\n-\t\".dtd\"\t\t=>\t\"text/xml\",\n-\t\".xml\"\t\t=>\t\"text/xml\",\n-\t\".mpeg\"\t\t=>\t\"video/mpeg\",\n-\t\".mpg\"\t\t=>\t\"video/mpeg\",\n-\t\".mov\"\t\t=>\t\"video/quicktime\",\n-\t\".qt\"\t\t=>\t\"video/quicktime\",\n-\t\".avi\"\t\t=>\t\"video/x-msvideo\",\n-\t\".asf\"\t\t=>\t\"video/x-ms-asf\",\n-\t\".asx\"\t\t=>\t\"video/x-ms-asf\",\n-\t\".wmv\"\t\t=>\t\"video/x-ms-wmv\",\n-\t\".bz2\"\t\t=>\t\"application/x-bzip\",\n-\t\".tbz\"\t\t=>\t\"application/x-bzip-compressed-tar\",\n-\t\".tar.bz2\"\t=>\t\"application/x-bzip-compressed-tar\",\n-\t\"\"\t\t=>\t\"text/plain\"\n-\t)\n-\n-\tfastcgi.server = ( \".php\" =>\n-\t(\"localhost\" =>\n-\t( \"socket\" => \"$CURR_DIR/$WEB_TMP/php.socket\",\n-\t\"bin-path\" => \"$PHP_DIR/php-cgi -c $CURR_DIR/$WEB/php.ini\"\n-\n-\t)\n-\t)\n-\t)\n-EOF\n-\n-\tcat > $WEB/php.ini <<EOF\n-\tsession.save_path ='$CURR_DIR/$WEB_TMP'\n-EOF\n-}\n-\n-# start_lighttpd\n-#\n-# Start or restart daemon lighttpd. If restart, rewrite configuration files.\n-start_lighttpd () {\n-\tif test -f \"$WEB_TMP/pid\"; then\n-\t\techo \"Instance already running. Restarting...\"\n-\t\tstop_lighttpd\n-\tfi\n-\tconfig_lighttpd\n-\t\"$LIGHTTPD_DIR\"/lighttpd -f \"$WEB\"/lighttpd.conf\n-\n-\tif test $? -ne 0 ; then\n-\t\techo \"Could not execute http daemon lighttpd\"\n-\t\texit 1\n-\tfi\n-}\n-\n-# stop_lighttpd\n-#\n-# Kill daemon lighttpd and removes files and folders associated.\n-stop_lighttpd () {\n-\ttest -f \"$WEB_TMP/pid\" && kill $(cat \"$WEB_TMP/pid\")\n-}\n-\n-wiki_delete_db () {\n-\trm -rf \\\n-\t   \"$FILES_FOLDER_DB\"/* || error \"Couldn't delete $FILES_FOLDER_DB/\"\n-}\n-\n-wiki_delete_db_backup () {\n-\trm -rf \\\n-\t   \"$FILES_FOLDER_POST_INSTALL_DB\"/* || error \"Couldn't delete $FILES_FOLDER_POST_INSTALL_DB/\"\n-}\n-\n-# Install MediaWiki using its install.php script. If the database file\n-# already exists, it will be deleted.\n-install_mediawiki () {\n-\n-\tlocalsettings=\"$WIKI_DIR_INST/$WIKI_DIR_NAME/LocalSettings.php\"\n-\tif test -f \"$localsettings\"\n-\tthen\n-\t\terror \"We already installed the wiki, since $localsettings exists\" \\\n-\t\t      \"perhaps you wanted to run 'delete' first?\"\n-\tfi\n-\n-\twiki_delete_db\n-\twiki_delete_db_backup\n-\tmkdir \\\n-\t\t\"$FILES_FOLDER_DB/\" \\\n-\t\t\"$FILES_FOLDER_POST_INSTALL_DB/\"\n-\n-\tinstall_script=\"$WIKI_DIR_INST/$WIKI_DIR_NAME/maintenance/install.php\"\n-\techo \"Installing MediaWiki using $install_script. This may take some time ...\"\n-\n-\tphp \"$WIKI_DIR_INST/$WIKI_DIR_NAME/maintenance/install.php\" \\\n-\t    --server $WIKI_BASE_URL \\\n-\t    --scriptpath /wiki \\\n-\t    --lang en \\\n-\t    --dbtype sqlite \\\n-\t    --dbpath $PWD/$FILES_FOLDER_DB/ \\\n-\t    --pass \"$WIKI_PASSW\" \\\n-\t    Git-MediaWiki-Test \\\n-\t    \"$WIKI_ADMIN\" ||\n-\t\terror \"Couldn't run $install_script, see errors above. Try to run ./install-wiki.sh delete first.\"\n-\tcat <<-'EOF' >>$localsettings\n-# Custom settings added by test-gitmw-lib.sh\n-#\n-# Uploading text files is needed for\n-# t9363-mw-to-git-export-import.sh\n-$wgEnableUploads = true;\n-$wgFileExtensions[] = 'txt';\n-EOF\n-\n-\t# Copy the initially generated database file into our backup\n-\t# folder\n-\tcp -R \"$FILES_FOLDER_DB/\"* \"$FILES_FOLDER_POST_INSTALL_DB/\" ||\n-\t\terror \"Unable to copy $FILES_FOLDER_DB/* to $FILES_FOLDER_POST_INSTALL_DB/*\"\n-}\n-\n-# Install a wiki in your web server directory.\n-wiki_install () {\n-\tif test $LIGHTTPD = \"true\" ; then\n-\t\tstart_lighttpd\n-\tfi\n-\n-\t# In this part, we change directory to $TMP in order to download,\n-\t# unpack and copy the files of MediaWiki\n-\t(\n-\tmkdir -p \"$WIKI_DIR_INST/$WIKI_DIR_NAME\"\n-\tif ! test -d \"$WIKI_DIR_INST/$WIKI_DIR_NAME\"\n-\tthen\n-\t\terror \"Folder $WIKI_DIR_INST/$WIKI_DIR_NAME doesn't exist.\n-\t\tPlease create it and launch the script again.\"\n-\tfi\n-\n-\t# Fetch MediaWiki's archive if not already present in the\n-\t# download directory\n-\tmkdir -p \"$FILES_FOLDER_DOWNLOAD\"\n-\tMW_FILENAME=\"mediawiki-$MW_VERSION_MAJOR.$MW_VERSION_MINOR.tar.gz\"\n-\tcd \"$FILES_FOLDER_DOWNLOAD\"\n-\tif ! test -f $MW_FILENAME\n-\tthen\n-\t\techo \"Downloading $MW_VERSION_MAJOR.$MW_VERSION_MINOR sources ...\"\n-\t\twget \"http://download.wikimedia.org/mediawiki/$MW_VERSION_MAJOR/$MW_FILENAME\" ||\n-\t\t\terror \"Unable to download \"\\\n-\t\t\t\"http://download.wikimedia.org/mediawiki/$MW_VERSION_MAJOR/\"\\\n-\t\t\t\"$MW_FILENAME. \"\\\n-\t\t\t\"Please fix your connection and launch the script again.\"\n-\t\techo \"$MW_FILENAME downloaded in $(pwd)/;\" \\\n-\t\t     \"you can delete it later if you want.\"\n-\telse\n-\t\techo \"Reusing existing $MW_FILENAME downloaded in $(pwd)/\"\n-\tfi\n-\tarchive_abs_path=$(pwd)/$MW_FILENAME\n-\tcd \"$WIKI_DIR_INST/$WIKI_DIR_NAME/\" ||\n-\t\terror \"can't cd to $WIKI_DIR_INST/$WIKI_DIR_NAME/\"\n-\ttar xzf \"$archive_abs_path\" --strip-components=1 ||\n-\t\terror \"Unable to extract WikiMedia's files from $archive_abs_path to \"\\\n-\t\t\t\"$WIKI_DIR_INST/$WIKI_DIR_NAME\"\n-\t) || exit 1\n-\techo Extracted in \"$WIKI_DIR_INST/$WIKI_DIR_NAME\"\n-\n-\tinstall_mediawiki\n-\n-\techo \"Your wiki has been installed. You can check it at\n-\t\t$WIKI_URL\"\n-}\n-\n-# Reset the database of the wiki and the password of the admin\n-#\n-# Warning: This function must be called only in a subdirectory of t/ directory\n-wiki_reset () {\n-\t# Copy initial database of the wiki\n-\tif ! test -d \"../$FILES_FOLDER_DB\"\n-\tthen\n-\t\terror \"No wiki database at ../$FILES_FOLDER_DB, not installed yet?\"\n-\tfi\n-\tif ! test -d \"../$FILES_FOLDER_POST_INSTALL_DB\"\n-\tthen\n-\t\terror \"No wiki backup database at ../$FILES_FOLDER_POST_INSTALL_DB, failed installation?\"\n-\tfi\n-\twiki_delete_db\n-\tcp -R \"../$FILES_FOLDER_POST_INSTALL_DB/\"* \"../$FILES_FOLDER_DB/\" ||\n-\t\terror \"Can't copy ../$FILES_FOLDER_POST_INSTALL_DB/* to ../$FILES_FOLDER_DB/*\"\n-\techo \"File $FILES_FOLDER_DB/* has been reset\"\n-}\n-\n-# Delete the wiki created in the web server's directory and all its content\n-# saved in the database.\n-wiki_delete () {\n-\tif test $LIGHTTPD = \"true\"; then\n-\t\tstop_lighttpd\n-\t\trm -fr \"$WEB\"\n-\telse\n-\t\t# Delete the wiki's directory.\n-\t\trm -rf \"$WIKI_DIR_INST/$WIKI_DIR_NAME\" ||\n-\t\t\terror \"Wiki's directory $WIKI_DIR_INST/\" \\\n-\t\t\t\"$WIKI_DIR_NAME could not be deleted\"\n-\tfi\n-\twiki_delete_db\n-\twiki_delete_db_backup\n-}\ndiff --git a/contrib/mw-to-git/t/test-gitmw.pl b/contrib/mw-to-git/t/test-gitmw.pl\ndeleted file mode 100755\nindex c5d687f078a..00000000000\n--- a/contrib/mw-to-git/t/test-gitmw.pl\n+++ /dev/null\n@@ -1,223 +0,0 @@\n-#!/usr/bin/perl -w -s\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-# License: GPL v2 or later\n-\n-# Usage:\n-#       ./test-gitmw.pl <command> [argument]*\n-# Execute in terminal using the name of the function to call as first\n-# parameter, and the function's arguments as following parameters\n-#\n-# Example:\n-#     ./test-gitmw.pl \"get_page\" foo .\n-# will call <wiki_getpage> with arguments <foo> and <.>\n-#\n-# Available functions are:\n-#     \"get_page\"\n-#     \"delete_page\"\n-#     \"edit_page\"\n-#     \"getallpagename\"\n-\n-use MediaWiki::API;\n-use Getopt::Long;\n-use DateTime::Format::ISO8601;\n-use constant SLASH_REPLACEMENT => \"%2F\";\n-\n-#Parsing of the config file\n-\n-my $configfile = \"$ENV{'CURR_DIR'}/test.config\";\n-my %config;\n-open my $CONFIG, \"<\",  $configfile or die \"can't open $configfile: $!\";\n-while (<$CONFIG>)\n-{\n-\tchomp;\n-\ts/#.*//;\n-\ts/^\\s+//;\n-\ts/\\s+$//;\n-\tnext unless length;\n-\tmy ($key, $value) = split (/\\s*=\\s*/,$_, 2);\n-\t$config{$key} = $value;\n-\tlast if ($key eq 'LIGHTTPD' and $value eq 'false');\n-\tlast if ($key eq 'PORT');\n-}\n-close $CONFIG or die \"can't close $configfile: $!\";\n-\n-my $wiki_address = \"http://$config{'SERVER_ADDR'}\".\":\".\"$config{'PORT'}\";\n-my $wiki_url = \"$wiki_address/$config{'WIKI_DIR_NAME'}/api.php\";\n-my $wiki_admin = \"$config{'WIKI_ADMIN'}\";\n-my $wiki_admin_pass = \"$config{'WIKI_PASSW'}\";\n-my $mw = MediaWiki::API->new;\n-$mw->{config}->{api_url} = $wiki_url;\n-\n-\n-# wiki_login <name> <password>\n-#\n-# Logs the user with <name> and <password> in the global variable\n-# of the mediawiki $mw\n-sub wiki_login {\n-\t$mw->login( { lgname => \"$_[0]\",lgpassword => \"$_[1]\" } )\n-\t|| die \"getpage: login failed\";\n-}\n-\n-# wiki_getpage <wiki_page> <dest_path>\n-#\n-# fetch a page <wiki_page> from the wiki referenced in the global variable\n-# $mw and copies its content in directory dest_path\n-sub wiki_getpage {\n-\tmy $pagename = $_[0];\n-\tmy $destdir = $_[1];\n-\n-\tmy $page = $mw->get_page( { title => $pagename } );\n-\tif (!defined($page)) {\n-\t\tdie \"getpage: wiki does not exist\";\n-\t}\n-\n-\tmy $content = $page->{'*'};\n-\tif (!defined($content)) {\n-\t\tdie \"getpage: page does not exist\";\n-\t}\n-\n-\t$pagename=$page->{'title'};\n-\t# Replace spaces by underscore in the page name\n-\t$pagename =~ s/ /_/g;\n-\t$pagename =~ s/\\//%2F/g;\n-\topen(my $file, \">:encoding(UTF-8)\", \"$destdir/$pagename.mw\");\n-\tprint $file \"$content\";\n-\tclose ($file);\n-\n-}\n-\n-# wiki_delete_page <page_name>\n-#\n-# delete the page with name <page_name> from the wiki referenced\n-# in the global variable $mw\n-sub wiki_delete_page {\n-\tmy $pagename = $_[0];\n-\n-\tmy $exist=$mw->get_page({title => $pagename});\n-\n-\tif (defined($exist->{'*'})){\n-\t\t$mw->edit({ action => 'delete',\n-\t\t\t\ttitle => $pagename})\n-\t\t|| die $mw->{error}->{code} . \": \" . $mw->{error}->{details};\n-\t} else {\n-\t\tdie \"no page with such name found: $pagename\\n\";\n-\t}\n-}\n-\n-# wiki_editpage <wiki_page> <wiki_content> <wiki_append> [-c=<category>] [-s=<summary>]\n-#\n-# Edit a page named <wiki_page> with content <wiki_content> on the wiki\n-# referenced with the global variable $mw\n-# If <wiki_append> == true : append <wiki_content> at the end of the actual\n-# content of the page <wiki_page>\n-# If <wik_page> doesn't exist, that page is created with the <wiki_content>\n-sub wiki_editpage {\n-\tmy $wiki_page = $_[0];\n-\tmy $wiki_content = $_[1];\n-\tmy $wiki_append = $_[2];\n-\tmy $summary = \"\";\n-\tmy ($summ, $cat) = ();\n-\tGetOptions('s=s' => \\$summ, 'c=s' => \\$cat);\n-\n-\tmy $append = 0;\n-\tif (defined($wiki_append) && $wiki_append eq 'true') {\n-\t\t$append=1;\n-\t}\n-\n-\tmy $previous_text =\"\";\n-\n-\tif ($append) {\n-\t\tmy $ref = $mw->get_page( { title => $wiki_page } );\n-\t\t$previous_text = $ref->{'*'};\n-\t}\n-\n-\tmy $text = $wiki_content;\n-\tif (defined($previous_text)) {\n-\t\t$text=\"$previous_text$text\";\n-\t}\n-\n-\t# Eventually, add this page to a category.\n-\tif (defined($cat)) {\n-\t\tmy $category_name=\"[[Category:$cat]]\";\n-\t\t$text=\"$text\\n $category_name\";\n-\t}\n-\tif(defined($summ)){\n-\t\t$summary=$summ;\n-\t}\n-\n-\t$mw->edit( { action => 'edit', title => $wiki_page, summary => $summary, text => \"$text\"} );\n-}\n-\n-# wiki_getallpagename [<category>]\n-#\n-# Fetch all pages of the wiki referenced by the global variable $mw\n-# and print the names of each one in the file all.txt with a new line\n-# (\"\\n\") between these.\n-# If the argument <category> is defined, then this function get only the pages\n-# belonging to <category>.\n-sub wiki_getallpagename {\n-\t# fetch the pages of the wiki\n-\tif (defined($_[0])) {\n-\t\tmy $mw_pages = $mw->list ( { action => 'query',\n-\t\t\t\tlist => 'categorymembers',\n-\t\t\t\tcmtitle => \"Category:$_[0]\",\n-\t\t\t\tcmnamespace => 0,\n-\t\t\t\tcmlimit => 500 },\n-\t\t)\n-\t\t|| die $mw->{error}->{code}.\": \".$mw->{error}->{details};\n-\t\topen(my $file, \">:encoding(UTF-8)\", \"all.txt\");\n-\t\tforeach my $page (@{$mw_pages}) {\n-\t\t\tprint $file \"$page->{title}\\n\";\n-\t\t}\n-\t\tclose ($file);\n-\n-\t} else {\n-\t\tmy $mw_pages = $mw->list({\n-\t\t\t\taction => 'query',\n-\t\t\t\tlist => 'allpages',\n-\t\t\t\taplimit => 500,\n-\t\t\t})\n-\t\t|| die $mw->{error}->{code}.\": \".$mw->{error}->{details};\n-\t\topen(my $file, \">:encoding(UTF-8)\", \"all.txt\");\n-\t\tforeach my $page (@{$mw_pages}) {\n-\t\t\tprint $file \"$page->{title}\\n\";\n-\t\t}\n-\t\tclose ($file);\n-\t}\n-}\n-\n-sub wiki_upload_file {\n-\tmy $file_name = $_[0];\n-\tmy $resultat = $mw->edit ( {\n-\t\taction => 'upload',\n-\t\tfilename => $file_name,\n-\t\tcomment => 'upload a file',\n-\t\tfile => [ $file_name ],\n-\t\tignorewarnings=>1,\n-\t}, {\n-\t\tskip_encoding => 1\n-\t} ) || die $mw->{error}->{code} . ' : ' . $mw->{error}->{details};\n-}\n-\n-\n-\n-# Main part of this script: parse the command line arguments\n-# and select which function to execute\n-my $fct_to_call = shift;\n-\n-wiki_login($wiki_admin, $wiki_admin_pass);\n-\n-my %functions_to_call = (\n-\tupload_file    => \\&wiki_upload_file,\n-\tget_page       => \\&wiki_getpage,\n-\tdelete_page    => \\&wiki_delete_page,\n-\tedit_page      => \\&wiki_editpage,\n-\tgetallpagename => \\&wiki_getallpagename,\n-);\n-die \"$0 ERROR: wrong argument\" unless exists $functions_to_call{$fct_to_call};\n-$functions_to_call{$fct_to_call}->(map { utf8::decode($_); $_ } @ARGV);\ndiff --git a/contrib/mw-to-git/t/test.config b/contrib/mw-to-git/t/test.config\ndeleted file mode 100644\nindex ed10b3e4a47..00000000000\n--- a/contrib/mw-to-git/t/test.config\n+++ /dev/null\n@@ -1,40 +0,0 @@\n-# Name of the web server's directory dedicated to the wiki is WIKI_DIR_NAME\n-WIKI_DIR_NAME=wiki\n-\n-# Login and password of the wiki's admin\n-WIKI_ADMIN=WikiAdmin\n-WIKI_PASSW=AdminPass1\n-\n-# Address of the web server\n-SERVER_ADDR=localhost\n-\n-# If LIGHTTPD is not set to true, the script will use the default\n-# web server running in WIKI_DIR_INST.\n-WIKI_DIR_INST=/var/www\n-\n-# If LIGHTTPD is set to true, the script will use Lighttpd to run\n-# the wiki.\n-LIGHTTPD=true\n-\n-# The variables below are useful only if LIGHTTPD is set to true.\n-PORT=1234\n-PHP_DIR=/usr/bin\n-LIGHTTPD_DIR=/usr/sbin\n-WEB=WEB\n-WEB_TMP=$WEB/tmp\n-WEB_WWW=$WEB/www\n-\n-# Where our configuration for the wiki is located\n-FILES_FOLDER=mediawiki\n-FILES_FOLDER_DOWNLOAD=$FILES_FOLDER/download\n-FILES_FOLDER_DB=$FILES_FOLDER/db\n-FILES_FOLDER_POST_INSTALL_DB=$FILES_FOLDER/post-install-db\n-\n-# The variables below are used by the script to install a wiki.\n-# You should not modify these unless you are modifying the script itself.\n-# tested versions: 1.19.X -> 1.21.1 -> 1.34.2\n-#\n-# See https://www.mediawiki.org/wiki/Download for what the latest\n-# version is.\n-MW_VERSION_MAJOR=1.34\n-MW_VERSION_MINOR=2\n\n-- \n2.49.0.1045.g170613ef41.dirty\n\n"},{"id":"517350","messageId":"20250506-pks-contrib-spring-cleanup-v1-8-e6d5ddd79a72@pks.im","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","subject":"[PATCH 08/10] contrib: remove \"git-resurrect.sh\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-06T14:12:41Z","receivedAt":"2025-05-06T14:12:48Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"git-resurrect.sh\" script can be used to find traces of a branch tip\nin the reflog and resurrect that branch. Despite a couple of global\ncleanups, the script hasn't seen any activity since it was introduced in\ne1ff064e1bf (contrib git-resurrect: find traces of a branch name and\nresurrect it, 2009-02-04).\n\nRemove it.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/git-resurrect.sh | 181 -----------------------------------------------\n 1 file changed, 181 deletions(-)\n\ndiff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh\ndeleted file mode 100755\nindex d843df3afd0..00000000000\n--- a/contrib/git-resurrect.sh\n+++ /dev/null\n@@ -1,181 +0,0 @@\n-#!/bin/sh\n-\n-USAGE=\"[-a] [-r] [-m] [-t] [-n] [-b <newname>] <name>\"\n-LONG_USAGE=\"git-resurrect attempts to find traces of a branch tip\n-called <name>, and tries to resurrect it.  Currently, the reflog is\n-searched for checkout messages, and with -r also merge messages.  With\n--m and -t, the history of all refs is scanned for Merge <name> into\n-other/Merge <other> into <name> (respectively) commit subjects, which\n-is rather slow but allows you to resurrect other people's topic\n-branches.\"\n-\n-OPTIONS_KEEPDASHDASH=\n-OPTIONS_STUCKLONG=\n-OPTIONS_SPEC=\"\\\n-git resurrect $USAGE\n---\n-b,branch=            save branch as <newname> instead of <name>\n-a,all                same as -l -r -m -t\n-k,keep-going         full rev-list scan (instead of first match)\n-l,reflog             scan reflog for checkouts (enabled by default)\n-r,reflog-merges      scan for merges recorded in reflog\n-m,merges             scan for merges into other branches (slow)\n-t,merge-targets      scan for merges of other branches into <name>\n-n,dry-run            don't recreate the branch\"\n-\n-. git-sh-setup\n-\n-search_reflog () {\n-\tsed -ne 's~^\\([^ ]*\\) .*\tcheckout: moving from '\"$1\"' .*~\\1~p' \\\n-\t\t< \"$GIT_DIR\"/logs/HEAD\n-}\n-\n-search_reflog_merges () {\n-\tgit rev-parse $(\n-\t\tsed -ne 's~^[^ ]* \\([^ ]*\\) .*\tmerge '\"$1\"':.*~\\1^2~p' \\\n-\t\t\t< \"$GIT_DIR\"/logs/HEAD\n-\t)\n-}\n-\n-oid_pattern=$(git hash-object --stdin </dev/null | sed -e 's/./[0-9a-f]/g')\n-\n-search_merges () {\n-\tgit rev-list --all --grep=\"Merge branch '$1'\" \\\n-\t\t--pretty=tformat:\"%P %s\" |\n-\tsed -ne \"/^$oid_pattern \\($oid_pattern\\) Merge .*/ {s//\\1/p;$early_exit}\"\n-}\n-\n-search_merge_targets () {\n-\tgit rev-list --all --grep=\"Merge branch '[^']*' into $branch\\$\" \\\n-\t\t--pretty=tformat:\"%H %s\" --all |\n-\tsed -ne \"/^\\($oid_pattern\\) Merge .*/ {s//\\1/p;$early_exit} \"\n-}\n-\n-dry_run=\n-early_exit=q\n-scan_reflog=t\n-scan_reflog_merges=\n-scan_merges=\n-scan_merge_targets=\n-new_name=\n-\n-while test \"$#\" != 0; do\n-\tcase \"$1\" in\n-\t    -b|--branch)\n-\t\tshift\n-\t\tnew_name=\"$1\"\n-\t\t;;\n-\t    -n|--dry-run)\n-\t\tdry_run=t\n-\t\t;;\n-\t    --no-dry-run)\n-\t\tdry_run=\n-\t\t;;\n-\t    -k|--keep-going)\n-\t\tearly_exit=\n-\t\t;;\n-\t    --no-keep-going)\n-\t\tearly_exit=q\n-\t\t;;\n-\t    -m|--merges)\n-\t\tscan_merges=t\n-\t\t;;\n-\t    --no-merges)\n-\t\tscan_merges=\n-\t\t;;\n-\t    -l|--reflog)\n-\t\tscan_reflog=t\n-\t\t;;\n-\t    --no-reflog)\n-\t\tscan_reflog=\n-\t\t;;\n-\t    -r|--reflog_merges)\n-\t\tscan_reflog_merges=t\n-\t\t;;\n-\t    --no-reflog_merges)\n-\t\tscan_reflog_merges=\n-\t\t;;\n-\t    -t|--merge-targets)\n-\t\tscan_merge_targets=t\n-\t\t;;\n-\t    --no-merge-targets)\n-\t\tscan_merge_targets=\n-\t\t;;\n-\t    -a|--all)\n-\t\tscan_reflog=t\n-\t\tscan_reflog_merges=t\n-\t\tscan_merges=t\n-\t\tscan_merge_targets=t\n-\t\t;;\n-\t    --)\n-\t\tshift\n-\t\tbreak\n-\t\t;;\n-\t    *)\n-\t\tusage\n-\t\t;;\n-\tesac\n-\tshift\n-done\n-\n-test \"$#\" = 1 || usage\n-\n-all_strategies=\"$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets\"\n-if test -z \"$all_strategies\"; then\n-\tdie \"must enable at least one of -lrmt\"\n-fi\n-\n-branch=\"$1\"\n-test -z \"$new_name\" && new_name=\"$branch\"\n-\n-if test ! -z \"$scan_reflog\"; then\n-\tif test -r \"$GIT_DIR\"/logs/HEAD; then\n-\t\tcandidates=\"$(search_reflog $branch)\"\n-\telse\n-\t\tdie 'reflog scanning requested, but' \\\n-\t\t\t'$GIT_DIR/logs/HEAD not readable'\n-\tfi\n-fi\n-if test ! -z \"$scan_reflog_merges\"; then\n-\tif test -r \"$GIT_DIR\"/logs/HEAD; then\n-\t\tcandidates=\"$candidates $(search_reflog_merges $branch)\"\n-\telse\n-\t\tdie 'reflog scanning requested, but' \\\n-\t\t\t'$GIT_DIR/logs/HEAD not readable'\n-\tfi\n-fi\n-if test ! -z \"$scan_merges\"; then\n-\tcandidates=\"$candidates $(search_merges $branch)\"\n-fi\n-if test ! -z \"$scan_merge_targets\"; then\n-\tcandidates=\"$candidates $(search_merge_targets $branch)\"\n-fi\n-\n-candidates=\"$(git rev-parse $candidates | sort -u)\"\n-\n-if test -z \"$candidates\"; then\n-\thint=\n-\ttest \"z$all_strategies\" != \"ztttt\" \\\n-\t\t&& hint=\" (maybe try again with -a)\"\n-\tdie \"no candidates for $branch found$hint\"\n-fi\n-\n-echo \"** Candidates for $branch **\"\n-for cmt in $candidates; do\n-\tgit --no-pager log --pretty=tformat:\"%ct:%h [%cr] %s\" --abbrev-commit -1 $cmt\n-done \\\n-| sort -n | cut -d: -f2-\n-\n-newest=\"$(git rev-list -1 $candidates)\"\n-if test ! -z \"$dry_run\"; then\n-\tprintf \"** Most recent: \"\n-\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n-elif ! git rev-parse --verify --quiet $new_name >/dev/null; then\n-\tprintf \"** Restoring $new_name to \"\n-\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n-\tgit branch $new_name $newest\n-else\n-\tprintf \"Most recent: \"\n-\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n-\techo \"** $new_name already exists, doing nothing\"\n-fi\n\n-- \n2.49.0.1045.g170613ef41.dirty\n\n"},{"id":"517351","messageId":"20250506-pks-contrib-spring-cleanup-v1-9-e6d5ddd79a72@pks.im","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","subject":"[PATCH 09/10] contrib: remove \"emacs\" directory","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-06T14:12:42Z","receivedAt":"2025-05-06T14:12:49Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"While the \"emacs/\" directory still exists, all of its code has been\nreplaced with stubs in 6d5ed4836db (git{,-blame}.el: remove old\nbitrotting Emacs code, 2018-04-11). Instead, the recommendation is to\nuse Emacs' own vc-annotate mode.\n\nRemove the code altogether.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/emacs/README       | 33 ---------------------------------\n contrib/emacs/git-blame.el |  6 ------\n contrib/emacs/git.el       |  6 ------\n 3 files changed, 45 deletions(-)\n\ndiff --git a/contrib/emacs/README b/contrib/emacs/README\ndeleted file mode 100644\nindex 977a16f1e33..00000000000\n--- a/contrib/emacs/README\n+++ /dev/null\n@@ -1,33 +0,0 @@\n-This directory used to contain various modules for Emacs support.\n-\n-These were added shortly after Git was first released. Since then\n-Emacs's own support for Git got better than what was offered by these\n-modes. There are also popular 3rd-party Git modes such as Magit which\n-offer replacements for these.\n-\n-The following modules were available, and can be dug up from the Git\n-history:\n-\n-* git.el:\n-\n-  Wrapper for \"git status\" that provided access to other git commands.\n-\n-  Modern alternatives to this include Magit, and VC mode that ships\n-  with Emacs.\n-\n-* git-blame.el:\n-\n-  A wrapper for \"git blame\" written before Emacs's own vc-annotate\n-  mode learned to invoke git-blame, which can be done via C-x v g.\n-\n-* vc-git.el:\n-\n-  This file used to contain the VC-mode backend for git, but it is no\n-  longer distributed with git. It is now maintained as part of Emacs\n-  and included in standard Emacs distributions starting from version\n-  22.2.\n-\n-  If you have an earlier Emacs version, upgrading to Emacs 22 is\n-  recommended, since the VC mode in older Emacs is not generic enough\n-  to be able to support git in a reasonable manner, and no attempt has\n-  been made to backport vc-git.el.\ndiff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el\ndeleted file mode 100644\nindex 6a8a2b8ff19..00000000000\n--- a/contrib/emacs/git-blame.el\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-(error \"git-blame.el no longer ships with git. It's recommended\n-to replace its use with Emacs's own vc-annotate. See\n-contrib/emacs/README in git's\n-sources (https://github.com/git/git/blob/master/contrib/emacs/README)\n-for more info on suggested alternatives and for why this\n-happened.\")\ndiff --git a/contrib/emacs/git.el b/contrib/emacs/git.el\ndeleted file mode 100644\nindex 03f926281fb..00000000000\n--- a/contrib/emacs/git.el\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-(error \"git.el no longer ships with git. It's recommended to\n-replace its use with Magit, or simply delete references to git.el\n-in your initialization file(s). See contrib/emacs/README in git's\n-sources (https://github.com/git/git/blob/master/contrib/emacs/README)\n-for suggested alternatives and for why this happened. Emacs's own\n-VC mode and Magit are viable alternatives.\")\n\n-- \n2.49.0.1045.g170613ef41.dirty\n\n"},{"id":"517352","messageId":"20250506-pks-contrib-spring-cleanup-v1-7-e6d5ddd79a72@pks.im","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","subject":"[PATCH 07/10] contrib: remove \"persistent-https\" remote helper","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-06T14:12:40Z","receivedAt":"2025-05-06T14:12:49Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"persistent-https\" remote helper supposedly speeds up SSL operations\nby running a daemon that keeps a connection open to a remote server. It\nis effectively unmaintained nowadays: the last time it received an\nupdate was in accb613afd2 (contrib/persistent-https: use Git version for\nbuild label, 2016-07-20) and its parent commits to make it compile with\nGo 1.7+.\n\nThis Go toolchain is somewhat dated by now though and unsupported. The\noldest still-supported toolchain is Go 1.23, which was released in\nAugust 2024. It is not possible to compile the remote helper with that\nGo version anymore:\n\n    $ go version\n    go version go1.23.8 linux/amd64\n    $ make\n    case $(go version) in \\\n    \"go version go\"1.[0-5].*) EQ=\" \" ;; *) EQ=\"=\" ;; esac && \\\n    go build -o git-remote-persistent-https \\\n            -ldflags \"-X main._BUILD_EMBED_LABEL${EQ}GIT_VERSION=2.49.0.943.g965a70ebf62\"\n    go: cannot find main module, but found .git/config in /home/pks/Development/git\n            to create a module there, run:\n            cd ../.. && go mod init\n    make: *** [Makefile:31: git-remote-persistent-https] Error 1\n\nThe problem is that modern Go toolchains require a \"go.mod\" file, but we\ndon't have any such files. This requirement exists since quite a while\nalready, so it's clear that nobody has tried to use this remote helper\nanytime recent.\n\nRemove the remote helper.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/persistent-https/LICENSE   | 202 -------------------------------------\n contrib/persistent-https/Makefile  |  43 --------\n contrib/persistent-https/README    |  72 -------------\n contrib/persistent-https/client.go | 189 ----------------------------------\n contrib/persistent-https/main.go   |  82 ---------------\n contrib/persistent-https/proxy.go  | 190 ----------------------------------\n contrib/persistent-https/socket.go |  97 ------------------\n 7 files changed, 875 deletions(-)\n\ndiff --git a/contrib/persistent-https/LICENSE b/contrib/persistent-https/LICENSE\ndeleted file mode 100644\nindex d6456956733..00000000000\n--- a/contrib/persistent-https/LICENSE\n+++ /dev/null\n@@ -1,202 +0,0 @@\n-\n-                                 Apache License\n-                           Version 2.0, January 2004\n-                        http://www.apache.org/licenses/\n-\n-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n-\n-   1. Definitions.\n-\n-      \"License\" shall mean the terms and conditions for use, reproduction,\n-      and distribution as defined by Sections 1 through 9 of this document.\n-\n-      \"Licensor\" shall mean the copyright owner or entity authorized by\n-      the copyright owner that is granting the License.\n-\n-      \"Legal Entity\" shall mean the union of the acting entity and all\n-      other entities that control, are controlled by, or are under common\n-      control with that entity. For the purposes of this definition,\n-      \"control\" means (i) the power, direct or indirect, to cause the\n-      direction or management of such entity, whether by contract or\n-      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n-      outstanding shares, or (iii) beneficial ownership of such entity.\n-\n-      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n-      exercising permissions granted by this License.\n-\n-      \"Source\" form shall mean the preferred form for making modifications,\n-      including but not limited to software source code, documentation\n-      source, and configuration files.\n-\n-      \"Object\" form shall mean any form resulting from mechanical\n-      transformation or translation of a Source form, including but\n-      not limited to compiled object code, generated documentation,\n-      and conversions to other media types.\n-\n-      \"Work\" shall mean the work of authorship, whether in Source or\n-      Object form, made available under the License, as indicated by a\n-      copyright notice that is included in or attached to the work\n-      (an example is provided in the Appendix below).\n-\n-      \"Derivative Works\" shall mean any work, whether in Source or Object\n-      form, that is based on (or derived from) the Work and for which the\n-      editorial revisions, annotations, elaborations, or other modifications\n-      represent, as a whole, an original work of authorship. For the purposes\n-      of this License, Derivative Works shall not include works that remain\n-      separable from, or merely link (or bind by name) to the interfaces of,\n-      the Work and Derivative Works thereof.\n-\n-      \"Contribution\" shall mean any work of authorship, including\n-      the original version of the Work and any modifications or additions\n-      to that Work or Derivative Works thereof, that is intentionally\n-      submitted to Licensor for inclusion in the Work by the copyright owner\n-      or by an individual or Legal Entity authorized to submit on behalf of\n-      the copyright owner. For the purposes of this definition, \"submitted\"\n-      means any form of electronic, verbal, or written communication sent\n-      to the Licensor or its representatives, including but not limited to\n-      communication on electronic mailing lists, source code control systems,\n-      and issue tracking systems that are managed by, or on behalf of, the\n-      Licensor for the purpose of discussing and improving the Work, but\n-      excluding communication that is conspicuously marked or otherwise\n-      designated in writing by the copyright owner as \"Not a Contribution.\"\n-\n-      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n-      on behalf of whom a Contribution has been received by Licensor and\n-      subsequently incorporated within the Work.\n-\n-   2. Grant of Copyright License. Subject to the terms and conditions of\n-      this License, each Contributor hereby grants to You a perpetual,\n-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n-      copyright license to reproduce, prepare Derivative Works of,\n-      publicly display, publicly perform, sublicense, and distribute the\n-      Work and such Derivative Works in Source or Object form.\n-\n-   3. Grant of Patent License. Subject to the terms and conditions of\n-      this License, each Contributor hereby grants to You a perpetual,\n-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n-      (except as stated in this section) patent license to make, have made,\n-      use, offer to sell, sell, import, and otherwise transfer the Work,\n-      where such license applies only to those patent claims licensable\n-      by such Contributor that are necessarily infringed by their\n-      Contribution(s) alone or by combination of their Contribution(s)\n-      with the Work to which such Contribution(s) was submitted. If You\n-      institute patent litigation against any entity (including a\n-      cross-claim or counterclaim in a lawsuit) alleging that the Work\n-      or a Contribution incorporated within the Work constitutes direct\n-      or contributory patent infringement, then any patent licenses\n-      granted to You under this License for that Work shall terminate\n-      as of the date such litigation is filed.\n-\n-   4. Redistribution. You may reproduce and distribute copies of the\n-      Work or Derivative Works thereof in any medium, with or without\n-      modifications, and in Source or Object form, provided that You\n-      meet the following conditions:\n-\n-      (a) You must give any other recipients of the Work or\n-          Derivative Works a copy of this License; and\n-\n-      (b) You must cause any modified files to carry prominent notices\n-          stating that You changed the files; and\n-\n-      (c) You must retain, in the Source form of any Derivative Works\n-          that You distribute, all copyright, patent, trademark, and\n-          attribution notices from the Source form of the Work,\n-          excluding those notices that do not pertain to any part of\n-          the Derivative Works; and\n-\n-      (d) If the Work includes a \"NOTICE\" text file as part of its\n-          distribution, then any Derivative Works that You distribute must\n-          include a readable copy of the attribution notices contained\n-          within such NOTICE file, excluding those notices that do not\n-          pertain to any part of the Derivative Works, in at least one\n-          of the following places: within a NOTICE text file distributed\n-          as part of the Derivative Works; within the Source form or\n-          documentation, if provided along with the Derivative Works; or,\n-          within a display generated by the Derivative Works, if and\n-          wherever such third-party notices normally appear. The contents\n-          of the NOTICE file are for informational purposes only and\n-          do not modify the License. You may add Your own attribution\n-          notices within Derivative Works that You distribute, alongside\n-          or as an addendum to the NOTICE text from the Work, provided\n-          that such additional attribution notices cannot be construed\n-          as modifying the License.\n-\n-      You may add Your own copyright statement to Your modifications and\n-      may provide additional or different license terms and conditions\n-      for use, reproduction, or distribution of Your modifications, or\n-      for any such Derivative Works as a whole, provided Your use,\n-      reproduction, and distribution of the Work otherwise complies with\n-      the conditions stated in this License.\n-\n-   5. Submission of Contributions. Unless You explicitly state otherwise,\n-      any Contribution intentionally submitted for inclusion in the Work\n-      by You to the Licensor shall be under the terms and conditions of\n-      this License, without any additional terms or conditions.\n-      Notwithstanding the above, nothing herein shall supersede or modify\n-      the terms of any separate license agreement you may have executed\n-      with Licensor regarding such Contributions.\n-\n-   6. Trademarks. This License does not grant permission to use the trade\n-      names, trademarks, service marks, or product names of the Licensor,\n-      except as required for reasonable and customary use in describing the\n-      origin of the Work and reproducing the content of the NOTICE file.\n-\n-   7. Disclaimer of Warranty. Unless required by applicable law or\n-      agreed to in writing, Licensor provides the Work (and each\n-      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n-      implied, including, without limitation, any warranties or conditions\n-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n-      PARTICULAR PURPOSE. You are solely responsible for determining the\n-      appropriateness of using or redistributing the Work and assume any\n-      risks associated with Your exercise of permissions under this License.\n-\n-   8. Limitation of Liability. In no event and under no legal theory,\n-      whether in tort (including negligence), contract, or otherwise,\n-      unless required by applicable law (such as deliberate and grossly\n-      negligent acts) or agreed to in writing, shall any Contributor be\n-      liable to You for damages, including any direct, indirect, special,\n-      incidental, or consequential damages of any character arising as a\n-      result of this License or out of the use or inability to use the\n-      Work (including but not limited to damages for loss of goodwill,\n-      work stoppage, computer failure or malfunction, or any and all\n-      other commercial damages or losses), even if such Contributor\n-      has been advised of the possibility of such damages.\n-\n-   9. Accepting Warranty or Additional Liability. While redistributing\n-      the Work or Derivative Works thereof, You may choose to offer,\n-      and charge a fee for, acceptance of support, warranty, indemnity,\n-      or other liability obligations and/or rights consistent with this\n-      License. However, in accepting such obligations, You may act only\n-      on Your own behalf and on Your sole responsibility, not on behalf\n-      of any other Contributor, and only if You agree to indemnify,\n-      defend, and hold each Contributor harmless for any liability\n-      incurred by, or claims asserted against, such Contributor by reason\n-      of your accepting any such warranty or additional liability.\n-\n-   END OF TERMS AND CONDITIONS\n-\n-   APPENDIX: How to apply the Apache License to your work.\n-\n-      To apply the Apache License to your work, attach the following\n-      boilerplate notice, with the fields enclosed by brackets \"[]\"\n-      replaced with your own identifying information. (Don't include\n-      the brackets!)  The text should be enclosed in the appropriate\n-      comment syntax for the file format. We also recommend that a\n-      file or class name and description of purpose be included on the\n-      same \"printed page\" as the copyright notice for easier\n-      identification within third-party archives.\n-\n-   Copyright [yyyy] [name of copyright owner]\n-\n-   Licensed under the Apache License, Version 2.0 (the \"License\");\n-   you may not use this file except in compliance with the License.\n-   You may obtain a copy of the License at\n-\n-       http://www.apache.org/licenses/LICENSE-2.0\n-\n-   Unless required by applicable law or agreed to in writing, software\n-   distributed under the License is distributed on an \"AS IS\" BASIS,\n-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-   See the License for the specific language governing permissions and\n-   limitations under the License.\ndiff --git a/contrib/persistent-https/Makefile b/contrib/persistent-https/Makefile\ndeleted file mode 100644\nindex 691737e76be..00000000000\n--- a/contrib/persistent-https/Makefile\n+++ /dev/null\n@@ -1,43 +0,0 @@\n-# Copyright 2012 Google Inc. All Rights Reserved.\n-#\n-# Licensed under the Apache License, Version 2.0 (the \"License\");\n-# you may not use this file except in compliance with the License.\n-# You may obtain a copy of the License at\n-#\n-#     http://www.apache.org/licenses/LICENSE-2.0\n-#\n-# Unless required by applicable law or agreed to in writing, software\n-# distributed under the License is distributed on an \"AS IS\" BASIS,\n-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-# See the License for the specific language governing permissions and\n-# limitations under the License.\n-\n-# The default target of this Makefile is...\n-all::\n-\n-BUILD_LABEL=$(shell cut -d\" \" -f3 ../../GIT-VERSION-FILE)\n-TAR_OUT=$(shell go env GOOS)_$(shell go env GOARCH).tar.gz\n-\n-all:: git-remote-persistent-https git-remote-persistent-https--proxy \\\n-\tgit-remote-persistent-http\n-\n-git-remote-persistent-https--proxy: git-remote-persistent-https\n-\tln -f -s git-remote-persistent-https git-remote-persistent-https--proxy\n-\n-git-remote-persistent-http: git-remote-persistent-https\n-\tln -f -s git-remote-persistent-https git-remote-persistent-http\n-\n-git-remote-persistent-https:\n-\tcase $$(go version) in \\\n-\t\"go version go\"1.[0-5].*) EQ=\" \" ;; *) EQ=\"=\" ;; esac && \\\n-\tgo build -o git-remote-persistent-https \\\n-\t\t-ldflags \"-X main._BUILD_EMBED_LABEL$${EQ}$(BUILD_LABEL)\"\n-\n-clean:\n-\trm -f git-remote-persistent-http* *.tar.gz\n-\n-tar: clean all\n-\t@chmod 555 git-remote-persistent-https\n-\t@tar -czf $(TAR_OUT) git-remote-persistent-http* README LICENSE\n-\t@echo\n-\t@echo \"Created $(TAR_OUT)\"\ndiff --git a/contrib/persistent-https/README b/contrib/persistent-https/README\ndeleted file mode 100644\nindex 7c4cd8d257d..00000000000\n--- a/contrib/persistent-https/README\n+++ /dev/null\n@@ -1,72 +0,0 @@\n-git-remote-persistent-https\n-\n-The git-remote-persistent-https binary speeds up SSL operations\n-by running a daemon job (git-remote-persistent-https--proxy) that\n-keeps a connection open to a server.\n-\n-\n-PRE-BUILT BINARIES\n-\n-Darwin amd64:\n-https://commondatastorage.googleapis.com/git-remote-persistent-https/darwin_amd64.tar.gz\n-\n-Linux amd64:\n-https://commondatastorage.googleapis.com/git-remote-persistent-https/linux_amd64.tar.gz\n-\n-\n-INSTALLING\n-\n-Move all of the git-remote-persistent-http* binaries to a directory\n-in PATH.\n-\n-\n-USAGE\n-\n-HTTPS requests can be delegated to the proxy by using the\n-\"persistent-https\" scheme, e.g.\n-\n-git clone persistent-https://kernel.googlesource.com/pub/scm/git/git\n-\n-Likewise, .gitconfig can be updated as follows to rewrite https urls\n-to use persistent-https:\n-\n-[url \"persistent-https\"]\n-\tinsteadof = https\n-[url \"persistent-http\"]\n-\tinsteadof = http\n-\n-You may also want to allow the use of the persistent-https helper for\n-submodule URLs (since any https URLs pointing to submodules will be\n-rewritten, and Git's out-of-the-box defaults forbid submodules from\n-using unknown remote helpers):\n-\n-[protocol \"persistent-https\"]\n-\tallow = always\n-[protocol \"persistent-http\"]\n-\tallow = always\n-\n-\n-#####################################################################\n-# BUILDING FROM SOURCE\n-#####################################################################\n-\n-LOCATION\n-\n-The source is available in the contrib/persistent-https directory of\n-the Git source repository. The Git source repository is available at\n-git://git.kernel.org/pub/scm/git/git.git/\n-https://kernel.googlesource.com/pub/scm/git/git\n-\n-\n-PREREQUISITES\n-\n-The code is written in Go (http://golang.org/) and the Go compiler is\n-required. Currently, the compiler must be built and installed from tip\n-of source, in order to include a fix in the reverse http proxy:\n-http://code.google.com/p/go/source/detail?r=a615b796570a2cd8591884767a7d67ede74f6648\n-\n-\n-BUILDING\n-\n-Run \"make\" to build the binaries. See the section on\n-INSTALLING above.\ndiff --git a/contrib/persistent-https/client.go b/contrib/persistent-https/client.go\ndeleted file mode 100644\nindex 71125b5832d..00000000000\n--- a/contrib/persistent-https/client.go\n+++ /dev/null\n@@ -1,189 +0,0 @@\n-// Copyright 2012 Google Inc. All Rights Reserved.\n-//\n-// Licensed under the Apache License, Version 2.0 (the \"License\");\n-// you may not use this file except in compliance with the License.\n-// You may obtain a copy of the License at\n-//\n-//     http://www.apache.org/licenses/LICENSE-2.0\n-//\n-// Unless required by applicable law or agreed to in writing, software\n-// distributed under the License is distributed on an \"AS IS\" BASIS,\n-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-// See the License for the specific language governing permissions and\n-// limitations under the License.\n-\n-package main\n-\n-import (\n-\t\"bufio\"\n-\t\"errors\"\n-\t\"fmt\"\n-\t\"net\"\n-\t\"net/url\"\n-\t\"os\"\n-\t\"os/exec\"\n-\t\"strings\"\n-\t\"syscall\"\n-\t\"time\"\n-)\n-\n-type Client struct {\n-\tProxyBin string\n-\tArgs     []string\n-\n-\tinsecure bool\n-}\n-\n-func (c *Client) Run() error {\n-\tif err := c.resolveArgs(); err != nil {\n-\t\treturn fmt.Errorf(\"resolveArgs() got error: %v\", err)\n-\t}\n-\n-\t// Connect to the proxy.\n-\tuconn, hconn, addr, err := c.connect()\n-\tif err != nil {\n-\t\treturn fmt.Errorf(\"connect() got error: %v\", err)\n-\t}\n-\t// Keep the unix socket connection open for the duration of the request.\n-\tdefer uconn.Close()\n-\t// Keep a connection to the HTTP server open, so no other user can\n-\t// bind on the same address so long as the process is running.\n-\tdefer hconn.Close()\n-\n-\t// Start the git-remote-http subprocess.\n-\tcargs := []string{\"-c\", fmt.Sprintf(\"http.proxy=%v\", addr), \"remote-http\"}\n-\tcargs = append(cargs, c.Args...)\n-\tcmd := exec.Command(\"git\", cargs...)\n-\n-\tfor _, v := range os.Environ() {\n-\t\tif !strings.HasPrefix(v, \"GIT_PERSISTENT_HTTPS_SECURE=\") {\n-\t\t\tcmd.Env = append(cmd.Env, v)\n-\t\t}\n-\t}\n-\t// Set the GIT_PERSISTENT_HTTPS_SECURE environment variable when\n-\t// the proxy is using a SSL connection.  This allows credential helpers\n-\t// to identify secure proxy connections, despite being passed an HTTP\n-\t// scheme.\n-\tif !c.insecure {\n-\t\tcmd.Env = append(cmd.Env, \"GIT_PERSISTENT_HTTPS_SECURE=1\")\n-\t}\n-\n-\tcmd.Stdin = os.Stdin\n-\tcmd.Stdout = os.Stdout\n-\tcmd.Stderr = os.Stderr\n-\tif err := cmd.Run(); err != nil {\n-\t\tif eerr, ok := err.(*exec.ExitError); ok {\n-\t\t\tif stat, ok := eerr.ProcessState.Sys().(syscall.WaitStatus); ok && stat.ExitStatus() != 0 {\n-\t\t\t\tos.Exit(stat.ExitStatus())\n-\t\t\t}\n-\t\t}\n-\t\treturn fmt.Errorf(\"git-remote-http subprocess got error: %v\", err)\n-\t}\n-\treturn nil\n-}\n-\n-func (c *Client) connect() (uconn net.Conn, hconn net.Conn, addr string, err error) {\n-\tuconn, err = DefaultSocket.Dial()\n-\tif err != nil {\n-\t\tif e, ok := err.(*net.OpError); ok && (os.IsNotExist(e.Err) || e.Err == syscall.ECONNREFUSED) {\n-\t\t\tif err = c.startProxy(); err == nil {\n-\t\t\t\tuconn, err = DefaultSocket.Dial()\n-\t\t\t}\n-\t\t}\n-\t\tif err != nil {\n-\t\t\treturn\n-\t\t}\n-\t}\n-\n-\tif addr, err = c.readAddr(uconn); err != nil {\n-\t\treturn\n-\t}\n-\n-\t// Open a tcp connection to the proxy.\n-\tif hconn, err = net.Dial(\"tcp\", addr); err != nil {\n-\t\treturn\n-\t}\n-\n-\t// Verify the address hasn't changed ownership.\n-\tvar addr2 string\n-\tif addr2, err = c.readAddr(uconn); err != nil {\n-\t\treturn\n-\t} else if addr != addr2 {\n-\t\terr = fmt.Errorf(\"address changed after connect. got %q, want %q\", addr2, addr)\n-\t\treturn\n-\t}\n-\treturn\n-}\n-\n-func (c *Client) readAddr(conn net.Conn) (string, error) {\n-\tconn.SetDeadline(time.Now().Add(5 * time.Second))\n-\tdata := make([]byte, 100)\n-\tn, err := conn.Read(data)\n-\tif err != nil {\n-\t\treturn \"\", fmt.Errorf(\"error reading unix socket: %v\", err)\n-\t} else if n == 0 {\n-\t\treturn \"\", errors.New(\"empty data response\")\n-\t}\n-\tconn.Write([]byte{1}) // Ack\n-\n-\tvar addr string\n-\tif addrs := strings.Split(string(data[:n]), \"\\n\"); len(addrs) != 2 {\n-\t\treturn \"\", fmt.Errorf(\"got %q, wanted 2 addresses\", data[:n])\n-\t} else if c.insecure {\n-\t\taddr = addrs[1]\n-\t} else {\n-\t\taddr = addrs[0]\n-\t}\n-\treturn addr, nil\n-}\n-\n-func (c *Client) startProxy() error {\n-\tcmd := exec.Command(c.ProxyBin)\n-\tcmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}\n-\tstdout, err := cmd.StdoutPipe()\n-\tif err != nil {\n-\t\treturn err\n-\t}\n-\tdefer stdout.Close()\n-\tif err := cmd.Start(); err != nil {\n-\t\treturn err\n-\t}\n-\tresult := make(chan error)\n-\tgo func() {\n-\t\tbytes, _, err := bufio.NewReader(stdout).ReadLine()\n-\t\tif line := string(bytes); err == nil && line != \"OK\" {\n-\t\t\terr = fmt.Errorf(\"proxy returned %q, want \\\"OK\\\"\", line)\n-\t\t}\n-\t\tresult <- err\n-\t}()\n-\tselect {\n-\tcase err := <-result:\n-\t\treturn err\n-\tcase <-time.After(5 * time.Second):\n-\t\treturn errors.New(\"timeout waiting for proxy to start\")\n-\t}\n-\tpanic(\"not reachable\")\n-}\n-\n-func (c *Client) resolveArgs() error {\n-\tif nargs := len(c.Args); nargs == 0 {\n-\t\treturn errors.New(\"remote needed\")\n-\t} else if nargs > 2 {\n-\t\treturn fmt.Errorf(\"want at most 2 args, got %v\", c.Args)\n-\t}\n-\n-\t// Rewrite the url scheme to be http.\n-\tidx := len(c.Args) - 1\n-\trawurl := c.Args[idx]\n-\trurl, err := url.Parse(rawurl)\n-\tif err != nil {\n-\t\treturn fmt.Errorf(\"invalid remote: %v\", err)\n-\t}\n-\tc.insecure = rurl.Scheme == \"persistent-http\"\n-\trurl.Scheme = \"http\"\n-\tc.Args[idx] = rurl.String()\n-\tif idx != 0 && c.Args[0] == rawurl {\n-\t\tc.Args[0] = c.Args[idx]\n-\t}\n-\treturn nil\n-}\ndiff --git a/contrib/persistent-https/main.go b/contrib/persistent-https/main.go\ndeleted file mode 100644\nindex fd1b1077439..00000000000\n--- a/contrib/persistent-https/main.go\n+++ /dev/null\n@@ -1,82 +0,0 @@\n-// Copyright 2012 Google Inc. All Rights Reserved.\n-//\n-// Licensed under the Apache License, Version 2.0 (the \"License\");\n-// you may not use this file except in compliance with the License.\n-// You may obtain a copy of the License at\n-//\n-//     http://www.apache.org/licenses/LICENSE-2.0\n-//\n-// Unless required by applicable law or agreed to in writing, software\n-// distributed under the License is distributed on an \"AS IS\" BASIS,\n-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-// See the License for the specific language governing permissions and\n-// limitations under the License.\n-\n-// The git-remote-persistent-https binary speeds up SSL operations by running\n-// a daemon job that keeps a connection open to a Git server. This ensures the\n-// git-remote-persistent-https--proxy is running and delegating execution\n-// to the git-remote-http binary with the http_proxy set to the daemon job.\n-// A unix socket is used to authenticate the proxy and discover the\n-// HTTP address. Note, both the client and proxy are included in the same\n-// binary.\n-package main\n-\n-import (\n-\t\"flag\"\n-\t\"fmt\"\n-\t\"log\"\n-\t\"os\"\n-\t\"strings\"\n-\t\"time\"\n-)\n-\n-var (\n-\tforceProxy = flag.Bool(\"proxy\", false, \"Whether to start the binary in proxy mode\")\n-\tproxyBin   = flag.String(\"proxy_bin\", \"git-remote-persistent-https--proxy\", \"Path to the proxy binary\")\n-\tprintLabel = flag.Bool(\"print_label\", false, \"Prints the build label for the binary\")\n-\n-\t// Variable that should be defined through the -X linker flag.\n-\t_BUILD_EMBED_LABEL string\n-)\n-\n-const (\n-\tdefaultMaxIdleDuration    = 24 * time.Hour\n-\tdefaultPollUpdateInterval = 15 * time.Minute\n-)\n-\n-func main() {\n-\tflag.Parse()\n-\tif *printLabel {\n-\t\t// Short circuit execution to print the build label\n-\t\tfmt.Println(buildLabel())\n-\t\treturn\n-\t}\n-\n-\tvar err error\n-\tif *forceProxy || strings.HasSuffix(os.Args[0], \"--proxy\") {\n-\t\tlog.SetPrefix(\"git-remote-persistent-https--proxy: \")\n-\t\tproxy := &Proxy{\n-\t\t\tBuildLabel:         buildLabel(),\n-\t\t\tMaxIdleDuration:    defaultMaxIdleDuration,\n-\t\t\tPollUpdateInterval: defaultPollUpdateInterval,\n-\t\t}\n-\t\terr = proxy.Run()\n-\t} else {\n-\t\tlog.SetPrefix(\"git-remote-persistent-https: \")\n-\t\tclient := &Client{\n-\t\t\tProxyBin: *proxyBin,\n-\t\t\tArgs:     flag.Args(),\n-\t\t}\n-\t\terr = client.Run()\n-\t}\n-\tif err != nil {\n-\t\tlog.Fatalln(err)\n-\t}\n-}\n-\n-func buildLabel() string {\n-\tif _BUILD_EMBED_LABEL == \"\" {\n-\t\tlog.Println(`unlabeled build; build with \"make\" to label`)\n-\t}\n-\treturn _BUILD_EMBED_LABEL\n-}\ndiff --git a/contrib/persistent-https/proxy.go b/contrib/persistent-https/proxy.go\ndeleted file mode 100644\nindex bb0cdba3864..00000000000\n--- a/contrib/persistent-https/proxy.go\n+++ /dev/null\n@@ -1,190 +0,0 @@\n-// Copyright 2012 Google Inc. All Rights Reserved.\n-//\n-// Licensed under the Apache License, Version 2.0 (the \"License\");\n-// you may not use this file except in compliance with the License.\n-// You may obtain a copy of the License at\n-//\n-//     http://www.apache.org/licenses/LICENSE-2.0\n-//\n-// Unless required by applicable law or agreed to in writing, software\n-// distributed under the License is distributed on an \"AS IS\" BASIS,\n-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-// See the License for the specific language governing permissions and\n-// limitations under the License.\n-\n-package main\n-\n-import (\n-\t\"fmt\"\n-\t\"log\"\n-\t\"net\"\n-\t\"net/http\"\n-\t\"net/http/httputil\"\n-\t\"os\"\n-\t\"os/exec\"\n-\t\"os/signal\"\n-\t\"sync\"\n-\t\"syscall\"\n-\t\"time\"\n-)\n-\n-type Proxy struct {\n-\tBuildLabel         string\n-\tMaxIdleDuration    time.Duration\n-\tPollUpdateInterval time.Duration\n-\n-\tul        net.Listener\n-\thttpAddr  string\n-\thttpsAddr string\n-}\n-\n-func (p *Proxy) Run() error {\n-\thl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n-\tif err != nil {\n-\t\treturn fmt.Errorf(\"http listen failed: %v\", err)\n-\t}\n-\tdefer hl.Close()\n-\n-\thsl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n-\tif err != nil {\n-\t\treturn fmt.Errorf(\"https listen failed: %v\", err)\n-\t}\n-\tdefer hsl.Close()\n-\n-\tp.ul, err = DefaultSocket.Listen()\n-\tif err != nil {\n-\t\tc, derr := DefaultSocket.Dial()\n-\t\tif derr == nil {\n-\t\t\tc.Close()\n-\t\t\tfmt.Println(\"OK\\nA proxy is already running... exiting\")\n-\t\t\treturn nil\n-\t\t} else if e, ok := derr.(*net.OpError); ok && e.Err == syscall.ECONNREFUSED {\n-\t\t\t// Nothing is listening on the socket, unlink it and try again.\n-\t\t\tsyscall.Unlink(DefaultSocket.Path())\n-\t\t\tp.ul, err = DefaultSocket.Listen()\n-\t\t}\n-\t\tif err != nil {\n-\t\t\treturn fmt.Errorf(\"unix listen failed on %v: %v\", DefaultSocket.Path(), err)\n-\t\t}\n-\t}\n-\tdefer p.ul.Close()\n-\tgo p.closeOnSignal()\n-\tgo p.closeOnUpdate()\n-\n-\tp.httpAddr = hl.Addr().String()\n-\tp.httpsAddr = hsl.Addr().String()\n-\tfmt.Printf(\"OK\\nListening on unix socket=%v http=%v https=%v\\n\",\n-\t\tp.ul.Addr(), p.httpAddr, p.httpsAddr)\n-\n-\tresult := make(chan error, 2)\n-\tgo p.serveUnix(result)\n-\tgo func() {\n-\t\tresult <- http.Serve(hl, &httputil.ReverseProxy{\n-\t\t\tFlushInterval: 500 * time.Millisecond,\n-\t\t\tDirector:      func(r *http.Request) {},\n-\t\t})\n-\t}()\n-\tgo func() {\n-\t\tresult <- http.Serve(hsl, &httputil.ReverseProxy{\n-\t\t\tFlushInterval: 500 * time.Millisecond,\n-\t\t\tDirector: func(r *http.Request) {\n-\t\t\t\tr.URL.Scheme = \"https\"\n-\t\t\t},\n-\t\t})\n-\t}()\n-\treturn <-result\n-}\n-\n-type socketContext struct {\n-\tsync.WaitGroup\n-\tmutex sync.Mutex\n-\tlast  time.Time\n-}\n-\n-func (sc *socketContext) Done() {\n-\tsc.mutex.Lock()\n-\tdefer sc.mutex.Unlock()\n-\tsc.last = time.Now()\n-\tsc.WaitGroup.Done()\n-}\n-\n-func (p *Proxy) serveUnix(result chan<- error) {\n-\tsockCtx := &socketContext{}\n-\tgo p.closeOnIdle(sockCtx)\n-\n-\tvar err error\n-\tfor {\n-\t\tvar uconn net.Conn\n-\t\tuconn, err = p.ul.Accept()\n-\t\tif err != nil {\n-\t\t\terr = fmt.Errorf(\"accept failed: %v\", err)\n-\t\t\tbreak\n-\t\t}\n-\t\tsockCtx.Add(1)\n-\t\tgo p.handleUnixConn(sockCtx, uconn)\n-\t}\n-\tsockCtx.Wait()\n-\tresult <- err\n-}\n-\n-func (p *Proxy) handleUnixConn(sockCtx *socketContext, uconn net.Conn) {\n-\tdefer sockCtx.Done()\n-\tdefer uconn.Close()\n-\tdata := []byte(fmt.Sprintf(\"%v\\n%v\", p.httpsAddr, p.httpAddr))\n-\tuconn.SetDeadline(time.Now().Add(5 * time.Second))\n-\tfor i := 0; i < 2; i++ {\n-\t\tif n, err := uconn.Write(data); err != nil {\n-\t\t\tlog.Printf(\"error sending http addresses: %+v\\n\", err)\n-\t\t\treturn\n-\t\t} else if n != len(data) {\n-\t\t\tlog.Printf(\"sent %d data bytes, wanted %d\\n\", n, len(data))\n-\t\t\treturn\n-\t\t}\n-\t\tif _, err := uconn.Read([]byte{0, 0, 0, 0}); err != nil {\n-\t\t\tlog.Printf(\"error waiting for Ack: %+v\\n\", err)\n-\t\t\treturn\n-\t\t}\n-\t}\n-\t// Wait without a deadline for the client to finish via EOF\n-\tuconn.SetDeadline(time.Time{})\n-\tuconn.Read([]byte{0, 0, 0, 0})\n-}\n-\n-func (p *Proxy) closeOnIdle(sockCtx *socketContext) {\n-\tfor d := p.MaxIdleDuration; d > 0; {\n-\t\ttime.Sleep(d)\n-\t\tsockCtx.Wait()\n-\t\tsockCtx.mutex.Lock()\n-\t\tif d = sockCtx.last.Add(p.MaxIdleDuration).Sub(time.Now()); d <= 0 {\n-\t\t\tlog.Println(\"graceful shutdown from idle timeout\")\n-\t\t\tp.ul.Close()\n-\t\t}\n-\t\tsockCtx.mutex.Unlock()\n-\t}\n-}\n-\n-func (p *Proxy) closeOnUpdate() {\n-\tfor {\n-\t\ttime.Sleep(p.PollUpdateInterval)\n-\t\tif out, err := exec.Command(os.Args[0], \"--print_label\").Output(); err != nil {\n-\t\t\tlog.Printf(\"error polling for updated binary: %v\\n\", err)\n-\t\t} else if s := string(out[:len(out)-1]); p.BuildLabel != s {\n-\t\t\tlog.Printf(\"graceful shutdown from updated binary: %q --> %q\\n\", p.BuildLabel, s)\n-\t\t\tp.ul.Close()\n-\t\t\tbreak\n-\t\t}\n-\t}\n-}\n-\n-func (p *Proxy) closeOnSignal() {\n-\tch := make(chan os.Signal, 10)\n-\tsignal.Notify(ch, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGHUP))\n-\tsig := <-ch\n-\tp.ul.Close()\n-\tswitch sig {\n-\tcase os.Signal(syscall.SIGHUP):\n-\t\tlog.Printf(\"graceful shutdown from signal: %v\\n\", sig)\n-\tdefault:\n-\t\tlog.Fatalf(\"exiting from signal: %v\\n\", sig)\n-\t}\n-}\ndiff --git a/contrib/persistent-https/socket.go b/contrib/persistent-https/socket.go\ndeleted file mode 100644\nindex 193b911dd13..00000000000\n--- a/contrib/persistent-https/socket.go\n+++ /dev/null\n@@ -1,97 +0,0 @@\n-// Copyright 2012 Google Inc. All Rights Reserved.\n-//\n-// Licensed under the Apache License, Version 2.0 (the \"License\");\n-// you may not use this file except in compliance with the License.\n-// You may obtain a copy of the License at\n-//\n-//     http://www.apache.org/licenses/LICENSE-2.0\n-//\n-// Unless required by applicable law or agreed to in writing, software\n-// distributed under the License is distributed on an \"AS IS\" BASIS,\n-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-// See the License for the specific language governing permissions and\n-// limitations under the License.\n-\n-package main\n-\n-import (\n-\t\"fmt\"\n-\t\"log\"\n-\t\"net\"\n-\t\"os\"\n-\t\"path/filepath\"\n-\t\"syscall\"\n-)\n-\n-// A Socket is a wrapper around a Unix socket that verifies directory\n-// permissions.\n-type Socket struct {\n-\tDir string\n-}\n-\n-func defaultDir() string {\n-\tsockPath := \".git-credential-cache\"\n-\tif home := os.Getenv(\"HOME\"); home != \"\" {\n-\t\treturn filepath.Join(home, sockPath)\n-\t}\n-\tlog.Printf(\"socket: cannot find HOME path. using relative directory %q for socket\", sockPath)\n-\treturn sockPath\n-}\n-\n-// DefaultSocket is a Socket in the $HOME/.git-credential-cache directory.\n-var DefaultSocket = Socket{Dir: defaultDir()}\n-\n-// Listen announces the local network address of the unix socket. The\n-// permissions on the socket directory are verified before attempting\n-// the actual listen.\n-func (s Socket) Listen() (net.Listener, error) {\n-\tnetwork, addr := \"unix\", s.Path()\n-\tif err := s.mkdir(); err != nil {\n-\t\treturn nil, &net.OpError{Op: \"listen\", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err}\n-\t}\n-\treturn net.Listen(network, addr)\n-}\n-\n-// Dial connects to the unix socket. The permissions on the socket directory\n-// are verified before attempting the actual dial.\n-func (s Socket) Dial() (net.Conn, error) {\n-\tnetwork, addr := \"unix\", s.Path()\n-\tif err := s.checkPermissions(); err != nil {\n-\t\treturn nil, &net.OpError{Op: \"dial\", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err}\n-\t}\n-\treturn net.Dial(network, addr)\n-}\n-\n-// Path returns the fully specified file name of the unix socket.\n-func (s Socket) Path() string {\n-\treturn filepath.Join(s.Dir, \"persistent-https-proxy-socket\")\n-}\n-\n-func (s Socket) mkdir() error {\n-\tif err := s.checkPermissions(); err == nil {\n-\t\treturn nil\n-\t} else if !os.IsNotExist(err) {\n-\t\treturn err\n-\t}\n-\tif err := os.MkdirAll(s.Dir, 0700); err != nil {\n-\t\treturn err\n-\t}\n-\treturn s.checkPermissions()\n-}\n-\n-func (s Socket) checkPermissions() error {\n-\tfi, err := os.Stat(s.Dir)\n-\tif err != nil {\n-\t\treturn err\n-\t}\n-\tif !fi.IsDir() {\n-\t\treturn fmt.Errorf(\"socket: got file, want directory for %q\", s.Dir)\n-\t}\n-\tif fi.Mode().Perm() != 0700 {\n-\t\treturn fmt.Errorf(\"socket: got perm %o, want 700 for %q\", fi.Mode().Perm(), s.Dir)\n-\t}\n-\tif st := fi.Sys().(*syscall.Stat_t); int(st.Uid) != os.Getuid() {\n-\t\treturn fmt.Errorf(\"socket: got uid %d, want %d for %q\", st.Uid, os.Getuid(), s.Dir)\n-\t}\n-\treturn nil\n-}\n\n-- \n2.49.0.1045.g170613ef41.dirty\n\n"},{"id":"517353","messageId":"20250506-pks-contrib-spring-cleanup-v1-10-e6d5ddd79a72@pks.im","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","subject":"[PATCH 10/10] contrib: remove \"git-new-workdir\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-06T14:12:43Z","receivedAt":"2025-05-06T14:12:50Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"git-new-workdir\" command has been introduced to make it possible to\nhave a separate working directory in a different place. The command thus\npredates git-worktree(1), which is what people use nowadays to create\nany such working directory. As such, the script doesn't really have much\nof a reason to exist nowadays anymore.\n\nIt also doesn't seem like the script is still in use: the last time it\nhas received an update was in e32afab7b03 (git-new-workdir: don't fail\nif the target directory is empty, 2014-11-26), more than a decade ago.\nRemove it as well as the tests that depend on it.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/workdir/.gitattributes  |   1 -\n contrib/workdir/git-new-workdir | 105 ----------------------------------------\n t/meson.build                   |   1 -\n t/t1021-rerere-in-workdir.sh    |  58 ----------------------\n t/t3000-ls-files-others.sh      |  19 --------\n 5 files changed, 184 deletions(-)\n\ndiff --git a/contrib/workdir/.gitattributes b/contrib/workdir/.gitattributes\ndeleted file mode 100644\nindex 1f78c5d1bd3..00000000000\n--- a/contrib/workdir/.gitattributes\n+++ /dev/null\n@@ -1 +0,0 @@\n-/git-new-workdir eol=lf\ndiff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir\ndeleted file mode 100755\nindex 989197aace0..00000000000\n--- a/contrib/workdir/git-new-workdir\n+++ /dev/null\n@@ -1,105 +0,0 @@\n-#!/bin/sh\n-\n-usage () {\n-\techo \"usage:\" $@\n-\texit 127\n-}\n-\n-die () {\n-\techo $@\n-\texit 128\n-}\n-\n-failed () {\n-\tdie \"unable to create new workdir '$new_workdir'!\"\n-}\n-\n-if test $# -lt 2 || test $# -gt 3\n-then\n-\tusage \"$0 <repository> <new_workdir> [<branch>]\"\n-fi\n-\n-orig_git=$1\n-new_workdir=$2\n-branch=$3\n-\n-# want to make sure that what is pointed to has a .git directory ...\n-git_dir=$(cd \"$orig_git\" 2>/dev/null &&\n-  git rev-parse --git-dir 2>/dev/null) ||\n-  die \"Not a git repository: \\\"$orig_git\\\"\"\n-\n-case \"$git_dir\" in\n-.git)\n-\tgit_dir=\"$orig_git/.git\"\n-\t;;\n-.)\n-\tgit_dir=$orig_git\n-\t;;\n-esac\n-\n-# don't link to a configured bare repository\n-isbare=$(git --git-dir=\"$git_dir\" config --bool --get core.bare)\n-if test ztrue = \"z$isbare\"\n-then\n-\tdie \"\\\"$git_dir\\\" has core.bare set to true,\" \\\n-\t\t\" remove from \\\"$git_dir/config\\\" to use $0\"\n-fi\n-\n-# don't link to a workdir\n-if test -h \"$git_dir/config\"\n-then\n-\tdie \"\\\"$orig_git\\\" is a working directory only, please specify\" \\\n-\t\t\"a complete repository.\"\n-fi\n-\n-# make sure the links in the workdir have full paths to the original repo\n-git_dir=$(cd \"$git_dir\" && pwd) || exit 1\n-\n-# don't recreate a workdir over an existing directory, unless it's empty\n-if test -d \"$new_workdir\"\n-then\n-\tif test $(ls -a1 \"$new_workdir/.\" | wc -l) -ne 2\n-\tthen\n-\t\tdie \"destination directory '$new_workdir' is not empty.\"\n-\tfi\n-\tcleandir=\"$new_workdir/.git\"\n-else\n-\tcleandir=\"$new_workdir\"\n-fi\n-\n-mkdir -p \"$new_workdir/.git\" || failed\n-cleandir=$(cd \"$cleandir\" && pwd) || failed\n-\n-cleanup () {\n-\trm -rf \"$cleandir\"\n-}\n-siglist=\"0 1 2 15\"\n-trap cleanup $siglist\n-\n-# create the links to the original repo.  explicitly exclude index, HEAD and\n-# logs/HEAD from the list since they are purely related to the current working\n-# directory, and should not be shared.\n-for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn reftable\n-do\n-\t# create a containing directory if needed\n-\tcase $x in\n-\t*/*)\n-\t\tmkdir -p \"$new_workdir/.git/${x%/*}\"\n-\t\t;;\n-\tesac\n-\n-\tln -s \"$git_dir/$x\" \"$new_workdir/.git/$x\" || failed\n-done\n-\n-# commands below this are run in the context of the new workdir\n-cd \"$new_workdir\" || failed\n-\n-# copy the HEAD from the original repository as a default branch\n-cp \"$git_dir/HEAD\" .git/HEAD || failed\n-\n-# the workdir is set up.  if the checkout fails, the user can fix it.\n-trap - $siglist\n-\n-# checkout the branch (either the same as HEAD from the original repository,\n-# or the one that was asked for)\n-git checkout -f $branch\ndiff --git a/t/meson.build b/t/meson.build\nindex b09c0becb8d..9206090fedc 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -178,7 +178,6 @@ integration_tests = [\n   't1015-read-index-unmerged.sh',\n   't1016-compatObjectFormat.sh',\n   't1020-subdirectory.sh',\n-  't1021-rerere-in-workdir.sh',\n   't1022-read-tree-partial-clone.sh',\n   't1050-large.sh',\n   't1051-large-conversion.sh',\ndiff --git a/t/t1021-rerere-in-workdir.sh b/t/t1021-rerere-in-workdir.sh\ndeleted file mode 100755\nindex 0b892894eb9..00000000000\n--- a/t/t1021-rerere-in-workdir.sh\n+++ /dev/null\n@@ -1,58 +0,0 @@\n-#!/bin/sh\n-\n-test_description='rerere run in a workdir'\n-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n-\n-. ./test-lib.sh\n-\n-test_expect_success SYMLINKS setup '\n-\tgit config rerere.enabled true &&\n-\t>world &&\n-\tgit add world &&\n-\ttest_tick &&\n-\tgit commit -m initial &&\n-\n-\techo hello >world &&\n-\ttest_tick &&\n-\tgit commit -a -m hello &&\n-\n-\tgit checkout -b side HEAD^ &&\n-\techo goodbye >world &&\n-\ttest_tick &&\n-\tgit commit -a -m goodbye &&\n-\n-\tgit checkout main\n-'\n-\n-test_expect_success SYMLINKS 'rerere in workdir' '\n-\trm -rf .git/rr-cache &&\n-\t\"$SHELL_PATH\" \"$TEST_DIRECTORY/../contrib/workdir/git-new-workdir\" . work &&\n-\t(\n-\t\tcd work &&\n-\t\ttest_must_fail git merge side &&\n-\t\tgit rerere status >actual &&\n-\t\techo world >expect &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-# This fails because we don't resolve relative symlink in mkdir_in_gitdir()\n-# For the purpose of helping contrib/workdir/git-new-workdir users, we do not\n-# have to support relative symlinks, but it might be nicer to make this work\n-# with a relative symbolic link someday.\n-test_expect_failure SYMLINKS 'rerere in workdir (relative)' '\n-\trm -rf .git/rr-cache &&\n-\t\"$SHELL_PATH\" \"$TEST_DIRECTORY/../contrib/workdir/git-new-workdir\" . krow &&\n-\t(\n-\t\tcd krow &&\n-\t\trm -f .git/rr-cache &&\n-\t\tln -s ../.git/rr-cache .git/rr-cache &&\n-\t\ttest_must_fail git merge side &&\n-\t\tgit rerere status >actual &&\n-\t\techo world >expect &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_done\ndiff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh\nindex 13f66fd649d..b41e7f0daa4 100755\n--- a/t/t3000-ls-files-others.sh\n+++ b/t/t3000-ls-files-others.sh\n@@ -73,25 +73,6 @@ test_expect_success 'ls-files --others handles non-submodule .git' '\n \ttest_cmp expected1 output\n '\n \n-test_expect_success SYMLINKS 'ls-files --others with symlinked submodule' '\n-\tgit init super &&\n-\tgit init sub &&\n-\t(\n-\t\tcd sub &&\n-\t\t>a &&\n-\t\tgit add a &&\n-\t\tgit commit -m sub &&\n-\t\tgit pack-refs --all\n-\t) &&\n-\t(\n-\t\tcd super &&\n-\t\t\"$SHELL_PATH\" \"$TEST_DIRECTORY/../contrib/workdir/git-new-workdir\" ../sub sub &&\n-\t\tgit ls-files --others --exclude-standard >../actual\n-\t) &&\n-\techo sub/ >expect &&\n-\ttest_cmp expect actual\n-'\n-\n test_expect_success 'setup nested pathspec search' '\n \ttest_create_repo nested &&\n \t(\n\n-- \n2.49.0.1045.g170613ef41.dirty\n\n"},{"id":"517385","messageId":"xmqqr011ikjc.fsf@gitster.g","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-1-e6d5ddd79a72@pks.im","subject":"Re: [PATCH 01/10] contrib: remove \"remotes2config.sh\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-05-06T19:52:39Z","receivedAt":"2025-05-06T19:52:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> Remotes can be configured either via a repository's config or by using\n> the \".git/branches/\" or \".git/remotes/\" directories. Back when the new\n> config-based mechanism has been introduced we also introduced a helper\n> script that migrates from the old-style remote configuration to the new\n> config-based mechanism.\n>\n> With the recent removal announcement for the two directories we also\n> started to instruct users to migrate repositories that still use these\n> mechanism to use config-based remotes. Notably though, the migration\n> path doesn't even use the migration script. Instead, git-remote(1)\n> itself knows how to migrate any such remote via `git remote rename`.\n\nMakes sense.\n\nThis logically comes after your series, which ended at 8ccc75c2\n(remote: announce removal of \"branches/\" and \"remotes/\",\n2025-01-22), lands.  After that, we can safely remove this script at\nthe 3.0 boundary, at the same time we clean up the code paths that\nare conditionally compiled with WITH_BREAKING_CHANGES.\n\n> Furthermore, the script hasn't been touched since afa75bc8aa1 (contrib:\n> Make remotes2config.sh script more robust, 2007-12-02).\n\nA single-purpose thing that is done correctly on top of a right\nabstraction does not necessarily need further updates, so I doubt\nthis paragraph contributes to the decision to remove the script in\nany way.\n\n> Given that there is a migration path without this script it is very\n> unlikely that anyone still uses the script. Remove it.\n\nSounds good, but not right now, I presume?\n"},{"id":"517386","messageId":"xmqqjz6tikak.fsf@gitster.g","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-10-e6d5ddd79a72@pks.im","subject":"Re: [PATCH 10/10] contrib: remove \"git-new-workdir\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-05-06T19:57:55Z","receivedAt":"2025-05-06T19:57:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> The \"git-new-workdir\" command has been introduced to make it possible to\n> have a separate working directory in a different place. The command thus\n> predates git-worktree(1), which is what people use nowadays to create\n> any such working directory. As such, the script doesn't really have much\n> of a reason to exist nowadays anymore.\n\nYup.  As the original inventor of the concept, I still have one \"new\nworkdir\" I use daily, without any problems, so I think the layout it\ncreates (actually, it created 10 years ago) is more or less still OK\nwith the current binary.\n\nBut even I do not create a new one (not that I run \"git worktree\" to\ncreate an equivalent all that often).  I do not have an objection\nagainst the removal.\n\n>  contrib/workdir/.gitattributes  |   1 -\n>  contrib/workdir/git-new-workdir | 105 ----------------------------------------\n>  t/meson.build                   |   1 -\n>  t/t1021-rerere-in-workdir.sh    |  58 ----------------------\n>  t/t3000-ls-files-others.sh      |  19 --------\n>  5 files changed, 184 deletions(-)\n\nOops, do we have tests that depend on contrib/ stuff (other than\ncompletion and prompt, which should have moved up long ago)?  That\nchanges the story a bit.\n\nShouldn't we do the usual WITH_BREAKING_CHANGES transition, then?\n"},{"id":"517387","messageId":"xmqqfrhhik8q.fsf@gitster.g","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-9-e6d5ddd79a72@pks.im","subject":"Re: [PATCH 09/10] contrib: remove \"emacs\" directory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-05-06T19:59:01Z","receivedAt":"2025-05-06T19:59:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> While the \"emacs/\" directory still exists, all of its code has been\n> replaced with stubs in 6d5ed4836db (git{,-blame}.el: remove old\n> bitrotting Emacs code, 2018-04-11). Instead, the recommendation is to\n> use Emacs' own vc-annotate mode.\n>\n> Remove the code altogether.\n\nSounds good.  I do not see any need to wait for 3.0 for this step.\n"},{"id":"517388","messageId":"xmqq34dhijnq.fsf@gitster.g","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-8-e6d5ddd79a72@pks.im","subject":"Re: [PATCH 08/10] contrib: remove \"git-resurrect.sh\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-05-06T20:11:37Z","receivedAt":"2025-05-06T20:11:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> The \"git-resurrect.sh\" script can be used to find traces of a branch tip\n> in the reflog and resurrect that branch. Despite a couple of global\n> cleanups, the script hasn't seen any activity since it was introduced in\n> e1ff064e1bf (contrib git-resurrect: find traces of a branch name and\n> resurrect it, 2009-02-04).\n\nA single-purpose thing that is done correctly on top of a right\nabstraction does not necessarily need further updates, so I doubt\nthis paragraph contributes to the decision to remove the script in\nany way.\n\nHaving said that, I would not be surprised at all if large bugs\nstill remain in the script.  The reason why we scarcely heard\ncomplaints about it is due to the fact that people simply are not\naware of it, people do not lose branches too often, and when it\nhappens, it is crystal clear what needs to be done with the output\nof \"git reflog HEAD@{0}\", once people learn about \"git reflog\".\nEven though it may be tedious to inspect \"git reflog\" output and\npick the right record to use with \"git branch\" to resurrect, as long\nas it is a one-off thing, it would be more assuring to end-users than\nsome rarely used script with no correctness guarantee magically picks\na commit to place on the \"resurrected\" branch tip, I suspect.\n\nSo I personally do not think many people shed tears if we remove\nthis script.  I am for its removal.\n\nDo we have a better alternative we officially support, by the way?\n"},{"id":"517389","messageId":"xmqqwmath4fq.fsf@gitster.g","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-7-e6d5ddd79a72@pks.im","subject":"Re: [PATCH 07/10] contrib: remove \"persistent-https\" remote helper","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-05-06T20:25:45Z","receivedAt":"2025-05-06T20:25:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> The \"persistent-https\" remote helper supposedly speeds up SSL operations\n> by running a daemon that keeps a connection open to a remote server. It\n> is effectively unmaintained nowadays: the last time it received an\n> update was in accb613afd2 (contrib/persistent-https: use Git version for\n> build label, 2016-07-20) and its parent commits to make it compile with\n> Go 1.7+.\n\nUnlike other ones you said \"haven't been touched\" and I responded\nwith \"that alone is not a reason it is stale and useless\", the above\ndescription clearly shows that it has bitrotted.\n\nIf somebody wants to use it as a reference to build their own remote\nhelper, they can always go back in the history to find the copy.\n\nLet's remove it.\n"},{"id":"517390","messageId":"xmqqr011h41x.fsf@gitster.g","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-6-e6d5ddd79a72@pks.im","subject":"Re: [PATCH 06/10] contrib: remove \"mw-to-git\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-05-06T20:34:02Z","receivedAt":"2025-05-06T20:34:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> The \"mw-to-git\" directory contains tools for accessing MediaWiki via\n> Git. The scripts are essentially unmaintained in Git: despite a couple\n> of global cleanups, the last changes were a couple of security-related\n> issues part of 9a8606465e8 (remote-mediawiki: use \"sh\" to eliminate\n> unquoted commands, 2020-09-21) and its parents. We don't ever run any of\n> the tests so it is more likely than not that many of the tests have been\n> bitrotting, like e.g. documented in f8ab018dafc (remote-mediawiki tests:\n> annotate failing tests, 2020-09-21).\n>\n> Furthermore, the code has been spun out into a separate project at [2].\n> Remove the directory in favor of this new home.\n>\n> [2]: https://github.com/Git-Mediawiki/Git-Mediawiki\n\nOK, the new home also lacks activity since Mar 2022, but it\nstill is certainly a much better home than having it here.\n\nAnd I doubt this removal needs to wait for Git 3.0 boundary; unlike\nother things, we shouldn't even have been its primary home to begin\nwith.\n\n"},{"id":"517391","messageId":"xmqqmsbph3lw.fsf@gitster.g","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","subject":"Re: [PATCH 00/10] Spring cleanup of \"contrib/\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-05-06T20:43:39Z","receivedAt":"2025-05-06T20:43:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> I have used the following reasons for removal:\n>\n>   - The tool is clearly broken, e.g. it doesn't even compile.\n>\n>   - The tool hasn't received any updates for at least the last 5 years.\n>\n>   - The tool has a clear alternative or just isn't useful anymore.\n\nI've expressed my opinions on many of the individual patches, but\nnot all of them.  For some, it may be better done at 3.0 boundary\nwith the BreakingChanges transition like everybody else, and some\nothers with clear \"new home\", we can remove them much earlier and\nindependent from 3.0 plan.  Some others with no \"new home\" may be\nin the gray area, but my gut feeling is that many of them do not\nneed a careful BreakingChanges transition as some others do.\n\n> With this model, \"contrib/\" would be closer to Linux' staging drivers\n> with the expectation that a tool should eventually be part of proper Git\n> in case it proves to be useful, or booted out when it doesn't seem to be\n> getting there.\n\nOK.\n\n> Another subsequent step would be to split out some parts of \"contrib/\"\n> to be hosted in their own hierarchy. CMake, Coccinelle, Unicode updates,\n> VScode and the like are all tools that are used during development, so\n> they should probably not be part of \"contrib/\" but rather of a new\n> \"tools/\" hierarchy (we can bikeshed the name at a later point, I'm not\n> yet doing that in this series).\n\nYeah, I think we had a discussion like this, and I do not remember\nthe new names proposed for things that are out of core but still\npart of our tree, but \"tools\" sounds like a good place.\n\n> There's also other bits and pieces that serve as examples. I think we\n> should move these into our documentation instead of having those in\n> \"contrib/\".\n\nSounds sensible.\n\nThanks for getting the ball rolling.\n"},{"id":"517402","messageId":"CAPig+cT6XbdzeOFoeZUmX+ozPa2XNOv=H85xQhY4y8NYmJZ6-g@mail.gmail.com","threadId":"63406","inReplyTo":"xmqqmsbph3lw.fsf@gitster.g","subject":"Re: [PATCH 00/10] Spring cleanup of \"contrib/\"","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-05-06T22:51:25Z","receivedAt":"2025-05-06T22:51:37Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, May 6, 2025 at 4:43 PM Junio C Hamano <gitster@pobox.com> wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> > I have used the following reasons for removal:\n> >\n> >   - The tool is clearly broken, e.g. it doesn't even compile.\n> >\n> >   - The tool hasn't received any updates for at least the last 5 years.\n> >\n> >   - The tool has a clear alternative or just isn't useful anymore.\n>\n> I've expressed my opinions on many of the individual patches, but\n> not all of them.  For some, it may be better done at 3.0 boundary\n> with the BreakingChanges transition like everybody else, and some\n> others with clear \"new home\", we can remove them much earlier and\n> independent from 3.0 plan.  Some others with no \"new home\" may be\n> in the gray area, but my gut feeling is that many of them do not\n> need a careful BreakingChanges transition as some others do.\n\nAlthough we periodically hear that someone uses it, git-contacts\nprobably falls into the \"tool has a clear alternative\" category. In\nthis case, git-related[*] is a separately-maintained more functional\ndrop-in replacement which people could be using instead of\ngit-contacts.\n\n[*]: https://github.com/felipec/git-related/blob/master/git-related\n"},{"id":"517422","messageId":"aBq4J6UTZVPF8rb4@teonanacatl.net","threadId":"63406","inReplyTo":"CAPig+cT6XbdzeOFoeZUmX+ozPa2XNOv=H85xQhY4y8NYmJZ6-g@mail.gmail.com","subject":"Re: [PATCH 00/10] Spring cleanup of \"contrib/\"","fromName":"Todd Zullinger","fromEmail":"tmz@pobox.com","sentAt":"2025-05-07T01:32:23Z","receivedAt":"2025-05-07T01:32:26Z","isPatch":true,"sender":{"key":"tmz@pobox.com","avatar":"https://avatars.githubusercontent.com/u/806319?v=4"},"body":"Eric Sunshine wrote:\n> Although we periodically hear that someone uses it, git-contacts\n> probably falls into the \"tool has a clear alternative\" category. In\n> this case, git-related[*] is a separately-maintained more functional\n> drop-in replacement which people could be using instead of\n> git-contacts.\n> \n> [*]: https://github.com/felipec/git-related/blob/master/git-related\n\nIt may be worth noting that git-contacts is suggested in\nboth MyFirstContribution.adoc and SubmittingPatches.\n\nIt probably helps that since 824503ce88 (SubmittingPatches:\nclarify 'git-contacts' location, 2024-04-18) there has been\na note stating this isn't \"part of the core `git` binary and\nmust be called directly.\" That is relatively recent, though.\n\nI added git-contacts to the Fedora git packaging shortly\nafter 92a5dbbc22 (SubmittingPatches: mention the git\ncontacts command, 2018-04-11), presuming some readers would\nwant to use it.  (I never want to penalize users who are\ndiligent enough to read SubmittingPatches. :)\n\nAll that said, I don't have any strong opinion on whether it\nis kept or removed, let alone when that might happen.  I\ndon't know that I've ever used it, other than for testing\nthat it worked while packaging it.\n\n-- \nTodd\n"},{"id":"517439","messageId":"CAPig+cRxDQBmPu_-ci5vEuwtsAHadfCiFOccdYseBSj2F52JGw@mail.gmail.com","threadId":"63406","inReplyTo":"aBq4J6UTZVPF8rb4@teonanacatl.net","subject":"Re: [PATCH 00/10] Spring cleanup of \"contrib/\"","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-05-07T03:55:20Z","receivedAt":"2025-05-07T03:55:31Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, May 6, 2025 at 9:32 PM Todd Zullinger <tmz@pobox.com> wrote:\n> Eric Sunshine wrote:\n> > Although we periodically hear that someone uses it, git-contacts\n> > probably falls into the \"tool has a clear alternative\" category. In\n> > this case, git-related[*] is a separately-maintained more functional\n> > drop-in replacement which people could be using instead of\n> > git-contacts.\n> >\n> > [*]: https://github.com/felipec/git-related/blob/master/git-related\n>\n> It may be worth noting that git-contacts is suggested in\n> both MyFirstContribution.adoc and SubmittingPatches.\n\nI think I knew but forgot about those mentions. Certainly useful\ninformation if Patrick decides to pursue retirement of git-contacts.\n\n> It probably helps that since 824503ce88 (SubmittingPatches:\n> clarify 'git-contacts' location, 2024-04-18) there has been\n> a note stating this isn't \"part of the core `git` binary and\n> must be called directly.\" That is relatively recent, though.\n\nOut of curiosity, I Googled git-contacts but didn't find any\nmeaningful hits. Pretty much the only pages found were renderings of\nGit's documentation (including SubmittingPatches and the man page for\ngit-contacts itself), as well as the few patches to the Git mailing\nlist which introduced or touched git-contacts over the years. I did\nnot find any general discussion or recommendations to use\ngit-contacts, so perhaps it indeed is not very much used.\n\n> I added git-contacts to the Fedora git packaging shortly\n> after 92a5dbbc22 (SubmittingPatches: mention the git\n> contacts command, 2018-04-11), presuming some readers would\n> want to use it.  (I never want to penalize users who are\n> diligent enough to read SubmittingPatches. :)\n>\n> All that said, I don't have any strong opinion on whether it\n> is kept or removed, let alone when that might happen.  I\n> don't know that I've ever used it, other than for testing\n> that it worked while packaging it.\n\nI've never used git-contacts either, despite the fact that I'm the one\nwho ported Felipe's git-related from Ruby[1] to Perl[2] for inclusion\nin Git's \"contrib\" since the Ruby version had been rejected due to\nbeing written in a language not already employed elsewhere in the\nproject. The Perl rewrite also included a number of useful\nenhancements which Felipe later incorporated into git-related after he\npublished it as a standalone project. He has since extended it to\ninclude even more features, so it's functionally a superset of\ngit-contacts.\n\nBy the way, Felipe also sent a patch series[3] eleven years ago with\nthe same intention of Patrick's series under discussion. Felipe's\nseries was never picked up but did undertake the retirement of\ngit-contacts.\n\n[1]: https://lore.kernel.org/git/1369986380-412-1-git-send-email-felipe.contreras@gmail.com/\n[2]: https://lore.kernel.org/git/1374403962-48361-1-git-send-email-sunshine@sunshineco.com/\n[3]: https://lore.kernel.org/git/1399662703-355-1-git-send-email-felipe.contreras@gmail.com/T/\n"},{"id":"517441","messageId":"aBr9VuYurCCvRYnj@pks.im","threadId":"63406","inReplyTo":"xmqqr011ikjc.fsf@gitster.g","subject":"Re: [PATCH 01/10] contrib: remove \"remotes2config.sh\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-07T06:27:34Z","receivedAt":"2025-05-07T06:27:43Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, May 06, 2025 at 12:52:39PM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > Remotes can be configured either via a repository's config or by using\n> > the \".git/branches/\" or \".git/remotes/\" directories. Back when the new\n> > config-based mechanism has been introduced we also introduced a helper\n> > script that migrates from the old-style remote configuration to the new\n> > config-based mechanism.\n> >\n> > With the recent removal announcement for the two directories we also\n> > started to instruct users to migrate repositories that still use these\n> > mechanism to use config-based remotes. Notably though, the migration\n> > path doesn't even use the migration script. Instead, git-remote(1)\n> > itself knows how to migrate any such remote via `git remote rename`.\n> \n> Makes sense.\n> \n> This logically comes after your series, which ended at 8ccc75c2\n> (remote: announce removal of \"branches/\" and \"remotes/\",\n> 2025-01-22), lands.  After that, we can safely remove this script at\n> the 3.0 boundary, at the same time we clean up the code paths that\n> are conditionally compiled with WITH_BREAKING_CHANGES.\n> \n> > Furthermore, the script hasn't been touched since afa75bc8aa1 (contrib:\n> > Make remotes2config.sh script more robust, 2007-12-02).\n> \n> A single-purpose thing that is done correctly on top of a right\n> abstraction does not necessarily need further updates, so I doubt\n> this paragraph contributes to the decision to remove the script in\n> any way.\n> \n> > Given that there is a migration path without this script it is very\n> > unlikely that anyone still uses the script. Remove it.\n> \n> Sounds good, but not right now, I presume?\n\nI think it should be fine to remove it right now. If it was the\ncanonical way to migrate repos to the new format we definitely\nshouldn't. But nowhere in our tree do we even point to this script, and\nthe deprecation notice we have created explicitly tells users to use\n`git remote rename`:\n\n    static void warn_about_deprecated_remote_type(const char *type,\n                              const struct remote *remote)\n    {\n        warning(_(\"reading remote from \\\"%s/%s\\\", which is nominated for removal.\\n\"\n              \"\\n\"\n              \"If you still use the \\\"remotes/\\\" directory it is recommended to\\n\"\n              \"migrate to config-based remotes:\\n\"\n              \"\\n\"\n              \"\\tgit remote rename %s %s\\n\"\n              \"\\n\"\n              \"If you cannot, please let us know why you still need to use it by\\n\"\n              \"sending an e-mail to <git@vger.kernel.org>.\"),\n            type, remote->name, remote->name, remote->name);\n    }\n\nSo I highly doubt this script plays any role in the migration towards\nconfig-based remotes. Also because the script only cares about the\n\"remotes/\" directory -- it doesn't even know to migrate remotes from\n\"branches/\".\n\nAnother data point: the script doesn't even work as-is, as it sources\n\". git-sh-setup\". So for it to work it would need to be installed into\n\"$prefix/libexec/git-core\" together with our other tools, or it needs to\nbe called so that \"git-sh-setup\" is in the user's PATH. Both of which\nseems quite unlikely to me.\n\nAll of this reasoning should of course go into the commit message.\n\nPatrick\n"},{"id":"517442","messageId":"aBr9ZhSmbYtRp6o0@pks.im","threadId":"63406","inReplyTo":"xmqqjz6tikak.fsf@gitster.g","subject":"Re: [PATCH 10/10] contrib: remove \"git-new-workdir\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-07T06:27:50Z","receivedAt":"2025-05-07T06:27:54Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, May 06, 2025 at 12:57:55PM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > The \"git-new-workdir\" command has been introduced to make it possible to\n> > have a separate working directory in a different place. The command thus\n> > predates git-worktree(1), which is what people use nowadays to create\n> > any such working directory. As such, the script doesn't really have much\n> > of a reason to exist nowadays anymore.\n> \n> Yup.  As the original inventor of the concept, I still have one \"new\n> workdir\" I use daily, without any problems, so I think the layout it\n> creates (actually, it created 10 years ago) is more or less still OK\n> with the current binary.\n> \n> But even I do not create a new one (not that I run \"git worktree\" to\n> create an equivalent all that often).  I do not have an objection\n> against the removal.\n> \n> >  contrib/workdir/.gitattributes  |   1 -\n> >  contrib/workdir/git-new-workdir | 105 ----------------------------------------\n> >  t/meson.build                   |   1 -\n> >  t/t1021-rerere-in-workdir.sh    |  58 ----------------------\n> >  t/t3000-ls-files-others.sh      |  19 --------\n> >  5 files changed, 184 deletions(-)\n> \n> Oops, do we have tests that depend on contrib/ stuff (other than\n> completion and prompt, which should have moved up long ago)?  That\n> changes the story a bit.\n\nThe tests we have only verify that unrelated feature A works well with\nworkdirs created by git-new-workdir(1). So that alone isn't a good\nenough reason from my point of view to do a full deprecation cycle.\n\n> Shouldn't we do the usual WITH_BREAKING_CHANGES transition, then?\n\nThat is a very good question, and something that we should hash out as\npart of this cleanup.\n\nAs you have mentioned, \"contrib/\" nowadays is expected to work as a\nstaging area for features that aren't quite ready yet, but that should\neventually move into Git proper. But if that future never materializes,\neither because we notice that the feature just doesn't work well or that\nthe interest goes away, then it should be fine to drop that feature\nwithout a full deprecation cycle.\n\nTo me this feels a bit like the whole point about \"contrib/\": it is an\nstepping stone to get something into Git, but we don't provied any\nguarantees until it has been promoted into Git core.\n\nI know that this is certainly a revisionist attitude, as \"contrib/\"\nhasn't really worked like that in the past. I think though that we have\noutgrown the old concept of \"contrib/\" for quite a while already, and\nthat it doesn't really work well in its current form. The ecosystem\naround Git has grown big enough that we don't want to and cannot host it\nas part of Git itself anymore, so that original need has gone away.\n\nSo I think we should rework our \"contrib/\" policy so that it serves a\nbetter purpose in the modern Git ecosystem. It should be a staging area\nfor new features that are experimental. We don't provide guarantees, but\nwe will ensure that things don't bitrot. If they do start to bitrot and\nnobody cares, we throw them out. If we eventually see that the feature\nis getting nowhere, we throw it out. Otherwise, if the feature is\neventually deemed to be stable enough, we promote it to Git proper and\nare happy everafter.\n\nIf this is something that the project can agree on I'm happy to rewrite\n\"contrib/README\" accordingly. Maybe I should even do it without having\nany consent yet so that it can serve as a starting point for discussion.\n\nPatrick\n"},{"id":"517443","messageId":"aBr9bwNQ1J46NNXI@pks.im","threadId":"63406","inReplyTo":"CAPig+cRxDQBmPu_-ci5vEuwtsAHadfCiFOccdYseBSj2F52JGw@mail.gmail.com","subject":"Re: [PATCH 00/10] Spring cleanup of \"contrib/\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-07T06:27:59Z","receivedAt":"2025-05-07T06:28:02Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, May 06, 2025 at 11:55:20PM -0400, Eric Sunshine wrote:\n> On Tue, May 6, 2025 at 9:32 PM Todd Zullinger <tmz@pobox.com> wrote:\n> > Eric Sunshine wrote:\n> > > Although we periodically hear that someone uses it, git-contacts\n> > > probably falls into the \"tool has a clear alternative\" category. In\n> > > this case, git-related[*] is a separately-maintained more functional\n> > > drop-in replacement which people could be using instead of\n> > > git-contacts.\n> > >\n> > > [*]: https://github.com/felipec/git-related/blob/master/git-related\n> >\n> > It may be worth noting that git-contacts is suggested in\n> > both MyFirstContribution.adoc and SubmittingPatches.\n> \n> I think I knew but forgot about those mentions. Certainly useful\n> information if Patrick decides to pursue retirement of git-contacts.\n\nI thought about moving git-contacts out of \"contrib/\" into \"tools/\"\nexactly because of that. Tooling related to working with Git itself\nshouldn't live in \"contrib/\" in the first place, but really should have\nits own home in the Git project.\n\n> > It probably helps that since 824503ce88 (SubmittingPatches:\n> > clarify 'git-contacts' location, 2024-04-18) there has been\n> > a note stating this isn't \"part of the core `git` binary and\n> > must be called directly.\" That is relatively recent, though.\n> \n> Out of curiosity, I Googled git-contacts but didn't find any\n> meaningful hits. Pretty much the only pages found were renderings of\n> Git's documentation (including SubmittingPatches and the man page for\n> git-contacts itself), as well as the few patches to the Git mailing\n> list which introduced or touched git-contacts over the years. I did\n> not find any general discussion or recommendations to use\n> git-contacts, so perhaps it indeed is not very much used.\n> \n> > I added git-contacts to the Fedora git packaging shortly\n> > after 92a5dbbc22 (SubmittingPatches: mention the git\n> > contacts command, 2018-04-11), presuming some readers would\n> > want to use it.  (I never want to penalize users who are\n> > diligent enough to read SubmittingPatches. :)\n> >\n> > All that said, I don't have any strong opinion on whether it\n> > is kept or removed, let alone when that might happen.  I\n> > don't know that I've ever used it, other than for testing\n> > that it worked while packaging it.\n> \n> I've never used git-contacts either, despite the fact that I'm the one\n> who ported Felipe's git-related from Ruby[1] to Perl[2] for inclusion\n> in Git's \"contrib\" since the Ruby version had been rejected due to\n> being written in a language not already employed elsewhere in the\n> project. The Perl rewrite also included a number of useful\n> enhancements which Felipe later incorporated into git-related after he\n> published it as a standalone project. He has since extended it to\n> include even more features, so it's functionally a superset of\n> git-contacts.\n> \n> By the way, Felipe also sent a patch series[3] eleven years ago with\n> the same intention of Patrick's series under discussion. Felipe's\n> series was never picked up but did undertake the retirement of\n> git-contacts.\n\nThanks for the pointers.\n\nPatrick\n"},{"id":"517446","messageId":"aBsEooYbNl1cjlFO@pks.im","threadId":"63406","inReplyTo":"xmqq34dhijnq.fsf@gitster.g","subject":"Re: [PATCH 08/10] contrib: remove \"git-resurrect.sh\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-07T06:58:42Z","receivedAt":"2025-05-07T06:58:47Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, May 06, 2025 at 01:11:37PM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > The \"git-resurrect.sh\" script can be used to find traces of a branch tip\n> > in the reflog and resurrect that branch. Despite a couple of global\n> > cleanups, the script hasn't seen any activity since it was introduced in\n> > e1ff064e1bf (contrib git-resurrect: find traces of a branch name and\n> > resurrect it, 2009-02-04).\n> \n> A single-purpose thing that is done correctly on top of a right\n> abstraction does not necessarily need further updates, so I doubt\n> this paragraph contributes to the decision to remove the script in\n> any way.\n> \n> Having said that, I would not be surprised at all if large bugs\n> still remain in the script.  The reason why we scarcely heard\n> complaints about it is due to the fact that people simply are not\n> aware of it, people do not lose branches too often, and when it\n> happens, it is crystal clear what needs to be done with the output\n> of \"git reflog HEAD@{0}\", once people learn about \"git reflog\".\n> Even though it may be tedious to inspect \"git reflog\" output and\n> pick the right record to use with \"git branch\" to resurrect, as long\n> as it is a one-off thing, it would be more assuring to end-users than\n> some rarely used script with no correctness guarantee magically picks\n> a commit to place on the \"resurrected\" branch tip, I suspect.\n> \n> So I personally do not think many people shed tears if we remove\n> this script.  I am for its removal.\n> \n> Do we have a better alternative we officially support, by the way?\n\nNot that I'd know of, no, but I also had the same thought. If this was a\ncommon scenario then we really should provide an easy user interface for\nit. The question is whether it does come up often.\n\nIn any case, the best solution from my perspective would be to stop\ndeleting reflogs for deleted branches, even if only for the reftable\nbackend. We don't have to worry about file/directory conflicts there, so\nthere isn't really a good reason why reflogs need to be deleted with\ntheir branch. I would even say that it's counter to the intent to\nreflogs in the first place: the intent is to restore old versions of a\nspecific reference, and I cannot see a good reason why a user wouldn't\nwant to do that even for deleted refs.\n\nIf we did that, a user can trivially learn about the old state of that\nbranch (and even states before!) even after they have accidentally\ndeleted it. Just type `git reflog show refs/heads/branch` and you got\nit. There wouldn't even be any extra need for a new command, this feels\nsimple enough to me.\n\nPatrick\n"},{"id":"517467","messageId":"108f297a-b415-4742-80e4-51ea02af18e9@matthieu-moy.fr","threadId":"63406","inReplyTo":"xmqqr011h41x.fsf@gitster.g","subject":"Re: [PATCH 06/10] contrib: remove \"mw-to-git\"","fromName":"Matthieu Moy","fromEmail":"git@matthieu-moy.fr","sentAt":"2025-05-07T09:11:45Z","receivedAt":"2025-05-07T09:46:19Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"On 5/6/25 22:34, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n>> The \"mw-to-git\" directory contains tools for accessing MediaWiki via\n>> Git. The scripts are essentially unmaintained in Git: despite a couple\n>> of global cleanups, the last changes were a couple of security-related\n>> issues part of 9a8606465e8 (remote-mediawiki: use \"sh\" to eliminate\n>> unquoted commands, 2020-09-21) and its parents. We don't ever run any of\n>> the tests so it is more likely than not that many of the tests have been\n>> bitrotting, like e.g. documented in f8ab018dafc (remote-mediawiki tests:\n>> annotate failing tests, 2020-09-21).\n>>\n>> Furthermore, the code has been spun out into a separate project at [2].\n>> Remove the directory in favor of this new home.\n >>\n >> [2]: https://github.com/Git-Mediawiki/Git-Mediawiki\n\nAFAICT, there was no activity at all on this project, neither in \ncontrib/ nor on GitHub, for years. I'm not using it anymore and lack \nmotivation to maintain/contribute to it, and no one really stepped in to \ntake over.\n\n> OK, the new home also lacks activity since Mar 2022, but it\n> still is certainly a much better home than having it here.\n\nPerhaps it's a grave more than a home, but that's indeed a better place \nfor the project to rest.\n\n> And I doubt this removal needs to wait for Git 3.0 boundary;\n\nRight, actually IIRC we already discussed its removal and it could have \nbeen removed long ago already.\n\nAnyway, many thanks to this list for the help building up this project \nwhen we created it, loooong ago!\n\n-- \nMatthieu Moy\n\n"},{"id":"517506","messageId":"xmqq1pt0e3qk.fsf@gitster.g","threadId":"63406","inReplyTo":"aBr9VuYurCCvRYnj@pks.im","subject":"Re: [PATCH 01/10] contrib: remove \"remotes2config.sh\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-05-07T17:21:23Z","receivedAt":"2025-05-07T17:21:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> the deprecation notice we have created explicitly tells users to use\n> `git remote rename`:\n\nGood.  Then let's do this together with others that we can do\nimmediately.\n\nThanks.\n\n> All of this reasoning should of course go into the commit message.\n"},{"id":"517507","messageId":"xmqqwmascoyj.fsf@gitster.g","threadId":"63406","inReplyTo":"aBr9ZhSmbYtRp6o0@pks.im","subject":"Re: [PATCH 10/10] contrib: remove \"git-new-workdir\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-05-07T17:25:56Z","receivedAt":"2025-05-07T17:25:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> hasn't really worked like that in the past. I think though that we have\n> outgrown the old concept of \"contrib/\" for quite a while already, and\n> that it doesn't really work well in its current form. The ecosystem\n> around Git has grown big enough that we don't want to and cannot host it\n> as part of Git itself anymore, so that original need has gone away.\n\nYes.  Thanks for saying this again---I think I said something along\nthe line from time to time for the past 10 years ;-).  The ecosystem\nis mature enough that we no longer have to ship \"batteries included\"\nmanner to make the initial end-user experience of Git tolerable.\n\n> If this is something that the project can agree on I'm happy to rewrite\n> \"contrib/README\" accordingly. Maybe I should even do it without having\n> any consent yet so that it can serve as a starting point for discussion.\n\nYeah, the latter is a good attitude to see.  It is way easier for\neverybody involved what the issues are if we have something to base\nour discussion on.\n\nThanks.\n\n"},{"id":"517509","messageId":"xmqqjz6scnxo.fsf@gitster.g","threadId":"63406","inReplyTo":"aBsEooYbNl1cjlFO@pks.im","subject":"Re: [PATCH 08/10] contrib: remove \"git-resurrect.sh\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-05-07T17:48:03Z","receivedAt":"2025-05-07T17:48:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> If we did that, a user can trivially learn about the old state of that\n> branch (and even states before!) even after they have accidentally\n> deleted it. Just type `git reflog show refs/heads/branch` and you got\n> it. There wouldn't even be any extra need for a new command, this feels\n> simple enough to me.\n\nIf we have a way to enumerate reflogs for branches, including the\nones that have been deleted, then the user can learn what to give to\n\"git reflog refs/heads/<branchname-they-no-longer-remember>\", so all\nwill work well.  I like the direction.\n"},{"id":"517512","messageId":"3fff50c2-d36b-451e-b324-aa00f8548bf5@app.fastmail.com","threadId":"63406","inReplyTo":"xmqq34dhijnq.fsf@gitster.g","subject":"Re: [PATCH 08/10] contrib: remove \"git-resurrect.sh\"","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-05-07T18:36:26Z","receivedAt":"2025-05-07T18:36:47Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Tue, May 6, 2025, at 22:11, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n>\n>> The \"git-resurrect.sh\" script can be used to find traces of a branch tip\n>> in the reflog and resurrect that branch. Despite a couple of global\n>> cleanups, the script hasn't seen any activity since it was introduced in\n>> e1ff064e1bf (contrib git-resurrect: find traces of a branch name and\n>> resurrect it, 2009-02-04).\n>\n> A single-purpose thing that is done correctly on top of a right\n> abstraction does not necessarily need further updates, so I doubt\n> this paragraph contributes to the decision to remove the script in\n> any way.\n>\n> Having said that, I would not be surprised at all if large bugs\n> still remain in the script.  The reason why we scarcely heard\n> complaints about it is due to the fact that people simply are not\n> aware of it, people do not lose branches too often, and when it\n> happens, it is crystal clear what needs to be done with the output\n> of \"git reflog HEAD@{0}\", once people learn about \"git reflog\".\n> Even though it may be tedious to inspect \"git reflog\" output and\n> pick the right record to use with \"git branch\" to resurrect, as long\n> as it is a one-off thing, it would be more assuring to end-users than\n> some rarely used script with no correctness guarantee magically picks\n> a commit to place on the \"resurrected\" branch tip, I suspect.\n\nThe top-voted answer here has 3029 votes:\nhttps://stackoverflow.com/a/3640806/\n\nWhich is in fact to just use the reflog manually.\n\nThe git-resurrect.sh answer was added two days later and has 16 votes:\nhttps://stackoverflow.com/a/3653931/\n\nGiven this vote-split,[1] it seems people would rather do some manual\nreflog work rather than figure out how to call scripts from contrib/.\n\nAlso the answer was by Jakub Narębski.  (Not a normal user who happened\nto find contrib/)\n\nIt seems that many people delete branches they would like back at least\none time during their tenure.  But they don’t seem to want to streamline\nthat process.  Which makes sense considering the previous discussion\nhere.\n\n† 1: Potentially popular answers can get buried on StackOverflow if they\n    are posted much later.  But this does not seem to be the case here.\n\n    Also a 337 vote answer was posted over three years later.[1]\n    Although that’s a more advanced answer which goes into using\n    git-fsck(1).\n\n    [2]: https://stackoverflow.com/a/22303923/\n"},{"id":"517631","messageId":"aB20kvkNy48JnJc0@pks.im","threadId":"63406","inReplyTo":"xmqqwmascoyj.fsf@gitster.g","subject":"Re: [PATCH 10/10] contrib: remove \"git-new-workdir\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-09T07:53:54Z","receivedAt":"2025-05-09T07:54:03Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, May 07, 2025 at 10:25:56AM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> > If this is something that the project can agree on I'm happy to rewrite\n> > \"contrib/README\" accordingly. Maybe I should even do it without having\n> > any consent yet so that it can serve as a starting point for discussion.\n> \n> Yeah, the latter is a good attitude to see.  It is way easier for\n> everybody involved what the issues are if we have something to base\n> our discussion on.\n\nOkay, I'll do that in a follow-up patch series.\n\nPatrick\n"},{"id":"517638","messageId":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","subject":"[PATCH v2 00/11] Spring cleanup of \"contrib/\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-09T09:17:00Z","receivedAt":"2025-05-09T09:17:13Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nas I have lamented multiple times multiple times already (e.g. [1]), the\n\"contrib/\" directory is a bit of a mess containing many bits and pieces\nthat just sit there gathering dust, without getting any maintenance and\nsometimes even in a clearly-broken state. So I decided to finally bite\nthe bullet and do a spring cleanup of \"contrib/\", which resulted in this\npatch series here.\n\nI have used the following reasons for removal:\n\n  - The tool is clearly broken, e.g. it doesn't even compile.\n\n  - The tool hasn't received any updates for at least the last 5 years.\n\n  - The tool has a clear alternative or just isn't useful anymore.\n\nThe next step for me would be to spell out a policy around \"contrib/\" to\nmatch what Junio has said [2]:\n\n    Things in contrib/ should either move up (to become a part of the\n    core), move out (to become an independent project), or disappear.\n\nWith this model, \"contrib/\" would be closer to Linux' staging drivers\nwith the expectation that a tool should eventually be part of proper Git\nin case it proves to be useful, or booted out when it doesn't seem to be\ngetting there.\n\nAnother subsequent step would be to split out some parts of \"contrib/\"\nto be hosted in their own hierarchy. CMake, Coccinelle, Unicode updates,\nVScode and the like are all tools that are used during development, so\nthey should probably not be part of \"contrib/\" but rather of a new\n\"tools/\" hierarchy (we can bikeshed the name at a later point, I'm not\nyet doing that in this series).\n\nThere's also other bits and pieces that serve as examples. I think we\nshould move these into our documentation instead of having those in\n\"contrib/\".\n\nAnway, this series here is just the first step.\n\nChanges in v2:\n  - Remove \"contrib/stats\".\n  - Provide some more details about why specific tools should be safe to\n    be removed.\n  - Link to v1: https://lore.kernel.org/r/20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im\n\nThanks!\n\nPatrick\n\n[1]: <aBhZHA7av8bWH9Ac@pks.im>\n[2]: <xmqq5xieq3fs.fsf@gitster.g>\n\n---\nPatrick Steinhardt (11):\n      contrib: remove \"remotes2config.sh\"\n      contrib: remove \"examples\" directory\n      contrib: remove remote-helper stubs\n      contrib: remove \"thunderbird-patch-inline\"\n      contrib: remove \"hooks\" directory\n      contrib: remove \"mw-to-git\"\n      contrib: remove \"persistent-https\" remote helper\n      contrib: remove \"git-resurrect.sh\"\n      contrib: remove \"emacs\" directory\n      contrib: remove \"git-new-workdir\"\n      contrib: remove \"stats\" directory\n\n contrib/emacs/README                               |   33 -\n contrib/emacs/git-blame.el                         |    6 -\n contrib/emacs/git.el                               |    6 -\n contrib/examples/README                            |   20 -\n contrib/git-resurrect.sh                           |  181 ---\n contrib/hooks/multimail/README.Git                 |    7 -\n contrib/hooks/post-receive-email                   |  759 -----------\n contrib/hooks/pre-auto-gc-battery                  |   42 -\n contrib/hooks/setgitperms.perl                     |  214 ---\n contrib/hooks/update-paranoid                      |  421 ------\n contrib/mw-to-git/.gitignore                       |    2 -\n contrib/mw-to-git/.perlcriticrc                    |   28 -\n contrib/mw-to-git/Git/Mediawiki.pm                 |  101 --\n contrib/mw-to-git/Makefile                         |   61 -\n contrib/mw-to-git/bin-wrapper/git                  |   14 -\n contrib/mw-to-git/git-mw.perl                      |  368 ------\n contrib/mw-to-git/git-remote-mediawiki.perl        | 1390 --------------------\n contrib/mw-to-git/git-remote-mediawiki.txt         |    7 -\n contrib/mw-to-git/t/.gitignore                     |    4 -\n contrib/mw-to-git/t/Makefile                       |   32 -\n contrib/mw-to-git/t/README                         |  124 --\n contrib/mw-to-git/t/install-wiki.sh                |   55 -\n contrib/mw-to-git/t/push-pull-tests.sh             |  144 --\n contrib/mw-to-git/t/t9360-mw-to-git-clone.sh       |  257 ----\n contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh   |   24 -\n contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh        |  347 -----\n .../mw-to-git/t/t9363-mw-to-git-export-import.sh   |  218 ---\n contrib/mw-to-git/t/t9364-pull-by-rev.sh           |   17 -\n contrib/mw-to-git/t/t9365-continuing-queries.sh    |   23 -\n contrib/mw-to-git/t/test-gitmw-lib.sh              |  432 ------\n contrib/mw-to-git/t/test-gitmw.pl                  |  223 ----\n contrib/mw-to-git/t/test.config                    |   40 -\n contrib/persistent-https/LICENSE                   |  202 ---\n contrib/persistent-https/Makefile                  |   43 -\n contrib/persistent-https/README                    |   72 -\n contrib/persistent-https/client.go                 |  189 ---\n contrib/persistent-https/main.go                   |   82 --\n contrib/persistent-https/proxy.go                  |  190 ---\n contrib/persistent-https/socket.go                 |   97 --\n contrib/remote-helpers/README                      |   15 -\n contrib/remote-helpers/git-remote-bzr              |   11 -\n contrib/remote-helpers/git-remote-hg               |   11 -\n contrib/remotes2config.sh                          |   33 -\n contrib/stats/git-common-hash                      |   26 -\n contrib/stats/mailmap.pl                           |   70 -\n contrib/stats/packinfo.pl                          |  212 ---\n contrib/thunderbird-patch-inline/README            |   20 -\n contrib/thunderbird-patch-inline/appp.sh           |   55 -\n contrib/workdir/.gitattributes                     |    1 -\n contrib/workdir/git-new-workdir                    |  105 --\n t/meson.build                                      |    1 -\n t/t1021-rerere-in-workdir.sh                       |   58 -\n t/t3000-ls-files-others.sh                         |   19 -\n 53 files changed, 7112 deletions(-)\n\nRange-diff versus v1:\n\n 1:  2554d816f13 !  1:  f5208d15e68 contrib: remove \"remotes2config.sh\"\n    @@ Commit message\n         mechanism to use config-based remotes. Notably though, the migration\n         path doesn't even use the migration script. Instead, git-remote(1)\n         itself knows how to migrate any such remote via `git remote rename`.\n    -    Furthermore, the script hasn't been touched since afa75bc8aa1 (contrib:\n    -    Make remotes2config.sh script more robust, 2007-12-02).\n     \n    -    Given that there is a migration path without this script it is very\n    -    unlikely that anyone still uses the script. Remove it.\n    +    In fact, a full migration _cannot_ use the script as it only knows to\n    +    migrate remotes from \".git/remotes/\", but not \".git/branches/\". As such,\n    +    the migration path via `git remote rename` is the only feasible way to\n    +    fully migrate repositories over to the new format.\n    +\n    +    Last but not least, the script doesn't even work as-is as it sources\n    +    \"git-sh-setup\". For this to work it would need to be invoked either via\n    +    Git so that this script is in our PATH, users would have to manually\n    +    call it with an adjusted PATH, or distributions need to install the\n    +    script into \"$prefix/libexec/git-core\" with a \"git-\" prefix. All of\n    +    these steps are unlikely enough to underpin the claim that this script\n    +    is not used at all.\n    +\n    +    So given that:\n    +\n    +      - The script cannot perform a full migration of all deprecated remote\n    +        types.\n    +\n    +      - We don't advertise it anywhere.\n    +\n    +      - It has been basically untouched since 2007.\n    +\n    +      - It doesn't even work unless users do manual steps.\n    +\n    +    It should be safe enough to just remove it. Do so.\n     \n         Signed-off-by: Patrick Steinhardt <ps@pks.im>\n     \n 2:  bfd219d38e6 =  2:  5da41b6f6ff contrib: remove \"examples\" directory\n 3:  23b029002fc =  3:  cb0307a65dd contrib: remove remote-helper stubs\n 4:  2ae101c8533 =  4:  8156f427807 contrib: remove \"thunderbird-patch-inline\"\n 5:  a2e22a4ce7e =  5:  7fbc6d73eb1 contrib: remove \"hooks\" directory\n 6:  54df2acaf10 !  6:  1267e17e14a contrib: remove \"mw-to-git\"\n    @@ Commit message\n         bitrotting, like e.g. documented in f8ab018dafc (remote-mediawiki tests:\n         annotate failing tests, 2020-09-21).\n     \n    -    Furthermore, the code has been spun out into a separate project at [2].\n    -    Remove the directory in favor of this new home.\n    +    According to Matthieu Moy [1], one of the original developers of this\n    +    tool, it didn't receive any attention recently and there is no\n    +    motivation to keep maintaining it anymore in the community. The project\n    +    has been spun out of Git [2] and thus has a new official home, but did\n    +    not receive much attention over there, either.\n     \n    +    As such, it seems like the MediaWiki transport helper is slowly fading\n    +    away. But given that there is a new home, it doesn't make sense to have\n    +    it as part of Git anymore only to let it rot. Remove the directory.\n    +\n    +    [1]: <108f297a-b415-4742-80e4-51ea02af18e9@matthieu-moy.fr>\n         [2]: https://github.com/Git-Mediawiki/Git-Mediawiki\n     \n         Signed-off-by: Patrick Steinhardt <ps@pks.im>\n 7:  80862dd3f3a =  7:  711a73eed73 contrib: remove \"persistent-https\" remote helper\n 8:  5bff15bb57c !  8:  8e5cf72a292 contrib: remove \"git-resurrect.sh\"\n    @@ Commit message\n         e1ff064e1bf (contrib git-resurrect: find traces of a branch name and\n         resurrect it, 2009-02-04).\n     \n    -    Remove it.\n    +    Furthermore, the tool does not work with the \"reftable\" backend at all\n    +    as it directly reads \".git/logs/HEAD\". As reflogs are stored as part of\n    +    the individual tables though that file wouldn't exist in a \"reftable\"-\n    +    enabled repository.\n    +\n    +    Last but not least, the tool doesn't even work unless it is explicitly\n    +    invoked via `git resurrect` as it sources \"git-sh-setup\". As none of our\n    +    build systems know to install this script, users thus have to go out of\n    +    their way to really make it work, which is highly unlikely.\n    +\n    +    Another source that indicates that this tool can be removed is a\n    +    question for how to restore deleted branches on StackOverflow [1]. The\n    +    top-voted answer uses git-reflog(1) directly and has received more than\n    +    3000 votes to date. While \"git-resurrect.sh\" is also mentioned, it only\n    +    got 16 upvotes, and comments mention the above caveat that users have to\n    +    do some manual setup to make it work.\n    +\n    +    It's thus rather clear that the tool doesn't have a lot or even any\n    +    users. Remove it.\n    +\n    +    [1]: https://stackoverflow.com/questions/3640764/can-i-recover-a-branch-after-its-deletion-in-git\n     \n         Signed-off-by: Patrick Steinhardt <ps@pks.im>\n     \n 9:  8ec63910c32 =  9:  ae90457d6cc contrib: remove \"emacs\" directory\n10:  5f673e64206 = 10:  a9258add931 contrib: remove \"git-new-workdir\"\n -:  ----------- > 11:  fda94bd7579 contrib: remove \"stats\" directory\n\n---\nbase-commit: 6f84262c44a89851c3ae5a6e4c1a9d06b2068d75\nchange-id: 20250506-pks-contrib-spring-cleanup-bb23b15d6df4\n\n"},{"id":"517639","messageId":"20250509-pks-contrib-spring-cleanup-v2-1-14e20d95ec68@pks.im","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","subject":"[PATCH v2 01/11] contrib: remove \"remotes2config.sh\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-09T09:17:01Z","receivedAt":"2025-05-09T09:17:13Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Remotes can be configured either via a repository's config or by using\nthe \".git/branches/\" or \".git/remotes/\" directories. Back when the new\nconfig-based mechanism has been introduced we also introduced a helper\nscript that migrates from the old-style remote configuration to the new\nconfig-based mechanism.\n\nWith the recent removal announcement for the two directories we also\nstarted to instruct users to migrate repositories that still use these\nmechanism to use config-based remotes. Notably though, the migration\npath doesn't even use the migration script. Instead, git-remote(1)\nitself knows how to migrate any such remote via `git remote rename`.\n\nIn fact, a full migration _cannot_ use the script as it only knows to\nmigrate remotes from \".git/remotes/\", but not \".git/branches/\". As such,\nthe migration path via `git remote rename` is the only feasible way to\nfully migrate repositories over to the new format.\n\nLast but not least, the script doesn't even work as-is as it sources\n\"git-sh-setup\". For this to work it would need to be invoked either via\nGit so that this script is in our PATH, users would have to manually\ncall it with an adjusted PATH, or distributions need to install the\nscript into \"$prefix/libexec/git-core\" with a \"git-\" prefix. All of\nthese steps are unlikely enough to underpin the claim that this script\nis not used at all.\n\nSo given that:\n\n  - The script cannot perform a full migration of all deprecated remote\n    types.\n\n  - We don't advertise it anywhere.\n\n  - It has been basically untouched since 2007.\n\n  - It doesn't even work unless users do manual steps.\n\nIt should be safe enough to just remove it. Do so.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/remotes2config.sh | 33 ---------------------------------\n 1 file changed, 33 deletions(-)\n\ndiff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh\ndeleted file mode 100755\nindex 1cda19f66af..00000000000\n--- a/contrib/remotes2config.sh\n+++ /dev/null\n@@ -1,33 +0,0 @@\n-#!/bin/sh\n-\n-# Use this tool to rewrite your .git/remotes/ files into the config.\n-\n-. git-sh-setup\n-\n-if [ -d \"$GIT_DIR\"/remotes ]; then\n-\techo \"Rewriting $GIT_DIR/remotes\" >&2\n-\terror=0\n-\t# rewrite into config\n-\t{\n-\t\tcd \"$GIT_DIR\"/remotes\n-\t\tls | while read f; do\n-\t\t\tname=$(printf \"$f\" | tr -c \"A-Za-z0-9-\" \".\")\n-\t\t\tsed -n \\\n-\t\t\t-e \"s/^URL:[ \t]*\\(.*\\)$/remote.$name.url \\1 ./p\" \\\n-\t\t\t-e \"s/^Pull:[ \t]*\\(.*\\)$/remote.$name.fetch \\1 ^$ /p\" \\\n-\t\t\t-e \"s/^Push:[ \t]*\\(.*\\)$/remote.$name.push \\1 ^$ /p\" \\\n-\t\t\t< \"$f\"\n-\t\tdone\n-\t\techo done\n-\t} | while read key value regex; do\n-\t\tcase $key in\n-\t\tdone)\n-\t\t\tif [ $error = 0 ]; then\n-\t\t\t\tmv \"$GIT_DIR\"/remotes \"$GIT_DIR\"/remotes.old\n-\t\t\tfi ;;\n-\t\t*)\n-\t\t\techo \"git config $key \"$value\" $regex\"\n-\t\t\tgit config $key \"$value\" $regex || error=1 ;;\n-\t\tesac\n-\tdone\n-fi\n\n-- \n2.49.0.1077.gc0e912fd4c.dirty\n\n"},{"id":"517641","messageId":"20250509-pks-contrib-spring-cleanup-v2-2-14e20d95ec68@pks.im","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","subject":"[PATCH v2 02/11] contrib: remove \"examples\" directory","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-09T09:17:02Z","receivedAt":"2025-05-09T09:17:14Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"examples\" directory used to contain scripted versions of some of\nour builtins. These have all been removed in 49eb8d39c78 (Remove\ncontrib/examples/*, 2018-03-25), but we left a note in the directory to\nmake it discoverable that there used to be examples.\n\nIt is unlikely that anybody still looks at these examples more than 7\nyears after they have been removed. Remove the note and its directory.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/examples/README | 20 --------------------\n 1 file changed, 20 deletions(-)\n\ndiff --git a/contrib/examples/README b/contrib/examples/README\ndeleted file mode 100644\nindex 18bc60b021b..00000000000\n--- a/contrib/examples/README\n+++ /dev/null\n@@ -1,20 +0,0 @@\n-This directory used to contain scripted implementations of builtins\n-that have since been rewritten in C.\n-\n-They have now been removed, but can be retrieved from an older commit\n-that removed them from this directory.\n-\n-They're interesting for their reference value to any aspiring plumbing\n-users who want to learn how pieces can be fit together, but in many\n-cases have drifted enough from the actual implementations Git uses to\n-be instructive.\n-\n-Other things that can be useful:\n-\n- * Some commands such as git-gc wrap other commands, and what they're\n-   doing behind the scenes can be seen by running them under\n-   GIT_TRACE=1\n-\n- * Doing `git log` on paths matching '*--helper.c' will show\n-   incremental effort in the direction of moving existing shell\n-   scripts to C.\n\n-- \n2.49.0.1077.gc0e912fd4c.dirty\n\n"},{"id":"517640","messageId":"20250509-pks-contrib-spring-cleanup-v2-3-14e20d95ec68@pks.im","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","subject":"[PATCH v2 03/11] contrib: remove remote-helper stubs","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-09T09:17:03Z","receivedAt":"2025-05-09T09:17:15Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"remote-helpers\" directory contains two remote helper scripts for\nMercurial and Bazaar. These scripts have since been converted into stubs\nin b2c851a8e67 (Revert \"Merge branch 'jc/graduate-remote-hg-bzr' (early\npart)\", 2014-05-20) as the helpers have been moved into their own\nupstream projects [1][2].\n\nGiven that these stubs have been created more than a decade ago it is\nvery unlikely that anybody still tries to use them. Remove them.\n\n[1]: https://github.com/felipec/git-remote-bzr\n[1]: https://github.com/felipec/git-remote-hg\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/remote-helpers/README         | 15 ---------------\n contrib/remote-helpers/git-remote-bzr | 11 -----------\n contrib/remote-helpers/git-remote-hg  | 11 -----------\n 3 files changed, 37 deletions(-)\n\ndiff --git a/contrib/remote-helpers/README b/contrib/remote-helpers/README\ndeleted file mode 100644\nindex ac72332517a..00000000000\n--- a/contrib/remote-helpers/README\n+++ /dev/null\n@@ -1,15 +0,0 @@\n-The remote-helper bridges to access data stored in Mercurial and\n-Bazaar are maintained outside the git.git tree in the repositories\n-of their primary author:\n-\n-    https://github.com/felipec/git-remote-hg (for Mercurial)\n-    https://github.com/felipec/git-remote-bzr (for Bazaar)\n-\n-You can pick a directory on your $PATH and download them from these\n-repositories, e.g.:\n-\n-  $ wget -O $HOME/bin/git-remote-hg \\\n-    https://raw.github.com/felipec/git-remote-hg/master/git-remote-hg\n-  $ wget -O $HOME/bin/git-remote-bzr \\\n-    https://raw.github.com/felipec/git-remote-bzr/master/git-remote-bzr\n-  $ chmod +x $HOME/bin/git-remote-hg $HOME/bin/git-remote-bzr\ndiff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr\ndeleted file mode 100755\nindex 1c3d87f8619..00000000000\n--- a/contrib/remote-helpers/git-remote-bzr\n+++ /dev/null\n@@ -1,11 +0,0 @@\n-#!/bin/sh\n-\n-cat >&2 <<'EOT'\n-WARNING: git-remote-bzr is now maintained independently.\n-WARNING: For more information visit https://github.com/felipec/git-remote-bzr\n-WARNING:\n-WARNING: You can pick a directory on your $PATH and download it, e.g.:\n-WARNING:   $ wget -O $HOME/bin/git-remote-bzr \\\n-WARNING:     https://raw.github.com/felipec/git-remote-bzr/master/git-remote-bzr\n-WARNING:   $ chmod +x $HOME/bin/git-remote-bzr\n-EOT\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\ndeleted file mode 100755\nindex 8e9188364c6..00000000000\n--- a/contrib/remote-helpers/git-remote-hg\n+++ /dev/null\n@@ -1,11 +0,0 @@\n-#!/bin/sh\n-\n-cat >&2 <<'EOT'\n-WARNING: git-remote-hg is now maintained independently.\n-WARNING: For more information visit https://github.com/felipec/git-remote-hg\n-WARNING:\n-WARNING: You can pick a directory on your $PATH and download it, e.g.:\n-WARNING:   $ wget -O $HOME/bin/git-remote-hg \\\n-WARNING:     https://raw.github.com/felipec/git-remote-hg/master/git-remote-hg\n-WARNING:   $ chmod +x $HOME/bin/git-remote-hg\n-EOT\n\n-- \n2.49.0.1077.gc0e912fd4c.dirty\n\n"},{"id":"517642","messageId":"20250509-pks-contrib-spring-cleanup-v2-4-14e20d95ec68@pks.im","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","subject":"[PATCH v2 04/11] contrib: remove \"thunderbird-patch-inline\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-09T09:17:04Z","receivedAt":"2025-05-09T09:17:15Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"thunderbird-patch-inline\" directory in \"contrib/\" contains a script\nto send patch files via Thunderbird. This script depends on the\nExternalEditor extension [1], which seems to be effectively unmaintained\nwith the last update being in 2008. While the extension has eventually\nbeen maintained in [2], that fork hasn't received any updates since\n2020, either.\n\nIn addition, the script itself hasn't really seen a lot of maintenance\noutside of a couple of global cleanups. It is quite unlikely that this\nsetup still works, and if it did it's even less likely that somebody\nuses this script.\n\nRemove it.\n\n[1]: https://globs.org/articles.php?lng=en&pg=2\n[2]: https://github.com/exteditor/exteditor/releases\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/thunderbird-patch-inline/README  | 20 ------------\n contrib/thunderbird-patch-inline/appp.sh | 55 --------------------------------\n 2 files changed, 75 deletions(-)\n\ndiff --git a/contrib/thunderbird-patch-inline/README b/contrib/thunderbird-patch-inline/README\ndeleted file mode 100644\nindex 000147bbe4a..00000000000\n--- a/contrib/thunderbird-patch-inline/README\n+++ /dev/null\n@@ -1,20 +0,0 @@\n-appp.sh is a script that is supposed to be used together with ExternalEditor\n-for Mozilla Thunderbird. It will let you include patches inline in e-mails\n-in an easy way.\n-\n-Usage:\n-- Generate the patch with git format-patch.\n-- Start writing a new e-mail in Thunderbird.\n-- Press the external editor button (or Ctrl-E) to run appp.sh\n-- Select the previously generated patch file.\n-- Finish editing the e-mail.\n-\n-Any text that is entered into the message editor before appp.sh is called\n-will be moved to the section between the --- and the diffstat.\n-\n-All S-O-B:s and Cc:s in the patch will be added to the CC list.\n-\n-To set it up, just install External Editor and tell it to use appp.sh as the\n-editor.\n-\n-Zenity is a required dependency.\ndiff --git a/contrib/thunderbird-patch-inline/appp.sh b/contrib/thunderbird-patch-inline/appp.sh\ndeleted file mode 100755\nindex fdcc9483520..00000000000\n--- a/contrib/thunderbird-patch-inline/appp.sh\n+++ /dev/null\n@@ -1,55 +0,0 @@\n-#!/bin/sh\n-# Copyright 2008 Lukas Sandström <luksan@gmail.com>\n-#\n-# AppendPatch - A script to be used together with ExternalEditor\n-# for Mozilla Thunderbird to properly include patches inline in e-mails.\n-\n-# ExternalEditor can be downloaded at http://globs.org/articles.php?lng=en&pg=2\n-\n-CONFFILE=~/.appprc\n-\n-SEP=\"-=-=-=-=-=-=-=-=-=# Don't remove this line #=-=-=-=-=-=-=-=-=-\"\n-if [ -e \"$CONFFILE\" ] ; then\n-\tLAST_DIR=$(grep -m 1 \"^LAST_DIR=\" \"${CONFFILE}\"|sed -e 's/^LAST_DIR=//')\n-\tcd \"${LAST_DIR}\"\n-else\n-\tcd > /dev/null\n-fi\n-\n-PATCH=$(zenity --file-selection)\n-\n-if [ \"$?\" != \"0\" ] ; then\n-\t#zenity --error --text \"No patchfile given.\"\n-\texit 1\n-fi\n-\n-cd - > /dev/null\n-\n-SUBJECT=$(sed -n -e '/^Subject: /p' \"${PATCH}\")\n-HEADERS=$(sed -e '/^'\"${SEP}\"'$/,$d' $1)\n-BODY=$(sed -e \"1,/${SEP}/d\" $1)\n-CMT_MSG=$(sed -e '1,/^$/d' -e '/^---$/,$d' \"${PATCH}\")\n-DIFF=$(sed -e '1,/^---$/d' \"${PATCH}\")\n-\n-CCS=$(printf '%s\\n%s\\n' \"$CMT_MSG\" \"$HEADERS\" | sed -n -e 's/^Cc: \\(.*\\)$/\\1,/gp' \\\n-\t-e 's/^Signed-off-by: \\(.*\\)/\\1,/gp')\n-\n-echo \"$SUBJECT\" > $1\n-echo \"Cc: $CCS\" >> $1\n-echo \"$HEADERS\" | sed -e '/^Subject: /d' -e '/^Cc: /d' >> $1\n-echo \"$SEP\" >> $1\n-\n-echo \"$CMT_MSG\" >> $1\n-echo \"---\" >> $1\n-if [ \"x${BODY}x\" != \"xx\" ] ; then\n-\techo >> $1\n-\techo \"$BODY\" >> $1\n-\techo >> $1\n-fi\n-echo \"$DIFF\" >> $1\n-\n-LAST_DIR=$(dirname \"${PATCH}\")\n-\n-grep -v \"^LAST_DIR=\" \"${CONFFILE}\" > \"${CONFFILE}_\"\n-echo \"LAST_DIR=${LAST_DIR}\" >> \"${CONFFILE}_\"\n-mv \"${CONFFILE}_\" \"${CONFFILE}\"\n\n-- \n2.49.0.1077.gc0e912fd4c.dirty\n\n"},{"id":"517646","messageId":"20250509-pks-contrib-spring-cleanup-v2-5-14e20d95ec68@pks.im","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","subject":"[PATCH v2 05/11] contrib: remove \"hooks\" directory","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-09T09:17:05Z","receivedAt":"2025-05-09T09:17:16Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"hooks\" directory contains a handful of example hooks. Most of these\nhooks are highly specific and haven't really received any updates over\nthe last couple of years, except for some global cleanups. The multimail\nhook has also been removed in f74d11471fa (multimail: stop shipping a\ncopy, 2021-06-10) in favor of its upstream project [1].\n\nRemove those hooks. If we want to provide examples for how to use Git\nhooks we should do that as part of our documentation, for example in\ngithooks(5).\n\n[1]: https://github.com/git-multimail/git-multimail\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/hooks/multimail/README.Git |   7 -\n contrib/hooks/post-receive-email   | 759 -------------------------------------\n contrib/hooks/pre-auto-gc-battery  |  42 --\n contrib/hooks/setgitperms.perl     | 214 -----------\n contrib/hooks/update-paranoid      | 421 --------------------\n 5 files changed, 1443 deletions(-)\n\ndiff --git a/contrib/hooks/multimail/README.Git b/contrib/hooks/multimail/README.Git\ndeleted file mode 100644\nindex c427efc7bd8..00000000000\n--- a/contrib/hooks/multimail/README.Git\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-git-multimail is developed as an independent project at the following\n-website:\n-\n-    https://github.com/git-multimail/git-multimail\n-\n-Please refer to that project page for information about how to report\n-bugs or contribute to git-multimail.\ndiff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email\ndeleted file mode 100755\nindex ff565eb3d88..00000000000\n--- a/contrib/hooks/post-receive-email\n+++ /dev/null\n@@ -1,759 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (c) 2007 Andy Parkins\n-#\n-# An example hook script to mail out commit update information.\n-#\n-# NOTE: This script is no longer under active development.  There\n-# is another script, git-multimail, which is more capable and\n-# configurable and is largely backwards-compatible with this script;\n-# please see \"contrib/hooks/multimail/\".  For instructions on how to\n-# migrate from post-receive-email to git-multimail, please see\n-# \"README.migrate-from-post-receive-email\" in that directory.\n-#\n-# This hook sends emails listing new revisions to the repository\n-# introduced by the change being reported.  The rule is that (for\n-# branch updates) each commit will appear on one email and one email\n-# only.\n-#\n-# This hook is stored in the contrib/hooks directory.  Your distribution\n-# will have put this somewhere standard.  You should make this script\n-# executable then link to it in the repository you would like to use it in.\n-# For example, on debian the hook is stored in\n-# /usr/share/git-core/contrib/hooks/post-receive-email:\n-#\n-#  cd /path/to/your/repository.git\n-#  ln -sf /usr/share/git-core/contrib/hooks/post-receive-email hooks/post-receive\n-#\n-# This hook script assumes it is enabled on the central repository of a\n-# project, with all users pushing only to it and not between each other.  It\n-# will still work if you don't operate in that style, but it would become\n-# possible for the email to be from someone other than the person doing the\n-# push.\n-#\n-# To help with debugging and use on pre-v1.5.1 git servers, this script will\n-# also obey the interface of hooks/update, taking its arguments on the\n-# command line.  Unfortunately, hooks/update is called once for each ref.\n-# To avoid firing one email per ref, this script just prints its output to\n-# the screen when used in this mode.  The output can then be redirected if\n-# wanted.\n-#\n-# Config\n-# ------\n-# hooks.mailinglist\n-#   This is the list that all pushes will go to; leave it blank to not send\n-#   emails for every ref update.\n-# hooks.announcelist\n-#   This is the list that all pushes of annotated tags will go to.  Leave it\n-#   blank to default to the mailinglist field.  The announce emails lists\n-#   the short log summary of the changes since the last annotated tag.\n-# hooks.envelopesender\n-#   If set then the -f option is passed to sendmail to allow the envelope\n-#   sender address to be set\n-# hooks.emailprefix\n-#   All emails have their subjects prefixed with this prefix, or \"[SCM]\"\n-#   if emailprefix is unset, to aid filtering\n-# hooks.showrev\n-#   The shell command used to format each revision in the email, with\n-#   \"%s\" replaced with the commit id.  Defaults to \"git rev-list -1\n-#   --pretty %s\", displaying the commit id, author, date and log\n-#   message.  To list full patches separated by a blank line, you\n-#   could set this to \"git show -C %s; echo\".\n-#   To list a gitweb/cgit URL *and* a full patch for each change set, use this:\n-#     \"t=%s; printf 'http://.../?id=%%s' \\$t; echo;echo; git show -C \\$t; echo\"\n-#   Be careful if \"...\" contains things that will be expanded by shell \"eval\"\n-#   or printf.\n-# hooks.emailmaxlines\n-#   The maximum number of lines that should be included in the generated\n-#   email body. If not specified, there is no limit.\n-#   Lines beyond the limit are suppressed and counted, and a final\n-#   line is added indicating the number of suppressed lines.\n-# hooks.diffopts\n-#   Alternate options for the git diff-tree invocation that shows changes.\n-#   Default is \"--stat --summary --find-copies-harder\". Add -p to those\n-#   options to include a unified diff of changes in addition to the usual\n-#   summary output.\n-#\n-# Notes\n-# -----\n-# All emails include the headers \"X-Git-Refname\", \"X-Git-Oldrev\",\n-# \"X-Git-Newrev\", and \"X-Git-Reftype\" to enable fine tuned filtering and\n-# give information for debugging.\n-#\n-\n-# ---------------------------- Functions\n-\n-#\n-# Function to prepare for email generation. This decides what type\n-# of update this is and whether an email should even be generated.\n-#\n-prep_for_email()\n-{\n-\t# --- Arguments\n-\toldrev=$(git rev-parse $1)\n-\tnewrev=$(git rev-parse $2)\n-\trefname=\"$3\"\n-\n-\t# --- Interpret\n-\t# 0000->1234 (create)\n-\t# 1234->2345 (update)\n-\t# 2345->0000 (delete)\n-\tif expr \"$oldrev\" : '0*$' >/dev/null\n-\tthen\n-\t\tchange_type=\"create\"\n-\telse\n-\t\tif expr \"$newrev\" : '0*$' >/dev/null\n-\t\tthen\n-\t\t\tchange_type=\"delete\"\n-\t\telse\n-\t\t\tchange_type=\"update\"\n-\t\tfi\n-\tfi\n-\n-\t# --- Get the revision types\n-\tnewrev_type=$(git cat-file -t $newrev 2> /dev/null)\n-\toldrev_type=$(git cat-file -t \"$oldrev\" 2> /dev/null)\n-\tcase \"$change_type\" in\n-\tcreate|update)\n-\t\trev=\"$newrev\"\n-\t\trev_type=\"$newrev_type\"\n-\t\t;;\n-\tdelete)\n-\t\trev=\"$oldrev\"\n-\t\trev_type=\"$oldrev_type\"\n-\t\t;;\n-\tesac\n-\n-\t# The revision type tells us what type the commit is, combined with\n-\t# the location of the ref we can decide between\n-\t#  - working branch\n-\t#  - tracking branch\n-\t#  - unannoted tag\n-\t#  - annotated tag\n-\tcase \"$refname\",\"$rev_type\" in\n-\t\trefs/tags/*,commit)\n-\t\t\t# un-annotated tag\n-\t\t\trefname_type=\"tag\"\n-\t\t\tshort_refname=${refname##refs/tags/}\n-\t\t\t;;\n-\t\trefs/tags/*,tag)\n-\t\t\t# annotated tag\n-\t\t\trefname_type=\"annotated tag\"\n-\t\t\tshort_refname=${refname##refs/tags/}\n-\t\t\t# change recipients\n-\t\t\tif [ -n \"$announcerecipients\" ]; then\n-\t\t\t\trecipients=\"$announcerecipients\"\n-\t\t\tfi\n-\t\t\t;;\n-\t\trefs/heads/*,commit)\n-\t\t\t# branch\n-\t\t\trefname_type=\"branch\"\n-\t\t\tshort_refname=${refname##refs/heads/}\n-\t\t\t;;\n-\t\trefs/remotes/*,commit)\n-\t\t\t# tracking branch\n-\t\t\trefname_type=\"tracking branch\"\n-\t\t\tshort_refname=${refname##refs/remotes/}\n-\t\t\techo >&2 \"*** Push-update of tracking branch, $refname\"\n-\t\t\techo >&2 \"***  - no email generated.\"\n-\t\t\treturn 1\n-\t\t\t;;\n-\t\t*)\n-\t\t\t# Anything else (is there anything else?)\n-\t\t\techo >&2 \"*** Unknown type of update to $refname ($rev_type)\"\n-\t\t\techo >&2 \"***  - no email generated\"\n-\t\t\treturn 1\n-\t\t\t;;\n-\tesac\n-\n-\t# Check if we've got anyone to send to\n-\tif [ -z \"$recipients\" ]; then\n-\t\tcase \"$refname_type\" in\n-\t\t\t\"annotated tag\")\n-\t\t\t\tconfig_name=\"hooks.announcelist\"\n-\t\t\t\t;;\n-\t\t\t*)\n-\t\t\t\tconfig_name=\"hooks.mailinglist\"\n-\t\t\t\t;;\n-\t\tesac\n-\t\techo >&2 \"*** $config_name is not set so no email will be sent\"\n-\t\techo >&2 \"*** for $refname update $oldrev->$newrev\"\n-\t\treturn 1\n-\tfi\n-\n-\treturn 0\n-}\n-\n-#\n-# Top level email generation function.  This calls the appropriate\n-# body-generation routine after outputting the common header.\n-#\n-# Note this function doesn't actually generate any email output, that is\n-# taken care of by the functions it calls:\n-#  - generate_email_header\n-#  - generate_create_XXXX_email\n-#  - generate_update_XXXX_email\n-#  - generate_delete_XXXX_email\n-#  - generate_email_footer\n-#\n-# Note also that this function cannot 'exit' from the script; when this\n-# function is running (in hook script mode), the send_mail() function\n-# is already executing in another process, connected via a pipe, and\n-# if this function exits without, whatever has been generated to that\n-# point will be sent as an email... even if nothing has been generated.\n-#\n-generate_email()\n-{\n-\t# Email parameters\n-\t# The email subject will contain the best description of the ref\n-\t# that we can build from the parameters\n-\tdescribe=$(git describe $rev 2>/dev/null)\n-\tif [ -z \"$describe\" ]; then\n-\t\tdescribe=$rev\n-\tfi\n-\n-\tgenerate_email_header\n-\n-\t# Call the correct body generation function\n-\tfn_name=general\n-\tcase \"$refname_type\" in\n-\t\"tracking branch\"|branch)\n-\t\tfn_name=branch\n-\t\t;;\n-\t\"annotated tag\")\n-\t\tfn_name=atag\n-\t\t;;\n-\tesac\n-\n-\tif [ -z \"$maxlines\" ]; then\n-\t\tgenerate_${change_type}_${fn_name}_email\n-\telse\n-\t\tgenerate_${change_type}_${fn_name}_email | limit_lines $maxlines\n-\tfi\n-\n-\tgenerate_email_footer\n-}\n-\n-generate_email_header()\n-{\n-\t# --- Email (all stdout will be the email)\n-\t# Generate header\n-\tcat <<-EOF\n-\tTo: $recipients\n-\tSubject: ${emailprefix}$projectdesc $refname_type $short_refname ${change_type}d. $describe\n-\tMIME-Version: 1.0\n-\tContent-Type: text/plain; charset=utf-8\n-\tContent-Transfer-Encoding: 8bit\n-\tX-Git-Refname: $refname\n-\tX-Git-Reftype: $refname_type\n-\tX-Git-Oldrev: $oldrev\n-\tX-Git-Newrev: $newrev\n-\tAuto-Submitted: auto-generated\n-\n-\tThis is an automated email from the git hooks/post-receive script. It was\n-\tgenerated because a ref change was pushed to the repository containing\n-\tthe project \"$projectdesc\".\n-\n-\tThe $refname_type, $short_refname has been ${change_type}d\n-\tEOF\n-}\n-\n-generate_email_footer()\n-{\n-\tSPACE=\" \"\n-\tcat <<-EOF\n-\n-\n-\thooks/post-receive\n-\t--${SPACE}\n-\t$projectdesc\n-\tEOF\n-}\n-\n-# --------------- Branches\n-\n-#\n-# Called for the creation of a branch\n-#\n-generate_create_branch_email()\n-{\n-\t# This is a new branch and so oldrev is not valid\n-\techo \"        at  $newrev ($newrev_type)\"\n-\techo \"\"\n-\n-\techo $LOGBEGIN\n-\tshow_new_revisions\n-\techo $LOGEND\n-}\n-\n-#\n-# Called for the change of a pre-existing branch\n-#\n-generate_update_branch_email()\n-{\n-\t# Consider this:\n-\t#   1 --- 2 --- O --- X --- 3 --- 4 --- N\n-\t#\n-\t# O is $oldrev for $refname\n-\t# N is $newrev for $refname\n-\t# X is a revision pointed to by some other ref, for which we may\n-\t#   assume that an email has already been generated.\n-\t# In this case we want to issue an email containing only revisions\n-\t# 3, 4, and N.  Given (almost) by\n-\t#\n-\t#  git rev-list N ^O --not --all\n-\t#\n-\t# The reason for the \"almost\", is that the \"--not --all\" will take\n-\t# precedence over the \"N\", and effectively will translate to\n-\t#\n-\t#  git rev-list N ^O ^X ^N\n-\t#\n-\t# So, we need to build up the list more carefully.  git rev-parse\n-\t# will generate a list of revs that may be fed into git rev-list.\n-\t# We can get it to make the \"--not --all\" part and then filter out\n-\t# the \"^N\" with:\n-\t#\n-\t#  git rev-parse --not --all | grep -v N\n-\t#\n-\t# Then, using the --stdin switch to git rev-list we have effectively\n-\t# manufactured\n-\t#\n-\t#  git rev-list N ^O ^X\n-\t#\n-\t# This leaves a problem when someone else updates the repository\n-\t# while this script is running.  Their new value of the ref we're\n-\t# working on would be included in the \"--not --all\" output; and as\n-\t# our $newrev would be an ancestor of that commit, it would exclude\n-\t# all of our commits.  What we really want is to exclude the current\n-\t# value of $refname from the --not list, rather than N itself.  So:\n-\t#\n-\t#  git rev-parse --not --all | grep -v $(git rev-parse $refname)\n-\t#\n-\t# Gets us to something pretty safe (apart from the small time\n-\t# between refname being read, and git rev-parse running - for that,\n-\t# I give up)\n-\t#\n-\t#\n-\t# Next problem, consider this:\n-\t#   * --- B --- * --- O ($oldrev)\n-\t#          \\\n-\t#           * --- X --- * --- N ($newrev)\n-\t#\n-\t# That is to say, there is no guarantee that oldrev is a strict\n-\t# subset of newrev (it would have required a --force, but that's\n-\t# allowed).  So, we can't simply say rev-list $oldrev..$newrev.\n-\t# Instead we find the common base of the two revs and list from\n-\t# there.\n-\t#\n-\t# As above, we need to take into account the presence of X; if\n-\t# another branch is already in the repository and points at some of\n-\t# the revisions that we are about to output - we don't want them.\n-\t# The solution is as before: git rev-parse output filtered.\n-\t#\n-\t# Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N\n-\t#\n-\t# Tags pushed into the repository generate nice shortlog emails that\n-\t# summarise the commits between them and the previous tag.  However,\n-\t# those emails don't include the full commit messages that we output\n-\t# for a branch update.  Therefore we still want to output revisions\n-\t# that have been output on a tag email.\n-\t#\n-\t# Luckily, git rev-parse includes just the tool.  Instead of using\n-\t# \"--all\" we use \"--branches\"; this has the added benefit that\n-\t# \"remotes/\" will be ignored as well.\n-\n-\t# List all of the revisions that were removed by this update, in a\n-\t# fast-forward update, this list will be empty, because rev-list O\n-\t# ^N is empty.  For a non-fast-forward, O ^N is the list of removed\n-\t# revisions\n-\tfast_forward=\"\"\n-\trev=\"\"\n-\tfor rev in $(git rev-list $newrev..$oldrev)\n-\tdo\n-\t\trevtype=$(git cat-file -t \"$rev\")\n-\t\techo \"  discards  $rev ($revtype)\"\n-\tdone\n-\tif [ -z \"$rev\" ]; then\n-\t\tfast_forward=1\n-\tfi\n-\n-\t# List all the revisions from baserev to newrev in a kind of\n-\t# \"table-of-contents\"; note this list can include revisions that\n-\t# have already had notification emails and is present to show the\n-\t# full detail of the change from rolling back the old revision to\n-\t# the base revision and then forward to the new revision\n-\tfor rev in $(git rev-list $oldrev..$newrev)\n-\tdo\n-\t\trevtype=$(git cat-file -t \"$rev\")\n-\t\techo \"       via  $rev ($revtype)\"\n-\tdone\n-\n-\tif [ \"$fast_forward\" ]; then\n-\t\techo \"      from  $oldrev ($oldrev_type)\"\n-\telse\n-\t\t#  1. Existing revisions were removed.  In this case newrev\n-\t\t#     is a subset of oldrev - this is the reverse of a\n-\t\t#     fast-forward, a rewind\n-\t\t#  2. New revisions were added on top of an old revision,\n-\t\t#     this is a rewind and addition.\n-\n-\t\t# (1) certainly happened, (2) possibly.  When (2) hasn't\n-\t\t# happened, we set a flag to indicate that no log printout\n-\t\t# is required.\n-\n-\t\techo \"\"\n-\n-\t\t# Find the common ancestor of the old and new revisions and\n-\t\t# compare it with newrev\n-\t\tbaserev=$(git merge-base $oldrev $newrev)\n-\t\trewind_only=\"\"\n-\t\tif [ \"$baserev\" = \"$newrev\" ]; then\n-\t\t\techo \"This update discarded existing revisions and left the branch pointing at\"\n-\t\t\techo \"a previous point in the repository history.\"\n-\t\t\techo \"\"\n-\t\t\techo \" * -- * -- N ($newrev)\"\n-\t\t\techo \"            \\\\\"\n-\t\t\techo \"             O -- O -- O ($oldrev)\"\n-\t\t\techo \"\"\n-\t\t\techo \"The removed revisions are not necessarily gone - if another reference\"\n-\t\t\techo \"still refers to them they will stay in the repository.\"\n-\t\t\trewind_only=1\n-\t\telse\n-\t\t\techo \"This update added new revisions after undoing existing revisions.  That is\"\n-\t\t\techo \"to say, the old revision is not a strict subset of the new revision.  This\"\n-\t\t\techo \"situation occurs when you --force push a change and generate a repository\"\n-\t\t\techo \"containing something like this:\"\n-\t\t\techo \"\"\n-\t\t\techo \" * -- * -- B -- O -- O -- O ($oldrev)\"\n-\t\t\techo \"            \\\\\"\n-\t\t\techo \"             N -- N -- N ($newrev)\"\n-\t\t\techo \"\"\n-\t\t\techo \"When this happens we assume that you've already had alert emails for all\"\n-\t\t\techo \"of the O revisions, and so we here report only the revisions in the N\"\n-\t\t\techo \"branch from the common base, B.\"\n-\t\tfi\n-\tfi\n-\n-\techo \"\"\n-\tif [ -z \"$rewind_only\" ]; then\n-\t\techo \"Those revisions listed above that are new to this repository have\"\n-\t\techo \"not appeared on any other notification email; so we list those\"\n-\t\techo \"revisions in full, below.\"\n-\n-\t\techo \"\"\n-\t\techo $LOGBEGIN\n-\t\tshow_new_revisions\n-\n-\t\t# XXX: Need a way of detecting whether git rev-list actually\n-\t\t# outputted anything, so that we can issue a \"no new\n-\t\t# revisions added by this update\" message\n-\n-\t\techo $LOGEND\n-\telse\n-\t\techo \"No new revisions were added by this update.\"\n-\tfi\n-\n-\t# The diffstat is shown from the old revision to the new revision.\n-\t# This is to show the truth of what happened in this change.\n-\t# There's no point showing the stat from the base to the new\n-\t# revision because the base is effectively a random revision at this\n-\t# point - the user will be interested in what this revision changed\n-\t# - including the undoing of previous revisions in the case of\n-\t# non-fast-forward updates.\n-\techo \"\"\n-\techo \"Summary of changes:\"\n-\tgit diff-tree $diffopts $oldrev..$newrev\n-}\n-\n-#\n-# Called for the deletion of a branch\n-#\n-generate_delete_branch_email()\n-{\n-\techo \"       was  $oldrev\"\n-\techo \"\"\n-\techo $LOGBEGIN\n-\tgit diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev\n-\techo $LOGEND\n-}\n-\n-# --------------- Annotated tags\n-\n-#\n-# Called for the creation of an annotated tag\n-#\n-generate_create_atag_email()\n-{\n-\techo \"        at  $newrev ($newrev_type)\"\n-\n-\tgenerate_atag_email\n-}\n-\n-#\n-# Called for the update of an annotated tag (this is probably a rare event\n-# and may not even be allowed)\n-#\n-generate_update_atag_email()\n-{\n-\techo \"        to  $newrev ($newrev_type)\"\n-\techo \"      from  $oldrev (which is now obsolete)\"\n-\n-\tgenerate_atag_email\n-}\n-\n-#\n-# Called when an annotated tag is created or changed\n-#\n-generate_atag_email()\n-{\n-\t# Use git for-each-ref to pull out the individual fields from the\n-\t# tag\n-\teval $(git for-each-ref --shell --format='\n-\ttagobject=%(*objectname)\n-\ttagtype=%(*objecttype)\n-\ttagger=%(taggername)\n-\ttagged=%(taggerdate)' $refname\n-\t)\n-\n-\techo \"   tagging  $tagobject ($tagtype)\"\n-\tcase \"$tagtype\" in\n-\tcommit)\n-\n-\t\t# If the tagged object is a commit, then we assume this is a\n-\t\t# release, and so we calculate which tag this tag is\n-\t\t# replacing\n-\t\tprevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)\n-\n-\t\tif [ -n \"$prevtag\" ]; then\n-\t\t\techo \"  replaces  $prevtag\"\n-\t\tfi\n-\t\t;;\n-\t*)\n-\t\techo \"    length  $(git cat-file -s $tagobject) bytes\"\n-\t\t;;\n-\tesac\n-\techo \" tagged by  $tagger\"\n-\techo \"        on  $tagged\"\n-\n-\techo \"\"\n-\techo $LOGBEGIN\n-\n-\t# Show the content of the tag message; this might contain a change\n-\t# log or release notes so is worth displaying.\n-\tgit cat-file tag $newrev | sed -e '1,/^$/d'\n-\n-\techo \"\"\n-\tcase \"$tagtype\" in\n-\tcommit)\n-\t\t# Only commit tags make sense to have rev-list operations\n-\t\t# performed on them\n-\t\tif [ -n \"$prevtag\" ]; then\n-\t\t\t# Show changes since the previous release\n-\t\t\tgit shortlog \"$prevtag..$newrev\"\n-\t\telse\n-\t\t\t# No previous tag, show all the changes since time\n-\t\t\t# began\n-\t\t\tgit shortlog $newrev\n-\t\tfi\n-\t\t;;\n-\t*)\n-\t\t# XXX: Is there anything useful we can do for non-commit\n-\t\t# objects?\n-\t\t;;\n-\tesac\n-\n-\techo $LOGEND\n-}\n-\n-#\n-# Called for the deletion of an annotated tag\n-#\n-generate_delete_atag_email()\n-{\n-\techo \"       was  $oldrev\"\n-\techo \"\"\n-\techo $LOGBEGIN\n-\tgit diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev\n-\techo $LOGEND\n-}\n-\n-# --------------- General references\n-\n-#\n-# Called when any other type of reference is created (most likely a\n-# non-annotated tag)\n-#\n-generate_create_general_email()\n-{\n-\techo \"        at  $newrev ($newrev_type)\"\n-\n-\tgenerate_general_email\n-}\n-\n-#\n-# Called when any other type of reference is updated (most likely a\n-# non-annotated tag)\n-#\n-generate_update_general_email()\n-{\n-\techo \"        to  $newrev ($newrev_type)\"\n-\techo \"      from  $oldrev\"\n-\n-\tgenerate_general_email\n-}\n-\n-#\n-# Called for creation or update of any other type of reference\n-#\n-generate_general_email()\n-{\n-\t# Unannotated tags are more about marking a point than releasing a\n-\t# version; therefore we don't do the shortlog summary that we do for\n-\t# annotated tags above - we simply show that the point has been\n-\t# marked, and print the log message for the marked point for\n-\t# reference purposes\n-\t#\n-\t# Note this section also catches any other reference type (although\n-\t# there aren't any) and deals with them in the same way.\n-\n-\techo \"\"\n-\tif [ \"$newrev_type\" = \"commit\" ]; then\n-\t\techo $LOGBEGIN\n-\t\tgit diff-tree -s --always --encoding=UTF-8 --pretty=medium $newrev\n-\t\techo $LOGEND\n-\telse\n-\t\t# What can we do here?  The tag marks an object that is not\n-\t\t# a commit, so there is no log for us to display.  It's\n-\t\t# probably not wise to output git cat-file as it could be a\n-\t\t# binary blob.  We'll just say how big it is\n-\t\techo \"$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long.\"\n-\tfi\n-}\n-\n-#\n-# Called for the deletion of any other type of reference\n-#\n-generate_delete_general_email()\n-{\n-\techo \"       was  $oldrev\"\n-\techo \"\"\n-\techo $LOGBEGIN\n-\tgit diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev\n-\techo $LOGEND\n-}\n-\n-\n-# --------------- Miscellaneous utilities\n-\n-#\n-# Show new revisions as the user would like to see them in the email.\n-#\n-show_new_revisions()\n-{\n-\t# This shows all log entries that are not already covered by\n-\t# another ref - i.e. commits that are now accessible from this\n-\t# ref that were previously not accessible\n-\t# (see generate_update_branch_email for the explanation of this\n-\t# command)\n-\n-\t# Revision range passed to rev-list differs for new vs. updated\n-\t# branches.\n-\tif [ \"$change_type\" = create ]\n-\tthen\n-\t\t# Show all revisions exclusive to this (new) branch.\n-\t\trevspec=$newrev\n-\telse\n-\t\t# Branch update; show revisions not part of $oldrev.\n-\t\trevspec=$oldrev..$newrev\n-\tfi\n-\n-\tother_branches=$(git for-each-ref --format='%(refname)' refs/heads/ |\n-\t    grep -F -v $refname)\n-\tgit rev-parse --not $other_branches |\n-\tif [ -z \"$custom_showrev\" ]\n-\tthen\n-\t\tgit rev-list --pretty --stdin $revspec\n-\telse\n-\t\tgit rev-list --stdin $revspec |\n-\t\twhile read onerev\n-\t\tdo\n-\t\t\teval $(printf \"$custom_showrev\" $onerev)\n-\t\tdone\n-\tfi\n-}\n-\n-\n-limit_lines()\n-{\n-\tlines=0\n-\tskipped=0\n-\twhile IFS=\"\" read -r line; do\n-\t\tlines=$((lines + 1))\n-\t\tif [ $lines -gt $1 ]; then\n-\t\t\tskipped=$((skipped + 1))\n-\t\telse\n-\t\t\tprintf \"%s\\n\" \"$line\"\n-\t\tfi\n-\tdone\n-\tif [ $skipped -ne 0 ]; then\n-\t\techo \"... $skipped lines suppressed ...\"\n-\tfi\n-}\n-\n-\n-send_mail()\n-{\n-\tif [ -n \"$envelopesender\" ]; then\n-\t\t/usr/sbin/sendmail -t -f \"$envelopesender\"\n-\telse\n-\t\t/usr/sbin/sendmail -t\n-\tfi\n-}\n-\n-# ---------------------------- main()\n-\n-# --- Constants\n-LOGBEGIN=\"- Log -----------------------------------------------------------------\"\n-LOGEND=\"-----------------------------------------------------------------------\"\n-\n-# --- Config\n-# Set GIT_DIR either from the working directory, or from the environment\n-# variable.\n-GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)\n-if [ -z \"$GIT_DIR\" ]; then\n-\techo >&2 \"fatal: post-receive: GIT_DIR not set\"\n-\texit 1\n-fi\n-\n-projectdesc=$(sed -ne '1p' \"$GIT_DIR/description\" 2>/dev/null)\n-# Check if the description is unchanged from it's default, and shorten it to\n-# a more manageable length if it is\n-if expr \"$projectdesc\" : \"Unnamed repository.*$\" >/dev/null\n-then\n-\tprojectdesc=\"UNNAMED PROJECT\"\n-fi\n-\n-recipients=$(git config hooks.mailinglist)\n-announcerecipients=$(git config hooks.announcelist)\n-envelopesender=$(git config hooks.envelopesender)\n-emailprefix=$(git config hooks.emailprefix || echo '[SCM] ')\n-custom_showrev=$(git config hooks.showrev)\n-maxlines=$(git config hooks.emailmaxlines)\n-diffopts=$(git config hooks.diffopts)\n-: ${diffopts:=\"--stat --summary --find-copies-harder\"}\n-\n-# --- Main loop\n-# Allow dual mode: run from the command line just like the update hook, or\n-# if no arguments are given then run as a hook script\n-if [ -n \"$1\" -a -n \"$2\" -a -n \"$3\" ]; then\n-\t# Output to the terminal in command line mode - if someone wanted to\n-\t# resend an email; they could redirect the output to sendmail\n-\t# themselves\n-\tprep_for_email $2 $3 $1 && PAGER= generate_email\n-else\n-\twhile read oldrev newrev refname\n-\tdo\n-\t\tprep_for_email $oldrev $newrev $refname || continue\n-\t\tgenerate_email $maxlines | send_mail\n-\tdone\n-fi\ndiff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery\ndeleted file mode 100755\nindex 7ba78c4dff6..00000000000\n--- a/contrib/hooks/pre-auto-gc-battery\n+++ /dev/null\n@@ -1,42 +0,0 @@\n-#!/bin/sh\n-#\n-# An example hook script to verify if you are on battery, in case you\n-# are running Linux or OS X. Called by git-gc --auto with no arguments.\n-# The hook should exit with non-zero status after issuing an appropriate\n-# message if it wants to stop the auto repacking.\n-#\n-# This hook is stored in the contrib/hooks directory. Your distribution\n-# may have put this somewhere else. If you want to use this hook, you\n-# should make this script executable then link to it in the repository\n-# you would like to use it in.\n-#\n-# For example, if the hook is stored in\n-# /usr/share/git-core/contrib/hooks/pre-auto-gc-battery:\n-#\n-# cd /path/to/your/repository.git\n-# ln -sf /usr/share/git-core/contrib/hooks/pre-auto-gc-battery \\\n-#\thooks/pre-auto-gc\n-\n-if test -x /sbin/on_ac_power && (/sbin/on_ac_power;test $? -ne 1)\n-then\n-\texit 0\n-elif test \"$(cat /sys/class/power_supply/AC/online 2>/dev/null)\" = 1\n-then\n-\texit 0\n-elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null\n-then\n-\texit 0\n-elif grep -q '0x01$' /proc/apm 2>/dev/null\n-then\n-\texit 0\n-elif grep -q \"AC Power \\+: 1\" /proc/pmu/info 2>/dev/null\n-then\n-\texit 0\n-elif test -x /usr/bin/pmset && /usr/bin/pmset -g batt |\n-\tgrep -q \"drawing from 'AC Power'\"\n-then\n-\texit 0\n-fi\n-\n-echo \"Auto packing deferred; not on AC\"\n-exit 1\ndiff --git a/contrib/hooks/setgitperms.perl b/contrib/hooks/setgitperms.perl\ndeleted file mode 100755\nindex 2770a1b1d20..00000000000\n--- a/contrib/hooks/setgitperms.perl\n+++ /dev/null\n@@ -1,214 +0,0 @@\n-#!/usr/bin/perl\n-#\n-# Copyright (c) 2006 Josh England\n-#\n-# This script can be used to save/restore full permissions and ownership data\n-# within a git working tree.\n-#\n-# To save permissions/ownership data, place this script in your .git/hooks\n-# directory and enable a `pre-commit` hook with the following lines:\n-#      #!/bin/sh\n-#     SUBDIRECTORY_OK=1 . git-sh-setup\n-#     $GIT_DIR/hooks/setgitperms.perl -r\n-#\n-# To restore permissions/ownership data, place this script in your .git/hooks\n-# directory and enable a `post-merge` and `post-checkout` hook with the\n-# following lines:\n-#      #!/bin/sh\n-#     SUBDIRECTORY_OK=1 . git-sh-setup\n-#     $GIT_DIR/hooks/setgitperms.perl -w\n-#\n-use strict;\n-use Getopt::Long;\n-use File::Find;\n-use File::Basename;\n-\n-my $usage =\n-\"usage: setgitperms.perl [OPTION]... <--read|--write>\n-This program uses a file `.gitmeta` to store/restore permissions and uid/gid\n-info for all files/dirs tracked by git in the repository.\n-\n----------------------------------Read Mode-------------------------------------\n--r,  --read         Reads perms/etc from working dir into a .gitmeta file\n--s,  --stdout       Output to stdout instead of .gitmeta\n--d,  --diff         Show unified diff of perms file (XOR with --stdout)\n-\n----------------------------------Write Mode------------------------------------\n--w,  --write        Modify perms/etc in working dir to match the .gitmeta file\n--v,  --verbose      Be verbose\n-\n-\\n\";\n-\n-my ($stdout, $showdiff, $verbose, $read_mode, $write_mode);\n-\n-if ((@ARGV < 0) || !GetOptions(\n-\t\t\t       \"stdout\",         \\$stdout,\n-\t\t\t       \"diff\",           \\$showdiff,\n-\t\t\t       \"read\",           \\$read_mode,\n-\t\t\t       \"write\",          \\$write_mode,\n-\t\t\t       \"verbose\",        \\$verbose,\n-\t\t\t      )) { die $usage; }\n-die $usage unless ($read_mode xor $write_mode);\n-\n-my $topdir = `git rev-parse --show-cdup` or die \"\\n\"; chomp $topdir;\n-my $gitdir = $topdir . '.git';\n-my $gitmeta = $topdir . '.gitmeta';\n-\n-if ($write_mode) {\n-    # Update the working dir permissions/ownership based on data from .gitmeta\n-    open (IN, \"<$gitmeta\") or die \"Could not open $gitmeta for reading: $!\\n\";\n-    while (defined ($_ = <IN>)) {\n-\tchomp;\n-\tif (/^(.*)  mode=(\\S+)\\s+uid=(\\d+)\\s+gid=(\\d+)/) {\n-\t    # Compare recorded perms to actual perms in the working dir\n-\t    my ($path, $mode, $uid, $gid) = ($1, $2, $3, $4);\n-\t    my $fullpath = $topdir . $path;\n-\t    my (undef,undef,$wmode,undef,$wuid,$wgid) = lstat($fullpath);\n-\t    $wmode = sprintf \"%04o\", $wmode & 07777;\n-\t    if ($mode ne $wmode) {\n-\t\t$verbose && print \"Updating permissions on $path: old=$wmode, new=$mode\\n\";\n-\t\tchmod oct($mode), $fullpath;\n-\t    }\n-\t    if ($uid != $wuid || $gid != $wgid) {\n-\t\tif ($verbose) {\n-\t\t    # Print out user/group names instead of uid/gid\n-\t\t    my $pwname  = getpwuid($uid);\n-\t\t    my $grpname  = getgrgid($gid);\n-\t\t    my $wpwname  = getpwuid($wuid);\n-\t\t    my $wgrpname  = getgrgid($wgid);\n-\t\t    $pwname = $uid if !defined $pwname;\n-\t\t    $grpname = $gid if !defined $grpname;\n-\t\t    $wpwname = $wuid if !defined $wpwname;\n-\t\t    $wgrpname = $wgid if !defined $wgrpname;\n-\n-\t\t    print \"Updating uid/gid on $path: old=$wpwname/$wgrpname, new=$pwname/$grpname\\n\";\n-\t\t}\n-\t\tchown $uid, $gid, $fullpath;\n-\t    }\n-\t}\n-\telse {\n-\t    warn \"Invalid input format in $gitmeta:\\n\\t$_\\n\";\n-\t}\n-    }\n-    close IN;\n-}\n-elsif ($read_mode) {\n-    # Handle merge conflicts in the .gitperms file\n-    if (-e \"$gitdir/MERGE_MSG\") {\n-\tif (`grep ====== $gitmeta`) {\n-\t    # Conflict not resolved -- abort the commit\n-\t    print \"PERMISSIONS/OWNERSHIP CONFLICT\\n\";\n-\t    print \"    Resolve the conflict in the $gitmeta file and then run\\n\";\n-\t    print \"    `.git/hooks/setgitperms.perl --write` to reconcile.\\n\";\n-\t    exit 1;\n-\t}\n-\telsif (`grep $gitmeta $gitdir/MERGE_MSG`) {\n-\t    # A conflict in .gitmeta has been manually resolved. Verify that\n-\t    # the working dir perms matches the current .gitmeta perms for\n-\t    # each file/dir that conflicted.\n-\t    # This is here because a `setgitperms.perl --write` was not\n-\t    # performed due to a merge conflict, so permissions/ownership\n-\t    # may not be consistent with the manually merged .gitmeta file.\n-\t    my @conflict_diff = `git show \\$(cat $gitdir/MERGE_HEAD)`;\n-\t    my @conflict_files;\n-\t    my $metadiff = 0;\n-\n-\t    # Build a list of files that conflicted from the .gitmeta diff\n-\t    foreach my $line (@conflict_diff) {\n-\t\tif ($line =~ m|^diff --git a/$gitmeta b/$gitmeta|) {\n-\t\t    $metadiff = 1;\n-\t\t}\n-\t\telsif ($line =~ /^diff --git/) {\n-\t\t    $metadiff = 0;\n-\t\t}\n-\t\telsif ($metadiff && $line =~ /^\\+(.*)  mode=/) {\n-\t\t    push @conflict_files, $1;\n-\t\t}\n-\t    }\n-\n-\t    # Verify that each conflict file now has permissions consistent\n-\t    # with the .gitmeta file\n-\t    foreach my $file (@conflict_files) {\n-\t\tmy $absfile = $topdir . $file;\n-\t\tmy $gm_entry = `grep \"^$file  mode=\" $gitmeta`;\n-\t\tif ($gm_entry =~ /mode=(\\d+)  uid=(\\d+)  gid=(\\d+)/) {\n-\t\t    my ($gm_mode, $gm_uid, $gm_gid) = ($1, $2, $3);\n-\t\t    my (undef,undef,$mode,undef,$uid,$gid) = lstat(\"$absfile\");\n-\t\t    $mode = sprintf(\"%04o\", $mode & 07777);\n-\t\t    if (($gm_mode ne $mode) || ($gm_uid != $uid)\n-\t\t\t|| ($gm_gid != $gid)) {\n-\t\t\tprint \"PERMISSIONS/OWNERSHIP CONFLICT\\n\";\n-\t\t\tprint \"    Mismatch found for file: $file\\n\";\n-\t\t\tprint \"    Run `.git/hooks/setgitperms.perl --write` to reconcile.\\n\";\n-\t\t\texit 1;\n-\t\t    }\n-\t\t}\n-\t\telse {\n-\t\t    print \"Warning! Permissions/ownership no longer being tracked for file: $file\\n\";\n-\t\t}\n-\t    }\n-\t}\n-    }\n-\n-    # No merge conflicts -- write out perms/ownership data to .gitmeta file\n-    unless ($stdout) {\n-\topen (OUT, \">$gitmeta.tmp\") or die \"Could not open $gitmeta.tmp for writing: $!\\n\";\n-    }\n-\n-    my @files = `git ls-files`;\n-    my %dirs;\n-\n-    foreach my $path (@files) {\n-\tchomp $path;\n-\t# We have to manually add stats for parent directories\n-\tmy $parent = dirname($path);\n-\twhile (!exists $dirs{$parent}) {\n-\t    $dirs{$parent} = 1;\n-\t    next if $parent eq '.';\n-\t    printstats($parent);\n-\t    $parent = dirname($parent);\n-\t}\n-\t# Now the git-tracked file\n-\tprintstats($path);\n-    }\n-\n-    # diff the temporary metadata file to see if anything has changed\n-    # If no metadata has changed, don't overwrite the real file\n-    # This is just so `git commit -a` doesn't try to commit a bogus update\n-    unless ($stdout) {\n-\tif (! -e $gitmeta) {\n-\t    rename \"$gitmeta.tmp\", $gitmeta;\n-\t}\n-\telse {\n-\t    my $diff = `diff -U 0 $gitmeta $gitmeta.tmp`;\n-\t    if ($diff ne '') {\n-\t\trename \"$gitmeta.tmp\", $gitmeta;\n-\t    }\n-\t    else {\n-\t\tunlink \"$gitmeta.tmp\";\n-\t    }\n-\t    if ($showdiff) {\n-\t\tprint $diff;\n-\t    }\n-\t}\n-\tclose OUT;\n-    }\n-    # Make sure the .gitmeta file is tracked\n-    system(\"git add $gitmeta\");\n-}\n-\n-\n-sub printstats {\n-    my $path = $_[0];\n-    $path =~ s/@/\\@/g;\n-    my (undef,undef,$mode,undef,$uid,$gid) = lstat($path);\n-    $path =~ s/%/\\%/g;\n-    if ($stdout) {\n-\tprint $path;\n-\tprintf \"  mode=%04o  uid=$uid  gid=$gid\\n\", $mode & 07777;\n-    }\n-    else {\n-\tprint OUT $path;\n-\tprintf OUT \"  mode=%04o  uid=$uid  gid=$gid\\n\", $mode & 07777;\n-    }\n-}\ndiff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid\ndeleted file mode 100755\nindex 0092d67b8a4..00000000000\n--- a/contrib/hooks/update-paranoid\n+++ /dev/null\n@@ -1,421 +0,0 @@\n-#!/usr/bin/perl\n-\n-use strict;\n-use File::Spec;\n-\n-$ENV{PATH}     = '/opt/git/bin';\n-my $acl_git    = '/vcs/acls.git';\n-my $acl_branch = 'refs/heads/master';\n-my $debug      = 0;\n-\n-=doc\n-Invoked as: update refname old-sha1 new-sha1\n-\n-This script is run by git-receive-pack once for each ref that the\n-client is trying to modify.  If we exit with a non-zero exit value\n-then the update for that particular ref is denied, but updates for\n-other refs in the same run of receive-pack may still be allowed.\n-\n-We are run after the objects have been uploaded, but before the\n-ref is actually modified.  We take advantage of that fact when we\n-look for \"new\" commits and tags (the new objects won't show up in\n-`rev-list --all`).\n-\n-This script loads and parses the content of the config file\n-\"users/$this_user.acl\" from the $acl_branch commit of $acl_git ODB.\n-The acl file is a git-config style file, but uses a slightly more\n-restricted syntax as the Perl parser contained within this script\n-is not nearly as permissive as git-config.\n-\n-Example:\n-\n-  [user]\n-    committer = John Doe <john.doe@example.com>\n-    committer = John R. Doe <john.doe@example.com>\n-\n-  [repository \"acls\"]\n-    allow = heads/master\n-    allow = CDUR for heads/jd/\n-    allow = C    for ^tags/v\\\\d+$\n-\n-For all new commit or tag objects the committer (or tagger) line\n-within the object must exactly match one of the user.committer\n-values listed in the acl file (\"HEAD:users/$this_user.acl\").\n-\n-For a branch to be modified an allow line within the matching\n-repository section must be matched for both the refname and the\n-opcode.\n-\n-Repository sections are matched on the basename of the repository\n-(after removing the .git suffix).\n-\n-The opcode abbreviations are:\n-\n-  C: create new ref\n-  D: delete existing ref\n-  U: fast-forward existing ref (no commit loss)\n-  R: rewind/rebase existing ref (commit loss)\n-\n-if no opcodes are listed before the \"for\" keyword then \"U\" (for\n-fast-forward update only) is assumed as this is the most common\n-usage.\n-\n-Refnames are matched by always assuming a prefix of \"refs/\".\n-This hook forbids pushing or deleting anything not under \"refs/\".\n-\n-Refnames that start with ^ are Perl regular expressions, and the ^\n-is kept as part of the regexp.  \\\\ is needed to get just one \\, so\n-\\\\d expands to \\d in Perl.  The 3rd allow line above is an example.\n-\n-Refnames that don't start with ^ but that end with / are prefix\n-matches (2nd allow line above); all other refnames are strict\n-equality matches (1st allow line).\n-\n-Anything pushed to \"heads/\" (ok, really \"refs/heads/\") must be\n-a commit.  Tags are not permitted here.\n-\n-Anything pushed to \"tags/\" (err, really \"refs/tags/\") must be an\n-annotated tag.  Commits, blobs, trees, etc. are not permitted here.\n-Annotated tag signatures aren't checked, nor are they required.\n-\n-The special subrepository of 'info/new-commit-check' can\n-be created and used to allow users to push new commits and\n-tags from another local repository to this one, even if they\n-aren't the committer/tagger of those objects.  In a nut shell\n-the info/new-commit-check directory is a Git repository whose\n-objects/info/alternates file lists this repository and all other\n-possible sources, and whose refs subdirectory contains symlinks\n-to this repository's refs subdirectory, and to all other possible\n-sources refs subdirectories.  Yes, this means that you cannot\n-use packed-refs in those repositories as they won't be resolved\n-correctly.\n-\n-=cut\n-\n-my $git_dir = $ENV{GIT_DIR};\n-my $new_commit_check = \"$git_dir/info/new-commit-check\";\n-my $ref = $ARGV[0];\n-my $old = $ARGV[1];\n-my $new = $ARGV[2];\n-my $new_type;\n-my ($this_user) = getpwuid $<; # REAL_USER_ID\n-my $repository_name;\n-my %user_committer;\n-my @allow_rules;\n-my @path_rules;\n-my %diff_cache;\n-\n-sub deny ($) {\n-\tprint STDERR \"-Deny-    $_[0]\\n\" if $debug;\n-\tprint STDERR \"\\ndenied: $_[0]\\n\\n\";\n-\texit 1;\n-}\n-\n-sub grant ($) {\n-\tprint STDERR \"-Grant-   $_[0]\\n\" if $debug;\n-\texit 0;\n-}\n-\n-sub info ($) {\n-\tprint STDERR \"-Info-    $_[0]\\n\" if $debug;\n-}\n-\n-sub git_value (@) {\n-\topen(T,'-|','git',@_); local $_ = <T>; chop; close T; $_;\n-}\n-\n-sub match_string ($$) {\n-\tmy ($acl_n, $ref) = @_;\n-\t   ($acl_n eq $ref)\n-\t|| ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n)\n-\t|| ($acl_n =~ m,^\\^, && $ref =~ m:$acl_n:);\n-}\n-\n-sub parse_config ($$$$) {\n-\tmy $data = shift;\n-\tlocal $ENV{GIT_DIR} = shift;\n-\tmy $br = shift;\n-\tmy $fn = shift;\n-\treturn unless git_value('rev-list','--max-count=1',$br,'--',$fn);\n-\tinfo \"Loading $br:$fn\";\n-\topen(I,'-|','git','cat-file','blob',\"$br:$fn\");\n-\tmy $section = '';\n-\twhile (<I>) {\n-\t\tchomp;\n-\t\tif (/^\\s*$/ || /^\\s*#/) {\n-\t\t} elsif (/^\\[([a-z]+)\\]$/i) {\n-\t\t\t$section = lc $1;\n-\t\t} elsif (/^\\[([a-z]+)\\s+\"(.*)\"\\]$/i) {\n-\t\t\t$section = join('.',lc $1,$2);\n-\t\t} elsif (/^\\s*([a-z][a-z0-9]+)\\s*=\\s*(.*?)\\s*$/i) {\n-\t\t\tpush @{$data->{join('.',$section,lc $1)}}, $2;\n-\t\t} else {\n-\t\t\tdeny \"bad config file line $. in $br:$fn\";\n-\t\t}\n-\t}\n-\tclose I;\n-}\n-\n-sub all_new_committers () {\n-\tlocal $ENV{GIT_DIR} = $git_dir;\n-\t$ENV{GIT_DIR} = $new_commit_check if -d $new_commit_check;\n-\n-\tinfo \"Getting committers of new commits.\";\n-\tmy %used;\n-\topen(T,'-|','git','rev-list','--pretty=raw',$new,'--not','--all');\n-\twhile (<T>) {\n-\t\tnext unless s/^committer //;\n-\t\tchop;\n-\t\ts/>.*$/>/;\n-\t\tinfo \"Found $_.\" unless $used{$_}++;\n-\t}\n-\tclose T;\n-\tinfo \"No new commits.\" unless %used;\n-\tkeys %used;\n-}\n-\n-sub all_new_taggers () {\n-\tmy %exists;\n-\topen(T,'-|','git','for-each-ref','--format=%(objectname)','refs/tags');\n-\twhile (<T>) {\n-\t\tchop;\n-\t\t$exists{$_} = 1;\n-\t}\n-\tclose T;\n-\n-\tinfo \"Getting taggers of new tags.\";\n-\tmy %used;\n-\tmy $obj = $new;\n-\tmy $obj_type = $new_type;\n-\twhile ($obj_type eq 'tag') {\n-\t\tlast if $exists{$obj};\n-\t\t$obj_type = '';\n-\t\topen(T,'-|','git','cat-file','tag',$obj);\n-\t\twhile (<T>) {\n-\t\t\tchop;\n-\t\t\tif (/^object ([a-z0-9]{40})$/) {\n-\t\t\t\t$obj = $1;\n-\t\t\t} elsif (/^type (.+)$/) {\n-\t\t\t\t$obj_type = $1;\n-\t\t\t} elsif (s/^tagger //) {\n-\t\t\t\ts/>.*$/>/;\n-\t\t\t\tinfo \"Found $_.\" unless $used{$_}++;\n-\t\t\t\tlast;\n-\t\t\t}\n-\t\t}\n-\t\tclose T;\n-\t}\n-\tinfo \"No new tags.\" unless %used;\n-\tkeys %used;\n-}\n-\n-sub check_committers (@) {\n-\tmy @bad;\n-\tforeach (@_) { push @bad, $_ unless $user_committer{$_}; }\n-\tif (@bad) {\n-\t\tprint STDERR \"\\n\";\n-\t\tprint STDERR \"You are not $_.\\n\" foreach (sort @bad);\n-\t\tdeny \"You cannot push changes not committed by you.\";\n-\t}\n-}\n-\n-sub load_diff ($) {\n-\tmy $base = shift;\n-\tmy $d = $diff_cache{$base};\n-\tunless ($d) {\n-\t\tlocal $/ = \"\\0\";\n-\t\tmy %this_diff;\n-\t\tif ($base =~ /^0{40}$/) {\n-\t\t\t# Don't load the diff at all; we are making the\n-\t\t\t# branch and have no base to compare to in this\n-\t\t\t# case.  A file level ACL makes no sense in this\n-\t\t\t# context.  Having an empty diff will allow the\n-\t\t\t# branch creation.\n-\t\t\t#\n-\t\t} else {\n-\t\t\topen(T,'-|','git','diff-tree',\n-\t\t\t\t'-r','--name-status','-z',\n-\t\t\t\t$base,$new) or return undef;\n-\t\t\twhile (<T>) {\n-\t\t\t\tmy $op = $_;\n-\t\t\t\tchop $op;\n-\n-\t\t\t\tmy $path = <T>;\n-\t\t\t\tchop $path;\n-\n-\t\t\t\t$this_diff{$path} = $op;\n-\t\t\t}\n-\t\t\tclose T or return undef;\n-\t\t}\n-\t\t$d = \\%this_diff;\n-\t\t$diff_cache{$base} = $d;\n-\t}\n-\treturn $d;\n-}\n-\n-deny \"No GIT_DIR inherited from caller\" unless $git_dir;\n-deny \"Need a ref name\" unless $ref;\n-deny \"Refusing funny ref $ref\" unless $ref =~ s,^refs/,,;\n-deny \"Bad old value $old\" unless $old =~ /^[a-z0-9]{40}$/;\n-deny \"Bad new value $new\" unless $new =~ /^[a-z0-9]{40}$/;\n-deny \"Cannot determine who you are.\" unless $this_user;\n-grant \"No change requested.\" if $old eq $new;\n-\n-$repository_name = File::Spec->rel2abs($git_dir);\n-$repository_name =~ m,/([^/]+)(?:\\.git|/\\.git)$,;\n-$repository_name = $1;\n-info \"Updating in '$repository_name'.\";\n-\n-my $op;\n-if    ($old =~ /^0{40}$/) { $op = 'C'; }\n-elsif ($new =~ /^0{40}$/) { $op = 'D'; }\n-else                      { $op = 'R'; }\n-\n-# This is really an update (fast-forward) if the\n-# merge base of $old and $new is $old.\n-#\n-$op = 'U' if ($op eq 'R'\n-\t&& $ref =~ m,^heads/,\n-\t&& $old eq git_value('merge-base',$old,$new));\n-\n-# Load the user's ACL file. Expand groups (user.memberof) one level.\n-{\n-\tmy %data = ('user.committer' => []);\n-\tparse_config(\\%data,$acl_git,$acl_branch,\"external/$repository_name.acl\");\n-\n-\t%data = (\n-\t\t'user.committer' => $data{'user.committer'},\n-\t\t'user.memberof' => [],\n-\t);\n-\tparse_config(\\%data,$acl_git,$acl_branch,\"users/$this_user.acl\");\n-\n-\t%user_committer = map {$_ => $_} @{$data{'user.committer'}};\n-\tmy $rule_key = \"repository.$repository_name.allow\";\n-\tmy $rules = $data{$rule_key} || [];\n-\n-\tforeach my $group (@{$data{'user.memberof'}}) {\n-\t\tmy %g;\n-\t\tparse_config(\\%g,$acl_git,$acl_branch,\"groups/$group.acl\");\n-\t\tmy $group_rules = $g{$rule_key};\n-\t\tpush @$rules, @$group_rules if $group_rules;\n-\t}\n-\n-RULE:\n-\tforeach (@$rules) {\n-\t\twhile (/\\${user\\.([a-z][a-zA-Z0-9]+)}/) {\n-\t\t\tmy $k = lc $1;\n-\t\t\tmy $v = $data{\"user.$k\"};\n-\t\t\tnext RULE unless defined $v;\n-\t\t\tnext RULE if @$v != 1;\n-\t\t\tnext RULE unless defined $v->[0];\n-\t\t\ts/\\${user\\.$k}/$v->[0]/g;\n-\t\t}\n-\n-\t\tif (/^([AMD ]+)\\s+of\\s+([^\\s]+)\\s+for\\s+([^\\s]+)\\s+diff\\s+([^\\s]+)$/) {\n-\t\t\tmy ($ops, $pth, $ref, $bst) = ($1, $2, $3, $4);\n-\t\t\t$ops =~ s/ //g;\n-\t\t\t$pth =~ s/\\\\\\\\/\\\\/g;\n-\t\t\t$ref =~ s/\\\\\\\\/\\\\/g;\n-\t\t\tpush @path_rules, [$ops, $pth, $ref, $bst];\n-\t\t} elsif (/^([AMD ]+)\\s+of\\s+([^\\s]+)\\s+for\\s+([^\\s]+)$/) {\n-\t\t\tmy ($ops, $pth, $ref) = ($1, $2, $3);\n-\t\t\t$ops =~ s/ //g;\n-\t\t\t$pth =~ s/\\\\\\\\/\\\\/g;\n-\t\t\t$ref =~ s/\\\\\\\\/\\\\/g;\n-\t\t\tpush @path_rules, [$ops, $pth, $ref, $old];\n-\t\t} elsif (/^([CDRU ]+)\\s+for\\s+([^\\s]+)$/) {\n-\t\t\tmy $ops = $1;\n-\t\t\tmy $ref = $2;\n-\t\t\t$ops =~ s/ //g;\n-\t\t\t$ref =~ s/\\\\\\\\/\\\\/g;\n-\t\t\tpush @allow_rules, [$ops, $ref];\n-\t\t} elsif (/^for\\s+([^\\s]+)$/) {\n-\t\t\t# Mentioned, but nothing granted?\n-\t\t} elsif (/^[^\\s]+$/) {\n-\t\t\ts/\\\\\\\\/\\\\/g;\n-\t\t\tpush @allow_rules, ['U', $_];\n-\t\t}\n-\t}\n-}\n-\n-if ($op ne 'D') {\n-\t$new_type = git_value('cat-file','-t',$new);\n-\n-\tif ($ref =~ m,^heads/,) {\n-\t\tdeny \"$ref must be a commit.\" unless $new_type eq 'commit';\n-\t} elsif ($ref =~ m,^tags/,) {\n-\t\tdeny \"$ref must be an annotated tag.\" unless $new_type eq 'tag';\n-\t}\n-\n-\tcheck_committers (all_new_committers);\n-\tcheck_committers (all_new_taggers) if $new_type eq 'tag';\n-}\n-\n-info \"$this_user wants $op for $ref\";\n-foreach my $acl_entry (@allow_rules) {\n-\tmy ($acl_ops, $acl_n) = @$acl_entry;\n-\tnext unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen.\n-\tnext unless $acl_n;\n-\tnext unless $op =~ /^[$acl_ops]$/;\n-\tnext unless match_string $acl_n, $ref;\n-\n-\t# Don't test path rules on branch deletes.\n-\t#\n-\tgrant \"Allowed by: $acl_ops for $acl_n\" if $op eq 'D';\n-\n-\t# Aggregate matching path rules; allow if there aren't\n-\t# any matching this ref.\n-\t#\n-\tmy %pr;\n-\tforeach my $p_entry (@path_rules) {\n-\t\tmy ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry;\n-\t\tnext unless $p_ref;\n-\t\tpush @{$pr{$p_bst}}, $p_entry if match_string $p_ref, $ref;\n-\t}\n-\tgrant \"Allowed by: $acl_ops for $acl_n\" unless %pr;\n-\n-\t# Allow only if all changes against a single base are\n-\t# allowed by file path rules.\n-\t#\n-\tmy @bad;\n-\tforeach my $p_bst (keys %pr) {\n-\t\tmy $diff_ref = load_diff $p_bst;\n-\t\tdeny \"Cannot difference trees.\" unless ref $diff_ref;\n-\n-\t\tmy %fd = %$diff_ref;\n-\t\tforeach my $p_entry (@{$pr{$p_bst}}) {\n-\t\t\tmy ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry;\n-\t\t\tnext unless $p_ops =~ /^[AMD]+$/;\n-\t\t\tnext unless $p_n;\n-\n-\t\t\tforeach my $f_n (keys %fd) {\n-\t\t\t\tmy $f_op = $fd{$f_n};\n-\t\t\t\tnext unless $f_op;\n-\t\t\t\tnext unless $f_op =~ /^[$p_ops]$/;\n-\t\t\t\tdelete $fd{$f_n} if match_string $p_n, $f_n;\n-\t\t\t}\n-\t\t\tlast unless %fd;\n-\t\t}\n-\n-\t\tif (%fd) {\n-\t\t\tpush @bad, [$p_bst, \\%fd];\n-\t\t} else {\n-\t\t\t# All changes relative to $p_bst were allowed.\n-\t\t\t#\n-\t\t\tgrant \"Allowed by: $acl_ops for $acl_n diff $p_bst\";\n-\t\t}\n-\t}\n-\n-\tforeach my $bad_ref (@bad) {\n-\t\tmy ($p_bst, $fd) = @$bad_ref;\n-\t\tprint STDERR \"\\n\";\n-\t\tprint STDERR \"Not allowed to make the following changes:\\n\";\n-\t\tprint STDERR \"(base: $p_bst)\\n\";\n-\t\tforeach my $f_n (sort keys %$fd) {\n-\t\t\tprint STDERR \"  $fd->{$f_n} $f_n\\n\";\n-\t\t}\n-\t}\n-\tdeny \"You are not permitted to $op $ref\";\n-}\n-close A;\n-deny \"You are not permitted to $op $ref\";\n\n-- \n2.49.0.1077.gc0e912fd4c.dirty\n\n"},{"id":"517649","messageId":"20250509-pks-contrib-spring-cleanup-v2-6-14e20d95ec68@pks.im","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","subject":"[PATCH v2 06/11] contrib: remove \"mw-to-git\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-09T09:17:06Z","receivedAt":"2025-05-09T09:17:17Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"mw-to-git\" directory contains tools for accessing MediaWiki via\nGit. The scripts are essentially unmaintained in Git: despite a couple\nof global cleanups, the last changes were a couple of security-related\nissues part of 9a8606465e8 (remote-mediawiki: use \"sh\" to eliminate\nunquoted commands, 2020-09-21) and its parents. We don't ever run any of\nthe tests so it is more likely than not that many of the tests have been\nbitrotting, like e.g. documented in f8ab018dafc (remote-mediawiki tests:\nannotate failing tests, 2020-09-21).\n\nAccording to Matthieu Moy [1], one of the original developers of this\ntool, it didn't receive any attention recently and there is no\nmotivation to keep maintaining it anymore in the community. The project\nhas been spun out of Git [2] and thus has a new official home, but did\nnot receive much attention over there, either.\n\nAs such, it seems like the MediaWiki transport helper is slowly fading\naway. But given that there is a new home, it doesn't make sense to have\nit as part of Git anymore only to let it rot. Remove the directory.\n\n[1]: <108f297a-b415-4742-80e4-51ea02af18e9@matthieu-moy.fr>\n[2]: https://github.com/Git-Mediawiki/Git-Mediawiki\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/mw-to-git/.gitignore                       |    2 -\n contrib/mw-to-git/.perlcriticrc                    |   28 -\n contrib/mw-to-git/Git/Mediawiki.pm                 |  101 --\n contrib/mw-to-git/Makefile                         |   61 -\n contrib/mw-to-git/bin-wrapper/git                  |   14 -\n contrib/mw-to-git/git-mw.perl                      |  368 ------\n contrib/mw-to-git/git-remote-mediawiki.perl        | 1390 --------------------\n contrib/mw-to-git/git-remote-mediawiki.txt         |    7 -\n contrib/mw-to-git/t/.gitignore                     |    4 -\n contrib/mw-to-git/t/Makefile                       |   32 -\n contrib/mw-to-git/t/README                         |  124 --\n contrib/mw-to-git/t/install-wiki.sh                |   55 -\n contrib/mw-to-git/t/push-pull-tests.sh             |  144 --\n contrib/mw-to-git/t/t9360-mw-to-git-clone.sh       |  257 ----\n contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh   |   24 -\n contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh        |  347 -----\n .../mw-to-git/t/t9363-mw-to-git-export-import.sh   |  218 ---\n contrib/mw-to-git/t/t9364-pull-by-rev.sh           |   17 -\n contrib/mw-to-git/t/t9365-continuing-queries.sh    |   23 -\n contrib/mw-to-git/t/test-gitmw-lib.sh              |  432 ------\n contrib/mw-to-git/t/test-gitmw.pl                  |  223 ----\n contrib/mw-to-git/t/test.config                    |   40 -\n 22 files changed, 3911 deletions(-)\n\ndiff --git a/contrib/mw-to-git/.gitignore b/contrib/mw-to-git/.gitignore\ndeleted file mode 100644\nindex ae545b013dc..00000000000\n--- a/contrib/mw-to-git/.gitignore\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-git-remote-mediawiki\n-git-mw\ndiff --git a/contrib/mw-to-git/.perlcriticrc b/contrib/mw-to-git/.perlcriticrc\ndeleted file mode 100644\nindex b7333267ada..00000000000\n--- a/contrib/mw-to-git/.perlcriticrc\n+++ /dev/null\n@@ -1,28 +0,0 @@\n-# These 3 rules demand to add the s, m and x flag to *every* regexp. This is\n-# overkill and would be harmful for readability.\n-[-RegularExpressions::RequireExtendedFormatting]\n-[-RegularExpressions::RequireDotMatchAnything]\n-[-RegularExpressions::RequireLineBoundaryMatching]\n-\n-# This rule says that builtin functions should not be called with parentheses\n-# e.g.: (taken from CPAN's documentation)\n-# open($handle, '>', $filename); #not ok\n-# open $handle, '>', $filename;  #ok\n-# Applying such a rule would mean modifying a huge number of lines for a\n-# question of style.\n-[-CodeLayout::ProhibitParensWithBuiltins]\n-\n-# This rule states that each system call should have its return value checked\n-# The problem is that it includes the print call. Checking every print call's\n-# return value would be harmful to the code readability.\n-# This configuration keeps all default function but print.\n-[InputOutput::RequireCheckedSyscalls]\n-functions = open say close\n-\n-# This rule demands to add a dependency for the Readonly module. This is not\n-# wished.\n-[-ValuesAndExpressions::ProhibitConstantPragma]\n-\n-# This rule is not really useful (rather a question of style) and produces many\n-# warnings among the code.\n-[-ValuesAndExpressions::ProhibitNoisyQuotes]\ndiff --git a/contrib/mw-to-git/Git/Mediawiki.pm b/contrib/mw-to-git/Git/Mediawiki.pm\ndeleted file mode 100644\nindex 629c0cea442..00000000000\n--- a/contrib/mw-to-git/Git/Mediawiki.pm\n+++ /dev/null\n@@ -1,101 +0,0 @@\n-package Git::Mediawiki;\n-\n-require v5.26;\n-use strict;\n-use POSIX;\n-use Git;\n-\n-BEGIN {\n-\n-our ($VERSION, @ISA, @EXPORT, @EXPORT_OK);\n-\n-# Totally unstable API.\n-$VERSION = '0.01';\n-\n-require Exporter;\n-\n-@ISA = qw(Exporter);\n-\n-@EXPORT = ();\n-\n-# Methods which can be called as standalone functions as well:\n-@EXPORT_OK = qw(clean_filename smudge_filename connect_maybe\n-\t\t\t\tEMPTY HTTP_CODE_OK HTTP_CODE_PAGE_NOT_FOUND);\n-}\n-\n-# Mediawiki filenames can contain forward slashes. This variable decides by which pattern they should be replaced\n-use constant SLASH_REPLACEMENT => '%2F';\n-\n-# Used to test for empty strings\n-use constant EMPTY => q{};\n-\n-# HTTP codes\n-use constant HTTP_CODE_OK => 200;\n-use constant HTTP_CODE_PAGE_NOT_FOUND => 404;\n-\n-sub clean_filename {\n-\tmy $filename = shift;\n-\t$filename =~ s{@{[SLASH_REPLACEMENT]}}{/}g;\n-\t# [, ], |, {, and } are forbidden by MediaWiki, even URL-encoded.\n-\t# Do a variant of URL-encoding, i.e. looks like URL-encoding,\n-\t# but with _ added to prevent MediaWiki from thinking this is\n-\t# an actual special character.\n-\t$filename =~ s/[\\[\\]\\{\\}\\|]/sprintf(\"_%%_%x\", ord($&))/ge;\n-\t# If we use the uri escape before\n-\t# we should unescape here, before anything\n-\n-\treturn $filename;\n-}\n-\n-sub smudge_filename {\n-\tmy $filename = shift;\n-\t$filename =~ s{/}{@{[SLASH_REPLACEMENT]}}g;\n-\t$filename =~ s/ /_/g;\n-\t# Decode forbidden characters encoded in clean_filename\n-\t$filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf('%c', hex($1))/ge;\n-\treturn substr($filename, 0, NAME_MAX-length('.mw'));\n-}\n-\n-sub connect_maybe {\n-\tmy $wiki = shift;\n-\tif ($wiki) {\n-\t\treturn $wiki;\n-\t}\n-\n-\tmy $remote_name = shift;\n-\tmy $remote_url = shift;\n-\tmy ($wiki_login, $wiki_password, $wiki_domain);\n-\n-\t$wiki_login = Git::config(\"remote.${remote_name}.mwLogin\");\n-\t$wiki_password = Git::config(\"remote.${remote_name}.mwPassword\");\n-\t$wiki_domain = Git::config(\"remote.${remote_name}.mwDomain\");\n-\n-\t$wiki = MediaWiki::API->new;\n-\t$wiki->{config}->{api_url} = \"${remote_url}/api.php\";\n-\tif ($wiki_login) {\n-\t\tmy %credential = (\n-\t\t\t'url' => $remote_url,\n-\t\t\t'username' => $wiki_login,\n-\t\t\t'password' => $wiki_password\n-\t\t);\n-\t\tGit::credential(\\%credential);\n-\t\tmy $request = {lgname => $credential{username},\n-\t\t\t       lgpassword => $credential{password},\n-\t\t\t       lgdomain => $wiki_domain};\n-\t\tif ($wiki->login($request)) {\n-\t\t\tGit::credential(\\%credential, 'approve');\n-\t\t\tprint {*STDERR} qq(Logged in mediawiki user \"$credential{username}\".\\n);\n-\t\t} else {\n-\t\t\tprint {*STDERR} qq(Failed to log in mediawiki user \"$credential{username}\" on ${remote_url}\\n);\n-\t\t\tprint {*STDERR} '  (error ' .\n-\t\t\t\t$wiki->{error}->{code} . ': ' .\n-\t\t\t\t$wiki->{error}->{details} . \")\\n\";\n-\t\t\tGit::credential(\\%credential, 'reject');\n-\t\t\texit 1;\n-\t\t}\n-\t}\n-\n-\treturn $wiki;\n-}\n-\n-1; # Famous last words\ndiff --git a/contrib/mw-to-git/Makefile b/contrib/mw-to-git/Makefile\ndeleted file mode 100644\nindex 497ac434d60..00000000000\n--- a/contrib/mw-to-git/Makefile\n+++ /dev/null\n@@ -1,61 +0,0 @@\n-#\n-# Copyright (C) 2013\n-#     Matthieu Moy <Matthieu.Moy@imag.fr>\n-#\n-# To build and test:\n-#\n-#   make\n-#   bin-wrapper/git mw preview Some_page.mw\n-#   bin-wrapper/git clone mediawiki::http://example.com/wiki/\n-#\n-# To install, run Git's toplevel 'make install' then run:\n-#\n-#   make install\n-\n-# The default target of this Makefile is...\n-all::\n-\n-GIT_MEDIAWIKI_PM=Git/Mediawiki.pm\n-SCRIPT_PERL=git-remote-mediawiki.perl\n-SCRIPT_PERL+=git-mw.perl\n-GIT_ROOT_DIR=../..\n-HERE=contrib/mw-to-git/\n-\n-INSTALL = install\n-\n-SCRIPT_PERL_FULL=$(patsubst %,$(HERE)/%,$(SCRIPT_PERL))\n-INSTLIBDIR=$(shell $(MAKE) -C $(GIT_ROOT_DIR)/ \\\n-                -s --no-print-directory prefix=$(prefix) \\\n-                perllibdir=$(perllibdir) perllibdir)\n-DESTDIR_SQ = $(subst ','\\'',$(DESTDIR))\n-INSTLIBDIR_SQ = $(subst ','\\'',$(INSTLIBDIR))\n-\n-all:: build\n-\n-test: all\n-\t$(MAKE) -C t\n-\n-check: perlcritic test\n-\n-install_pm:\n-\t$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(INSTLIBDIR_SQ)/Git'\n-\t$(INSTALL) -m 644 $(GIT_MEDIAWIKI_PM) \\\n-\t\t'$(DESTDIR_SQ)$(INSTLIBDIR_SQ)/$(GIT_MEDIAWIKI_PM)'\n-\n-build:\n-\t$(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL=\"$(SCRIPT_PERL_FULL)\" \\\n-                build-perl-script\n-\n-install: install_pm\n-\t$(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL=\"$(SCRIPT_PERL_FULL)\" \\\n-                install-perl-script\n-\n-clean:\n-\t$(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL=\"$(SCRIPT_PERL_FULL)\" \\\n-                clean-perl-script\n-\n-perlcritic:\n-\tperlcritic -5 $(SCRIPT_PERL)\n-\t-perlcritic -2 $(SCRIPT_PERL)\n-\n-.PHONY: all test check install_pm install clean perlcritic\ndiff --git a/contrib/mw-to-git/bin-wrapper/git b/contrib/mw-to-git/bin-wrapper/git\ndeleted file mode 100755\nindex 6663ae57e86..00000000000\n--- a/contrib/mw-to-git/bin-wrapper/git\n+++ /dev/null\n@@ -1,14 +0,0 @@\n-#!/bin/sh\n-\n-# git executable wrapper script for Git-Mediawiki to run tests without\n-# installing all the scripts and perl packages.\n-\n-GIT_ROOT_DIR=../../..\n-GIT_EXEC_PATH=$(cd \"$(dirname \"$0\")\" && cd ${GIT_ROOT_DIR} && pwd)\n-\n-GITPERLLIB=\"$GIT_EXEC_PATH\"'/contrib/mw-to-git'\"${GITPERLLIB:+:$GITPERLLIB}\"\n-PATH=\"$GIT_EXEC_PATH\"'/contrib/mw-to-git:'\"$PATH\"\n-\n-export GITPERLLIB PATH\n-\n-exec \"${GIT_EXEC_PATH}/bin-wrappers/git\" \"$@\"\ndiff --git a/contrib/mw-to-git/git-mw.perl b/contrib/mw-to-git/git-mw.perl\ndeleted file mode 100755\nindex eb52a53d320..00000000000\n--- a/contrib/mw-to-git/git-mw.perl\n+++ /dev/null\n@@ -1,368 +0,0 @@\n-#!/usr/bin/perl\n-\n-# Copyright (C) 2013\n-#     Benoit Person <benoit.person@ensimag.imag.fr>\n-#     Celestin Matte <celestin.matte@ensimag.imag.fr>\n-# License: GPL v2 or later\n-\n-# Set of tools for git repo with a mediawiki remote.\n-# Documentation & bugtracker: https://github.com/Git-Mediawiki/Git-Mediawiki\n-\n-use strict;\n-use warnings;\n-\n-use Getopt::Long;\n-use URI::URL qw(url);\n-use LWP::UserAgent;\n-use HTML::TreeBuilder;\n-\n-use Git;\n-use MediaWiki::API;\n-use Git::Mediawiki qw(clean_filename connect_maybe\n-\t\t\t\t\tEMPTY HTTP_CODE_PAGE_NOT_FOUND);\n-\n-# By default, use UTF-8 to communicate with Git and the user\n-binmode STDERR, ':encoding(UTF-8)';\n-binmode STDOUT, ':encoding(UTF-8)';\n-\n-# Global parameters\n-my $verbose = 0;\n-sub v_print {\n-\tif ($verbose) {\n-\t\treturn print {*STDERR} @_;\n-\t}\n-\treturn;\n-}\n-\n-# Preview parameters\n-my $file_name = EMPTY;\n-my $remote_name = EMPTY;\n-my $preview_file_name = EMPTY;\n-my $autoload = 0;\n-sub file {\n-\t$file_name = shift;\n-\treturn $file_name;\n-}\n-\n-my %commands = (\n-\t'help' =>\n-\t\t[\\&help, {}, \\&help],\n-\t'preview' =>\n-\t\t[\\&preview, {\n-\t\t\t'<>' => \\&file,\n-\t\t\t'output|o=s' => \\$preview_file_name,\n-\t\t\t'remote|r=s' => \\$remote_name,\n-\t\t\t'autoload|a' => \\$autoload\n-\t\t}, \\&preview_help]\n-);\n-\n-# Search for sub-command\n-my $cmd = $commands{'help'};\n-for (0..@ARGV-1) {\n-\tif (defined $commands{$ARGV[$_]}) {\n-\t\t$cmd = $commands{$ARGV[$_]};\n-\t\tsplice @ARGV, $_, 1;\n-\t\tlast;\n-\t}\n-};\n-GetOptions( %{$cmd->[1]},\n-\t'help|h' => \\&{$cmd->[2]},\n-\t'verbose|v'  => \\$verbose);\n-\n-# Launch command\n-&{$cmd->[0]};\n-\n-############################# Preview Functions ################################\n-\n-sub preview_help {\n-\tprint {*STDOUT} <<'END';\n-USAGE: git mw preview [--remote|-r <remote name>] [--autoload|-a]\n-                      [--output|-o <output filename>] [--verbose|-v]\n-                      <blob> | <filename>\n-\n-DESCRIPTION:\n-Preview is an utiliy to preview local content of a mediawiki repo as if it was\n-pushed on the remote.\n-\n-For that, preview searches for the remote name of the current branch's\n-upstream if --remote is not set. If that remote is not found or if it\n-is not a mediawiki, it lists all mediawiki remotes configured and asks\n-you to replay your command with the --remote option set properly.\n-\n-Then, it searches for a file named 'filename'. If it's not found in\n-the current dir, it will assume it's a blob.\n-\n-The content retrieved in the file (or in the blob) will then be parsed\n-by the remote mediawiki and combined with a template retrieved from\n-the mediawiki.\n-\n-Finally, preview will save the HTML result in a file. and autoload it\n-in your default web browser if the option --autoload is present.\n-\n-OPTIONS:\n-    -r <remote name>, --remote <remote name>\n-        If the remote is a mediawiki, the template and the parse engine\n-        used for the preview will be those of that remote.\n-        If not, a list of valid remotes will be shown.\n-\n-    -a, --autoload\n-        Try to load the HTML output in a new tab (or new window) of your\n-        default web browser.\n-\n-    -o <output filename>, --output <output filename>\n-        Change the HTML output filename. Default filename is based on the\n-        input filename with its extension replaced by '.html'.\n-\n-    -v, --verbose\n-        Show more information on what's going on under the hood.\n-END\n-\texit;\n-}\n-\n-sub preview {\n-\tmy $wiki;\n-\tmy ($remote_url, $wiki_page_name);\n-\tmy ($new_content, $template);\n-\tmy $file_content;\n-\n-\tif ($file_name eq EMPTY) {\n-\t\tdie \"Missing file argument, see `git mw help`\\n\";\n-\t}\n-\n-\tv_print(\"### Selecting remote\\n\");\n-\tif ($remote_name eq EMPTY) {\n-\t\t$remote_name = find_upstream_remote_name();\n-\t\tif ($remote_name) {\n-\t\t\t$remote_url = mediawiki_remote_url_maybe($remote_name);\n-\t\t}\n-\n-\t\tif (! $remote_url) {\n-\t\t\tmy @valid_remotes = find_mediawiki_remotes();\n-\n-\t\t\tif ($#valid_remotes == 0) {\n-\t\t\t\tprint {*STDERR} \"No mediawiki remote in this repo. \\n\";\n-\t\t\t\texit 1;\n-\t\t\t} else {\n-\t\t\t\tmy $remotes_list = join(\"\\n\\t\", @valid_remotes);\n-\t\t\t\tprint {*STDERR} <<\"MESSAGE\";\n-There are multiple mediawiki remotes, which of:\n-\t${remotes_list}\n-do you want ? Use the -r option to specify the remote.\n-MESSAGE\n-\t\t\t}\n-\n-\t\t\texit 1;\n-\t\t}\n-\t} else {\n-\t\tif (!is_valid_remote($remote_name)) {\n-\t\t\tdie \"${remote_name} is not a remote\\n\";\n-\t\t}\n-\n-\t\t$remote_url = mediawiki_remote_url_maybe($remote_name);\n-\t\tif (! $remote_url) {\n-\t\t\tdie \"${remote_name} is not a mediawiki remote\\n\";\n-\t\t}\n-\t}\n-\tv_print(\"selected remote:\\n\\tname: ${remote_name}\\n\\turl: ${remote_url}\\n\");\n-\n-\t$wiki = connect_maybe($wiki, $remote_name, $remote_url);\n-\n-\t# Read file content\n-\tif (! -e $file_name) {\n-\t\t$file_content = git_cmd_try {\n-\t\t\tGit::command('cat-file', 'blob', $file_name); }\n-\t\t\t\"%s failed w/ code %d\";\n-\n-\t\tif ($file_name =~ /(.+):(.+)/) {\n-\t\t\t$file_name = $2;\n-\t\t}\n-\t} else {\n-\t\topen my $read_fh, \"<\", $file_name\n-\t\t\tor die \"could not open ${file_name}: $!\\n\";\n-\t\t$file_content = do { local $/ = undef; <$read_fh> };\n-\t\tclose $read_fh\n-\t\t\tor die \"unable to close: $!\\n\";\n-\t}\n-\n-\tv_print(\"### Retrieving template\\n\");\n-\t($wiki_page_name = clean_filename($file_name)) =~ s/\\.[^.]+$//;\n-\t$template = get_template($remote_url, $wiki_page_name);\n-\n-\tv_print(\"### Parsing local content\\n\");\n-\t$new_content = $wiki->api({\n-\t\taction => 'parse',\n-\t\ttext => $file_content,\n-\t\ttitle => $wiki_page_name\n-\t}, {\n-\t\tskip_encoding => 1\n-\t}) or die \"No response from remote mediawiki\\n\";\n-\t$new_content = $new_content->{'parse'}->{'text'}->{'*'};\n-\n-\tv_print(\"### Merging contents\\n\");\n-\tif ($preview_file_name eq EMPTY) {\n-\t\t($preview_file_name = $file_name) =~ s/\\.[^.]+$/.html/;\n-\t}\n-\topen(my $save_fh, '>:encoding(UTF-8)', $preview_file_name)\n-\t\tor die \"Could not open: $!\\n\";\n-\tprint {$save_fh} merge_contents($template, $new_content, $remote_url);\n-\tclose($save_fh)\n-\t\tor die \"Could not close: $!\\n\";\n-\n-\tv_print(\"### Results\\n\");\n-\tif ($autoload) {\n-\t\tv_print(\"Launching browser w/ file: ${preview_file_name}\");\n-\t\tsystem('git', 'web--browse', $preview_file_name);\n-\t} else {\n-\t\tprint {*STDERR} \"Preview file saved as: ${preview_file_name}\\n\";\n-\t}\n-\n-\texit;\n-}\n-\n-# uses global scope variable: $remote_name\n-sub merge_contents {\n-\tmy $template = shift;\n-\tmy $content = shift;\n-\tmy $remote_url = shift;\n-\tmy ($content_tree, $html_tree, $mw_content_text);\n-\tmy $template_content_id = 'bodyContent';\n-\n-\t$html_tree = HTML::TreeBuilder->new;\n-\t$html_tree->parse($template);\n-\n-\t$content_tree = HTML::TreeBuilder->new;\n-\t$content_tree->parse($content);\n-\n-\t$template_content_id = Git::config(\"remote.${remote_name}.mwIDcontent\")\n-\t\t|| $template_content_id;\n-\tv_print(\"Using '${template_content_id}' as the content ID\\n\");\n-\n-\t$mw_content_text = $html_tree->look_down('id', $template_content_id);\n-\tif (!defined $mw_content_text) {\n-\t\tprint {*STDERR} <<\"CONFIG\";\n-Could not combine the new content with the template. You might want to\n-configure `mediawiki.IDContent` in your config:\n-\tgit config --add remote.${remote_name}.mwIDcontent <id>\n-and re-run the command afterward.\n-CONFIG\n-\t\texit 1;\n-\t}\n-\t$mw_content_text->delete_content();\n-\t$mw_content_text->push_content($content_tree);\n-\n-\tmake_links_absolute($html_tree, $remote_url);\n-\n-\treturn $html_tree->as_HTML;\n-}\n-\n-sub make_links_absolute {\n-\tmy $html_tree = shift;\n-\tmy $remote_url = shift;\n-\tfor (@{ $html_tree->extract_links() }) {\n-\t\tmy ($link, $element, $attr) = @{ $_ };\n-\t\tmy $url = url($link)->canonical;\n-\t\tif ($url !~ /#/) {\n-\t\t\t$element->attr($attr, URI->new_abs($url, $remote_url));\n-\t\t}\n-\t}\n-\treturn $html_tree;\n-}\n-\n-sub is_valid_remote {\n-\tmy $remote = shift;\n-\tmy @remotes = git_cmd_try {\n-\t\tGit::command('remote') }\n-\t\t\"%s failed w/ code %d\";\n-\tmy $found_remote = 0;\n-\tforeach my $remote (@remotes) {\n-\t\tif ($remote eq $remote) {\n-\t\t\t$found_remote = 1;\n-\t\t\tlast;\n-\t\t}\n-\t}\n-\treturn $found_remote;\n-}\n-\n-sub find_mediawiki_remotes {\n-\tmy @remotes = git_cmd_try {\n-\t\tGit::command('remote'); }\n-\t\t\"%s failed w/ code %d\";\n-\tmy $remote_url;\n-\tmy @valid_remotes = ();\n-\tforeach my $remote (@remotes) {\n-\t\t$remote_url = mediawiki_remote_url_maybe($remote);\n-\t\tif ($remote_url) {\n-\t\t\tpush(@valid_remotes, $remote);\n-\t\t}\n-\t}\n-\treturn @valid_remotes;\n-}\n-\n-sub find_upstream_remote_name {\n-\tmy $current_branch = git_cmd_try {\n-\t\tGit::command_oneline('symbolic-ref', '--short', 'HEAD') }\n-\t\t\"%s failed w/ code %d\";\n-\treturn Git::config(\"branch.${current_branch}.remote\");\n-}\n-\n-sub mediawiki_remote_url_maybe {\n-\tmy $remote = shift;\n-\n-\t# Find remote url\n-\tmy $remote_url = Git::config(\"remote.${remote}.url\");\n-\tif ($remote_url =~ s/mediawiki::(.*)/$1/) {\n-\t\treturn url($remote_url)->canonical;\n-\t}\n-\n-\treturn;\n-}\n-\n-sub get_template {\n-\tmy $url = shift;\n-\tmy $page_name = shift;\n-\tmy ($req, $res, $code, $url_after);\n-\n-\t$req = LWP::UserAgent->new;\n-\tif ($verbose) {\n-\t\t$req->show_progress(1);\n-\t}\n-\n-\t$res = $req->get(\"${url}/index.php?title=${page_name}\");\n-\tif (!$res->is_success) {\n-\t\t$code = $res->code;\n-\t\t$url_after = $res->request()->uri(); # resolve all redirections\n-\t\tif ($code == HTTP_CODE_PAGE_NOT_FOUND) {\n-\t\t\tif ($verbose) {\n-\t\t\t\tprint {*STDERR} <<\"WARNING\";\n-Warning: Failed to retrieve '$page_name'. Create it on the mediawiki if you want\n-all the links to work properly.\n-Trying to use the mediawiki homepage as a fallback template ...\n-WARNING\n-\t\t\t}\n-\n-\t\t\t# LWP automatically redirects GET request\n-\t\t\t$res = $req->get(\"${url}/index.php\");\n-\t\t\tif (!$res->is_success) {\n-\t\t\t\t$url_after = $res->request()->uri(); # resolve all redirections\n-\t\t\t\tdie \"Failed to get homepage @ ${url_after} w/ code ${code}\\n\";\n-\t\t\t}\n-\t\t} else {\n-\t\t\tdie \"Failed to get '${page_name}' @ ${url_after} w/ code ${code}\\n\";\n-\t\t}\n-\t}\n-\n-\treturn $res->decoded_content;\n-}\n-\n-############################## Help Functions ##################################\n-\n-sub help {\n-\tprint {*STDOUT} <<'END';\n-usage: git mw <command> <args>\n-\n-git mw commands are:\n-    help        Display help information about git mw\n-    preview     Parse and render local file into HTML\n-END\n-\texit;\n-}\ndiff --git a/contrib/mw-to-git/git-remote-mediawiki.perl b/contrib/mw-to-git/git-remote-mediawiki.perl\ndeleted file mode 100755\nindex a5624413dc1..00000000000\n--- a/contrib/mw-to-git/git-remote-mediawiki.perl\n+++ /dev/null\n@@ -1,1390 +0,0 @@\n-#! /usr/bin/perl\n-\n-# Copyright (C) 2011\n-#     Jérémie Nikaes <jeremie.nikaes@ensimag.imag.fr>\n-#     Arnaud Lacurie <arnaud.lacurie@ensimag.imag.fr>\n-#     Claire Fousse <claire.fousse@ensimag.imag.fr>\n-#     David Amouyal <david.amouyal@ensimag.imag.fr>\n-#     Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n-# License: GPL v2 or later\n-\n-# Gateway between Git and MediaWiki.\n-# Documentation & bugtracker: https://github.com/Git-Mediawiki/Git-Mediawiki\n-\n-use strict;\n-use MediaWiki::API;\n-use Git;\n-use Git::Mediawiki qw(clean_filename smudge_filename connect_maybe\n-\t\t\t\t\tEMPTY HTTP_CODE_OK);\n-use DateTime::Format::ISO8601;\n-use warnings;\n-\n-# By default, use UTF-8 to communicate with Git and the user\n-binmode STDERR, ':encoding(UTF-8)';\n-binmode STDOUT, ':encoding(UTF-8)';\n-\n-use URI::Escape;\n-\n-# It's not always possible to delete pages (may require some\n-# privileges). Deleted pages are replaced with this content.\n-use constant DELETED_CONTENT => \"[[Category:Deleted]]\\n\";\n-\n-# It's not possible to create empty pages. New empty files in Git are\n-# sent with this content instead.\n-use constant EMPTY_CONTENT => \"<!-- empty page -->\\n\";\n-\n-# used to reflect file creation or deletion in diff.\n-use constant NULL_SHA1 => '0000000000000000000000000000000000000000';\n-\n-# Used on Git's side to reflect empty edit messages on the wiki\n-use constant EMPTY_MESSAGE => '*Empty MediaWiki Message*';\n-\n-# Number of pages taken into account at once in submodule get_mw_page_list\n-use constant SLICE_SIZE => 50;\n-\n-# Number of linked mediafile to get at once in get_linked_mediafiles\n-# The query is split in small batches because of the MW API limit of\n-# the number of links to be returned (500 links max).\n-use constant BATCH_SIZE => 10;\n-\n-if (@ARGV != 2) {\n-\texit_error_usage();\n-}\n-\n-my $remotename = $ARGV[0];\n-my $url = $ARGV[1];\n-\n-# Accept both space-separated and multiple keys in config file.\n-# Spaces should be written as _ anyway because we'll use chomp.\n-my @tracked_pages = split(/[ \\n]/, run_git_quoted([\"config\", \"--get-all\", \"remote.${remotename}.pages\"]));\n-chomp(@tracked_pages);\n-\n-# Just like @tracked_pages, but for MediaWiki categories.\n-my @tracked_categories = split(/[ \\n]/, run_git_quoted([\"config\", \"--get-all\", \"remote.${remotename}.categories\"]));\n-chomp(@tracked_categories);\n-\n-# Just like @tracked_categories, but for MediaWiki namespaces.\n-my @tracked_namespaces = split(/[ \\n]/, run_git_quoted([\"config\", \"--get-all\", \"remote.${remotename}.namespaces\"]));\n-for (@tracked_namespaces) { s/_/ /g; }\n-chomp(@tracked_namespaces);\n-\n-# Import media files on pull\n-my $import_media = run_git_quoted([\"config\", \"--get\", \"--bool\", \"remote.${remotename}.mediaimport\"]);\n-chomp($import_media);\n-$import_media = ($import_media eq 'true');\n-\n-# Export media files on push\n-my $export_media = run_git_quoted([\"config\", \"--get\", \"--bool\", \"remote.${remotename}.mediaexport\"]);\n-chomp($export_media);\n-$export_media = !($export_media eq 'false');\n-\n-my $wiki_login = run_git_quoted([\"config\", \"--get\", \"remote.${remotename}.mwLogin\"]);\n-# Note: mwPassword is discouraged. Use the credential system instead.\n-my $wiki_passwd = run_git_quoted([\"config\", \"--get\", \"remote.${remotename}.mwPassword\"]);\n-my $wiki_domain = run_git_quoted([\"config\", \"--get\", \"remote.${remotename}.mwDomain\"]);\n-chomp($wiki_login);\n-chomp($wiki_passwd);\n-chomp($wiki_domain);\n-\n-# Import only last revisions (both for clone and fetch)\n-my $shallow_import = run_git_quoted([\"config\", \"--get\", \"--bool\", \"remote.${remotename}.shallow\"]);\n-chomp($shallow_import);\n-$shallow_import = ($shallow_import eq 'true');\n-\n-# Fetch (clone and pull) by revisions instead of by pages. This behavior\n-# is more efficient when we have a wiki with lots of pages and we fetch\n-# the revisions quite often so that they concern only few pages.\n-# Possible values:\n-# - by_rev: perform one query per new revision on the remote wiki\n-# - by_page: query each tracked page for new revision\n-my $fetch_strategy = run_git_quoted([\"config\", \"--get\", \"remote.${remotename}.fetchStrategy\"]);\n-if (!$fetch_strategy) {\n-\t$fetch_strategy = run_git_quoted([\"config\", \"--get\", \"mediawiki.fetchStrategy\"]);\n-}\n-chomp($fetch_strategy);\n-if (!$fetch_strategy) {\n-\t$fetch_strategy = 'by_page';\n-}\n-\n-# Remember the timestamp corresponding to a revision id.\n-my %basetimestamps;\n-\n-# Dumb push: don't update notes and mediawiki ref to reflect the last push.\n-#\n-# Configurable with mediawiki.dumbPush, or per-remote with\n-# remote.<remotename>.dumbPush.\n-#\n-# This means the user will have to re-import the just-pushed\n-# revisions. On the other hand, this means that the Git revisions\n-# corresponding to MediaWiki revisions are all imported from the wiki,\n-# regardless of whether they were initially created in Git or from the\n-# web interface, hence all users will get the same history (i.e. if\n-# the push from Git to MediaWiki loses some information, everybody\n-# will get the history with information lost). If the import is\n-# deterministic, this means everybody gets the same sha1 for each\n-# MediaWiki revision.\n-my $dumb_push = run_git_quoted([\"config\", \"--get\", \"--bool\", \"remote.${remotename}.dumbPush\"]);\n-if (!$dumb_push) {\n-\t$dumb_push = run_git_quoted([\"config\", \"--get\", \"--bool\", \"mediawiki.dumbPush\"]);\n-}\n-chomp($dumb_push);\n-$dumb_push = ($dumb_push eq 'true');\n-\n-my $wiki_name = $url;\n-$wiki_name =~ s{[^/]*://}{};\n-# If URL is like http://user:password@example.com/, we clearly don't\n-# want the password in $wiki_name. While we're there, also remove user\n-# and '@' sign, to avoid author like MWUser@HTTPUser@host.com\n-$wiki_name =~ s/^.*@//;\n-\n-# Commands parser\n-while (<STDIN>) {\n-\tchomp;\n-\n-\tif (!parse_command($_)) {\n-\t\tlast;\n-\t}\n-\n-\tBEGIN { $| = 1 } # flush STDOUT, to make sure the previous\n-\t\t\t # command is fully processed.\n-}\n-\n-########################## Functions ##############################\n-\n-## error handling\n-sub exit_error_usage {\n-\tdie \"ERROR: git-remote-mediawiki module was not called with a correct number of\\n\" .\n-\t    \"parameters\\n\" .\n-\t    \"You may obtain this error because you attempted to run the git-remote-mediawiki\\n\" .\n-            \"module directly.\\n\" .\n-\t    \"This module can be used the following way:\\n\" .\n-\t    \"\\tgit clone mediawiki://<address of a mediawiki>\\n\" .\n-\t    \"Then, use git commit, push and pull as with every normal git repository.\\n\";\n-}\n-\n-sub parse_command {\n-\tmy ($line) = @_;\n-\tmy @cmd = split(/ /, $line);\n-\tif (!defined $cmd[0]) {\n-\t\treturn 0;\n-\t}\n-\tif ($cmd[0] eq 'capabilities') {\n-\t\tdie(\"Too many arguments for capabilities\\n\")\n-\t\t    if (defined($cmd[1]));\n-\t\tmw_capabilities();\n-\t} elsif ($cmd[0] eq 'list') {\n-\t\tdie(\"Too many arguments for list\\n\") if (defined($cmd[2]));\n-\t\tmw_list($cmd[1]);\n-\t} elsif ($cmd[0] eq 'import') {\n-\t\tdie(\"Invalid argument for import\\n\")\n-\t\t    if ($cmd[1] eq EMPTY);\n-\t\tdie(\"Too many arguments for import\\n\")\n-\t\t    if (defined($cmd[2]));\n-\t\tmw_import($cmd[1]);\n-\t} elsif ($cmd[0] eq 'option') {\n-\t\tdie(\"Invalid arguments for option\\n\")\n-\t\t    if ($cmd[1] eq EMPTY || $cmd[2] eq EMPTY);\n-\t\tdie(\"Too many arguments for option\\n\")\n-\t\t    if (defined($cmd[3]));\n-\t\tmw_option($cmd[1],$cmd[2]);\n-\t} elsif ($cmd[0] eq 'push') {\n-\t\tmw_push($cmd[1]);\n-\t} else {\n-\t\tprint {*STDERR} \"Unknown command. Aborting...\\n\";\n-\t\treturn 0;\n-\t}\n-\treturn 1;\n-}\n-\n-# MediaWiki API instance, created lazily.\n-my $mediawiki;\n-\n-sub fatal_mw_error {\n-\tmy $action = shift;\n-\tprint STDERR \"fatal: could not $action.\\n\";\n-\tprint STDERR \"fatal: '$url' does not appear to be a mediawiki\\n\";\n-\tif ($url =~ /^https/) {\n-\t\tprint STDERR \"fatal: make sure '$url/api.php' is a valid page\\n\";\n-\t\tprint STDERR \"fatal: and the SSL certificate is correct.\\n\";\n-\t} else {\n-\t\tprint STDERR \"fatal: make sure '$url/api.php' is a valid page.\\n\";\n-\t}\n-\tprint STDERR \"fatal: (error \" .\n-\t    $mediawiki->{error}->{code} . ': ' .\n-\t    $mediawiki->{error}->{details} . \")\\n\";\n-\texit 1;\n-}\n-\n-## Functions for listing pages on the remote wiki\n-sub get_mw_tracked_pages {\n-\tmy $pages = shift;\n-\tget_mw_page_list(\\@tracked_pages, $pages);\n-\treturn;\n-}\n-\n-sub get_mw_page_list {\n-\tmy $page_list = shift;\n-\tmy $pages = shift;\n-\tmy @some_pages = @{$page_list};\n-\twhile (@some_pages) {\n-\t\tmy $last_page = SLICE_SIZE;\n-\t\tif ($#some_pages < $last_page) {\n-\t\t\t$last_page = $#some_pages;\n-\t\t}\n-\t\tmy @slice = @some_pages[0..$last_page];\n-\t\tget_mw_first_pages(\\@slice, $pages);\n-\t\t@some_pages = @some_pages[(SLICE_SIZE + 1)..$#some_pages];\n-\t}\n-\treturn;\n-}\n-\n-sub get_mw_tracked_categories {\n-\tmy $pages = shift;\n-\tforeach my $category (@tracked_categories) {\n-\t\tif (index($category, ':') < 0) {\n-\t\t\t# Mediawiki requires the Category\n-\t\t\t# prefix, but let's not force the user\n-\t\t\t# to specify it.\n-\t\t\t$category = \"Category:${category}\";\n-\t\t}\n-\t\tmy $mw_pages = $mediawiki->list( {\n-\t\t\taction => 'query',\n-\t\t\tlist => 'categorymembers',\n-\t\t\tcmtitle => $category,\n-\t\t\tcmlimit => 'max' } )\n-\t\t\t|| die $mediawiki->{error}->{code} . ': '\n-\t\t\t\t. $mediawiki->{error}->{details} . \"\\n\";\n-\t\tforeach my $page (@{$mw_pages}) {\n-\t\t\t$pages->{$page->{title}} = $page;\n-\t\t}\n-\t}\n-\treturn;\n-}\n-\n-sub get_mw_tracked_namespaces {\n-    my $pages = shift;\n-    foreach my $local_namespace (sort @tracked_namespaces) {\n-        my $namespace_id;\n-        if ($local_namespace eq \"(Main)\") {\n-            $namespace_id = 0;\n-        } else {\n-            $namespace_id = get_mw_namespace_id($local_namespace);\n-        }\n-        # virtual namespaces don't support allpages\n-        next if !defined($namespace_id) || $namespace_id < 0;\n-        my $mw_pages = $mediawiki->list( {\n-            action => 'query',\n-            list => 'allpages',\n-            apnamespace => $namespace_id,\n-            aplimit => 'max' } )\n-            || die $mediawiki->{error}->{code} . ': '\n-                . $mediawiki->{error}->{details} . \"\\n\";\n-        print {*STDERR} \"$#{$mw_pages} found in namespace $local_namespace ($namespace_id)\\n\";\n-        foreach my $page (@{$mw_pages}) {\n-            $pages->{$page->{title}} = $page;\n-        }\n-    }\n-    return;\n-}\n-\n-sub get_mw_all_pages {\n-\tmy $pages = shift;\n-\t# No user-provided list, get the list of pages from the API.\n-\tmy $mw_pages = $mediawiki->list({\n-\t\taction => 'query',\n-\t\tlist => 'allpages',\n-\t\taplimit => 'max'\n-\t});\n-\tif (!defined($mw_pages)) {\n-\t\tfatal_mw_error(\"get the list of wiki pages\");\n-\t}\n-\tforeach my $page (@{$mw_pages}) {\n-\t\t$pages->{$page->{title}} = $page;\n-\t}\n-\treturn;\n-}\n-\n-# queries the wiki for a set of pages. Meant to be used within a loop\n-# querying the wiki for slices of page list.\n-sub get_mw_first_pages {\n-\tmy $some_pages = shift;\n-\tmy @some_pages = @{$some_pages};\n-\n-\tmy $pages = shift;\n-\n-\t# pattern 'page1|page2|...' required by the API\n-\tmy $titles = join('|', @some_pages);\n-\n-\tmy $mw_pages = $mediawiki->api({\n-\t\taction => 'query',\n-\t\ttitles => $titles,\n-\t});\n-\tif (!defined($mw_pages)) {\n-\t\tfatal_mw_error(\"query the list of wiki pages\");\n-\t}\n-\twhile (my ($id, $page) = each(%{$mw_pages->{query}->{pages}})) {\n-\t\tif ($id < 0) {\n-\t\t\tprint {*STDERR} \"Warning: page $page->{title} not found on wiki\\n\";\n-\t\t} else {\n-\t\t\t$pages->{$page->{title}} = $page;\n-\t\t}\n-\t}\n-\treturn;\n-}\n-\n-# Get the list of pages to be fetched according to configuration.\n-sub get_mw_pages {\n-\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\n-\tprint {*STDERR} \"Listing pages on remote wiki...\\n\";\n-\n-\tmy %pages; # hash on page titles to avoid duplicates\n-\tmy $user_defined;\n-\tif (@tracked_pages) {\n-\t\t$user_defined = 1;\n-\t\t# The user provided a list of pages titles, but we\n-\t\t# still need to query the API to get the page IDs.\n-\t\tget_mw_tracked_pages(\\%pages);\n-\t}\n-\tif (@tracked_categories) {\n-\t\t$user_defined = 1;\n-\t\tget_mw_tracked_categories(\\%pages);\n-\t}\n-\tif (@tracked_namespaces) {\n-\t\t$user_defined = 1;\n-\t\tget_mw_tracked_namespaces(\\%pages);\n-\t}\n-\tif (!$user_defined) {\n-\t\tget_mw_all_pages(\\%pages);\n-\t}\n-\tif ($import_media) {\n-\t\tprint {*STDERR} \"Getting media files for selected pages...\\n\";\n-\t\tif ($user_defined) {\n-\t\t\tget_linked_mediafiles(\\%pages);\n-\t\t} else {\n-\t\t\tget_all_mediafiles(\\%pages);\n-\t\t}\n-\t}\n-\tprint {*STDERR} (scalar keys %pages) . \" pages found.\\n\";\n-\treturn %pages;\n-}\n-\n-# usage: $out = run_git_quoted([\"command\", \"args\", ...]);\n-#        $out = run_git_quoted([\"command\", \"args\", ...], \"raw\"); # don't interpret output as UTF-8.\n-#        $out = run_git_quoted_nostderr([\"command\", \"args\", ...]); # discard stderr\n-#        $out = run_git_quoted_nostderr([\"command\", \"args\", ...], \"raw\"); # ditto but raw instead of UTF-8 as above\n-sub _run_git {\n-\tmy $args = shift;\n-\tmy $encoding = (shift || 'encoding(UTF-8)');\n-\topen(my $git, \"-|:${encoding}\", @$args)\n-\t    or die \"Unable to fork: $!\\n\";\n-\tmy $res = do {\n-\t\tlocal $/ = undef;\n-\t\t<$git>\n-\t};\n-\tclose($git);\n-\n-\treturn $res;\n-}\n-\n-sub run_git_quoted {\n-    _run_git([\"git\", @{$_[0]}], $_[1]);\n-}\n-\n-sub run_git_quoted_nostderr {\n-    _run_git(['sh', '-c', 'git \"$@\" 2>/dev/null', '--', @{$_[0]}], $_[1]);\n-}\n-\n-sub get_all_mediafiles {\n-\tmy $pages = shift;\n-\t# Attach list of all pages for media files from the API,\n-\t# they are in a different namespace, only one namespace\n-\t# can be queried at the same moment\n-\tmy $mw_pages = $mediawiki->list({\n-\t\taction => 'query',\n-\t\tlist => 'allpages',\n-\t\tapnamespace => get_mw_namespace_id('File'),\n-\t\taplimit => 'max'\n-\t});\n-\tif (!defined($mw_pages)) {\n-\t\tprint {*STDERR} \"fatal: could not get the list of pages for media files.\\n\";\n-\t\tprint {*STDERR} \"fatal: '$url' does not appear to be a mediawiki\\n\";\n-\t\tprint {*STDERR} \"fatal: make sure '$url/api.php' is a valid page.\\n\";\n-\t\texit 1;\n-\t}\n-\tforeach my $page (@{$mw_pages}) {\n-\t\t$pages->{$page->{title}} = $page;\n-\t}\n-\treturn;\n-}\n-\n-sub get_linked_mediafiles {\n-\tmy $pages = shift;\n-\tmy @titles = map { $_->{title} } values(%{$pages});\n-\n-\tmy $batch = BATCH_SIZE;\n-\twhile (@titles) {\n-\t\tif ($#titles < $batch) {\n-\t\t\t$batch = $#titles;\n-\t\t}\n-\t\tmy @slice = @titles[0..$batch];\n-\n-\t\t# pattern 'page1|page2|...' required by the API\n-\t\tmy $mw_titles = join('|', @slice);\n-\n-\t\t# Media files could be included or linked from\n-\t\t# a page, get all related\n-\t\tmy $query = {\n-\t\t\taction => 'query',\n-\t\t\tprop => 'links|images',\n-\t\t\ttitles => $mw_titles,\n-\t\t\tplnamespace => get_mw_namespace_id('File'),\n-\t\t\tpllimit => 'max'\n-\t\t};\n-\t\tmy $result = $mediawiki->api($query);\n-\n-\t\twhile (my ($id, $page) = each(%{$result->{query}->{pages}})) {\n-\t\t\tmy @media_titles;\n-\t\t\tif (defined($page->{links})) {\n-\t\t\t\tmy @link_titles\n-\t\t\t\t    = map { $_->{title} } @{$page->{links}};\n-\t\t\t\tpush(@media_titles, @link_titles);\n-\t\t\t}\n-\t\t\tif (defined($page->{images})) {\n-\t\t\t\tmy @image_titles\n-\t\t\t\t    = map { $_->{title} } @{$page->{images}};\n-\t\t\t\tpush(@media_titles, @image_titles);\n-\t\t\t}\n-\t\t\tif (@media_titles) {\n-\t\t\t\tget_mw_page_list(\\@media_titles, $pages);\n-\t\t\t}\n-\t\t}\n-\n-\t\t@titles = @titles[($batch+1)..$#titles];\n-\t}\n-\treturn;\n-}\n-\n-sub get_mw_mediafile_for_page_revision {\n-\t# Name of the file on Wiki, with the prefix.\n-\tmy $filename = shift;\n-\tmy $timestamp = shift;\n-\tmy %mediafile;\n-\n-\t# Search if on a media file with given timestamp exists on\n-\t# MediaWiki. In that case download the file.\n-\tmy $query = {\n-\t\taction => 'query',\n-\t\tprop => 'imageinfo',\n-\t\ttitles => \"File:${filename}\",\n-\t\tiistart => $timestamp,\n-\t\tiiend => $timestamp,\n-\t\tiiprop => 'timestamp|archivename|url',\n-\t\tiilimit => 1\n-\t};\n-\tmy $result = $mediawiki->api($query);\n-\n-\tmy ($fileid, $file) = each( %{$result->{query}->{pages}} );\n-\t# If not defined it means there is no revision of the file for\n-\t# given timestamp.\n-\tif (defined($file->{imageinfo})) {\n-\t\t$mediafile{title} = $filename;\n-\n-\t\tmy $fileinfo = pop(@{$file->{imageinfo}});\n-\t\t$mediafile{timestamp} = $fileinfo->{timestamp};\n-\t\t# Mediawiki::API's download function doesn't support https URLs\n-\t\t# and can't download old versions of files.\n-\t\tprint {*STDERR} \"\\tDownloading file $mediafile{title}, version $mediafile{timestamp}\\n\";\n-\t\t$mediafile{content} = download_mw_mediafile($fileinfo->{url});\n-\t}\n-\treturn %mediafile;\n-}\n-\n-sub download_mw_mediafile {\n-\tmy $download_url = shift;\n-\n-\tmy $response = $mediawiki->{ua}->get($download_url);\n-\tif ($response->code == HTTP_CODE_OK) {\n-\t\t# It is tempting to return\n-\t\t# $response->decoded_content({charset => \"none\"}), but\n-\t\t# when doing so, utf8::downgrade($content) fails with\n-\t\t# \"Wide character in subroutine entry\".\n-\t\t$response->decode();\n-\t\treturn $response->content();\n-\t} else {\n-\t\tprint {*STDERR} \"Error downloading mediafile from :\\n\";\n-\t\tprint {*STDERR} \"URL: ${download_url}\\n\";\n-\t\tprint {*STDERR} 'Server response: ' . $response->code . q{ } . $response->message . \"\\n\";\n-\t\texit 1;\n-\t}\n-}\n-\n-sub get_last_local_revision {\n-\t# Get note regarding last mediawiki revision.\n-\tmy $note = run_git_quoted_nostderr([\"notes\", \"--ref=${remotename}/mediawiki\",\n-\t\t\t\t\t    \"show\", \"refs/mediawiki/${remotename}/master\"]);\n-\tmy @note_info = split(/ /, $note);\n-\n-\tmy $lastrevision_number;\n-\tif (!(defined($note_info[0]) && $note_info[0] eq 'mediawiki_revision:')) {\n-\t\tprint {*STDERR} 'No previous mediawiki revision found';\n-\t\t$lastrevision_number = 0;\n-\t} else {\n-\t\t# Notes are formatted : mediawiki_revision: #number\n-\t\t$lastrevision_number = $note_info[1];\n-\t\tchomp($lastrevision_number);\n-\t\tprint {*STDERR} \"Last local mediawiki revision found is ${lastrevision_number}\";\n-\t}\n-\treturn $lastrevision_number;\n-}\n-\n-# Get the last remote revision without taking in account which pages are\n-# tracked or not. This function makes a single request to the wiki thus\n-# avoid a loop onto all tracked pages. This is useful for the fetch-by-rev\n-# option.\n-sub get_last_global_remote_rev {\n-\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\n-\tmy $query = {\n-\t\taction => 'query',\n-\t\tlist => 'recentchanges',\n-\t\tprop => 'revisions',\n-\t\trclimit => '1',\n-\t\trcdir => 'older',\n-\t};\n-\tmy $result = $mediawiki->api($query);\n-\treturn $result->{query}->{recentchanges}[0]->{revid};\n-}\n-\n-# Get the last remote revision concerning the tracked pages and the tracked\n-# categories.\n-sub get_last_remote_revision {\n-\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\n-\tmy %pages_hash = get_mw_pages();\n-\tmy @pages = values(%pages_hash);\n-\n-\tmy $max_rev_num = 0;\n-\n-\tprint {*STDERR} \"Getting last revision id on tracked pages...\\n\";\n-\n-\tforeach my $page (@pages) {\n-\t\tmy $id = $page->{pageid};\n-\n-\t\tmy $query = {\n-\t\t\taction => 'query',\n-\t\t\tprop => 'revisions',\n-\t\t\trvprop => 'ids|timestamp',\n-\t\t\tpageids => $id,\n-\t\t};\n-\n-\t\tmy $result = $mediawiki->api($query);\n-\n-\t\tmy $lastrev = pop(@{$result->{query}->{pages}->{$id}->{revisions}});\n-\n-\t\t$basetimestamps{$lastrev->{revid}} = $lastrev->{timestamp};\n-\n-\t\t$max_rev_num = ($lastrev->{revid} > $max_rev_num ? $lastrev->{revid} : $max_rev_num);\n-\t}\n-\n-\tprint {*STDERR} \"Last remote revision found is $max_rev_num.\\n\";\n-\treturn $max_rev_num;\n-}\n-\n-# Clean content before sending it to MediaWiki\n-sub mediawiki_clean {\n-\tmy $string = shift;\n-\tmy $page_created = shift;\n-\t# Mediawiki does not allow blank space at the end of a page and ends with a single \\n.\n-\t# This function right trims a string and adds a \\n at the end to follow this rule\n-\t$string =~ s/\\s+$//;\n-\tif ($string eq EMPTY && $page_created) {\n-\t\t# Creating empty pages is forbidden.\n-\t\t$string = EMPTY_CONTENT;\n-\t}\n-\treturn $string.\"\\n\";\n-}\n-\n-# Filter applied on MediaWiki data before adding them to Git\n-sub mediawiki_smudge {\n-\tmy $string = shift;\n-\tif ($string eq EMPTY_CONTENT) {\n-\t\t$string = EMPTY;\n-\t}\n-\t# This \\n is important. This is due to mediawiki's way to handle end of files.\n-\treturn \"${string}\\n\";\n-}\n-\n-sub literal_data {\n-\tmy ($content) = @_;\n-\tprint {*STDOUT} 'data ', bytes::length($content), \"\\n\", $content;\n-\treturn;\n-}\n-\n-sub literal_data_raw {\n-\t# Output possibly binary content.\n-\tmy ($content) = @_;\n-\t# Avoid confusion between size in bytes and in characters\n-\tutf8::downgrade($content);\n-\tbinmode STDOUT, ':raw';\n-\tprint {*STDOUT} 'data ', bytes::length($content), \"\\n\", $content;\n-\tbinmode STDOUT, ':encoding(UTF-8)';\n-\treturn;\n-}\n-\n-sub mw_capabilities {\n-\t# Revisions are imported to the private namespace\n-\t# refs/mediawiki/$remotename/ by the helper and fetched into\n-\t# refs/remotes/$remotename later by fetch.\n-\tprint {*STDOUT} \"refspec refs/heads/*:refs/mediawiki/${remotename}/*\\n\";\n-\tprint {*STDOUT} \"import\\n\";\n-\tprint {*STDOUT} \"list\\n\";\n-\tprint {*STDOUT} \"push\\n\";\n-\tif ($dumb_push) {\n-\t\tprint {*STDOUT} \"no-private-update\\n\";\n-\t}\n-\tprint {*STDOUT} \"\\n\";\n-\treturn;\n-}\n-\n-sub mw_list {\n-\t# MediaWiki do not have branches, we consider one branch arbitrarily\n-\t# called master, and HEAD pointing to it.\n-\tprint {*STDOUT} \"? refs/heads/master\\n\";\n-\tprint {*STDOUT} \"\\@refs/heads/master HEAD\\n\";\n-\tprint {*STDOUT} \"\\n\";\n-\treturn;\n-}\n-\n-sub mw_option {\n-\tprint {*STDERR} \"remote-helper command 'option $_[0]' not yet implemented\\n\";\n-\tprint {*STDOUT} \"unsupported\\n\";\n-\treturn;\n-}\n-\n-sub fetch_mw_revisions_for_page {\n-\tmy $page = shift;\n-\tmy $id = shift;\n-\tmy $fetch_from = shift;\n-\tmy @page_revs = ();\n-\tmy $query = {\n-\t\taction => 'query',\n-\t\tprop => 'revisions',\n-\t\trvprop => 'ids',\n-\t\trvdir => 'newer',\n-\t\trvstartid => $fetch_from,\n-\t\trvlimit => 500,\n-\t\tpageids => $id,\n-\n-\t\t# Let MediaWiki know that we support the latest API.\n-\t\tcontinue => '',\n-\t};\n-\n-\tmy $revnum = 0;\n-\t# Get 500 revisions at a time due to the mediawiki api limit\n-\twhile (1) {\n-\t\tmy $result = $mediawiki->api($query);\n-\n-\t\t# Parse each of those 500 revisions\n-\t\tforeach my $revision (@{$result->{query}->{pages}->{$id}->{revisions}}) {\n-\t\t\tmy $page_rev_ids;\n-\t\t\t$page_rev_ids->{pageid} = $page->{pageid};\n-\t\t\t$page_rev_ids->{revid} = $revision->{revid};\n-\t\t\tpush(@page_revs, $page_rev_ids);\n-\t\t\t$revnum++;\n-\t\t}\n-\n-\t\tif ($result->{'query-continue'}) { # For legacy APIs\n-\t\t\t$query->{rvstartid} = $result->{'query-continue'}->{revisions}->{rvstartid};\n-\t\t} elsif ($result->{continue}) { # For newer APIs\n-\t\t\t$query->{rvstartid} = $result->{continue}->{rvcontinue};\n-\t\t\t$query->{continue} = $result->{continue}->{continue};\n-\t\t} else {\n-\t\t\tlast;\n-\t\t}\n-\t}\n-\tif ($shallow_import && @page_revs) {\n-\t\tprint {*STDERR} \"  Found 1 revision (shallow import).\\n\";\n-\t\t@page_revs = sort {$b->{revid} <=> $a->{revid}} (@page_revs);\n-\t\treturn $page_revs[0];\n-\t}\n-\tprint {*STDERR} \"  Found ${revnum} revision(s).\\n\";\n-\treturn @page_revs;\n-}\n-\n-sub fetch_mw_revisions {\n-\tmy $pages = shift; my @pages = @{$pages};\n-\tmy $fetch_from = shift;\n-\n-\tmy @revisions = ();\n-\tmy $n = 1;\n-\tforeach my $page (@pages) {\n-\t\tmy $id = $page->{pageid};\n-\t\tprint {*STDERR} \"page ${n}/\", scalar(@pages), ': ', $page->{title}, \"\\n\";\n-\t\t$n++;\n-\t\tmy @page_revs = fetch_mw_revisions_for_page($page, $id, $fetch_from);\n-\t\t@revisions = (@page_revs, @revisions);\n-\t}\n-\n-\treturn ($n, @revisions);\n-}\n-\n-sub fe_escape_path {\n-    my $path = shift;\n-    $path =~ s/\\\\/\\\\\\\\/g;\n-    $path =~ s/\"/\\\\\"/g;\n-    $path =~ s/\\n/\\\\n/g;\n-    return qq(\"${path}\");\n-}\n-\n-sub import_file_revision {\n-\tmy $commit = shift;\n-\tmy %commit = %{$commit};\n-\tmy $full_import = shift;\n-\tmy $n = shift;\n-\tmy $mediafile = shift;\n-\tmy %mediafile;\n-\tif ($mediafile) {\n-\t\t%mediafile = %{$mediafile};\n-\t}\n-\n-\tmy $title = $commit{title};\n-\tmy $comment = $commit{comment};\n-\tmy $content = $commit{content};\n-\tmy $author = $commit{author};\n-\tmy $date = $commit{date};\n-\n-\tprint {*STDOUT} \"commit refs/mediawiki/${remotename}/master\\n\";\n-\tprint {*STDOUT} \"mark :${n}\\n\";\n-\tprint {*STDOUT} \"committer ${author} <${author}\\@${wiki_name}> \" . $date->epoch . \" +0000\\n\";\n-\tliteral_data($comment);\n-\n-\t# If it's not a clone, we need to know where to start from\n-\tif (!$full_import && $n == 1) {\n-\t\tprint {*STDOUT} \"from refs/mediawiki/${remotename}/master^0\\n\";\n-\t}\n-\tif ($content ne DELETED_CONTENT) {\n-\t\tprint {*STDOUT} 'M 644 inline ' .\n-\t\t    fe_escape_path(\"${title}.mw\") . \"\\n\";\n-\t\tliteral_data($content);\n-\t\tif (%mediafile) {\n-\t\t\tprint {*STDOUT} 'M 644 inline '\n-\t\t\t    . fe_escape_path($mediafile{title}) . \"\\n\";\n-\t\t\tliteral_data_raw($mediafile{content});\n-\t\t}\n-\t\tprint {*STDOUT} \"\\n\\n\";\n-\t} else {\n-\t\tprint {*STDOUT} 'D ' . fe_escape_path(\"${title}.mw\") . \"\\n\";\n-\t}\n-\n-\t# mediawiki revision number in the git note\n-\tif ($full_import && $n == 1) {\n-\t\tprint {*STDOUT} \"reset refs/notes/${remotename}/mediawiki\\n\";\n-\t}\n-\tprint {*STDOUT} \"commit refs/notes/${remotename}/mediawiki\\n\";\n-\tprint {*STDOUT} \"committer ${author} <${author}\\@${wiki_name}> \" . $date->epoch . \" +0000\\n\";\n-\tliteral_data('Note added by git-mediawiki during import');\n-\tif (!$full_import && $n == 1) {\n-\t\tprint {*STDOUT} \"from refs/notes/${remotename}/mediawiki^0\\n\";\n-\t}\n-\tprint {*STDOUT} \"N inline :${n}\\n\";\n-\tliteral_data(\"mediawiki_revision: $commit{mw_revision}\");\n-\tprint {*STDOUT} \"\\n\\n\";\n-\treturn;\n-}\n-\n-# parse a sequence of\n-# <cmd> <arg1>\n-# <cmd> <arg2>\n-# \\n\n-# (like batch sequence of import and sequence of push statements)\n-sub get_more_refs {\n-\tmy $cmd = shift;\n-\tmy @refs;\n-\twhile (1) {\n-\t\tmy $line = <STDIN>;\n-\t\tif ($line =~ /^$cmd (.*)$/) {\n-\t\t\tpush(@refs, $1);\n-\t\t} elsif ($line eq \"\\n\") {\n-\t\t\treturn @refs;\n-\t\t} else {\n-\t\t\tdie(\"Invalid command in a '$cmd' batch: $_\\n\");\n-\t\t}\n-\t}\n-\treturn;\n-}\n-\n-sub mw_import {\n-\t# multiple import commands can follow each other.\n-\tmy @refs = (shift, get_more_refs('import'));\n-\tmy $processedRefs;\n-\tforeach my $ref (@refs) {\n-\t\tnext if $processedRefs->{$ref}; # skip duplicates: \"import refs/heads/master\" being issued twice; TODO: why?\n-\t\t$processedRefs->{$ref} = 1;\n-\t\tmw_import_ref($ref);\n-\t}\n-\tprint {*STDOUT} \"done\\n\";\n-\treturn;\n-}\n-\n-sub mw_import_ref {\n-\tmy $ref = shift;\n-\t# The remote helper will call \"import HEAD\" and\n-\t# \"import refs/heads/master\".\n-\t# Since HEAD is a symbolic ref to master (by convention,\n-\t# followed by the output of the command \"list\" that we gave),\n-\t# we don't need to do anything in this case.\n-\tif ($ref eq 'HEAD') {\n-\t\treturn;\n-\t}\n-\n-\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\n-\tprint {*STDERR} \"Searching revisions...\\n\";\n-\tmy $last_local = get_last_local_revision();\n-\tmy $fetch_from = $last_local + 1;\n-\tif ($fetch_from == 1) {\n-\t\tprint {*STDERR} \", fetching from beginning.\\n\";\n-\t} else {\n-\t\tprint {*STDERR} \", fetching from here.\\n\";\n-\t}\n-\n-\tmy $n = 0;\n-\tif ($fetch_strategy eq 'by_rev') {\n-\t\tprint {*STDERR} \"Fetching & writing export data by revs...\\n\";\n-\t\t$n = mw_import_ref_by_revs($fetch_from);\n-\t} elsif ($fetch_strategy eq 'by_page') {\n-\t\tprint {*STDERR} \"Fetching & writing export data by pages...\\n\";\n-\t\t$n = mw_import_ref_by_pages($fetch_from);\n-\t} else {\n-\t\tprint {*STDERR} qq(fatal: invalid fetch strategy \"${fetch_strategy}\".\\n);\n-\t\tprint {*STDERR} \"Check your configuration variables remote.${remotename}.fetchStrategy and mediawiki.fetchStrategy\\n\";\n-\t\texit 1;\n-\t}\n-\n-\tif ($fetch_from == 1 && $n == 0) {\n-\t\tprint {*STDERR} \"You appear to have cloned an empty MediaWiki.\\n\";\n-\t\t# Something has to be done remote-helper side. If nothing is done, an error is\n-\t\t# thrown saying that HEAD is referring to unknown object 0000000000000000000\n-\t\t# and the clone fails.\n-\t}\n-\treturn;\n-}\n-\n-sub mw_import_ref_by_pages {\n-\n-\tmy $fetch_from = shift;\n-\tmy %pages_hash = get_mw_pages();\n-\tmy @pages = values(%pages_hash);\n-\n-\tmy ($n, @revisions) = fetch_mw_revisions(\\@pages, $fetch_from);\n-\n-\t@revisions = sort {$a->{revid} <=> $b->{revid}} @revisions;\n-\tmy @revision_ids = map { $_->{revid} } @revisions;\n-\n-\treturn mw_import_revids($fetch_from, \\@revision_ids, \\%pages_hash);\n-}\n-\n-sub mw_import_ref_by_revs {\n-\n-\tmy $fetch_from = shift;\n-\tmy %pages_hash = get_mw_pages();\n-\n-\tmy $last_remote = get_last_global_remote_rev();\n-\tmy @revision_ids = $fetch_from..$last_remote;\n-\treturn mw_import_revids($fetch_from, \\@revision_ids, \\%pages_hash);\n-}\n-\n-# Import revisions given in second argument (array of integers).\n-# Only pages appearing in the third argument (hash indexed by page titles)\n-# will be imported.\n-sub mw_import_revids {\n-\tmy $fetch_from = shift;\n-\tmy $revision_ids = shift;\n-\tmy $pages = shift;\n-\n-\tmy $n = 0;\n-\tmy $n_actual = 0;\n-\tmy $last_timestamp = 0; # Placeholder in case $rev->timestamp is undefined\n-\n-\tforeach my $pagerevid (@{$revision_ids}) {\n-\t        # Count page even if we skip it, since we display\n-\t\t# $n/$total and $total includes skipped pages.\n-\t\t$n++;\n-\n-\t\t# fetch the content of the pages\n-\t\tmy $query = {\n-\t\t\taction => 'query',\n-\t\t\tprop => 'revisions',\n-\t\t\trvprop => 'content|timestamp|comment|user|ids',\n-\t\t\trevids => $pagerevid,\n-\t\t};\n-\n-\t\tmy $result = $mediawiki->api($query);\n-\n-\t\tif (!$result) {\n-\t\t\tdie \"Failed to retrieve modified page for revision $pagerevid\\n\";\n-\t\t}\n-\n-\t\tif (defined($result->{query}->{badrevids}->{$pagerevid})) {\n-\t\t\t# The revision id does not exist on the remote wiki.\n-\t\t\tnext;\n-\t\t}\n-\n-\t\tif (!defined($result->{query}->{pages})) {\n-\t\t\tdie \"Invalid revision ${pagerevid}.\\n\";\n-\t\t}\n-\n-\t\tmy @result_pages = values(%{$result->{query}->{pages}});\n-\t\tmy $result_page = $result_pages[0];\n-\t\tmy $rev = $result_pages[0]->{revisions}->[0];\n-\n-\t\tmy $page_title = $result_page->{title};\n-\n-\t\tif (!exists($pages->{$page_title})) {\n-\t\t\tprint {*STDERR} \"${n}/\", scalar(@{$revision_ids}),\n-\t\t\t\t\": Skipping revision #$rev->{revid} of ${page_title}\\n\";\n-\t\t\tnext;\n-\t\t}\n-\n-\t\t$n_actual++;\n-\n-\t\tmy %commit;\n-\t\t$commit{author} = $rev->{user} || 'Anonymous';\n-\t\t$commit{comment} = $rev->{comment} || EMPTY_MESSAGE;\n-\t\t$commit{title} = smudge_filename($page_title);\n-\t\t$commit{mw_revision} = $rev->{revid};\n-\t\t$commit{content} = mediawiki_smudge($rev->{'*'});\n-\n-\t\tif (!defined($rev->{timestamp})) {\n-\t\t\t$last_timestamp++;\n-\t\t} else {\n-\t\t\t$last_timestamp = $rev->{timestamp};\n-\t\t}\n-\t\t$commit{date} = DateTime::Format::ISO8601->parse_datetime($last_timestamp);\n-\n-\t\t# Differentiates classic pages and media files.\n-\t\tmy ($namespace, $filename) = $page_title =~ /^([^:]*):(.*)$/;\n-\t\tmy %mediafile;\n-\t\tif ($namespace) {\n-\t\t\tmy $id = get_mw_namespace_id($namespace);\n-\t\t\tif ($id && $id == get_mw_namespace_id('File')) {\n-\t\t\t\t%mediafile = get_mw_mediafile_for_page_revision($filename, $rev->{timestamp});\n-\t\t\t}\n-\t\t}\n-\t\t# If this is a revision of the media page for new version\n-\t\t# of a file do one common commit for both file and media page.\n-\t\t# Else do commit only for that page.\n-\t\tprint {*STDERR} \"${n}/\", scalar(@{$revision_ids}), \": Revision #$rev->{revid} of $commit{title}\\n\";\n-\t\timport_file_revision(\\%commit, ($fetch_from == 1), $n_actual, \\%mediafile);\n-\t}\n-\n-\treturn $n_actual;\n-}\n-\n-sub error_non_fast_forward {\n-\tmy $advice = run_git_quoted([\"config\", \"--bool\", \"advice.pushNonFastForward\"]);\n-\tchomp($advice);\n-\tif ($advice ne 'false') {\n-\t\t# Native git-push would show this after the summary.\n-\t\t# We can't ask it to display it cleanly, so print it\n-\t\t# ourselves before.\n-\t\tprint {*STDERR} \"To prevent you from losing history, non-fast-forward updates were rejected\\n\";\n-\t\tprint {*STDERR} \"Merge the remote changes (e.g. 'git pull') before pushing again. See the\\n\";\n-\t\tprint {*STDERR} \"'Note about fast-forwards' section of 'git push --help' for details.\\n\";\n-\t}\n-\tprint {*STDOUT} qq(error $_[0] \"non-fast-forward\"\\n);\n-\treturn 0;\n-}\n-\n-sub mw_upload_file {\n-\tmy $complete_file_name = shift;\n-\tmy $new_sha1 = shift;\n-\tmy $extension = shift;\n-\tmy $file_deleted = shift;\n-\tmy $summary = shift;\n-\tmy $newrevid;\n-\tmy $path = \"File:${complete_file_name}\";\n-\tmy %hashFiles = get_allowed_file_extensions();\n-\tif (!exists($hashFiles{$extension})) {\n-\t\tprint {*STDERR} \"${complete_file_name} is not a permitted file on this wiki.\\n\";\n-\t\tprint {*STDERR} \"Check the configuration of file uploads in your mediawiki.\\n\";\n-\t\treturn $newrevid;\n-\t}\n-\t# Deleting and uploading a file requires a privileged user\n-\tif ($file_deleted) {\n-\t\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\t\tmy $query = {\n-\t\t\taction => 'delete',\n-\t\t\ttitle => $path,\n-\t\t\treason => $summary\n-\t\t};\n-\t\tif (!$mediawiki->edit($query)) {\n-\t\t\tprint {*STDERR} \"Failed to delete file on remote wiki\\n\";\n-\t\t\tprint {*STDERR} \"Check your permissions on the remote site. Error code:\\n\";\n-\t\t\tprint {*STDERR} $mediawiki->{error}->{code} . ':' . $mediawiki->{error}->{details};\n-\t\t\texit 1;\n-\t\t}\n-\t} else {\n-\t\t# Don't let perl try to interpret file content as UTF-8 => use \"raw\"\n-\t\tmy $content = run_git_quoted([\"cat-file\", \"blob\", $new_sha1], 'raw');\n-\t\tif ($content ne EMPTY) {\n-\t\t\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\t\t\t$mediawiki->{config}->{upload_url} =\n-\t\t\t\t\"${url}/index.php/Special:Upload\";\n-\t\t\t$mediawiki->edit({\n-\t\t\t\taction => 'upload',\n-\t\t\t\tfilename => $complete_file_name,\n-\t\t\t\tcomment => $summary,\n-\t\t\t\tfile => [undef,\n-\t\t\t\t\t $complete_file_name,\n-\t\t\t\t\t Content => $content],\n-\t\t\t\tignorewarnings => 1,\n-\t\t\t}, {\n-\t\t\t\tskip_encoding => 1\n-\t\t\t} ) || die $mediawiki->{error}->{code} . ':'\n-\t\t\t\t . $mediawiki->{error}->{details} . \"\\n\";\n-\t\t\tmy $last_file_page = $mediawiki->get_page({title => $path});\n-\t\t\t$newrevid = $last_file_page->{revid};\n-\t\t\tprint {*STDERR} \"Pushed file: ${new_sha1} - ${complete_file_name}.\\n\";\n-\t\t} else {\n-\t\t\tprint {*STDERR} \"Empty file ${complete_file_name} not pushed.\\n\";\n-\t\t}\n-\t}\n-\treturn $newrevid;\n-}\n-\n-sub mw_push_file {\n-\tmy $diff_info = shift;\n-\t# $diff_info contains a string in this format:\n-\t# 100644 100644 <sha1_of_blob_before_commit> <sha1_of_blob_now> <status>\n-\tmy @diff_info_split = split(/[ \\t]/, $diff_info);\n-\n-\t# Filename, including .mw extension\n-\tmy $complete_file_name = shift;\n-\t# Commit message\n-\tmy $summary = shift;\n-\t# MediaWiki revision number. Keep the previous one by default,\n-\t# in case there's no edit to perform.\n-\tmy $oldrevid = shift;\n-\tmy $newrevid;\n-\n-\tif ($summary eq EMPTY_MESSAGE) {\n-\t\t$summary = EMPTY;\n-\t}\n-\n-\tmy $new_sha1 = $diff_info_split[3];\n-\tmy $old_sha1 = $diff_info_split[2];\n-\tmy $page_created = ($old_sha1 eq NULL_SHA1);\n-\tmy $page_deleted = ($new_sha1 eq NULL_SHA1);\n-\t$complete_file_name = clean_filename($complete_file_name);\n-\n-\tmy ($title, $extension) = $complete_file_name =~ /^(.*)\\.([^\\.]*)$/;\n-\tif (!defined($extension)) {\n-\t\t$extension = EMPTY;\n-\t}\n-\tif ($extension eq 'mw') {\n-\t\tmy $ns = get_mw_namespace_id_for_page($complete_file_name);\n-\t\tif ($ns && $ns == get_mw_namespace_id('File') && (!$export_media)) {\n-\t\t\tprint {*STDERR} \"Ignoring media file related page: ${complete_file_name}\\n\";\n-\t\t\treturn ($oldrevid, 'ok');\n-\t\t}\n-\t\tmy $file_content;\n-\t\tif ($page_deleted) {\n-\t\t\t# Deleting a page usually requires\n-\t\t\t# special privileges. A common\n-\t\t\t# convention is to replace the page\n-\t\t\t# with this content instead:\n-\t\t\t$file_content = DELETED_CONTENT;\n-\t\t} else {\n-\t\t\t$file_content = run_git_quoted([\"cat-file\", \"blob\", $new_sha1]);\n-\t\t}\n-\n-\t\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\n-\t\tmy $result = $mediawiki->edit( {\n-\t\t\taction => 'edit',\n-\t\t\tsummary => $summary,\n-\t\t\ttitle => $title,\n-\t\t\tbasetimestamp => $basetimestamps{$oldrevid},\n-\t\t\ttext => mediawiki_clean($file_content, $page_created),\n-\t\t\t\t  }, {\n-\t\t\t\t\t  skip_encoding => 1 # Helps with names with accentuated characters\n-\t\t\t\t  });\n-\t\tif (!$result) {\n-\t\t\tif ($mediawiki->{error}->{code} == 3) {\n-\t\t\t\t# edit conflicts, considered as non-fast-forward\n-\t\t\t\tprint {*STDERR} 'Warning: Error ' .\n-\t\t\t\t    $mediawiki->{error}->{code} .\n-\t\t\t\t    ' from mediawiki: ' . $mediawiki->{error}->{details} .\n-\t\t\t\t    \".\\n\";\n-\t\t\t\treturn ($oldrevid, 'non-fast-forward');\n-\t\t\t} else {\n-\t\t\t\t# Other errors. Shouldn't happen => just die()\n-\t\t\t\tdie 'Fatal: Error ' .\n-\t\t\t\t    $mediawiki->{error}->{code} .\n-\t\t\t\t    ' from mediawiki: ' . $mediawiki->{error}->{details} . \"\\n\";\n-\t\t\t}\n-\t\t}\n-\t\t$newrevid = $result->{edit}->{newrevid};\n-\t\tprint {*STDERR} \"Pushed file: ${new_sha1} - ${title}\\n\";\n-\t} elsif ($export_media) {\n-\t\t$newrevid = mw_upload_file($complete_file_name, $new_sha1,\n-\t\t\t\t\t   $extension, $page_deleted,\n-\t\t\t\t\t   $summary);\n-\t} else {\n-\t\tprint {*STDERR} \"Ignoring media file ${title}\\n\";\n-\t}\n-\t$newrevid = ($newrevid or $oldrevid);\n-\treturn ($newrevid, 'ok');\n-}\n-\n-sub mw_push {\n-\t# multiple push statements can follow each other\n-\tmy @refsspecs = (shift, get_more_refs('push'));\n-\tmy $pushed;\n-\tfor my $refspec (@refsspecs) {\n-\t\tmy ($force, $local, $remote) = $refspec =~ /^(\\+)?([^:]*):([^:]*)$/\n-\t\t    or die(\"Invalid refspec for push. Expected <src>:<dst> or +<src>:<dst>\\n\");\n-\t\tif ($force) {\n-\t\t\tprint {*STDERR} \"Warning: forced push not allowed on a MediaWiki.\\n\";\n-\t\t}\n-\t\tif ($local eq EMPTY) {\n-\t\t\tprint {*STDERR} \"Cannot delete remote branch on a MediaWiki\\n\";\n-\t\t\tprint {*STDOUT} \"error ${remote} cannot delete\\n\";\n-\t\t\tnext;\n-\t\t}\n-\t\tif ($remote ne 'refs/heads/master') {\n-\t\t\tprint {*STDERR} \"Only push to the branch 'master' is supported on a MediaWiki\\n\";\n-\t\t\tprint {*STDOUT} \"error ${remote} only master allowed\\n\";\n-\t\t\tnext;\n-\t\t}\n-\t\tif (mw_push_revision($local, $remote)) {\n-\t\t\t$pushed = 1;\n-\t\t}\n-\t}\n-\n-\t# Notify Git that the push is done\n-\tprint {*STDOUT} \"\\n\";\n-\n-\tif ($pushed && $dumb_push) {\n-\t\tprint {*STDERR} \"Just pushed some revisions to MediaWiki.\\n\";\n-\t\tprint {*STDERR} \"The pushed revisions now have to be re-imported, and your current branch\\n\";\n-\t\tprint {*STDERR} \"needs to be updated with these re-imported commits. You can do this with\\n\";\n-\t\tprint {*STDERR} \"\\n\";\n-\t\tprint {*STDERR} \"  git pull --rebase\\n\";\n-\t\tprint {*STDERR} \"\\n\";\n-\t}\n-\treturn;\n-}\n-\n-sub mw_push_revision {\n-\tmy $local = shift;\n-\tmy $remote = shift; # actually, this has to be \"refs/heads/master\" at this point.\n-\tmy $last_local_revid = get_last_local_revision();\n-\tprint {*STDERR} \".\\n\"; # Finish sentence started by get_last_local_revision()\n-\tmy $last_remote_revid = get_last_remote_revision();\n-\tmy $mw_revision = $last_remote_revid;\n-\n-\t# Get sha1 of commit pointed by local HEAD\n-\tmy $HEAD_sha1 = run_git_quoted_nostderr([\"rev-parse\", $local]);\n-\tchomp($HEAD_sha1);\n-\t# Get sha1 of commit pointed by remotes/$remotename/master\n-\tmy $remoteorigin_sha1 = run_git_quoted_nostderr([\"rev-parse\", \"refs/remotes/${remotename}/master\"]);\n-\tchomp($remoteorigin_sha1);\n-\n-\tif ($last_local_revid > 0 &&\n-\t    $last_local_revid < $last_remote_revid) {\n-\t\treturn error_non_fast_forward($remote);\n-\t}\n-\n-\tif ($HEAD_sha1 eq $remoteorigin_sha1) {\n-\t\t# nothing to push\n-\t\treturn 0;\n-\t}\n-\n-\t# Get every commit in between HEAD and refs/remotes/origin/master,\n-\t# including HEAD and refs/remotes/origin/master\n-\tmy @commit_pairs = ();\n-\tif ($last_local_revid > 0) {\n-\t\tmy $parsed_sha1 = $remoteorigin_sha1;\n-\t\t# Find a path from last MediaWiki commit to pushed commit\n-\t\tprint {*STDERR} \"Computing path from local to remote ...\\n\";\n-\t\tmy @local_ancestry = split(/\\n/, run_git_quoted([\"rev-list\", \"--boundary\", \"--parents\", $local, \"^${parsed_sha1}\"]));\n-\t\tmy %local_ancestry;\n-\t\tforeach my $line (@local_ancestry) {\n-\t\t\tif (my ($child, $parents) = $line =~ /^-?([a-f0-9]+) ([a-f0-9 ]+)/) {\n-\t\t\t\tforeach my $parent (split(/ /, $parents)) {\n-\t\t\t\t\t$local_ancestry{$parent} = $child;\n-\t\t\t\t}\n-\t\t\t} elsif (!$line =~ /^([a-f0-9]+)/) {\n-\t\t\t\tdie \"Unexpected output from git rev-list: ${line}\\n\";\n-\t\t\t}\n-\t\t}\n-\t\twhile ($parsed_sha1 ne $HEAD_sha1) {\n-\t\t\tmy $child = $local_ancestry{$parsed_sha1};\n-\t\t\tif (!$child) {\n-\t\t\t\tprint {*STDERR} \"Cannot find a path in history from remote commit to last commit\\n\";\n-\t\t\t\treturn error_non_fast_forward($remote);\n-\t\t\t}\n-\t\t\tpush(@commit_pairs, [$parsed_sha1, $child]);\n-\t\t\t$parsed_sha1 = $child;\n-\t\t}\n-\t} else {\n-\t\t# No remote mediawiki revision. Export the whole\n-\t\t# history (linearized with --first-parent)\n-\t\tprint {*STDERR} \"Warning: no common ancestor, pushing complete history\\n\";\n-\t\tmy $history = run_git_quoted([\"rev-list\", \"--first-parent\", \"--children\", $local]);\n-\t\tmy @history = split(/\\n/, $history);\n-\t\t@history = @history[1..$#history];\n-\t\tforeach my $line (reverse @history) {\n-\t\t\tmy @commit_info_split = split(/[ \\n]/, $line);\n-\t\t\tpush(@commit_pairs, \\@commit_info_split);\n-\t\t}\n-\t}\n-\n-\tforeach my $commit_info_split (@commit_pairs) {\n-\t\tmy $sha1_child = @{$commit_info_split}[0];\n-\t\tmy $sha1_commit = @{$commit_info_split}[1];\n-\t\tmy $diff_infos = run_git_quoted([\"diff-tree\", \"-r\", \"--raw\", \"-z\", $sha1_child, $sha1_commit]);\n-\t\t# TODO: we could detect rename, and encode them with a #redirect on the wiki.\n-\t\t# TODO: for now, it's just a delete+add\n-\t\tmy @diff_info_list = split(/\\0/, $diff_infos);\n-\t\t# Keep the subject line of the commit message as mediawiki comment for the revision\n-\t\tmy $commit_msg = run_git_quoted([\"log\", \"--no-walk\", '--format=\"%s\"', $sha1_commit]);\n-\t\tchomp($commit_msg);\n-\t\t# Push every blob\n-\t\twhile (@diff_info_list) {\n-\t\t\tmy $status;\n-\t\t\t# git diff-tree -z gives an output like\n-\t\t\t# <metadata>\\0<filename1>\\0\n-\t\t\t# <metadata>\\0<filename2>\\0\n-\t\t\t# and we've split on \\0.\n-\t\t\tmy $info = shift(@diff_info_list);\n-\t\t\tmy $file = shift(@diff_info_list);\n-\t\t\t($mw_revision, $status) = mw_push_file($info, $file, $commit_msg, $mw_revision);\n-\t\t\tif ($status eq 'non-fast-forward') {\n-\t\t\t\t# we may already have sent part of the\n-\t\t\t\t# commit to MediaWiki, but it's too\n-\t\t\t\t# late to cancel it. Stop the push in\n-\t\t\t\t# the middle, but still give an\n-\t\t\t\t# accurate error message.\n-\t\t\t\treturn error_non_fast_forward($remote);\n-\t\t\t}\n-\t\t\tif ($status ne 'ok') {\n-\t\t\t\tdie(\"Unknown error from mw_push_file()\\n\");\n-\t\t\t}\n-\t\t}\n-\t\tif (!$dumb_push) {\n-\t\t\trun_git_quoted([\"notes\", \"--ref=${remotename}/mediawiki\",\n-\t\t\t\t\t\"add\", \"-f\", \"-m\",\n-\t\t\t\t\t\"mediawiki_revision: ${mw_revision}\",\n-\t\t\t\t\t$sha1_commit]);\n-\t\t}\n-\t}\n-\n-\tprint {*STDOUT} \"ok ${remote}\\n\";\n-\treturn 1;\n-}\n-\n-sub get_allowed_file_extensions {\n-\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\n-\tmy $query = {\n-\t\taction => 'query',\n-\t\tmeta => 'siteinfo',\n-\t\tsiprop => 'fileextensions'\n-\t\t};\n-\tmy $result = $mediawiki->api($query);\n-\tmy @file_extensions = map { $_->{ext}} @{$result->{query}->{fileextensions}};\n-\tmy %hashFile = map { $_ => 1 } @file_extensions;\n-\n-\treturn %hashFile;\n-}\n-\n-# In memory cache for MediaWiki namespace ids.\n-my %namespace_id;\n-\n-# Namespaces whose id is cached in the configuration file\n-# (to avoid duplicates)\n-my %cached_mw_namespace_id;\n-\n-# Return MediaWiki id for a canonical namespace name.\n-# Ex.: \"File\", \"Project\".\n-sub get_mw_namespace_id {\n-\t$mediawiki = connect_maybe($mediawiki, $remotename, $url);\n-\tmy $name = shift;\n-\n-\tif (!exists $namespace_id{$name}) {\n-\t\t# Look at configuration file, if the record for that namespace is\n-\t\t# already cached. Namespaces are stored in form:\n-\t\t# \"Name_of_namespace:Id_namespace\", ex.: \"File:6\".\n-\t\tmy @temp = split(/\\n/,\n-\t\t\t\t run_git_quoted([\"config\", \"--get-all\", \"remote.${remotename}.namespaceCache\"]));\n-\t\tchomp(@temp);\n-\t\tforeach my $ns (@temp) {\n-\t\t\tmy ($n, $id) = split(/:/, $ns);\n-\t\t\tif ($id eq 'notANameSpace') {\n-\t\t\t\t$namespace_id{$n} = {is_namespace => 0};\n-\t\t\t} else {\n-\t\t\t\t$namespace_id{$n} = {is_namespace => 1, id => $id};\n-\t\t\t}\n-\t\t\t$cached_mw_namespace_id{$n} = 1;\n-\t\t}\n-\t}\n-\n-\tif (!exists $namespace_id{$name}) {\n-\t\tprint {*STDERR} \"Namespace ${name} not found in cache, querying the wiki ...\\n\";\n-\t\t# NS not found => get namespace id from MW and store it in\n-\t        # configuration file.\n-\t        my $query = {\n-\t                action => 'query',\n-\t                meta => 'siteinfo',\n-\t                siprop => 'namespaces'\n-\t        };\n-\t        my $result = $mediawiki->api($query);\n-\n-\t        while (my ($id, $ns) = each(%{$result->{query}->{namespaces}})) {\n-\t                if (defined($ns->{id}) && defined($ns->{canonical})) {\n-\t\t\t\t$namespace_id{$ns->{canonical}} = {is_namespace => 1, id => $ns->{id}};\n-\t\t\t\tif ($ns->{'*'}) {\n-\t\t\t\t\t# alias (e.g. french Fichier: as alias for canonical File:)\n-\t\t\t\t\t$namespace_id{$ns->{'*'}} = {is_namespace => 1, id => $ns->{id}};\n-\t\t\t\t}\n-\t\t\t}\n-\t        }\n-\t}\n-\n-\tmy $ns = $namespace_id{$name};\n-\tmy $id;\n-\n-\tif (!defined $ns) {\n-\t\tmy @namespaces = map { s/ /_/g; $_; } sort keys %namespace_id;\n-\t\tprint {*STDERR} \"No such namespace ${name} on MediaWiki, known namespaces: @namespaces\\n\";\n-\t\t$ns = {is_namespace => 0};\n-\t\t$namespace_id{$name} = $ns;\n-\t}\n-\n-\tif ($ns->{is_namespace}) {\n-\t\t$id = $ns->{id};\n-\t}\n-\n-\t# Store \"notANameSpace\" as special value for inexisting namespaces\n-\tmy $store_id = ($id || 'notANameSpace');\n-\n-\t# Store explicitly requested namespaces on disk\n-\tif (!exists $cached_mw_namespace_id{$name}) {\n-\t\trun_git_quoted([\"config\", \"--add\", \"remote.${remotename}.namespaceCache\", \"${name}:${store_id}\"]);\n-\t\t$cached_mw_namespace_id{$name} = 1;\n-\t}\n-\treturn $id;\n-}\n-\n-sub get_mw_namespace_id_for_page {\n-\tmy $namespace = shift;\n-\tif ($namespace =~ /^([^:]*):/) {\n-\t\treturn get_mw_namespace_id($namespace);\n-\t} else {\n-\t\treturn;\n-\t}\n-}\ndiff --git a/contrib/mw-to-git/git-remote-mediawiki.txt b/contrib/mw-to-git/git-remote-mediawiki.txt\ndeleted file mode 100644\nindex 5da825f61e2..00000000000\n--- a/contrib/mw-to-git/git-remote-mediawiki.txt\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-Git-Mediawiki is a project which aims the creation of a gate\n-between git and mediawiki, allowing git users to push and pull\n-objects from mediawiki just as one would do with a classic git\n-repository thanks to remote-helpers.\n-\n-For more information, visit the wiki at\n-https://github.com/Git-Mediawiki/Git-Mediawiki\ndiff --git a/contrib/mw-to-git/t/.gitignore b/contrib/mw-to-git/t/.gitignore\ndeleted file mode 100644\nindex 2b8dc30c6db..00000000000\n--- a/contrib/mw-to-git/t/.gitignore\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-WEB/\n-mediawiki/\n-trash directory.t*/\n-test-results/\ndiff --git a/contrib/mw-to-git/t/Makefile b/contrib/mw-to-git/t/Makefile\ndeleted file mode 100644\nindex 6c9f377caac..00000000000\n--- a/contrib/mw-to-git/t/Makefile\n+++ /dev/null\n@@ -1,32 +0,0 @@\n-#\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-#\n-## Test git-remote-mediawiki\n-\n-# The default target of this Makefile is...\n-all:: test\n-\n--include ../../../config.mak.autogen\n--include ../../../config.mak\n-\n-T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)\n-\n-.PHONY: help test clean all\n-\n-help:\n-\t@echo 'Run \"$(MAKE) test\" to launch test scripts'\n-\t@echo 'Run \"$(MAKE) clean\" to remove trash folders'\n-\n-test:\n-\t@for t in $(T); do \\\n-\t\techo \"$$t\"; \\\n-\t\t\"./$$t\" || exit 1; \\\n-\tdone\n-\n-clean:\n-\t$(RM) -r 'trash directory'.*\ndiff --git a/contrib/mw-to-git/t/README b/contrib/mw-to-git/t/README\ndeleted file mode 100644\nindex 72c4889db7a..00000000000\n--- a/contrib/mw-to-git/t/README\n+++ /dev/null\n@@ -1,124 +0,0 @@\n-Tests for Mediawiki-to-Git\n-==========================\n-\n-Introduction\n-------------\n-This manual describes how to install the git-remote-mediawiki test\n-environment on a machine with git installed on it.\n-\n-Prerequisite\n-------------\n-\n-In order to run this test environment correctly, you will need to\n-install the following packages (Debian/Ubuntu names, may need to be\n-adapted for another distribution):\n-\n-* lighttpd\n-* php\n-* php-cgi\n-* php-cli\n-* php-curl\n-* php-sqlite\n-\n-Principles and Technical Choices\n---------------------------------\n-\n-The test environment makes it easy to install and manipulate one or\n-several MediaWiki instances. To allow developers to run the testsuite\n-easily, the environment does not require root privilege (except to\n-install the required packages if needed). It starts a webserver\n-instance on the user's account (using lighttpd greatly helps for\n-that), and does not need a separate database daemon (thanks to the use\n-of sqlite).\n-\n-Run the test environment\n-------------------------\n-\n-Install a new wiki\n-~~~~~~~~~~~~~~~~~~\n-\n-Once you have all the prerequisite, you need to install a MediaWiki\n-instance on your machine. If you already have one, it is still\n-strongly recommended to install one with the script provided. Here's\n-how to work it:\n-\n-a. change directory to contrib/mw-to-git/t/\n-b. if needed, edit test.config to choose your installation parameters\n-c. run `./install-wiki.sh install`\n-d. check on your favourite web browser if your wiki is correctly\n-   installed.\n-\n-Remove an existing wiki\n-~~~~~~~~~~~~~~~~~~~~~~~\n-\n-Edit the file test.config to fit the wiki you want to delete, and then\n-execute the command `./install-wiki.sh delete` from the\n-contrib/mw-to-git/t directory.\n-\n-Run the existing tests\n-~~~~~~~~~~~~~~~~~~~~~~\n-\n-The provided tests are currently in the `contrib/mw-to-git/t` directory.\n-The files are all the t936[0-9]-*.sh shell scripts.\n-\n-a. Run all tests:\n-To do so, run \"make test\" from the contrib/mw-to-git/ directory.\n-\n-b. Run a specific test:\n-To run a given test <test_name>, run ./<test_name> from the\n-contrib/mw-to-git/t directory.\n-\n-How to create new tests\n------------------------\n-\n-Available functions\n-~~~~~~~~~~~~~~~~~~~\n-\n-The test environment of git-remote-mediawiki provides some functions\n-useful to test its behaviour. for more details about the functions'\n-parameters, please refer to the `test-gitmw-lib.sh` and\n-`test-gitmw.pl` files.\n-\n-** `test_check_wiki_precond`:\n-Check if the tests must be skipped or not. Please use this function\n-at the beginning of each new test file.\n-\n-** `wiki_getpage`:\n-Fetch a given page from the wiki and puts its content in the\n-directory in parameter.\n-\n-** `wiki_delete_page`:\n-Delete a given page from the wiki.\n-\n-** `wiki_edit_page`:\n-Create or modify a given page in the wiki. You can specify several\n-parameters like a summary for the page edition, or add the page to a\n-given category.\n-See test-gitmw.pl for more details.\n-\n-** `wiki_getallpage`:\n-Fetch all pages from the wiki into a given directory. The directory\n-is created if it does not exists.\n-\n-** `test_diff_directories`:\n-Compare the content of two directories. The content must be the same.\n-Use this function to compare the content of a git directory and a wiki\n-one created by wiki_getallpage.\n-\n-** `test_contains_N_files`:\n-Check if the given directory contains a given number of file.\n-\n-** `wiki_page_exists`:\n-Tests if a given page exists on the wiki.\n-\n-** `wiki_reset`:\n-Reset the wiki, i.e. flush the database. Use this function at the\n-beginning of each new test, except if the test re-uses the same wiki\n-(and history) as the previous test.\n-\n-How to write a new test\n-~~~~~~~~~~~~~~~~~~~~~~~\n-\n-Please, follow the standards given by git. See git/t/README.\n-New file should be named as t936[0-9]-*.sh.\n-Be sure to reset your wiki regularly with the function `wiki_reset`.\ndiff --git a/contrib/mw-to-git/t/install-wiki.sh b/contrib/mw-to-git/t/install-wiki.sh\ndeleted file mode 100755\nindex c215213c4bf..00000000000\n--- a/contrib/mw-to-git/t/install-wiki.sh\n+++ /dev/null\n@@ -1,55 +0,0 @@\n-#!/bin/sh\n-\n-# This script installs or deletes a MediaWiki on your computer.\n-# It requires a web server with PHP and SQLite running. In addition, if you\n-# do not have MediaWiki sources on your computer, the option 'install'\n-# downloads them for you.\n-# Please set the CONFIGURATION VARIABLES in ./test-gitmw-lib.sh\n-\n-WIKI_TEST_DIR=$(cd \"$(dirname \"$0\")\" && pwd)\n-\n-if test -z \"$WIKI_TEST_DIR\"\n-then\n-\tWIKI_TEST_DIR=.\n-fi\n-\n-. \"$WIKI_TEST_DIR\"/test-gitmw-lib.sh\n-usage () {\n-\techo \"usage: \"\n-\techo \"\t./install-wiki.sh <install | delete | --help>\"\n-\techo \"\t\tinstall | -i :\tInstall a wiki on your computer.\"\n-\techo \"\t\tdelete | -d : Delete the wiki and all its pages and \"\n-\techo \"\t\t\tcontent.\"\n-\techo \"\t\tstart  | -s : Start the previously configured lighttpd daemon\"\n-\techo \"\t\tstop        : Stop lighttpd daemon.\"\n-}\n-\n-\n-# Argument: install, delete, --help | -h\n-case \"$1\" in\n-\t\"install\" | \"-i\")\n-\t\twiki_install\n-\t\texit 0\n-\t\t;;\n-\t\"delete\" | \"-d\")\n-\t\twiki_delete\n-\t\texit 0\n-\t\t;;\n-\t\"start\" | \"-s\")\n-\t\tstart_lighttpd\n-\t\texit\n-\t\t;;\n-\t\"stop\")\n-\t\tstop_lighttpd\n-\t\texit\n-\t\t;;\n-\t\"--help\" | \"-h\")\n-\t\tusage\n-\t\texit 0\n-\t\t;;\n-\t*)\n-\t\techo \"Invalid argument: $1\"\n-\t\tusage\n-\t\texit 1\n-\t\t;;\n-esac\ndiff --git a/contrib/mw-to-git/t/push-pull-tests.sh b/contrib/mw-to-git/t/push-pull-tests.sh\ndeleted file mode 100644\nindex 9da2dc5ff03..00000000000\n--- a/contrib/mw-to-git/t/push-pull-tests.sh\n+++ /dev/null\n@@ -1,144 +0,0 @@\n-test_push_pull () {\n-\n-\ttest_expect_success 'Git pull works after adding a new wiki page' '\n-\t\twiki_reset &&\n-\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_1 &&\n-\t\twiki_editpage Foo \"page created after the git clone\" false &&\n-\n-\t\t(\n-\t\t\tcd mw_dir_1 &&\n-\t\t\tgit pull\n-\t\t) &&\n-\n-\t\twiki_getallpage ref_page_1 &&\n-\t\ttest_diff_directories mw_dir_1 ref_page_1\n-\t'\n-\n-\ttest_expect_success 'Git pull works after editing a wiki page' '\n-\t\twiki_reset &&\n-\n-\t\twiki_editpage Foo \"page created before the git clone\" false &&\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_2 &&\n-\t\twiki_editpage Foo \"new line added on the wiki\" true &&\n-\n-\t\t(\n-\t\t\tcd mw_dir_2 &&\n-\t\t\tgit pull\n-\t\t) &&\n-\n-\t\twiki_getallpage ref_page_2 &&\n-\t\ttest_diff_directories mw_dir_2 ref_page_2\n-\t'\n-\n-\ttest_expect_success 'git pull works on conflict handled by auto-merge' '\n-\t\twiki_reset &&\n-\n-\t\twiki_editpage Foo \"1 init\n-3\n-5\n-\t\" false &&\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_3 &&\n-\n-\t\twiki_editpage Foo \"1 init\n-2 content added on wiki after clone\n-3\n-5\n-\t\" false &&\n-\n-\t\t(\n-\t\t\tcd mw_dir_3 &&\n-\t\techo \"1 init\n-3\n-4 content added on git after clone\n-5\n-\" >Foo.mw &&\n-\t\t\tgit commit -am \"conflicting change on foo\" &&\n-\t\t\tgit pull &&\n-\t\t\tgit push\n-\t\t)\n-\t'\n-\n-\ttest_expect_success 'Git push works after adding a file .mw' '\n-\t\twiki_reset &&\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_4 &&\n-\t\twiki_getallpage ref_page_4 &&\n-\t\t(\n-\t\t\tcd mw_dir_4 &&\n-\t\t\ttest_path_is_missing Foo.mw &&\n-\t\t\ttouch Foo.mw &&\n-\t\t\techo \"hello world\" >>Foo.mw &&\n-\t\t\tgit add Foo.mw &&\n-\t\t\tgit commit -m \"Foo\" &&\n-\t\t\tgit push\n-\t\t) &&\n-\t\twiki_getallpage ref_page_4 &&\n-\t\ttest_diff_directories mw_dir_4 ref_page_4\n-\t'\n-\n-\ttest_expect_success 'Git push works after editing a file .mw' '\n-\t\twiki_reset &&\n-\t\twiki_editpage \"Foo\" \"page created before the git clone\" false &&\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_5 &&\n-\n-\t\t(\n-\t\t\tcd mw_dir_5 &&\n-\t\t\techo \"new line added in the file Foo.mw\" >>Foo.mw &&\n-\t\t\tgit commit -am \"edit file Foo.mw\" &&\n-\t\t\tgit push\n-\t\t) &&\n-\n-\t\twiki_getallpage ref_page_5 &&\n-\t\ttest_diff_directories mw_dir_5 ref_page_5\n-\t'\n-\n-\ttest_expect_failure 'Git push works after deleting a file' '\n-\t\twiki_reset &&\n-\t\twiki_editpage Foo \"wiki page added before git clone\" false &&\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_6 &&\n-\n-\t\t(\n-\t\t\tcd mw_dir_6 &&\n-\t\t\tgit rm Foo.mw &&\n-\t\t\tgit commit -am \"page Foo.mw deleted\" &&\n-\t\t\tgit push\n-\t\t) &&\n-\n-\t\ttest_must_fail wiki_page_exist Foo\n-\t'\n-\n-\ttest_expect_success 'Merge conflict expected and solving it' '\n-\t\twiki_reset &&\n-\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_7 &&\n-\t\twiki_editpage Foo \"1 conflict\n-3 wiki\n-4\" false &&\n-\n-\t\t(\n-\t\t\tcd mw_dir_7 &&\n-\t\techo \"1 conflict\n-2 git\n-4\" >Foo.mw &&\n-\t\t\tgit add Foo.mw &&\n-\t\t\tgit commit -m \"conflict created\" &&\n-\t\t\ttest_must_fail git pull &&\n-\t\t\t\"$PERL_PATH\" -pi -e \"s/[<=>].*//g\" Foo.mw &&\n-\t\t\tgit commit -am \"merge conflict solved\" &&\n-\t\t\tgit push\n-\t\t)\n-\t'\n-\n-\ttest_expect_failure 'git pull works after deleting a wiki page' '\n-\t\twiki_reset &&\n-\t\twiki_editpage Foo \"wiki page added before the git clone\" false &&\n-\t\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_8 &&\n-\n-\t\twiki_delete_page Foo &&\n-\t\t(\n-\t\t\tcd mw_dir_8 &&\n-\t\t\tgit pull &&\n-\t\t\ttest_path_is_missing Foo.mw\n-\t\t)\n-\t'\n-}\ndiff --git a/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh b/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh\ndeleted file mode 100755\nindex f08890d9e7a..00000000000\n--- a/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh\n+++ /dev/null\n@@ -1,257 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-#\n-# License: GPL v2 or later\n-\n-\n-test_description='Test the Git Mediawiki remote helper: git clone'\n-\n-. ./test-gitmw-lib.sh\n-. $TEST_DIRECTORY/test-lib.sh\n-\n-\n-test_check_precond\n-\n-\n-test_expect_success 'Git clone creates the expected git log with one file' '\n-\twiki_reset &&\n-\twiki_editpage foo \"this is not important\" false -c cat -s \"this must be the same\" &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_1 &&\n-\t(\n-\t\tcd mw_dir_1 &&\n-\t\tgit log --format=%s HEAD^..HEAD >log.tmp\n-\t) &&\n-\techo \"this must be the same\" >msg.tmp &&\n-\ttest_cmp msg.tmp mw_dir_1/log.tmp\n-'\n-\n-\n-test_expect_success 'Git clone creates the expected git log with multiple files' '\n-\twiki_reset &&\n-\twiki_editpage daddy \"this is not important\" false -s=\"this must be the same\" &&\n-\twiki_editpage daddy \"neither is this\" true -s=\"this must also be the same\" &&\n-\twiki_editpage daddy \"neither is this\" true -s=\"same same same\" &&\n-\twiki_editpage dj \"dont care\" false -s=\"identical\" &&\n-\twiki_editpage dj \"dont care either\" true -s=\"identical too\" &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_2 &&\n-\t(\n-\t\tcd mw_dir_2 &&\n-\t\tgit log --format=%s Daddy.mw  >logDaddy.tmp &&\n-\t\tgit log --format=%s Dj.mw >logDj.tmp\n-\t) &&\n-\techo \"same same same\" >msgDaddy.tmp &&\n-\techo \"this must also be the same\" >>msgDaddy.tmp &&\n-\techo \"this must be the same\" >>msgDaddy.tmp &&\n-\techo \"identical too\" >msgDj.tmp &&\n-\techo \"identical\" >>msgDj.tmp &&\n-\ttest_cmp msgDaddy.tmp mw_dir_2/logDaddy.tmp &&\n-\ttest_cmp msgDj.tmp mw_dir_2/logDj.tmp\n-'\n-\n-\n-test_expect_success 'Git clone creates only Main_Page.mw with an empty wiki' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_3 &&\n-\ttest_contains_N_files mw_dir_3 1 &&\n-\ttest_path_is_file mw_dir_3/Main_Page.mw\n-'\n-\n-test_expect_success 'Git clone does not fetch a deleted page' '\n-\twiki_reset &&\n-\twiki_editpage foo \"this page must be deleted before the clone\" false &&\n-\twiki_delete_page foo &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_4 &&\n-\ttest_contains_N_files mw_dir_4 1 &&\n-\ttest_path_is_file mw_dir_4/Main_Page.mw &&\n-\ttest_path_is_missing mw_dir_4/Foo.mw\n-'\n-\n-test_expect_success 'Git clone works with page added' '\n-\twiki_reset &&\n-\twiki_editpage foo \" I will be cloned\" false &&\n-\twiki_editpage bar \"I will be cloned\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_5 &&\n-\twiki_getallpage ref_page_5 &&\n-\ttest_diff_directories mw_dir_5 ref_page_5 &&\n-\twiki_delete_page foo &&\n-\twiki_delete_page bar\n-'\n-\n-test_expect_success 'Git clone works with an edited page ' '\n-\twiki_reset &&\n-\twiki_editpage foo \"this page will be edited\" \\\n-\t\tfalse -s \"first edition of page foo\" &&\n-\twiki_editpage foo \"this page has been edited and must be on the clone \" true &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_6 &&\n-\ttest_path_is_file mw_dir_6/Foo.mw &&\n-\ttest_path_is_file mw_dir_6/Main_Page.mw &&\n-\twiki_getallpage mw_dir_6/page_ref_6 &&\n-\ttest_diff_directories mw_dir_6 mw_dir_6/page_ref_6 &&\n-\t(\n-\t\tcd mw_dir_6 &&\n-\t\tgit log --format=%s HEAD^ Foo.mw > ../Foo.log\n-\t) &&\n-\techo \"first edition of page foo\" > FooExpect.log &&\n-\tdiff FooExpect.log Foo.log\n-'\n-\n-\n-test_expect_success 'Git clone works with several pages and some deleted ' '\n-\twiki_reset &&\n-\twiki_editpage foo \"this page will not be deleted\" false &&\n-\twiki_editpage bar \"I must not be erased\" false &&\n-\twiki_editpage namnam \"I will not be there at the end\" false &&\n-\twiki_editpage nyancat \"nyan nyan nyan delete me\" false &&\n-\twiki_delete_page namnam &&\n-\twiki_delete_page nyancat &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_7 &&\n-\ttest_path_is_file mw_dir_7/Foo.mw &&\n-\ttest_path_is_file mw_dir_7/Bar.mw &&\n-\ttest_path_is_missing mw_dir_7/Namnam.mw &&\n-\ttest_path_is_missing mw_dir_7/Nyancat.mw &&\n-\twiki_getallpage mw_dir_7/page_ref_7 &&\n-\ttest_diff_directories mw_dir_7 mw_dir_7/page_ref_7\n-'\n-\n-\n-test_expect_success 'Git clone works with one specific page cloned ' '\n-\twiki_reset &&\n-\twiki_editpage foo \"I will not be cloned\" false &&\n-\twiki_editpage bar \"Do not clone me\" false &&\n-\twiki_editpage namnam \"I will be cloned :)\" false  -s=\"this log must stay\" &&\n-\twiki_editpage nyancat \"nyan nyan nyan you cant clone me\" false &&\n-\tgit clone -c remote.origin.pages=namnam \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_8 &&\n-\ttest_contains_N_files mw_dir_8 1 &&\n-\ttest_path_is_file mw_dir_8/Namnam.mw &&\n-\ttest_path_is_missing mw_dir_8/Main_Page.mw &&\n-\t(\n-\t\tcd mw_dir_8 &&\n-\t\techo \"this log must stay\" >msg.tmp &&\n-\t\tgit log --format=%s >log.tmp &&\n-\t\ttest_cmp msg.tmp log.tmp\n-\t) &&\n-\twiki_check_content mw_dir_8/Namnam.mw Namnam\n-'\n-\n-test_expect_success 'Git clone works with multiple specific page cloned ' '\n-\twiki_reset &&\n-\twiki_editpage foo \"I will be there\" false &&\n-\twiki_editpage bar \"I will not disappear\" false &&\n-\twiki_editpage namnam \"I be erased\" false &&\n-\twiki_editpage nyancat \"nyan nyan nyan you will not erase me\" false &&\n-\twiki_delete_page namnam &&\n-\tgit clone -c remote.origin.pages=\"foo bar nyancat namnam\" \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_9 &&\n-\ttest_contains_N_files mw_dir_9 3 &&\n-\ttest_path_is_missing mw_dir_9/Namnam.mw &&\n-\ttest_path_is_file mw_dir_9/Foo.mw &&\n-\ttest_path_is_file mw_dir_9/Nyancat.mw &&\n-\ttest_path_is_file mw_dir_9/Bar.mw &&\n-\twiki_check_content mw_dir_9/Foo.mw Foo &&\n-\twiki_check_content mw_dir_9/Bar.mw Bar &&\n-\twiki_check_content mw_dir_9/Nyancat.mw Nyancat\n-'\n-\n-test_expect_success 'Mediawiki-clone of several specific pages on wiki' '\n-\twiki_reset &&\n-\twiki_editpage foo \"foo 1\" false &&\n-\twiki_editpage bar \"bar 1\" false &&\n-\twiki_editpage dummy \"dummy 1\" false &&\n-\twiki_editpage cloned_1 \"cloned_1 1\" false &&\n-\twiki_editpage cloned_2 \"cloned_2 2\" false &&\n-\twiki_editpage cloned_3 \"cloned_3 3\" false &&\n-\tmkdir -p ref_page_10 &&\n-\twiki_getpage cloned_1 ref_page_10 &&\n-\twiki_getpage cloned_2 ref_page_10 &&\n-\twiki_getpage cloned_3 ref_page_10 &&\n-\tgit clone -c remote.origin.pages=\"cloned_1 cloned_2 cloned_3\" \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_10 &&\n-\ttest_diff_directories mw_dir_10 ref_page_10\n-'\n-\n-test_expect_success 'Git clone works with the shallow option' '\n-\twiki_reset &&\n-\twiki_editpage foo \"1st revision, should be cloned\" false &&\n-\twiki_editpage bar \"1st revision, should be cloned\" false &&\n-\twiki_editpage nyan \"1st revision, should not be cloned\" false &&\n-\twiki_editpage nyan \"2nd revision, should be cloned\" false &&\n-\tgit -c remote.origin.shallow=true clone \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_11 &&\n-\ttest_contains_N_files mw_dir_11 4 &&\n-\ttest_path_is_file mw_dir_11/Nyan.mw &&\n-\ttest_path_is_file mw_dir_11/Foo.mw &&\n-\ttest_path_is_file mw_dir_11/Bar.mw &&\n-\ttest_path_is_file mw_dir_11/Main_Page.mw &&\n-\t(\n-\t\tcd mw_dir_11 &&\n-\t\ttest $(git log --oneline Nyan.mw | wc -l) -eq 1 &&\n-\t\ttest $(git log --oneline Foo.mw | wc -l) -eq 1 &&\n-\t\ttest $(git log --oneline Bar.mw | wc -l) -eq 1 &&\n-\t\ttest $(git log --oneline Main_Page.mw | wc -l ) -eq 1\n-\t) &&\n-\twiki_check_content mw_dir_11/Nyan.mw Nyan &&\n-\twiki_check_content mw_dir_11/Foo.mw Foo &&\n-\twiki_check_content mw_dir_11/Bar.mw Bar &&\n-\twiki_check_content mw_dir_11/Main_Page.mw Main_Page\n-'\n-\n-test_expect_success 'Git clone works with the shallow option with a delete page' '\n-\twiki_reset &&\n-\twiki_editpage foo \"1st revision, will be deleted\" false &&\n-\twiki_editpage bar \"1st revision, should be cloned\" false &&\n-\twiki_editpage nyan \"1st revision, should not be cloned\" false &&\n-\twiki_editpage nyan \"2nd revision, should be cloned\" false &&\n-\twiki_delete_page foo &&\n-\tgit -c remote.origin.shallow=true clone \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_12 &&\n-\ttest_contains_N_files mw_dir_12 3 &&\n-\ttest_path_is_file mw_dir_12/Nyan.mw &&\n-\ttest_path_is_missing mw_dir_12/Foo.mw &&\n-\ttest_path_is_file mw_dir_12/Bar.mw &&\n-\ttest_path_is_file mw_dir_12/Main_Page.mw &&\n-\t(\n-\t\tcd mw_dir_12 &&\n-\t\ttest $(git log --oneline Nyan.mw | wc -l) -eq 1 &&\n-\t\ttest $(git log --oneline Bar.mw | wc -l) -eq 1 &&\n-\t\ttest $(git log --oneline Main_Page.mw | wc -l ) -eq 1\n-\t) &&\n-\twiki_check_content mw_dir_12/Nyan.mw Nyan &&\n-\twiki_check_content mw_dir_12/Bar.mw Bar &&\n-\twiki_check_content mw_dir_12/Main_Page.mw Main_Page\n-'\n-\n-test_expect_success 'Test of fetching a category' '\n-\twiki_reset &&\n-\twiki_editpage Foo \"I will be cloned\" false -c=Category &&\n-\twiki_editpage Bar \"Meet me on the repository\" false -c=Category &&\n-\twiki_editpage Dummy \"I will not come\" false &&\n-\twiki_editpage BarWrong \"I will stay online only\" false -c=NotCategory &&\n-\tgit clone -c remote.origin.categories=\"Category\" \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_13 &&\n-\twiki_getallpage ref_page_13 Category &&\n-\ttest_diff_directories mw_dir_13 ref_page_13\n-'\n-\n-test_expect_success 'Test of resistance to modification of category on wiki for clone' '\n-\twiki_reset &&\n-\twiki_editpage Tobedeleted \"this page will be deleted\" false -c=Catone &&\n-\twiki_editpage Tobeedited \"this page will be modified\" false -c=Catone &&\n-\twiki_editpage Normalone \"this page wont be modified and will be on git\" false -c=Catone &&\n-\twiki_editpage Notconsidered \"this page will not appear on local\" false &&\n-\twiki_editpage Othercategory \"this page will not appear on local\" false -c=Cattwo &&\n-\twiki_editpage Tobeedited \"this page have been modified\" true -c=Catone &&\n-\twiki_delete_page Tobedeleted &&\n-\tgit clone -c remote.origin.categories=\"Catone\" \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_14 &&\n-\twiki_getallpage ref_page_14 Catone &&\n-\ttest_diff_directories mw_dir_14 ref_page_14\n-'\n-\n-test_done\ndiff --git a/contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh b/contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh\ndeleted file mode 100755\nindex 9ea201459b5..00000000000\n--- a/contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh\n+++ /dev/null\n@@ -1,24 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-#\n-# License: GPL v2 or later\n-\n-# tests for git-remote-mediawiki\n-\n-test_description='Test the Git Mediawiki remote helper: git push and git pull simple test cases'\n-\n-. ./test-gitmw-lib.sh\n-. ./push-pull-tests.sh\n-. $TEST_DIRECTORY/test-lib.sh\n-\n-test_check_precond\n-\n-test_push_pull\n-\n-test_done\ndiff --git a/contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh b/contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh\ndeleted file mode 100755\nindex 526d92850ff..00000000000\n--- a/contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh\n+++ /dev/null\n@@ -1,347 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-#\n-# License: GPL v2 or later\n-\n-# tests for git-remote-mediawiki\n-\n-test_description='Test git-mediawiki with special characters in filenames'\n-\n-. ./test-gitmw-lib.sh\n-. $TEST_DIRECTORY/test-lib.sh\n-\n-\n-test_check_precond\n-\n-\n-test_expect_success 'Git clone works for a wiki with accents in the page names' '\n-\twiki_reset &&\n-\twiki_editpage féé \"This page must be délétéd before clone\" false &&\n-\twiki_editpage kèè \"This page must be deleted before clone\" false &&\n-\twiki_editpage hàà \"This page must be deleted before clone\" false &&\n-\twiki_editpage kîî \"This page must be deleted before clone\" false &&\n-\twiki_editpage foo \"This page must be deleted before clone\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_1 &&\n-\twiki_getallpage ref_page_1 &&\n-\ttest_diff_directories mw_dir_1 ref_page_1\n-'\n-\n-\n-test_expect_success 'Git pull works with a wiki with accents in the pages names' '\n-\twiki_reset &&\n-\twiki_editpage kîî \"this page must be cloned\" false &&\n-\twiki_editpage foo \"this page must be cloned\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_2 &&\n-\twiki_editpage éàîôû \"This page must be pulled\" false &&\n-\t(\n-\t\tcd mw_dir_2 &&\n-\t\tgit pull\n-\t) &&\n-\twiki_getallpage ref_page_2 &&\n-\ttest_diff_directories mw_dir_2 ref_page_2\n-'\n-\n-\n-test_expect_success 'Cloning a chosen page works with accents' '\n-\twiki_reset &&\n-\twiki_editpage kîî \"this page must be cloned\" false &&\n-\tgit clone -c remote.origin.pages=kîî \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_3 &&\n-\twiki_check_content mw_dir_3/Kîî.mw Kîî &&\n-\ttest_path_is_file mw_dir_3/Kîî.mw &&\n-\trm -rf mw_dir_3\n-'\n-\n-\n-test_expect_success 'The shallow option works with accents' '\n-\twiki_reset &&\n-\twiki_editpage néoà \"1st revision, should not be cloned\" false &&\n-\twiki_editpage néoà \"2nd revision, should be cloned\" false &&\n-\tgit -c remote.origin.shallow=true clone \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_4 &&\n-\ttest_contains_N_files mw_dir_4 2 &&\n-\ttest_path_is_file mw_dir_4/Néoà.mw &&\n-\ttest_path_is_file mw_dir_4/Main_Page.mw &&\n-\t(\n-\t\tcd mw_dir_4 &&\n-\t\ttest $(git log --oneline Néoà.mw | wc -l) -eq 1 &&\n-\t\ttest $(git log --oneline Main_Page.mw | wc -l ) -eq 1\n-\t) &&\n-\twiki_check_content mw_dir_4/Néoà.mw Néoà &&\n-\twiki_check_content mw_dir_4/Main_Page.mw Main_Page\n-'\n-\n-\n-test_expect_success 'Cloning works when page name first letter has an accent' '\n-\twiki_reset &&\n-\twiki_editpage îî \"this page must be cloned\" false &&\n-\tgit clone -c remote.origin.pages=îî \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_5 &&\n-\ttest_path_is_file mw_dir_5/Îî.mw &&\n-\twiki_check_content mw_dir_5/Îî.mw Îî\n-'\n-\n-\n-test_expect_success 'Git push works with a wiki with accents' '\n-\twiki_reset &&\n-\twiki_editpage féé \"lots of accents : éèàÖ\" false &&\n-\twiki_editpage foo \"this page must be cloned\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_6 &&\n-\t(\n-\t\tcd mw_dir_6 &&\n-\t\techo \"A wild Pîkächû appears on the wiki\" >Pîkächû.mw &&\n-\t\tgit add Pîkächû.mw &&\n-\t\tgit commit -m \"A new page appears\" &&\n-\t\tgit push\n-\t) &&\n-\twiki_getallpage ref_page_6 &&\n-\ttest_diff_directories mw_dir_6 ref_page_6\n-'\n-\n-test_expect_success 'Git clone works with accentsand spaces' '\n-\twiki_reset &&\n-\twiki_editpage \"é à î\" \"this page must be délété before the clone\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_7 &&\n-\twiki_getallpage ref_page_7 &&\n-\ttest_diff_directories mw_dir_7 ref_page_7\n-'\n-\n-test_expect_success 'character $ in page name (mw -> git)' '\n-\twiki_reset &&\n-\twiki_editpage file_\\$_foo \"expect to be called file_$_foo\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_8 &&\n-\ttest_path_is_file mw_dir_8/File_\\$_foo.mw &&\n-\twiki_getallpage ref_page_8 &&\n-\ttest_diff_directories mw_dir_8 ref_page_8\n-'\n-\n-\n-\n-test_expect_success 'character $ in file name (git -> mw) ' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_9 &&\n-\t(\n-\t\tcd mw_dir_9 &&\n-\t\techo \"this file is called File_\\$_foo.mw\" >File_\\$_foo.mw &&\n-\t\tgit add . &&\n-\t\tgit commit -am \"file File_\\$_foo.mw\" &&\n-\t\tgit pull &&\n-\t\tgit push\n-\t) &&\n-\twiki_getallpage ref_page_9 &&\n-\ttest_diff_directories mw_dir_9 ref_page_9\n-'\n-\n-\n-test_expect_failure 'capital at the beginning of file names' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_10 &&\n-\t(\n-\t\tcd mw_dir_10 &&\n-\t\techo \"my new file foo\" >foo.mw &&\n-\t\techo \"my new file Foo... Finger crossed\" >Foo.mw &&\n-\t\tgit add . &&\n-\t\tgit commit -am \"file foo.mw\" &&\n-\t\tgit pull &&\n-\t\tgit push\n-\t) &&\n-\twiki_getallpage ref_page_10 &&\n-\ttest_diff_directories mw_dir_10 ref_page_10\n-'\n-\n-\n-test_expect_failure 'special character at the beginning of file name from mw to git' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_11 &&\n-\twiki_editpage {char_1 \"expect to be renamed {char_1\" false &&\n-\twiki_editpage [char_2 \"expect to be renamed [char_2\" false &&\n-\t(\n-\t\tcd mw_dir_11 &&\n-\t\tgit pull\n-\t) &&\n-\ttest_path_is_file mw_dir_11/{char_1 &&\n-\ttest_path_is_file mw_dir_11/[char_2\n-'\n-\n-test_expect_success 'Pull page with title containing \":\" other than namespace separator' '\n-\twiki_editpage Foo:Bar content false &&\n-\t(\n-\t\tcd mw_dir_11 &&\n-\t\tgit pull\n-\t) &&\n-\ttest_path_is_file mw_dir_11/Foo:Bar.mw\n-'\n-\n-test_expect_success 'Push page with title containing \":\" other than namespace separator' '\n-\t(\n-\t\tcd mw_dir_11 &&\n-\t\techo content >NotANameSpace:Page.mw &&\n-\t\tgit add NotANameSpace:Page.mw &&\n-\t\tgit commit -m \"add page with colon\" &&\n-\t\tgit push\n-\t) &&\n-\twiki_page_exist NotANameSpace:Page\n-'\n-\n-test_expect_success 'test of correct formatting for file name from mw to git' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_12 &&\n-\twiki_editpage char_%_7b_1 \"expect to be renamed char{_1\" false &&\n-\twiki_editpage char_%_5b_2 \"expect to be renamed char{_2\" false &&\n-\t(\n-\t\tcd mw_dir_12 &&\n-\t\tgit pull\n-\t) &&\n-\ttest_path_is_file mw_dir_12/Char\\{_1.mw &&\n-\ttest_path_is_file mw_dir_12/Char\\[_2.mw &&\n-\twiki_getallpage ref_page_12 &&\n-\tmv ref_page_12/Char_%_7b_1.mw ref_page_12/Char\\{_1.mw &&\n-\tmv ref_page_12/Char_%_5b_2.mw ref_page_12/Char\\[_2.mw &&\n-\ttest_diff_directories mw_dir_12 ref_page_12\n-'\n-\n-\n-test_expect_failure 'test of correct formatting for file name beginning with special character' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_13 &&\n-\t(\n-\t\tcd mw_dir_13 &&\n-\t\techo \"my new file {char_1\" >\\{char_1.mw &&\n-\t\techo \"my new file [char_2\" >\\[char_2.mw &&\n-\t\tgit add . &&\n-\t\tgit commit -am \"committing some exotic file name...\" &&\n-\t\tgit push &&\n-\t\tgit pull\n-\t) &&\n-\twiki_getallpage ref_page_13 &&\n-\ttest_path_is_file ref_page_13/{char_1.mw &&\n-\ttest_path_is_file ref_page_13/[char_2.mw &&\n-\ttest_diff_directories mw_dir_13 ref_page_13\n-'\n-\n-\n-test_expect_success 'test of correct formatting for file name from git to mw' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_14 &&\n-\t(\n-\t\tcd mw_dir_14 &&\n-\t\techo \"my new file char{_1\" >Char\\{_1.mw &&\n-\t\techo \"my new file char[_2\" >Char\\[_2.mw &&\n-\t\tgit add . &&\n-\t\tgit commit -m \"committing some exotic file name...\" &&\n-\t\tgit push\n-\t) &&\n-\twiki_getallpage ref_page_14 &&\n-\tmv mw_dir_14/Char\\{_1.mw mw_dir_14/Char_%_7b_1.mw &&\n-\tmv mw_dir_14/Char\\[_2.mw mw_dir_14/Char_%_5b_2.mw &&\n-\ttest_diff_directories mw_dir_14 ref_page_14\n-'\n-\n-\n-test_expect_success 'git clone with /' '\n-\twiki_reset &&\n-\twiki_editpage \\/fo\\/o \"this is not important\" false -c=Deleted &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_15 &&\n-\ttest_path_is_file mw_dir_15/%2Ffo%2Fo.mw &&\n-\twiki_check_content mw_dir_15/%2Ffo%2Fo.mw \\/fo\\/o\n-'\n-\n-\n-test_expect_success 'git push with /' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_16 &&\n-\techo \"I will be on the wiki\" >mw_dir_16/%2Ffo%2Fo.mw &&\n-\t(\n-\t\tcd mw_dir_16 &&\n-\t\tgit add %2Ffo%2Fo.mw &&\n-\t\tgit commit -m \" %2Ffo%2Fo added\" &&\n-\t\tgit push\n-\t) &&\n-\twiki_page_exist \\/fo\\/o &&\n-\twiki_check_content mw_dir_16/%2Ffo%2Fo.mw \\/fo\\/o\n-\n-'\n-\n-\n-test_expect_success 'git clone with \\' '\n-\twiki_reset &&\n-\twiki_editpage \\\\ko\\\\o \"this is not important\" false -c=Deleted &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_17 &&\n-\ttest_path_is_file mw_dir_17/\\\\ko\\\\o.mw &&\n-\twiki_check_content mw_dir_17/\\\\ko\\\\o.mw \\\\ko\\\\o\n-'\n-\n-\n-test_expect_success 'git push with \\' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_18 &&\n-\techo \"I will be on the wiki\" >mw_dir_18/\\\\ko\\\\o.mw &&\n-\t(\n-\t\tcd mw_dir_18 &&\n-\t\tgit add \\\\ko\\\\o.mw &&\n-\t\tgit commit -m \" \\\\ko\\\\o added\" &&\n-\t\tgit push\n-\t) &&\n-\twiki_page_exist \\\\ko\\\\o &&\n-\twiki_check_content mw_dir_18/\\\\ko\\\\o.mw \\\\ko\\\\o\n-\n-'\n-\n-test_expect_success 'git clone with \\ in format control' '\n-\twiki_reset &&\n-\twiki_editpage \\\\no\\\\o \"this is not important\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_19 &&\n-\ttest_path_is_file mw_dir_19/\\\\no\\\\o.mw &&\n-\twiki_check_content mw_dir_19/\\\\no\\\\o.mw \\\\no\\\\o\n-'\n-\n-\n-test_expect_success 'git push with \\ in format control' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_20 &&\n-\techo \"I will be on the wiki\" >mw_dir_20/\\\\fo\\\\o.mw &&\n-\t(\n-\t\tcd mw_dir_20 &&\n-\t\tgit add \\\\fo\\\\o.mw &&\n-\t\tgit commit -m \" \\\\fo\\\\o added\" &&\n-\t\tgit push\n-\t) &&\n-\twiki_page_exist \\\\fo\\\\o &&\n-\twiki_check_content mw_dir_20/\\\\fo\\\\o.mw \\\\fo\\\\o\n-\n-'\n-\n-\n-test_expect_success 'fast-import meta-characters in page name (mw -> git)' '\n-\twiki_reset &&\n-\twiki_editpage \\\"file\\\"_\\\\_foo \"expect to be called \\\"file\\\"_\\\\_foo\" false &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_21 &&\n-\ttest_path_is_file mw_dir_21/\\\"file\\\"_\\\\_foo.mw &&\n-\twiki_getallpage ref_page_21 &&\n-\ttest_diff_directories mw_dir_21 ref_page_21\n-'\n-\n-\n-test_expect_success 'fast-import meta-characters in page name (git -> mw) ' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir_22 &&\n-\t(\n-\t\tcd mw_dir_22 &&\n-\t\techo \"this file is called \\\"file\\\"_\\\\_foo.mw\" >\\\"file\\\"_\\\\_foo &&\n-\t\tgit add . &&\n-\t\tgit commit -am \"file \\\"file\\\"_\\\\_foo\" &&\n-\t\tgit pull &&\n-\t\tgit push\n-\t) &&\n-\twiki_getallpage ref_page_22 &&\n-\ttest_diff_directories mw_dir_22 ref_page_22\n-'\n-\n-\n-test_done\ndiff --git a/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh b/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh\ndeleted file mode 100755\nindex 7139995a405..00000000000\n--- a/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh\n+++ /dev/null\n@@ -1,218 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-#\n-# License: GPL v2 or later\n-\n-# tests for git-remote-mediawiki\n-\n-test_description='Test the Git Mediawiki remote helper: git push and git pull simple test cases'\n-\n-. ./test-gitmw-lib.sh\n-. $TEST_DIRECTORY/test-lib.sh\n-\n-\n-test_check_precond\n-\n-\n-test_git_reimport () {\n-\tgit -c remote.origin.dumbPush=true push &&\n-\tgit -c remote.origin.mediaImport=true pull --rebase\n-}\n-\n-# Don't bother with permissions, be administrator by default\n-test_expect_success 'setup config' '\n-\tgit config --global remote.origin.mwLogin \"$WIKI_ADMIN\" &&\n-\tgit config --global remote.origin.mwPassword \"$WIKI_PASSW\" &&\n-\ttest_might_fail git config --global --unset remote.origin.mediaImport\n-'\n-\n-test_expect_failure 'git push can upload media (File:) files' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\techo \"hello world\" >Foo.txt &&\n-\t\tgit add Foo.txt &&\n-\t\tgit commit -m \"add a text file\" &&\n-\t\tgit push &&\n-\t\t\"$PERL_PATH\" -e \"print STDOUT \\\"binary content: \\\".chr(255);\" >Foo.txt &&\n-\t\tgit add Foo.txt &&\n-\t\tgit commit -m \"add a text file with binary content\" &&\n-\t\tgit push\n-\t)\n-'\n-\n-test_expect_failure 'git clone works on previously created wiki with media files' '\n-\ttest_when_finished \"rm -rf mw_dir mw_dir_clone\" &&\n-\tgit clone -c remote.origin.mediaimport=true \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_clone &&\n-\ttest_cmp mw_dir_clone/Foo.txt mw_dir/Foo.txt &&\n-\t(cd mw_dir_clone && git checkout HEAD^) &&\n-\t(cd mw_dir && git checkout HEAD^) &&\n-\ttest_path_is_file mw_dir_clone/Foo.txt &&\n-\ttest_cmp mw_dir_clone/Foo.txt mw_dir/Foo.txt\n-'\n-\n-test_expect_success 'git push can upload media (File:) files containing valid UTF-8' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\t\"$PERL_PATH\" -e \"print STDOUT \\\"UTF-8 content: éèàéê€.\\\";\" >Bar.txt &&\n-\t\tgit add Bar.txt &&\n-\t\tgit commit -m \"add a text file with UTF-8 content\" &&\n-\t\tgit push\n-\t)\n-'\n-\n-test_expect_success 'git clone works on previously created wiki with media files containing valid UTF-8' '\n-\ttest_when_finished \"rm -rf mw_dir mw_dir_clone\" &&\n-\tgit clone -c remote.origin.mediaimport=true \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_clone &&\n-\ttest_cmp mw_dir_clone/Bar.txt mw_dir/Bar.txt\n-'\n-\n-test_expect_success 'git push & pull work with locally renamed media files' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\ttest_when_finished \"rm -fr mw_dir\" &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\techo \"A File\" >Foo.txt &&\n-\t\tgit add Foo.txt &&\n-\t\tgit commit -m \"add a file\" &&\n-\t\tgit mv Foo.txt Bar.txt &&\n-\t\tgit commit -m \"Rename a file\" &&\n-\t\ttest_git_reimport &&\n-\t\techo \"A File\" >expect &&\n-\t\ttest_cmp expect Bar.txt &&\n-\t\ttest_path_is_missing Foo.txt\n-\t)\n-'\n-\n-test_expect_success 'git push can propagate local page deletion' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\ttest_when_finished \"rm -fr mw_dir\" &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\ttest_path_is_missing Foo.mw &&\n-\t\techo \"hello world\" >Foo.mw &&\n-\t\tgit add Foo.mw &&\n-\t\tgit commit -m \"Add the page Foo\" &&\n-\t\tgit push &&\n-\t\trm -f Foo.mw &&\n-\t\tgit commit -am \"Delete the page Foo\" &&\n-\t\ttest_git_reimport &&\n-\t\ttest_path_is_missing Foo.mw\n-\t)\n-'\n-\n-test_expect_success 'git push can propagate local media file deletion' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\ttest_when_finished \"rm -fr mw_dir\" &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\techo \"hello world\" >Foo.txt &&\n-\t\tgit add Foo.txt &&\n-\t\tgit commit -m \"Add the text file Foo\" &&\n-\t\tgit rm Foo.txt &&\n-\t\tgit commit -m \"Delete the file Foo\" &&\n-\t\ttest_git_reimport &&\n-\t\ttest_path_is_missing Foo.txt\n-\t)\n-'\n-\n-# test failure: the file is correctly uploaded, and then deleted but\n-# as no page link to it, the import (which looks at page revisions)\n-# doesn't notice the file deletion on the wiki. We fetch the list of\n-# files from the wiki, but as the file is deleted, it doesn't appear.\n-test_expect_failure 'git pull correctly imports media file deletion when no page link to it' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\ttest_when_finished \"rm -fr mw_dir\" &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\techo \"hello world\" >Foo.txt &&\n-\t\tgit add Foo.txt &&\n-\t\tgit commit -m \"Add the text file Foo\" &&\n-\t\tgit push &&\n-\t\tgit rm Foo.txt &&\n-\t\tgit commit -m \"Delete the file Foo\" &&\n-\t\ttest_git_reimport &&\n-\t\ttest_path_is_missing Foo.txt\n-\t)\n-'\n-\n-test_expect_success 'git push properly warns about insufficient permissions' '\n-\twiki_reset &&\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir &&\n-\ttest_when_finished \"rm -fr mw_dir\" &&\n-\t(\n-\t\tcd mw_dir &&\n-\t\techo \"A File\" >foo.forbidden &&\n-\t\tgit add foo.forbidden &&\n-\t\tgit commit -m \"add a file\" &&\n-\t\tgit push 2>actual &&\n-\t\ttest_grep \"foo.forbidden is not a permitted file\" actual\n-\t)\n-'\n-\n-test_expect_success 'setup a repository with media files' '\n-\twiki_reset &&\n-\twiki_editpage testpage \"I am linking a file [[File:File.txt]]\" false &&\n-\techo \"File content\" >File.txt &&\n-\twiki_upload_file File.txt &&\n-\techo \"Another file content\" >AnotherFile.txt &&\n-\twiki_upload_file AnotherFile.txt\n-'\n-\n-test_expect_success 'git clone works with one specific page cloned and mediaimport=true' '\n-\tgit clone -c remote.origin.pages=testpage \\\n-\t\t  -c remote.origin.mediaimport=true \\\n-\t\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_15 &&\n-\ttest_when_finished \"rm -rf mw_dir_15\" &&\n-\ttest_contains_N_files mw_dir_15 3 &&\n-\ttest_path_is_file mw_dir_15/Testpage.mw &&\n-\ttest_path_is_file mw_dir_15/File:File.txt.mw &&\n-\ttest_path_is_file mw_dir_15/File.txt &&\n-\ttest_path_is_missing mw_dir_15/Main_Page.mw &&\n-\ttest_path_is_missing mw_dir_15/File:AnotherFile.txt.mw &&\n-\ttest_path_is_missing mw_dir_15/AnothetFile.txt &&\n-\twiki_check_content mw_dir_15/Testpage.mw Testpage &&\n-\ttest_cmp mw_dir_15/File.txt File.txt\n-'\n-\n-test_expect_success 'git clone works with one specific page cloned and mediaimport=false' '\n-\ttest_when_finished \"rm -rf mw_dir_16\" &&\n-\tgit clone -c remote.origin.pages=testpage \\\n-\t\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_16 &&\n-\ttest_contains_N_files mw_dir_16 1 &&\n-\ttest_path_is_file mw_dir_16/Testpage.mw &&\n-\ttest_path_is_missing mw_dir_16/File:File.txt.mw &&\n-\ttest_path_is_missing mw_dir_16/File.txt &&\n-\ttest_path_is_missing mw_dir_16/Main_Page.mw &&\n-\twiki_check_content mw_dir_16/Testpage.mw Testpage\n-'\n-\n-# should behave like mediaimport=false\n-test_expect_success 'git clone works with one specific page cloned and mediaimport unset' '\n-\ttest_when_finished \"rm -fr mw_dir_17\" &&\n-\tgit clone -c remote.origin.pages=testpage \\\n-\t\tmediawiki::'\"$WIKI_URL\"' mw_dir_17 &&\n-\ttest_contains_N_files mw_dir_17 1 &&\n-\ttest_path_is_file mw_dir_17/Testpage.mw &&\n-\ttest_path_is_missing mw_dir_17/File:File.txt.mw &&\n-\ttest_path_is_missing mw_dir_17/File.txt &&\n-\ttest_path_is_missing mw_dir_17/Main_Page.mw &&\n-\twiki_check_content mw_dir_17/Testpage.mw Testpage\n-'\n-\n-test_done\ndiff --git a/contrib/mw-to-git/t/t9364-pull-by-rev.sh b/contrib/mw-to-git/t/t9364-pull-by-rev.sh\ndeleted file mode 100755\nindex 5c22457a0b6..00000000000\n--- a/contrib/mw-to-git/t/t9364-pull-by-rev.sh\n+++ /dev/null\n@@ -1,17 +0,0 @@\n-#!/bin/sh\n-\n-test_description='Test the Git Mediawiki remote helper: git pull by revision'\n-\n-. ./test-gitmw-lib.sh\n-. ./push-pull-tests.sh\n-. $TEST_DIRECTORY/test-lib.sh\n-\n-test_check_precond\n-\n-test_expect_success 'configuration' '\n-\tgit config --global mediawiki.fetchStrategy by_rev\n-'\n-\n-test_push_pull\n-\n-test_done\ndiff --git a/contrib/mw-to-git/t/t9365-continuing-queries.sh b/contrib/mw-to-git/t/t9365-continuing-queries.sh\ndeleted file mode 100755\nindex d3e73126595..00000000000\n--- a/contrib/mw-to-git/t/t9365-continuing-queries.sh\n+++ /dev/null\n@@ -1,23 +0,0 @@\n-#!/bin/sh\n-\n-test_description='Test the Git Mediawiki remote helper: queries w/ more than 500 results'\n-\n-. ./test-gitmw-lib.sh\n-. $TEST_DIRECTORY/test-lib.sh\n-\n-test_check_precond\n-\n-test_expect_success 'creating page w/ >500 revisions' '\n-\twiki_reset &&\n-\tfor i in $(test_seq 501)\n-\tdo\n-\t\techo \"creating revision $i\" &&\n-\t\twiki_editpage foo \"revision $i<br/>\" true || return 1\n-\tdone\n-'\n-\n-test_expect_success 'cloning page w/ >500 revisions' '\n-\tgit clone mediawiki::'\"$WIKI_URL\"' mw_dir\n-'\n-\n-test_done\ndiff --git a/contrib/mw-to-git/t/test-gitmw-lib.sh b/contrib/mw-to-git/t/test-gitmw-lib.sh\ndeleted file mode 100755\nindex 64e46c16716..00000000000\n--- a/contrib/mw-to-git/t/test-gitmw-lib.sh\n+++ /dev/null\n@@ -1,432 +0,0 @@\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-# License: GPL v2 or later\n-\n-#\n-# CONFIGURATION VARIABLES\n-# You might want to change these ones\n-#\n-\n-. ./test.config\n-\n-WIKI_BASE_URL=http://$SERVER_ADDR:$PORT\n-WIKI_URL=$WIKI_BASE_URL/$WIKI_DIR_NAME\n-CURR_DIR=$(pwd)\n-TEST_OUTPUT_DIRECTORY=$(pwd)\n-TEST_DIRECTORY=\"$CURR_DIR\"/../../../t\n-\n-export TEST_OUTPUT_DIRECTORY TEST_DIRECTORY CURR_DIR\n-\n-if test \"$LIGHTTPD\" = \"false\" ; then\n-\tPORT=80\n-else\n-\tWIKI_DIR_INST=\"$CURR_DIR/$WEB_WWW\"\n-fi\n-\n-wiki_upload_file () {\n-\t\"$CURR_DIR\"/test-gitmw.pl upload_file \"$@\"\n-}\n-\n-wiki_getpage () {\n-\t\"$CURR_DIR\"/test-gitmw.pl get_page \"$@\"\n-}\n-\n-wiki_delete_page () {\n-\t\"$CURR_DIR\"/test-gitmw.pl delete_page \"$@\"\n-}\n-\n-wiki_editpage () {\n-\t\"$CURR_DIR\"/test-gitmw.pl edit_page \"$@\"\n-}\n-\n-die () {\n-\tdie_with_status 1 \"$@\"\n-}\n-\n-die_with_status () {\n-\tstatus=$1\n-\tshift\n-\techo >&2 \"$*\"\n-\texit \"$status\"\n-}\n-\n-\n-# Check the preconditions to run git-remote-mediawiki's tests\n-test_check_precond () {\n-\tif ! test_have_prereq PERL\n-\tthen\n-\t\tskip_all='skipping gateway git-mw tests, perl not available'\n-\t\ttest_done\n-\tfi\n-\n-\tGIT_EXEC_PATH=$(cd \"$(dirname \"$0\")\" && cd \"../..\" && pwd)\n-\tPATH=\"$GIT_EXEC_PATH\"'/bin-wrapper:'\"$PATH\"\n-\n-\tif ! test -d \"$WIKI_DIR_INST/$WIKI_DIR_NAME\"\n-\tthen\n-\t\tskip_all='skipping gateway git-mw tests, no mediawiki found'\n-\t\ttest_done\n-\tfi\n-}\n-\n-# test_diff_directories <dir_git> <dir_wiki>\n-#\n-# Compare the contents of directories <dir_git> and <dir_wiki> with diff\n-# and errors if they do not match. The program will\n-# not look into .git in the process.\n-# Warning: the first argument MUST be the directory containing the git data\n-test_diff_directories () {\n-\trm -rf \"$1_tmp\"\n-\tmkdir -p \"$1_tmp\"\n-\tcp \"$1\"/*.mw \"$1_tmp\"\n-\tdiff -r -b \"$1_tmp\" \"$2\"\n-}\n-\n-# $1=<dir>\n-# $2=<N>\n-#\n-# Check that <dir> contains exactly <N> files\n-test_contains_N_files () {\n-\tif test $(ls -- \"$1\" | wc -l) -ne \"$2\"; then\n-\t\techo \"directory $1 should contain $2 files\"\n-\t\techo \"it contains these files:\"\n-\t\tls \"$1\"\n-\t\tfalse\n-\tfi\n-}\n-\n-\n-# wiki_check_content <file_name> <page_name>\n-#\n-# Compares the contents of the file <file_name> and the wiki page\n-# <page_name> and exits with error 1 if they do not match.\n-wiki_check_content () {\n-\tmkdir -p wiki_tmp\n-\twiki_getpage \"$2\" wiki_tmp\n-\t# replacement of forbidden character in file name\n-\tpage_name=$(printf \"%s\\n\" \"$2\" | sed -e \"s/\\//%2F/g\")\n-\n-\tdiff -b \"$1\" wiki_tmp/\"$page_name\".mw\n-\tif test $? -ne 0\n-\tthen\n-\t\trm -rf wiki_tmp\n-\t\terror \"ERROR: file $2 not found on wiki\"\n-\tfi\n-\trm -rf wiki_tmp\n-}\n-\n-# wiki_page_exist <page_name>\n-#\n-# Check the existence of the page <page_name> on the wiki and exits\n-# with error if it is absent from it.\n-wiki_page_exist () {\n-\tmkdir -p wiki_tmp\n-\twiki_getpage \"$1\" wiki_tmp\n-\tpage_name=$(printf \"%s\\n\" \"$1\" | sed \"s/\\//%2F/g\")\n-\tif test -f wiki_tmp/\"$page_name\".mw ; then\n-\t\trm -rf wiki_tmp\n-\telse\n-\t\trm -rf wiki_tmp\n-\t\terror \"test failed: file $1 not found on wiki\"\n-\tfi\n-}\n-\n-# wiki_getallpagename\n-#\n-# Fetch the name of each page on the wiki.\n-wiki_getallpagename () {\n-\t\"$CURR_DIR\"/test-gitmw.pl getallpagename\n-}\n-\n-# wiki_getallpagecategory <category>\n-#\n-# Fetch the name of each page belonging to <category> on the wiki.\n-wiki_getallpagecategory () {\n-\t\"$CURR_DIR\"/test-gitmw.pl getallpagename \"$@\"\n-}\n-\n-# wiki_getallpage <dest_dir> [<category>]\n-#\n-# Fetch all the pages from the wiki and place them in the directory\n-# <dest_dir>.\n-# If <category> is define, then wiki_getallpage fetch the pages included\n-# in <category>.\n-wiki_getallpage () {\n-\tif test -z \"$2\";\n-\tthen\n-\t\twiki_getallpagename\n-\telse\n-\t\twiki_getallpagecategory \"$2\"\n-\tfi\n-\tmkdir -p \"$1\"\n-\twhile read -r line; do\n-\t\twiki_getpage \"$line\" $1;\n-\tdone < all.txt\n-}\n-\n-# ================= Install part =================\n-\n-error () {\n-\techo \"$@\" >&2\n-\texit 1\n-}\n-\n-# config_lighttpd\n-#\n-# Create the configuration files and the folders necessary to start lighttpd.\n-# Overwrite any existing file.\n-config_lighttpd () {\n-\tmkdir -p $WEB\n-\tmkdir -p $WEB_TMP\n-\tmkdir -p $WEB_WWW\n-\tcat > $WEB/lighttpd.conf <<EOF\n-\tserver.document-root = \"$CURR_DIR/$WEB_WWW\"\n-\tserver.port = $PORT\n-\tserver.pid-file = \"$CURR_DIR/$WEB_TMP/pid\"\n-\n-\tserver.modules = (\n-\t\"mod_rewrite\",\n-\t\"mod_redirect\",\n-\t\"mod_access\",\n-\t\"mod_accesslog\",\n-\t\"mod_fastcgi\"\n-\t)\n-\n-\tindex-file.names = (\"index.php\" , \"index.html\")\n-\n-\tmimetype.assign\t\t    = (\n-\t\".pdf\"\t\t=>\t\"application/pdf\",\n-\t\".sig\"\t\t=>\t\"application/pgp-signature\",\n-\t\".spl\"\t\t=>\t\"application/futuresplash\",\n-\t\".class\"\t=>\t\"application/octet-stream\",\n-\t\".ps\"\t\t=>\t\"application/postscript\",\n-\t\".torrent\"\t=>\t\"application/x-bittorrent\",\n-\t\".dvi\"\t\t=>\t\"application/x-dvi\",\n-\t\".gz\"\t\t=>\t\"application/x-gzip\",\n-\t\".pac\"\t\t=>\t\"application/x-ns-proxy-autoconfig\",\n-\t\".swf\"\t\t=>\t\"application/x-shockwave-flash\",\n-\t\".tar.gz\"\t=>\t\"application/x-tgz\",\n-\t\".tgz\"\t\t=>\t\"application/x-tgz\",\n-\t\".tar\"\t\t=>\t\"application/x-tar\",\n-\t\".zip\"\t\t=>\t\"application/zip\",\n-\t\".mp3\"\t\t=>\t\"audio/mpeg\",\n-\t\".m3u\"\t\t=>\t\"audio/x-mpegurl\",\n-\t\".wma\"\t\t=>\t\"audio/x-ms-wma\",\n-\t\".wax\"\t\t=>\t\"audio/x-ms-wax\",\n-\t\".ogg\"\t\t=>\t\"application/ogg\",\n-\t\".wav\"\t\t=>\t\"audio/x-wav\",\n-\t\".gif\"\t\t=>\t\"image/gif\",\n-\t\".jpg\"\t\t=>\t\"image/jpeg\",\n-\t\".jpeg\"\t\t=>\t\"image/jpeg\",\n-\t\".png\"\t\t=>\t\"image/png\",\n-\t\".xbm\"\t\t=>\t\"image/x-xbitmap\",\n-\t\".xpm\"\t\t=>\t\"image/x-xpixmap\",\n-\t\".xwd\"\t\t=>\t\"image/x-xwindowdump\",\n-\t\".css\"\t\t=>\t\"text/css\",\n-\t\".html\"\t\t=>\t\"text/html\",\n-\t\".htm\"\t\t=>\t\"text/html\",\n-\t\".js\"\t\t=>\t\"text/javascript\",\n-\t\".asc\"\t\t=>\t\"text/plain\",\n-\t\".c\"\t\t=>\t\"text/plain\",\n-\t\".cpp\"\t\t=>\t\"text/plain\",\n-\t\".log\"\t\t=>\t\"text/plain\",\n-\t\".conf\"\t\t=>\t\"text/plain\",\n-\t\".text\"\t\t=>\t\"text/plain\",\n-\t\".txt\"\t\t=>\t\"text/plain\",\n-\t\".dtd\"\t\t=>\t\"text/xml\",\n-\t\".xml\"\t\t=>\t\"text/xml\",\n-\t\".mpeg\"\t\t=>\t\"video/mpeg\",\n-\t\".mpg\"\t\t=>\t\"video/mpeg\",\n-\t\".mov\"\t\t=>\t\"video/quicktime\",\n-\t\".qt\"\t\t=>\t\"video/quicktime\",\n-\t\".avi\"\t\t=>\t\"video/x-msvideo\",\n-\t\".asf\"\t\t=>\t\"video/x-ms-asf\",\n-\t\".asx\"\t\t=>\t\"video/x-ms-asf\",\n-\t\".wmv\"\t\t=>\t\"video/x-ms-wmv\",\n-\t\".bz2\"\t\t=>\t\"application/x-bzip\",\n-\t\".tbz\"\t\t=>\t\"application/x-bzip-compressed-tar\",\n-\t\".tar.bz2\"\t=>\t\"application/x-bzip-compressed-tar\",\n-\t\"\"\t\t=>\t\"text/plain\"\n-\t)\n-\n-\tfastcgi.server = ( \".php\" =>\n-\t(\"localhost\" =>\n-\t( \"socket\" => \"$CURR_DIR/$WEB_TMP/php.socket\",\n-\t\"bin-path\" => \"$PHP_DIR/php-cgi -c $CURR_DIR/$WEB/php.ini\"\n-\n-\t)\n-\t)\n-\t)\n-EOF\n-\n-\tcat > $WEB/php.ini <<EOF\n-\tsession.save_path ='$CURR_DIR/$WEB_TMP'\n-EOF\n-}\n-\n-# start_lighttpd\n-#\n-# Start or restart daemon lighttpd. If restart, rewrite configuration files.\n-start_lighttpd () {\n-\tif test -f \"$WEB_TMP/pid\"; then\n-\t\techo \"Instance already running. Restarting...\"\n-\t\tstop_lighttpd\n-\tfi\n-\tconfig_lighttpd\n-\t\"$LIGHTTPD_DIR\"/lighttpd -f \"$WEB\"/lighttpd.conf\n-\n-\tif test $? -ne 0 ; then\n-\t\techo \"Could not execute http daemon lighttpd\"\n-\t\texit 1\n-\tfi\n-}\n-\n-# stop_lighttpd\n-#\n-# Kill daemon lighttpd and removes files and folders associated.\n-stop_lighttpd () {\n-\ttest -f \"$WEB_TMP/pid\" && kill $(cat \"$WEB_TMP/pid\")\n-}\n-\n-wiki_delete_db () {\n-\trm -rf \\\n-\t   \"$FILES_FOLDER_DB\"/* || error \"Couldn't delete $FILES_FOLDER_DB/\"\n-}\n-\n-wiki_delete_db_backup () {\n-\trm -rf \\\n-\t   \"$FILES_FOLDER_POST_INSTALL_DB\"/* || error \"Couldn't delete $FILES_FOLDER_POST_INSTALL_DB/\"\n-}\n-\n-# Install MediaWiki using its install.php script. If the database file\n-# already exists, it will be deleted.\n-install_mediawiki () {\n-\n-\tlocalsettings=\"$WIKI_DIR_INST/$WIKI_DIR_NAME/LocalSettings.php\"\n-\tif test -f \"$localsettings\"\n-\tthen\n-\t\terror \"We already installed the wiki, since $localsettings exists\" \\\n-\t\t      \"perhaps you wanted to run 'delete' first?\"\n-\tfi\n-\n-\twiki_delete_db\n-\twiki_delete_db_backup\n-\tmkdir \\\n-\t\t\"$FILES_FOLDER_DB/\" \\\n-\t\t\"$FILES_FOLDER_POST_INSTALL_DB/\"\n-\n-\tinstall_script=\"$WIKI_DIR_INST/$WIKI_DIR_NAME/maintenance/install.php\"\n-\techo \"Installing MediaWiki using $install_script. This may take some time ...\"\n-\n-\tphp \"$WIKI_DIR_INST/$WIKI_DIR_NAME/maintenance/install.php\" \\\n-\t    --server $WIKI_BASE_URL \\\n-\t    --scriptpath /wiki \\\n-\t    --lang en \\\n-\t    --dbtype sqlite \\\n-\t    --dbpath $PWD/$FILES_FOLDER_DB/ \\\n-\t    --pass \"$WIKI_PASSW\" \\\n-\t    Git-MediaWiki-Test \\\n-\t    \"$WIKI_ADMIN\" ||\n-\t\terror \"Couldn't run $install_script, see errors above. Try to run ./install-wiki.sh delete first.\"\n-\tcat <<-'EOF' >>$localsettings\n-# Custom settings added by test-gitmw-lib.sh\n-#\n-# Uploading text files is needed for\n-# t9363-mw-to-git-export-import.sh\n-$wgEnableUploads = true;\n-$wgFileExtensions[] = 'txt';\n-EOF\n-\n-\t# Copy the initially generated database file into our backup\n-\t# folder\n-\tcp -R \"$FILES_FOLDER_DB/\"* \"$FILES_FOLDER_POST_INSTALL_DB/\" ||\n-\t\terror \"Unable to copy $FILES_FOLDER_DB/* to $FILES_FOLDER_POST_INSTALL_DB/*\"\n-}\n-\n-# Install a wiki in your web server directory.\n-wiki_install () {\n-\tif test $LIGHTTPD = \"true\" ; then\n-\t\tstart_lighttpd\n-\tfi\n-\n-\t# In this part, we change directory to $TMP in order to download,\n-\t# unpack and copy the files of MediaWiki\n-\t(\n-\tmkdir -p \"$WIKI_DIR_INST/$WIKI_DIR_NAME\"\n-\tif ! test -d \"$WIKI_DIR_INST/$WIKI_DIR_NAME\"\n-\tthen\n-\t\terror \"Folder $WIKI_DIR_INST/$WIKI_DIR_NAME doesn't exist.\n-\t\tPlease create it and launch the script again.\"\n-\tfi\n-\n-\t# Fetch MediaWiki's archive if not already present in the\n-\t# download directory\n-\tmkdir -p \"$FILES_FOLDER_DOWNLOAD\"\n-\tMW_FILENAME=\"mediawiki-$MW_VERSION_MAJOR.$MW_VERSION_MINOR.tar.gz\"\n-\tcd \"$FILES_FOLDER_DOWNLOAD\"\n-\tif ! test -f $MW_FILENAME\n-\tthen\n-\t\techo \"Downloading $MW_VERSION_MAJOR.$MW_VERSION_MINOR sources ...\"\n-\t\twget \"http://download.wikimedia.org/mediawiki/$MW_VERSION_MAJOR/$MW_FILENAME\" ||\n-\t\t\terror \"Unable to download \"\\\n-\t\t\t\"http://download.wikimedia.org/mediawiki/$MW_VERSION_MAJOR/\"\\\n-\t\t\t\"$MW_FILENAME. \"\\\n-\t\t\t\"Please fix your connection and launch the script again.\"\n-\t\techo \"$MW_FILENAME downloaded in $(pwd)/;\" \\\n-\t\t     \"you can delete it later if you want.\"\n-\telse\n-\t\techo \"Reusing existing $MW_FILENAME downloaded in $(pwd)/\"\n-\tfi\n-\tarchive_abs_path=$(pwd)/$MW_FILENAME\n-\tcd \"$WIKI_DIR_INST/$WIKI_DIR_NAME/\" ||\n-\t\terror \"can't cd to $WIKI_DIR_INST/$WIKI_DIR_NAME/\"\n-\ttar xzf \"$archive_abs_path\" --strip-components=1 ||\n-\t\terror \"Unable to extract WikiMedia's files from $archive_abs_path to \"\\\n-\t\t\t\"$WIKI_DIR_INST/$WIKI_DIR_NAME\"\n-\t) || exit 1\n-\techo Extracted in \"$WIKI_DIR_INST/$WIKI_DIR_NAME\"\n-\n-\tinstall_mediawiki\n-\n-\techo \"Your wiki has been installed. You can check it at\n-\t\t$WIKI_URL\"\n-}\n-\n-# Reset the database of the wiki and the password of the admin\n-#\n-# Warning: This function must be called only in a subdirectory of t/ directory\n-wiki_reset () {\n-\t# Copy initial database of the wiki\n-\tif ! test -d \"../$FILES_FOLDER_DB\"\n-\tthen\n-\t\terror \"No wiki database at ../$FILES_FOLDER_DB, not installed yet?\"\n-\tfi\n-\tif ! test -d \"../$FILES_FOLDER_POST_INSTALL_DB\"\n-\tthen\n-\t\terror \"No wiki backup database at ../$FILES_FOLDER_POST_INSTALL_DB, failed installation?\"\n-\tfi\n-\twiki_delete_db\n-\tcp -R \"../$FILES_FOLDER_POST_INSTALL_DB/\"* \"../$FILES_FOLDER_DB/\" ||\n-\t\terror \"Can't copy ../$FILES_FOLDER_POST_INSTALL_DB/* to ../$FILES_FOLDER_DB/*\"\n-\techo \"File $FILES_FOLDER_DB/* has been reset\"\n-}\n-\n-# Delete the wiki created in the web server's directory and all its content\n-# saved in the database.\n-wiki_delete () {\n-\tif test $LIGHTTPD = \"true\"; then\n-\t\tstop_lighttpd\n-\t\trm -fr \"$WEB\"\n-\telse\n-\t\t# Delete the wiki's directory.\n-\t\trm -rf \"$WIKI_DIR_INST/$WIKI_DIR_NAME\" ||\n-\t\t\terror \"Wiki's directory $WIKI_DIR_INST/\" \\\n-\t\t\t\"$WIKI_DIR_NAME could not be deleted\"\n-\tfi\n-\twiki_delete_db\n-\twiki_delete_db_backup\n-}\ndiff --git a/contrib/mw-to-git/t/test-gitmw.pl b/contrib/mw-to-git/t/test-gitmw.pl\ndeleted file mode 100755\nindex c5d687f078a..00000000000\n--- a/contrib/mw-to-git/t/test-gitmw.pl\n+++ /dev/null\n@@ -1,223 +0,0 @@\n-#!/usr/bin/perl -w -s\n-# Copyright (C) 2012\n-#     Charles Roussel <charles.roussel@ensimag.imag.fr>\n-#     Simon Cathebras <simon.cathebras@ensimag.imag.fr>\n-#     Julien Khayat <julien.khayat@ensimag.imag.fr>\n-#     Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr>\n-#     Simon Perrat <simon.perrat@ensimag.imag.fr>\n-# License: GPL v2 or later\n-\n-# Usage:\n-#       ./test-gitmw.pl <command> [argument]*\n-# Execute in terminal using the name of the function to call as first\n-# parameter, and the function's arguments as following parameters\n-#\n-# Example:\n-#     ./test-gitmw.pl \"get_page\" foo .\n-# will call <wiki_getpage> with arguments <foo> and <.>\n-#\n-# Available functions are:\n-#     \"get_page\"\n-#     \"delete_page\"\n-#     \"edit_page\"\n-#     \"getallpagename\"\n-\n-use MediaWiki::API;\n-use Getopt::Long;\n-use DateTime::Format::ISO8601;\n-use constant SLASH_REPLACEMENT => \"%2F\";\n-\n-#Parsing of the config file\n-\n-my $configfile = \"$ENV{'CURR_DIR'}/test.config\";\n-my %config;\n-open my $CONFIG, \"<\",  $configfile or die \"can't open $configfile: $!\";\n-while (<$CONFIG>)\n-{\n-\tchomp;\n-\ts/#.*//;\n-\ts/^\\s+//;\n-\ts/\\s+$//;\n-\tnext unless length;\n-\tmy ($key, $value) = split (/\\s*=\\s*/,$_, 2);\n-\t$config{$key} = $value;\n-\tlast if ($key eq 'LIGHTTPD' and $value eq 'false');\n-\tlast if ($key eq 'PORT');\n-}\n-close $CONFIG or die \"can't close $configfile: $!\";\n-\n-my $wiki_address = \"http://$config{'SERVER_ADDR'}\".\":\".\"$config{'PORT'}\";\n-my $wiki_url = \"$wiki_address/$config{'WIKI_DIR_NAME'}/api.php\";\n-my $wiki_admin = \"$config{'WIKI_ADMIN'}\";\n-my $wiki_admin_pass = \"$config{'WIKI_PASSW'}\";\n-my $mw = MediaWiki::API->new;\n-$mw->{config}->{api_url} = $wiki_url;\n-\n-\n-# wiki_login <name> <password>\n-#\n-# Logs the user with <name> and <password> in the global variable\n-# of the mediawiki $mw\n-sub wiki_login {\n-\t$mw->login( { lgname => \"$_[0]\",lgpassword => \"$_[1]\" } )\n-\t|| die \"getpage: login failed\";\n-}\n-\n-# wiki_getpage <wiki_page> <dest_path>\n-#\n-# fetch a page <wiki_page> from the wiki referenced in the global variable\n-# $mw and copies its content in directory dest_path\n-sub wiki_getpage {\n-\tmy $pagename = $_[0];\n-\tmy $destdir = $_[1];\n-\n-\tmy $page = $mw->get_page( { title => $pagename } );\n-\tif (!defined($page)) {\n-\t\tdie \"getpage: wiki does not exist\";\n-\t}\n-\n-\tmy $content = $page->{'*'};\n-\tif (!defined($content)) {\n-\t\tdie \"getpage: page does not exist\";\n-\t}\n-\n-\t$pagename=$page->{'title'};\n-\t# Replace spaces by underscore in the page name\n-\t$pagename =~ s/ /_/g;\n-\t$pagename =~ s/\\//%2F/g;\n-\topen(my $file, \">:encoding(UTF-8)\", \"$destdir/$pagename.mw\");\n-\tprint $file \"$content\";\n-\tclose ($file);\n-\n-}\n-\n-# wiki_delete_page <page_name>\n-#\n-# delete the page with name <page_name> from the wiki referenced\n-# in the global variable $mw\n-sub wiki_delete_page {\n-\tmy $pagename = $_[0];\n-\n-\tmy $exist=$mw->get_page({title => $pagename});\n-\n-\tif (defined($exist->{'*'})){\n-\t\t$mw->edit({ action => 'delete',\n-\t\t\t\ttitle => $pagename})\n-\t\t|| die $mw->{error}->{code} . \": \" . $mw->{error}->{details};\n-\t} else {\n-\t\tdie \"no page with such name found: $pagename\\n\";\n-\t}\n-}\n-\n-# wiki_editpage <wiki_page> <wiki_content> <wiki_append> [-c=<category>] [-s=<summary>]\n-#\n-# Edit a page named <wiki_page> with content <wiki_content> on the wiki\n-# referenced with the global variable $mw\n-# If <wiki_append> == true : append <wiki_content> at the end of the actual\n-# content of the page <wiki_page>\n-# If <wik_page> doesn't exist, that page is created with the <wiki_content>\n-sub wiki_editpage {\n-\tmy $wiki_page = $_[0];\n-\tmy $wiki_content = $_[1];\n-\tmy $wiki_append = $_[2];\n-\tmy $summary = \"\";\n-\tmy ($summ, $cat) = ();\n-\tGetOptions('s=s' => \\$summ, 'c=s' => \\$cat);\n-\n-\tmy $append = 0;\n-\tif (defined($wiki_append) && $wiki_append eq 'true') {\n-\t\t$append=1;\n-\t}\n-\n-\tmy $previous_text =\"\";\n-\n-\tif ($append) {\n-\t\tmy $ref = $mw->get_page( { title => $wiki_page } );\n-\t\t$previous_text = $ref->{'*'};\n-\t}\n-\n-\tmy $text = $wiki_content;\n-\tif (defined($previous_text)) {\n-\t\t$text=\"$previous_text$text\";\n-\t}\n-\n-\t# Eventually, add this page to a category.\n-\tif (defined($cat)) {\n-\t\tmy $category_name=\"[[Category:$cat]]\";\n-\t\t$text=\"$text\\n $category_name\";\n-\t}\n-\tif(defined($summ)){\n-\t\t$summary=$summ;\n-\t}\n-\n-\t$mw->edit( { action => 'edit', title => $wiki_page, summary => $summary, text => \"$text\"} );\n-}\n-\n-# wiki_getallpagename [<category>]\n-#\n-# Fetch all pages of the wiki referenced by the global variable $mw\n-# and print the names of each one in the file all.txt with a new line\n-# (\"\\n\") between these.\n-# If the argument <category> is defined, then this function get only the pages\n-# belonging to <category>.\n-sub wiki_getallpagename {\n-\t# fetch the pages of the wiki\n-\tif (defined($_[0])) {\n-\t\tmy $mw_pages = $mw->list ( { action => 'query',\n-\t\t\t\tlist => 'categorymembers',\n-\t\t\t\tcmtitle => \"Category:$_[0]\",\n-\t\t\t\tcmnamespace => 0,\n-\t\t\t\tcmlimit => 500 },\n-\t\t)\n-\t\t|| die $mw->{error}->{code}.\": \".$mw->{error}->{details};\n-\t\topen(my $file, \">:encoding(UTF-8)\", \"all.txt\");\n-\t\tforeach my $page (@{$mw_pages}) {\n-\t\t\tprint $file \"$page->{title}\\n\";\n-\t\t}\n-\t\tclose ($file);\n-\n-\t} else {\n-\t\tmy $mw_pages = $mw->list({\n-\t\t\t\taction => 'query',\n-\t\t\t\tlist => 'allpages',\n-\t\t\t\taplimit => 500,\n-\t\t\t})\n-\t\t|| die $mw->{error}->{code}.\": \".$mw->{error}->{details};\n-\t\topen(my $file, \">:encoding(UTF-8)\", \"all.txt\");\n-\t\tforeach my $page (@{$mw_pages}) {\n-\t\t\tprint $file \"$page->{title}\\n\";\n-\t\t}\n-\t\tclose ($file);\n-\t}\n-}\n-\n-sub wiki_upload_file {\n-\tmy $file_name = $_[0];\n-\tmy $resultat = $mw->edit ( {\n-\t\taction => 'upload',\n-\t\tfilename => $file_name,\n-\t\tcomment => 'upload a file',\n-\t\tfile => [ $file_name ],\n-\t\tignorewarnings=>1,\n-\t}, {\n-\t\tskip_encoding => 1\n-\t} ) || die $mw->{error}->{code} . ' : ' . $mw->{error}->{details};\n-}\n-\n-\n-\n-# Main part of this script: parse the command line arguments\n-# and select which function to execute\n-my $fct_to_call = shift;\n-\n-wiki_login($wiki_admin, $wiki_admin_pass);\n-\n-my %functions_to_call = (\n-\tupload_file    => \\&wiki_upload_file,\n-\tget_page       => \\&wiki_getpage,\n-\tdelete_page    => \\&wiki_delete_page,\n-\tedit_page      => \\&wiki_editpage,\n-\tgetallpagename => \\&wiki_getallpagename,\n-);\n-die \"$0 ERROR: wrong argument\" unless exists $functions_to_call{$fct_to_call};\n-$functions_to_call{$fct_to_call}->(map { utf8::decode($_); $_ } @ARGV);\ndiff --git a/contrib/mw-to-git/t/test.config b/contrib/mw-to-git/t/test.config\ndeleted file mode 100644\nindex ed10b3e4a47..00000000000\n--- a/contrib/mw-to-git/t/test.config\n+++ /dev/null\n@@ -1,40 +0,0 @@\n-# Name of the web server's directory dedicated to the wiki is WIKI_DIR_NAME\n-WIKI_DIR_NAME=wiki\n-\n-# Login and password of the wiki's admin\n-WIKI_ADMIN=WikiAdmin\n-WIKI_PASSW=AdminPass1\n-\n-# Address of the web server\n-SERVER_ADDR=localhost\n-\n-# If LIGHTTPD is not set to true, the script will use the default\n-# web server running in WIKI_DIR_INST.\n-WIKI_DIR_INST=/var/www\n-\n-# If LIGHTTPD is set to true, the script will use Lighttpd to run\n-# the wiki.\n-LIGHTTPD=true\n-\n-# The variables below are useful only if LIGHTTPD is set to true.\n-PORT=1234\n-PHP_DIR=/usr/bin\n-LIGHTTPD_DIR=/usr/sbin\n-WEB=WEB\n-WEB_TMP=$WEB/tmp\n-WEB_WWW=$WEB/www\n-\n-# Where our configuration for the wiki is located\n-FILES_FOLDER=mediawiki\n-FILES_FOLDER_DOWNLOAD=$FILES_FOLDER/download\n-FILES_FOLDER_DB=$FILES_FOLDER/db\n-FILES_FOLDER_POST_INSTALL_DB=$FILES_FOLDER/post-install-db\n-\n-# The variables below are used by the script to install a wiki.\n-# You should not modify these unless you are modifying the script itself.\n-# tested versions: 1.19.X -> 1.21.1 -> 1.34.2\n-#\n-# See https://www.mediawiki.org/wiki/Download for what the latest\n-# version is.\n-MW_VERSION_MAJOR=1.34\n-MW_VERSION_MINOR=2\n\n-- \n2.49.0.1077.gc0e912fd4c.dirty\n\n"},{"id":"517643","messageId":"20250509-pks-contrib-spring-cleanup-v2-8-14e20d95ec68@pks.im","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","subject":"[PATCH v2 08/11] contrib: remove \"git-resurrect.sh\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-09T09:17:08Z","receivedAt":"2025-05-09T09:17:18Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"git-resurrect.sh\" script can be used to find traces of a branch tip\nin the reflog and resurrect that branch. Despite a couple of global\ncleanups, the script hasn't seen any activity since it was introduced in\ne1ff064e1bf (contrib git-resurrect: find traces of a branch name and\nresurrect it, 2009-02-04).\n\nFurthermore, the tool does not work with the \"reftable\" backend at all\nas it directly reads \".git/logs/HEAD\". As reflogs are stored as part of\nthe individual tables though that file wouldn't exist in a \"reftable\"-\nenabled repository.\n\nLast but not least, the tool doesn't even work unless it is explicitly\ninvoked via `git resurrect` as it sources \"git-sh-setup\". As none of our\nbuild systems know to install this script, users thus have to go out of\ntheir way to really make it work, which is highly unlikely.\n\nAnother source that indicates that this tool can be removed is a\nquestion for how to restore deleted branches on StackOverflow [1]. The\ntop-voted answer uses git-reflog(1) directly and has received more than\n3000 votes to date. While \"git-resurrect.sh\" is also mentioned, it only\ngot 16 upvotes, and comments mention the above caveat that users have to\ndo some manual setup to make it work.\n\nIt's thus rather clear that the tool doesn't have a lot or even any\nusers. Remove it.\n\n[1]: https://stackoverflow.com/questions/3640764/can-i-recover-a-branch-after-its-deletion-in-git\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/git-resurrect.sh | 181 -----------------------------------------------\n 1 file changed, 181 deletions(-)\n\ndiff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh\ndeleted file mode 100755\nindex d843df3afd0..00000000000\n--- a/contrib/git-resurrect.sh\n+++ /dev/null\n@@ -1,181 +0,0 @@\n-#!/bin/sh\n-\n-USAGE=\"[-a] [-r] [-m] [-t] [-n] [-b <newname>] <name>\"\n-LONG_USAGE=\"git-resurrect attempts to find traces of a branch tip\n-called <name>, and tries to resurrect it.  Currently, the reflog is\n-searched for checkout messages, and with -r also merge messages.  With\n--m and -t, the history of all refs is scanned for Merge <name> into\n-other/Merge <other> into <name> (respectively) commit subjects, which\n-is rather slow but allows you to resurrect other people's topic\n-branches.\"\n-\n-OPTIONS_KEEPDASHDASH=\n-OPTIONS_STUCKLONG=\n-OPTIONS_SPEC=\"\\\n-git resurrect $USAGE\n---\n-b,branch=            save branch as <newname> instead of <name>\n-a,all                same as -l -r -m -t\n-k,keep-going         full rev-list scan (instead of first match)\n-l,reflog             scan reflog for checkouts (enabled by default)\n-r,reflog-merges      scan for merges recorded in reflog\n-m,merges             scan for merges into other branches (slow)\n-t,merge-targets      scan for merges of other branches into <name>\n-n,dry-run            don't recreate the branch\"\n-\n-. git-sh-setup\n-\n-search_reflog () {\n-\tsed -ne 's~^\\([^ ]*\\) .*\tcheckout: moving from '\"$1\"' .*~\\1~p' \\\n-\t\t< \"$GIT_DIR\"/logs/HEAD\n-}\n-\n-search_reflog_merges () {\n-\tgit rev-parse $(\n-\t\tsed -ne 's~^[^ ]* \\([^ ]*\\) .*\tmerge '\"$1\"':.*~\\1^2~p' \\\n-\t\t\t< \"$GIT_DIR\"/logs/HEAD\n-\t)\n-}\n-\n-oid_pattern=$(git hash-object --stdin </dev/null | sed -e 's/./[0-9a-f]/g')\n-\n-search_merges () {\n-\tgit rev-list --all --grep=\"Merge branch '$1'\" \\\n-\t\t--pretty=tformat:\"%P %s\" |\n-\tsed -ne \"/^$oid_pattern \\($oid_pattern\\) Merge .*/ {s//\\1/p;$early_exit}\"\n-}\n-\n-search_merge_targets () {\n-\tgit rev-list --all --grep=\"Merge branch '[^']*' into $branch\\$\" \\\n-\t\t--pretty=tformat:\"%H %s\" --all |\n-\tsed -ne \"/^\\($oid_pattern\\) Merge .*/ {s//\\1/p;$early_exit} \"\n-}\n-\n-dry_run=\n-early_exit=q\n-scan_reflog=t\n-scan_reflog_merges=\n-scan_merges=\n-scan_merge_targets=\n-new_name=\n-\n-while test \"$#\" != 0; do\n-\tcase \"$1\" in\n-\t    -b|--branch)\n-\t\tshift\n-\t\tnew_name=\"$1\"\n-\t\t;;\n-\t    -n|--dry-run)\n-\t\tdry_run=t\n-\t\t;;\n-\t    --no-dry-run)\n-\t\tdry_run=\n-\t\t;;\n-\t    -k|--keep-going)\n-\t\tearly_exit=\n-\t\t;;\n-\t    --no-keep-going)\n-\t\tearly_exit=q\n-\t\t;;\n-\t    -m|--merges)\n-\t\tscan_merges=t\n-\t\t;;\n-\t    --no-merges)\n-\t\tscan_merges=\n-\t\t;;\n-\t    -l|--reflog)\n-\t\tscan_reflog=t\n-\t\t;;\n-\t    --no-reflog)\n-\t\tscan_reflog=\n-\t\t;;\n-\t    -r|--reflog_merges)\n-\t\tscan_reflog_merges=t\n-\t\t;;\n-\t    --no-reflog_merges)\n-\t\tscan_reflog_merges=\n-\t\t;;\n-\t    -t|--merge-targets)\n-\t\tscan_merge_targets=t\n-\t\t;;\n-\t    --no-merge-targets)\n-\t\tscan_merge_targets=\n-\t\t;;\n-\t    -a|--all)\n-\t\tscan_reflog=t\n-\t\tscan_reflog_merges=t\n-\t\tscan_merges=t\n-\t\tscan_merge_targets=t\n-\t\t;;\n-\t    --)\n-\t\tshift\n-\t\tbreak\n-\t\t;;\n-\t    *)\n-\t\tusage\n-\t\t;;\n-\tesac\n-\tshift\n-done\n-\n-test \"$#\" = 1 || usage\n-\n-all_strategies=\"$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets\"\n-if test -z \"$all_strategies\"; then\n-\tdie \"must enable at least one of -lrmt\"\n-fi\n-\n-branch=\"$1\"\n-test -z \"$new_name\" && new_name=\"$branch\"\n-\n-if test ! -z \"$scan_reflog\"; then\n-\tif test -r \"$GIT_DIR\"/logs/HEAD; then\n-\t\tcandidates=\"$(search_reflog $branch)\"\n-\telse\n-\t\tdie 'reflog scanning requested, but' \\\n-\t\t\t'$GIT_DIR/logs/HEAD not readable'\n-\tfi\n-fi\n-if test ! -z \"$scan_reflog_merges\"; then\n-\tif test -r \"$GIT_DIR\"/logs/HEAD; then\n-\t\tcandidates=\"$candidates $(search_reflog_merges $branch)\"\n-\telse\n-\t\tdie 'reflog scanning requested, but' \\\n-\t\t\t'$GIT_DIR/logs/HEAD not readable'\n-\tfi\n-fi\n-if test ! -z \"$scan_merges\"; then\n-\tcandidates=\"$candidates $(search_merges $branch)\"\n-fi\n-if test ! -z \"$scan_merge_targets\"; then\n-\tcandidates=\"$candidates $(search_merge_targets $branch)\"\n-fi\n-\n-candidates=\"$(git rev-parse $candidates | sort -u)\"\n-\n-if test -z \"$candidates\"; then\n-\thint=\n-\ttest \"z$all_strategies\" != \"ztttt\" \\\n-\t\t&& hint=\" (maybe try again with -a)\"\n-\tdie \"no candidates for $branch found$hint\"\n-fi\n-\n-echo \"** Candidates for $branch **\"\n-for cmt in $candidates; do\n-\tgit --no-pager log --pretty=tformat:\"%ct:%h [%cr] %s\" --abbrev-commit -1 $cmt\n-done \\\n-| sort -n | cut -d: -f2-\n-\n-newest=\"$(git rev-list -1 $candidates)\"\n-if test ! -z \"$dry_run\"; then\n-\tprintf \"** Most recent: \"\n-\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n-elif ! git rev-parse --verify --quiet $new_name >/dev/null; then\n-\tprintf \"** Restoring $new_name to \"\n-\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n-\tgit branch $new_name $newest\n-else\n-\tprintf \"Most recent: \"\n-\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n-\techo \"** $new_name already exists, doing nothing\"\n-fi\n\n-- \n2.49.0.1077.gc0e912fd4c.dirty\n\n"},{"id":"517644","messageId":"20250509-pks-contrib-spring-cleanup-v2-7-14e20d95ec68@pks.im","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","subject":"[PATCH v2 07/11] contrib: remove \"persistent-https\" remote helper","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-09T09:17:07Z","receivedAt":"2025-05-09T09:17:18Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"persistent-https\" remote helper supposedly speeds up SSL operations\nby running a daemon that keeps a connection open to a remote server. It\nis effectively unmaintained nowadays: the last time it received an\nupdate was in accb613afd2 (contrib/persistent-https: use Git version for\nbuild label, 2016-07-20) and its parent commits to make it compile with\nGo 1.7+.\n\nThis Go toolchain is somewhat dated by now though and unsupported. The\noldest still-supported toolchain is Go 1.23, which was released in\nAugust 2024. It is not possible to compile the remote helper with that\nGo version anymore:\n\n    $ go version\n    go version go1.23.8 linux/amd64\n    $ make\n    case $(go version) in \\\n    \"go version go\"1.[0-5].*) EQ=\" \" ;; *) EQ=\"=\" ;; esac && \\\n    go build -o git-remote-persistent-https \\\n            -ldflags \"-X main._BUILD_EMBED_LABEL${EQ}GIT_VERSION=2.49.0.943.g965a70ebf62\"\n    go: cannot find main module, but found .git/config in /home/pks/Development/git\n            to create a module there, run:\n            cd ../.. && go mod init\n    make: *** [Makefile:31: git-remote-persistent-https] Error 1\n\nThe problem is that modern Go toolchains require a \"go.mod\" file, but we\ndon't have any such files. This requirement exists since quite a while\nalready, so it's clear that nobody has tried to use this remote helper\nanytime recent.\n\nRemove the remote helper.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/persistent-https/LICENSE   | 202 -------------------------------------\n contrib/persistent-https/Makefile  |  43 --------\n contrib/persistent-https/README    |  72 -------------\n contrib/persistent-https/client.go | 189 ----------------------------------\n contrib/persistent-https/main.go   |  82 ---------------\n contrib/persistent-https/proxy.go  | 190 ----------------------------------\n contrib/persistent-https/socket.go |  97 ------------------\n 7 files changed, 875 deletions(-)\n\ndiff --git a/contrib/persistent-https/LICENSE b/contrib/persistent-https/LICENSE\ndeleted file mode 100644\nindex d6456956733..00000000000\n--- a/contrib/persistent-https/LICENSE\n+++ /dev/null\n@@ -1,202 +0,0 @@\n-\n-                                 Apache License\n-                           Version 2.0, January 2004\n-                        http://www.apache.org/licenses/\n-\n-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n-\n-   1. Definitions.\n-\n-      \"License\" shall mean the terms and conditions for use, reproduction,\n-      and distribution as defined by Sections 1 through 9 of this document.\n-\n-      \"Licensor\" shall mean the copyright owner or entity authorized by\n-      the copyright owner that is granting the License.\n-\n-      \"Legal Entity\" shall mean the union of the acting entity and all\n-      other entities that control, are controlled by, or are under common\n-      control with that entity. For the purposes of this definition,\n-      \"control\" means (i) the power, direct or indirect, to cause the\n-      direction or management of such entity, whether by contract or\n-      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n-      outstanding shares, or (iii) beneficial ownership of such entity.\n-\n-      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n-      exercising permissions granted by this License.\n-\n-      \"Source\" form shall mean the preferred form for making modifications,\n-      including but not limited to software source code, documentation\n-      source, and configuration files.\n-\n-      \"Object\" form shall mean any form resulting from mechanical\n-      transformation or translation of a Source form, including but\n-      not limited to compiled object code, generated documentation,\n-      and conversions to other media types.\n-\n-      \"Work\" shall mean the work of authorship, whether in Source or\n-      Object form, made available under the License, as indicated by a\n-      copyright notice that is included in or attached to the work\n-      (an example is provided in the Appendix below).\n-\n-      \"Derivative Works\" shall mean any work, whether in Source or Object\n-      form, that is based on (or derived from) the Work and for which the\n-      editorial revisions, annotations, elaborations, or other modifications\n-      represent, as a whole, an original work of authorship. For the purposes\n-      of this License, Derivative Works shall not include works that remain\n-      separable from, or merely link (or bind by name) to the interfaces of,\n-      the Work and Derivative Works thereof.\n-\n-      \"Contribution\" shall mean any work of authorship, including\n-      the original version of the Work and any modifications or additions\n-      to that Work or Derivative Works thereof, that is intentionally\n-      submitted to Licensor for inclusion in the Work by the copyright owner\n-      or by an individual or Legal Entity authorized to submit on behalf of\n-      the copyright owner. For the purposes of this definition, \"submitted\"\n-      means any form of electronic, verbal, or written communication sent\n-      to the Licensor or its representatives, including but not limited to\n-      communication on electronic mailing lists, source code control systems,\n-      and issue tracking systems that are managed by, or on behalf of, the\n-      Licensor for the purpose of discussing and improving the Work, but\n-      excluding communication that is conspicuously marked or otherwise\n-      designated in writing by the copyright owner as \"Not a Contribution.\"\n-\n-      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n-      on behalf of whom a Contribution has been received by Licensor and\n-      subsequently incorporated within the Work.\n-\n-   2. Grant of Copyright License. Subject to the terms and conditions of\n-      this License, each Contributor hereby grants to You a perpetual,\n-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n-      copyright license to reproduce, prepare Derivative Works of,\n-      publicly display, publicly perform, sublicense, and distribute the\n-      Work and such Derivative Works in Source or Object form.\n-\n-   3. Grant of Patent License. Subject to the terms and conditions of\n-      this License, each Contributor hereby grants to You a perpetual,\n-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n-      (except as stated in this section) patent license to make, have made,\n-      use, offer to sell, sell, import, and otherwise transfer the Work,\n-      where such license applies only to those patent claims licensable\n-      by such Contributor that are necessarily infringed by their\n-      Contribution(s) alone or by combination of their Contribution(s)\n-      with the Work to which such Contribution(s) was submitted. If You\n-      institute patent litigation against any entity (including a\n-      cross-claim or counterclaim in a lawsuit) alleging that the Work\n-      or a Contribution incorporated within the Work constitutes direct\n-      or contributory patent infringement, then any patent licenses\n-      granted to You under this License for that Work shall terminate\n-      as of the date such litigation is filed.\n-\n-   4. Redistribution. You may reproduce and distribute copies of the\n-      Work or Derivative Works thereof in any medium, with or without\n-      modifications, and in Source or Object form, provided that You\n-      meet the following conditions:\n-\n-      (a) You must give any other recipients of the Work or\n-          Derivative Works a copy of this License; and\n-\n-      (b) You must cause any modified files to carry prominent notices\n-          stating that You changed the files; and\n-\n-      (c) You must retain, in the Source form of any Derivative Works\n-          that You distribute, all copyright, patent, trademark, and\n-          attribution notices from the Source form of the Work,\n-          excluding those notices that do not pertain to any part of\n-          the Derivative Works; and\n-\n-      (d) If the Work includes a \"NOTICE\" text file as part of its\n-          distribution, then any Derivative Works that You distribute must\n-          include a readable copy of the attribution notices contained\n-          within such NOTICE file, excluding those notices that do not\n-          pertain to any part of the Derivative Works, in at least one\n-          of the following places: within a NOTICE text file distributed\n-          as part of the Derivative Works; within the Source form or\n-          documentation, if provided along with the Derivative Works; or,\n-          within a display generated by the Derivative Works, if and\n-          wherever such third-party notices normally appear. The contents\n-          of the NOTICE file are for informational purposes only and\n-          do not modify the License. You may add Your own attribution\n-          notices within Derivative Works that You distribute, alongside\n-          or as an addendum to the NOTICE text from the Work, provided\n-          that such additional attribution notices cannot be construed\n-          as modifying the License.\n-\n-      You may add Your own copyright statement to Your modifications and\n-      may provide additional or different license terms and conditions\n-      for use, reproduction, or distribution of Your modifications, or\n-      for any such Derivative Works as a whole, provided Your use,\n-      reproduction, and distribution of the Work otherwise complies with\n-      the conditions stated in this License.\n-\n-   5. Submission of Contributions. Unless You explicitly state otherwise,\n-      any Contribution intentionally submitted for inclusion in the Work\n-      by You to the Licensor shall be under the terms and conditions of\n-      this License, without any additional terms or conditions.\n-      Notwithstanding the above, nothing herein shall supersede or modify\n-      the terms of any separate license agreement you may have executed\n-      with Licensor regarding such Contributions.\n-\n-   6. Trademarks. This License does not grant permission to use the trade\n-      names, trademarks, service marks, or product names of the Licensor,\n-      except as required for reasonable and customary use in describing the\n-      origin of the Work and reproducing the content of the NOTICE file.\n-\n-   7. Disclaimer of Warranty. Unless required by applicable law or\n-      agreed to in writing, Licensor provides the Work (and each\n-      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n-      implied, including, without limitation, any warranties or conditions\n-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n-      PARTICULAR PURPOSE. You are solely responsible for determining the\n-      appropriateness of using or redistributing the Work and assume any\n-      risks associated with Your exercise of permissions under this License.\n-\n-   8. Limitation of Liability. In no event and under no legal theory,\n-      whether in tort (including negligence), contract, or otherwise,\n-      unless required by applicable law (such as deliberate and grossly\n-      negligent acts) or agreed to in writing, shall any Contributor be\n-      liable to You for damages, including any direct, indirect, special,\n-      incidental, or consequential damages of any character arising as a\n-      result of this License or out of the use or inability to use the\n-      Work (including but not limited to damages for loss of goodwill,\n-      work stoppage, computer failure or malfunction, or any and all\n-      other commercial damages or losses), even if such Contributor\n-      has been advised of the possibility of such damages.\n-\n-   9. Accepting Warranty or Additional Liability. While redistributing\n-      the Work or Derivative Works thereof, You may choose to offer,\n-      and charge a fee for, acceptance of support, warranty, indemnity,\n-      or other liability obligations and/or rights consistent with this\n-      License. However, in accepting such obligations, You may act only\n-      on Your own behalf and on Your sole responsibility, not on behalf\n-      of any other Contributor, and only if You agree to indemnify,\n-      defend, and hold each Contributor harmless for any liability\n-      incurred by, or claims asserted against, such Contributor by reason\n-      of your accepting any such warranty or additional liability.\n-\n-   END OF TERMS AND CONDITIONS\n-\n-   APPENDIX: How to apply the Apache License to your work.\n-\n-      To apply the Apache License to your work, attach the following\n-      boilerplate notice, with the fields enclosed by brackets \"[]\"\n-      replaced with your own identifying information. (Don't include\n-      the brackets!)  The text should be enclosed in the appropriate\n-      comment syntax for the file format. We also recommend that a\n-      file or class name and description of purpose be included on the\n-      same \"printed page\" as the copyright notice for easier\n-      identification within third-party archives.\n-\n-   Copyright [yyyy] [name of copyright owner]\n-\n-   Licensed under the Apache License, Version 2.0 (the \"License\");\n-   you may not use this file except in compliance with the License.\n-   You may obtain a copy of the License at\n-\n-       http://www.apache.org/licenses/LICENSE-2.0\n-\n-   Unless required by applicable law or agreed to in writing, software\n-   distributed under the License is distributed on an \"AS IS\" BASIS,\n-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-   See the License for the specific language governing permissions and\n-   limitations under the License.\ndiff --git a/contrib/persistent-https/Makefile b/contrib/persistent-https/Makefile\ndeleted file mode 100644\nindex 691737e76be..00000000000\n--- a/contrib/persistent-https/Makefile\n+++ /dev/null\n@@ -1,43 +0,0 @@\n-# Copyright 2012 Google Inc. All Rights Reserved.\n-#\n-# Licensed under the Apache License, Version 2.0 (the \"License\");\n-# you may not use this file except in compliance with the License.\n-# You may obtain a copy of the License at\n-#\n-#     http://www.apache.org/licenses/LICENSE-2.0\n-#\n-# Unless required by applicable law or agreed to in writing, software\n-# distributed under the License is distributed on an \"AS IS\" BASIS,\n-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-# See the License for the specific language governing permissions and\n-# limitations under the License.\n-\n-# The default target of this Makefile is...\n-all::\n-\n-BUILD_LABEL=$(shell cut -d\" \" -f3 ../../GIT-VERSION-FILE)\n-TAR_OUT=$(shell go env GOOS)_$(shell go env GOARCH).tar.gz\n-\n-all:: git-remote-persistent-https git-remote-persistent-https--proxy \\\n-\tgit-remote-persistent-http\n-\n-git-remote-persistent-https--proxy: git-remote-persistent-https\n-\tln -f -s git-remote-persistent-https git-remote-persistent-https--proxy\n-\n-git-remote-persistent-http: git-remote-persistent-https\n-\tln -f -s git-remote-persistent-https git-remote-persistent-http\n-\n-git-remote-persistent-https:\n-\tcase $$(go version) in \\\n-\t\"go version go\"1.[0-5].*) EQ=\" \" ;; *) EQ=\"=\" ;; esac && \\\n-\tgo build -o git-remote-persistent-https \\\n-\t\t-ldflags \"-X main._BUILD_EMBED_LABEL$${EQ}$(BUILD_LABEL)\"\n-\n-clean:\n-\trm -f git-remote-persistent-http* *.tar.gz\n-\n-tar: clean all\n-\t@chmod 555 git-remote-persistent-https\n-\t@tar -czf $(TAR_OUT) git-remote-persistent-http* README LICENSE\n-\t@echo\n-\t@echo \"Created $(TAR_OUT)\"\ndiff --git a/contrib/persistent-https/README b/contrib/persistent-https/README\ndeleted file mode 100644\nindex 7c4cd8d257d..00000000000\n--- a/contrib/persistent-https/README\n+++ /dev/null\n@@ -1,72 +0,0 @@\n-git-remote-persistent-https\n-\n-The git-remote-persistent-https binary speeds up SSL operations\n-by running a daemon job (git-remote-persistent-https--proxy) that\n-keeps a connection open to a server.\n-\n-\n-PRE-BUILT BINARIES\n-\n-Darwin amd64:\n-https://commondatastorage.googleapis.com/git-remote-persistent-https/darwin_amd64.tar.gz\n-\n-Linux amd64:\n-https://commondatastorage.googleapis.com/git-remote-persistent-https/linux_amd64.tar.gz\n-\n-\n-INSTALLING\n-\n-Move all of the git-remote-persistent-http* binaries to a directory\n-in PATH.\n-\n-\n-USAGE\n-\n-HTTPS requests can be delegated to the proxy by using the\n-\"persistent-https\" scheme, e.g.\n-\n-git clone persistent-https://kernel.googlesource.com/pub/scm/git/git\n-\n-Likewise, .gitconfig can be updated as follows to rewrite https urls\n-to use persistent-https:\n-\n-[url \"persistent-https\"]\n-\tinsteadof = https\n-[url \"persistent-http\"]\n-\tinsteadof = http\n-\n-You may also want to allow the use of the persistent-https helper for\n-submodule URLs (since any https URLs pointing to submodules will be\n-rewritten, and Git's out-of-the-box defaults forbid submodules from\n-using unknown remote helpers):\n-\n-[protocol \"persistent-https\"]\n-\tallow = always\n-[protocol \"persistent-http\"]\n-\tallow = always\n-\n-\n-#####################################################################\n-# BUILDING FROM SOURCE\n-#####################################################################\n-\n-LOCATION\n-\n-The source is available in the contrib/persistent-https directory of\n-the Git source repository. The Git source repository is available at\n-git://git.kernel.org/pub/scm/git/git.git/\n-https://kernel.googlesource.com/pub/scm/git/git\n-\n-\n-PREREQUISITES\n-\n-The code is written in Go (http://golang.org/) and the Go compiler is\n-required. Currently, the compiler must be built and installed from tip\n-of source, in order to include a fix in the reverse http proxy:\n-http://code.google.com/p/go/source/detail?r=a615b796570a2cd8591884767a7d67ede74f6648\n-\n-\n-BUILDING\n-\n-Run \"make\" to build the binaries. See the section on\n-INSTALLING above.\ndiff --git a/contrib/persistent-https/client.go b/contrib/persistent-https/client.go\ndeleted file mode 100644\nindex 71125b5832d..00000000000\n--- a/contrib/persistent-https/client.go\n+++ /dev/null\n@@ -1,189 +0,0 @@\n-// Copyright 2012 Google Inc. All Rights Reserved.\n-//\n-// Licensed under the Apache License, Version 2.0 (the \"License\");\n-// you may not use this file except in compliance with the License.\n-// You may obtain a copy of the License at\n-//\n-//     http://www.apache.org/licenses/LICENSE-2.0\n-//\n-// Unless required by applicable law or agreed to in writing, software\n-// distributed under the License is distributed on an \"AS IS\" BASIS,\n-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-// See the License for the specific language governing permissions and\n-// limitations under the License.\n-\n-package main\n-\n-import (\n-\t\"bufio\"\n-\t\"errors\"\n-\t\"fmt\"\n-\t\"net\"\n-\t\"net/url\"\n-\t\"os\"\n-\t\"os/exec\"\n-\t\"strings\"\n-\t\"syscall\"\n-\t\"time\"\n-)\n-\n-type Client struct {\n-\tProxyBin string\n-\tArgs     []string\n-\n-\tinsecure bool\n-}\n-\n-func (c *Client) Run() error {\n-\tif err := c.resolveArgs(); err != nil {\n-\t\treturn fmt.Errorf(\"resolveArgs() got error: %v\", err)\n-\t}\n-\n-\t// Connect to the proxy.\n-\tuconn, hconn, addr, err := c.connect()\n-\tif err != nil {\n-\t\treturn fmt.Errorf(\"connect() got error: %v\", err)\n-\t}\n-\t// Keep the unix socket connection open for the duration of the request.\n-\tdefer uconn.Close()\n-\t// Keep a connection to the HTTP server open, so no other user can\n-\t// bind on the same address so long as the process is running.\n-\tdefer hconn.Close()\n-\n-\t// Start the git-remote-http subprocess.\n-\tcargs := []string{\"-c\", fmt.Sprintf(\"http.proxy=%v\", addr), \"remote-http\"}\n-\tcargs = append(cargs, c.Args...)\n-\tcmd := exec.Command(\"git\", cargs...)\n-\n-\tfor _, v := range os.Environ() {\n-\t\tif !strings.HasPrefix(v, \"GIT_PERSISTENT_HTTPS_SECURE=\") {\n-\t\t\tcmd.Env = append(cmd.Env, v)\n-\t\t}\n-\t}\n-\t// Set the GIT_PERSISTENT_HTTPS_SECURE environment variable when\n-\t// the proxy is using a SSL connection.  This allows credential helpers\n-\t// to identify secure proxy connections, despite being passed an HTTP\n-\t// scheme.\n-\tif !c.insecure {\n-\t\tcmd.Env = append(cmd.Env, \"GIT_PERSISTENT_HTTPS_SECURE=1\")\n-\t}\n-\n-\tcmd.Stdin = os.Stdin\n-\tcmd.Stdout = os.Stdout\n-\tcmd.Stderr = os.Stderr\n-\tif err := cmd.Run(); err != nil {\n-\t\tif eerr, ok := err.(*exec.ExitError); ok {\n-\t\t\tif stat, ok := eerr.ProcessState.Sys().(syscall.WaitStatus); ok && stat.ExitStatus() != 0 {\n-\t\t\t\tos.Exit(stat.ExitStatus())\n-\t\t\t}\n-\t\t}\n-\t\treturn fmt.Errorf(\"git-remote-http subprocess got error: %v\", err)\n-\t}\n-\treturn nil\n-}\n-\n-func (c *Client) connect() (uconn net.Conn, hconn net.Conn, addr string, err error) {\n-\tuconn, err = DefaultSocket.Dial()\n-\tif err != nil {\n-\t\tif e, ok := err.(*net.OpError); ok && (os.IsNotExist(e.Err) || e.Err == syscall.ECONNREFUSED) {\n-\t\t\tif err = c.startProxy(); err == nil {\n-\t\t\t\tuconn, err = DefaultSocket.Dial()\n-\t\t\t}\n-\t\t}\n-\t\tif err != nil {\n-\t\t\treturn\n-\t\t}\n-\t}\n-\n-\tif addr, err = c.readAddr(uconn); err != nil {\n-\t\treturn\n-\t}\n-\n-\t// Open a tcp connection to the proxy.\n-\tif hconn, err = net.Dial(\"tcp\", addr); err != nil {\n-\t\treturn\n-\t}\n-\n-\t// Verify the address hasn't changed ownership.\n-\tvar addr2 string\n-\tif addr2, err = c.readAddr(uconn); err != nil {\n-\t\treturn\n-\t} else if addr != addr2 {\n-\t\terr = fmt.Errorf(\"address changed after connect. got %q, want %q\", addr2, addr)\n-\t\treturn\n-\t}\n-\treturn\n-}\n-\n-func (c *Client) readAddr(conn net.Conn) (string, error) {\n-\tconn.SetDeadline(time.Now().Add(5 * time.Second))\n-\tdata := make([]byte, 100)\n-\tn, err := conn.Read(data)\n-\tif err != nil {\n-\t\treturn \"\", fmt.Errorf(\"error reading unix socket: %v\", err)\n-\t} else if n == 0 {\n-\t\treturn \"\", errors.New(\"empty data response\")\n-\t}\n-\tconn.Write([]byte{1}) // Ack\n-\n-\tvar addr string\n-\tif addrs := strings.Split(string(data[:n]), \"\\n\"); len(addrs) != 2 {\n-\t\treturn \"\", fmt.Errorf(\"got %q, wanted 2 addresses\", data[:n])\n-\t} else if c.insecure {\n-\t\taddr = addrs[1]\n-\t} else {\n-\t\taddr = addrs[0]\n-\t}\n-\treturn addr, nil\n-}\n-\n-func (c *Client) startProxy() error {\n-\tcmd := exec.Command(c.ProxyBin)\n-\tcmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}\n-\tstdout, err := cmd.StdoutPipe()\n-\tif err != nil {\n-\t\treturn err\n-\t}\n-\tdefer stdout.Close()\n-\tif err := cmd.Start(); err != nil {\n-\t\treturn err\n-\t}\n-\tresult := make(chan error)\n-\tgo func() {\n-\t\tbytes, _, err := bufio.NewReader(stdout).ReadLine()\n-\t\tif line := string(bytes); err == nil && line != \"OK\" {\n-\t\t\terr = fmt.Errorf(\"proxy returned %q, want \\\"OK\\\"\", line)\n-\t\t}\n-\t\tresult <- err\n-\t}()\n-\tselect {\n-\tcase err := <-result:\n-\t\treturn err\n-\tcase <-time.After(5 * time.Second):\n-\t\treturn errors.New(\"timeout waiting for proxy to start\")\n-\t}\n-\tpanic(\"not reachable\")\n-}\n-\n-func (c *Client) resolveArgs() error {\n-\tif nargs := len(c.Args); nargs == 0 {\n-\t\treturn errors.New(\"remote needed\")\n-\t} else if nargs > 2 {\n-\t\treturn fmt.Errorf(\"want at most 2 args, got %v\", c.Args)\n-\t}\n-\n-\t// Rewrite the url scheme to be http.\n-\tidx := len(c.Args) - 1\n-\trawurl := c.Args[idx]\n-\trurl, err := url.Parse(rawurl)\n-\tif err != nil {\n-\t\treturn fmt.Errorf(\"invalid remote: %v\", err)\n-\t}\n-\tc.insecure = rurl.Scheme == \"persistent-http\"\n-\trurl.Scheme = \"http\"\n-\tc.Args[idx] = rurl.String()\n-\tif idx != 0 && c.Args[0] == rawurl {\n-\t\tc.Args[0] = c.Args[idx]\n-\t}\n-\treturn nil\n-}\ndiff --git a/contrib/persistent-https/main.go b/contrib/persistent-https/main.go\ndeleted file mode 100644\nindex fd1b1077439..00000000000\n--- a/contrib/persistent-https/main.go\n+++ /dev/null\n@@ -1,82 +0,0 @@\n-// Copyright 2012 Google Inc. All Rights Reserved.\n-//\n-// Licensed under the Apache License, Version 2.0 (the \"License\");\n-// you may not use this file except in compliance with the License.\n-// You may obtain a copy of the License at\n-//\n-//     http://www.apache.org/licenses/LICENSE-2.0\n-//\n-// Unless required by applicable law or agreed to in writing, software\n-// distributed under the License is distributed on an \"AS IS\" BASIS,\n-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-// See the License for the specific language governing permissions and\n-// limitations under the License.\n-\n-// The git-remote-persistent-https binary speeds up SSL operations by running\n-// a daemon job that keeps a connection open to a Git server. This ensures the\n-// git-remote-persistent-https--proxy is running and delegating execution\n-// to the git-remote-http binary with the http_proxy set to the daemon job.\n-// A unix socket is used to authenticate the proxy and discover the\n-// HTTP address. Note, both the client and proxy are included in the same\n-// binary.\n-package main\n-\n-import (\n-\t\"flag\"\n-\t\"fmt\"\n-\t\"log\"\n-\t\"os\"\n-\t\"strings\"\n-\t\"time\"\n-)\n-\n-var (\n-\tforceProxy = flag.Bool(\"proxy\", false, \"Whether to start the binary in proxy mode\")\n-\tproxyBin   = flag.String(\"proxy_bin\", \"git-remote-persistent-https--proxy\", \"Path to the proxy binary\")\n-\tprintLabel = flag.Bool(\"print_label\", false, \"Prints the build label for the binary\")\n-\n-\t// Variable that should be defined through the -X linker flag.\n-\t_BUILD_EMBED_LABEL string\n-)\n-\n-const (\n-\tdefaultMaxIdleDuration    = 24 * time.Hour\n-\tdefaultPollUpdateInterval = 15 * time.Minute\n-)\n-\n-func main() {\n-\tflag.Parse()\n-\tif *printLabel {\n-\t\t// Short circuit execution to print the build label\n-\t\tfmt.Println(buildLabel())\n-\t\treturn\n-\t}\n-\n-\tvar err error\n-\tif *forceProxy || strings.HasSuffix(os.Args[0], \"--proxy\") {\n-\t\tlog.SetPrefix(\"git-remote-persistent-https--proxy: \")\n-\t\tproxy := &Proxy{\n-\t\t\tBuildLabel:         buildLabel(),\n-\t\t\tMaxIdleDuration:    defaultMaxIdleDuration,\n-\t\t\tPollUpdateInterval: defaultPollUpdateInterval,\n-\t\t}\n-\t\terr = proxy.Run()\n-\t} else {\n-\t\tlog.SetPrefix(\"git-remote-persistent-https: \")\n-\t\tclient := &Client{\n-\t\t\tProxyBin: *proxyBin,\n-\t\t\tArgs:     flag.Args(),\n-\t\t}\n-\t\terr = client.Run()\n-\t}\n-\tif err != nil {\n-\t\tlog.Fatalln(err)\n-\t}\n-}\n-\n-func buildLabel() string {\n-\tif _BUILD_EMBED_LABEL == \"\" {\n-\t\tlog.Println(`unlabeled build; build with \"make\" to label`)\n-\t}\n-\treturn _BUILD_EMBED_LABEL\n-}\ndiff --git a/contrib/persistent-https/proxy.go b/contrib/persistent-https/proxy.go\ndeleted file mode 100644\nindex bb0cdba3864..00000000000\n--- a/contrib/persistent-https/proxy.go\n+++ /dev/null\n@@ -1,190 +0,0 @@\n-// Copyright 2012 Google Inc. All Rights Reserved.\n-//\n-// Licensed under the Apache License, Version 2.0 (the \"License\");\n-// you may not use this file except in compliance with the License.\n-// You may obtain a copy of the License at\n-//\n-//     http://www.apache.org/licenses/LICENSE-2.0\n-//\n-// Unless required by applicable law or agreed to in writing, software\n-// distributed under the License is distributed on an \"AS IS\" BASIS,\n-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-// See the License for the specific language governing permissions and\n-// limitations under the License.\n-\n-package main\n-\n-import (\n-\t\"fmt\"\n-\t\"log\"\n-\t\"net\"\n-\t\"net/http\"\n-\t\"net/http/httputil\"\n-\t\"os\"\n-\t\"os/exec\"\n-\t\"os/signal\"\n-\t\"sync\"\n-\t\"syscall\"\n-\t\"time\"\n-)\n-\n-type Proxy struct {\n-\tBuildLabel         string\n-\tMaxIdleDuration    time.Duration\n-\tPollUpdateInterval time.Duration\n-\n-\tul        net.Listener\n-\thttpAddr  string\n-\thttpsAddr string\n-}\n-\n-func (p *Proxy) Run() error {\n-\thl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n-\tif err != nil {\n-\t\treturn fmt.Errorf(\"http listen failed: %v\", err)\n-\t}\n-\tdefer hl.Close()\n-\n-\thsl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n-\tif err != nil {\n-\t\treturn fmt.Errorf(\"https listen failed: %v\", err)\n-\t}\n-\tdefer hsl.Close()\n-\n-\tp.ul, err = DefaultSocket.Listen()\n-\tif err != nil {\n-\t\tc, derr := DefaultSocket.Dial()\n-\t\tif derr == nil {\n-\t\t\tc.Close()\n-\t\t\tfmt.Println(\"OK\\nA proxy is already running... exiting\")\n-\t\t\treturn nil\n-\t\t} else if e, ok := derr.(*net.OpError); ok && e.Err == syscall.ECONNREFUSED {\n-\t\t\t// Nothing is listening on the socket, unlink it and try again.\n-\t\t\tsyscall.Unlink(DefaultSocket.Path())\n-\t\t\tp.ul, err = DefaultSocket.Listen()\n-\t\t}\n-\t\tif err != nil {\n-\t\t\treturn fmt.Errorf(\"unix listen failed on %v: %v\", DefaultSocket.Path(), err)\n-\t\t}\n-\t}\n-\tdefer p.ul.Close()\n-\tgo p.closeOnSignal()\n-\tgo p.closeOnUpdate()\n-\n-\tp.httpAddr = hl.Addr().String()\n-\tp.httpsAddr = hsl.Addr().String()\n-\tfmt.Printf(\"OK\\nListening on unix socket=%v http=%v https=%v\\n\",\n-\t\tp.ul.Addr(), p.httpAddr, p.httpsAddr)\n-\n-\tresult := make(chan error, 2)\n-\tgo p.serveUnix(result)\n-\tgo func() {\n-\t\tresult <- http.Serve(hl, &httputil.ReverseProxy{\n-\t\t\tFlushInterval: 500 * time.Millisecond,\n-\t\t\tDirector:      func(r *http.Request) {},\n-\t\t})\n-\t}()\n-\tgo func() {\n-\t\tresult <- http.Serve(hsl, &httputil.ReverseProxy{\n-\t\t\tFlushInterval: 500 * time.Millisecond,\n-\t\t\tDirector: func(r *http.Request) {\n-\t\t\t\tr.URL.Scheme = \"https\"\n-\t\t\t},\n-\t\t})\n-\t}()\n-\treturn <-result\n-}\n-\n-type socketContext struct {\n-\tsync.WaitGroup\n-\tmutex sync.Mutex\n-\tlast  time.Time\n-}\n-\n-func (sc *socketContext) Done() {\n-\tsc.mutex.Lock()\n-\tdefer sc.mutex.Unlock()\n-\tsc.last = time.Now()\n-\tsc.WaitGroup.Done()\n-}\n-\n-func (p *Proxy) serveUnix(result chan<- error) {\n-\tsockCtx := &socketContext{}\n-\tgo p.closeOnIdle(sockCtx)\n-\n-\tvar err error\n-\tfor {\n-\t\tvar uconn net.Conn\n-\t\tuconn, err = p.ul.Accept()\n-\t\tif err != nil {\n-\t\t\terr = fmt.Errorf(\"accept failed: %v\", err)\n-\t\t\tbreak\n-\t\t}\n-\t\tsockCtx.Add(1)\n-\t\tgo p.handleUnixConn(sockCtx, uconn)\n-\t}\n-\tsockCtx.Wait()\n-\tresult <- err\n-}\n-\n-func (p *Proxy) handleUnixConn(sockCtx *socketContext, uconn net.Conn) {\n-\tdefer sockCtx.Done()\n-\tdefer uconn.Close()\n-\tdata := []byte(fmt.Sprintf(\"%v\\n%v\", p.httpsAddr, p.httpAddr))\n-\tuconn.SetDeadline(time.Now().Add(5 * time.Second))\n-\tfor i := 0; i < 2; i++ {\n-\t\tif n, err := uconn.Write(data); err != nil {\n-\t\t\tlog.Printf(\"error sending http addresses: %+v\\n\", err)\n-\t\t\treturn\n-\t\t} else if n != len(data) {\n-\t\t\tlog.Printf(\"sent %d data bytes, wanted %d\\n\", n, len(data))\n-\t\t\treturn\n-\t\t}\n-\t\tif _, err := uconn.Read([]byte{0, 0, 0, 0}); err != nil {\n-\t\t\tlog.Printf(\"error waiting for Ack: %+v\\n\", err)\n-\t\t\treturn\n-\t\t}\n-\t}\n-\t// Wait without a deadline for the client to finish via EOF\n-\tuconn.SetDeadline(time.Time{})\n-\tuconn.Read([]byte{0, 0, 0, 0})\n-}\n-\n-func (p *Proxy) closeOnIdle(sockCtx *socketContext) {\n-\tfor d := p.MaxIdleDuration; d > 0; {\n-\t\ttime.Sleep(d)\n-\t\tsockCtx.Wait()\n-\t\tsockCtx.mutex.Lock()\n-\t\tif d = sockCtx.last.Add(p.MaxIdleDuration).Sub(time.Now()); d <= 0 {\n-\t\t\tlog.Println(\"graceful shutdown from idle timeout\")\n-\t\t\tp.ul.Close()\n-\t\t}\n-\t\tsockCtx.mutex.Unlock()\n-\t}\n-}\n-\n-func (p *Proxy) closeOnUpdate() {\n-\tfor {\n-\t\ttime.Sleep(p.PollUpdateInterval)\n-\t\tif out, err := exec.Command(os.Args[0], \"--print_label\").Output(); err != nil {\n-\t\t\tlog.Printf(\"error polling for updated binary: %v\\n\", err)\n-\t\t} else if s := string(out[:len(out)-1]); p.BuildLabel != s {\n-\t\t\tlog.Printf(\"graceful shutdown from updated binary: %q --> %q\\n\", p.BuildLabel, s)\n-\t\t\tp.ul.Close()\n-\t\t\tbreak\n-\t\t}\n-\t}\n-}\n-\n-func (p *Proxy) closeOnSignal() {\n-\tch := make(chan os.Signal, 10)\n-\tsignal.Notify(ch, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGHUP))\n-\tsig := <-ch\n-\tp.ul.Close()\n-\tswitch sig {\n-\tcase os.Signal(syscall.SIGHUP):\n-\t\tlog.Printf(\"graceful shutdown from signal: %v\\n\", sig)\n-\tdefault:\n-\t\tlog.Fatalf(\"exiting from signal: %v\\n\", sig)\n-\t}\n-}\ndiff --git a/contrib/persistent-https/socket.go b/contrib/persistent-https/socket.go\ndeleted file mode 100644\nindex 193b911dd13..00000000000\n--- a/contrib/persistent-https/socket.go\n+++ /dev/null\n@@ -1,97 +0,0 @@\n-// Copyright 2012 Google Inc. All Rights Reserved.\n-//\n-// Licensed under the Apache License, Version 2.0 (the \"License\");\n-// you may not use this file except in compliance with the License.\n-// You may obtain a copy of the License at\n-//\n-//     http://www.apache.org/licenses/LICENSE-2.0\n-//\n-// Unless required by applicable law or agreed to in writing, software\n-// distributed under the License is distributed on an \"AS IS\" BASIS,\n-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-// See the License for the specific language governing permissions and\n-// limitations under the License.\n-\n-package main\n-\n-import (\n-\t\"fmt\"\n-\t\"log\"\n-\t\"net\"\n-\t\"os\"\n-\t\"path/filepath\"\n-\t\"syscall\"\n-)\n-\n-// A Socket is a wrapper around a Unix socket that verifies directory\n-// permissions.\n-type Socket struct {\n-\tDir string\n-}\n-\n-func defaultDir() string {\n-\tsockPath := \".git-credential-cache\"\n-\tif home := os.Getenv(\"HOME\"); home != \"\" {\n-\t\treturn filepath.Join(home, sockPath)\n-\t}\n-\tlog.Printf(\"socket: cannot find HOME path. using relative directory %q for socket\", sockPath)\n-\treturn sockPath\n-}\n-\n-// DefaultSocket is a Socket in the $HOME/.git-credential-cache directory.\n-var DefaultSocket = Socket{Dir: defaultDir()}\n-\n-// Listen announces the local network address of the unix socket. The\n-// permissions on the socket directory are verified before attempting\n-// the actual listen.\n-func (s Socket) Listen() (net.Listener, error) {\n-\tnetwork, addr := \"unix\", s.Path()\n-\tif err := s.mkdir(); err != nil {\n-\t\treturn nil, &net.OpError{Op: \"listen\", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err}\n-\t}\n-\treturn net.Listen(network, addr)\n-}\n-\n-// Dial connects to the unix socket. The permissions on the socket directory\n-// are verified before attempting the actual dial.\n-func (s Socket) Dial() (net.Conn, error) {\n-\tnetwork, addr := \"unix\", s.Path()\n-\tif err := s.checkPermissions(); err != nil {\n-\t\treturn nil, &net.OpError{Op: \"dial\", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err}\n-\t}\n-\treturn net.Dial(network, addr)\n-}\n-\n-// Path returns the fully specified file name of the unix socket.\n-func (s Socket) Path() string {\n-\treturn filepath.Join(s.Dir, \"persistent-https-proxy-socket\")\n-}\n-\n-func (s Socket) mkdir() error {\n-\tif err := s.checkPermissions(); err == nil {\n-\t\treturn nil\n-\t} else if !os.IsNotExist(err) {\n-\t\treturn err\n-\t}\n-\tif err := os.MkdirAll(s.Dir, 0700); err != nil {\n-\t\treturn err\n-\t}\n-\treturn s.checkPermissions()\n-}\n-\n-func (s Socket) checkPermissions() error {\n-\tfi, err := os.Stat(s.Dir)\n-\tif err != nil {\n-\t\treturn err\n-\t}\n-\tif !fi.IsDir() {\n-\t\treturn fmt.Errorf(\"socket: got file, want directory for %q\", s.Dir)\n-\t}\n-\tif fi.Mode().Perm() != 0700 {\n-\t\treturn fmt.Errorf(\"socket: got perm %o, want 700 for %q\", fi.Mode().Perm(), s.Dir)\n-\t}\n-\tif st := fi.Sys().(*syscall.Stat_t); int(st.Uid) != os.Getuid() {\n-\t\treturn fmt.Errorf(\"socket: got uid %d, want %d for %q\", st.Uid, os.Getuid(), s.Dir)\n-\t}\n-\treturn nil\n-}\n\n-- \n2.49.0.1077.gc0e912fd4c.dirty\n\n"},{"id":"517645","messageId":"20250509-pks-contrib-spring-cleanup-v2-9-14e20d95ec68@pks.im","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","subject":"[PATCH v2 09/11] contrib: remove \"emacs\" directory","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-09T09:17:09Z","receivedAt":"2025-05-09T09:17:19Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"While the \"emacs/\" directory still exists, all of its code has been\nreplaced with stubs in 6d5ed4836db (git{,-blame}.el: remove old\nbitrotting Emacs code, 2018-04-11). Instead, the recommendation is to\nuse Emacs' own vc-annotate mode.\n\nRemove the code altogether.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/emacs/README       | 33 ---------------------------------\n contrib/emacs/git-blame.el |  6 ------\n contrib/emacs/git.el       |  6 ------\n 3 files changed, 45 deletions(-)\n\ndiff --git a/contrib/emacs/README b/contrib/emacs/README\ndeleted file mode 100644\nindex 977a16f1e33..00000000000\n--- a/contrib/emacs/README\n+++ /dev/null\n@@ -1,33 +0,0 @@\n-This directory used to contain various modules for Emacs support.\n-\n-These were added shortly after Git was first released. Since then\n-Emacs's own support for Git got better than what was offered by these\n-modes. There are also popular 3rd-party Git modes such as Magit which\n-offer replacements for these.\n-\n-The following modules were available, and can be dug up from the Git\n-history:\n-\n-* git.el:\n-\n-  Wrapper for \"git status\" that provided access to other git commands.\n-\n-  Modern alternatives to this include Magit, and VC mode that ships\n-  with Emacs.\n-\n-* git-blame.el:\n-\n-  A wrapper for \"git blame\" written before Emacs's own vc-annotate\n-  mode learned to invoke git-blame, which can be done via C-x v g.\n-\n-* vc-git.el:\n-\n-  This file used to contain the VC-mode backend for git, but it is no\n-  longer distributed with git. It is now maintained as part of Emacs\n-  and included in standard Emacs distributions starting from version\n-  22.2.\n-\n-  If you have an earlier Emacs version, upgrading to Emacs 22 is\n-  recommended, since the VC mode in older Emacs is not generic enough\n-  to be able to support git in a reasonable manner, and no attempt has\n-  been made to backport vc-git.el.\ndiff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el\ndeleted file mode 100644\nindex 6a8a2b8ff19..00000000000\n--- a/contrib/emacs/git-blame.el\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-(error \"git-blame.el no longer ships with git. It's recommended\n-to replace its use with Emacs's own vc-annotate. See\n-contrib/emacs/README in git's\n-sources (https://github.com/git/git/blob/master/contrib/emacs/README)\n-for more info on suggested alternatives and for why this\n-happened.\")\ndiff --git a/contrib/emacs/git.el b/contrib/emacs/git.el\ndeleted file mode 100644\nindex 03f926281fb..00000000000\n--- a/contrib/emacs/git.el\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-(error \"git.el no longer ships with git. It's recommended to\n-replace its use with Magit, or simply delete references to git.el\n-in your initialization file(s). See contrib/emacs/README in git's\n-sources (https://github.com/git/git/blob/master/contrib/emacs/README)\n-for suggested alternatives and for why this happened. Emacs's own\n-VC mode and Magit are viable alternatives.\")\n\n-- \n2.49.0.1077.gc0e912fd4c.dirty\n\n"},{"id":"517647","messageId":"20250509-pks-contrib-spring-cleanup-v2-10-14e20d95ec68@pks.im","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","subject":"[PATCH v2 10/11] contrib: remove \"git-new-workdir\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-09T09:17:10Z","receivedAt":"2025-05-09T09:17:20Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"git-new-workdir\" command has been introduced to make it possible to\nhave a separate working directory in a different place. The command thus\npredates git-worktree(1), which is what people use nowadays to create\nany such working directory. As such, the script doesn't really have much\nof a reason to exist nowadays anymore.\n\nIt also doesn't seem like the script is still in use: the last time it\nhas received an update was in e32afab7b03 (git-new-workdir: don't fail\nif the target directory is empty, 2014-11-26), more than a decade ago.\nRemove it as well as the tests that depend on it.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/workdir/.gitattributes  |   1 -\n contrib/workdir/git-new-workdir | 105 ----------------------------------------\n t/meson.build                   |   1 -\n t/t1021-rerere-in-workdir.sh    |  58 ----------------------\n t/t3000-ls-files-others.sh      |  19 --------\n 5 files changed, 184 deletions(-)\n\ndiff --git a/contrib/workdir/.gitattributes b/contrib/workdir/.gitattributes\ndeleted file mode 100644\nindex 1f78c5d1bd3..00000000000\n--- a/contrib/workdir/.gitattributes\n+++ /dev/null\n@@ -1 +0,0 @@\n-/git-new-workdir eol=lf\ndiff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir\ndeleted file mode 100755\nindex 989197aace0..00000000000\n--- a/contrib/workdir/git-new-workdir\n+++ /dev/null\n@@ -1,105 +0,0 @@\n-#!/bin/sh\n-\n-usage () {\n-\techo \"usage:\" $@\n-\texit 127\n-}\n-\n-die () {\n-\techo $@\n-\texit 128\n-}\n-\n-failed () {\n-\tdie \"unable to create new workdir '$new_workdir'!\"\n-}\n-\n-if test $# -lt 2 || test $# -gt 3\n-then\n-\tusage \"$0 <repository> <new_workdir> [<branch>]\"\n-fi\n-\n-orig_git=$1\n-new_workdir=$2\n-branch=$3\n-\n-# want to make sure that what is pointed to has a .git directory ...\n-git_dir=$(cd \"$orig_git\" 2>/dev/null &&\n-  git rev-parse --git-dir 2>/dev/null) ||\n-  die \"Not a git repository: \\\"$orig_git\\\"\"\n-\n-case \"$git_dir\" in\n-.git)\n-\tgit_dir=\"$orig_git/.git\"\n-\t;;\n-.)\n-\tgit_dir=$orig_git\n-\t;;\n-esac\n-\n-# don't link to a configured bare repository\n-isbare=$(git --git-dir=\"$git_dir\" config --bool --get core.bare)\n-if test ztrue = \"z$isbare\"\n-then\n-\tdie \"\\\"$git_dir\\\" has core.bare set to true,\" \\\n-\t\t\" remove from \\\"$git_dir/config\\\" to use $0\"\n-fi\n-\n-# don't link to a workdir\n-if test -h \"$git_dir/config\"\n-then\n-\tdie \"\\\"$orig_git\\\" is a working directory only, please specify\" \\\n-\t\t\"a complete repository.\"\n-fi\n-\n-# make sure the links in the workdir have full paths to the original repo\n-git_dir=$(cd \"$git_dir\" && pwd) || exit 1\n-\n-# don't recreate a workdir over an existing directory, unless it's empty\n-if test -d \"$new_workdir\"\n-then\n-\tif test $(ls -a1 \"$new_workdir/.\" | wc -l) -ne 2\n-\tthen\n-\t\tdie \"destination directory '$new_workdir' is not empty.\"\n-\tfi\n-\tcleandir=\"$new_workdir/.git\"\n-else\n-\tcleandir=\"$new_workdir\"\n-fi\n-\n-mkdir -p \"$new_workdir/.git\" || failed\n-cleandir=$(cd \"$cleandir\" && pwd) || failed\n-\n-cleanup () {\n-\trm -rf \"$cleandir\"\n-}\n-siglist=\"0 1 2 15\"\n-trap cleanup $siglist\n-\n-# create the links to the original repo.  explicitly exclude index, HEAD and\n-# logs/HEAD from the list since they are purely related to the current working\n-# directory, and should not be shared.\n-for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn reftable\n-do\n-\t# create a containing directory if needed\n-\tcase $x in\n-\t*/*)\n-\t\tmkdir -p \"$new_workdir/.git/${x%/*}\"\n-\t\t;;\n-\tesac\n-\n-\tln -s \"$git_dir/$x\" \"$new_workdir/.git/$x\" || failed\n-done\n-\n-# commands below this are run in the context of the new workdir\n-cd \"$new_workdir\" || failed\n-\n-# copy the HEAD from the original repository as a default branch\n-cp \"$git_dir/HEAD\" .git/HEAD || failed\n-\n-# the workdir is set up.  if the checkout fails, the user can fix it.\n-trap - $siglist\n-\n-# checkout the branch (either the same as HEAD from the original repository,\n-# or the one that was asked for)\n-git checkout -f $branch\ndiff --git a/t/meson.build b/t/meson.build\nindex b09c0becb8d..9206090fedc 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -178,7 +178,6 @@ integration_tests = [\n   't1015-read-index-unmerged.sh',\n   't1016-compatObjectFormat.sh',\n   't1020-subdirectory.sh',\n-  't1021-rerere-in-workdir.sh',\n   't1022-read-tree-partial-clone.sh',\n   't1050-large.sh',\n   't1051-large-conversion.sh',\ndiff --git a/t/t1021-rerere-in-workdir.sh b/t/t1021-rerere-in-workdir.sh\ndeleted file mode 100755\nindex 0b892894eb9..00000000000\n--- a/t/t1021-rerere-in-workdir.sh\n+++ /dev/null\n@@ -1,58 +0,0 @@\n-#!/bin/sh\n-\n-test_description='rerere run in a workdir'\n-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n-\n-. ./test-lib.sh\n-\n-test_expect_success SYMLINKS setup '\n-\tgit config rerere.enabled true &&\n-\t>world &&\n-\tgit add world &&\n-\ttest_tick &&\n-\tgit commit -m initial &&\n-\n-\techo hello >world &&\n-\ttest_tick &&\n-\tgit commit -a -m hello &&\n-\n-\tgit checkout -b side HEAD^ &&\n-\techo goodbye >world &&\n-\ttest_tick &&\n-\tgit commit -a -m goodbye &&\n-\n-\tgit checkout main\n-'\n-\n-test_expect_success SYMLINKS 'rerere in workdir' '\n-\trm -rf .git/rr-cache &&\n-\t\"$SHELL_PATH\" \"$TEST_DIRECTORY/../contrib/workdir/git-new-workdir\" . work &&\n-\t(\n-\t\tcd work &&\n-\t\ttest_must_fail git merge side &&\n-\t\tgit rerere status >actual &&\n-\t\techo world >expect &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-# This fails because we don't resolve relative symlink in mkdir_in_gitdir()\n-# For the purpose of helping contrib/workdir/git-new-workdir users, we do not\n-# have to support relative symlinks, but it might be nicer to make this work\n-# with a relative symbolic link someday.\n-test_expect_failure SYMLINKS 'rerere in workdir (relative)' '\n-\trm -rf .git/rr-cache &&\n-\t\"$SHELL_PATH\" \"$TEST_DIRECTORY/../contrib/workdir/git-new-workdir\" . krow &&\n-\t(\n-\t\tcd krow &&\n-\t\trm -f .git/rr-cache &&\n-\t\tln -s ../.git/rr-cache .git/rr-cache &&\n-\t\ttest_must_fail git merge side &&\n-\t\tgit rerere status >actual &&\n-\t\techo world >expect &&\n-\t\ttest_cmp expect actual\n-\t)\n-'\n-\n-test_done\ndiff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh\nindex 13f66fd649d..b41e7f0daa4 100755\n--- a/t/t3000-ls-files-others.sh\n+++ b/t/t3000-ls-files-others.sh\n@@ -73,25 +73,6 @@ test_expect_success 'ls-files --others handles non-submodule .git' '\n \ttest_cmp expected1 output\n '\n \n-test_expect_success SYMLINKS 'ls-files --others with symlinked submodule' '\n-\tgit init super &&\n-\tgit init sub &&\n-\t(\n-\t\tcd sub &&\n-\t\t>a &&\n-\t\tgit add a &&\n-\t\tgit commit -m sub &&\n-\t\tgit pack-refs --all\n-\t) &&\n-\t(\n-\t\tcd super &&\n-\t\t\"$SHELL_PATH\" \"$TEST_DIRECTORY/../contrib/workdir/git-new-workdir\" ../sub sub &&\n-\t\tgit ls-files --others --exclude-standard >../actual\n-\t) &&\n-\techo sub/ >expect &&\n-\ttest_cmp expect actual\n-'\n-\n test_expect_success 'setup nested pathspec search' '\n \ttest_create_repo nested &&\n \t(\n\n-- \n2.49.0.1077.gc0e912fd4c.dirty\n\n"},{"id":"517648","messageId":"20250509-pks-contrib-spring-cleanup-v2-11-14e20d95ec68@pks.im","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","subject":"[PATCH v2 11/11] contrib: remove \"stats\" directory","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-09T09:17:11Z","receivedAt":"2025-05-09T09:17:20Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"stats\" directory contains a couple of scripts to do some statistics\non a repository:\n\n  - \"git-common-hash\" shows the longest common hash prefixes and can be\n    used to determine the minimum prefix length to use for object names\n    to be unique. The script has last been touched in 53474eb92ff\n    (contrib: update stats/mailmap script, 2012-12-12) and searching for\n    it on the internet doesn't really surface any potential use cases or\n    even mentions of it.\n\n    Modern Git also shouldn't really need this tool as it knows to\n    automatically scale printed prefixes via some heuristics.\n\n  - \"mailmap.pl\" performs some statistics on the number of mailmapped\n    commits in a repository. It has last been modified in 53474eb92ff\n    (contrib: update stats/mailmap script, 2012-12-12) and has since\n    been bitrotting. It doesn't even compile nowadays anymore:\n\n        $ perl contrib/stats/mailmap.pl\n        Experimental keys on scalar is now forbidden at contrib/stats/mailmap.pl line 57.\n        Type of arg 1 to keys must be hash or array (not hash element) at contrib/stats/mailmap.pl line 57, near \"}) \"\n        Experimental keys on scalar is now forbidden at contrib/stats/mailmap.pl line 57.\n        Type of arg 1 to keys must be hash or array (not private variable) at contrib/stats/mailmap.pl line 57, near \"$h)\"\n        Experimental keys on scalar is now forbidden at contrib/stats/mailmap.pl line 64.\n        Type of arg 1 to keys must be hash or array (not private variable) at contrib/stats/mailmap.pl line 64, near \"$h)\"\n        Execution of contrib/stats/mailmap.pl aborted due to compilation errors.\n\n    This should be good-enough signal to indicate that nodoby is using\n    this script at all anymore.\n\n  - \"packinfo.pl\" takes the output from git-verify-pack(1) and performs\n    some pretty printing thereof. On the one hand it reformats the\n    output to be easier to read and provide some summaries. On the other\n    hand it may also print filenames of blobs.\n\n    The script has last been touched in 3b1eb124932 (contrib: update\n    packinfo.pl to not use dashed commands, 2008-10-17), but it still\n    works nowadays. Even so, it is quite unlikely that anybody is still\n    using it. And if the provided information really was useful we\n    should rather think about moving it into git-verify-pack(1) itself.\n\nRemove the whole directory.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n contrib/stats/git-common-hash |  26 ------\n contrib/stats/mailmap.pl      |  70 --------------\n contrib/stats/packinfo.pl     | 212 ------------------------------------------\n 3 files changed, 308 deletions(-)\n\ndiff --git a/contrib/stats/git-common-hash b/contrib/stats/git-common-hash\ndeleted file mode 100755\nindex e27fd088be1..00000000000\n--- a/contrib/stats/git-common-hash\n+++ /dev/null\n@@ -1,26 +0,0 @@\n-#!/bin/sh\n-\n-# This script displays the distribution of longest common hash prefixes.\n-# This can be used to determine the minimum prefix length to use\n-# for object names to be unique.\n-\n-git rev-list --objects --all | sort | perl -lne '\n-  substr($_, 40) = \"\";\n-  # uncomment next line for a distribution of bits instead of hex chars\n-  # $_ = unpack(\"B*\",pack(\"H*\",$_));\n-  if (defined $p) {\n-    ($p ^ $_) =~ /^(\\0*)/;\n-    $common = length $1;\n-    if (defined $pcommon) {\n-      $count[$pcommon > $common ? $pcommon : $common]++;\n-    } else {\n-      $count[$common]++; # first item\n-    }\n-  }\n-  $p = $_;\n-  $pcommon = $common;\n-  END {\n-    $count[$common]++; # last item\n-    print \"$_: $count[$_]\" for 0..$#count;\n-  }\n-'\ndiff --git a/contrib/stats/mailmap.pl b/contrib/stats/mailmap.pl\ndeleted file mode 100755\nindex 9513f5e35b4..00000000000\n--- a/contrib/stats/mailmap.pl\n+++ /dev/null\n@@ -1,70 +0,0 @@\n-#!/usr/bin/perl\n-\n-use warnings 'all';\n-use strict;\n-use Getopt::Long;\n-\n-my $match_emails;\n-my $match_names;\n-my $order_by = 'count';\n-Getopt::Long::Configure(qw(bundling));\n-GetOptions(\n-\t'emails|e!' => \\$match_emails,\n-\t'names|n!'  => \\$match_names,\n-\t'count|c'   => sub { $order_by = 'count' },\n-\t'time|t'    => sub { $order_by = 'stamp' },\n-) or exit 1;\n-$match_emails = 1 unless $match_names;\n-\n-my $email = {};\n-my $name = {};\n-\n-open(my $fh, '-|', \"git log --format='%at <%aE> %aN'\");\n-while(<$fh>) {\n-\tmy ($t, $e, $n) = /(\\S+) <(\\S+)> (.*)/;\n-\tmark($email, $e, $n, $t);\n-\tmark($name, $n, $e, $t);\n-}\n-close($fh);\n-\n-if ($match_emails) {\n-\tforeach my $e (dups($email)) {\n-\t\tforeach my $n (vals($email->{$e})) {\n-\t\t\tshow($n, $e, $email->{$e}->{$n});\n-\t\t}\n-\t\tprint \"\\n\";\n-\t}\n-}\n-if ($match_names) {\n-\tforeach my $n (dups($name)) {\n-\t\tforeach my $e (vals($name->{$n})) {\n-\t\t\tshow($n, $e, $name->{$n}->{$e});\n-\t\t}\n-\t\tprint \"\\n\";\n-\t}\n-}\n-exit 0;\n-\n-sub mark {\n-\tmy ($h, $k, $v, $t) = @_;\n-\tmy $e = $h->{$k}->{$v} ||= { count => 0, stamp => 0 };\n-\t$e->{count}++;\n-\t$e->{stamp} = $t unless $t < $e->{stamp};\n-}\n-\n-sub dups {\n-\tmy $h = shift;\n-\treturn grep { keys($h->{$_}) > 1 } keys($h);\n-}\n-\n-sub vals {\n-\tmy $h = shift;\n-\treturn sort {\n-\t\t$h->{$b}->{$order_by} <=> $h->{$a}->{$order_by}\n-\t} keys($h);\n-}\n-\n-sub show {\n-\tmy ($n, $e, $h) = @_;\n-\tprint \"$n <$e> ($h->{$order_by})\\n\";\n-}\ndiff --git a/contrib/stats/packinfo.pl b/contrib/stats/packinfo.pl\ndeleted file mode 100755\nindex be188c0f11d..00000000000\n--- a/contrib/stats/packinfo.pl\n+++ /dev/null\n@@ -1,212 +0,0 @@\n-#!/usr/bin/perl\n-#\n-# This tool will print vaguely pretty information about a pack.  It\n-# expects the output of \"git verify-pack -v\" as input on stdin.\n-#\n-# $ git verify-pack -v | packinfo.pl\n-#\n-# This prints some full-pack statistics; currently \"all sizes\", \"all\n-# path sizes\", \"tree sizes\", \"tree path sizes\", and \"depths\".\n-#\n-# * \"all sizes\" stats are across every object size in the file;\n-#   full sizes for base objects, and delta size for deltas.\n-# * \"all path sizes\" stats are across all object's \"path sizes\".\n-#   A path size is the sum of the size of the delta chain, including the\n-#   base object.  In other words, it's how many bytes need be read to\n-#   reassemble the file from deltas.\n-# * \"tree sizes\" are object sizes grouped into delta trees.\n-# * \"tree path sizes\" are path sizes grouped into delta trees.\n-# * \"depths\" should be obvious.\n-#\n-# When run as:\n-#\n-# $ git verify-pack -v | packinfo.pl -tree\n-#\n-# the trees of objects are output along with the stats.  This looks\n-# like:\n-#\n-#   0 commit 031321c6...      803      803\n-#\n-#   0   blob 03156f21...     1767     1767\n-#   1    blob f52a9d7f...       10     1777\n-#   2     blob a8cc5739...       51     1828\n-#   3      blob 660e90b1...       15     1843\n-#   4       blob 0cb8e3bb...       33     1876\n-#   2     blob e48607f0...      311     2088\n-#      size: count 6 total 2187 min 10 max 1767 mean 364.50 median 51 std_dev 635.85\n-# path size: count 6 total 11179 min 1767 max 2088 mean 1863.17 median 1843 std_dev 107.26\n-#\n-# The first number after the sha1 is the object size, the second\n-# number is the path size.  The statistics are across all objects in\n-# the previous delta tree.  Obviously they are omitted for trees of\n-# one object.\n-#\n-# When run as:\n-#\n-# $ git verify-pack -v | packinfo.pl -tree -filenames\n-#\n-# it adds filenames to the tree.  Getting this information is slow:\n-#\n-#   0   blob 03156f21...     1767     1767 Documentation/git-lost-found.txt @ tags/v1.2.0~142\n-#   1    blob f52a9d7f...       10     1777 Documentation/git-lost-found.txt @ tags/v1.5.0-rc1~74\n-#   2     blob a8cc5739...       51     1828 Documentation/git-lost+found.txt @ tags/v0.99.9h^0\n-#   3      blob 660e90b1...       15     1843 Documentation/git-lost+found.txt @ master~3222^2~2\n-#   4       blob 0cb8e3bb...       33     1876 Documentation/git-lost+found.txt @ master~3222^2~3\n-#   2     blob e48607f0...      311     2088 Documentation/git-lost-found.txt @ tags/v1.5.2-rc3~4\n-#      size: count 6 total 2187 min 10 max 1767 mean 364.50 median 51 std_dev 635.85\n-# path size: count 6 total 11179 min 1767 max 2088 mean 1863.17 median 1843 std_dev 107.26\n-#\n-# When run as:\n-#\n-# $ git verify-pack -v | packinfo.pl -dump\n-#\n-# it prints out \"sha1 size pathsize depth\" for each sha1 in lexical\n-# order.\n-#\n-# 000079a2eaef17b7eae70e1f0f635557ea67b644 30 472 7\n-# 00013cafe6980411aa6fdd940784917b5ff50f0a 44 1542 4\n-# 000182eacf99cde27d5916aa415921924b82972c 499 499 0\n-# ...\n-#\n-# This is handy for comparing two packs.  Adding \"-filenames\" will add\n-# filenames, as per \"-tree -filenames\" above.\n-\n-use strict;\n-use Getopt::Long;\n-\n-my $filenames = 0;\n-my $tree = 0;\n-my $dump = 0;\n-GetOptions(\"tree\" => \\$tree,\n-           \"filenames\" => \\$filenames,\n-           \"dump\" => \\$dump);\n-\n-my %parents;\n-my %children;\n-my %sizes;\n-my @roots;\n-my %paths;\n-my %types;\n-my @commits;\n-my %names;\n-my %depths;\n-my @depths;\n-\n-while (<STDIN>) {\n-    my ($sha1, $type, $size, $space, $offset, $depth, $parent) = split(/\\s+/, $_);\n-    next unless ($sha1 =~ /^[0-9a-f]{40}$/);\n-    $depths{$sha1} = $depth || 0;\n-    push(@depths, $depth || 0);\n-    push(@commits, $sha1) if ($type eq 'commit');\n-    push(@roots, $sha1) unless $parent;\n-    $parents{$sha1} = $parent;\n-    $types{$sha1} = $type;\n-    push(@{$children{$parent}}, $sha1);\n-    $sizes{$sha1} = $size;\n-}\n-\n-if ($filenames && ($tree || $dump)) {\n-    open(NAMES, \"git name-rev --all|\");\n-    while (<NAMES>) {\n-        if (/^(\\S+)\\s+(.*)$/) {\n-            my ($sha1, $name) = ($1, $2);\n-            $names{$sha1} = $name;\n-        }\n-    }\n-    close NAMES;\n-\n-    for my $commit (@commits) {\n-        my $name = $names{$commit};\n-        open(TREE, \"git ls-tree -t -r $commit|\");\n-        print STDERR \"Plumbing tree $name\\n\";\n-        while (<TREE>) {\n-            if (/^(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(.*)$/) {\n-                my ($mode, $type, $sha1, $path) = ($1, $2, $3, $4);\n-                $paths{$sha1} = \"$path @ $name\";\n-            }\n-        }\n-        close TREE;\n-    }\n-}\n-\n-sub stats {\n-    my @data = sort {$a <=> $b} @_;\n-    my $min = $data[0];\n-    my $max = $data[$#data];\n-    my $total = 0;\n-    my $count = scalar @data;\n-    for my $datum (@data) {\n-        $total += $datum;\n-    }\n-    my $mean = $total / $count;\n-    my $median = $data[int(@data / 2)];\n-    my $diff_sum = 0;\n-    for my $datum (@data) {\n-        $diff_sum += ($datum - $mean)**2;\n-    }\n-    my $std_dev = sqrt($diff_sum / $count);\n-    return ($count, $total, $min, $max, $mean, $median, $std_dev);\n-}\n-\n-sub print_stats {\n-    my $name = shift;\n-    my ($count, $total, $min, $max, $mean, $median, $std_dev) = stats(@_);\n-    printf(\"%s: count %s total %s min %s max %s mean %.2f median %s std_dev %.2f\\n\",\n-           $name, $count, $total, $min, $max, $mean, $median, $std_dev);\n-}\n-\n-my @sizes;\n-my @path_sizes;\n-my @all_sizes;\n-my @all_path_sizes;\n-my %path_sizes;\n-\n-sub dig {\n-    my ($sha1, $depth, $path_size) = @_;\n-    $path_size += $sizes{$sha1};\n-    push(@sizes, $sizes{$sha1});\n-    push(@all_sizes, $sizes{$sha1});\n-    push(@path_sizes, $path_size);\n-    push(@all_path_sizes, $path_size);\n-    $path_sizes{$sha1} = $path_size;\n-    if ($tree) {\n-        printf(\"%3d%s %6s %s %8d %8d %s\\n\",\n-               $depth, (\" \" x $depth), $types{$sha1},\n-               $sha1, $sizes{$sha1}, $path_size, $paths{$sha1});\n-    }\n-    for my $child (@{$children{$sha1}}) {\n-        dig($child, $depth + 1, $path_size);\n-    }\n-}\n-\n-my @tree_sizes;\n-my @tree_path_sizes;\n-\n-for my $root (@roots) {\n-    undef @sizes;\n-    undef @path_sizes;\n-    dig($root, 0, 0);\n-    my ($aa, $sz_total) = stats(@sizes);\n-    my ($bb, $psz_total) = stats(@path_sizes);\n-    push(@tree_sizes, $sz_total);\n-    push(@tree_path_sizes, $psz_total);\n-    if ($tree) {\n-        if (@sizes > 1) {\n-            print_stats(\"     size\", @sizes);\n-            print_stats(\"path size\", @path_sizes);\n-        }\n-        print \"\\n\";\n-    }\n-}\n-\n-if ($dump) {\n-    for my $sha1 (sort keys %sizes) {\n-        print \"$sha1 $sizes{$sha1} $path_sizes{$sha1} $depths{$sha1} $paths{$sha1}\\n\";\n-    }\n-} else {\n-    print_stats(\"      all sizes\", @all_sizes);\n-    print_stats(\" all path sizes\", @all_path_sizes);\n-    print_stats(\"     tree sizes\", @tree_sizes);\n-    print_stats(\"tree path sizes\", @tree_path_sizes);\n-    print_stats(\"         depths\", @depths);\n-}\n\n-- \n2.49.0.1077.gc0e912fd4c.dirty\n\n"},{"id":"517716","messageId":"CABPp-BFUUwRe6q=gw2=XtUF3=cWk_=Rq083Q=FbBaTK5L+vXzg@mail.gmail.com","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-11-14e20d95ec68@pks.im","subject":"Re: [PATCH v2 11/11] contrib: remove \"stats\" directory","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-05-09T23:31:42Z","receivedAt":"2025-05-09T23:31:55Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, May 9, 2025 at 2:45 AM Patrick Steinhardt <ps@pks.im> wrote:\n>\n> The \"stats\" directory contains a couple of scripts to do some statistics\n> on a repository:\n>\n>   - \"git-common-hash\" shows the longest common hash prefixes and can be\n>     used to determine the minimum prefix length to use for object names\n>     to be unique. The script has last been touched in 53474eb92ff\n>     (contrib: update stats/mailmap script, 2012-12-12) and searching for\n>     it on the internet doesn't really surface any potential use cases or\n>     even mentions of it.\n>\n>     Modern Git also shouldn't really need this tool as it knows to\n>     automatically scale printed prefixes via some heuristics.\n>\n>   - \"mailmap.pl\" performs some statistics on the number of mailmapped\n>     commits in a repository. It has last been modified in 53474eb92ff\n>     (contrib: update stats/mailmap script, 2012-12-12) and has since\n>     been bitrotting. It doesn't even compile nowadays anymore:\n>\n>         $ perl contrib/stats/mailmap.pl\n>         Experimental keys on scalar is now forbidden at contrib/stats/mailmap.pl line 57.\n>         Type of arg 1 to keys must be hash or array (not hash element) at contrib/stats/mailmap.pl line 57, near \"}) \"\n>         Experimental keys on scalar is now forbidden at contrib/stats/mailmap.pl line 57.\n>         Type of arg 1 to keys must be hash or array (not private variable) at contrib/stats/mailmap.pl line 57, near \"$h)\"\n>         Experimental keys on scalar is now forbidden at contrib/stats/mailmap.pl line 64.\n>         Type of arg 1 to keys must be hash or array (not private variable) at contrib/stats/mailmap.pl line 64, near \"$h)\"\n>         Execution of contrib/stats/mailmap.pl aborted due to compilation errors.\n>\n>     This should be good-enough signal to indicate that nodoby is using\n>     this script at all anymore.\n\ns/nodoby/nobody/\n\n>   - \"packinfo.pl\" takes the output from git-verify-pack(1) and performs\n>     some pretty printing thereof. On the one hand it reformats the\n>     output to be easier to read and provide some summaries. On the other\n>     hand it may also print filenames of blobs.\n>\n>     The script has last been touched in 3b1eb124932 (contrib: update\n>     packinfo.pl to not use dashed commands, 2008-10-17), but it still\n>     works nowadays. Even so, it is quite unlikely that anybody is still\n>     using it. And if the provided information really was useful we\n>     should rather think about moving it into git-verify-pack(1) itself.\n>\n> Remove the whole directory.\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  contrib/stats/git-common-hash |  26 ------\n>  contrib/stats/mailmap.pl      |  70 --------------\n>  contrib/stats/packinfo.pl     | 212 ------------------------------------------\n>  3 files changed, 308 deletions(-)\n>\n> diff --git a/contrib/stats/git-common-hash b/contrib/stats/git-common-hash\n> deleted file mode 100755\n> index e27fd088be1..00000000000\n> --- a/contrib/stats/git-common-hash\n> +++ /dev/null\n> @@ -1,26 +0,0 @@\n> -#!/bin/sh\n> -\n> -# This script displays the distribution of longest common hash prefixes.\n> -# This can be used to determine the minimum prefix length to use\n> -# for object names to be unique.\n> -\n> -git rev-list --objects --all | sort | perl -lne '\n> -  substr($_, 40) = \"\";\n> -  # uncomment next line for a distribution of bits instead of hex chars\n> -  # $_ = unpack(\"B*\",pack(\"H*\",$_));\n> -  if (defined $p) {\n> -    ($p ^ $_) =~ /^(\\0*)/;\n> -    $common = length $1;\n> -    if (defined $pcommon) {\n> -      $count[$pcommon > $common ? $pcommon : $common]++;\n> -    } else {\n> -      $count[$common]++; # first item\n> -    }\n> -  }\n> -  $p = $_;\n> -  $pcommon = $common;\n> -  END {\n> -    $count[$common]++; # last item\n> -    print \"$_: $count[$_]\" for 0..$#count;\n> -  }\n> -'\n> diff --git a/contrib/stats/mailmap.pl b/contrib/stats/mailmap.pl\n> deleted file mode 100755\n> index 9513f5e35b4..00000000000\n> --- a/contrib/stats/mailmap.pl\n> +++ /dev/null\n> @@ -1,70 +0,0 @@\n> -#!/usr/bin/perl\n> -\n> -use warnings 'all';\n> -use strict;\n> -use Getopt::Long;\n> -\n> -my $match_emails;\n> -my $match_names;\n> -my $order_by = 'count';\n> -Getopt::Long::Configure(qw(bundling));\n> -GetOptions(\n> -       'emails|e!' => \\$match_emails,\n> -       'names|n!'  => \\$match_names,\n> -       'count|c'   => sub { $order_by = 'count' },\n> -       'time|t'    => sub { $order_by = 'stamp' },\n> -) or exit 1;\n> -$match_emails = 1 unless $match_names;\n> -\n> -my $email = {};\n> -my $name = {};\n> -\n> -open(my $fh, '-|', \"git log --format='%at <%aE> %aN'\");\n> -while(<$fh>) {\n> -       my ($t, $e, $n) = /(\\S+) <(\\S+)> (.*)/;\n> -       mark($email, $e, $n, $t);\n> -       mark($name, $n, $e, $t);\n> -}\n> -close($fh);\n> -\n> -if ($match_emails) {\n> -       foreach my $e (dups($email)) {\n> -               foreach my $n (vals($email->{$e})) {\n> -                       show($n, $e, $email->{$e}->{$n});\n> -               }\n> -               print \"\\n\";\n> -       }\n> -}\n> -if ($match_names) {\n> -       foreach my $n (dups($name)) {\n> -               foreach my $e (vals($name->{$n})) {\n> -                       show($n, $e, $name->{$n}->{$e});\n> -               }\n> -               print \"\\n\";\n> -       }\n> -}\n> -exit 0;\n> -\n> -sub mark {\n> -       my ($h, $k, $v, $t) = @_;\n> -       my $e = $h->{$k}->{$v} ||= { count => 0, stamp => 0 };\n> -       $e->{count}++;\n> -       $e->{stamp} = $t unless $t < $e->{stamp};\n> -}\n> -\n> -sub dups {\n> -       my $h = shift;\n> -       return grep { keys($h->{$_}) > 1 } keys($h);\n> -}\n> -\n> -sub vals {\n> -       my $h = shift;\n> -       return sort {\n> -               $h->{$b}->{$order_by} <=> $h->{$a}->{$order_by}\n> -       } keys($h);\n> -}\n> -\n> -sub show {\n> -       my ($n, $e, $h) = @_;\n> -       print \"$n <$e> ($h->{$order_by})\\n\";\n> -}\n> diff --git a/contrib/stats/packinfo.pl b/contrib/stats/packinfo.pl\n> deleted file mode 100755\n> index be188c0f11d..00000000000\n> --- a/contrib/stats/packinfo.pl\n> +++ /dev/null\n> @@ -1,212 +0,0 @@\n> -#!/usr/bin/perl\n> -#\n> -# This tool will print vaguely pretty information about a pack.  It\n> -# expects the output of \"git verify-pack -v\" as input on stdin.\n> -#\n> -# $ git verify-pack -v | packinfo.pl\n> -#\n> -# This prints some full-pack statistics; currently \"all sizes\", \"all\n> -# path sizes\", \"tree sizes\", \"tree path sizes\", and \"depths\".\n> -#\n> -# * \"all sizes\" stats are across every object size in the file;\n> -#   full sizes for base objects, and delta size for deltas.\n> -# * \"all path sizes\" stats are across all object's \"path sizes\".\n> -#   A path size is the sum of the size of the delta chain, including the\n> -#   base object.  In other words, it's how many bytes need be read to\n> -#   reassemble the file from deltas.\n> -# * \"tree sizes\" are object sizes grouped into delta trees.\n> -# * \"tree path sizes\" are path sizes grouped into delta trees.\n> -# * \"depths\" should be obvious.\n> -#\n> -# When run as:\n> -#\n> -# $ git verify-pack -v | packinfo.pl -tree\n> -#\n> -# the trees of objects are output along with the stats.  This looks\n> -# like:\n> -#\n> -#   0 commit 031321c6...      803      803\n> -#\n> -#   0   blob 03156f21...     1767     1767\n> -#   1    blob f52a9d7f...       10     1777\n> -#   2     blob a8cc5739...       51     1828\n> -#   3      blob 660e90b1...       15     1843\n> -#   4       blob 0cb8e3bb...       33     1876\n> -#   2     blob e48607f0...      311     2088\n> -#      size: count 6 total 2187 min 10 max 1767 mean 364.50 median 51 std_dev 635.85\n> -# path size: count 6 total 11179 min 1767 max 2088 mean 1863.17 median 1843 std_dev 107.26\n> -#\n> -# The first number after the sha1 is the object size, the second\n> -# number is the path size.  The statistics are across all objects in\n> -# the previous delta tree.  Obviously they are omitted for trees of\n> -# one object.\n> -#\n> -# When run as:\n> -#\n> -# $ git verify-pack -v | packinfo.pl -tree -filenames\n> -#\n> -# it adds filenames to the tree.  Getting this information is slow:\n> -#\n> -#   0   blob 03156f21...     1767     1767 Documentation/git-lost-found.txt @ tags/v1.2.0~142\n> -#   1    blob f52a9d7f...       10     1777 Documentation/git-lost-found.txt @ tags/v1.5.0-rc1~74\n> -#   2     blob a8cc5739...       51     1828 Documentation/git-lost+found.txt @ tags/v0.99.9h^0\n> -#   3      blob 660e90b1...       15     1843 Documentation/git-lost+found.txt @ master~3222^2~2\n> -#   4       blob 0cb8e3bb...       33     1876 Documentation/git-lost+found.txt @ master~3222^2~3\n> -#   2     blob e48607f0...      311     2088 Documentation/git-lost-found.txt @ tags/v1.5.2-rc3~4\n> -#      size: count 6 total 2187 min 10 max 1767 mean 364.50 median 51 std_dev 635.85\n> -# path size: count 6 total 11179 min 1767 max 2088 mean 1863.17 median 1843 std_dev 107.26\n> -#\n> -# When run as:\n> -#\n> -# $ git verify-pack -v | packinfo.pl -dump\n> -#\n> -# it prints out \"sha1 size pathsize depth\" for each sha1 in lexical\n> -# order.\n> -#\n> -# 000079a2eaef17b7eae70e1f0f635557ea67b644 30 472 7\n> -# 00013cafe6980411aa6fdd940784917b5ff50f0a 44 1542 4\n> -# 000182eacf99cde27d5916aa415921924b82972c 499 499 0\n> -# ...\n> -#\n> -# This is handy for comparing two packs.  Adding \"-filenames\" will add\n> -# filenames, as per \"-tree -filenames\" above.\n> -\n> -use strict;\n> -use Getopt::Long;\n> -\n> -my $filenames = 0;\n> -my $tree = 0;\n> -my $dump = 0;\n> -GetOptions(\"tree\" => \\$tree,\n> -           \"filenames\" => \\$filenames,\n> -           \"dump\" => \\$dump);\n> -\n> -my %parents;\n> -my %children;\n> -my %sizes;\n> -my @roots;\n> -my %paths;\n> -my %types;\n> -my @commits;\n> -my %names;\n> -my %depths;\n> -my @depths;\n> -\n> -while (<STDIN>) {\n> -    my ($sha1, $type, $size, $space, $offset, $depth, $parent) = split(/\\s+/, $_);\n> -    next unless ($sha1 =~ /^[0-9a-f]{40}$/);\n> -    $depths{$sha1} = $depth || 0;\n> -    push(@depths, $depth || 0);\n> -    push(@commits, $sha1) if ($type eq 'commit');\n> -    push(@roots, $sha1) unless $parent;\n> -    $parents{$sha1} = $parent;\n> -    $types{$sha1} = $type;\n> -    push(@{$children{$parent}}, $sha1);\n> -    $sizes{$sha1} = $size;\n> -}\n> -\n> -if ($filenames && ($tree || $dump)) {\n> -    open(NAMES, \"git name-rev --all|\");\n> -    while (<NAMES>) {\n> -        if (/^(\\S+)\\s+(.*)$/) {\n> -            my ($sha1, $name) = ($1, $2);\n> -            $names{$sha1} = $name;\n> -        }\n> -    }\n> -    close NAMES;\n> -\n> -    for my $commit (@commits) {\n> -        my $name = $names{$commit};\n> -        open(TREE, \"git ls-tree -t -r $commit|\");\n> -        print STDERR \"Plumbing tree $name\\n\";\n> -        while (<TREE>) {\n> -            if (/^(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(.*)$/) {\n> -                my ($mode, $type, $sha1, $path) = ($1, $2, $3, $4);\n> -                $paths{$sha1} = \"$path @ $name\";\n> -            }\n> -        }\n> -        close TREE;\n> -    }\n> -}\n> -\n> -sub stats {\n> -    my @data = sort {$a <=> $b} @_;\n> -    my $min = $data[0];\n> -    my $max = $data[$#data];\n> -    my $total = 0;\n> -    my $count = scalar @data;\n> -    for my $datum (@data) {\n> -        $total += $datum;\n> -    }\n> -    my $mean = $total / $count;\n> -    my $median = $data[int(@data / 2)];\n> -    my $diff_sum = 0;\n> -    for my $datum (@data) {\n> -        $diff_sum += ($datum - $mean)**2;\n> -    }\n> -    my $std_dev = sqrt($diff_sum / $count);\n> -    return ($count, $total, $min, $max, $mean, $median, $std_dev);\n> -}\n> -\n> -sub print_stats {\n> -    my $name = shift;\n> -    my ($count, $total, $min, $max, $mean, $median, $std_dev) = stats(@_);\n> -    printf(\"%s: count %s total %s min %s max %s mean %.2f median %s std_dev %.2f\\n\",\n> -           $name, $count, $total, $min, $max, $mean, $median, $std_dev);\n> -}\n> -\n> -my @sizes;\n> -my @path_sizes;\n> -my @all_sizes;\n> -my @all_path_sizes;\n> -my %path_sizes;\n> -\n> -sub dig {\n> -    my ($sha1, $depth, $path_size) = @_;\n> -    $path_size += $sizes{$sha1};\n> -    push(@sizes, $sizes{$sha1});\n> -    push(@all_sizes, $sizes{$sha1});\n> -    push(@path_sizes, $path_size);\n> -    push(@all_path_sizes, $path_size);\n> -    $path_sizes{$sha1} = $path_size;\n> -    if ($tree) {\n> -        printf(\"%3d%s %6s %s %8d %8d %s\\n\",\n> -               $depth, (\" \" x $depth), $types{$sha1},\n> -               $sha1, $sizes{$sha1}, $path_size, $paths{$sha1});\n> -    }\n> -    for my $child (@{$children{$sha1}}) {\n> -        dig($child, $depth + 1, $path_size);\n> -    }\n> -}\n> -\n> -my @tree_sizes;\n> -my @tree_path_sizes;\n> -\n> -for my $root (@roots) {\n> -    undef @sizes;\n> -    undef @path_sizes;\n> -    dig($root, 0, 0);\n> -    my ($aa, $sz_total) = stats(@sizes);\n> -    my ($bb, $psz_total) = stats(@path_sizes);\n> -    push(@tree_sizes, $sz_total);\n> -    push(@tree_path_sizes, $psz_total);\n> -    if ($tree) {\n> -        if (@sizes > 1) {\n> -            print_stats(\"     size\", @sizes);\n> -            print_stats(\"path size\", @path_sizes);\n> -        }\n> -        print \"\\n\";\n> -    }\n> -}\n> -\n> -if ($dump) {\n> -    for my $sha1 (sort keys %sizes) {\n> -        print \"$sha1 $sizes{$sha1} $path_sizes{$sha1} $depths{$sha1} $paths{$sha1}\\n\";\n> -    }\n> -} else {\n> -    print_stats(\"      all sizes\", @all_sizes);\n> -    print_stats(\" all path sizes\", @all_path_sizes);\n> -    print_stats(\"     tree sizes\", @tree_sizes);\n> -    print_stats(\"tree path sizes\", @tree_path_sizes);\n> -    print_stats(\"         depths\", @depths);\n> -}\n>\n> --\n> 2.49.0.1077.gc0e912fd4c.dirty\n>\n>\n"},{"id":"517717","messageId":"CABPp-BFYuYnbHG9uUOig8gpqrLywESf6fs62e_CMi2uCtNXYYw@mail.gmail.com","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","subject":"Re: [PATCH v2 00/11] Spring cleanup of \"contrib/\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-05-09T23:53:54Z","receivedAt":"2025-05-09T23:54:05Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, May 9, 2025 at 2:20 AM Patrick Steinhardt <ps@pks.im> wrote:\n>\n> Hi,\n>\n> as I have lamented multiple times multiple times already (e.g. [1]), the\n> \"contrib/\" directory is a bit of a mess containing many bits and pieces\n> that just sit there gathering dust, without getting any maintenance and\n> sometimes even in a clearly-broken state. So I decided to finally bite\n> the bullet and do a spring cleanup of \"contrib/\", which resulted in this\n> patch series here.\n>\n> I have used the following reasons for removal:\n>\n>   - The tool is clearly broken, e.g. it doesn't even compile.\n>\n>   - The tool hasn't received any updates for at least the last 5 years.\n>\n>   - The tool has a clear alternative or just isn't useful anymore.\n\nDo we still want to use your second reason listed as a reason to\nremove?  Junio brought that up on v1, and it bothers me a bit too.\nNow, if you couple that with \"contrib is meant as staging and projects\nshould graduate or be removed\", then I'd be fine with it, but you said\nlater in this cover letter that you were going to post that\ninformation in a follow-up series.  That makes me wonder whether the\nremoval of tools for this reason should be deferred to that later\nseries where that new direction is recorded.\n\nThat all said, I tried to do a count of which patches used which\nrationale (though I split out a fourth because it makes more sense to\nme that way).  I see:\n\n  - broken tool: patches 1, 7\n  - not updated: patches 4, part of 11\n  - better alternative: patches 1, 3, 5, 6, 8, 9, 10, part of 11\n  - already removed with just a stub left behind: patches 2, 3\n\n(Here I excluded patches from the \"not updated\" category if there was\nalso an additional rationale given in the commit message.  For other\nrationales, I put the patch under each category that was brought up as\na reason for retirement.)\n\nSo this series doesn't rely solely on the \"not updated\" rationale very\nmuch.  Even in patch 4 you allude to the fact that you _suspect_ that\ntool also falls under the \"broken tool\" category, and in patch 11, you\nalso argue that it should be handled differently if it's useful (which\nrhymes with saying that a better alternative exists, but isn't the\nsame since one doesn't necessarily exist yet).\n\n\n\nAnyway, I like the series, I'm just a little uncomfortable with this\npart of the cover letter and the wording of some of the commit\nmessages.  \"not updated in 5 years\" is good supplemental information,\nbut I think other git contributors reading those commit messages might\nget the wrong idea and apply it elsewhere.\n"},{"id":"517718","messageId":"xmqqzfflxrks.fsf@gitster.g","threadId":"63406","inReplyTo":"CABPp-BFYuYnbHG9uUOig8gpqrLywESf6fs62e_CMi2uCtNXYYw@mail.gmail.com","subject":"Re: [PATCH v2 00/11] Spring cleanup of \"contrib/\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-05-10T00:00:35Z","receivedAt":"2025-05-10T00:00:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> On Fri, May 9, 2025 at 2:20 AM Patrick Steinhardt <ps@pks.im> wrote:\n>>\n>> Hi,\n>>\n>> as I have lamented multiple times multiple times already (e.g. [1]), the\n>> \"contrib/\" directory is a bit of a mess containing many bits and pieces\n>> that just sit there gathering dust, without getting any maintenance and\n>> sometimes even in a clearly-broken state. So I decided to finally bite\n>> the bullet and do a spring cleanup of \"contrib/\", which resulted in this\n>> patch series here.\n>>\n>> I have used the following reasons for removal:\n>>\n>>   - The tool is clearly broken, e.g. it doesn't even compile.\n>>\n>>   - The tool hasn't received any updates for at least the last 5 years.\n>>\n>>   - The tool has a clear alternative or just isn't useful anymore.\n>\n> Do we still want to use your second reason listed as a reason to\n> remove?  Junio brought that up on v1, and it bothers me a bit too.\n\nFWIW, I would drop the second one and not mention, even if one\npersonally used as an initial filter to find candidates for removal.\nIt alone is not a valid justification.  Not mentioning it would\nclose the door for complaints like \"It may not have needed updates\nsince it works perfectly well for me\".\n"},{"id":"517734","messageId":"1a09f4cf68e9f4955aaaf2c58c7e7649@softwolves.pp.se","threadId":"63406","inReplyTo":"20250506-pks-contrib-spring-cleanup-v1-0-e6d5ddd79a72@pks.im","subject":"Re: [PATCH 00/10] Spring cleanup of \"contrib/\"","fromName":"Peter Krefting","fromEmail":"peter@softwolves.pp.se","sentAt":"2025-05-10T12:30:16Z","receivedAt":"2025-05-10T12:55:01Z","isPatch":true,"sender":{"key":"peter@softwolves.pp.se","avatar":"https://avatars.githubusercontent.com/u/990764?v=4"},"body":"2025-05-06 16:12 skrev Patrick Steinhardt:\n\n> Another subsequent step would be to split out some parts of \"contrib/\"\n> to be hosted in their own hierarchy.\n\nAuthor of contrib/fast-import/import-directories.perl here; I use this \nevery now and then to import old tools I wrote back before I learned \nabout version control (1990s, mostly).\n\nThe tool works as it is in the Git repository, but if there is interest \nin having it moved out of there, I can publish it somewhere else. I am a \nbit worried about the discoverability of the tools, though, but that can \nbe fixed in documentation.\n\n-- \n\\\\// Peter - http://www.softwolves.pp.se/\n"},{"id":"517760","messageId":"CALnO6CDp3Kr_Ma49jzftN_sMkOU95xGZ0sMe2J2boG2pWggCzQ@mail.gmail.com","threadId":"63406","inReplyTo":"CAPig+cRxDQBmPu_-ci5vEuwtsAHadfCiFOccdYseBSj2F52JGw@mail.gmail.com","subject":"Re: [PATCH 00/10] Spring cleanup of \"contrib/\"","fromName":"D. Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2025-05-10T20:07:26Z","receivedAt":"2025-05-10T20:07:39Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"On Tue, May 6, 2025 at 11:55 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Tue, May 6, 2025 at 9:32 PM Todd Zullinger <tmz@pobox.com> wrote:\n> > Eric Sunshine wrote:\n> > > Although we periodically hear that someone uses it, git-contacts\n> > > probably falls into the \"tool has a clear alternative\" category. In\n> > > this case, git-related[*] is a separately-maintained more functional\n> > > drop-in replacement which people could be using instead of\n> > > git-contacts.\n> > >\n> > > [*]: https://github.com/felipec/git-related/blob/master/git-related\n> >\n> > It may be worth noting that git-contacts is suggested in\n> > both MyFirstContribution.adoc and SubmittingPatches.\n>\n> I think I knew but forgot about those mentions. Certainly useful\n> information if Patrick decides to pursue retirement of git-contacts.\n>\n> > It probably helps that since 824503ce88 (SubmittingPatches:\n> > clarify 'git-contacts' location, 2024-04-18) there has been\n> > a note stating this isn't \"part of the core `git` binary and\n> > must be called directly.\" That is relatively recent, though.\n>\n> Out of curiosity, I Googled git-contacts but didn't find any\n> meaningful hits. Pretty much the only pages found were renderings of\n> Git's documentation (including SubmittingPatches and the man page for\n> git-contacts itself), as well as the few patches to the Git mailing\n> list which introduced or touched git-contacts over the years. I did\n> not find any general discussion or recommendations to use\n> git-contacts, so perhaps it indeed is not very much used.\n>\n> > I added git-contacts to the Fedora git packaging shortly\n> > after 92a5dbbc22 (SubmittingPatches: mention the git\n> > contacts command, 2018-04-11), presuming some readers would\n> > want to use it.  (I never want to penalize users who are\n> > diligent enough to read SubmittingPatches. :)\n> >\n> > All that said, I don't have any strong opinion on whether it\n> > is kept or removed, let alone when that might happen.  I\n> > don't know that I've ever used it, other than for testing\n> > that it worked while packaging it.\n>\n> I've never used git-contacts either, despite the fact that I'm the one\n> who ported Felipe's git-related from Ruby[1] to Perl[2] for inclusion\n> in Git's \"contrib\" since the Ruby version had been rejected due to\n> being written in a language not already employed elsewhere in the\n> project. The Perl rewrite also included a number of useful\n> enhancements which Felipe later incorporated into git-related after he\n> published it as a standalone project. He has since extended it to\n> include even more features, so it's functionally a superset of\n> git-contacts.\n\nOn a related note, installing git-related seems like rather more work\nthan using git-contacts [1]: I have to keep a Ruby environment with\nthe right gem working since I don't see this packaged anywhere.\nKeeping a Perl or Ruby environment working (at least for me) has been\nenough trouble as it is, heh.\n\n[1]: https://github.com/felipec/git-related#installation\n\nNot that we couldn't drop the script, but ideally the replacement is\nan easy install (since, e.g., some of us use git-contacts as a ccCmd\nper the documentation).\n\n>\n> By the way, Felipe also sent a patch series[3] eleven years ago with\n> the same intention of Patrick's series under discussion. Felipe's\n> series was never picked up but did undertake the retirement of\n> git-contacts.\n>\n> [1]: https://lore.kernel.org/git/1369986380-412-1-git-send-email-felipe.contreras@gmail.com/\n> [2]: https://lore.kernel.org/git/1374403962-48361-1-git-send-email-sunshine@sunshineco.com/\n> [3]: https://lore.kernel.org/git/1399662703-355-1-git-send-email-felipe.contreras@gmail.com/T/\n>\n\n\n-- \nD. Ben Knoble\n"},{"id":"517798","messageId":"87jz6mo4n9.fsf@gmail.com","threadId":"63406","inReplyTo":"20250509-pks-contrib-spring-cleanup-v2-0-14e20d95ec68@pks.im","subject":"Re: [PATCH v2 04/11] contrib: remove \"thunderbird-patch-inline\"","fromName":"Collin Funk","fromEmail":"collin.funk1@gmail.com","sentAt":"2025-05-12T04:05:14Z","receivedAt":"2025-05-12T04:05:17Z","isPatch":true,"sender":{"key":"collin.funk1@gmail.com","avatar":"https://avatars.githubusercontent.com/u/65689063?v=4"},"body":"Hi,\n\nYou said:\n\n> The \"thunderbird-patch-inline\" directory in \"contrib/\" contains a script\n> to send patch files via Thunderbird. This script depends on the\n> ExternalEditor extension [1], which seems to be effectively unmaintained\n> with the last update being in 2008. While the extension has eventually\n> been maintained in [2], that fork hasn't received any updates since\n> 2020, either.\n>\n> In addition, the script itself hasn't really seen a lot of maintenance\n> outside of a couple of global cleanups. It is quite unlikely that this\n> setup still works, and if it did it's even less likely that somebody\n> uses this script.\n\nFWIW, in case it helps any Thunderbird users, there is External Editor\nRevived which seems to work pretty well when I used Thunderbird [1].\n\nI think this script would be irrelevant with that extension though.\n\nCollin\n\n[1] https://github.com/Frederick888/external-editor-revived\n"},{"id":"517825","messageId":"aCHB08edPcxWCMiP@pks.im","threadId":"63406","inReplyTo":"CABPp-BFYuYnbHG9uUOig8gpqrLywESf6fs62e_CMi2uCtNXYYw@mail.gmail.com","subject":"Re: [PATCH v2 00/11] Spring cleanup of \"contrib/\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-12T09:39:31Z","receivedAt":"2025-05-12T09:39:36Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, May 09, 2025 at 04:53:54PM -0700, Elijah Newren wrote:\n> On Fri, May 9, 2025 at 2:20 AM Patrick Steinhardt <ps@pks.im> wrote:\n> >\n> > Hi,\n> >\n> > as I have lamented multiple times multiple times already (e.g. [1]), the\n> > \"contrib/\" directory is a bit of a mess containing many bits and pieces\n> > that just sit there gathering dust, without getting any maintenance and\n> > sometimes even in a clearly-broken state. So I decided to finally bite\n> > the bullet and do a spring cleanup of \"contrib/\", which resulted in this\n> > patch series here.\n> >\n> > I have used the following reasons for removal:\n> >\n> >   - The tool is clearly broken, e.g. it doesn't even compile.\n> >\n> >   - The tool hasn't received any updates for at least the last 5 years.\n> >\n> >   - The tool has a clear alternative or just isn't useful anymore.\n> \n> Do we still want to use your second reason listed as a reason to\n> remove?  Junio brought that up on v1, and it bothers me a bit too.\n> Now, if you couple that with \"contrib is meant as staging and projects\n> should graduate or be removed\", then I'd be fine with it, but you said\n> later in this cover letter that you were going to post that\n> information in a follow-up series.  That makes me wonder whether the\n> removal of tools for this reason should be deferred to that later\n> series where that new direction is recorded.\n\nThe only part that I would count into the category is parts of commit\n11, and I'd be okay with dropping it for now.\n\n> That all said, I tried to do a count of which patches used which\n> rationale (though I split out a fourth because it makes more sense to\n> me that way).  I see:\n> \n>   - broken tool: patches 1, 7\n>   - not updated: patches 4, part of 11\n>   - better alternative: patches 1, 3, 5, 6, 8, 9, 10, part of 11\n>   - already removed with just a stub left behind: patches 2, 3\n> \n> (Here I excluded patches from the \"not updated\" category if there was\n> also an additional rationale given in the commit message.  For other\n> rationales, I put the patch under each category that was brought up as\n> a reason for retirement.)\n> \n> So this series doesn't rely solely on the \"not updated\" rationale very\n> much.  Even in patch 4 you allude to the fact that you _suspect_ that\n> tool also falls under the \"broken tool\" category, and in patch 11, you\n> also argue that it should be handled differently if it's useful (which\n> rhymes with saying that a better alternative exists, but isn't the\n> same since one doesn't necessarily exist yet).\n\nYeah, I wouldn't categorize patch 4 into the \"not updated\" category.\nIt's the ecosystem it depends on that hasn't been updated, which isn't\nquite the same.\n\n> Anyway, I like the series, I'm just a little uncomfortable with this\n> part of the cover letter and the wording of some of the commit\n> messages.  \"not updated in 5 years\" is good supplemental information,\n> but I think other git contributors reading those commit messages might\n> get the wrong idea and apply it elsewhere.\n\nOkay, I'll try to rephrase both the cover letter and the indvidual\ncommit messages to make sufficiently clear that this is only used as\nsupporting evidence that something can be removed.\n\nPatrick\n"},{"id":"517838","messageId":"a3aaa11a-a842-4c10-8189-07b681663573@gmail.com","threadId":"63406","inReplyTo":"87jz6mo4n9.fsf@gmail.com","subject":"Re: [PATCH v2 04/11] contrib: remove \"thunderbird-patch-inline\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-05-12T13:02:43Z","receivedAt":"2025-05-12T13:02:47Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Collin\n\nOn 12/05/2025 05:05, Collin Funk wrote:\n> Hi,\n> \n> You said:\n> \n>> The \"thunderbird-patch-inline\" directory in \"contrib/\" contains a script\n>> to send patch files via Thunderbird. This script depends on the\n>> ExternalEditor extension [1], which seems to be effectively unmaintained\n>> with the last update being in 2008. While the extension has eventually\n>> been maintained in [2], that fork hasn't received any updates since\n>> 2020, either.\n>>\n>> In addition, the script itself hasn't really seen a lot of maintenance\n>> outside of a couple of global cleanups. It is quite unlikely that this\n>> setup still works, and if it did it's even less likely that somebody\n>> uses this script.\n> \n> FWIW, in case it helps any Thunderbird users, there is External Editor\n> Revived which seems to work pretty well when I used Thunderbird [1].\n\nThanks for pointing that out. I noticed that extensional the other day\nbut had not got around to posting it.\n\n> I think this script would be irrelevant with that extension though.\n\nUsers who want to post patches using thunderbird would still benefit\nfrom the script. It needs tweaking to account for a different\nseparator between the headers and message body though. The patch below\ndoes that.\n\nBest Wishes\n\nPhillip\n\n---- >8 ----\nFrom: Phillip Wood <phillip.wood@dunelm.org.uk>\nSubject: [PATCH] contrib: update thunderbird-patch-inline\n\nThis script uses an extension to run an external editor from\nthunderbird that is no-longer maintained. Replace it with a different\nextension that is maintained[1]. The new extension uses a different\nseparator between the mail headers and message body so update the\nscript to accommodate that.\n\nSigned-Off-By: Phillip Wood <phillip.wood@dunelm.org.uk>\n\n[1] https://github.com/Frederick888/external-editor-revived\n---\n\ndiff --git a/contrib/thunderbird-patch-inline/README b/contrib/thunderbird-patch-inline/README\nindex 000147bbe4a..aa00b379a60 100644\n--- a/contrib/thunderbird-patch-inline/README\n+++ b/contrib/thunderbird-patch-inline/README\n@@ -1,6 +1,6 @@\n-appp.sh is a script that is supposed to be used together with ExternalEditor\n-for Mozilla Thunderbird. It will let you include patches inline in e-mails\n-in an easy way.\n+appp.sh is a script that is supposed to be used together with\n+ExternalEditorRevived for Mozilla Thunderbird. It will let you include\n+patches inline in e-mails in an easy way.\n  \n  Usage:\n  - Generate the patch with git format-patch.\n@@ -14,7 +14,7 @@ will be moved to the section between the --- and the diffstat.\n  \n  All S-O-B:s and Cc:s in the patch will be added to the CC list.\n  \n-To set it up, just install External Editor and tell it to use appp.sh as the\n-editor.\n+To set it up, just install External Editor Revived and tell it to use\n+appp.sh as the editor.\n  \n  Zenity is a required dependency.\ndiff --git a/contrib/thunderbird-patch-inline/appp.sh b/contrib/thunderbird-patch-inline/appp.sh\nindex fdcc9483520..72c37aace4e 100755\n--- a/contrib/thunderbird-patch-inline/appp.sh\n+++ b/contrib/thunderbird-patch-inline/appp.sh\n@@ -1,10 +1,12 @@\n  #!/bin/sh\n  # Copyright 2008 Lukas Sandström <luksan@gmail.com>\n  #\n-# AppendPatch - A script to be used together with ExternalEditor\n+# AppendPatch - A script to be used together with ExternalEditorRevived\n  # for Mozilla Thunderbird to properly include patches inline in e-mails.\n  \n-# ExternalEditor can be downloaded at http://globs.org/articles.php?lng=en&pg=2\n+# ExternalEditorRevived extension can be installed using the Add-ons\n+# manager in thunderbird, the source is available at\n+# https://github.com/Frederick888/external-editor-revived\n  \n  CONFFILE=~/.appprc\n  \n@@ -25,8 +27,12 @@ fi\n  \n  cd - > /dev/null\n  \n+# The headers are separated from the message body by a blanks\n+# line. However the message uses CR LF line ending so on platforms\n+# where the native line ending is LF we see a line with a single CR.\n+SEP=\"$(printf '^\\r\\\\{0,1\\\\}$')\"\n  SUBJECT=$(sed -n -e '/^Subject: /p' \"${PATCH}\")\n-HEADERS=$(sed -e '/^'\"${SEP}\"'$/,$d' $1)\n+HEADERS=$(sed -e \"/${SEP}/\"',$d' $1)\n  BODY=$(sed -e \"1,/${SEP}/d\" $1)\n  CMT_MSG=$(sed -e '1,/^$/d' -e '/^---$/,$d' \"${PATCH}\")\n  DIFF=$(sed -e '1,/^---$/d' \"${PATCH}\")\n@@ -37,7 +43,7 @@ CCS=$(printf '%s\\n%s\\n' \"$CMT_MSG\" \"$HEADERS\" | sed -n -e 's/^Cc: \\(.*\\)$/\\1,/gp\n  echo \"$SUBJECT\" > $1\n  echo \"Cc: $CCS\" >> $1\n  echo \"$HEADERS\" | sed -e '/^Subject: /d' -e '/^Cc: /d' >> $1\n-echo \"$SEP\" >> $1\n+echo >> $1\n  \n  echo \"$CMT_MSG\" >> $1\n  echo \"---\" >> $1\n"},{"id":"517841","messageId":"73be767c-229d-4914-bc7f-52fff36a9e5f@gmail.com","threadId":"63406","inReplyTo":"CALnO6CDp3Kr_Ma49jzftN_sMkOU95xGZ0sMe2J2boG2pWggCzQ@mail.gmail.com","subject":"Re: [PATCH 00/10] Spring cleanup of \"contrib/\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-05-12T13:10:39Z","receivedAt":"2025-05-12T13:10:42Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 10/05/2025 21:07, D. Ben Knoble wrote:\n> On Tue, May 6, 2025 at 11:55 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>>\n>> On Tue, May 6, 2025 at 9:32 PM Todd Zullinger <tmz@pobox.com> wrote:\n>>> Eric Sunshine wrote:\n>>>> Although we periodically hear that someone uses it, git-contacts\n>>>> probably falls into the \"tool has a clear alternative\" category. In\n>>>> this case, git-related[*] is a separately-maintained more functional\n>>>> drop-in replacement which people could be using instead of\n>>>> git-contacts.\n\nI think there is a value in having git-contacts in our tree as it\nmeans that new contributors do not have to spend time installing an\nexternal tool just to find out who to Cc their patch to. Given that\nthe script works and we recommend it in our documentation for\ncontributors it would make sense to keep it. After our experience with\nthe maintainer of git-related I'd be wary of changing our\ndocumentation to recommend it instead of git-contacts.\n\nBest Wishes\n\nPhillip\n"},{"id":"517857","messageId":"aCIJhfw8Am_T0Khz@pks.im","threadId":"63406","inReplyTo":"a3aaa11a-a842-4c10-8189-07b681663573@gmail.com","subject":"Re: [PATCH v2 04/11] contrib: remove \"thunderbird-patch-inline\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-12T14:45:25Z","receivedAt":"2025-05-12T14:45:31Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, May 12, 2025 at 02:02:43PM +0100, Phillip Wood wrote:\n> Hi Collin\n> \n> On 12/05/2025 05:05, Collin Funk wrote:\n> > Hi,\n> > \n> > You said:\n> > \n> > > The \"thunderbird-patch-inline\" directory in \"contrib/\" contains a script\n> > > to send patch files via Thunderbird. This script depends on the\n> > > ExternalEditor extension [1], which seems to be effectively unmaintained\n> > > with the last update being in 2008. While the extension has eventually\n> > > been maintained in [2], that fork hasn't received any updates since\n> > > 2020, either.\n> > > \n> > > In addition, the script itself hasn't really seen a lot of maintenance\n> > > outside of a couple of global cleanups. It is quite unlikely that this\n> > > setup still works, and if it did it's even less likely that somebody\n> > > uses this script.\n> > \n> > FWIW, in case it helps any Thunderbird users, there is External Editor\n> > Revived which seems to work pretty well when I used Thunderbird [1].\n> \n> Thanks for pointing that out. I noticed that extensional the other day\n> but had not got around to posting it.\n> \n> > I think this script would be irrelevant with that extension though.\n> \n> Users who want to post patches using thunderbird would still benefit\n> from the script. It needs tweaking to account for a different\n> separator between the headers and message body though. The patch below\n> does that.\n\nThe question to me is whether we want that patch though. It is clear\nthat nobody has bothered in the past couple of years, so why would\nsomeone care now?\n\nIt's nice to have this patch on the list so that somebody searching\nfor discussions around the tool may find it. But I'd personally prefer\nto not carry around legacy code that nobody seems to be using and\nmaintaining.\n\nPatrick\n"},{"id":"517865","messageId":"xmqqfrh9vlxr.fsf@gitster.g","threadId":"63406","inReplyTo":"a3aaa11a-a842-4c10-8189-07b681663573@gmail.com","subject":"Re: [PATCH v2 04/11] contrib: remove \"thunderbird-patch-inline\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-05-12T16:22:08Z","receivedAt":"2025-05-12T16:22:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\nDue to 'text/plan; format=flowed', the attached patch was damaged so\nthe version I may be commenting on may be slightly different from\nwhat you wanted to show, but there is one thing I noticed.\n\n> diff --git a/contrib/thunderbird-patch-inline/appp.sh b/contrib/thunderbird-patch-inline/appp.sh\n> index fdcc9483520..72c37aace4e 100755\n> --- a/contrib/thunderbird-patch-inline/appp.sh\n> +++ b/contrib/thunderbird-patch-inline/appp.sh\n> @@ -1,10 +1,12 @@\n>  #!/bin/sh\n>  # Copyright 2008 Lukas Sandström <luksan@gmail.com>\n>  #\n> -# AppendPatch - A script to be used together with ExternalEditor\n> +# AppendPatch - A script to be used together with ExternalEditorRevived\n>  # for Mozilla Thunderbird to properly include patches inline in e-mails.\n> -# ExternalEditor can be downloaded at\n>    http://globs.org/articles.php?lng=en&pg=2\n> +# ExternalEditorRevived extension can be installed using the Add-ons\n> +# manager in thunderbird, the source is available at\n> +# https://github.com/Frederick888/external-editor-revived\n>    CONFFILE=~/.appprc\n>  @@ -25,8 +27,12 @@ fi\n>    cd - > /dev/null\n> +# The headers are separated from the message body by a blanks\n> +# line. However the message uses CR LF line ending so on platforms\n> +# where the native line ending is LF we see a line with a single CR.\n> +SEP=\"$(printf '^\\r\\\\{0,1\\\\}$')\"\n\nHere we assign to the variable SEP\n\n>  SUBJECT=$(sed -n -e '/^Subject: /p' \"${PATCH}\")\n> -HEADERS=$(sed -e '/^'\"${SEP}\"'$/,$d' $1)\n> +HEADERS=$(sed -e \"/${SEP}/\"',$d' $1)\n\nThe old reference must be expecting that the variable SEP should be\nalready set up.  I understand that a different separator is used in\nthe new version, so it is perfectly fine that HEADERS need to be\nchanged, but shouldn't the previous hunk that assign to SEP be\nremoving an old assignment to SEP that gave the separator wanted by\nthe older version?\n\nIt turns out that after the post-context of the first hunk there is\nassignment that the old separator value is assigned to SEP.  I think\nthat should go.\n\n>  BODY=$(sed -e \"1,/${SEP}/d\" $1)\n>  CMT_MSG=$(sed -e '1,/^$/d' -e '/^---$/,$d' \"${PATCH}\")\n>  DIFF=$(sed -e '1,/^---$/d' \"${PATCH}\")\n> @@ -37,7 +43,7 @@ CCS=$(printf '%s\\n%s\\n' \"$CMT_MSG\" \"$HEADERS\" | sed -n -e 's/^Cc: \\(.*\\)$/\\1,/gp\n>  echo \"$SUBJECT\" > $1\n>  echo \"Cc: $CCS\" >> $1\n>  echo \"$HEADERS\" | sed -e '/^Subject: /d' -e '/^Cc: /d' >> $1\n> -echo \"$SEP\" >> $1\n> +echo >> $1\n>    echo \"$CMT_MSG\" >> $1\n>  echo \"---\" >> $1\n"},{"id":"518049","messageId":"ed18e1e5-2941-4f00-9139-66bf95f0faa7@gmail.com","threadId":"63406","inReplyTo":"xmqqfrh9vlxr.fsf@gitster.g","subject":"Re: [PATCH v2 04/11] contrib: remove \"thunderbird-patch-inline\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-05-14T15:19:25Z","receivedAt":"2025-05-14T15:19:31Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 12/05/2025 17:22, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood123@gmail.com> writes:\n> \n> Due to 'text/plan; format=flowed', the attached patch was damaged so\n> the version I may be commenting on may be slightly different from\n> what you wanted to show, but there is one thing I noticed.\n\nOh dear, I need to find out how to turn that off.\n>> +SEP=\"$(printf '^\\r\\\\{0,1\\\\}$')\"\n> \n> Here we assign to the variable SEP\n> \n>>   SUBJECT=$(sed -n -e '/^Subject: /p' \"${PATCH}\")\n>> -HEADERS=$(sed -e '/^'\"${SEP}\"'$/,$d' $1)\n>> +HEADERS=$(sed -e \"/${SEP}/\"',$d' $1)\n> \n> The old reference must be expecting that the variable SEP should be\n> already set up.  I understand that a different separator is used in\n> the new version, so it is perfectly fine that HEADERS need to be\n> changed, but shouldn't the previous hunk that assign to SEP be\n> removing an old assignment to SEP that gave the separator wanted by\n> the older version?\n> \n> It turns out that after the post-context of the first hunk there is\n> assignment that the old separator value is assigned to SEP.  I think\n> that should go.\nGood point - for some reason I forgot to delete the original definition \n- I'll send a re-roll.\n\nThanks\n\nPhillip>\n>>   BODY=$(sed -e \"1,/${SEP}/d\" $1)\n>>   CMT_MSG=$(sed -e '1,/^$/d' -e '/^---$/,$d' \"${PATCH}\")\n>>   DIFF=$(sed -e '1,/^---$/d' \"${PATCH}\")\n>> @@ -37,7 +43,7 @@ CCS=$(printf '%s\\n%s\\n' \"$CMT_MSG\" \"$HEADERS\" | sed -n -e 's/^Cc: \\(.*\\)$/\\1,/gp\n>>   echo \"$SUBJECT\" > $1\n>>   echo \"Cc: $CCS\" >> $1\n>>   echo \"$HEADERS\" | sed -e '/^Subject: /d' -e '/^Cc: /d' >> $1\n>> -echo \"$SEP\" >> $1\n>> +echo >> $1\n>>     echo \"$CMT_MSG\" >> $1\n>>   echo \"---\" >> $1\n"},{"id":"518239","messageId":"20250516135540.218937-1-phillip.wood123@gmail.com","threadId":"63406","inReplyTo":"a3aaa11a-a842-4c10-8189-07b681663573@gmail.com","subject":"[PATCH v2] contrib: update thunderbird-patch-inline","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-05-16T13:53:55Z","receivedAt":"2025-05-16T13:55:55Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nThis script uses an extension to run an external editor from\nthunderbird that is no-longer maintained. Replace it with a different\nextension that is maintained[1]. The new extension uses a different\nseparator between the mail headers and message body so update the\nscript to accommodate that.\n\nSigned-Off-By: Phillip Wood <phillip.wood@dunelm.org.uk>\n\n[1] https://github.com/Frederick888/external-editor-revived\n---\nChanges since V1:\n\n  - removed the original definition of SEP (thanks to Junio for\n    pointing that out)\n\ncontrib/thunderbird-patch-inline/README  | 10 +++++-----\n contrib/thunderbird-patch-inline/appp.sh | 15 ++++++++++-----\n 2 files changed, 15 insertions(+), 10 deletions(-)\n\ndiff --git a/contrib/thunderbird-patch-inline/README b/contrib/thunderbird-patch-inline/README\nindex 000147bbe4a..aa00b379a60 100644\n--- a/contrib/thunderbird-patch-inline/README\n+++ b/contrib/thunderbird-patch-inline/README\n@@ -1,6 +1,6 @@\n-appp.sh is a script that is supposed to be used together with ExternalEditor\n-for Mozilla Thunderbird. It will let you include patches inline in e-mails\n-in an easy way.\n+appp.sh is a script that is supposed to be used together with\n+ExternalEditorRevived for Mozilla Thunderbird. It will let you include\n+patches inline in e-mails in an easy way.\n \n Usage:\n - Generate the patch with git format-patch.\n@@ -14,7 +14,7 @@ will be moved to the section between the --- and the diffstat.\n \n All S-O-B:s and Cc:s in the patch will be added to the CC list.\n \n-To set it up, just install External Editor and tell it to use appp.sh as the\n-editor.\n+To set it up, just install External Editor Revived and tell it to use\n+appp.sh as the editor.\n \n Zenity is a required dependency.\ndiff --git a/contrib/thunderbird-patch-inline/appp.sh b/contrib/thunderbird-patch-inline/appp.sh\nindex fdcc9483520..c4dd88024bd 100755\n--- a/contrib/thunderbird-patch-inline/appp.sh\n+++ b/contrib/thunderbird-patch-inline/appp.sh\n@@ -1,14 +1,15 @@\n #!/bin/sh\n # Copyright 2008 Lukas Sandström <luksan@gmail.com>\n #\n-# AppendPatch - A script to be used together with ExternalEditor\n+# AppendPatch - A script to be used together with ExternalEditorRevived\n # for Mozilla Thunderbird to properly include patches inline in e-mails.\n \n-# ExternalEditor can be downloaded at http://globs.org/articles.php?lng=en&pg=2\n+# ExternalEditorRevived extension can be installed using the Add-ons\n+# manager in thunderbird, the source is available at\n+# https://github.com/Frederick888/external-editor-revived\n \n CONFFILE=~/.appprc\n \n-SEP=\"-=-=-=-=-=-=-=-=-=# Don't remove this line #=-=-=-=-=-=-=-=-=-\"\n if [ -e \"$CONFFILE\" ] ; then\n \tLAST_DIR=$(grep -m 1 \"^LAST_DIR=\" \"${CONFFILE}\"|sed -e 's/^LAST_DIR=//')\n \tcd \"${LAST_DIR}\"\n@@ -25,8 +26,12 @@ fi\n \n cd - > /dev/null\n \n+# The headers are separated from the message body by a blanks\n+# line. However the message uses CR LF line ending so on platforms\n+# where the native line ending is LF we see a line with a single CR.\n+SEP=\"$(printf '^\\r\\\\{0,1\\\\}$')\"\n SUBJECT=$(sed -n -e '/^Subject: /p' \"${PATCH}\")\n-HEADERS=$(sed -e '/^'\"${SEP}\"'$/,$d' $1)\n+HEADERS=$(sed -e \"/${SEP}/\"',$d' $1)\n BODY=$(sed -e \"1,/${SEP}/d\" $1)\n CMT_MSG=$(sed -e '1,/^$/d' -e '/^---$/,$d' \"${PATCH}\")\n DIFF=$(sed -e '1,/^---$/d' \"${PATCH}\")\n@@ -37,7 +42,7 @@ CCS=$(printf '%s\\n%s\\n' \"$CMT_MSG\" \"$HEADERS\" | sed -n -e 's/^Cc: \\(.*\\)$/\\1,/gp\n echo \"$SUBJECT\" > $1\n echo \"Cc: $CCS\" >> $1\n echo \"$HEADERS\" | sed -e '/^Subject: /d' -e '/^Cc: /d' >> $1\n-echo \"$SEP\" >> $1\n+echo >> $1\n \n echo \"$CMT_MSG\" >> $1\n echo \"---\" >> $1\n-- \n2.49.0.897.gfad3eb7d210\n\n"},{"id":"518240","messageId":"2608da69-b554-4c45-b4b0-4b128e25c2a1@app.fastmail.com","threadId":"63406","inReplyTo":"20250516135540.218937-1-phillip.wood123@gmail.com","subject":"Re: [PATCH v2] contrib: update thunderbird-patch-inline","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-05-16T14:05:00Z","receivedAt":"2025-05-16T14:05:23Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Fri, May 16, 2025, at 15:53, Phillip Wood wrote:\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> This script uses an extension to run an external editor from\n> thunderbird that is no-longer maintained. Replace it with a different\n> extension that is maintained[1]. The new extension uses a different\n> separator between the mail headers and message body so update the\n> script to accommodate that.\n>\n> Signed-Off-By: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> [1] https://github.com/Frederick888/external-editor-revived\n> ---\n\nWhy the link after the signoff?\n"},{"id":"518375","messageId":"aCrD6RlO0xUmXr6i@pks.im","threadId":"63406","inReplyTo":"20250516135540.218937-1-phillip.wood123@gmail.com","subject":"Re: [PATCH v2] contrib: update thunderbird-patch-inline","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-05-19T05:38:49Z","receivedAt":"2025-05-19T05:38:55Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, May 16, 2025 at 02:53:55PM +0100, Phillip Wood wrote:\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n> \n> This script uses an extension to run an external editor from\n> thunderbird that is no-longer maintained. Replace it with a different\n> extension that is maintained[1]. The new extension uses a different\n> separator between the mail headers and message body so update the\n> script to accommodate that.\n> \n> Signed-Off-By: Phillip Wood <phillip.wood@dunelm.org.uk>\n> \n> [1] https://github.com/Frederick888/external-editor-revived\n> ---\n> Changes since V1:\n> \n>   - removed the original definition of SEP (thanks to Junio for\n>     pointing that out)\n\nWhat I still don't quite understand: who is this update for? The\nexisting script is broken for years and nobody showed interest so far to\nfix it. So I don't think it makes sense to fix the script just to keep\non dragging it with us.\n\nIf the answer is \"you\" then I'd be fine with it. But if the answer is\na hypothetical \"someone\" then I'd honestly prefer to just remove dead\nweight and not bother with this update.\n\nPatrick\n"},{"id":"518376","messageId":"87zff9jghp.fsf@gmail.com","threadId":"63406","inReplyTo":"aCrD6RlO0xUmXr6i@pks.im","subject":"Re: [PATCH v2] contrib: update thunderbird-patch-inline","fromName":"Collin Funk","fromEmail":"collin.funk1@gmail.com","sentAt":"2025-05-19T05:50:58Z","receivedAt":"2025-05-19T05:51:00Z","isPatch":true,"sender":{"key":"collin.funk1@gmail.com","avatar":"https://avatars.githubusercontent.com/u/65689063?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> What I still don't quite understand: who is this update for? The\n> existing script is broken for years and nobody showed interest so far to\n> fix it. So I don't think it makes sense to fix the script just to keep\n> on dragging it with us.\n>\n> If the answer is \"you\" then I'd be fine with it. But if the answer is\n> a hypothetical \"someone\" then I'd honestly prefer to just remove dead\n> weight and not bother with this update.\n\nWithout a further explanation, I agree.\n\nI feel like doing:\n\n    $ git format-patch -1\n    $ cat 0001-subject.patch | xclip\n\nAnd then pasting in the external editor opened by Thunderbird and\nremoving some headers is pretty simple. I'm not sure a script is really\nneeded for that.\n\nAnd who knows when ExternalEditorRevivedRevived will end up being\nneeded.\n\nCollin\n"},{"id":"518419","messageId":"e524f5ef-0def-44b8-81b4-a8a13ec5eace@gmail.com","threadId":"63406","inReplyTo":"aCrD6RlO0xUmXr6i@pks.im","subject":"Re: [PATCH v2] contrib: update thunderbird-patch-inline","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-05-19T14:21:04Z","receivedAt":"2025-05-19T14:21:08Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Patrick\n\nOn 19/05/2025 06:38, Patrick Steinhardt wrote:\n> On Fri, May 16, 2025 at 02:53:55PM +0100, Phillip Wood wrote:\n>> Changes since V1:\n>>\n>>    - removed the original definition of SEP (thanks to Junio for\n>>      pointing that out)\n> \n> What I still don't quite understand: who is this update for? The\n> existing script is broken for years and nobody showed interest so far to\n> fix it. So I don't think it makes sense to fix the script just to keep\n> on dragging it with us.\n> \n> If the answer is \"you\" then I'd be fine with it. But if the answer is\n> a hypothetical \"someone\" then I'd honestly prefer to just remove dead\n> weight and not bother with this update.\n\nPersonally I don't mind that much either way as I think using the \n\"toggle line wrap\" is more convenient for including patches in a mail \n(though that unfortunately only works with the current esr release and \nearlier). I agree with your previous mail that having this patch on the \nmailing list could be useful for anyone searching for this in the future \nso I re-rolled to fix the issue that Junio pointed out.\n\nNote directly related to this series but looking at the format-patch \ndocumentation in mentions the \"external editor\" plugin which has been \nsuperseded by \"external editor revived\" and \"toggle word wrap\" which has \nbeen superseded by \"toggle line wrap\" so we should probably update that.\n\nBest Wishes\n\nPhillip\n"},{"id":"518429","messageId":"xmqqo6voob35.fsf@gitster.g","threadId":"63406","inReplyTo":"aCrD6RlO0xUmXr6i@pks.im","subject":"Re: [PATCH v2] contrib: update thunderbird-patch-inline","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-05-19T15:48:46Z","receivedAt":"2025-05-19T15:48:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> What I still don't quite understand: who is this update for? The\n> existing script is broken for years and nobody showed interest so far to\n> fix it.\n\nWhat we can safely say from that is that those who tried either\nfailed and gave up, or tweaked for themselves and did not bother\nspending cycles to upstream their fix.\n\n> So I don't think it makes sense to fix the script just to keep\n> on dragging it with us.\n\nSo I think that is a bit narrow-sighted.\n"},{"id":"519601","messageId":"xmqq7c1sh3u1.fsf@gitster.g","threadId":"63406","inReplyTo":"e524f5ef-0def-44b8-81b4-a8a13ec5eace@gmail.com","subject":"Re: [PATCH v2] contrib: update thunderbird-patch-inline","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-03T22:12:38Z","receivedAt":"2025-06-03T22:12:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> Hi Patrick\n>\n> On 19/05/2025 06:38, Patrick Steinhardt wrote:\n>> On Fri, May 16, 2025 at 02:53:55PM +0100, Phillip Wood wrote:\n>>> Changes since V1:\n>>>\n>>>    - removed the original definition of SEP (thanks to Junio for\n>>>      pointing that out)\n>> What I still don't quite understand: who is this update for? The\n>> existing script is broken for years and nobody showed interest so far to\n>> fix it. So I don't think it makes sense to fix the script just to keep\n>> on dragging it with us.\n>> If the answer is \"you\" then I'd be fine with it. But if the answer\n>> is\n>> a hypothetical \"someone\" then I'd honestly prefer to just remove dead\n>> weight and not bother with this update.\n>\n> Personally I don't mind that much either way as I think using the\n> \"toggle line wrap\" is more convenient for including patches in a mail\n> (though that unfortunately only works with the current esr release and\n> earlier). I agree with your previous mail that having this patch on\n> the mailing list could be useful for anyone searching for this in the\n> future so I re-rolled to fix the issue that Junio pointed out.\n\nAs the discussion seems to have died out here, shall I drop this\npatch out of my tree?\n\n> Note directly related to this series but looking at the format-patch\n> documentation in mentions the \"external editor\" plugin which has been\n> superseded by \"external editor revived\" and \"toggle word wrap\" which\n> has been superseded by \"toggle line wrap\" so we should probably update\n> that.\n\nAnother documentation patch may be in order either way, though.  I\nam not a Thunderbird user myself so I'll let others who _do_ care\nabout it write one.\n\nThanks.\n\n"}]}