{"thread":{"id":"64048","subject":"[PATCH 0/4] you-still-use-that??: improve breaking changes troubleshooting","startedAt":"2025-08-27T16:30:07Z","lastAt":"2025-09-18T20:28:54Z","messageCount":102,"participants":["kristofferhaugsbakk@fastmail.com","Junio C Hamano","Kristoffer Haugsbakk","Eric Sunshine","Jeff King","Patrick Steinhardt"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"525052","messageId":"cover.1756311355.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":null,"subject":"[PATCH 0/4] you-still-use-that??: improve breaking changes troubleshooting","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-08-27T16:29:45Z","receivedAt":"2025-08-27T16:30:07Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nBased on the recent i-still-use-that reports about whatchanged, improve\nthe error reporting with this command in mind:\n\n1. Give more possible actions instead of just (only) asking them to send\n   an email\n2. Hint how to replace their git-whatchanged(1) use with git-log(1)\n3. Minor documentation changes\n\nI don’t know how much does matters now that 2.51.0 is out.  I guess it\ndepends on when the next maintenance release is and how many platforms\nwill upgrade it?  I don’t know anything about that.  (But maybe they\nwill do it straight away since they are releasing for the latest\nversion?)\n\nKristoffer Haugsbakk (4):\n  usage: help the user help themselves\n  whatchanged: tell users the git-log(1) equivalent\n  whatchanged: remove not-even-shorter clause\n  BreakingChanges: remove claim about whatchanged reports\n\n Documentation/BreakingChanges.adoc |  2 +-\n Documentation/git-whatchanged.adoc |  8 ++++++--\n builtin/log.c                      |  8 ++++++--\n builtin/pack-redundant.c           |  2 +-\n git-compat-util.h                  |  2 +-\n usage.c                            | 31 +++++++++++++++++++++++-------\n 6 files changed, 39 insertions(+), 14 deletions(-)\n\n\nbase-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0\n-- \n2.51.0.11.g23cedd8a747\n\n"},{"id":"525053","messageId":"e81023edb2d78d2be0ecffc071f2c5316b0c7a32.1756311355.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1756311355.git.code@khaugsbakk.name","subject":"[PATCH 1/4] usage: help the user help themselves","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-08-27T16:29:46Z","receivedAt":"2025-08-27T16:30:26Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nGive the user a list of suggestions for what to do when they run a\ndeprecated command.\n\nThe first order of action will be to check the breaking changes\ndocument;[1] this short error message says nothing about why this\ncommand is deprecated, and in any case going into any kind of detail\nmight overwhelm the user.\n\nThen they can find out if this has been discussed on the mailing list.\nThen users who e.g. are using git-whatchanged(1) can learn that this is\narguably a plug-in replacement:\n\n    git log <opts> --raw --no-merges\n\nFinally they are invited to send an email to the mailing list.\n\nAlso drop the “please add” part in favor of just using the “refusing”\ndie-message; these two would have been right after each other in this\nnew version.\n\nAlso drop “Thanks” since it now would require a new paragraph.\n\n[1]: git-scm has a disclaimer for these internal documents that says\n    that “This information is specific to the Git project”.  That’s\n    misleading in this particular case.  But users are unlikely to get\n    discouraged from reading about why they (or their programs) cannot\n    run a command any more; it clearly concerns them.\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    An alternative to linking to git-scm is to move this document to a\n    regular installed man page:\n    \n        gitbreaking-changes(7)\n    \n    What do you think?\n    \n    I would then have to base my topic on the in-flight\n    pw/3.0-commentchar-auto-deprecation, which in turn depends on\n    ps/config-wo-the-repository.\n    \n    Or just wait a bit for these to settle in.\n\n usage.c | 21 ++++++++++++++++-----\n 1 file changed, 16 insertions(+), 5 deletions(-)\n\ndiff --git a/usage.c b/usage.c\nindex 81913236a4a..29988395f19 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -7,6 +7,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"trace2.h\"\n+#include \"strbuf.h\"\n \n static void vfreportf(FILE *f, const char *prefix, const char *err, va_list params)\n {\n@@ -377,12 +378,22 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n \n NORETURN void you_still_use_that(const char *command_name)\n {\n+\tstruct strbuf percent_encoded = STRBUF_INIT;\n+\tstrbuf_add_percentencode(&percent_encoded,\n+\t\t\t\t command_name,\n+\t\t\t\t STRBUF_ENCODE_SLASH);\n+\n \tfprintf(stderr,\n \t\t_(\"'%s' is nominated for removal.\\n\"\n-\t\t  \"If you still use this command, please add an extra\\n\"\n-\t\t  \"option, '--i-still-use-this', on the command line\\n\"\n-\t\t  \"and let us know you still use it by sending an e-mail\\n\"\n-\t\t  \"to <git@vger.kernel.org>.  Thanks.\\n\"),\n-\t\tcommand_name);\n+\t\t  \"If you still use this command, here's what you can do:\\n\"\n+\t\t  \"\\n\"\n+\t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n+\t\t  \"- check if anyone has discussed this on the mailing\\n\"\n+\t\t  \"  list and if they came up with something that can\\n\"\n+\t\t  \"  help you: https://lore.kernel.org/git/?q=%s\\n\"\n+\t\t  \"- send an email to <git@vger.kernel.org>\\n\"\n+\t\t  \"\\n\"),\n+\t\tcommand_name, percent_encoded.buf);\n+\tstrbuf_release(&percent_encoded);\n \tdie(_(\"refusing to run without --i-still-use-this\"));\n }\n-- \n2.51.0.11.g23cedd8a747\n\n"},{"id":"525054","messageId":"5407c0955af7b5bb30fbc32264481990b2e4e8b8.1756311355.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1756311355.git.code@khaugsbakk.name","subject":"[PATCH 2/4] whatchanged: tell users the git-log(1) equivalent","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-08-27T16:29:47Z","receivedAt":"2025-08-27T16:30:45Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThere have been quite a few `--i-still-use-this` user reports since Git\n2.51.0 was released.[1][2]  And it doesn’t seem like they are reading\nthe man page about the git-log(1) equivalent.\n\nTell them what options to plug into git-log(1).  That template produces\nalmost the same output[3] and is arguably a plug-in replacement.\nConcretely, add an optional `hint` argument so that we can use it right\nafter the initial error line.\n\nAlso mention the same concrete options in the documentation while we’re\nat it.\n\n[1]: E.g.,\n    • https://lore.kernel.org/git/e1a69dea-bcb6-45fc-83d3-9e50d32c410b@5y5.one/\n    • https://lore.kernel.org/git/1011073f-9930-4360-a42f-71eb7421fe3f@chrispalmer.uk/#t\n    • https://lore.kernel.org/git/9fcbfcc4-79f9-421f-b9a4-dc455f7db485@acm.org/#t\n    • https://lore.kernel.org/git/83241BDE-1E0D-489A-9181-C608E9FCC17B@gmail.com/\n[2] The error message on 2.51.0 does tell them to report it, unconditionally\n[3]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n Documentation/git-whatchanged.adoc |  6 +++++-\n builtin/log.c                      |  8 ++++++--\n builtin/pack-redundant.c           |  2 +-\n git-compat-util.h                  |  2 +-\n usage.c                            | 14 ++++++++++----\n 5 files changed, 23 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-whatchanged.adoc b/Documentation/git-whatchanged.adoc\nindex d21484026fe..e71d2aa2d27 100644\n--- a/Documentation/git-whatchanged.adoc\n+++ b/Documentation/git-whatchanged.adoc\n@@ -24,7 +24,11 @@ Shows commit logs and diff output each commit introduces.\n \n New users are encouraged to use linkgit:git-log[1] instead.  The\n `whatchanged` command is essentially the same as linkgit:git-log[1]\n-but defaults to showing the raw format diff output and skipping merges.\n+but defaults to showing the raw format diff output and skipping merges:\n+\n+----\n+git log --raw --no-merges\n+----\n \n The command is primarily kept for historical reasons; fingers of\n many people who learned Git long before `git log` was invented by\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c2f8bbf8630..2f9e5e5a898 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -542,8 +542,12 @@ int cmd_whatchanged(int argc,\n \topt.revarg_opt = REVARG_COMMITTISH;\n \tcmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);\n \n-\tif (!cfg.i_still_use_this)\n-\t\tyou_still_use_that(\"git whatchanged\");\n+        if (!cfg.i_still_use_this)\n+\t\tyou_still_use_that(\"git whatchanged\",\n+\t\t\t\t   _(\"\\n\"\n+\t\t\t\t     \"hint: You can replace 'git whatchanged <opts>' with:\\n\"\n+\t\t\t\t     \"    git log <opts> --raw --no-merges\\n\"\n+\t\t\t\t     \"\\n\"));\n \n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\ndiff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c\nindex fe81c293e3a..5d5ae4afa28 100644\n--- a/builtin/pack-redundant.c\n+++ b/builtin/pack-redundant.c\n@@ -626,7 +626,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED, s\n \t}\n \n \tif (!i_still_use_this)\n-\t\tyou_still_use_that(\"git pack-redundant\");\n+\t\tyou_still_use_that(\"git pack-redundant\", NULL);\n \n \tif (load_all_packs)\n \t\tload_all();\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 9408f463e31..398e0fac4fa 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -460,7 +460,7 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));\n \n void show_usage_if_asked(int ac, const char **av, const char *err);\n \n-NORETURN void you_still_use_that(const char *command_name);\n+NORETURN void you_still_use_that(const char *command_name, const char *hint);\n \n #ifndef NO_OPENSSL\n #ifdef APPLE_COMMON_CRYPTO\ndiff --git a/usage.c b/usage.c\nindex 29988395f19..c661561b149 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -376,7 +376,8 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n \tva_end(ap);\n }\n \n-NORETURN void you_still_use_that(const char *command_name)\n+\n+NORETURN void you_still_use_that(const char *command_name, const char *hint)\n {\n \tstruct strbuf percent_encoded = STRBUF_INIT;\n \tstrbuf_add_percentencode(&percent_encoded,\n@@ -384,8 +385,13 @@ NORETURN void you_still_use_that(const char *command_name)\n \t\t\t\t STRBUF_ENCODE_SLASH);\n \n \tfprintf(stderr,\n-\t\t_(\"'%s' is nominated for removal.\\n\"\n-\t\t  \"If you still use this command, here's what you can do:\\n\"\n+\t\t_(\"'%s' is nominated for removal.\\n\"), command_name);\n+\n+\tif (hint)\n+\t\tfputs(hint, stderr);\n+\n+\tfprintf(stderr,\n+\t\t_(\"If you still use this command, here's what you can do:\\n\"\n \t\t  \"\\n\"\n \t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n \t\t  \"- check if anyone has discussed this on the mailing\\n\"\n@@ -393,7 +399,7 @@ NORETURN void you_still_use_that(const char *command_name)\n \t\t  \"  help you: https://lore.kernel.org/git/?q=%s\\n\"\n \t\t  \"- send an email to <git@vger.kernel.org>\\n\"\n \t\t  \"\\n\"),\n-\t\tcommand_name, percent_encoded.buf);\n+\t\tpercent_encoded.buf);\n \tstrbuf_release(&percent_encoded);\n \tdie(_(\"refusing to run without --i-still-use-this\"));\n }\n-- \n2.51.0.11.g23cedd8a747\n\n"},{"id":"525055","messageId":"5fad164d7d1333feabe644a3df1d491648001f9b.1756311355.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1756311355.git.code@khaugsbakk.name","subject":"[PATCH 3/4] whatchanged: remove not-even-shorter clause","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-08-27T16:29:48Z","receivedAt":"2025-08-27T16:31:04Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThe closest equivalent is `git log --raw --no-merges`.\n\nAlso change to “defaults” (implicit plural).\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n Documentation/git-whatchanged.adoc | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-whatchanged.adoc b/Documentation/git-whatchanged.adoc\nindex e71d2aa2d27..436e219b7d0 100644\n--- a/Documentation/git-whatchanged.adoc\n+++ b/Documentation/git-whatchanged.adoc\n@@ -15,7 +15,7 @@ WARNING\n -------\n `git whatchanged` has been deprecated and is scheduled for removal in\n a future version of Git, as it is merely `git log` with different\n-default; `whatchanged` is not even shorter to type than `log --raw`.\n+defaults.\n \n DESCRIPTION\n -----------\n-- \n2.51.0.11.g23cedd8a747\n\n"},{"id":"525056","messageId":"f1bf0ea3846852eb8df0d0d0f6cba72a493e8c1b.1756311355.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1756311355.git.code@khaugsbakk.name","subject":"[PATCH 4/4] BreakingChanges: remove claim about whatchanged reports","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-08-27T16:29:49Z","receivedAt":"2025-08-27T16:31:22Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThis was written in e836757e14b (whatschanged: list it in\nBreakingChanges document, 2025-05-12) which was on the same\ntopic that added the `--i-still-use-this` requirement.[1]\n\nMaybe it was a work-in-progress comment/status.\n\n[1]: jc/you-still-use-whatchanged\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    Footnote solely to avoid awkward paragraph wrapping...\n\n Documentation/BreakingChanges.adoc | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc\nindex f8d2eba061c..c4985163c3c 100644\n--- a/Documentation/BreakingChanges.adoc\n+++ b/Documentation/BreakingChanges.adoc\n@@ -235,7 +235,7 @@ These features will be removed.\n   equivalent `git log --raw`.  We have nominated the command for\n   removal, have changed the command to refuse to work unless the\n   `--i-still-use-this` option is given, and asked the users to report\n-  when they do so.  So far there hasn't been a single complaint.\n+  when they do so.\n +\n The command will be removed.\n \n-- \n2.51.0.11.g23cedd8a747\n\n"},{"id":"525058","messageId":"xmqqa53k3eb5.fsf@gitster.g","threadId":"64048","inReplyTo":"cover.1756311355.git.code@khaugsbakk.name","subject":"Re: [PATCH 0/4] you-still-use-that??: improve breaking changes troubleshooting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-27T16:43:58Z","receivedAt":"2025-08-27T16:44:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"kristofferhaugsbakk@fastmail.com writes:\n\n> From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n>\n> Based on the recent i-still-use-that reports about whatchanged, improve\n> the error reporting with this command in mind:\n>\n> 1. Give more possible actions instead of just (only) asking them to send\n>    an email\n> 2. Hint how to replace their git-whatchanged(1) use with git-log(1)\n> 3. Minor documentation changes\n\nNice.  Especially the attention to minor details shown in [3/4] and [4/4]\nis very much appreciated.\n\n"},{"id":"525059","messageId":"xmqq5xe83e8q.fsf@gitster.g","threadId":"64048","inReplyTo":"5407c0955af7b5bb30fbc32264481990b2e4e8b8.1756311355.git.code@khaugsbakk.name","subject":"Re: [PATCH 2/4] whatchanged: tell users the git-log(1) equivalent","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-27T16:45:25Z","receivedAt":"2025-08-27T16:45:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"kristofferhaugsbakk@fastmail.com writes:\n\n> -\tif (!cfg.i_still_use_this)\n> -\t\tyou_still_use_that(\"git whatchanged\");\n> +        if (!cfg.i_still_use_this)\n> +\t\tyou_still_use_that(\"git whatchanged\",\n\nI spot a whitespace breakage here.  I didn't check if there are\nother instances.  Please check your editor settings (or run \"git\ndiff --check --cached\" before committing yoru changes, or both).\n\nThanks.\n"},{"id":"525060","messageId":"xmqq1pow3e4b.fsf@gitster.g","threadId":"64048","inReplyTo":"xmqq5xe83e8q.fsf@gitster.g","subject":"Re: [PATCH 2/4] whatchanged: tell users the git-log(1) equivalent","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-27T16:48:04Z","receivedAt":"2025-08-27T16:48:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> kristofferhaugsbakk@fastmail.com writes:\n>\n>> -\tif (!cfg.i_still_use_this)\n>> -\t\tyou_still_use_that(\"git whatchanged\");\n>> +        if (!cfg.i_still_use_this)\n>> +\t\tyou_still_use_that(\"git whatchanged\",\n>\n> I spot a whitespace breakage here.  I didn't check if there are\n\nSorry, I read the patch backwards.  You are fixing an existing\nwhitespace breakage, which is very much appreciated, as this is\nimmediate vicinity of the real change of this topic.\n\nThanks.\n\n"},{"id":"525063","messageId":"28875b20-91d9-44ed-bad1-d0a7aedfbfbd@app.fastmail.com","threadId":"64048","inReplyTo":"xmqq1pow3e4b.fsf@gitster.g","subject":"Re: [PATCH 2/4] whatchanged: tell users the git-log(1) equivalent","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-08-27T17:08:43Z","receivedAt":"2025-08-27T17:09:51Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Wed, Aug 27, 2025, at 18:48, Junio C Hamano wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> kristofferhaugsbakk@fastmail.com writes:\n>>\n>>> -\tif (!cfg.i_still_use_this)\n>>> -\t\tyou_still_use_that(\"git whatchanged\");\n>>> +        if (!cfg.i_still_use_this)\n>>> +\t\tyou_still_use_that(\"git whatchanged\",\n>>\n>> I spot a whitespace breakage here.  I didn't check if there are\n>\n> Sorry, I read the patch backwards.  You are fixing an existing\n> whitespace breakage, which is very much appreciated, as this is\n> immediate vicinity of the real change of this topic.\n\nNo, I did inadvertently replace the tab indents with spaces.  I’ll have\nto configure my editor better.  I’ll fix it.\n\n(`ci/check-whitespace.sh v2.51.0` turned out to be helpful for me here)\n\n-- \nKristoffer Haugsbakk\n\n"},{"id":"525074","messageId":"9611573e-fb0b-49d5-933a-50d8e0603701@app.fastmail.com","threadId":"64048","inReplyTo":"e81023edb2d78d2be0ecffc071f2c5316b0c7a32.1756311355.git.code@khaugsbakk.name","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2025-08-27T20:36:04Z","receivedAt":"2025-08-27T20:36:25Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"> usage: help the user help themselves\n\nI think I’ll change the area to the more pointed:\n\n    you-still-use-that??:\n\nOn Wed, Aug 27, 2025, at 18:29, kristofferhaugsbakk@fastmail.com wrote:\n> @@ -377,12 +378,22 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n>\n>  NORETURN void you_still_use_that(const char *command_name)\n>  {\n> +\tstruct strbuf percent_encoded = STRBUF_INIT;\n> +\tstrbuf_add_percentencode(&percent_encoded,\n> +\t\t\t\t command_name,\n> +\t\t\t\t STRBUF_ENCODE_SLASH);\n> +\n>  \tfprintf(stderr,\n>  \t\t_(\"'%s' is nominated for removal.\\n\"\n> -\t\t  \"If you still use this command, please add an extra\\n\"\n> -\t\t  \"option, '--i-still-use-this', on the command line\\n\"\n> -\t\t  \"and let us know you still use it by sending an e-mail\\n\"\n> -\t\t  \"to <git@vger.kernel.org>.  Thanks.\\n\"),\n> -\t\tcommand_name);\n> +\t\t  \"If you still use this command, here's what you can do:\\n\"\n> +\t\t  \"\\n\"\n> +\t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n> +\t\t  \"- check if anyone has discussed this on the mailing\\n\"\n> +\t\t  \"  list and if they came up with something that can\\n\"\n> +\t\t  \"  help you: https://lore.kernel.org/git/?q=%s\\n\"\n> +\t\t  \"- send an email to <git@vger.kernel.org>\\n\"\n\nMaybe (thinking out loud) this should retain some part of the “let us\nknow you still use this” spirit:\n\n    - send an email to <git@vger.kernel.org> and let us know\n      that you still use this command\n\n> +\t\t  \"\\n\"),\n> +\t\tcommand_name, percent_encoded.buf);\n> +\tstrbuf_release(&percent_encoded);\n>  \tdie(_(\"refusing to run without --i-still-use-this\"));\n>  }\n> --\n> 2.51.0.11.g23cedd8a747\n"},{"id":"525076","messageId":"CAPig+cRgBXX+b=P31VjQ6Dd4mciFvaUJ4T1oeOGjk7mwV-9KMw@mail.gmail.com","threadId":"64048","inReplyTo":"9611573e-fb0b-49d5-933a-50d8e0603701@app.fastmail.com","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-08-27T21:02:28Z","receivedAt":"2025-08-27T21:02:40Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Aug 27, 2025 at 4:36 PM Kristoffer Haugsbakk\n<code@khaugsbakk.name> wrote:\n> On Wed, Aug 27, 2025, at 18:29, kristofferhaugsbakk@fastmail.com wrote:\n> >       fprintf(stderr,\n> >               _(\"'%s' is nominated for removal.\\n\"\n> > +               \"If you still use this command, here's what you can do:\\n\"\n> > +               \"\\n\"\n> > +               \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n> > +               \"- check if anyone has discussed this on the mailing\\n\"\n> > +               \"  list and if they came up with something that can\\n\"\n> > +               \"  help you: https://lore.kernel.org/git/?q=%s\\n\"\n> > +               \"- send an email to <git@vger.kernel.org>\\n\"\n>\n> Maybe (thinking out loud) this should retain some part of the “let us\n> know you still use this” spirit:\n>\n>     - send an email to <git@vger.kernel.org> and let us know\n>       that you still use this command\n\nThat's still inviting unnecessary emails, isn't it? It would probably\nbe better add the qualification that people should send the email only\nif they were unable to find any workable replacement. Perhaps:\n\n    - send an email to <git@...> to let us know\n      that you still use this command and were unable\n      to determine a suitable replacement\n"},{"id":"525077","messageId":"xmqq7byozcv1.fsf@gitster.g","threadId":"64048","inReplyTo":"9611573e-fb0b-49d5-933a-50d8e0603701@app.fastmail.com","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-27T21:14:10Z","receivedAt":"2025-08-27T21:14:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Kristoffer Haugsbakk\" <code@khaugsbakk.name> writes:\n\n>> usage: help the user help themselves\n>\n> I think I’ll change the area to the more pointed:\n>\n>     you-still-use-that??:\n\nAh, that makes sense.\n\n> On Wed, Aug 27, 2025, at 18:29, kristofferhaugsbakk@fastmail.com wrote:\n>> @@ -377,12 +378,22 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n>>\n>>  NORETURN void you_still_use_that(const char *command_name)\n>>  {\n>> +\tstruct strbuf percent_encoded = STRBUF_INIT;\n>> +\tstrbuf_add_percentencode(&percent_encoded,\n>> +\t\t\t\t command_name,\n>> +\t\t\t\t STRBUF_ENCODE_SLASH);\n>> +\n>>  \tfprintf(stderr,\n>>  \t\t_(\"'%s' is nominated for removal.\\n\"\n>> -\t\t  \"If you still use this command, please add an extra\\n\"\n>> -\t\t  \"option, '--i-still-use-this', on the command line\\n\"\n>> -\t\t  \"and let us know you still use it by sending an e-mail\\n\"\n>> -\t\t  \"to <git@vger.kernel.org>.  Thanks.\\n\"),\n>> -\t\tcommand_name);\n>> +\t\t  \"If you still use this command, here's what you can do:\\n\"\n>> +\t\t  \"\\n\"\n>> +\t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n>> +\t\t  \"- check if anyone has discussed this on the mailing\\n\"\n>> +\t\t  \"  list and if they came up with something that can\\n\"\n>> +\t\t  \"  help you: https://lore.kernel.org/git/?q=%s\\n\"\n>> +\t\t  \"- send an email to <git@vger.kernel.org>\\n\"\n>\n> Maybe (thinking out loud) this should retain some part of the “let us\n> know you still use this” spirit:\n>\n>     - send an email to <git@vger.kernel.org> and let us know\n>       that you still use this command\n\nWe do not actually want to bother individual users about reporting.\n\nWe may want to catch third-party tools (like we heard a problem with\nJenkins from its users), so\n\n    - notify <git@vger.kernel.org> mailing list if you are a lead\n      developer of a widely used tool, especially if you heard a\n      breakage report on your tool from your users due to this\n      message.\n\nperhaps?  If we know that version A of tool X (or older) hasn't\nmigrated, users of the same tool will see the same breakage, and\nsome of them may come to this list.  If we know about their problem\nahead of time, we'd be better prepared to give them a definitive\n\"Yes, we know version A of tool X (or older) still uses it, and the\ndeveloper of tool X is aware of the problem.\"\n\nHopefully ;-)\n"},{"id":"525078","messageId":"xmqq349czcl8.fsf@gitster.g","threadId":"64048","inReplyTo":"CAPig+cRgBXX+b=P31VjQ6Dd4mciFvaUJ4T1oeOGjk7mwV-9KMw@mail.gmail.com","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-27T21:20:03Z","receivedAt":"2025-08-27T21:20:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> That's still inviting unnecessary emails, isn't it? It would probably\n> be better add the qualification that people should send the email only\n> if they were unable to find any workable replacement. Perhaps:\n>\n>     - send an email to <git@...> to let us know\n>       that you still use this command and were unable\n>       to determine a suitable replacement\n\nIn practice, people will respond to such an instruction by always\nsending an e-mail.  Asking others who sound as if they are promising\nto give answers when asked is cheaper than investigating themselves\n;-).\n"},{"id":"525079","messageId":"CAPig+cQkVP57n_FE6dJ0uxvai-J7usxKFp8gzfEbPY=Ytsd6=Q@mail.gmail.com","threadId":"64048","inReplyTo":"xmqq349czcl8.fsf@gitster.g","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-08-27T21:27:15Z","receivedAt":"2025-08-27T21:27:28Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Aug 27, 2025 at 5:20 PM Junio C Hamano <gitster@pobox.com> wrote:\n> Eric Sunshine <sunshine@sunshineco.com> writes:\n> > That's still inviting unnecessary emails, isn't it? It would probably\n> > be better add the qualification that people should send the email only\n> > if they were unable to find any workable replacement. Perhaps:\n> >\n> >     - send an email to <git@...> to let us know\n> >       that you still use this command and were unable\n> >       to determine a suitable replacement\n>\n> In practice, people will respond to such an instruction by always\n> sending an e-mail.  Asking others who sound as if they are promising\n> to give answers when asked is cheaper than investigating themselves\n\nThanks for pointing out that my final editing made my suggested\nwording too succinct. What I really had in mind -- in conjunction with\nKristoffer's patch [2/4] which provides hint(s) for replacing the\ncommand being retired -- was to reference the provided hints. So,\nsomething like this:\n\n  - send an email to <...> to let us know\n    that you still use this command and were unable\n    to determine a suitable replacement using the hints\n    provided here\n"},{"id":"525081","messageId":"xmqqy0r4xuy7.fsf@gitster.g","threadId":"64048","inReplyTo":"CAPig+cQkVP57n_FE6dJ0uxvai-J7usxKFp8gzfEbPY=Ytsd6=Q@mail.gmail.com","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-27T22:26:24Z","receivedAt":"2025-08-27T22:26:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> command being retired -- was to reference the provided hints. So,\n> something like this:\n>\n>   - send an email to <...> to let us know\n>     that you still use this command and were unable\n>     to determine a suitable replacement using the hints\n>     provided here\n\nAh, of course, yes, with Kristoffer's update to tell what the\nalternative is, your phrasing is perfect.\n\nThanks.\n"},{"id":"525087","messageId":"fde74862-a5f8-495e-aacd-59429fe5cd20@app.fastmail.com","threadId":"64048","inReplyTo":"xmqqy0r4xuy7.fsf@gitster.g","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2025-08-28T06:39:55Z","receivedAt":"2025-08-28T06:40:17Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Thu, Aug 28, 2025, at 00:26, Junio C Hamano wrote:\n> Eric Sunshine <sunshine@sunshineco.com> writes:\n>\n>> command being retired -- was to reference the provided hints. So,\n>> something like this:\n>>\n>>   - send an email to <...> to let us know\n>>     that you still use this command and were unable\n>>     to determine a suitable replacement using the hints\n>>     provided here\n>\n> Ah, of course, yes, with Kristoffer's update to tell what the\n> alternative is, your phrasing is perfect.\n\nI think so too.  Just keep in mind that pack-redundant does not have\nthat part.\n\nI think I’ll use that suggestion verbatim.\n\n-- \nKristoffer\n\n"},{"id":"525098","messageId":"97e67403-7f46-4555-8edc-9fc1c1109bfd@app.fastmail.com","threadId":"64048","inReplyTo":"5407c0955af7b5bb30fbc32264481990b2e4e8b8.1756311355.git.code@khaugsbakk.name","subject":"Re: [PATCH 2/4] whatchanged: tell users the git-log(1) equivalent","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2025-08-28T12:07:11Z","receivedAt":"2025-08-28T12:07:32Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Wed, Aug 27, 2025, at 18:29, kristofferhaugsbakk@fastmail.com wrote:\n> From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n>[snip]\n> [1]: E.g.,\n>     •\n> https://lore.kernel.org/git/e1a69dea-bcb6-45fc-83d3-9e50d32c410b@5y5.one/\n>     •\n> https://lore.kernel.org/git/1011073f-9930-4360-a42f-71eb7421fe3f@chrispalmer.uk/#t\n>     •\n> https://lore.kernel.org/git/9fcbfcc4-79f9-421f-b9a4-dc455f7db485@acm.org/#t\n>     •\n> https://lore.kernel.org/git/83241BDE-1E0D-489A-9181-C608E9FCC17B@gmail.com/\n> [2] The error message on 2.51.0 does tell them to report it, unconditionally\n\nMissing colon.\n\n> [3]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n\nI’ll expand on this.\n\n    [3]: You only get different outputs if you happen to have empty\n         commits (no changes)[4]\n    [4]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n\n-- \nKristoffer\n"},{"id":"525114","messageId":"xmqqldn3xz3h.fsf@gitster.g","threadId":"64048","inReplyTo":"fde74862-a5f8-495e-aacd-59429fe5cd20@app.fastmail.com","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-28T15:09:06Z","receivedAt":"2025-08-28T15:09:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Kristoffer Haugsbakk\" <code@khaugsbakk.name> writes:\n\n> On Thu, Aug 28, 2025, at 00:26, Junio C Hamano wrote:\n>> Eric Sunshine <sunshine@sunshineco.com> writes:\n>>\n>>> command being retired -- was to reference the provided hints. So,\n>>> something like this:\n>>>\n>>>   - send an email to <...> to let us know\n>>>     that you still use this command and were unable\n>>>     to determine a suitable replacement using the hints\n>>>     provided here\n>>\n>> Ah, of course, yes, with Kristoffer's update to tell what the\n>> alternative is, your phrasing is perfect.\n>\n> I think so too.  Just keep in mind that pack-redundant does not have\n> that part.\n\nFor pack-redundant, isn't it because there is no need to find\nsuitable replacement?  That is, instead of finding a redundant pack,\nall of whose objects are contained in some other packs, which\npractically is impossible to exist in a non-toy repository, and deal\nwith it yourself, you can let the normal \"repack\" to eject redundant\nobjects from all the non-kept packs.\n\n> I think I’ll use that suggestion verbatim.\n\nSounds good.\n"},{"id":"525209","messageId":"cover.1756480827.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1756311355.git.code@khaugsbakk.name","subject":"[PATCH v2 0/4] you-still-use-that??: improve breaking changes troubleshooting","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-08-29T15:21:02Z","receivedAt":"2025-08-29T15:21:36Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nBased on the recent i-still-use-that reports about whatchanged, improve\nthe error reporting with this command in mind:\n\n1. Give more possible actions instead of just (only) asking them to send\n   an email\n2. Hint how to replace their git-whatchanged(1) use with git-log(1)\n3. Minor documentation changes\n\nAlso:[1] an alternative to linking to www.git-scm.com (in patch 1/4) is\nto move this document to a regular installed man page:\n\n    gitbreaking-changes(7)\n\n[1]: Not mentioned on the v1 cover letter\n\n§ What the errors now look like\n\n    $ ./git pack-redundant\n    'git pack-redundant' is nominated for removal.\n    If you still use this command, here's what you can do:\n\n    - read https://git-scm.com/docs/BreakingChanges.html\n    - check if anyone has discussed this on the mailing\n      list and if they came up with something that can\n      help you: https://lore.kernel.org/git/?q=git%20pack-redundant\n    - send an email to <git@vger.kernel.org> to let us\n      know that you still use this command and were unable\n      to determine a suitable replacement\n\n    fatal: refusing to run without --i-still-use-this\n    $ ./git whatchanged\n    'git whatchanged' is nominated for removal.\n\n    hint: You can replace 'git whatchanged <opts>' with:\n        git log <opts> --raw --no-merges\n\n    If you still use this command, here's what you can do:\n\n    - read https://git-scm.com/docs/BreakingChanges.html\n    - check if anyone has discussed this on the mailing\n      list and if they came up with something that can\n      help you: https://lore.kernel.org/git/?q=git%20whatchanged\n    - send an email to <git@vger.kernel.org> to let us\n      know that you still use this command and were unable\n      to determine a suitable replacement\n\n    fatal: refusing to run without --i-still-use-this\n\n§ Changes in v2\n\nThese are also posted on the patches.\n\n• Patch 1/4:\n  • Change send-an-email bullet point\n  • Change the area\n  • Use www.git-scm.com, not simply git-scm\n• Patch 2/4:\n  • Fix whitespace error (I should have used `ci/check-whitespace.sh\n    v2.51.0`)\n  • Add missing colon (:) to footnote\n  • Expand on footnote; a sentence is enough to summarize the difference\n\nKristoffer Haugsbakk (4):\n  you-still-use-that??: help the user help themselves\n  whatchanged: tell users the git-log(1) equivalent\n  whatchanged: remove not-even-shorter clause\n  BreakingChanges: remove claim about whatchanged reports\n\n Documentation/BreakingChanges.adoc |  2 +-\n Documentation/git-whatchanged.adoc |  8 ++++++--\n builtin/log.c                      |  6 +++++-\n builtin/pack-redundant.c           |  2 +-\n git-compat-util.h                  |  2 +-\n usage.c                            | 33 +++++++++++++++++++++++-------\n 6 files changed, 40 insertions(+), 13 deletions(-)\n\nInterdiff against v1:\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 2f9e5e5a898..5dbb90c014d 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -542,7 +542,7 @@ int cmd_whatchanged(int argc,\n \topt.revarg_opt = REVARG_COMMITTISH;\n \tcmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);\n \n-        if (!cfg.i_still_use_this)\n+\tif (!cfg.i_still_use_this)\n \t\tyou_still_use_that(\"git whatchanged\",\n \t\t\t\t   _(\"\\n\"\n \t\t\t\t     \"hint: You can replace 'git whatchanged <opts>' with:\\n\"\ndiff --git a/usage.c b/usage.c\nindex c661561b149..7545a616453 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -397,7 +397,9 @@ NORETURN void you_still_use_that(const char *command_name, const char *hint)\n \t\t  \"- check if anyone has discussed this on the mailing\\n\"\n \t\t  \"  list and if they came up with something that can\\n\"\n \t\t  \"  help you: https://lore.kernel.org/git/?q=%s\\n\"\n-\t\t  \"- send an email to <git@vger.kernel.org>\\n\"\n+\t\t  \"- send an email to <git@vger.kernel.org> to let us\\n\"\n+\t\t  \"  know that you still use this command and were unable\\n\"\n+\t\t  \"  to determine a suitable replacement\\n\"\n \t\t  \"\\n\"),\n \t\tpercent_encoded.buf);\n \tstrbuf_release(&percent_encoded);\nRange-diff against v1:\n1:  e81023edb2d ! 1:  6803e2cc6c3 usage: help the user help themselves\n    @@ Metadata\n     Author: Kristoffer Haugsbakk <code@khaugsbakk.name>\n     \n      ## Commit message ##\n    -    usage: help the user help themselves\n    +    you-still-use-that??: help the user help themselves\n     \n         Give the user a list of suggestions for what to do when they run a\n         deprecated command.\n    @@ Commit message\n     \n         Also drop “Thanks” since it now would require a new paragraph.\n     \n    -    [1]: git-scm has a disclaimer for these internal documents that says\n    -        that “This information is specific to the Git project”.  That’s\n    +    [1]: www.git-scm.com has a disclaimer for these internal documents that\n    +        says that “This information is specific to the Git project”.  That’s\n             misleading in this particular case.  But users are unlikely to get\n    -        discouraged from reading about why they (or their programs) cannot\n    -        run a command any more; it clearly concerns them.\n    +        discouraged from reading about why they (or their programs) cannot run a\n    +        command any more; it clearly concerns them.\n     \n    +    Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n         Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n     \n     \n      ## Notes (series) ##\n    -    An alternative to linking to git-scm is to move this document to a\n    -    regular installed man page:\n    +    v2:\n    +\n    +    I had second thoughts about the bullet point about send-an-email.\n    +    Change it to the one Eric Sunshine proposed;[1] make sure to spell\n    +    out that you can send an email conditioned on not finding a suitable\n    +    replacement.\n    +\n    +    Also change the area to something more pointed.\n    +\n    +    And also use a clear URL to refer to www.git-scm.com.\n    +\n    +    [1]: https://lore.kernel.org/git/CAPig+cQkVP57n_FE6dJ0uxvai-J7usxKFp8gzfEbPY=Ytsd6=Q@mail.gmail.com/\n    +\n    +    • Change send-an-email bullet point\n    +    • Change the area\n    +    • Use www.git-scm.com, not simply git-scm\n    +\n    +    v1:\n    +\n    +    An alternative to linking to www.git-scm.com is to move this document to\n    +    a regular installed man page:\n     \n             gitbreaking-changes(7)\n     \n    @@ usage.c: void bug_fl(const char *file, int line, const char *fmt, ...)\n     +\t\t  \"- check if anyone has discussed this on the mailing\\n\"\n     +\t\t  \"  list and if they came up with something that can\\n\"\n     +\t\t  \"  help you: https://lore.kernel.org/git/?q=%s\\n\"\n    -+\t\t  \"- send an email to <git@vger.kernel.org>\\n\"\n    ++\t\t  \"- send an email to <git@vger.kernel.org> to let us\\n\"\n    ++\t\t  \"  know that you still use this command and were unable\\n\"\n    ++\t\t  \"  to determine a suitable replacement\\n\"\n     +\t\t  \"\\n\"),\n     +\t\tcommand_name, percent_encoded.buf);\n     +\tstrbuf_release(&percent_encoded);\n2:  5407c0955af ! 2:  2f3ac952980 whatchanged: tell users the git-log(1) equivalent\n    @@ Commit message\n             • https://lore.kernel.org/git/1011073f-9930-4360-a42f-71eb7421fe3f@chrispalmer.uk/#t\n             • https://lore.kernel.org/git/9fcbfcc4-79f9-421f-b9a4-dc455f7db485@acm.org/#t\n             • https://lore.kernel.org/git/83241BDE-1E0D-489A-9181-C608E9FCC17B@gmail.com/\n    -    [2] The error message on 2.51.0 does tell them to report it, unconditionally\n    -    [3]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n    +    [2]: The error message on 2.51.0 does tell them to report it, unconditionally\n    +    [3]: You only get different outputs if you happen to have empty\n    +         commits (no changes)[4]\n    +    [4]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n     \n         Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n     \n    +\n    + ## Notes (series) ##\n    +    v2:\n    +\n    +    Review found a whitespace error in the prev. patch version.  I found a\n    +    broken footnote and wanted to expand on the last footnote.\n    +\n    +    • Fix whitespace error (I should have used `ci/check-whitespace.sh\n    +      v2.51.0`)\n    +    • Add missing colon (:) to footnote\n    +    • Expand on footnote; a sentence is enough to summarize the difference\n    +\n      ## Documentation/git-whatchanged.adoc ##\n     @@ Documentation/git-whatchanged.adoc: Shows commit logs and diff output each commit introduces.\n      \n    @@ Documentation/git-whatchanged.adoc: Shows commit logs and diff output each commi\n     \n      ## builtin/log.c ##\n     @@ builtin/log.c: int cmd_whatchanged(int argc,\n    - \topt.revarg_opt = REVARG_COMMITTISH;\n      \tcmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);\n      \n    --\tif (!cfg.i_still_use_this)\n    + \tif (!cfg.i_still_use_this)\n     -\t\tyou_still_use_that(\"git whatchanged\");\n    -+        if (!cfg.i_still_use_this)\n     +\t\tyou_still_use_that(\"git whatchanged\",\n     +\t\t\t\t   _(\"\\n\"\n     +\t\t\t\t     \"hint: You can replace 'git whatchanged <opts>' with:\\n\"\n    @@ usage.c: NORETURN void you_still_use_that(const char *command_name)\n      \t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n      \t\t  \"- check if anyone has discussed this on the mailing\\n\"\n     @@ usage.c: NORETURN void you_still_use_that(const char *command_name)\n    - \t\t  \"  help you: https://lore.kernel.org/git/?q=%s\\n\"\n    - \t\t  \"- send an email to <git@vger.kernel.org>\\n\"\n    + \t\t  \"  know that you still use this command and were unable\\n\"\n    + \t\t  \"  to determine a suitable replacement\\n\"\n      \t\t  \"\\n\"),\n     -\t\tcommand_name, percent_encoded.buf);\n     +\t\tpercent_encoded.buf);\n3:  5fad164d7d1 = 3:  a074e7be422 whatchanged: remove not-even-shorter clause\n4:  f1bf0ea3846 = 4:  9196c3c7e33 BreakingChanges: remove claim about whatchanged reports\n\nbase-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525210","messageId":"6803e2cc6c32d8ef6b7e6ceadc9e1b1a9d17d7b0.1756480827.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1756480827.git.code@khaugsbakk.name","subject":"[PATCH v2 1/4] you-still-use-that??: help the user help themselves","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-08-29T15:21:03Z","receivedAt":"2025-08-29T15:21:55Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nGive the user a list of suggestions for what to do when they run a\ndeprecated command.\n\nThe first order of action will be to check the breaking changes\ndocument;[1] this short error message says nothing about why this\ncommand is deprecated, and in any case going into any kind of detail\nmight overwhelm the user.\n\nThen they can find out if this has been discussed on the mailing list.\nThen users who e.g. are using git-whatchanged(1) can learn that this is\narguably a plug-in replacement:\n\n    git log <opts> --raw --no-merges\n\nFinally they are invited to send an email to the mailing list.\n\nAlso drop the “please add” part in favor of just using the “refusing”\ndie-message; these two would have been right after each other in this\nnew version.\n\nAlso drop “Thanks” since it now would require a new paragraph.\n\n[1]: www.git-scm.com has a disclaimer for these internal documents that\n    says that “This information is specific to the Git project”.  That’s\n    misleading in this particular case.  But users are unlikely to get\n    discouraged from reading about why they (or their programs) cannot run a\n    command any more; it clearly concerns them.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v2:\n    \n    I had second thoughts about the bullet point about send-an-email.\n    Change it to the one Eric Sunshine proposed;[1] make sure to spell\n    out that you can send an email conditioned on not finding a suitable\n    replacement.\n    \n    Also change the area to something more pointed.\n    \n    And also use a clear URL to refer to www.git-scm.com.\n    \n    [1]: https://lore.kernel.org/git/CAPig+cQkVP57n_FE6dJ0uxvai-J7usxKFp8gzfEbPY=Ytsd6=Q@mail.gmail.com/\n    \n    • Change send-an-email bullet point\n    • Change the area\n    • Use www.git-scm.com, not simply git-scm\n    \n    v1:\n    \n    An alternative to linking to www.git-scm.com is to move this document to\n    a regular installed man page:\n    \n        gitbreaking-changes(7)\n    \n    What do you think?\n    \n    I would then have to base my topic on the in-flight\n    pw/3.0-commentchar-auto-deprecation, which in turn depends on\n    ps/config-wo-the-repository.\n    \n    Or just wait a bit for these to settle in.\n\n usage.c | 23 ++++++++++++++++++-----\n 1 file changed, 18 insertions(+), 5 deletions(-)\n\ndiff --git a/usage.c b/usage.c\nindex 81913236a4a..35dc57eb07e 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -7,6 +7,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"trace2.h\"\n+#include \"strbuf.h\"\n \n static void vfreportf(FILE *f, const char *prefix, const char *err, va_list params)\n {\n@@ -377,12 +378,24 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n \n NORETURN void you_still_use_that(const char *command_name)\n {\n+\tstruct strbuf percent_encoded = STRBUF_INIT;\n+\tstrbuf_add_percentencode(&percent_encoded,\n+\t\t\t\t command_name,\n+\t\t\t\t STRBUF_ENCODE_SLASH);\n+\n \tfprintf(stderr,\n \t\t_(\"'%s' is nominated for removal.\\n\"\n-\t\t  \"If you still use this command, please add an extra\\n\"\n-\t\t  \"option, '--i-still-use-this', on the command line\\n\"\n-\t\t  \"and let us know you still use it by sending an e-mail\\n\"\n-\t\t  \"to <git@vger.kernel.org>.  Thanks.\\n\"),\n-\t\tcommand_name);\n+\t\t  \"If you still use this command, here's what you can do:\\n\"\n+\t\t  \"\\n\"\n+\t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n+\t\t  \"- check if anyone has discussed this on the mailing\\n\"\n+\t\t  \"  list and if they came up with something that can\\n\"\n+\t\t  \"  help you: https://lore.kernel.org/git/?q=%s\\n\"\n+\t\t  \"- send an email to <git@vger.kernel.org> to let us\\n\"\n+\t\t  \"  know that you still use this command and were unable\\n\"\n+\t\t  \"  to determine a suitable replacement\\n\"\n+\t\t  \"\\n\"),\n+\t\tcommand_name, percent_encoded.buf);\n+\tstrbuf_release(&percent_encoded);\n \tdie(_(\"refusing to run without --i-still-use-this\"));\n }\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525211","messageId":"2f3ac952980cb982a68086d1c30400d2e2ba2185.1756480827.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1756480827.git.code@khaugsbakk.name","subject":"[PATCH v2 2/4] whatchanged: tell users the git-log(1) equivalent","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-08-29T15:21:04Z","receivedAt":"2025-08-29T15:22:14Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThere have been quite a few `--i-still-use-this` user reports since Git\n2.51.0 was released.[1][2]  And it doesn’t seem like they are reading\nthe man page about the git-log(1) equivalent.\n\nTell them what options to plug into git-log(1).  That template produces\nalmost the same output[3] and is arguably a plug-in replacement.\nConcretely, add an optional `hint` argument so that we can use it right\nafter the initial error line.\n\nAlso mention the same concrete options in the documentation while we’re\nat it.\n\n[1]: E.g.,\n    • https://lore.kernel.org/git/e1a69dea-bcb6-45fc-83d3-9e50d32c410b@5y5.one/\n    • https://lore.kernel.org/git/1011073f-9930-4360-a42f-71eb7421fe3f@chrispalmer.uk/#t\n    • https://lore.kernel.org/git/9fcbfcc4-79f9-421f-b9a4-dc455f7db485@acm.org/#t\n    • https://lore.kernel.org/git/83241BDE-1E0D-489A-9181-C608E9FCC17B@gmail.com/\n[2]: The error message on 2.51.0 does tell them to report it, unconditionally\n[3]: You only get different outputs if you happen to have empty\n     commits (no changes)[4]\n[4]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v2:\n    \n    Review found a whitespace error in the prev. patch version.  I found a\n    broken footnote and wanted to expand on the last footnote.\n    \n    • Fix whitespace error (I should have used `ci/check-whitespace.sh\n      v2.51.0`)\n    • Add missing colon (:) to footnote\n    • Expand on footnote; a sentence is enough to summarize the difference\n\n Documentation/git-whatchanged.adoc |  6 +++++-\n builtin/log.c                      |  6 +++++-\n builtin/pack-redundant.c           |  2 +-\n git-compat-util.h                  |  2 +-\n usage.c                            | 14 ++++++++++----\n 5 files changed, 22 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-whatchanged.adoc b/Documentation/git-whatchanged.adoc\nindex d21484026fe..e71d2aa2d27 100644\n--- a/Documentation/git-whatchanged.adoc\n+++ b/Documentation/git-whatchanged.adoc\n@@ -24,7 +24,11 @@ Shows commit logs and diff output each commit introduces.\n \n New users are encouraged to use linkgit:git-log[1] instead.  The\n `whatchanged` command is essentially the same as linkgit:git-log[1]\n-but defaults to showing the raw format diff output and skipping merges.\n+but defaults to showing the raw format diff output and skipping merges:\n+\n+----\n+git log --raw --no-merges\n+----\n \n The command is primarily kept for historical reasons; fingers of\n many people who learned Git long before `git log` was invented by\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c2f8bbf8630..5dbb90c014d 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -543,7 +543,11 @@ int cmd_whatchanged(int argc,\n \tcmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);\n \n \tif (!cfg.i_still_use_this)\n-\t\tyou_still_use_that(\"git whatchanged\");\n+\t\tyou_still_use_that(\"git whatchanged\",\n+\t\t\t\t   _(\"\\n\"\n+\t\t\t\t     \"hint: You can replace 'git whatchanged <opts>' with:\\n\"\n+\t\t\t\t     \"    git log <opts> --raw --no-merges\\n\"\n+\t\t\t\t     \"\\n\"));\n \n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\ndiff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c\nindex fe81c293e3a..5d5ae4afa28 100644\n--- a/builtin/pack-redundant.c\n+++ b/builtin/pack-redundant.c\n@@ -626,7 +626,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED, s\n \t}\n \n \tif (!i_still_use_this)\n-\t\tyou_still_use_that(\"git pack-redundant\");\n+\t\tyou_still_use_that(\"git pack-redundant\", NULL);\n \n \tif (load_all_packs)\n \t\tload_all();\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 9408f463e31..398e0fac4fa 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -460,7 +460,7 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));\n \n void show_usage_if_asked(int ac, const char **av, const char *err);\n \n-NORETURN void you_still_use_that(const char *command_name);\n+NORETURN void you_still_use_that(const char *command_name, const char *hint);\n \n #ifndef NO_OPENSSL\n #ifdef APPLE_COMMON_CRYPTO\ndiff --git a/usage.c b/usage.c\nindex 35dc57eb07e..7545a616453 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -376,7 +376,8 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n \tva_end(ap);\n }\n \n-NORETURN void you_still_use_that(const char *command_name)\n+\n+NORETURN void you_still_use_that(const char *command_name, const char *hint)\n {\n \tstruct strbuf percent_encoded = STRBUF_INIT;\n \tstrbuf_add_percentencode(&percent_encoded,\n@@ -384,8 +385,13 @@ NORETURN void you_still_use_that(const char *command_name)\n \t\t\t\t STRBUF_ENCODE_SLASH);\n \n \tfprintf(stderr,\n-\t\t_(\"'%s' is nominated for removal.\\n\"\n-\t\t  \"If you still use this command, here's what you can do:\\n\"\n+\t\t_(\"'%s' is nominated for removal.\\n\"), command_name);\n+\n+\tif (hint)\n+\t\tfputs(hint, stderr);\n+\n+\tfprintf(stderr,\n+\t\t_(\"If you still use this command, here's what you can do:\\n\"\n \t\t  \"\\n\"\n \t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n \t\t  \"- check if anyone has discussed this on the mailing\\n\"\n@@ -395,7 +401,7 @@ NORETURN void you_still_use_that(const char *command_name)\n \t\t  \"  know that you still use this command and were unable\\n\"\n \t\t  \"  to determine a suitable replacement\\n\"\n \t\t  \"\\n\"),\n-\t\tcommand_name, percent_encoded.buf);\n+\t\tpercent_encoded.buf);\n \tstrbuf_release(&percent_encoded);\n \tdie(_(\"refusing to run without --i-still-use-this\"));\n }\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525212","messageId":"a074e7be4222fa2d3b92db5e5f6eca1daf2e1867.1756480827.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1756480827.git.code@khaugsbakk.name","subject":"[PATCH v2 3/4] whatchanged: remove not-even-shorter clause","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-08-29T15:21:05Z","receivedAt":"2025-08-29T15:22:32Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThe closest equivalent is `git log --raw --no-merges`.\n\nAlso change to “defaults” (implicit plural).\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n Documentation/git-whatchanged.adoc | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-whatchanged.adoc b/Documentation/git-whatchanged.adoc\nindex e71d2aa2d27..436e219b7d0 100644\n--- a/Documentation/git-whatchanged.adoc\n+++ b/Documentation/git-whatchanged.adoc\n@@ -15,7 +15,7 @@ WARNING\n -------\n `git whatchanged` has been deprecated and is scheduled for removal in\n a future version of Git, as it is merely `git log` with different\n-default; `whatchanged` is not even shorter to type than `log --raw`.\n+defaults.\n \n DESCRIPTION\n -----------\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525213","messageId":"9196c3c7e33dbd7027096cf7c7bf342869ee2c57.1756480827.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1756480827.git.code@khaugsbakk.name","subject":"[PATCH v2 4/4] BreakingChanges: remove claim about whatchanged reports","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-08-29T15:21:06Z","receivedAt":"2025-08-29T15:22:51Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThis was written in e836757e14b (whatschanged: list it in\nBreakingChanges document, 2025-05-12) which was on the same\ntopic that added the `--i-still-use-this` requirement.[1]\n\nMaybe it was a work-in-progress comment/status.\n\n[1]: jc/you-still-use-whatchanged\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    Footnote solely to avoid awkward paragraph wrapping...\n\n Documentation/BreakingChanges.adoc | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc\nindex f8d2eba061c..c4985163c3c 100644\n--- a/Documentation/BreakingChanges.adoc\n+++ b/Documentation/BreakingChanges.adoc\n@@ -235,7 +235,7 @@ These features will be removed.\n   equivalent `git log --raw`.  We have nominated the command for\n   removal, have changed the command to refuse to work unless the\n   `--i-still-use-this` option is given, and asked the users to report\n-  when they do so.  So far there hasn't been a single complaint.\n+  when they do so.\n +\n The command will be removed.\n \n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525448","messageId":"CAPig+cSL=-gD5+WomF7-hYjVJ_PH0m+0i8g3F=E_U3k=QNHr8Q@mail.gmail.com","threadId":"64048","inReplyTo":"CAPig+cQkVP57n_FE6dJ0uxvai-J7usxKFp8gzfEbPY=Ytsd6=Q@mail.gmail.com","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-09-03T16:50:58Z","receivedAt":"2025-09-03T16:51:11Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Aug 27, 2025 at 5:27 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Wed, Aug 27, 2025 at 5:20 PM Junio C Hamano <gitster@pobox.com> wrote:\n> > Eric Sunshine <sunshine@sunshineco.com> writes:\n> > > That's still inviting unnecessary emails, isn't it? It would probably\n> > > be better add the qualification that people should send the email only\n> > > if they were unable to find any workable replacement. Perhaps:\n> > >\n> > >     - send an email to <git@...> to let us know\n> > >       that you still use this command and were unable\n> > >       to determine a suitable replacement\n> >\n> > In practice, people will respond to such an instruction by always\n> > sending an e-mail.  Asking others who sound as if they are promising\n> > to give answers when asked is cheaper than investigating themselves\n>\n> Thanks for pointing out that my final editing made my suggested\n> wording too succinct. What I really had in mind -- in conjunction with\n> Kristoffer's patch [2/4] which provides hint(s) for replacing the\n> command being retired -- was to reference the provided hints. So,\n> something like this:\n>\n>   - send an email to <...> to let us know\n>     that you still use this command and were unable\n>     to determine a suitable replacement using the hints\n>     provided here\n\nI realize that the changes made by this series are not in any released\nversion yet, but from reading the emails still arriving which argue\nfor retaining the command for reasons of muscle memory or because of\nits (strong) mnemonic value, I suspect that the hint(s) this series\nadds may not be complete enough. In particular, the advice this series\nadds (use `git log --raw --no-merges`) seems to be primarily aimed at\nscripted use of the command. But the muscle memory and mnemonic\narguments suggest that advice should be given for interactive use, as\nwell, such as proposing that the user can create an alias.\n"},{"id":"525451","messageId":"15bb8d07-675b-4ccd-8345-ab5861319faf@app.fastmail.com","threadId":"64048","inReplyTo":"CAPig+cSL=-gD5+WomF7-hYjVJ_PH0m+0i8g3F=E_U3k=QNHr8Q@mail.gmail.com","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-03T17:53:32Z","receivedAt":"2025-09-03T17:54:12Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"(Sent from mobile, might have mistakes)\n\nOn Wed, Sep 3, 2025, at 18:50, Eric Sunshine wrote:\n> I realize that the changes made by this series are not in any released\n> version yet, but from reading the emails still arriving which argue\n> for retaining the command for reasons of muscle memory or because of\n> its (strong) mnemonic value, I suspect that the hint(s) this series\n> adds may not be complete enough. In particular, the advice this series\n> adds (use `git log --raw --no-merges`) seems to be primarily aimed at\n> scripted use of the command. But the muscle memory and mnemonic\n> arguments suggest that advice should be given for interactive use, as\n> well, such as proposing that the user can create an alias.\n\nGood point. I would suggest discussing it in the \nbreaking changes doc under a new \"For Users\"\nsection. I would like to avoid expanding the error\nmessage too much due to the already mentioned\nfear of overwhelming folks.\n\nI've seen  \"troubleshooting\" questions from git users\nwho got one of those long and well-described Hints\nlike e.g. detached head and default git init branch\nwhen either the msg already spelled eveything out\nor the msg was purely informational.\n\nCheers\n"},{"id":"525462","messageId":"CAPig+cQdwr-a_B++=2+q4mV8oZ7VLTYEQ9wnVP84jFYkTHFtHw@mail.gmail.com","threadId":"64048","inReplyTo":"15bb8d07-675b-4ccd-8345-ab5861319faf@app.fastmail.com","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-09-03T21:21:47Z","receivedAt":"2025-09-03T21:21:59Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Sep 3, 2025 at 1:54 PM Kristoffer Haugsbakk\n<kristofferhaugsbakk@fastmail.com> wrote:\n> On Wed, Sep 3, 2025, at 18:50, Eric Sunshine wrote:\n> > I realize that the changes made by this series are not in any released\n> > version yet, but from reading the emails still arriving which argue\n> > for retaining the command for reasons of muscle memory or because of\n> > its (strong) mnemonic value, I suspect that the hint(s) this series\n> > adds may not be complete enough. In particular, the advice this series\n> > adds (use `git log --raw --no-merges`) seems to be primarily aimed at\n> > scripted use of the command. But the muscle memory and mnemonic\n> > arguments suggest that advice should be given for interactive use, as\n> > well, such as proposing that the user can create an alias.\n>\n> Good point. I would suggest discussing it in the\n> breaking changes doc under a new \"For Users\"\n> section. I would like to avoid expanding the error\n> message too much due to the already mentioned\n> fear of overwhelming folks.\n\nI have doubts that users will consult a document when presented with\nthe message.\n\n> I've seen  \"troubleshooting\" questions from git users\n> who got one of those long and well-described Hints\n> like e.g. detached head and default git init branch\n> when either the msg already spelled eveything out\n> or the msg was purely informational.\n\nI didn't spell it out above, but what I had in mind was something very\nsimple... not at all ong and detailed; for instance:\n\n   For interactive use, define a Git alias `git whatchanged`\n   which runs `git log --raw --no-merges`.\n"},{"id":"525465","messageId":"f7f5ba15-c475-414f-9377-0f9c630a8796@app.fastmail.com","threadId":"64048","inReplyTo":"CAPig+cQdwr-a_B++=2+q4mV8oZ7VLTYEQ9wnVP84jFYkTHFtHw@mail.gmail.com","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-03T21:41:49Z","receivedAt":"2025-09-03T21:42:39Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Wed, Sep 3, 2025, at 23:21, Eric Sunshine wrote:\n>> I've seen  \"troubleshooting\" questions from git users\n>> who got one of those long and well-described Hints\n>> like e.g. detached head and default git init branch\n>> when either the msg already spelled eveything out\n>> or the msg was purely informational.\n>\n> I didn't spell it out above, but what I had in mind was something very\n> simple... not at all ong and detailed; for instance:\n>\n>    For interactive use, define a Git alias `git whatchanged`\n>    which runs `git log --raw --no-merges`.\n\nBut that won’t work since git-whatchanged(1) is a builtin and you cannot\nuse an alias to shadow a builtin.  Now you can suggest minor variations\nlike `whatchange`.  But then people will go back to talking about their\nfinger memory.[1][2][3]\n\nYou would need Peff’s:\n\nhttps://lore.kernel.org/git/20250830022718.GB567900@coredump.intra.peff.net/\n\n> I wonder if we should loosen the \"aliases cannot override builtins\" rule\n> for deprecated commands. Perhaps something like the patch below.\n\n† 1: If people are adamant about a command staying exactly as it is for\n    interactive use, I don’t imagine their tolerance for a slightly\n    different invocation will be much higher.\n† 2: And I wonder about the audience who has been using a command which\n    was deprecated for twelve years who also need to be reminded about\n    aliases.\n† 3: But there is one `whatchange` conversion: https://lore.kernel.org/git/CAAn3O_2iHVt5TctvwLLSXm5Nw2wS8e9Xk0is1=k=-qRS=gHVMQ@mail.gmail.com/\n\n-- \nKristoffer Haugsbakk\n"},{"id":"525466","messageId":"20250903214415.GA1915988@coredump.intra.peff.net","threadId":"64048","inReplyTo":"CAPig+cQdwr-a_B++=2+q4mV8oZ7VLTYEQ9wnVP84jFYkTHFtHw@mail.gmail.com","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-03T21:44:15Z","receivedAt":"2025-09-03T21:44:18Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Sep 03, 2025 at 05:21:47PM -0400, Eric Sunshine wrote:\n\n> > I've seen  \"troubleshooting\" questions from git users\n> > who got one of those long and well-described Hints\n> > like e.g. detached head and default git init branch\n> > when either the msg already spelled eveything out\n> > or the msg was purely informational.\n> \n> I didn't spell it out above, but what I had in mind was something very\n> simple... not at all ong and detailed; for instance:\n> \n>    For interactive use, define a Git alias `git whatchanged`\n>    which runs `git log --raw --no-merges`.\n\nI think that is good advice, but... it won't do anything until we\nactually drop the whatchanged command, since until then we'll refuse to\noverride the command (even the crippled --i-still-use-this one).\n\nWe'd need something like the patch here:\n\n  https://lore.kernel.org/git/20250830022718.GB567900@coredump.intra.peff.net/\n\n-Peff\n"},{"id":"525471","messageId":"CAPig+cTN9vwoquOKCRz9TXRLtwHaxF-mL6xbbXxUmoHxZkW5rA@mail.gmail.com","threadId":"64048","inReplyTo":"20250903214415.GA1915988@coredump.intra.peff.net","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-09-03T22:11:30Z","receivedAt":"2025-09-03T22:11:44Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Sep 3, 2025 at 5:44 PM Jeff King <peff@peff.net> wrote:\n> On Wed, Sep 03, 2025 at 05:21:47PM -0400, Eric Sunshine wrote:\n> > > I've seen  \"troubleshooting\" questions from git users\n> > > who got one of those long and well-described Hints\n> > > like e.g. detached head and default git init branch\n> > > when either the msg already spelled eveything out\n> > > or the msg was purely informational.\n> >\n> > I didn't spell it out above, but what I had in mind was something very\n> > simple... not at all ong and detailed; for instance:\n> >\n> >    For interactive use, define a Git alias `git whatchanged`\n> >    which runs `git log --raw --no-merges`.\n>\n> I think that is good advice, but... it won't do anything until we\n> actually drop the whatchanged command, since until then we'll refuse to\n> override the command (even the crippled --i-still-use-this one).\n>\n> We'd need something like the patch here:\n>\n>   https://lore.kernel.org/git/20250830022718.GB567900@coredump.intra.peff.net/\n\nIndeed. I saw your patch in the other thread and had it in mind when\ncomposing my earlier email, even though I didn't specifically mention\nit (though I probably should have) since I was more focused on raising\nthe point that -- given the recent spate of muscle-memory /\nmnemonic-value argument emails -- the existing hints in Kristoffer's\npatch series may be insufficient to quell future emails.\n"},{"id":"525483","messageId":"9845fa17-1b15-4aed-9dc1-c1af21709c2f@app.fastmail.com","threadId":"64048","inReplyTo":"CAPig+cTN9vwoquOKCRz9TXRLtwHaxF-mL6xbbXxUmoHxZkW5rA@mail.gmail.com","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-04T06:57:27Z","receivedAt":"2025-09-04T06:57:48Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Thu, Sep 4, 2025, at 00:11, Eric Sunshine wrote:\n> , even though I didn't specifically mention it (though I probably\n> should have)\n\nI thought Sunshine was supposed to illuminate.\n"},{"id":"525624","messageId":"20250905131158.GA596956@coredump.intra.peff.net","threadId":"64048","inReplyTo":"CAPig+cTN9vwoquOKCRz9TXRLtwHaxF-mL6xbbXxUmoHxZkW5rA@mail.gmail.com","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-05T13:11:58Z","receivedAt":"2025-09-05T13:12:00Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Sep 03, 2025 at 06:11:30PM -0400, Eric Sunshine wrote:\n\n> > > I didn't spell it out above, but what I had in mind was something very\n> > > simple... not at all ong and detailed; for instance:\n> > >\n> > >    For interactive use, define a Git alias `git whatchanged`\n> > >    which runs `git log --raw --no-merges`.\n> >\n> > I think that is good advice, but... it won't do anything until we\n> > actually drop the whatchanged command, since until then we'll refuse to\n> > override the command (even the crippled --i-still-use-this one).\n> >\n> > We'd need something like the patch here:\n> >\n> >   https://lore.kernel.org/git/20250830022718.GB567900@coredump.intra.peff.net/\n> \n> Indeed. I saw your patch in the other thread and had it in mind when\n> composing my earlier email, even though I didn't specifically mention\n> it (though I probably should have) since I was more focused on raising\n> the point that -- given the recent spate of muscle-memory /\n> mnemonic-value argument emails -- the existing hints in Kristoffer's\n> patch series may be insufficient to quell future emails.\n\nHeh, I should have figured. I didn't see any response in that thread, so\nthis was my attempt to pawn it off on folks who are more interested in\nthe topic (and working on the advice half). ;)\n\n-Peff\n"},{"id":"525837","messageId":"cover.1757345711.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1756480827.git.code@khaugsbakk.name","subject":"[PATCH v3 0/8] you-still-use-that??: improve breaking changes troubleshooting","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-08T15:36:11Z","receivedAt":"2025-09-08T15:38:05Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nBased on the recent i-still-use-that reports about whatchanged, improve\nthe error reporting with this command in mind:\n\n1. Give more possible actions instead of just (only) asking them to send\n   an email\n2. Hint how to replace their git-whatchanged(1) use with git-log(1) or\n   an alias `whatchanged` (you can alias deprecated commands now)\n3. Minor documentation changes\n4. Add `deprecated` to `git --list-cmds`\n\n§ What the errors now look like\n\n    $ /git whatchanged\n    'git whatchanged' is nominated for removal.\n\n    hint: You can replace 'git whatchanged <opts>' with:\n    hint:   git log <opts> --raw --no-merges\n    hint: Or make an alias:\n    hint:   git config set --global alias.whatchanged 'log --raw --no-merges'\n\n    If you still use this command, here's what you can do:\n\n    - read https://git-scm.com/docs/BreakingChanges.html\n    - check if anyone has discussed this on the mailing\n      list and if they came up with something that can\n      help you: https://lore.kernel.org/git/?q=git%20whatchanged\n    - send an email to <git@vger.kernel.org> to let us\n      know that you still use this command and were unable\n      to determine a suitable replacement\n\n    fatal: refusing to run without --i-still-use-this\n    $ git pack-redundant\n    'git pack-redundant' is nominated for removal.\n    If you still use this command, here's what you can do:\n\n    - read https://git-scm.com/docs/BreakingChanges.html\n    - check if anyone has discussed this on the mailing\n      list and if they came up with something that can\n      help you: https://lore.kernel.org/git/?q=git%20pack-redundant\n    - send an email to <git@vger.kernel.org> to let us\n      know that you still use this command and were unable\n      to determine a suitable replacement\n\n    fatal: refusing to run without --i-still-use-this\n\n§ Changes in v3\n\nPatches 1–4 are new:\n\n• Make aliases which shadow deprecated builtins possible (based on\n  Peff’s patch)\n  • This was based on Eric’s prompt[1]\n• Add `deprecated` category to `git --list-cmds`\n\nThen the patch “whatchanged: tell users the git-log(1) equivalent” is\nchanged to add the now-possible aliasing.\n\n🔗 1: https://lore.kernel.org/git/CAPig+cSL=-gD5+WomF7-hYjVJ_PH0m+0i8g3F=E_U3k=QNHr8Q@mail.gmail.com/\n\nKristoffer Haugsbakk (8):\n  git: add `deprecated` category to --list-cmds\n  git: make the two loops look more symmetric\n  git: allow alias-shadowing deprecated builtins\n  t0014: test shadowing of aliases for a sample of builtins\n  you-still-use-that??: help the user help themselves\n  whatchanged: tell users the git-log(1) equivalent\n  whatchanged: remove not-even-shorter clause\n  BreakingChanges: remove claim about whatchanged reports\n\n Documentation/BreakingChanges.adoc |  2 +-\n Documentation/config/alias.adoc    |  3 +-\n Documentation/git-whatchanged.adoc |  8 +++--\n Documentation/git.adoc             |  3 +-\n builtin/log.c                      |  8 ++++-\n builtin/pack-redundant.c           |  2 +-\n git-compat-util.h                  |  2 +-\n git.c                              | 55 ++++++++++++++++++++++++------\n t/t0014-alias.sh                   | 34 ++++++++++++++++++\n usage.c                            | 33 ++++++++++++++----\n 10 files changed, 124 insertions(+), 26 deletions(-)\n\nInterdiff against v2:\ndiff --git a/Documentation/config/alias.adoc b/Documentation/config/alias.adoc\nindex 2c5db0ad842..3c8fab3a95c 100644\n--- a/Documentation/config/alias.adoc\n+++ b/Documentation/config/alias.adoc\n@@ -3,7 +3,8 @@ alias.*::\n \tafter defining `alias.last = cat-file commit HEAD`, the invocation\n \t`git last` is equivalent to `git cat-file commit HEAD`. To avoid\n \tconfusion and troubles with script usage, aliases that\n-\thide existing Git commands are ignored. Arguments are split by\n+\thide existing Git commands are ignored except for deprecated\n+\tcommands.  Arguments are split by\n \tspaces, the usual shell quoting and escaping are supported.\n \tA quote pair or a backslash can be used to quote them.\n +\ndiff --git a/Documentation/git.adoc b/Documentation/git.adoc\nindex 743b7b00e4d..a2f0838b168 100644\n--- a/Documentation/git.adoc\n+++ b/Documentation/git.adoc\n@@ -219,7 +219,8 @@ If you just want to run git as if it was started in `<path>` then use\n \tList commands by group. This is an internal/experimental\n \toption and may change or be removed in the future. Supported\n \tgroups are: builtins, parseopt (builtin commands that use\n-\tparse-options), main (all commands in libexec directory),\n+\tparse-options), deprecated (deprecated builtins),\n+\tmain (all commands in libexec directory),\n \tothers (all other commands in `$PATH` that have git- prefix),\n \tlist-<category> (see categories in command-list.txt),\n \tnohelpers (exclude helper commands), alias and config\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 5dbb90c014d..1d1e6e9130a 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -546,7 +546,9 @@ int cmd_whatchanged(int argc,\n \t\tyou_still_use_that(\"git whatchanged\",\n \t\t\t\t   _(\"\\n\"\n \t\t\t\t     \"hint: You can replace 'git whatchanged <opts>' with:\\n\"\n-\t\t\t\t     \"    git log <opts> --raw --no-merges\\n\"\n+\t\t\t\t     \"hint:\\tgit log <opts> --raw --no-merges\\n\"\n+\t\t\t\t     \"hint: Or make an alias:\\n\"\n+\t\t\t\t     \"hint:\\tgit config set --global alias.whatchanged 'log --raw --no-merges'\\n\"\n \t\t\t\t     \"\\n\"));\n \n \tif (!rev.diffopt.output_format)\ndiff --git a/git.c b/git.c\nindex 83eac0aeab7..a452ce3f9e9 100644\n--- a/git.c\n+++ b/git.c\n@@ -28,6 +28,7 @@\n #define NEED_WORK_TREE\t\t(1<<3)\n #define DELAY_PAGER_CONFIG\t(1<<4)\n #define NO_PARSEOPT\t\t(1<<5) /* parse-options is not used */\n+#define DEPRECATED\t\t(1<<6)\n \n struct cmd_struct {\n \tconst char *cmd;\n@@ -51,7 +52,13 @@ const char git_more_info_string[] =\n \n static int use_pager = -1;\n \n-static void list_builtins(struct string_list *list, unsigned int exclude_option);\n+/*\n+ * 'include_option' and 'exclude_option' are mutually exclusive.\n+ *\n+ * The default ('!include_option') is to include everything\n+ * except those filtered out by 'exclude_option'.\n+ */\n+static void list_builtins(struct string_list *list, unsigned int include_option, unsigned int exclude_option);\n \n static void exclude_helpers_from_list(struct string_list *list)\n {\n@@ -88,7 +95,7 @@ static int list_cmds(const char *spec)\n \t\tint len = sep - spec;\n \n \t\tif (match_token(spec, len, \"builtins\"))\n-\t\t\tlist_builtins(&list, 0);\n+\t\t\tlist_builtins(&list, 0, 0);\n \t\telse if (match_token(spec, len, \"main\"))\n \t\t\tlist_all_main_cmds(&list);\n \t\telse if (match_token(spec, len, \"others\"))\n@@ -99,6 +106,8 @@ static int list_cmds(const char *spec)\n \t\t\tlist_aliases(&list);\n \t\telse if (match_token(spec, len, \"config\"))\n \t\t\tlist_cmds_by_config(&list);\n+\t\telse if (match_token(spec, len, \"deprecated\"))\n+\t\t\tlist_builtins(&list, DEPRECATED, 0);\n \t\telse if (len > 5 && !strncmp(spec, \"list-\", 5)) {\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n \n@@ -322,7 +331,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\tif (!strcmp(cmd, \"parseopt\")) {\n \t\t\t\tstruct string_list list = STRING_LIST_INIT_DUP;\n \n-\t\t\t\tlist_builtins(&list, NO_PARSEOPT);\n+\t\t\t\tlist_builtins(&list, 0, NO_PARSEOPT);\n \t\t\t\tfor (size_t i = 0; i < list.nr; i++)\n \t\t\t\t\tprintf(\"%s \", list.items[i].string);\n \t\t\t\tstring_list_clear(&list, 0);\n@@ -590,7 +599,7 @@ static struct cmd_struct commands[] = {\n \t{ \"notes\", cmd_notes, RUN_SETUP },\n \t{ \"pack-objects\", cmd_pack_objects, RUN_SETUP },\n #ifndef WITH_BREAKING_CHANGES\n-\t{ \"pack-redundant\", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT },\n+\t{ \"pack-redundant\", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT | DEPRECATED },\n #endif\n \t{ \"pack-refs\", cmd_pack_refs, RUN_SETUP },\n \t{ \"patch-id\", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },\n@@ -647,7 +656,7 @@ static struct cmd_struct commands[] = {\n \t{ \"verify-tag\", cmd_verify_tag, RUN_SETUP },\n \t{ \"version\", cmd_version },\n #ifndef WITH_BREAKING_CHANGES\n-\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP },\n+\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP | DEPRECATED },\n #endif\n \t{ \"worktree\", cmd_worktree, RUN_SETUP },\n \t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n@@ -668,13 +677,18 @@ int is_builtin(const char *s)\n \treturn !!get_builtin(s);\n }\n \n-static void list_builtins(struct string_list *out, unsigned int exclude_option)\n+static void list_builtins(struct string_list *out, unsigned int include_option, unsigned int exclude_option)\n {\n-\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n-\t\tif (exclude_option &&\n-\t\t    (commands[i].option & exclude_option))\n-\t\t\tcontinue;\n-\t\tstring_list_append(out, commands[i].cmd);\n+\tif (include_option && exclude_option)\n+\t\tBUG(\"'include_option' and 'exclude_option' are mutually exclusive\");\n+\tif (include_option) {\n+\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++)\n+\t\t\tif (commands[i].option & include_option)\n+\t\t\t\tstring_list_append(out, commands[i].cmd);\n+\t} else {\n+\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++)\n+\t\t\tif (!(commands[i].option & exclude_option))\n+\t\t\t\tstring_list_append(out, commands[i].cmd);\n \t}\n }\n \n@@ -793,6 +807,12 @@ static void execv_dashed_external(const char **argv)\n \t\texit(128);\n }\n \n+static int is_deprecated_command(const char *cmd)\n+{\n+\treturn !strcmp(cmd, \"whatchanged\") ||\n+\t       !strcmp(cmd, \"pack-redundant\");\n+}\n+\n static int run_argv(struct strvec *args)\n {\n \tint done_alias = 0;\n@@ -800,6 +820,19 @@ static int run_argv(struct strvec *args)\n \tstruct string_list_item *seen;\n \n \twhile (1) {\n+\t\t/*\n+\t\t * Allow deprecated commands to be overridden by aliases. This\n+\t\t * creates a seamless path forward for people who want to keep\n+\t\t * using the name after it is gone, but want to skip the\n+\t\t * deprecation complaint in the meantime.\n+\t\t */\n+\t\tif (is_deprecated_command(args->v[0]) &&\n+\t\t    alias_lookup(args->v[0])) {\n+\t\t\tif (!handle_alias(args))\n+\t\t\t\tbreak;\n+\t\t\tdone_alias = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\t/*\n \t\t * If we tried alias and futzed with our environment,\n \t\t * it no longer is safe to invoke builtins directly in\ndiff --git a/t/t0014-alias.sh b/t/t0014-alias.sh\nindex 854d59ec58c..bf7e6512bb1 100755\n--- a/t/t0014-alias.sh\n+++ b/t/t0014-alias.sh\n@@ -55,4 +55,38 @@ test_expect_success 'tracing a shell alias with arguments shows trace of prepare\n \ttest_cmp expect actual\n '\n \n+can_alias_deprecated_builtin () {\n+\tcmd=\"$1\" &&\n+\t# some git(1) commands will fail for `-h` (the case for\n+\t# git-status as of 2025-09-07)\n+\ttest_might_fail git status -h >expect &&\n+\ttest_file_not_empty expect &&\n+\ttest_might_fail git -c alias.\"$cmd\"=status \"$cmd\" -h >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'can alias-shadow deprecated builtins' '\n+\tfor cmd in $(git --list-cmds=deprecated)\n+\tdo\n+\t\tcan_alias_deprecated_builtin \"$cmd\" || return 1\n+\tdone\n+'\n+\n+cannot_alias_regular_builtin () {\n+\tcmd=\"$1\" &&\n+\t# some git(1) commands will fail... (see above)\n+\ttest_might_fail git \"$cmd\" -h >expect &&\n+\ttest_file_not_empty expect &&\n+\ttest_might_fail git -c alias.\"$cmd\"=status \"$cmd\" -h >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'cannot alias-shadow a sample of regular builtins' '\n+\tfor cmd in grep check-ref-format interpret-trailers \\\n+\t\tcheckout-index fast-import diagnose rev-list prune\n+\tdo\n+\t\tcannot_alias_regular_builtin \"$cmd\" || return 1\n+\tdone\n+'\n+\n test_done\nRange-diff against v2:\n-:  ----------- > 1:  bdc683a92b3 git: add `deprecated` category to --list-cmds\n-:  ----------- > 2:  183dd68d09d git: make the two loops look more symmetric\n-:  ----------- > 3:  eec01cbac16 git: allow alias-shadowing deprecated builtins\n-:  ----------- > 4:  80fb02caeeb t0014: test shadowing of aliases for a sample of builtins\n1:  6803e2cc6c3 = 5:  d25ee26f989 you-still-use-that??: help the user help themselves\n2:  2f3ac952980 ! 6:  50621a0748f whatchanged: tell users the git-log(1) equivalent\n    @@ Commit message\n     \n     \n      ## Notes (series) ##\n    +    v3:\n    +\n    +    Add an alias hint now that that is possible.  Also prefix each hint-line\n    +    with `hint: `.\n    +\n         v2:\n     \n         Review found a whitespace error in the prev. patch version.  I found a\n    @@ builtin/log.c: int cmd_whatchanged(int argc,\n     +\t\tyou_still_use_that(\"git whatchanged\",\n     +\t\t\t\t   _(\"\\n\"\n     +\t\t\t\t     \"hint: You can replace 'git whatchanged <opts>' with:\\n\"\n    -+\t\t\t\t     \"    git log <opts> --raw --no-merges\\n\"\n    ++\t\t\t\t     \"hint:\\tgit log <opts> --raw --no-merges\\n\"\n    ++\t\t\t\t     \"hint: Or make an alias:\\n\"\n    ++\t\t\t\t     \"hint:\\tgit config set --global alias.whatchanged 'log --raw --no-merges'\\n\"\n     +\t\t\t\t     \"\\n\"));\n      \n      \tif (!rev.diffopt.output_format)\n3:  a074e7be422 = 7:  812c9870f1b whatchanged: remove not-even-shorter clause\n4:  9196c3c7e33 = 8:  0d23a4badf0 BreakingChanges: remove claim about whatchanged reports\n\nbase-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525838","messageId":"bdc683a92b38884e9428cd4bade1f86960ef432b.1757345711.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757345711.git.code@khaugsbakk.name","subject":"[PATCH v3 1/8] git: add `deprecated` category to --list-cmds","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-08T15:36:12Z","receivedAt":"2025-09-08T15:38:25Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nWith 145 builtin commands (according to `git --list-cmds=builtins`),\nusers are probably not keeping on top of which ones (if any) are\ndeprecated.\n\nLet’s expand the experimental `--list-cmds`[1] to allow users and\nprograms to query for this information.  We will also use this in an\nupcoming commit to assert that all deprecated commands will have been\ncovered in some manner.\n\n[1]: Using something which is experimental to query for deprecations is\n    perhaps not the most ideal approach, but it is simple to implement\n    and better than having to scan the documentation\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v3 (new):\n    \n    This is something I wanted to submit independently until the point about\n    aliasing builtins was brought up.[1]  It will help (in a small way) with\n    the upcoming patch “git: allow alias-shadowing deprecated builtins”.\n    \n    By the way: should `command-list.txt` be updated in some way (I didn’t\n    know what way?)\n    \n    🔗 1: https://lore.kernel.org/git/cover.1756311355.git.code@khaugsbakk.name/T/#mee19f8d39572f9021f9d3000758e87b6c32c967c\n\n Documentation/git.adoc |  3 ++-\n git.c                  | 38 +++++++++++++++++++++++++++-----------\n 2 files changed, 29 insertions(+), 12 deletions(-)\n\ndiff --git a/Documentation/git.adoc b/Documentation/git.adoc\nindex 743b7b00e4d..a2f0838b168 100644\n--- a/Documentation/git.adoc\n+++ b/Documentation/git.adoc\n@@ -219,7 +219,8 @@ If you just want to run git as if it was started in `<path>` then use\n \tList commands by group. This is an internal/experimental\n \toption and may change or be removed in the future. Supported\n \tgroups are: builtins, parseopt (builtin commands that use\n-\tparse-options), main (all commands in libexec directory),\n+\tparse-options), deprecated (deprecated builtins),\n+\tmain (all commands in libexec directory),\n \tothers (all other commands in `$PATH` that have git- prefix),\n \tlist-<category> (see categories in command-list.txt),\n \tnohelpers (exclude helper commands), alias and config\ndiff --git a/git.c b/git.c\nindex 83eac0aeab7..87d61f12594 100644\n--- a/git.c\n+++ b/git.c\n@@ -28,6 +28,7 @@\n #define NEED_WORK_TREE\t\t(1<<3)\n #define DELAY_PAGER_CONFIG\t(1<<4)\n #define NO_PARSEOPT\t\t(1<<5) /* parse-options is not used */\n+#define DEPRECATED\t\t(1<<6)\n \n struct cmd_struct {\n \tconst char *cmd;\n@@ -51,7 +52,13 @@ const char git_more_info_string[] =\n \n static int use_pager = -1;\n \n-static void list_builtins(struct string_list *list, unsigned int exclude_option);\n+/*\n+ * 'include_option' and 'exclude_option' are mutually exclusive.\n+ *\n+ * The default ('!include_option') is to include everything\n+ * except those filtered out by 'exclude_option'.\n+ */\n+static void list_builtins(struct string_list *list, unsigned int include_option, unsigned int exclude_option);\n \n static void exclude_helpers_from_list(struct string_list *list)\n {\n@@ -88,7 +95,7 @@ static int list_cmds(const char *spec)\n \t\tint len = sep - spec;\n \n \t\tif (match_token(spec, len, \"builtins\"))\n-\t\t\tlist_builtins(&list, 0);\n+\t\t\tlist_builtins(&list, 0, 0);\n \t\telse if (match_token(spec, len, \"main\"))\n \t\t\tlist_all_main_cmds(&list);\n \t\telse if (match_token(spec, len, \"others\"))\n@@ -99,6 +106,8 @@ static int list_cmds(const char *spec)\n \t\t\tlist_aliases(&list);\n \t\telse if (match_token(spec, len, \"config\"))\n \t\t\tlist_cmds_by_config(&list);\n+\t\telse if (match_token(spec, len, \"deprecated\"))\n+\t\t\tlist_builtins(&list, DEPRECATED, 0);\n \t\telse if (len > 5 && !strncmp(spec, \"list-\", 5)) {\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n \n@@ -322,7 +331,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\tif (!strcmp(cmd, \"parseopt\")) {\n \t\t\t\tstruct string_list list = STRING_LIST_INIT_DUP;\n \n-\t\t\t\tlist_builtins(&list, NO_PARSEOPT);\n+\t\t\t\tlist_builtins(&list, 0, NO_PARSEOPT);\n \t\t\t\tfor (size_t i = 0; i < list.nr; i++)\n \t\t\t\t\tprintf(\"%s \", list.items[i].string);\n \t\t\t\tstring_list_clear(&list, 0);\n@@ -590,7 +599,7 @@ static struct cmd_struct commands[] = {\n \t{ \"notes\", cmd_notes, RUN_SETUP },\n \t{ \"pack-objects\", cmd_pack_objects, RUN_SETUP },\n #ifndef WITH_BREAKING_CHANGES\n-\t{ \"pack-redundant\", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT },\n+\t{ \"pack-redundant\", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT | DEPRECATED },\n #endif\n \t{ \"pack-refs\", cmd_pack_refs, RUN_SETUP },\n \t{ \"patch-id\", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },\n@@ -647,7 +656,7 @@ static struct cmd_struct commands[] = {\n \t{ \"verify-tag\", cmd_verify_tag, RUN_SETUP },\n \t{ \"version\", cmd_version },\n #ifndef WITH_BREAKING_CHANGES\n-\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP },\n+\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP | DEPRECATED },\n #endif\n \t{ \"worktree\", cmd_worktree, RUN_SETUP },\n \t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n@@ -668,13 +677,20 @@ int is_builtin(const char *s)\n \treturn !!get_builtin(s);\n }\n \n-static void list_builtins(struct string_list *out, unsigned int exclude_option)\n+static void list_builtins(struct string_list *out, unsigned int include_option, unsigned int exclude_option)\n {\n-\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n-\t\tif (exclude_option &&\n-\t\t    (commands[i].option & exclude_option))\n-\t\t\tcontinue;\n-\t\tstring_list_append(out, commands[i].cmd);\n+\tif (include_option && exclude_option)\n+\t\tBUG(\"'include_option' and 'exclude_option' are mutually exclusive\");\n+\tif (include_option) {\n+\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++)\n+\t\t\tif (commands[i].option & include_option)\n+\t\t\t\tstring_list_append(out, commands[i].cmd);\n+\t} else {\n+\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n+\t\t\tif (commands[i].option & exclude_option)\n+\t\t\t\tcontinue;\n+\t\t\tstring_list_append(out, commands[i].cmd);\n+\t\t}\n \t}\n }\n \n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525839","messageId":"183dd68d09d7785b449f0c5295094690f7f35509.1757345711.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757345711.git.code@khaugsbakk.name","subject":"[PATCH v3 2/8] git: make the two loops look more symmetric","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-08T15:36:13Z","receivedAt":"2025-09-08T15:38:44Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nRewrite the original conditional here to match the new sibling loop\nin structure.\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v3 (new):\n    \n    This is just a refactor commit to avoid having to add a “while at it”\n    for such small tweaks for the new context.\n\n git.c | 8 +++-----\n 1 file changed, 3 insertions(+), 5 deletions(-)\n\ndiff --git a/git.c b/git.c\nindex 87d61f12594..39dc9f8ec0f 100644\n--- a/git.c\n+++ b/git.c\n@@ -686,11 +686,9 @@ static void list_builtins(struct string_list *out, unsigned int include_option,\n \t\t\tif (commands[i].option & include_option)\n \t\t\t\tstring_list_append(out, commands[i].cmd);\n \t} else {\n-\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n-\t\t\tif (commands[i].option & exclude_option)\n-\t\t\t\tcontinue;\n-\t\t\tstring_list_append(out, commands[i].cmd);\n-\t\t}\n+\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++)\n+\t\t\tif (!(commands[i].option & exclude_option))\n+\t\t\t\tstring_list_append(out, commands[i].cmd);\n \t}\n }\n \n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525840","messageId":"eec01cbac16596c5e117843ae86956e1f66ec097.1757345711.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757345711.git.code@khaugsbakk.name","subject":"[PATCH v3 3/8] git: allow alias-shadowing deprecated builtins","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-08T15:36:14Z","receivedAt":"2025-09-08T15:39:02Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\ngit-whatchanged(1) is deprecated and you need to pass\n`--i-still-use-this` in order to force it to work as before.\nThere are two affected users, or usages:\n\n1. people who use the command in scripts; and\n2. people who are used to using it interactively.\n\nFor (1) the replacement is straightforward.[1]  But people in (2) might\nlike the name or be really used to typing it.[3]\n\nAn obvious first thought is to suggest aliasing `whatchanged` to the\ngit-log(1) equivalent.[1]  But this doesn’t work and is awkward since you\ncannot shadow builtins via aliases.\n\nNow you are left in an uncomfortable limbo; your alias won’t work until\nthe command is removed for good.\n\nLet’s lift this limitation by allowing *deprecated* builtins to be\nshadowed by aliases.\n\nThe only observed demand for aliasing has been for git-whatchanged(1),\nnot for git-pack-redundant(1).  But let’s be consistent and treat all\ndeprecated commands the same.\n\n[1]:\n\n        git log --raw --no-merges\n\n     With a minor caveat: you get different outputs if you happen to\n     have empty commits (no changes)[2]\n[2]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n[3]: https://lore.kernel.org/git/BL3P221MB0449288C8B0FA448A227FD48833AA@BL3P221MB0449.NAMP221.PROD.OUTLOOK.COM/\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v3 (new):\n    \n    Prerequisite for telling the user that they can alias `whatchanged` to\n    `git log --raw --no-merged`.\n    \n    Link: https://lore.kernel.org/git/cover.1756311355.git.code@khaugsbakk.name/T/#md434b0968f499263262fb1805d82b788b8349d9a\n    \n    > I think that is good advice, but... it won't do anything until we\n    > actually drop the whatchanged command, since until then we'll refuse to\n    > override the command (even the crippled --i-still-use-this one).\n    >\n    > We'd need something like the patch here:\n    \n    ❦\n    \n    The\n    \n        test_file_not_empty expect\n    \n    is here because the git(1) command could fail.  Make sure that it did\n    indeed output anyhing on stdout.  (Or if a previous redirect to `expect`\n    outputted something it should be completely different to `actual` in any\n    case)\n    \n    I don’t know if this is just a waste.\n\n Documentation/config/alias.adoc |  3 ++-\n git.c                           | 19 +++++++++++++++++++\n t/t0014-alias.sh                | 17 +++++++++++++++++\n 3 files changed, 38 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config/alias.adoc b/Documentation/config/alias.adoc\nindex 2c5db0ad842..3c8fab3a95c 100644\n--- a/Documentation/config/alias.adoc\n+++ b/Documentation/config/alias.adoc\n@@ -3,7 +3,8 @@ alias.*::\n \tafter defining `alias.last = cat-file commit HEAD`, the invocation\n \t`git last` is equivalent to `git cat-file commit HEAD`. To avoid\n \tconfusion and troubles with script usage, aliases that\n-\thide existing Git commands are ignored. Arguments are split by\n+\thide existing Git commands are ignored except for deprecated\n+\tcommands.  Arguments are split by\n \tspaces, the usual shell quoting and escaping are supported.\n \tA quote pair or a backslash can be used to quote them.\n +\ndiff --git a/git.c b/git.c\nindex 39dc9f8ec0f..a452ce3f9e9 100644\n--- a/git.c\n+++ b/git.c\n@@ -807,6 +807,12 @@ static void execv_dashed_external(const char **argv)\n \t\texit(128);\n }\n \n+static int is_deprecated_command(const char *cmd)\n+{\n+\treturn !strcmp(cmd, \"whatchanged\") ||\n+\t       !strcmp(cmd, \"pack-redundant\");\n+}\n+\n static int run_argv(struct strvec *args)\n {\n \tint done_alias = 0;\n@@ -814,6 +820,19 @@ static int run_argv(struct strvec *args)\n \tstruct string_list_item *seen;\n \n \twhile (1) {\n+\t\t/*\n+\t\t * Allow deprecated commands to be overridden by aliases. This\n+\t\t * creates a seamless path forward for people who want to keep\n+\t\t * using the name after it is gone, but want to skip the\n+\t\t * deprecation complaint in the meantime.\n+\t\t */\n+\t\tif (is_deprecated_command(args->v[0]) &&\n+\t\t    alias_lookup(args->v[0])) {\n+\t\t\tif (!handle_alias(args))\n+\t\t\t\tbreak;\n+\t\t\tdone_alias = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\t/*\n \t\t * If we tried alias and futzed with our environment,\n \t\t * it no longer is safe to invoke builtins directly in\ndiff --git a/t/t0014-alias.sh b/t/t0014-alias.sh\nindex 854d59ec58c..89bedb9f73b 100755\n--- a/t/t0014-alias.sh\n+++ b/t/t0014-alias.sh\n@@ -55,4 +55,21 @@ test_expect_success 'tracing a shell alias with arguments shows trace of prepare\n \ttest_cmp expect actual\n '\n \n+can_alias_deprecated_builtin () {\n+\tcmd=\"$1\" &&\n+\t# some git(1) commands will fail for `-h` (the case for\n+\t# git-status as of 2025-09-07)\n+\ttest_might_fail git status -h >expect &&\n+\ttest_file_not_empty expect &&\n+\ttest_might_fail git -c alias.\"$cmd\"=status \"$cmd\" -h >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'can alias-shadow deprecated builtins' '\n+\tfor cmd in $(git --list-cmds=deprecated)\n+\tdo\n+\t\tcan_alias_deprecated_builtin \"$cmd\" || return 1\n+\tdone\n+'\n+\n test_done\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525841","messageId":"80fb02caeebae82650cae91fd5bb291bc630ac02.1757345711.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757345711.git.code@khaugsbakk.name","subject":"[PATCH v3 4/8] t0014: test shadowing of aliases for a sample of builtins","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-08T15:36:15Z","receivedAt":"2025-09-08T15:39:21Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThe previous commit added a test for shadowing deprecated builtins.\nLet’s make the test suite more complete by exercising a sample of\nthe builtins and in turn test the documentation for git-config(1):\n\n    To avoid confusion and troubles with script usage, aliases that hide\n    existing Git commands are ignored except for deprecated commands.\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v3 (new):\n    \n    Only a sample of builtins since the test file takes about 600ms longer\n    if I test all of them.\n\n t/t0014-alias.sh | 17 +++++++++++++++++\n 1 file changed, 17 insertions(+)\n\ndiff --git a/t/t0014-alias.sh b/t/t0014-alias.sh\nindex 89bedb9f73b..bf7e6512bb1 100755\n--- a/t/t0014-alias.sh\n+++ b/t/t0014-alias.sh\n@@ -72,4 +72,21 @@ test_expect_success 'can alias-shadow deprecated builtins' '\n \tdone\n '\n \n+cannot_alias_regular_builtin () {\n+\tcmd=\"$1\" &&\n+\t# some git(1) commands will fail... (see above)\n+\ttest_might_fail git \"$cmd\" -h >expect &&\n+\ttest_file_not_empty expect &&\n+\ttest_might_fail git -c alias.\"$cmd\"=status \"$cmd\" -h >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'cannot alias-shadow a sample of regular builtins' '\n+\tfor cmd in grep check-ref-format interpret-trailers \\\n+\t\tcheckout-index fast-import diagnose rev-list prune\n+\tdo\n+\t\tcannot_alias_regular_builtin \"$cmd\" || return 1\n+\tdone\n+'\n+\n test_done\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525842","messageId":"d25ee26f98930a5ce02663ea7316ff1c5158dcce.1757345711.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757345711.git.code@khaugsbakk.name","subject":"[PATCH v3 5/8] you-still-use-that??: help the user help themselves","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-08T15:36:16Z","receivedAt":"2025-09-08T15:39:40Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nGive the user a list of suggestions for what to do when they run a\ndeprecated command.\n\nThe first order of action will be to check the breaking changes\ndocument;[1] this short error message says nothing about why this\ncommand is deprecated, and in any case going into any kind of detail\nmight overwhelm the user.\n\nThen they can find out if this has been discussed on the mailing list.\nThen users who e.g. are using git-whatchanged(1) can learn that this is\narguably a plug-in replacement:\n\n    git log <opts> --raw --no-merges\n\nFinally they are invited to send an email to the mailing list.\n\nAlso drop the “please add” part in favor of just using the “refusing”\ndie-message; these two would have been right after each other in this\nnew version.\n\nAlso drop “Thanks” since it now would require a new paragraph.\n\n[1]: www.git-scm.com has a disclaimer for these internal documents that\n    says that “This information is specific to the Git project”.  That’s\n    misleading in this particular case.  But users are unlikely to get\n    discouraged from reading about why they (or their programs) cannot run a\n    command any more; it clearly concerns them.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v2:\n    \n    I had second thoughts about the bullet point about send-an-email.\n    Change it to the one Eric Sunshine proposed;[1] make sure to spell\n    out that you can send an email conditioned on not finding a suitable\n    replacement.\n    \n    Also change the area to something more pointed.\n    \n    And also use a clear URL to refer to www.git-scm.com.\n    \n    [1]: https://lore.kernel.org/git/CAPig+cQkVP57n_FE6dJ0uxvai-J7usxKFp8gzfEbPY=Ytsd6=Q@mail.gmail.com/\n    \n    • Change send-an-email bullet point\n    • Change the area\n    • Use www.git-scm.com, not simply git-scm\n    \n    v1:\n    \n    An alternative to linking to www.git-scm.com is to move this document to\n    a regular installed man page:\n    \n        gitbreaking-changes(7)\n    \n    What do you think?\n    \n    I would then have to base my topic on the in-flight\n    pw/3.0-commentchar-auto-deprecation, which in turn depends on\n    ps/config-wo-the-repository.\n    \n    Or just wait a bit for these to settle in.\n\n usage.c | 23 ++++++++++++++++++-----\n 1 file changed, 18 insertions(+), 5 deletions(-)\n\ndiff --git a/usage.c b/usage.c\nindex 81913236a4a..35dc57eb07e 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -7,6 +7,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"trace2.h\"\n+#include \"strbuf.h\"\n \n static void vfreportf(FILE *f, const char *prefix, const char *err, va_list params)\n {\n@@ -377,12 +378,24 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n \n NORETURN void you_still_use_that(const char *command_name)\n {\n+\tstruct strbuf percent_encoded = STRBUF_INIT;\n+\tstrbuf_add_percentencode(&percent_encoded,\n+\t\t\t\t command_name,\n+\t\t\t\t STRBUF_ENCODE_SLASH);\n+\n \tfprintf(stderr,\n \t\t_(\"'%s' is nominated for removal.\\n\"\n-\t\t  \"If you still use this command, please add an extra\\n\"\n-\t\t  \"option, '--i-still-use-this', on the command line\\n\"\n-\t\t  \"and let us know you still use it by sending an e-mail\\n\"\n-\t\t  \"to <git@vger.kernel.org>.  Thanks.\\n\"),\n-\t\tcommand_name);\n+\t\t  \"If you still use this command, here's what you can do:\\n\"\n+\t\t  \"\\n\"\n+\t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n+\t\t  \"- check if anyone has discussed this on the mailing\\n\"\n+\t\t  \"  list and if they came up with something that can\\n\"\n+\t\t  \"  help you: https://lore.kernel.org/git/?q=%s\\n\"\n+\t\t  \"- send an email to <git@vger.kernel.org> to let us\\n\"\n+\t\t  \"  know that you still use this command and were unable\\n\"\n+\t\t  \"  to determine a suitable replacement\\n\"\n+\t\t  \"\\n\"),\n+\t\tcommand_name, percent_encoded.buf);\n+\tstrbuf_release(&percent_encoded);\n \tdie(_(\"refusing to run without --i-still-use-this\"));\n }\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525843","messageId":"50621a0748fd98b061352dce91e9a86e1e930b8d.1757345711.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757345711.git.code@khaugsbakk.name","subject":"[PATCH v3 6/8] whatchanged: tell users the git-log(1) equivalent","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-08T15:36:17Z","receivedAt":"2025-09-08T15:39:59Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThere have been quite a few `--i-still-use-this` user reports since Git\n2.51.0 was released.[1][2]  And it doesn’t seem like they are reading\nthe man page about the git-log(1) equivalent.\n\nTell them what options to plug into git-log(1).  That template produces\nalmost the same output[3] and is arguably a plug-in replacement.\nConcretely, add an optional `hint` argument so that we can use it right\nafter the initial error line.\n\nAlso mention the same concrete options in the documentation while we’re\nat it.\n\n[1]: E.g.,\n    • https://lore.kernel.org/git/e1a69dea-bcb6-45fc-83d3-9e50d32c410b@5y5.one/\n    • https://lore.kernel.org/git/1011073f-9930-4360-a42f-71eb7421fe3f@chrispalmer.uk/#t\n    • https://lore.kernel.org/git/9fcbfcc4-79f9-421f-b9a4-dc455f7db485@acm.org/#t\n    • https://lore.kernel.org/git/83241BDE-1E0D-489A-9181-C608E9FCC17B@gmail.com/\n[2]: The error message on 2.51.0 does tell them to report it, unconditionally\n[3]: You only get different outputs if you happen to have empty\n     commits (no changes)[4]\n[4]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v3:\n    \n    Add an alias hint now that that is possible.  Also prefix each hint-line\n    with `hint: `.\n    \n    v2:\n    \n    Review found a whitespace error in the prev. patch version.  I found a\n    broken footnote and wanted to expand on the last footnote.\n    \n    • Fix whitespace error (I should have used `ci/check-whitespace.sh\n      v2.51.0`)\n    • Add missing colon (:) to footnote\n    • Expand on footnote; a sentence is enough to summarize the difference\n\n Documentation/git-whatchanged.adoc |  6 +++++-\n builtin/log.c                      |  8 +++++++-\n builtin/pack-redundant.c           |  2 +-\n git-compat-util.h                  |  2 +-\n usage.c                            | 14 ++++++++++----\n 5 files changed, 24 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-whatchanged.adoc b/Documentation/git-whatchanged.adoc\nindex d21484026fe..e71d2aa2d27 100644\n--- a/Documentation/git-whatchanged.adoc\n+++ b/Documentation/git-whatchanged.adoc\n@@ -24,7 +24,11 @@ Shows commit logs and diff output each commit introduces.\n \n New users are encouraged to use linkgit:git-log[1] instead.  The\n `whatchanged` command is essentially the same as linkgit:git-log[1]\n-but defaults to showing the raw format diff output and skipping merges.\n+but defaults to showing the raw format diff output and skipping merges:\n+\n+----\n+git log --raw --no-merges\n+----\n \n The command is primarily kept for historical reasons; fingers of\n many people who learned Git long before `git log` was invented by\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c2f8bbf8630..1d1e6e9130a 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -543,7 +543,13 @@ int cmd_whatchanged(int argc,\n \tcmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);\n \n \tif (!cfg.i_still_use_this)\n-\t\tyou_still_use_that(\"git whatchanged\");\n+\t\tyou_still_use_that(\"git whatchanged\",\n+\t\t\t\t   _(\"\\n\"\n+\t\t\t\t     \"hint: You can replace 'git whatchanged <opts>' with:\\n\"\n+\t\t\t\t     \"hint:\\tgit log <opts> --raw --no-merges\\n\"\n+\t\t\t\t     \"hint: Or make an alias:\\n\"\n+\t\t\t\t     \"hint:\\tgit config set --global alias.whatchanged 'log --raw --no-merges'\\n\"\n+\t\t\t\t     \"\\n\"));\n \n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\ndiff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c\nindex fe81c293e3a..5d5ae4afa28 100644\n--- a/builtin/pack-redundant.c\n+++ b/builtin/pack-redundant.c\n@@ -626,7 +626,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED, s\n \t}\n \n \tif (!i_still_use_this)\n-\t\tyou_still_use_that(\"git pack-redundant\");\n+\t\tyou_still_use_that(\"git pack-redundant\", NULL);\n \n \tif (load_all_packs)\n \t\tload_all();\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 9408f463e31..398e0fac4fa 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -460,7 +460,7 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));\n \n void show_usage_if_asked(int ac, const char **av, const char *err);\n \n-NORETURN void you_still_use_that(const char *command_name);\n+NORETURN void you_still_use_that(const char *command_name, const char *hint);\n \n #ifndef NO_OPENSSL\n #ifdef APPLE_COMMON_CRYPTO\ndiff --git a/usage.c b/usage.c\nindex 35dc57eb07e..7545a616453 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -376,7 +376,8 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n \tva_end(ap);\n }\n \n-NORETURN void you_still_use_that(const char *command_name)\n+\n+NORETURN void you_still_use_that(const char *command_name, const char *hint)\n {\n \tstruct strbuf percent_encoded = STRBUF_INIT;\n \tstrbuf_add_percentencode(&percent_encoded,\n@@ -384,8 +385,13 @@ NORETURN void you_still_use_that(const char *command_name)\n \t\t\t\t STRBUF_ENCODE_SLASH);\n \n \tfprintf(stderr,\n-\t\t_(\"'%s' is nominated for removal.\\n\"\n-\t\t  \"If you still use this command, here's what you can do:\\n\"\n+\t\t_(\"'%s' is nominated for removal.\\n\"), command_name);\n+\n+\tif (hint)\n+\t\tfputs(hint, stderr);\n+\n+\tfprintf(stderr,\n+\t\t_(\"If you still use this command, here's what you can do:\\n\"\n \t\t  \"\\n\"\n \t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n \t\t  \"- check if anyone has discussed this on the mailing\\n\"\n@@ -395,7 +401,7 @@ NORETURN void you_still_use_that(const char *command_name)\n \t\t  \"  know that you still use this command and were unable\\n\"\n \t\t  \"  to determine a suitable replacement\\n\"\n \t\t  \"\\n\"),\n-\t\tcommand_name, percent_encoded.buf);\n+\t\tpercent_encoded.buf);\n \tstrbuf_release(&percent_encoded);\n \tdie(_(\"refusing to run without --i-still-use-this\"));\n }\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525844","messageId":"812c9870f1ba466ea001a05867b9b95bbc681d00.1757345711.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757345711.git.code@khaugsbakk.name","subject":"[PATCH v3 7/8] whatchanged: remove not-even-shorter clause","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-08T15:36:18Z","receivedAt":"2025-09-08T15:40:17Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThe closest equivalent is `git log --raw --no-merges`.\n\nAlso change to “defaults” (implicit plural).\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n Documentation/git-whatchanged.adoc | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-whatchanged.adoc b/Documentation/git-whatchanged.adoc\nindex e71d2aa2d27..436e219b7d0 100644\n--- a/Documentation/git-whatchanged.adoc\n+++ b/Documentation/git-whatchanged.adoc\n@@ -15,7 +15,7 @@ WARNING\n -------\n `git whatchanged` has been deprecated and is scheduled for removal in\n a future version of Git, as it is merely `git log` with different\n-default; `whatchanged` is not even shorter to type than `log --raw`.\n+defaults.\n \n DESCRIPTION\n -----------\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525845","messageId":"0d23a4badf0c723cd633999d9ee4d9a94f38fe15.1757345711.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757345711.git.code@khaugsbakk.name","subject":"[PATCH v3 8/8] BreakingChanges: remove claim about whatchanged reports","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-08T15:36:19Z","receivedAt":"2025-09-08T15:40:35Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThis was written in e836757e14b (whatschanged: list it in\nBreakingChanges document, 2025-05-12) which was on the same\ntopic that added the `--i-still-use-this` requirement.[1]\n\nMaybe it was a work-in-progress comment/status.\n\n[1]: jc/you-still-use-whatchanged\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    Footnote solely to avoid awkward paragraph wrapping...\n\n Documentation/BreakingChanges.adoc | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc\nindex f8d2eba061c..c4985163c3c 100644\n--- a/Documentation/BreakingChanges.adoc\n+++ b/Documentation/BreakingChanges.adoc\n@@ -235,7 +235,7 @@ These features will be removed.\n   equivalent `git log --raw`.  We have nominated the command for\n   removal, have changed the command to refuse to work unless the\n   `--i-still-use-this` option is given, and asked the users to report\n-  when they do so.  So far there hasn't been a single complaint.\n+  when they do so.\n +\n The command will be removed.\n \n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525878","messageId":"xmqqcy80wu0y.fsf@gitster.g","threadId":"64048","inReplyTo":"eec01cbac16596c5e117843ae86956e1f66ec097.1757345711.git.code@khaugsbakk.name","subject":"Re: [PATCH v3 3/8] git: allow alias-shadowing deprecated builtins","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-08T20:47:57Z","receivedAt":"2025-09-08T20:48:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"kristofferhaugsbakk@fastmail.com writes:\n\n> +static int is_deprecated_command(const char *cmd)\n> +{\n> +\treturn !strcmp(cmd, \"whatchanged\") ||\n> +\t       !strcmp(cmd, \"pack-redundant\");\n> +}\n\nThis is somewhat a shame as you introduced the DEPRECATED bit to\nannotate entries in \"struct cmd_struct commands[]\" array.  Shouldn't\nthis be moved to git.c and taught to consult that array instead?\n\n"},{"id":"525881","messageId":"20250908211141.GB1327610@coredump.intra.peff.net","threadId":"64048","inReplyTo":"xmqqcy80wu0y.fsf@gitster.g","subject":"Re: [PATCH v3 3/8] git: allow alias-shadowing deprecated builtins","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-08T21:11:41Z","receivedAt":"2025-09-08T21:11:42Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Sep 08, 2025 at 01:47:57PM -0700, Junio C Hamano wrote:\n\n> kristofferhaugsbakk@fastmail.com writes:\n> \n> > +static int is_deprecated_command(const char *cmd)\n> > +{\n> > +\treturn !strcmp(cmd, \"whatchanged\") ||\n> > +\t       !strcmp(cmd, \"pack-redundant\");\n> > +}\n> \n> This is somewhat a shame as you introduced the DEPRECATED bit to\n> annotate entries in \"struct cmd_struct commands[]\" array.  Shouldn't\n> this be moved to git.c and taught to consult that array instead?\n\nHmm, I did not see the original patch, as it does not seem to have hit\nthe list (or maybe vger is just slow today).\n\nBut yeah, if we have a bit in the cmd_struct array, that might be a\nnicer spot to match. It may or may not be trivial. When I posted my\n\"something like this\" patch I first tried to do it that way, but there\nare a lot of ordering constraints and the parts where we resolved the\ncmd_struct were not the right spots. OTOH, it would probably not be that\nhard to just do:\n\n  static int is_deprecated_command(const char *cmd)\n  {\n\tstruct cmd_struct *builtin = get_builtin(cmd);\n\treturn builtin && (builtin->flags & DEPRECATED);\n\n  }\n\nIn theory we could also have a deprecated non-builtin (i.e., implemented\nas an external program). And then we really would have to match by name.\nBut I kind of doubt that will happen in practice (and it would still be\npossible to add a string match later if it did).\n\n-Peff\n"},{"id":"525884","messageId":"xmqq348wwqwb.fsf@gitster.g","threadId":"64048","inReplyTo":"20250908211141.GB1327610@coredump.intra.peff.net","subject":"Re: [PATCH v3 3/8] git: allow alias-shadowing deprecated builtins","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-08T21:55:32Z","receivedAt":"2025-09-08T21:55:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> cmd_struct were not the right spots. OTOH, it would probably not be that\n> hard to just do:\n>\n>   static int is_deprecated_command(const char *cmd)\n>   {\n> \tstruct cmd_struct *builtin = get_builtin(cmd);\n> \treturn builtin && (builtin->flags & DEPRECATED);\n>\n>   }\n\nThat was exactly what I had in mind.\n\nThanks.\n"},{"id":"525900","messageId":"54833b28-658f-4eec-93a6-74db11c4b9e4@app.fastmail.com","threadId":"64048","inReplyTo":"xmqq348wwqwb.fsf@gitster.g","subject":"Re: [PATCH v3 3/8] git: allow alias-shadowing deprecated builtins","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-09T06:25:29Z","receivedAt":"2025-09-09T06:25:50Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Mon, Sep 8, 2025, at 23:55, Junio C Hamano wrote:\n> Jeff King <peff@peff.net> writes:\n>\n>> cmd_struct were not the right spots. OTOH, it would probably not be that\n>> hard to just do:\n>>\n>>   static int is_deprecated_command(const char *cmd)\n>>   {\n>> \tstruct cmd_struct *builtin = get_builtin(cmd);\n>> \treturn builtin && (builtin->flags & DEPRECATED);\n>>\n>>   }\n>\n> That was exactly what I had in mind.\n\nThanks to both.  I’ll use that suggestion.\n\n-- \nKristoffer Haugsbakk\n"},{"id":"525904","messageId":"aL_MdiBLx9sssBwc@pks.im","threadId":"64048","inReplyTo":"bdc683a92b38884e9428cd4bade1f86960ef432b.1757345711.git.code@khaugsbakk.name","subject":"Re: [PATCH v3 1/8] git: add `deprecated` category to --list-cmds","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-09T06:43:02Z","receivedAt":"2025-09-09T06:43:09Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Sep 08, 2025 at 05:36:12PM +0200, kristofferhaugsbakk@fastmail.com wrote:\n> Notes (series):\n>     v3 (new):\n>     \n>     This is something I wanted to submit independently until the point about\n>     aliasing builtins was brought up.[1]  It will help (in a small way) with\n>     the upcoming patch “git: allow alias-shadowing deprecated builtins”.\n>     \n>     By the way: should `command-list.txt` be updated in some way (I didn’t\n>     know what way?)\n\nI think it would be a good idea to introduce a new category for\ndeprecated commands. After all, next to planning for the removal we\nshould also actively discourage the use of such commands. But if they\nare still prominently featured in git(1) then people might be misled and\nuse them, only to get a deprecation warning thrown at them.\n\n> diff --git a/git.c b/git.c\n> index 83eac0aeab7..87d61f12594 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -51,7 +52,13 @@ const char git_more_info_string[] =\n>  \n>  static int use_pager = -1;\n>  \n> -static void list_builtins(struct string_list *list, unsigned int exclude_option);\n> +/*\n> + * 'include_option' and 'exclude_option' are mutually exclusive.\n> + *\n> + * The default ('!include_option') is to include everything\n> + * except those filtered out by 'exclude_option'.\n> + */\n> +static void list_builtins(struct string_list *list, unsigned int include_option, unsigned int exclude_option);\n\nNit: let's wrap this overly long line.\n\n> @@ -668,13 +677,20 @@ int is_builtin(const char *s)\n>  \treturn !!get_builtin(s);\n>  }\n>  \n> -static void list_builtins(struct string_list *out, unsigned int exclude_option)\n> +static void list_builtins(struct string_list *out, unsigned int include_option, unsigned int exclude_option)\n\nSame here.\n\n>  {\n> -\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n> -\t\tif (exclude_option &&\n> -\t\t    (commands[i].option & exclude_option))\n> -\t\t\tcontinue;\n> -\t\tstring_list_append(out, commands[i].cmd);\n> +\tif (include_option && exclude_option)\n> +\t\tBUG(\"'include_option' and 'exclude_option' are mutually exclusive\");\n> +\tif (include_option) {\n> +\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++)\n> +\t\t\tif (commands[i].option & include_option)\n> +\t\t\t\tstring_list_append(out, commands[i].cmd);\n> +\t} else {\n> +\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n> +\t\t\tif (commands[i].option & exclude_option)\n> +\t\t\t\tcontinue;\n> +\t\t\tstring_list_append(out, commands[i].cmd);\n> +\t\t}\n>  \t}\n>  }\n\nWe could combine these two loops into one:\n\n    for (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n        if (include_option && !(commands[i].option & include_option))\n            continue;\n        if (exclude_option && (commands[i].option & exclude_option))\n            continue;\n        string_list_append(out, commands[i].cmd);\n    }\n\nWhich results in a bit less code duplication.\n\npatrick\n"},{"id":"525923","messageId":"a56d716f-8598-46d0-9b63-8d44b482e1e4@app.fastmail.com","threadId":"64048","inReplyTo":"aL_MdiBLx9sssBwc@pks.im","subject":"Re: [PATCH v3 1/8] git: add `deprecated` category to --list-cmds","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2025-09-09T08:02:11Z","receivedAt":"2025-09-09T08:02:33Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Tue, Sep 9, 2025, at 08:43, Patrick Steinhardt wrote:\n> On Mon, Sep 08, 2025 at 05:36:12PM +0200,\n> kristofferhaugsbakk@fastmail.com wrote:\n>> Notes (series):\n>>     v3 (new):\n>>\n>>     This is something I wanted to submit independently until the point about\n>>     aliasing builtins was brought up.[1]  It will help (in a small way) with\n>>     the upcoming patch “git: allow alias-shadowing deprecated builtins”.\n>>\n>>     By the way: should `command-list.txt` be updated in some way (I didn’t\n>>     know what way?)\n>\n> I think it would be a good idea to introduce a new category for\n> deprecated commands. After all, next to planning for the removal we\n> should also actively discourage the use of such commands. But if they\n> are still prominently featured in git(1) then people might be misled and\n> use them, only to get a deprecation warning thrown at them.\n\nThanks.  I’ll add your Ack if that’s okay.\n\n>>[snip]\n>> +static void list_builtins(struct string_list *list, unsigned int include_option, unsigned int exclude_option);\n>\n> Nit: let's wrap this overly long line.\n\nI’ll do both of these, thanks.  In the same commit/patch I think.\n\n>[snip]\n> Same here.\n>[snip]\n>>  {\n>> -\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n>> -\t\tif (exclude_option &&\n>> -\t\t    (commands[i].option & exclude_option))\n>> -\t\t\tcontinue;\n>> -\t\tstring_list_append(out, commands[i].cmd);\n>> +\tif (include_option && exclude_option)\n>> +\t\tBUG(\"'include_option' and 'exclude_option' are mutually exclusive\");\n>> +\tif (include_option) {\n>> +\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++)\n>> +\t\t\tif (commands[i].option & include_option)\n>> +\t\t\t\tstring_list_append(out, commands[i].cmd);\n>> +\t} else {\n>> +\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n>> +\t\t\tif (commands[i].option & exclude_option)\n>> +\t\t\t\tcontinue;\n>> +\t\t\tstring_list_append(out, commands[i].cmd);\n>> +\t\t}\n>>  \t}\n>>  }\n>\n> We could combine these two loops into one:\n>\n>     for (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n>         if (include_option && !(commands[i].option & include_option))\n>             continue;\n>         if (exclude_option && (commands[i].option & exclude_option))\n>             continue;\n>         string_list_append(out, commands[i].cmd);\n>     }\n>\n> Which results in a bit less code duplication.\n\nThat’s more elegant.  I’ll use that.  And I’ll remove the function doc\nnow that it won’t be relevant.\n\nCheers\n\n-- \nKristoffer\n"},{"id":"525983","messageId":"cover.1757446619.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757345711.git.code@khaugsbakk.name","subject":"[PATCH v4 0/7] you-still-use-that??: improve breaking changes troubleshooting","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-09T19:45:50Z","receivedAt":"2025-09-09T19:46:21Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nBased on the recent i-still-use-that reports about whatchanged, improve\nthe error reporting with this command in mind:\n\n1. Give more possible actions instead of just (only) asking them to send\n   an email\n2. Hint how to replace their git-whatchanged(1) use with git-log(1) or\n   an alias `whatchanged` (you can alias deprecated commands now)\n3. Minor documentation changes\n4. Add `deprecated` to `git --list-cmds`\n\n§ What the errors now look like\n\n    $ /git whatchanged\n    'git whatchanged' is nominated for removal.\n\n    hint: You can replace 'git whatchanged <opts>' with:\n    hint:   git log <opts> --raw --no-merges\n    hint: Or make an alias:\n    hint:   git config set --global alias.whatchanged 'log --raw --no-merges'\n\n    If you still use this command, here's what you can do:\n\n    - read https://git-scm.com/docs/BreakingChanges.html\n    - check if anyone has discussed this on the mailing\n      list and if they came up with something that can\n      help you: https://lore.kernel.org/git/?q=git%20whatchanged\n    - send an email to <git@vger.kernel.org> to let us\n      know that you still use this command and were unable\n      to determine a suitable replacement\n\n    fatal: refusing to run without --i-still-use-this\n    $ git pack-redundant\n    'git pack-redundant' is nominated for removal.\n    If you still use this command, here's what you can do:\n\n    - read https://git-scm.com/docs/BreakingChanges.html\n    - check if anyone has discussed this on the mailing\n      list and if they came up with something that can\n      help you: https://lore.kernel.org/git/?q=git%20pack-redundant\n    - send an email to <git@vger.kernel.org> to let us\n      know that you still use this command and were unable\n      to determine a suitable replacement\n\n    fatal: refusing to run without --i-still-use-this\n\n§ Changes in v4\n\n• Patch 1: updated with suggestion from Patrick\n• Patch 2: better `is_deprecated_command` implementation suggested by\n  Junio and Peff\n• Minor adjustments based on the previous\n\nKristoffer Haugsbakk (7):\n  git: add `deprecated` category to --list-cmds\n  git: allow alias-shadowing deprecated builtins\n  t0014: test shadowing of aliases for a sample of builtins\n  you-still-use-that??: help the user help themselves\n  whatchanged: tell users the git-log(1) equivalent\n  whatchanged: remove not-even-shorter clause\n  BreakingChanges: remove claim about whatchanged reports\n\n Documentation/BreakingChanges.adoc |  2 +-\n Documentation/config/alias.adoc    |  3 ++-\n Documentation/git-whatchanged.adoc |  8 ++++--\n Documentation/git.adoc             |  3 ++-\n builtin/log.c                      |  8 +++++-\n builtin/pack-redundant.c           |  2 +-\n git-compat-util.h                  |  2 +-\n git.c                              | 43 ++++++++++++++++++++++++------\n t/t0014-alias.sh                   | 34 +++++++++++++++++++++++\n usage.c                            | 33 ++++++++++++++++++-----\n 10 files changed, 115 insertions(+), 23 deletions(-)\n\nInterdiff against v3:\ndiff --git a/git.c b/git.c\nindex a452ce3f9e9..b3aafebfe4c 100644\n--- a/git.c\n+++ b/git.c\n@@ -52,13 +52,9 @@ const char git_more_info_string[] =\n \n static int use_pager = -1;\n \n-/*\n- * 'include_option' and 'exclude_option' are mutually exclusive.\n- *\n- * The default ('!include_option') is to include everything\n- * except those filtered out by 'exclude_option'.\n- */\n-static void list_builtins(struct string_list *list, unsigned int include_option, unsigned int exclude_option);\n+static void list_builtins(struct string_list *list,\n+\t\t\t  unsigned int include_option,\n+\t\t\t  unsigned int exclude_option);\n \n static void exclude_helpers_from_list(struct string_list *list)\n {\n@@ -677,18 +673,16 @@ int is_builtin(const char *s)\n \treturn !!get_builtin(s);\n }\n \n-static void list_builtins(struct string_list *out, unsigned int include_option, unsigned int exclude_option)\n+static void list_builtins(struct string_list *out,\n+\t\t\t  unsigned int include_option,\n+\t\t\t  unsigned int exclude_option)\n {\n-\tif (include_option && exclude_option)\n-\t\tBUG(\"'include_option' and 'exclude_option' are mutually exclusive\");\n-\tif (include_option) {\n-\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++)\n-\t\t\tif (commands[i].option & include_option)\n-\t\t\t\tstring_list_append(out, commands[i].cmd);\n-\t} else {\n-\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++)\n-\t\t\tif (!(commands[i].option & exclude_option))\n-\t\t\t\tstring_list_append(out, commands[i].cmd);\n+\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n+\t\tif (include_option && !(commands[i].option & include_option))\n+\t\t\tcontinue;\n+\t\tif (exclude_option && (commands[i].option & exclude_option))\n+\t\t\tcontinue;\n+\t\tstring_list_append(out, commands[i].cmd);\n \t}\n }\n \n@@ -809,8 +803,8 @@ static void execv_dashed_external(const char **argv)\n \n static int is_deprecated_command(const char *cmd)\n {\n-\treturn !strcmp(cmd, \"whatchanged\") ||\n-\t       !strcmp(cmd, \"pack-redundant\");\n+\tstruct cmd_struct *builtin = get_builtin(cmd);\n+\treturn builtin && (builtin->option & DEPRECATED);\n }\n \n static int run_argv(struct strvec *args)\nRange-diff against v3:\n1:  bdc683a92b3 ! 1:  66e6a9554b1 git: add `deprecated` category to --list-cmds\n    @@ Commit message\n     \n         Let’s expand the experimental `--list-cmds`[1] to allow users and\n         programs to query for this information.  We will also use this in an\n    -    upcoming commit to assert that all deprecated commands will have been\n    -    covered in some manner.\n    +    upcoming commit to implement `is_deprecated_command`.\n     \n         [1]: Using something which is experimental to query for deprecations is\n             perhaps not the most ideal approach, but it is simple to implement\n             and better than having to scan the documentation\n     \n    +    Acked-by: Patrick Steinhardt <ps@pks.im>\n    +    Helped-by: Patrick Steinhardt <ps@pks.im>\n         Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n     \n     \n      ## Notes (series) ##\n    +    v4:\n    +\n    +    Incorporate Patrick’s suggestions about the for-loop refactor and\n    +    formatting the overlong lines.  Now drop the function doc since it\n    +    doesn’t apply anymore.\n    +\n    +    Also adjust the commit message now that we use it in the C source in an\n    +    upcoming commit, not just/only as an assert-helper (in an upcoming\n    +    commit).\n    +\n         v3 (new):\n     \n         This is something I wanted to submit independently until the point about\n    @@ git.c: const char git_more_info_string[] =\n      static int use_pager = -1;\n      \n     -static void list_builtins(struct string_list *list, unsigned int exclude_option);\n    -+/*\n    -+ * 'include_option' and 'exclude_option' are mutually exclusive.\n    -+ *\n    -+ * The default ('!include_option') is to include everything\n    -+ * except those filtered out by 'exclude_option'.\n    -+ */\n    -+static void list_builtins(struct string_list *list, unsigned int include_option, unsigned int exclude_option);\n    ++static void list_builtins(struct string_list *list,\n    ++\t\t\t  unsigned int include_option,\n    ++\t\t\t  unsigned int exclude_option);\n      \n      static void exclude_helpers_from_list(struct string_list *list)\n      {\n    @@ git.c: int is_builtin(const char *s)\n      }\n      \n     -static void list_builtins(struct string_list *out, unsigned int exclude_option)\n    -+static void list_builtins(struct string_list *out, unsigned int include_option, unsigned int exclude_option)\n    ++static void list_builtins(struct string_list *out,\n    ++\t\t\t  unsigned int include_option,\n    ++\t\t\t  unsigned int exclude_option)\n      {\n    --\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n    + \tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n     -\t\tif (exclude_option &&\n     -\t\t    (commands[i].option & exclude_option))\n    --\t\t\tcontinue;\n    --\t\tstring_list_append(out, commands[i].cmd);\n    -+\tif (include_option && exclude_option)\n    -+\t\tBUG(\"'include_option' and 'exclude_option' are mutually exclusive\");\n    -+\tif (include_option) {\n    -+\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++)\n    -+\t\t\tif (commands[i].option & include_option)\n    -+\t\t\t\tstring_list_append(out, commands[i].cmd);\n    -+\t} else {\n    -+\t\tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n    -+\t\t\tif (commands[i].option & exclude_option)\n    -+\t\t\t\tcontinue;\n    -+\t\t\tstring_list_append(out, commands[i].cmd);\n    -+\t\t}\n    ++\t\tif (include_option && !(commands[i].option & include_option))\n    ++\t\t\tcontinue;\n    ++\t\tif (exclude_option && (commands[i].option & exclude_option))\n    + \t\t\tcontinue;\n    + \t\tstring_list_append(out, commands[i].cmd);\n      \t}\n    - }\n    - \n2:  183dd68d09d < -:  ----------- git: make the two loops look more symmetric\n3:  eec01cbac16 ! 2:  672253e0e71 git: allow alias-shadowing deprecated builtins\n    @@ Commit message\n     \n     \n      ## Notes (series) ##\n    +    v4:\n    +\n    +    Better `is_deprecated_command` implementation.\n    +\n         v3 (new):\n     \n         Prerequisite for telling the user that they can alias `whatchanged` to\n    @@ git.c: static void execv_dashed_external(const char **argv)\n      \n     +static int is_deprecated_command(const char *cmd)\n     +{\n    -+\treturn !strcmp(cmd, \"whatchanged\") ||\n    -+\t       !strcmp(cmd, \"pack-redundant\");\n    ++\tstruct cmd_struct *builtin = get_builtin(cmd);\n    ++\treturn builtin && (builtin->option & DEPRECATED);\n     +}\n     +\n      static int run_argv(struct strvec *args)\n4:  80fb02caeeb = 3:  00108f28f82 t0014: test shadowing of aliases for a sample of builtins\n5:  d25ee26f989 = 4:  6bdcaf7f80f you-still-use-that??: help the user help themselves\n6:  50621a0748f = 5:  58de9767b22 whatchanged: tell users the git-log(1) equivalent\n7:  812c9870f1b = 6:  407b430d02c whatchanged: remove not-even-shorter clause\n8:  0d23a4badf0 = 7:  fee752d2fb0 BreakingChanges: remove claim about whatchanged reports\n\nbase-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525984","messageId":"66e6a9554b16b2079e5613a415a2d27a601d146b.1757446619.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757446619.git.code@khaugsbakk.name","subject":"[PATCH v4 1/7] git: add `deprecated` category to --list-cmds","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-09T19:45:51Z","receivedAt":"2025-09-09T19:46:41Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nWith 145 builtin commands (according to `git --list-cmds=builtins`),\nusers are probably not keeping on top of which ones (if any) are\ndeprecated.\n\nLet’s expand the experimental `--list-cmds`[1] to allow users and\nprograms to query for this information.  We will also use this in an\nupcoming commit to implement `is_deprecated_command`.\n\n[1]: Using something which is experimental to query for deprecations is\n    perhaps not the most ideal approach, but it is simple to implement\n    and better than having to scan the documentation\n\nAcked-by: Patrick Steinhardt <ps@pks.im>\nHelped-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v4:\n    \n    Incorporate Patrick’s suggestions about the for-loop refactor and\n    formatting the overlong lines.  Now drop the function doc since it\n    doesn’t apply anymore.\n    \n    Also adjust the commit message now that we use it in the C source in an\n    upcoming commit, not just/only as an assert-helper (in an upcoming\n    commit).\n    \n    v3 (new):\n    \n    This is something I wanted to submit independently until the point about\n    aliasing builtins was brought up.[1]  It will help (in a small way) with\n    the upcoming patch “git: allow alias-shadowing deprecated builtins”.\n    \n    By the way: should `command-list.txt` be updated in some way (I didn’t\n    know what way?)\n    \n    🔗 1: https://lore.kernel.org/git/cover.1756311355.git.code@khaugsbakk.name/T/#mee19f8d39572f9021f9d3000758e87b6c32c967c\n\n Documentation/git.adoc |  3 ++-\n git.c                  | 24 ++++++++++++++++--------\n 2 files changed, 18 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git.adoc b/Documentation/git.adoc\nindex 743b7b00e4d..a2f0838b168 100644\n--- a/Documentation/git.adoc\n+++ b/Documentation/git.adoc\n@@ -219,7 +219,8 @@ If you just want to run git as if it was started in `<path>` then use\n \tList commands by group. This is an internal/experimental\n \toption and may change or be removed in the future. Supported\n \tgroups are: builtins, parseopt (builtin commands that use\n-\tparse-options), main (all commands in libexec directory),\n+\tparse-options), deprecated (deprecated builtins),\n+\tmain (all commands in libexec directory),\n \tothers (all other commands in `$PATH` that have git- prefix),\n \tlist-<category> (see categories in command-list.txt),\n \tnohelpers (exclude helper commands), alias and config\ndiff --git a/git.c b/git.c\nindex 83eac0aeab7..fca6ebfce3d 100644\n--- a/git.c\n+++ b/git.c\n@@ -28,6 +28,7 @@\n #define NEED_WORK_TREE\t\t(1<<3)\n #define DELAY_PAGER_CONFIG\t(1<<4)\n #define NO_PARSEOPT\t\t(1<<5) /* parse-options is not used */\n+#define DEPRECATED\t\t(1<<6)\n \n struct cmd_struct {\n \tconst char *cmd;\n@@ -51,7 +52,9 @@ const char git_more_info_string[] =\n \n static int use_pager = -1;\n \n-static void list_builtins(struct string_list *list, unsigned int exclude_option);\n+static void list_builtins(struct string_list *list,\n+\t\t\t  unsigned int include_option,\n+\t\t\t  unsigned int exclude_option);\n \n static void exclude_helpers_from_list(struct string_list *list)\n {\n@@ -88,7 +91,7 @@ static int list_cmds(const char *spec)\n \t\tint len = sep - spec;\n \n \t\tif (match_token(spec, len, \"builtins\"))\n-\t\t\tlist_builtins(&list, 0);\n+\t\t\tlist_builtins(&list, 0, 0);\n \t\telse if (match_token(spec, len, \"main\"))\n \t\t\tlist_all_main_cmds(&list);\n \t\telse if (match_token(spec, len, \"others\"))\n@@ -99,6 +102,8 @@ static int list_cmds(const char *spec)\n \t\t\tlist_aliases(&list);\n \t\telse if (match_token(spec, len, \"config\"))\n \t\t\tlist_cmds_by_config(&list);\n+\t\telse if (match_token(spec, len, \"deprecated\"))\n+\t\t\tlist_builtins(&list, DEPRECATED, 0);\n \t\telse if (len > 5 && !strncmp(spec, \"list-\", 5)) {\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n \n@@ -322,7 +327,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\tif (!strcmp(cmd, \"parseopt\")) {\n \t\t\t\tstruct string_list list = STRING_LIST_INIT_DUP;\n \n-\t\t\t\tlist_builtins(&list, NO_PARSEOPT);\n+\t\t\t\tlist_builtins(&list, 0, NO_PARSEOPT);\n \t\t\t\tfor (size_t i = 0; i < list.nr; i++)\n \t\t\t\t\tprintf(\"%s \", list.items[i].string);\n \t\t\t\tstring_list_clear(&list, 0);\n@@ -590,7 +595,7 @@ static struct cmd_struct commands[] = {\n \t{ \"notes\", cmd_notes, RUN_SETUP },\n \t{ \"pack-objects\", cmd_pack_objects, RUN_SETUP },\n #ifndef WITH_BREAKING_CHANGES\n-\t{ \"pack-redundant\", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT },\n+\t{ \"pack-redundant\", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT | DEPRECATED },\n #endif\n \t{ \"pack-refs\", cmd_pack_refs, RUN_SETUP },\n \t{ \"patch-id\", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },\n@@ -647,7 +652,7 @@ static struct cmd_struct commands[] = {\n \t{ \"verify-tag\", cmd_verify_tag, RUN_SETUP },\n \t{ \"version\", cmd_version },\n #ifndef WITH_BREAKING_CHANGES\n-\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP },\n+\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP | DEPRECATED },\n #endif\n \t{ \"worktree\", cmd_worktree, RUN_SETUP },\n \t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n@@ -668,11 +673,14 @@ int is_builtin(const char *s)\n \treturn !!get_builtin(s);\n }\n \n-static void list_builtins(struct string_list *out, unsigned int exclude_option)\n+static void list_builtins(struct string_list *out,\n+\t\t\t  unsigned int include_option,\n+\t\t\t  unsigned int exclude_option)\n {\n \tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n-\t\tif (exclude_option &&\n-\t\t    (commands[i].option & exclude_option))\n+\t\tif (include_option && !(commands[i].option & include_option))\n+\t\t\tcontinue;\n+\t\tif (exclude_option && (commands[i].option & exclude_option))\n \t\t\tcontinue;\n \t\tstring_list_append(out, commands[i].cmd);\n \t}\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525985","messageId":"672253e0e7167c40290d1fef6d5076adfbe25d80.1757446619.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757446619.git.code@khaugsbakk.name","subject":"[PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-09T19:45:52Z","receivedAt":"2025-09-09T19:47:00Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\ngit-whatchanged(1) is deprecated and you need to pass\n`--i-still-use-this` in order to force it to work as before.\nThere are two affected users, or usages:\n\n1. people who use the command in scripts; and\n2. people who are used to using it interactively.\n\nFor (1) the replacement is straightforward.[1]  But people in (2) might\nlike the name or be really used to typing it.[3]\n\nAn obvious first thought is to suggest aliasing `whatchanged` to the\ngit-log(1) equivalent.[1]  But this doesn’t work and is awkward since you\ncannot shadow builtins via aliases.\n\nNow you are left in an uncomfortable limbo; your alias won’t work until\nthe command is removed for good.\n\nLet’s lift this limitation by allowing *deprecated* builtins to be\nshadowed by aliases.\n\nThe only observed demand for aliasing has been for git-whatchanged(1),\nnot for git-pack-redundant(1).  But let’s be consistent and treat all\ndeprecated commands the same.\n\n[1]:\n\n        git log --raw --no-merges\n\n     With a minor caveat: you get different outputs if you happen to\n     have empty commits (no changes)[2]\n[2]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n[3]: https://lore.kernel.org/git/BL3P221MB0449288C8B0FA448A227FD48833AA@BL3P221MB0449.NAMP221.PROD.OUTLOOK.COM/\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v4:\n    \n    Better `is_deprecated_command` implementation.\n    \n    v3 (new):\n    \n    Prerequisite for telling the user that they can alias `whatchanged` to\n    `git log --raw --no-merged`.\n    \n    Link: https://lore.kernel.org/git/cover.1756311355.git.code@khaugsbakk.name/T/#md434b0968f499263262fb1805d82b788b8349d9a\n    \n    > I think that is good advice, but... it won't do anything until we\n    > actually drop the whatchanged command, since until then we'll refuse to\n    > override the command (even the crippled --i-still-use-this one).\n    >\n    > We'd need something like the patch here:\n    \n    ❦\n    \n    The\n    \n        test_file_not_empty expect\n    \n    is here because the git(1) command could fail.  Make sure that it did\n    indeed output anyhing on stdout.  (Or if a previous redirect to `expect`\n    outputted something it should be completely different to `actual` in any\n    case)\n    \n    I don’t know if this is just a waste.\n\n Documentation/config/alias.adoc |  3 ++-\n git.c                           | 19 +++++++++++++++++++\n t/t0014-alias.sh                | 17 +++++++++++++++++\n 3 files changed, 38 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config/alias.adoc b/Documentation/config/alias.adoc\nindex 2c5db0ad842..3c8fab3a95c 100644\n--- a/Documentation/config/alias.adoc\n+++ b/Documentation/config/alias.adoc\n@@ -3,7 +3,8 @@ alias.*::\n \tafter defining `alias.last = cat-file commit HEAD`, the invocation\n \t`git last` is equivalent to `git cat-file commit HEAD`. To avoid\n \tconfusion and troubles with script usage, aliases that\n-\thide existing Git commands are ignored. Arguments are split by\n+\thide existing Git commands are ignored except for deprecated\n+\tcommands.  Arguments are split by\n \tspaces, the usual shell quoting and escaping are supported.\n \tA quote pair or a backslash can be used to quote them.\n +\ndiff --git a/git.c b/git.c\nindex fca6ebfce3d..b3aafebfe4c 100644\n--- a/git.c\n+++ b/git.c\n@@ -801,6 +801,12 @@ static void execv_dashed_external(const char **argv)\n \t\texit(128);\n }\n \n+static int is_deprecated_command(const char *cmd)\n+{\n+\tstruct cmd_struct *builtin = get_builtin(cmd);\n+\treturn builtin && (builtin->option & DEPRECATED);\n+}\n+\n static int run_argv(struct strvec *args)\n {\n \tint done_alias = 0;\n@@ -808,6 +814,19 @@ static int run_argv(struct strvec *args)\n \tstruct string_list_item *seen;\n \n \twhile (1) {\n+\t\t/*\n+\t\t * Allow deprecated commands to be overridden by aliases. This\n+\t\t * creates a seamless path forward for people who want to keep\n+\t\t * using the name after it is gone, but want to skip the\n+\t\t * deprecation complaint in the meantime.\n+\t\t */\n+\t\tif (is_deprecated_command(args->v[0]) &&\n+\t\t    alias_lookup(args->v[0])) {\n+\t\t\tif (!handle_alias(args))\n+\t\t\t\tbreak;\n+\t\t\tdone_alias = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\t/*\n \t\t * If we tried alias and futzed with our environment,\n \t\t * it no longer is safe to invoke builtins directly in\ndiff --git a/t/t0014-alias.sh b/t/t0014-alias.sh\nindex 854d59ec58c..89bedb9f73b 100755\n--- a/t/t0014-alias.sh\n+++ b/t/t0014-alias.sh\n@@ -55,4 +55,21 @@ test_expect_success 'tracing a shell alias with arguments shows trace of prepare\n \ttest_cmp expect actual\n '\n \n+can_alias_deprecated_builtin () {\n+\tcmd=\"$1\" &&\n+\t# some git(1) commands will fail for `-h` (the case for\n+\t# git-status as of 2025-09-07)\n+\ttest_might_fail git status -h >expect &&\n+\ttest_file_not_empty expect &&\n+\ttest_might_fail git -c alias.\"$cmd\"=status \"$cmd\" -h >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'can alias-shadow deprecated builtins' '\n+\tfor cmd in $(git --list-cmds=deprecated)\n+\tdo\n+\t\tcan_alias_deprecated_builtin \"$cmd\" || return 1\n+\tdone\n+'\n+\n test_done\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525986","messageId":"00108f28f82e618cb39dadb8161841c29a766d1f.1757446619.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757446619.git.code@khaugsbakk.name","subject":"[PATCH v4 3/7] t0014: test shadowing of aliases for a sample of builtins","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-09T19:45:53Z","receivedAt":"2025-09-09T19:47:19Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThe previous commit added a test for shadowing deprecated builtins.\nLet’s make the test suite more complete by exercising a sample of\nthe builtins and in turn test the documentation for git-config(1):\n\n    To avoid confusion and troubles with script usage, aliases that hide\n    existing Git commands are ignored except for deprecated commands.\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v3 (new):\n    \n    Only a sample of builtins since the test file takes about 600ms longer\n    if I test all of them.\n\n t/t0014-alias.sh | 17 +++++++++++++++++\n 1 file changed, 17 insertions(+)\n\ndiff --git a/t/t0014-alias.sh b/t/t0014-alias.sh\nindex 89bedb9f73b..bf7e6512bb1 100755\n--- a/t/t0014-alias.sh\n+++ b/t/t0014-alias.sh\n@@ -72,4 +72,21 @@ test_expect_success 'can alias-shadow deprecated builtins' '\n \tdone\n '\n \n+cannot_alias_regular_builtin () {\n+\tcmd=\"$1\" &&\n+\t# some git(1) commands will fail... (see above)\n+\ttest_might_fail git \"$cmd\" -h >expect &&\n+\ttest_file_not_empty expect &&\n+\ttest_might_fail git -c alias.\"$cmd\"=status \"$cmd\" -h >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'cannot alias-shadow a sample of regular builtins' '\n+\tfor cmd in grep check-ref-format interpret-trailers \\\n+\t\tcheckout-index fast-import diagnose rev-list prune\n+\tdo\n+\t\tcannot_alias_regular_builtin \"$cmd\" || return 1\n+\tdone\n+'\n+\n test_done\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525987","messageId":"6bdcaf7f80f48a10cea75ea179e6f06d8b998fbb.1757446619.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757446619.git.code@khaugsbakk.name","subject":"[PATCH v4 4/7] you-still-use-that??: help the user help themselves","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-09T19:45:54Z","receivedAt":"2025-09-09T19:47:38Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nGive the user a list of suggestions for what to do when they run a\ndeprecated command.\n\nThe first order of action will be to check the breaking changes\ndocument;[1] this short error message says nothing about why this\ncommand is deprecated, and in any case going into any kind of detail\nmight overwhelm the user.\n\nThen they can find out if this has been discussed on the mailing list.\nThen users who e.g. are using git-whatchanged(1) can learn that this is\narguably a plug-in replacement:\n\n    git log <opts> --raw --no-merges\n\nFinally they are invited to send an email to the mailing list.\n\nAlso drop the “please add” part in favor of just using the “refusing”\ndie-message; these two would have been right after each other in this\nnew version.\n\nAlso drop “Thanks” since it now would require a new paragraph.\n\n[1]: www.git-scm.com has a disclaimer for these internal documents that\n    says that “This information is specific to the Git project”.  That’s\n    misleading in this particular case.  But users are unlikely to get\n    discouraged from reading about why they (or their programs) cannot run a\n    command any more; it clearly concerns them.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v2:\n    \n    I had second thoughts about the bullet point about send-an-email.\n    Change it to the one Eric Sunshine proposed;[1] make sure to spell\n    out that you can send an email conditioned on not finding a suitable\n    replacement.\n    \n    Also change the area to something more pointed.\n    \n    And also use a clear URL to refer to www.git-scm.com.\n    \n    [1]: https://lore.kernel.org/git/CAPig+cQkVP57n_FE6dJ0uxvai-J7usxKFp8gzfEbPY=Ytsd6=Q@mail.gmail.com/\n    \n    • Change send-an-email bullet point\n    • Change the area\n    • Use www.git-scm.com, not simply git-scm\n    \n    v1:\n    \n    An alternative to linking to www.git-scm.com is to move this document to\n    a regular installed man page:\n    \n        gitbreaking-changes(7)\n    \n    What do you think?\n    \n    I would then have to base my topic on the in-flight\n    pw/3.0-commentchar-auto-deprecation, which in turn depends on\n    ps/config-wo-the-repository.\n    \n    Or just wait a bit for these to settle in.\n\n usage.c | 23 ++++++++++++++++++-----\n 1 file changed, 18 insertions(+), 5 deletions(-)\n\ndiff --git a/usage.c b/usage.c\nindex 81913236a4a..35dc57eb07e 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -7,6 +7,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"trace2.h\"\n+#include \"strbuf.h\"\n \n static void vfreportf(FILE *f, const char *prefix, const char *err, va_list params)\n {\n@@ -377,12 +378,24 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n \n NORETURN void you_still_use_that(const char *command_name)\n {\n+\tstruct strbuf percent_encoded = STRBUF_INIT;\n+\tstrbuf_add_percentencode(&percent_encoded,\n+\t\t\t\t command_name,\n+\t\t\t\t STRBUF_ENCODE_SLASH);\n+\n \tfprintf(stderr,\n \t\t_(\"'%s' is nominated for removal.\\n\"\n-\t\t  \"If you still use this command, please add an extra\\n\"\n-\t\t  \"option, '--i-still-use-this', on the command line\\n\"\n-\t\t  \"and let us know you still use it by sending an e-mail\\n\"\n-\t\t  \"to <git@vger.kernel.org>.  Thanks.\\n\"),\n-\t\tcommand_name);\n+\t\t  \"If you still use this command, here's what you can do:\\n\"\n+\t\t  \"\\n\"\n+\t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n+\t\t  \"- check if anyone has discussed this on the mailing\\n\"\n+\t\t  \"  list and if they came up with something that can\\n\"\n+\t\t  \"  help you: https://lore.kernel.org/git/?q=%s\\n\"\n+\t\t  \"- send an email to <git@vger.kernel.org> to let us\\n\"\n+\t\t  \"  know that you still use this command and were unable\\n\"\n+\t\t  \"  to determine a suitable replacement\\n\"\n+\t\t  \"\\n\"),\n+\t\tcommand_name, percent_encoded.buf);\n+\tstrbuf_release(&percent_encoded);\n \tdie(_(\"refusing to run without --i-still-use-this\"));\n }\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525988","messageId":"58de9767b22ab2563c6afb4bed992301a5772071.1757446619.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757446619.git.code@khaugsbakk.name","subject":"[PATCH v4 5/7] whatchanged: tell users the git-log(1) equivalent","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-09T19:45:55Z","receivedAt":"2025-09-09T19:47:56Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThere have been quite a few `--i-still-use-this` user reports since Git\n2.51.0 was released.[1][2]  And it doesn’t seem like they are reading\nthe man page about the git-log(1) equivalent.\n\nTell them what options to plug into git-log(1).  That template produces\nalmost the same output[3] and is arguably a plug-in replacement.\nConcretely, add an optional `hint` argument so that we can use it right\nafter the initial error line.\n\nAlso mention the same concrete options in the documentation while we’re\nat it.\n\n[1]: E.g.,\n    • https://lore.kernel.org/git/e1a69dea-bcb6-45fc-83d3-9e50d32c410b@5y5.one/\n    • https://lore.kernel.org/git/1011073f-9930-4360-a42f-71eb7421fe3f@chrispalmer.uk/#t\n    • https://lore.kernel.org/git/9fcbfcc4-79f9-421f-b9a4-dc455f7db485@acm.org/#t\n    • https://lore.kernel.org/git/83241BDE-1E0D-489A-9181-C608E9FCC17B@gmail.com/\n[2]: The error message on 2.51.0 does tell them to report it, unconditionally\n[3]: You only get different outputs if you happen to have empty\n     commits (no changes)[4]\n[4]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v3:\n    \n    Add an alias hint now that that is possible.  Also prefix each hint-line\n    with `hint: `.\n    \n    v2:\n    \n    Review found a whitespace error in the prev. patch version.  I found a\n    broken footnote and wanted to expand on the last footnote.\n    \n    • Fix whitespace error (I should have used `ci/check-whitespace.sh\n      v2.51.0`)\n    • Add missing colon (:) to footnote\n    • Expand on footnote; a sentence is enough to summarize the difference\n\n Documentation/git-whatchanged.adoc |  6 +++++-\n builtin/log.c                      |  8 +++++++-\n builtin/pack-redundant.c           |  2 +-\n git-compat-util.h                  |  2 +-\n usage.c                            | 14 ++++++++++----\n 5 files changed, 24 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-whatchanged.adoc b/Documentation/git-whatchanged.adoc\nindex d21484026fe..e71d2aa2d27 100644\n--- a/Documentation/git-whatchanged.adoc\n+++ b/Documentation/git-whatchanged.adoc\n@@ -24,7 +24,11 @@ Shows commit logs and diff output each commit introduces.\n \n New users are encouraged to use linkgit:git-log[1] instead.  The\n `whatchanged` command is essentially the same as linkgit:git-log[1]\n-but defaults to showing the raw format diff output and skipping merges.\n+but defaults to showing the raw format diff output and skipping merges:\n+\n+----\n+git log --raw --no-merges\n+----\n \n The command is primarily kept for historical reasons; fingers of\n many people who learned Git long before `git log` was invented by\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c2f8bbf8630..1d1e6e9130a 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -543,7 +543,13 @@ int cmd_whatchanged(int argc,\n \tcmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);\n \n \tif (!cfg.i_still_use_this)\n-\t\tyou_still_use_that(\"git whatchanged\");\n+\t\tyou_still_use_that(\"git whatchanged\",\n+\t\t\t\t   _(\"\\n\"\n+\t\t\t\t     \"hint: You can replace 'git whatchanged <opts>' with:\\n\"\n+\t\t\t\t     \"hint:\\tgit log <opts> --raw --no-merges\\n\"\n+\t\t\t\t     \"hint: Or make an alias:\\n\"\n+\t\t\t\t     \"hint:\\tgit config set --global alias.whatchanged 'log --raw --no-merges'\\n\"\n+\t\t\t\t     \"\\n\"));\n \n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\ndiff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c\nindex fe81c293e3a..5d5ae4afa28 100644\n--- a/builtin/pack-redundant.c\n+++ b/builtin/pack-redundant.c\n@@ -626,7 +626,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED, s\n \t}\n \n \tif (!i_still_use_this)\n-\t\tyou_still_use_that(\"git pack-redundant\");\n+\t\tyou_still_use_that(\"git pack-redundant\", NULL);\n \n \tif (load_all_packs)\n \t\tload_all();\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 9408f463e31..398e0fac4fa 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -460,7 +460,7 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));\n \n void show_usage_if_asked(int ac, const char **av, const char *err);\n \n-NORETURN void you_still_use_that(const char *command_name);\n+NORETURN void you_still_use_that(const char *command_name, const char *hint);\n \n #ifndef NO_OPENSSL\n #ifdef APPLE_COMMON_CRYPTO\ndiff --git a/usage.c b/usage.c\nindex 35dc57eb07e..7545a616453 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -376,7 +376,8 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n \tva_end(ap);\n }\n \n-NORETURN void you_still_use_that(const char *command_name)\n+\n+NORETURN void you_still_use_that(const char *command_name, const char *hint)\n {\n \tstruct strbuf percent_encoded = STRBUF_INIT;\n \tstrbuf_add_percentencode(&percent_encoded,\n@@ -384,8 +385,13 @@ NORETURN void you_still_use_that(const char *command_name)\n \t\t\t\t STRBUF_ENCODE_SLASH);\n \n \tfprintf(stderr,\n-\t\t_(\"'%s' is nominated for removal.\\n\"\n-\t\t  \"If you still use this command, here's what you can do:\\n\"\n+\t\t_(\"'%s' is nominated for removal.\\n\"), command_name);\n+\n+\tif (hint)\n+\t\tfputs(hint, stderr);\n+\n+\tfprintf(stderr,\n+\t\t_(\"If you still use this command, here's what you can do:\\n\"\n \t\t  \"\\n\"\n \t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n \t\t  \"- check if anyone has discussed this on the mailing\\n\"\n@@ -395,7 +401,7 @@ NORETURN void you_still_use_that(const char *command_name)\n \t\t  \"  know that you still use this command and were unable\\n\"\n \t\t  \"  to determine a suitable replacement\\n\"\n \t\t  \"\\n\"),\n-\t\tcommand_name, percent_encoded.buf);\n+\t\tpercent_encoded.buf);\n \tstrbuf_release(&percent_encoded);\n \tdie(_(\"refusing to run without --i-still-use-this\"));\n }\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525989","messageId":"407b430d02c95c2a82470e76ec0d5d810169314f.1757446619.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757446619.git.code@khaugsbakk.name","subject":"[PATCH v4 6/7] whatchanged: remove not-even-shorter clause","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-09T19:45:56Z","receivedAt":"2025-09-09T19:48:15Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThe closest equivalent is `git log --raw --no-merges`.\n\nAlso change to “defaults” (implicit plural).\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n Documentation/git-whatchanged.adoc | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-whatchanged.adoc b/Documentation/git-whatchanged.adoc\nindex e71d2aa2d27..436e219b7d0 100644\n--- a/Documentation/git-whatchanged.adoc\n+++ b/Documentation/git-whatchanged.adoc\n@@ -15,7 +15,7 @@ WARNING\n -------\n `git whatchanged` has been deprecated and is scheduled for removal in\n a future version of Git, as it is merely `git log` with different\n-default; `whatchanged` is not even shorter to type than `log --raw`.\n+defaults.\n \n DESCRIPTION\n -----------\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525990","messageId":"fee752d2fb0a8702683d94e76b6ee54e7fad8c92.1757446619.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757446619.git.code@khaugsbakk.name","subject":"[PATCH v4 7/7] BreakingChanges: remove claim about whatchanged reports","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-09T19:45:57Z","receivedAt":"2025-09-09T19:48:34Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThis was written in e836757e14b (whatschanged: list it in\nBreakingChanges document, 2025-05-12) which was on the same\ntopic that added the `--i-still-use-this` requirement.[1]\n\nMaybe it was a work-in-progress comment/status.\n\n[1]: jc/you-still-use-whatchanged\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    Footnote solely to avoid awkward paragraph wrapping...\n\n Documentation/BreakingChanges.adoc | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc\nindex f8d2eba061c..c4985163c3c 100644\n--- a/Documentation/BreakingChanges.adoc\n+++ b/Documentation/BreakingChanges.adoc\n@@ -235,7 +235,7 @@ These features will be removed.\n   equivalent `git log --raw`.  We have nominated the command for\n   removal, have changed the command to refuse to work unless the\n   `--i-still-use-this` option is given, and asked the users to report\n-  when they do so.  So far there hasn't been a single complaint.\n+  when they do so.\n +\n The command will be removed.\n \n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"525992","messageId":"c819384c-b3c4-4443-bded-130339a0b4bd@app.fastmail.com","threadId":"64048","inReplyTo":"CAPig+cSL=-gD5+WomF7-hYjVJ_PH0m+0i8g3F=E_U3k=QNHr8Q@mail.gmail.com","subject":"Re: [PATCH 1/4] usage: help the user help themselves","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-09T20:01:32Z","receivedAt":"2025-09-09T20:02:32Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Wed, Sep 3, 2025, at 18:50, Eric Sunshine wrote:\n> I realize that the changes made by this series are not in any released\n> version yet, but from reading the emails still arriving which argue\n> for retaining the command for reasons of muscle memory or because of\n> its (strong) mnemonic value, I suspect that the hint(s) this series\n> adds may not be complete enough. In particular, the advice this series\n> adds (use `git log --raw --no-merges`) seems to be primarily aimed at\n> scripted use of the command. But the muscle memory and mnemonic\n> arguments suggest that advice should be given for interactive use, as\n> well,[—]\n>\n> such as proposing that the user can create an alias.\n\nSorry, I completely missed the part (now a week ago) where you clearly\nwere alluding to making a whatchanged-alias to begin with, which made my\ninitial response here quite beside the point.\n\nI shouldn’t respond to this list on mobile.\n\n-- \nKristoffer Haugsbakk\n"},{"id":"525998","messageId":"xmqqjz27ti6i.fsf@gitster.g","threadId":"64048","inReplyTo":"66e6a9554b16b2079e5613a415a2d27a601d146b.1757446619.git.code@khaugsbakk.name","subject":"Re: [PATCH v4 1/7] git: add `deprecated` category to --list-cmds","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-09T21:44:21Z","receivedAt":"2025-09-09T21:44:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"kristofferhaugsbakk@fastmail.com writes:\n\n>     Incorporate Patrick’s suggestions about the for-loop refactor and\n>     formatting the overlong lines.  Now drop the function doc since it\n>     doesn’t apply anymore.\n\nSo, if \"include\" is specified, a command that does not match any of\ncriteria given by those flags is not included, and if \"exclude\" is\nspecified, a command that matches any of the criteria given is not\nincluded.  Which probably makes sense.  The only user of \"include\"\nuses the DEPRECATED flag bit without any exclude option, so it is\nyet to be seen which one between the previous and the current design\ngives an easier-to-use behaviour, but I have no objection to the\nupdated design presented here.\n\nNicely done.\n\nWill replace.  I guess we can mark this round as ready for 'next'?\n\nThanks.\n"},{"id":"526007","messageId":"20250910051347.GA556174@coredump.intra.peff.net","threadId":"64048","inReplyTo":"672253e0e7167c40290d1fef6d5076adfbe25d80.1757446619.git.code@khaugsbakk.name","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-10T05:13:47Z","receivedAt":"2025-09-10T05:13:54Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Sep 09, 2025 at 09:45:52PM +0200, kristofferhaugsbakk@fastmail.com wrote:\n\n> +\t\tif (is_deprecated_command(args->v[0]) &&\n> +\t\t    alias_lookup(args->v[0])) {\n> +\t\t\tif (!handle_alias(args))\n> +\t\t\t\tbreak;\n> +\t\t\tdone_alias = 1;\n> +\t\t\tcontinue;\n> +\t\t}\n\nI think this is failing the SANITIZE=leak jobs, because alias_lookup()\nreturns an allocated string with the alias. You need to capture and free\nit, or introduce an alias_exists() wrapper to do so.\n\nThere might also be a way to do it just by calling handle_alias() and\nchecking its return value (it seems to indicate whether an alias was\nfound, but I didn't look through the whole function carefully, or think\nabout the implications of how the done_alias flag works).\n\n-Peff\n"},{"id":"526025","messageId":"aMFj4MSHiFztkNa-@pks.im","threadId":"64048","inReplyTo":"xmqqjz27ti6i.fsf@gitster.g","subject":"Re: [PATCH v4 1/7] git: add `deprecated` category to --list-cmds","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-10T11:41:20Z","receivedAt":"2025-09-10T11:41:26Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Sep 09, 2025 at 02:44:21PM -0700, Junio C Hamano wrote:\n> kristofferhaugsbakk@fastmail.com writes:\n> \n> >     Incorporate Patrick’s suggestions about the for-loop refactor and\n> >     formatting the overlong lines.  Now drop the function doc since it\n> >     doesn’t apply anymore.\n> \n> So, if \"include\" is specified, a command that does not match any of\n> criteria given by those flags is not included, and if \"exclude\" is\n> specified, a command that matches any of the criteria given is not\n> included.  Which probably makes sense.  The only user of \"include\"\n> uses the DEPRECATED flag bit without any exclude option, so it is\n> yet to be seen which one between the previous and the current design\n> gives an easier-to-use behaviour, but I have no objection to the\n> updated design presented here.\n> \n> Nicely done.\n> \n> Will replace.  I guess we can mark this round as ready for 'next'?\n\nI guess so. All my review feedback was addressed, so I don't have\nanything else to add. Thanks!\n\nPatrick\n"},{"id":"526055","messageId":"20250910154859.GB562601@coredump.intra.peff.net","threadId":"64048","inReplyTo":"20250910051347.GA556174@coredump.intra.peff.net","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-10T15:48:59Z","receivedAt":"2025-09-10T15:49:00Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Sep 10, 2025 at 01:13:47AM -0400, Jeff King wrote:\n\n> On Tue, Sep 09, 2025 at 09:45:52PM +0200, kristofferhaugsbakk@fastmail.com wrote:\n> \n> > +\t\tif (is_deprecated_command(args->v[0]) &&\n> > +\t\t    alias_lookup(args->v[0])) {\n> > +\t\t\tif (!handle_alias(args))\n> > +\t\t\t\tbreak;\n> > +\t\t\tdone_alias = 1;\n> > +\t\t\tcontinue;\n> > +\t\t}\n> \n> I think this is failing the SANITIZE=leak jobs, because alias_lookup()\n> returns an allocated string with the alias. You need to capture and free\n> it, or introduce an alias_exists() wrapper to do so.\n> \n> There might also be a way to do it just by calling handle_alias() and\n> checking its return value (it seems to indicate whether an alias was\n> found, but I didn't look through the whole function carefully, or think\n> about the implications of how the done_alias flag works).\n\nI _think_ this is correct:\n\ndiff --git a/git.c b/git.c\nindex 47857d280f..ea9ec77a88 100644\n--- a/git.c\n+++ b/git.c\n@@ -823,9 +823,7 @@ static int run_argv(struct strvec *args)\n \t\t * deprecation complaint in the meantime.\n \t\t */\n \t\tif (is_deprecated_command(args->v[0]) &&\n-\t\t    alias_lookup(args->v[0])) {\n-\t\t\tif (!handle_alias(args))\n-\t\t\t\tbreak;\n+\t\t    handle_alias(args)) {\n \t\t\tdone_alias = 1;\n \t\t\tcontinue;\n \t\t}\n\nThe handle_alias() function will either:\n\n  - return 0 if it does not find an alias\n\n  - return 1 if it finds an alias and we should loop again to run that\n    command (but set done_alias so we know to suppress in-process\n    builtins below). The \"args\" array will have been updated with the\n    new command name.\n\n  - exit itself if it's a \"!\" shell alias that we exec\n\nSo we just need to loop again when it told us that it found an alias (it\nis tempting to just continue in the loop body, but that would miss the\ncase of a deprecated alias that resolves to another deprecated alias).\n\nAnyway, I believe that is correct and solves the leak issue (because\nhandle_alias() does the lookup and takes care of cleanup itself).\n\n-Peff\n"},{"id":"526056","messageId":"20250910155001.GA1134782@coredump.intra.peff.net","threadId":"64048","inReplyTo":"xmqqjz27ti6i.fsf@gitster.g","subject":"Re: [PATCH v4 1/7] git: add `deprecated` category to --list-cmds","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-10T15:50:01Z","receivedAt":"2025-09-10T15:50:02Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Sep 09, 2025 at 02:44:21PM -0700, Junio C Hamano wrote:\n\n> Will replace.  I guess we can mark this round as ready for 'next'?\n\nThere's a small leak issue in patch 2. It should be a small fix, but\nworth doing as it is caught in CI.\n\n-Peff\n"},{"id":"526063","messageId":"b39ba543-24ad-4bd6-be6e-dc1b8a416eb0@app.fastmail.com","threadId":"64048","inReplyTo":"20250910154859.GB562601@coredump.intra.peff.net","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2025-09-10T17:58:32Z","receivedAt":"2025-09-10T17:58:54Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Wed, Sep 10, 2025, at 17:48, Jeff King wrote:\n>>[snip]\n>> There might also be a way to do it just by calling handle_alias() and\n>> checking its return value (it seems to indicate whether an alias was\n>> found, but I didn't look through the whole function carefully, or think\n>> about the implications of how the done_alias flag works).\n>\n> I _think_ this is correct:\n>\n>[diff snip]\n\nThank you.  The best I could come up was making `alias.c:alias_exists`.\nWhich must be redundant given the call to `handlke_alias()` that you\nmention later here.\n\n> The handle_alias() function will either:\n>\n>   - return 0 if it does not find an alias\n>\n>   - return 1 if it finds an alias and we should loop again to run that\n>     command (but set done_alias so we know to suppress in-process\n>     builtins below). The \"args\" array will have been updated with the\n>     new command name.\n\nThis case and the `continue` seemed to make sense to me eventually.  Not\nthe `break` case though.\n\n>   - exit itself if it's a \"!\" shell alias that we exec\n\nYep.\n\n> So we just need to loop again when it told us that it found an alias (it\n> is tempting to just continue in the loop body, but that would miss the\n> case of a deprecated alias that resolves to another deprecated alias).\n\nYeah, that’s subtle.\n\nDo you think this should be a test case?  I guess it doesn’t hurt.\n\n> Anyway, I believe that is correct and solves the leak issue (because\n> handle_alias() does the lookup and takes care of cleanup itself).\n\nThanks again.  My local test suite passes.  (Except\n`t5801-remote-helpers.sh` which never works for me.)\n\nAnd thanks for pointing this out to begin with.\n\n-- \nKristoffer\n"},{"id":"526067","messageId":"20250910183418.GA1157772@coredump.intra.peff.net","threadId":"64048","inReplyTo":"b39ba543-24ad-4bd6-be6e-dc1b8a416eb0@app.fastmail.com","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-10T18:34:18Z","receivedAt":"2025-09-10T18:34:20Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Sep 10, 2025 at 07:58:32PM +0200, Kristoffer Haugsbakk wrote:\n\n> > So we just need to loop again when it told us that it found an alias (it\n> > is tempting to just continue in the loop body, but that would miss the\n> > case of a deprecated alias that resolves to another deprecated alias).\n> \n> Yeah, that’s subtle.\n> \n> Do you think this should be a test case?  I guess it doesn’t hurt.\n\nYeah, probably would be easy to add. We need two deprecated command\nnames, but it looks like we do have that at this point. :)\n\n> > Anyway, I believe that is correct and solves the leak issue (because\n> > handle_alias() does the lookup and takes care of cleanup itself).\n> \n> Thanks again.  My local test suite passes.  (Except\n> `t5801-remote-helpers.sh` which never works for me.)\n\nI sometimes see failures there because I've checked out and built an old\nversion which creates git-remote-testgit as a built script. That was\nlater moved into t/t5801 via 5afb2ce4cd (remote-testgit: move it into\nthe support directory for t5801, 2019-04-12). But if you have an old\nbuilt copy lying around in the project root, then \"git fetch\" will find\nthat old version first (because it prepends the root GIT_EXEC_PATH to\nPATH when it runs). So we run the old version, which may or may not\nsupport what the more modern test needs.\n\nAnd deleting \"git-remote-testgit\" (or just running \"git clean\") makes it\ngo away.\n\nThat may or may not be your problem, of course. ;)\n\n-Peff\n"},{"id":"526080","messageId":"20fd7486-5f65-490c-866e-d07a19d2b75a@app.fastmail.com","threadId":"64048","inReplyTo":"xmqqjz27ti6i.fsf@gitster.g","subject":"Re: [PATCH v4 1/7] git: add `deprecated` category to --list-cmds","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2025-09-10T20:23:50Z","receivedAt":"2025-09-10T20:24:12Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Tue, Sep 9, 2025, at 23:44, Junio C Hamano wrote:\n> kristofferhaugsbakk@fastmail.com writes:\n>\n>>     Incorporate Patrick’s suggestions about the for-loop refactor and\n>>     formatting the overlong lines.  Now drop the function doc since it\n>>     doesn’t apply anymore.\n>\n> So, if \"include\" is specified, a command that does not match any of\n> criteria given by those flags is not included, and if \"exclude\" is\n> specified, a command that matches any of the criteria given is not\n> included.  Which probably makes sense.\n\nOn second thought I want to add back the “mutually exclusive” check\nsince using both still doesn’t make sense.  (Dunno why I thought\notherwise.  Patrick’s change was strictly a refactoring.)\n\n> The only user of \"include\" uses the DEPRECATED flag bit without any\n> exclude option, so it is yet to be seen which one between the previous\n> and the current design gives an easier-to-use behaviour, but I have no\n> objection to the updated design presented here.\n\nI’m happy with it.  :) But people can suggest an alternative if they\nwould like only one of the include/exclude.\n\nUs Java app. programmers are only permitted to practice bitfields in our\nspare time.\n\n-- \nKristoffer\n"},{"id":"526095","messageId":"xmqqzfb2ouj8.fsf@gitster.g","threadId":"64048","inReplyTo":"20250910155001.GA1134782@coredump.intra.peff.net","subject":"Re: [PATCH v4 1/7] git: add `deprecated` category to --list-cmds","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-10T21:40:59Z","receivedAt":"2025-09-10T21:41:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Tue, Sep 09, 2025 at 02:44:21PM -0700, Junio C Hamano wrote:\n>\n>> Will replace.  I guess we can mark this round as ready for 'next'?\n>\n> There's a small leak issue in patch 2. It should be a small fix, but\n> worth doing as it is caught in CI.\n>\n> -Peff\n\nThanks for being careful, as always.  Very much appreciated.\n"},{"id":"526143","messageId":"eefc3524-9ecf-44e5-a6ba-1b4a0aff3158@app.fastmail.com","threadId":"64048","inReplyTo":"20250910183418.GA1157772@coredump.intra.peff.net","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2025-09-11T17:31:04Z","receivedAt":"2025-09-11T17:31:46Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Wed, Sep 10, 2025, at 20:34, Jeff King wrote:\n> On Wed, Sep 10, 2025 at 07:58:32PM +0200, Kristoffer Haugsbakk wrote:\n>\n>> > So we just need to loop again when it told us that it found an alias (it\n>> > is tempting to just continue in the loop body, but that would miss the\n>> > case of a deprecated alias that resolves to another deprecated alias).\n>>\n>> Yeah, that’s subtle.\n>>\n>> Do you think this should be a test case?  I guess it doesn’t hurt.\n>\n> Yeah, probably would be easy to add. We need two deprecated command\n> names, but it looks like we do have that at this point. :)\n\nThanks. That works fine:\n\n\n     test_expect_success 'can alias-shadow via two deprecated builtins' '\n             # some git(1) commands will fail... (see above)\n             test_might_fail git status -h >expect &&\n             test_file_not_empty expect &&\n             test_might_fail git -c alias.whatchanged=pack-redundant \\\n                     -c alias.pack-redundant=status whatchanged -h >actual &&\n             test_cmp expect actual\n     '\n\nBut it turns out there is a regression in my code with the recursion\ndetection.  Compare:\n\n    $ ./git -c alias.one=two -c alias.two=two one -h\n    'one' is aliased to 'two'\n    'two' is aliased to 'two'\n    fatal: recursive alias: two\n\nwith:\n\n    $ ./git -c alias.whatchanged=pack-redundant -c alias.pack-redundant=whatchanged whatchanged -h\n    'whatchanged' is aliased to 'pack-redundant'\n    'pack-redundant' is aliased to 'whatchanged'\n    'whatchanged' is aliased to 'pack-redundant'\n    'pack-redundant' is aliased to 'whatchanged'\n    'whatchanged' is aliased to 'pack-redundant'\n    'pack-redundant' is aliased to 'whatchanged'\n    'whatchanged' is aliased to 'pack-redundant'\n    'pack-redundant' is aliased to 'whatchanged'\n    [forever]\n\nBoth for your suggested change and my v4.\n"},{"id":"526144","messageId":"20250911203256.GA1894340@coredump.intra.peff.net","threadId":"64048","inReplyTo":"eefc3524-9ecf-44e5-a6ba-1b4a0aff3158@app.fastmail.com","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-11T20:32:56Z","receivedAt":"2025-09-11T20:33:06Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Sep 11, 2025 at 07:31:04PM +0200, Kristoffer Haugsbakk wrote:\n\n> But it turns out there is a regression in my code with the recursion\n> detection.  Compare:\n> \n>     $ ./git -c alias.one=two -c alias.two=two one -h\n>     'one' is aliased to 'two'\n>     'two' is aliased to 'two'\n>     fatal: recursive alias: two\n\nYour example there is a little funny; \"two\" is recursive to itself, even\nwithout \"one\". We detect that case in handle_alias(), and we would still\ndetect:\n\n  git -c alias.whatchanged=whatchanged whatchanged -h\n\nin the same spot. I assume what you meant was:\n\n  $ ./git -c alias.one=two -c alias.two=one one -h\n  'one' is aliased to 'two'\n  'two' is aliased to 'one'\n  fatal: alias loop detected: expansion of 'one' does not terminate:\n    one <==\n    two ==>\n\nwhich is handled in the caller. And indeed...\n\n>     $ ./git -c alias.whatchanged=pack-redundant -c alias.pack-redundant=whatchanged whatchanged -h\n>     'whatchanged' is aliased to 'pack-redundant'\n>     'pack-redundant' is aliased to 'whatchanged'\n>     'whatchanged' is aliased to 'pack-redundant'\n>     'pack-redundant' is aliased to 'whatchanged'\n>     'whatchanged' is aliased to 'pack-redundant'\n>     'pack-redundant' is aliased to 'whatchanged'\n>     'whatchanged' is aliased to 'pack-redundant'\n>     'pack-redundant' is aliased to 'whatchanged'\n>     [forever]\n\n...that cannot work with where we've placed the new conditional, because\nit continues the loop before hitting the alias check. If we move that to\nthe top, like the patch below, it works.\n\nIt is a little funny to do it up front even when we're going to directly\nrun a builtin, but I don't think it harms much. OTOH I find the whole\nplacement a little odd in the first place. We are speculatively adding\nto the cmd list before even finding out if we have an alias. It kind of\nfeels like this should be part of handle_alias() in the first place.\nWe'd need to pass in the cmd_list variable for it to add to and check.\n\n-Peff\n\n---\ndiff --git a/git.c b/git.c\nindex ca66e24639..17c566a802 100644\n--- a/git.c\n+++ b/git.c\n@@ -814,6 +814,24 @@ static int run_argv(struct strvec *args)\n \tstruct string_list_item *seen;\n \n \twhile (1) {\n+\t\tseen = unsorted_string_list_lookup(&cmd_list, args->v[0]);\n+\t\tif (seen) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tfor (size_t i = 0; i < cmd_list.nr; i++) {\n+\t\t\t\tstruct string_list_item *item = &cmd_list.items[i];\n+\n+\t\t\t\tstrbuf_addf(&sb, \"\\n  %s\", item->string);\n+\t\t\t\tif (item == seen)\n+\t\t\t\t\tstrbuf_addstr(&sb, \" <==\");\n+\t\t\t\telse if (i == cmd_list.nr - 1)\n+\t\t\t\t\tstrbuf_addstr(&sb, \" ==>\");\n+\t\t\t}\n+\t\t\tdie(_(\"alias loop detected: expansion of '%s' does\"\n+\t\t\t      \" not terminate:%s\"), cmd_list.items[0].string, sb.buf);\n+\t\t}\n+\n+\t\tstring_list_append(&cmd_list, args->v[0]);\n+\n \t\t/*\n \t\t * Allow deprecated commands to be overridden by aliases. This\n \t\t * creates a seamless path forward for people who want to keep\n@@ -874,24 +892,6 @@ static int run_argv(struct strvec *args)\n \t\t/* .. then try the external ones */\n \t\texecv_dashed_external(args->v);\n \n-\t\tseen = unsorted_string_list_lookup(&cmd_list, args->v[0]);\n-\t\tif (seen) {\n-\t\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\t\tfor (size_t i = 0; i < cmd_list.nr; i++) {\n-\t\t\t\tstruct string_list_item *item = &cmd_list.items[i];\n-\n-\t\t\t\tstrbuf_addf(&sb, \"\\n  %s\", item->string);\n-\t\t\t\tif (item == seen)\n-\t\t\t\t\tstrbuf_addstr(&sb, \" <==\");\n-\t\t\t\telse if (i == cmd_list.nr - 1)\n-\t\t\t\t\tstrbuf_addstr(&sb, \" ==>\");\n-\t\t\t}\n-\t\t\tdie(_(\"alias loop detected: expansion of '%s' does\"\n-\t\t\t      \" not terminate:%s\"), cmd_list.items[0].string, sb.buf);\n-\t\t}\n-\n-\t\tstring_list_append(&cmd_list, args->v[0]);\n-\n \t\t/*\n \t\t * It could be an alias -- this works around the insanity\n \t\t * of overriding \"git log\" with \"git show\" by having\n\n\n"},{"id":"526146","messageId":"20250911204302.GA1907101@coredump.intra.peff.net","threadId":"64048","inReplyTo":"20250911203256.GA1894340@coredump.intra.peff.net","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-11T20:43:02Z","receivedAt":"2025-09-11T20:43:04Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Sep 11, 2025 at 04:32:56PM -0400, Jeff King wrote:\n\n> It is a little funny to do it up front even when we're going to directly\n> run a builtin, but I don't think it harms much. OTOH I find the whole\n> placement a little odd in the first place. We are speculatively adding\n> to the cmd list before even finding out if we have an alias. It kind of\n> feels like this should be part of handle_alias() in the first place.\n> We'd need to pass in the cmd_list variable for it to add to and check.\n\nIOW, something like this (perhaps as a preparatory patch):\n\ndiff --git a/git.c b/git.c\nindex ca66e24639..06de0bacf3 100644\n--- a/git.c\n+++ b/git.c\n@@ -365,7 +365,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \treturn (*argv) - orig_argv;\n }\n \n-static int handle_alias(struct strvec *args)\n+static int handle_alias(struct strvec *args, struct string_list *cmd_list)\n {\n \tint envchanged = 0, ret = 0, saved_errno = errno;\n \tint count, option_count;\n@@ -376,6 +376,8 @@ static int handle_alias(struct strvec *args)\n \talias_command = args->v[0];\n \talias_string = alias_lookup(alias_command);\n \tif (alias_string) {\n+\t\tstruct string_list_item *seen;\n+\n \t\tif (args->nr == 2 && !strcmp(args->v[1], \"-h\"))\n \t\t\tfprintf_ln(stderr, _(\"'%s' is aliased to '%s'\"),\n \t\t\t\t   alias_command, alias_string);\n@@ -423,6 +425,24 @@ static int handle_alias(struct strvec *args)\n \t\tif (!strcmp(alias_command, new_argv[0]))\n \t\t\tdie(_(\"recursive alias: %s\"), alias_command);\n \n+\t\tseen = unsorted_string_list_lookup(cmd_list, args->v[0]);\n+\t\tif (seen) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tfor (size_t i = 0; i < cmd_list->nr; i++) {\n+\t\t\t\tstruct string_list_item *item = &cmd_list->items[i];\n+\n+\t\t\t\tstrbuf_addf(&sb, \"\\n  %s\", item->string);\n+\t\t\t\tif (item == seen)\n+\t\t\t\t\tstrbuf_addstr(&sb, \" <==\");\n+\t\t\t\telse if (i == cmd_list->nr - 1)\n+\t\t\t\t\tstrbuf_addstr(&sb, \" ==>\");\n+\t\t\t}\n+\t\t\tdie(_(\"alias loop detected: expansion of '%s' does\"\n+\t\t\t      \" not terminate:%s\"), cmd_list->items[0].string, sb.buf);\n+\t\t}\n+\n+\t\tstring_list_append(cmd_list, args->v[0]);\n+\n \t\ttrace_argv_printf(new_argv,\n \t\t\t\t  \"trace: alias expansion: %s =>\",\n \t\t\t\t  alias_command);\n@@ -811,7 +831,6 @@ static int run_argv(struct strvec *args)\n {\n \tint done_alias = 0;\n \tstruct string_list cmd_list = STRING_LIST_INIT_DUP;\n-\tstruct string_list_item *seen;\n \n \twhile (1) {\n \t\t/*\n@@ -821,7 +840,7 @@ static int run_argv(struct strvec *args)\n \t\t * deprecation complaint in the meantime.\n \t\t */\n \t\tif (is_deprecated_command(args->v[0]) &&\n-\t\t    handle_alias(args)) {\n+\t\t    handle_alias(args, &cmd_list)) {\n \t\t\tdone_alias = 1;\n \t\t\tcontinue;\n \t\t}\n@@ -874,30 +893,12 @@ static int run_argv(struct strvec *args)\n \t\t/* .. then try the external ones */\n \t\texecv_dashed_external(args->v);\n \n-\t\tseen = unsorted_string_list_lookup(&cmd_list, args->v[0]);\n-\t\tif (seen) {\n-\t\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\t\tfor (size_t i = 0; i < cmd_list.nr; i++) {\n-\t\t\t\tstruct string_list_item *item = &cmd_list.items[i];\n-\n-\t\t\t\tstrbuf_addf(&sb, \"\\n  %s\", item->string);\n-\t\t\t\tif (item == seen)\n-\t\t\t\t\tstrbuf_addstr(&sb, \" <==\");\n-\t\t\t\telse if (i == cmd_list.nr - 1)\n-\t\t\t\t\tstrbuf_addstr(&sb, \" ==>\");\n-\t\t\t}\n-\t\t\tdie(_(\"alias loop detected: expansion of '%s' does\"\n-\t\t\t      \" not terminate:%s\"), cmd_list.items[0].string, sb.buf);\n-\t\t}\n-\n-\t\tstring_list_append(&cmd_list, args->v[0]);\n-\n \t\t/*\n \t\t * It could be an alias -- this works around the insanity\n \t\t * of overriding \"git log\" with \"git show\" by having\n \t\t * alias.log = show\n \t\t */\n-\t\tif (!handle_alias(args))\n+\t\tif (!handle_alias(args, &cmd_list))\n \t\t\tbreak;\n \t\tdone_alias = 1;\n \t}\n\n\nAnd then along with that:\n\n  - a new test at least of the looping deprecated case (we have an\n    existing test for non-deprecated looping aliases)\n\n  - while we are moving all this code, I might consider calling cmd_list\n    something more obvious like \"expanded_aliases\" or something.\n\n  - the placement above puts it next to the direct-recursion check in\n    handle_alias(). But it does change the output a bit, since we now do\n    the check after the \"-h\" expansion. So Before we'd print:\n\n      $ git -c alias.one=two -c alias.two=one one -h\n      'one' is aliased to 'two'\n      'two' is aliased to 'one'\n      fatal: alias loop detected: expansion of 'one' does not terminate:\n      one <==\n      two ==>\n\n    And now we'll print:\n\n      $ ./git -c alias.one=two -c alias.two=one one -h\n      'one' is aliased to 'two'\n      'two' is aliased to 'one'\n      'one' is aliased to 'two'\n      fatal: alias loop detected: expansion of 'one' does not terminate:\n        one <==\n        two ==>\n\n    with one extra \"-h\" line. Probably not a big deal, but that perhaps\n    points to the fact that we could be detecting the cycle one loop\n    sooner. I.e., as soon as we see that \"two\" points to \"one\" we know\n    we have cycled. We could probably be checking new_argv there\n    instead, after adding the current name to cmd_list (and in fact that\n    would catch the direct-recursion case automatically and we would not\n    need to do so manually, though maybe people prefer the more specific\n    message it produces).\n\n    I'll leave that as an exercise for the reader. ;)\n\n-Peff\n"},{"id":"526147","messageId":"20250911204404.GA1907215@coredump.intra.peff.net","threadId":"64048","inReplyTo":"20250911203256.GA1894340@coredump.intra.peff.net","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-11T20:44:04Z","receivedAt":"2025-09-11T20:44:05Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Sep 11, 2025 at 04:32:56PM -0400, Jeff King wrote:\n\n> On Thu, Sep 11, 2025 at 07:31:04PM +0200, Kristoffer Haugsbakk wrote:\n> \n> > But it turns out there is a regression in my code with the recursion\n> > detection.  Compare:\n> > \n> >     $ ./git -c alias.one=two -c alias.two=two one -h\n> >     'one' is aliased to 'two'\n> >     'two' is aliased to 'two'\n> >     fatal: recursive alias: two\n> \n> Your example there is a little funny; \"two\" is recursive to itself, even\n> without \"one\". We detect that case in handle_alias(), and we would still\n> detect:\n\nAlso, I meant to say: good catch. :) I did not even think about loop\ndetection at all in my suggestions.\n\n-Peff\n"},{"id":"526149","messageId":"xmqqo6rgofeu.fsf@gitster.g","threadId":"64048","inReplyTo":"20250911204404.GA1907215@coredump.intra.peff.net","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-11T21:19:53Z","receivedAt":"2025-09-11T21:19:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Thu, Sep 11, 2025 at 04:32:56PM -0400, Jeff King wrote:\n>\n>> On Thu, Sep 11, 2025 at 07:31:04PM +0200, Kristoffer Haugsbakk wrote:\n>> \n>> > But it turns out there is a regression in my code with the recursion\n>> > detection.  Compare:\n>> > \n>> >     $ ./git -c alias.one=two -c alias.two=two one -h\n>> >     'one' is aliased to 'two'\n>> >     'two' is aliased to 'two'\n>> >     fatal: recursive alias: two\n>> \n>> Your example there is a little funny; \"two\" is recursive to itself, even\n>> without \"one\". We detect that case in handle_alias(), and we would still\n>> detect:\n>\n> Also, I meant to say: good catch. :) I did not even think about loop\n> detection at all in my suggestions.\n\nNeither did I.  I am very glad that you two are very efficiently and\neffectively collaborating on this topic together.  Thank you very\nmuch and kudos for both of you.\n\n"},{"id":"526242","messageId":"98d206ca-3dc1-42cc-9f01-e3241d5a24af@app.fastmail.com","threadId":"64048","inReplyTo":"20250911204302.GA1907101@coredump.intra.peff.net","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-13T14:10:57Z","receivedAt":"2025-09-13T14:11:18Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Thu, Sep 11, 2025, at 22:43, Jeff King wrote:\n> On Thu, Sep 11, 2025 at 04:32:56PM -0400, Jeff King wrote:\n>\n>> It is a little funny to do it up front even when we're going to directly\n>> run a builtin, but I don't think it harms much. OTOH I find the whole\n>> placement a little odd in the first place. We are speculatively adding\n>> to the cmd list before even finding out if we have an alias. It kind of\n>> feels like this should be part of handle_alias() in the first place.\n>> We'd need to pass in the cmd_list variable for it to add to and check.\n>\n> IOW, something like this (perhaps as a preparatory patch):\n>[snip diff]\n> And then along with that:\n>\n>   - a new test at least of the looping deprecated case (we have an\n>     existing test for non-deprecated looping aliases)\n>\n>   - while we are moving all this code, I might consider calling cmd_list\n>     something more obvious like \"expanded_aliases\" or something.\n>\n>   - the placement above puts it next to the direct-recursion check in\n>     handle_alias(). But it does change the output a bit, since we now do\n>     the check after the \"-h\" expansion. So Before we'd print:\n>\n>       $ git -c alias.one=two -c alias.two=one one -h\n>       'one' is aliased to 'two'\n>       'two' is aliased to 'one'\n>       fatal: alias loop detected: expansion of 'one' does not terminate:\n>       one <==\n>       two ==>\n>\n>     And now we'll print:\n>\n>       $ ./git -c alias.one=two -c alias.two=one one -h\n>       'one' is aliased to 'two'\n>       'two' is aliased to 'one'\n>       'one' is aliased to 'two'\n>       fatal: alias loop detected: expansion of 'one' does not terminate:\n>         one <==\n>         two ==>\n>\n>     with one extra \"-h\" line. Probably not a big deal, but that perhaps\n>     points to the fact that we could be detecting the cycle one loop\n>     sooner. I.e., as soon as we see that \"two\" points to \"one\" we know\n>     we have cycled. We could probably be checking new_argv there\n>     instead, after adding the current name to cmd_list (and in fact that\n>     would catch the direct-recursion case automatically and we would not\n>     need to do so manually, though maybe people prefer the more specific\n>     message it produces).\n>\n>     I'll leave that as an exercise for the reader. ;)\n\nThat’s great, thanks.  I’ve added it as a preparatory patch/commit with\nthis message.\n\n-----\n\ngit: move seen-command bookkeeping into handle_alias(...)\n\nWe are about to complicate the command handling by allowing *deprecated*\nbuiltins to be shadowed by aliases.  We need to organize the code in\norder to facilitate that.[1]\n\nThe code in the `while(1)` speculatively adds commands to the list\nbefore finding out if it’s an alias.  Let’s instead move it inside\n`handle_alias(...)` and in turn only run this logic when we have found\nan alias.\n\nThis is not a refactor since the error output is changed; we will\nnow print\n\n    '<cmd1>' is aliased to '<cmd2>'\n\nwhile iterating in addition to the final `fatal` message.\n\n[1]: We will do that by an additional call to `handle_alias(1)` inside\n    the loop.  *Not* moving this code leaves a blind spot; we still\n    detect regular alias looping/recursion but we miss it when it is\n    done using deprecated builtins.\n\n-- \nKris\n"},{"id":"526264","messageId":"56a5ba02-8bbf-4726-8a76-ca0fb5a38e53@app.fastmail.com","threadId":"64048","inReplyTo":"20250910183418.GA1157772@coredump.intra.peff.net","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2025-09-13T21:50:55Z","receivedAt":"2025-09-13T21:51:16Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Wed, Sep 10, 2025, at 20:34, Jeff King wrote:\n>> Thanks again.  My local test suite passes.  (Except\n>> `t5801-remote-helpers.sh` which never works for me.)\n>\n> I sometimes see failures there because I've checked out and built an old\n> version which creates git-remote-testgit as a built script. That was\n> later moved into t/t5801 via 5afb2ce4cd (remote-testgit: move it into\n> the support directory for t5801, 2019-04-12). But if you have an old\n> built copy lying around in the project root, then \"git fetch\" will find\n> that old version first (because it prepends the root GIT_EXEC_PATH to\n> PATH when it runs). So we run the old version, which may or may not\n> support what the more modern test needs.\n>\n> And deleting \"git-remote-testgit\" (or just running \"git clean\") makes it\n> go away.\n>\n> That may or may not be your problem, of course. ;)\n\nThanks—that was exactly it.:)\n"},{"id":"526265","messageId":"20250913220609.GA435444@coredump.intra.peff.net","threadId":"64048","inReplyTo":"98d206ca-3dc1-42cc-9f01-e3241d5a24af@app.fastmail.com","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-13T22:06:09Z","receivedAt":"2025-09-13T22:06:16Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Sep 13, 2025 at 04:10:57PM +0200, Kristoffer Haugsbakk wrote:\n\n> The code in the `while(1)` speculatively adds commands to the list\n> before finding out if it’s an alias.  Let’s instead move it inside\n> `handle_alias(...)` and in turn only run this logic when we have found\n> an alias.\n> \n> This is not a refactor since the error output is changed; we will\n> now print\n> \n>     '<cmd1>' is aliased to '<cmd2>'\n> \n> while iterating in addition to the final `fatal` message.\n\nIf you want to get rid of that last paragraph, I think it really is as\nsimple as checking the expanded alias new_argv[0] as soon as we see it,\nlike:\n\ndiff --git a/git.c b/git.c\nindex 06de0bacf3..f11ce416a3 100644\n--- a/git.c\n+++ b/git.c\n@@ -425,7 +425,9 @@ static int handle_alias(struct strvec *args, struct string_list *cmd_list)\n \t\tif (!strcmp(alias_command, new_argv[0]))\n \t\t\tdie(_(\"recursive alias: %s\"), alias_command);\n \n-\t\tseen = unsorted_string_list_lookup(cmd_list, args->v[0]);\n+\t\tstring_list_append(cmd_list, args->v[0]);\n+\n+\t\tseen = unsorted_string_list_lookup(cmd_list, new_argv[0]);\n \t\tif (seen) {\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n \t\t\tfor (size_t i = 0; i < cmd_list->nr; i++) {\n@@ -441,8 +443,6 @@ static int handle_alias(struct strvec *args, struct string_list *cmd_list)\n \t\t\t      \" not terminate:%s\"), cmd_list->items[0].string, sb.buf);\n \t\t}\n \n-\t\tstring_list_append(cmd_list, args->v[0]);\n-\n \t\ttrace_argv_printf(new_argv,\n \t\t\t\t  \"trace: alias expansion: %s =>\",\n \t\t\t\t  alias_command);\n\nIt is important to move the append of args->v[0] up (though arguably it\nshould be alias_command here, which is a local-variable alias that the\nrest of the function uses). Adding it up-front is not needed for loop\ndetection, but it is shown as part of the output when we do find a loop.\n\nYou could _also_ ditch the \"recursive alias\" check above at that point,\nthough I think it produces a slightly nicer message:\n\n  $ ./git -c alias.foo=foo foo\n  fatal: recursive alias: foo\n\nvs:\n\n  $ ./git -c alias.foo=foo foo\n  fatal: alias loop detected: expansion of 'foo' does not terminate:\n    foo <==\n\nThough perhaps if the output code marked it as both the start and end of\nthe loop it would be more sensible. Maybe like:\n\ndiff --git a/git.c b/git.c\nindex f11ce416a3..f1a83f2e6a 100644\n--- a/git.c\n+++ b/git.c\n@@ -422,9 +422,6 @@ static int handle_alias(struct strvec *args, struct string_list *cmd_list)\n \t\tif (count < 1)\n \t\t\tdie(_(\"empty alias for %s\"), alias_command);\n \n-\t\tif (!strcmp(alias_command, new_argv[0]))\n-\t\t\tdie(_(\"recursive alias: %s\"), alias_command);\n-\n \t\tstring_list_append(cmd_list, args->v[0]);\n \n \t\tseen = unsorted_string_list_lookup(cmd_list, new_argv[0]);\n@@ -436,8 +433,8 @@ static int handle_alias(struct strvec *args, struct string_list *cmd_list)\n \t\t\t\tstrbuf_addf(&sb, \"\\n  %s\", item->string);\n \t\t\t\tif (item == seen)\n \t\t\t\t\tstrbuf_addstr(&sb, \" <==\");\n-\t\t\t\telse if (i == cmd_list->nr - 1)\n-\t\t\t\t\tstrbuf_addstr(&sb, \" ==>\");\n+\t\t\t\tif (i == cmd_list->nr - 1)\n+\t\t\t\t\tstrbuf_addstr(&sb, item == seen ? \">\" : \" ==>\");\n \t\t\t}\n \t\t\tdie(_(\"alias loop detected: expansion of '%s' does\"\n \t\t\t      \" not terminate:%s\"), cmd_list->items[0].string, sb.buf);\n\nI dunno. Maybe it is not worth tinkering with too much. But the \"this\nchanges the output\" justification from your proposed message seems to me\nan indication that the refactor is a little iffy.\n\n-Peff\n"},{"id":"526277","messageId":"eb7e569a-b297-41d2-82b0-956210292b64@app.fastmail.com","threadId":"64048","inReplyTo":"20250913220609.GA435444@coredump.intra.peff.net","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-14T17:24:14Z","receivedAt":"2025-09-14T17:24:36Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Sun, Sep 14, 2025, at 00:06, Jeff King wrote:\n> On Sat, Sep 13, 2025 at 04:10:57PM +0200, Kristoffer Haugsbakk wrote:\n>\n>> The code in the `while(1)` speculatively adds commands to the list\n>> before finding out if it’s an alias.  Let’s instead move it inside\n>> `handle_alias(...)` and in turn only run this logic when we have found\n>> an alias.\n>>\n>> This is not a refactor since the error output is changed; we will\n>> now print\n>>\n>>     '<cmd1>' is aliased to '<cmd2>'\n>>\n>> while iterating in addition to the final `fatal` message.\n>\n> If you want to get rid of that last paragraph, I think it really is as\n> simple as checking the expanded alias new_argv[0] as soon as we see it,\n> like:\n\nI tried using both of these changes (the patches) but the `alias...`\ntest suite started failing.\n\n>[snip]\n> You could _also_ ditch the \"recursive alias\" check above at that point,\n> though I think it produces a slightly nicer message:\n>\n>   $ ./git -c alias.foo=foo foo\n>   fatal: recursive alias: foo\n>\n> vs:\n>\n>   $ ./git -c alias.foo=foo foo\n>   fatal: alias loop detected: expansion of 'foo' does not terminate:\n>     foo <==\n\nI didn’t try these.\n\n>[snip]\n> I dunno. Maybe it is not worth tinkering with too much. But the \"this\n> changes the output\" justification from your proposed message seems to me\n> an indication that the refactor is a little iffy.\n\nI ended up with not changing it for v5.  I missed the first time around\nthat this informational message is only “logged” in the specific case of\n`<git cmd> -h`.  In turn you only get one more line of output when you\nare (1) chaining deprecated aliases, and (2) making a loop.\n\nv5 still mentions this but I try to put it off to the side, in other\nwords a footnote.\n\nThanks\n"},{"id":"526285","messageId":"cover.1757879060.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757446619.git.code@khaugsbakk.name","subject":"[PATCH v5 0/8] you-still-use-that??: improve breaking changes troubleshooting","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-14T19:49:34Z","receivedAt":"2025-09-14T19:50:26Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nBased on the recent i-still-use-that reports about whatchanged, improve\nthe error reporting with this command in mind:\n\n1. Give more possible actions instead of just (only) asking them to send\n   an email\n2. Hint how to replace their git-whatchanged(1) use with git-log(1) or\n   an alias `whatchanged` (you can alias deprecated commands now)\n3. Minor documentation changes\n4. Add `deprecated` to `git --list-cmds`\n\n§ What the errors now look like\n\n    $ ./git whatchanged\n    'git whatchanged' is nominated for removal.\n\n    hint: You can replace 'git whatchanged <opts>' with:\n    hint:   git log <opts> --raw --no-merges\n    hint: Or make an alias:\n    hint:   git config set --global alias.whatchanged 'log --raw --no-merges'\n\n    If you still use this command, here's what you can do:\n\n    - read https://git-scm.com/docs/BreakingChanges.html\n    - check if anyone has discussed this on the mailing\n      list and if they came up with something that can\n      help you: https://lore.kernel.org/git/?q=git%20whatchanged\n    - send an email to <git@vger.kernel.org> to let us\n      know that you still use this command and were unable\n      to determine a suitable replacement\n\n    fatal: refusing to run without --i-still-use-this\n    $ ./git pack-redundant\n    'git pack-redundant' is nominated for removal.\n    If you still use this command, here's what you can do:\n\n    - read https://git-scm.com/docs/BreakingChanges.html\n    - check if anyone has discussed this on the mailing\n      list and if they came up with something that can\n      help you: https://lore.kernel.org/git/?q=git%20pack-redundant\n    - send an email to <git@vger.kernel.org> to let us\n      know that you still use this command and were unable\n      to determine a suitable replacement\n\n    fatal: refusing to run without --i-still-use-this\n\n§ Changes in v5\n\nFix the leak that Peff found (with Peff’s suggestion).  Also fix (with\nPeff’s help) a regression where you were able to get into an infinite\nalias loop specifically when using deprecated builtin names.\n\nLink: https://lore.kernel.org/git/20250910051347.GA556174@coredump.intra.peff.net/\n\nAlso bring back the `BUG` check.\n\nCheers\n\nKristoffer Haugsbakk (8):\n  git: add `deprecated` category to --list-cmds\n  git: move seen-alias bookkeeping into handle_alias(...)\n  git: allow alias-shadowing deprecated builtins\n  t0014: test shadowing of aliases for a sample of builtins\n  you-still-use-that??: help the user help themselves\n  whatchanged: hint about git-log(1) and aliasing\n  whatchanged: remove not-even-shorter clause\n  BreakingChanges: remove claim about whatchanged reports\n\n Documentation/BreakingChanges.adoc |  2 +-\n Documentation/config/alias.adoc    |  3 +-\n Documentation/git-whatchanged.adoc |  8 ++-\n Documentation/git.adoc             |  3 +-\n builtin/log.c                      |  8 ++-\n builtin/pack-redundant.c           |  2 +-\n git-compat-util.h                  |  2 +-\n git.c                              | 90 ++++++++++++++++++++----------\n t/t0014-alias.sh                   | 58 +++++++++++++++++++\n usage.c                            | 33 ++++++++---\n 10 files changed, 163 insertions(+), 46 deletions(-)\n\nInterdiff against v4:\ndiff --git a/git.c b/git.c\nindex b3aafebfe4c..1310363e5c4 100644\n--- a/git.c\n+++ b/git.c\n@@ -365,7 +365,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \treturn (*argv) - orig_argv;\n }\n \n-static int handle_alias(struct strvec *args)\n+static int handle_alias(struct strvec *args, struct string_list *expanded_aliases)\n {\n \tint envchanged = 0, ret = 0, saved_errno = errno;\n \tint count, option_count;\n@@ -376,6 +376,8 @@ static int handle_alias(struct strvec *args)\n \talias_command = args->v[0];\n \talias_string = alias_lookup(alias_command);\n \tif (alias_string) {\n+\t\tstruct string_list_item *seen;\n+\n \t\tif (args->nr == 2 && !strcmp(args->v[1], \"-h\"))\n \t\t\tfprintf_ln(stderr, _(\"'%s' is aliased to '%s'\"),\n \t\t\t\t   alias_command, alias_string);\n@@ -423,6 +425,24 @@ static int handle_alias(struct strvec *args)\n \t\tif (!strcmp(alias_command, new_argv[0]))\n \t\t\tdie(_(\"recursive alias: %s\"), alias_command);\n \n+\t\tseen = unsorted_string_list_lookup(expanded_aliases, args->v[0]);\n+\t\tif (seen) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tfor (size_t i = 0; i < expanded_aliases->nr; i++) {\n+\t\t\t\tstruct string_list_item *item = &expanded_aliases->items[i];\n+\n+\t\t\t\tstrbuf_addf(&sb, \"\\n  %s\", item->string);\n+\t\t\t\tif (item == seen)\n+\t\t\t\t\tstrbuf_addstr(&sb, \" <==\");\n+\t\t\t\telse if (i == expanded_aliases->nr - 1)\n+\t\t\t\t\tstrbuf_addstr(&sb, \" ==>\");\n+\t\t\t}\n+\t\t\tdie(_(\"alias loop detected: expansion of '%s' does\"\n+\t\t\t      \" not terminate:%s\"), expanded_aliases->items[0].string, sb.buf);\n+\t\t}\n+\n+\t\tstring_list_append(expanded_aliases, args->v[0]);\n+\n \t\ttrace_argv_printf(new_argv,\n \t\t\t\t  \"trace: alias expansion: %s =>\",\n \t\t\t\t  alias_command);\n@@ -677,6 +697,8 @@ static void list_builtins(struct string_list *out,\n \t\t\t  unsigned int include_option,\n \t\t\t  unsigned int exclude_option)\n {\n+\tif (include_option && exclude_option)\n+\t\tBUG(\"'include_option' and 'exclude_option' are mutually exclusive\");\n \tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n \t\tif (include_option && !(commands[i].option & include_option))\n \t\t\tcontinue;\n@@ -810,8 +832,7 @@ static int is_deprecated_command(const char *cmd)\n static int run_argv(struct strvec *args)\n {\n \tint done_alias = 0;\n-\tstruct string_list cmd_list = STRING_LIST_INIT_DUP;\n-\tstruct string_list_item *seen;\n+\tstruct string_list expanded_aliases = STRING_LIST_INIT_DUP;\n \n \twhile (1) {\n \t\t/*\n@@ -821,9 +842,7 @@ static int run_argv(struct strvec *args)\n \t\t * deprecation complaint in the meantime.\n \t\t */\n \t\tif (is_deprecated_command(args->v[0]) &&\n-\t\t    alias_lookup(args->v[0])) {\n-\t\t\tif (!handle_alias(args))\n-\t\t\t\tbreak;\n+\t\t    handle_alias(args, &expanded_aliases)) {\n \t\t\tdone_alias = 1;\n \t\t\tcontinue;\n \t\t}\n@@ -876,35 +895,17 @@ static int run_argv(struct strvec *args)\n \t\t/* .. then try the external ones */\n \t\texecv_dashed_external(args->v);\n \n-\t\tseen = unsorted_string_list_lookup(&cmd_list, args->v[0]);\n-\t\tif (seen) {\n-\t\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\t\tfor (size_t i = 0; i < cmd_list.nr; i++) {\n-\t\t\t\tstruct string_list_item *item = &cmd_list.items[i];\n-\n-\t\t\t\tstrbuf_addf(&sb, \"\\n  %s\", item->string);\n-\t\t\t\tif (item == seen)\n-\t\t\t\t\tstrbuf_addstr(&sb, \" <==\");\n-\t\t\t\telse if (i == cmd_list.nr - 1)\n-\t\t\t\t\tstrbuf_addstr(&sb, \" ==>\");\n-\t\t\t}\n-\t\t\tdie(_(\"alias loop detected: expansion of '%s' does\"\n-\t\t\t      \" not terminate:%s\"), cmd_list.items[0].string, sb.buf);\n-\t\t}\n-\n-\t\tstring_list_append(&cmd_list, args->v[0]);\n-\n \t\t/*\n \t\t * It could be an alias -- this works around the insanity\n \t\t * of overriding \"git log\" with \"git show\" by having\n \t\t * alias.log = show\n \t\t */\n-\t\tif (!handle_alias(args))\n+\t\tif (!handle_alias(args, &expanded_aliases))\n \t\t\tbreak;\n \t\tdone_alias = 1;\n \t}\n \n-\tstring_list_clear(&cmd_list, 0);\n+\tstring_list_clear(&expanded_aliases, 0);\n \n \treturn done_alias;\n }\ndiff --git a/t/t0014-alias.sh b/t/t0014-alias.sh\nindex bf7e6512bb1..1ac739a2737 100755\n--- a/t/t0014-alias.sh\n+++ b/t/t0014-alias.sh\n@@ -27,6 +27,21 @@ test_expect_success 'looping aliases - internal execution' '\n \ttest_grep \"^fatal: alias loop detected: expansion of\" output\n '\n \n+test_expect_success 'looping aliases - deprecated builtins' '\n+\ttest_config alias.whatchanged pack-redundant &&\n+\ttest_config alias.pack-redundant whatchanged &&\n+\tcat >expect <<-EOF &&\n+\t${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n+\t${SQ}pack-redundant${SQ} is aliased to ${SQ}whatchanged${SQ}\n+\t${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n+\tfatal: alias loop detected: expansion of ${SQ}whatchanged${SQ} does not terminate:\n+\t  whatchanged <==\n+\t  pack-redundant ==>\n+\tEOF\n+\ttest_must_fail git whatchanged -h 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n # This test is disabled until external loops are fixed, because would block\n # the test suite for a full minute.\n #\n@@ -72,6 +87,15 @@ test_expect_success 'can alias-shadow deprecated builtins' '\n \tdone\n '\n \n+test_expect_success 'can alias-shadow via two deprecated builtins' '\n+\t# some git(1) commands will fail... (see above)\n+\ttest_might_fail git status -h >expect &&\n+\ttest_file_not_empty expect &&\n+\ttest_might_fail git -c alias.whatchanged=pack-redundant \\\n+\t\t-c alias.pack-redundant=status whatchanged -h >actual &&\n+\ttest_cmp expect actual\n+'\n+\n cannot_alias_regular_builtin () {\n \tcmd=\"$1\" &&\n \t# some git(1) commands will fail... (see above)\nRange-diff against v4:\n1:  66e6a9554b1 ! 1:  13682553018 git: add `deprecated` category to --list-cmds\n    @@ Commit message\n     \n     \n      ## Notes (series) ##\n    +    v5:\n    +\n    +    Add back the `BUG` check from v3 because I think it makes sense to only\n    +    populate one of the two options.\n    +\n    +    Link: https://lore.kernel.org/git/cover.1757345711.git.code@khaugsbakk.name/T/#m922b852384911511c45afd458051f52b50dce62f\n    +\n         v4:\n     \n         Incorporate Patrick’s suggestions about the for-loop refactor and\n    @@ git.c: int is_builtin(const char *s)\n     +\t\t\t  unsigned int include_option,\n     +\t\t\t  unsigned int exclude_option)\n      {\n    ++\tif (include_option && exclude_option)\n    ++\t\tBUG(\"'include_option' and 'exclude_option' are mutually exclusive\");\n      \tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n     -\t\tif (exclude_option &&\n     -\t\t    (commands[i].option & exclude_option))\n-:  ----------- > 2:  7f82ef6e96f git: move seen-alias bookkeeping into handle_alias(...)\n2:  672253e0e71 ! 3:  4bfe36cccd0 git: allow alias-shadowing deprecated builtins\n    @@ Commit message\n         the command is removed for good.\n     \n         Let’s lift this limitation by allowing *deprecated* builtins to be\n    -    shadowed by aliases.\n    +    shadowed by aliases.[4]\n     \n         The only observed demand for aliasing has been for git-whatchanged(1),\n         not for git-pack-redundant(1).  But let’s be consistent and treat all\n    @@ Commit message\n              have empty commits (no changes)[2]\n         [2]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n         [3]: https://lore.kernel.org/git/BL3P221MB0449288C8B0FA448A227FD48833AA@BL3P221MB0449.NAMP221.PROD.OUTLOOK.COM/\n    +    [4]: The test “looping aliases - deprecated builtins” covers the\n    +        case of the extra output mentioned in footnote number 3 of the\n    +        previous commit\n     \n         Based-on-patch-by: Jeff King <peff@peff.net>\n         Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n     \n     \n      ## Notes (series) ##\n    +    v5:\n    +\n    +    Fix a leak caused by calling `alias_lookup(...)` without freeing the\n    +    return value by not calling that function.\n    +\n    +    Add tests for deprecated builtins for infinite alias looping as well as\n    +    simple chaining.  This is to exercise all code paths that we could think\n    +    of.\n    +\n    +    Expand on the infinite alias looping test on the whole error\n    +    output (compared to the one above).  Link this change to the\n    +    previous patch/commit by mentioning it in a footnote.\n    +\n    +    See the last footnote and the note on the previous commit/patch.  I have\n    +    opted not to get rid of this redundant line.  But this documents the\n    +    change and future changes that affect it will trigger this\n    +    assertion (say if they remove the redundant line).\n    +\n         v4:\n     \n         Better `is_deprecated_command` implementation.\n    @@ git.c: static void execv_dashed_external(const char **argv)\n      static int run_argv(struct strvec *args)\n      {\n      \tint done_alias = 0;\n    -@@ git.c: static int run_argv(struct strvec *args)\n    - \tstruct string_list_item *seen;\n    + \tstruct string_list expanded_aliases = STRING_LIST_INIT_DUP;\n      \n      \twhile (1) {\n     +\t\t/*\n    @@ git.c: static int run_argv(struct strvec *args)\n     +\t\t * deprecation complaint in the meantime.\n     +\t\t */\n     +\t\tif (is_deprecated_command(args->v[0]) &&\n    -+\t\t    alias_lookup(args->v[0])) {\n    -+\t\t\tif (!handle_alias(args))\n    -+\t\t\t\tbreak;\n    ++\t\t    handle_alias(args, &expanded_aliases)) {\n     +\t\t\tdone_alias = 1;\n     +\t\t\tcontinue;\n     +\t\t}\n    @@ git.c: static int run_argv(struct strvec *args)\n      \t\t * it no longer is safe to invoke builtins directly in\n     \n      ## t/t0014-alias.sh ##\n    +@@ t/t0014-alias.sh: test_expect_success 'looping aliases - internal execution' '\n    + \ttest_grep \"^fatal: alias loop detected: expansion of\" output\n    + '\n    + \n    ++test_expect_success 'looping aliases - deprecated builtins' '\n    ++\ttest_config alias.whatchanged pack-redundant &&\n    ++\ttest_config alias.pack-redundant whatchanged &&\n    ++\tcat >expect <<-EOF &&\n    ++\t${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n    ++\t${SQ}pack-redundant${SQ} is aliased to ${SQ}whatchanged${SQ}\n    ++\t${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n    ++\tfatal: alias loop detected: expansion of ${SQ}whatchanged${SQ} does not terminate:\n    ++\t  whatchanged <==\n    ++\t  pack-redundant ==>\n    ++\tEOF\n    ++\ttest_must_fail git whatchanged -h 2>actual &&\n    ++\ttest_cmp expect actual\n    ++'\n    ++\n    + # This test is disabled until external loops are fixed, because would block\n    + # the test suite for a full minute.\n    + #\n     @@ t/t0014-alias.sh: test_expect_success 'tracing a shell alias with arguments shows trace of prepare\n      \ttest_cmp expect actual\n      '\n    @@ t/t0014-alias.sh: test_expect_success 'tracing a shell alias with arguments show\n     +\t\tcan_alias_deprecated_builtin \"$cmd\" || return 1\n     +\tdone\n     +'\n    ++\n    ++test_expect_success 'can alias-shadow via two deprecated builtins' '\n    ++\t# some git(1) commands will fail... (see above)\n    ++\ttest_might_fail git status -h >expect &&\n    ++\ttest_file_not_empty expect &&\n    ++\ttest_might_fail git -c alias.whatchanged=pack-redundant \\\n    ++\t\t-c alias.pack-redundant=status whatchanged -h >actual &&\n    ++\ttest_cmp expect actual\n    ++'\n     +\n      test_done\n3:  00108f28f82 ! 4:  a04c6ae968e t0014: test shadowing of aliases for a sample of builtins\n    @@ Metadata\n      ## Commit message ##\n         t0014: test shadowing of aliases for a sample of builtins\n     \n    -    The previous commit added a test for shadowing deprecated builtins.\n    +    The previous commit added tests for shadowing deprecated builtins.\n         Let’s make the test suite more complete by exercising a sample of\n         the builtins and in turn test the documentation for git-config(1):\n     \n    @@ Notes (series)\n         if I test all of them.\n     \n      ## t/t0014-alias.sh ##\n    -@@ t/t0014-alias.sh: test_expect_success 'can alias-shadow deprecated builtins' '\n    - \tdone\n    +@@ t/t0014-alias.sh: test_expect_success 'can alias-shadow via two deprecated builtins' '\n    + \ttest_cmp expect actual\n      '\n      \n     +cannot_alias_regular_builtin () {\n4:  6bdcaf7f80f = 5:  2f78ab2e28c you-still-use-that??: help the user help themselves\n5:  58de9767b22 ! 6:  34443066645 whatchanged: tell users the git-log(1) equivalent\n    @@ Metadata\n     Author: Kristoffer Haugsbakk <code@khaugsbakk.name>\n     \n      ## Commit message ##\n    -    whatchanged: tell users the git-log(1) equivalent\n    +    whatchanged: hint about git-log(1) and aliasing\n     \n         There have been quite a few `--i-still-use-this` user reports since Git\n         2.51.0 was released.[1][2]  And it doesn’t seem like they are reading\n         the man page about the git-log(1) equivalent.\n     \n    -    Tell them what options to plug into git-log(1).  That template produces\n    -    almost the same output[3] and is arguably a plug-in replacement.\n    -    Concretely, add an optional `hint` argument so that we can use it right\n    -    after the initial error line.\n    +    Tell them what options to plug into git-log(1), either as a replacement\n    +    command or as an alias.[3]  That template produces almost the same\n    +    output[4] and is arguably a plug-in replacement.  Concretely, add\n    +    an optional `hint` argument so that we can use it right after the\n    +    initial error line.\n     \n         Also mention the same concrete options in the documentation while we’re\n         at it.\n    @@ Commit message\n             • https://lore.kernel.org/git/9fcbfcc4-79f9-421f-b9a4-dc455f7db485@acm.org/#t\n             • https://lore.kernel.org/git/83241BDE-1E0D-489A-9181-C608E9FCC17B@gmail.com/\n         [2]: The error message on 2.51.0 does tell them to report it, unconditionally\n    -    [3]: You only get different outputs if you happen to have empty\n    +    [3]: We allow aliasing deprecated builtins now for people who are very\n    +        used to the command name or just like it a lot\n    +    [4]: You only get different outputs if you happen to have empty\n              commits (no changes)[4]\n    -    [4]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n    +    [5]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n     \n         Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n     \n     \n      ## Notes (series) ##\n    +    v5:\n    +\n    +    Mention the aliasing hint in the commit message.\n    +\n         v3:\n     \n         Add an alias hint now that that is possible.  Also prefix each hint-line\n6:  407b430d02c = 7:  b0aabd793f0 whatchanged: remove not-even-shorter clause\n7:  fee752d2fb0 = 8:  5021647c245 BreakingChanges: remove claim about whatchanged reports\n\nbase-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"526286","messageId":"13682553018470845b4b000f0c5fbdac0539c8e3.1757879060.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757879060.git.code@khaugsbakk.name","subject":"[PATCH v5 1/8] git: add `deprecated` category to --list-cmds","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-14T19:49:35Z","receivedAt":"2025-09-14T19:50:45Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nWith 145 builtin commands (according to `git --list-cmds=builtins`),\nusers are probably not keeping on top of which ones (if any) are\ndeprecated.\n\nLet’s expand the experimental `--list-cmds`[1] to allow users and\nprograms to query for this information.  We will also use this in an\nupcoming commit to implement `is_deprecated_command`.\n\n[1]: Using something which is experimental to query for deprecations is\n    perhaps not the most ideal approach, but it is simple to implement\n    and better than having to scan the documentation\n\nAcked-by: Patrick Steinhardt <ps@pks.im>\nHelped-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v5:\n    \n    Add back the `BUG` check from v3 because I think it makes sense to only\n    populate one of the two options.\n    \n    Link: https://lore.kernel.org/git/cover.1757345711.git.code@khaugsbakk.name/T/#m922b852384911511c45afd458051f52b50dce62f\n    \n    v4:\n    \n    Incorporate Patrick’s suggestions about the for-loop refactor and\n    formatting the overlong lines.  Now drop the function doc since it\n    doesn’t apply anymore.\n    \n    Also adjust the commit message now that we use it in the C source in an\n    upcoming commit, not just/only as an assert-helper (in an upcoming\n    commit).\n    \n    v3 (new):\n    \n    This is something I wanted to submit independently until the point about\n    aliasing builtins was brought up.[1]  It will help (in a small way) with\n    the upcoming patch “git: allow alias-shadowing deprecated builtins”.\n    \n    By the way: should `command-list.txt` be updated in some way (I didn’t\n    know what way?)\n    \n    🔗 1: https://lore.kernel.org/git/cover.1756311355.git.code@khaugsbakk.name/T/#mee19f8d39572f9021f9d3000758e87b6c32c967c\n\n Documentation/git.adoc |  3 ++-\n git.c                  | 26 ++++++++++++++++++--------\n 2 files changed, 20 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git.adoc b/Documentation/git.adoc\nindex 743b7b00e4d..a2f0838b168 100644\n--- a/Documentation/git.adoc\n+++ b/Documentation/git.adoc\n@@ -219,7 +219,8 @@ If you just want to run git as if it was started in `<path>` then use\n \tList commands by group. This is an internal/experimental\n \toption and may change or be removed in the future. Supported\n \tgroups are: builtins, parseopt (builtin commands that use\n-\tparse-options), main (all commands in libexec directory),\n+\tparse-options), deprecated (deprecated builtins),\n+\tmain (all commands in libexec directory),\n \tothers (all other commands in `$PATH` that have git- prefix),\n \tlist-<category> (see categories in command-list.txt),\n \tnohelpers (exclude helper commands), alias and config\ndiff --git a/git.c b/git.c\nindex 83eac0aeab7..511efdf2056 100644\n--- a/git.c\n+++ b/git.c\n@@ -28,6 +28,7 @@\n #define NEED_WORK_TREE\t\t(1<<3)\n #define DELAY_PAGER_CONFIG\t(1<<4)\n #define NO_PARSEOPT\t\t(1<<5) /* parse-options is not used */\n+#define DEPRECATED\t\t(1<<6)\n \n struct cmd_struct {\n \tconst char *cmd;\n@@ -51,7 +52,9 @@ const char git_more_info_string[] =\n \n static int use_pager = -1;\n \n-static void list_builtins(struct string_list *list, unsigned int exclude_option);\n+static void list_builtins(struct string_list *list,\n+\t\t\t  unsigned int include_option,\n+\t\t\t  unsigned int exclude_option);\n \n static void exclude_helpers_from_list(struct string_list *list)\n {\n@@ -88,7 +91,7 @@ static int list_cmds(const char *spec)\n \t\tint len = sep - spec;\n \n \t\tif (match_token(spec, len, \"builtins\"))\n-\t\t\tlist_builtins(&list, 0);\n+\t\t\tlist_builtins(&list, 0, 0);\n \t\telse if (match_token(spec, len, \"main\"))\n \t\t\tlist_all_main_cmds(&list);\n \t\telse if (match_token(spec, len, \"others\"))\n@@ -99,6 +102,8 @@ static int list_cmds(const char *spec)\n \t\t\tlist_aliases(&list);\n \t\telse if (match_token(spec, len, \"config\"))\n \t\t\tlist_cmds_by_config(&list);\n+\t\telse if (match_token(spec, len, \"deprecated\"))\n+\t\t\tlist_builtins(&list, DEPRECATED, 0);\n \t\telse if (len > 5 && !strncmp(spec, \"list-\", 5)) {\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n \n@@ -322,7 +327,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\tif (!strcmp(cmd, \"parseopt\")) {\n \t\t\t\tstruct string_list list = STRING_LIST_INIT_DUP;\n \n-\t\t\t\tlist_builtins(&list, NO_PARSEOPT);\n+\t\t\t\tlist_builtins(&list, 0, NO_PARSEOPT);\n \t\t\t\tfor (size_t i = 0; i < list.nr; i++)\n \t\t\t\t\tprintf(\"%s \", list.items[i].string);\n \t\t\t\tstring_list_clear(&list, 0);\n@@ -590,7 +595,7 @@ static struct cmd_struct commands[] = {\n \t{ \"notes\", cmd_notes, RUN_SETUP },\n \t{ \"pack-objects\", cmd_pack_objects, RUN_SETUP },\n #ifndef WITH_BREAKING_CHANGES\n-\t{ \"pack-redundant\", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT },\n+\t{ \"pack-redundant\", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT | DEPRECATED },\n #endif\n \t{ \"pack-refs\", cmd_pack_refs, RUN_SETUP },\n \t{ \"patch-id\", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },\n@@ -647,7 +652,7 @@ static struct cmd_struct commands[] = {\n \t{ \"verify-tag\", cmd_verify_tag, RUN_SETUP },\n \t{ \"version\", cmd_version },\n #ifndef WITH_BREAKING_CHANGES\n-\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP },\n+\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP | DEPRECATED },\n #endif\n \t{ \"worktree\", cmd_worktree, RUN_SETUP },\n \t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n@@ -668,11 +673,16 @@ int is_builtin(const char *s)\n \treturn !!get_builtin(s);\n }\n \n-static void list_builtins(struct string_list *out, unsigned int exclude_option)\n+static void list_builtins(struct string_list *out,\n+\t\t\t  unsigned int include_option,\n+\t\t\t  unsigned int exclude_option)\n {\n+\tif (include_option && exclude_option)\n+\t\tBUG(\"'include_option' and 'exclude_option' are mutually exclusive\");\n \tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n-\t\tif (exclude_option &&\n-\t\t    (commands[i].option & exclude_option))\n+\t\tif (include_option && !(commands[i].option & include_option))\n+\t\t\tcontinue;\n+\t\tif (exclude_option && (commands[i].option & exclude_option))\n \t\t\tcontinue;\n \t\tstring_list_append(out, commands[i].cmd);\n \t}\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"526287","messageId":"7f82ef6e96f694cb1e40d7865b0520886a98f007.1757879060.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757879060.git.code@khaugsbakk.name","subject":"[PATCH v5 2/8] git: move seen-alias bookkeeping into handle_alias(...)","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-14T19:49:36Z","receivedAt":"2025-09-14T19:51:04Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nWe are about to complicate the command handling by allowing *deprecated*\nbuiltins to be shadowed by aliases.  We need to organize the code in\norder to facilitate that.[1]\n\nThe code in the `while(1)` speculatively adds commands to the list\nbefore finding out if it’s an alias.  Let’s instead move it inside\n`handle_alias(...)`—where it conceptually belongs anyway—and in turn\nonly run this logic when we have found an alias.[2][3]\n\n[1]: We will do that with an additional call to `handle_alias(1)` inside\n    the loop.  *Not* moving this code leaves a blind spot; we will miss\n    alias looping crafted via deprecated builtin names\n[2]: Also rename the list to a more descriptive name\n[3]: The error output `'<cmd1>' is aliased to '<cmd2>'` is used for the\n    `<cmd2> -h` case.  We will get one more (redundant) line of output\n    in the case of alias looping; that is covered in the test “looping\n    aliases - deprecated builtins” in the next commit\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v5 (new):\n    \n    Do a preliminary code movement in order to avoid loop aliasing in the\n    specific case of alias-shadowing deprecated builtins.\n    \n    Link: https://lore.kernel.org/git/20250911204302.GA1907101@coredump.intra.peff.net/\n    \n    As noted in footnote 3: you get one more line of output before the loop\n    aliasing is detected (and see the next commit):\n    \n        'whatchanged' is aliased to 'pack-redundant'\n        'pack-redundant' is aliased to 'whatchanged'\n        'whatchanged' is aliased to 'pack-redundant'\n        fatal: alias loop detected: expansion of 'whatchanged' does not terminate:\n          whatchanged <==\n          pack-redundant ==>\n    \n    This is an informational message specifically for `<git cmd> -h`.\n    This output can be gotten after the next commit.\n    \n    The output in this case is very incidental:\n    \n    1. You are aliasing deprecated commands in a chain\n    2. You are using `-h`\n    3. You are going to get caught by the alias loop checker\n\n git.c | 47 ++++++++++++++++++++++++-----------------------\n 1 file changed, 24 insertions(+), 23 deletions(-)\n\ndiff --git a/git.c b/git.c\nindex 511efdf2056..f9c2d8c8d86 100644\n--- a/git.c\n+++ b/git.c\n@@ -365,7 +365,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \treturn (*argv) - orig_argv;\n }\n \n-static int handle_alias(struct strvec *args)\n+static int handle_alias(struct strvec *args, struct string_list *expanded_aliases)\n {\n \tint envchanged = 0, ret = 0, saved_errno = errno;\n \tint count, option_count;\n@@ -376,6 +376,8 @@ static int handle_alias(struct strvec *args)\n \talias_command = args->v[0];\n \talias_string = alias_lookup(alias_command);\n \tif (alias_string) {\n+\t\tstruct string_list_item *seen;\n+\n \t\tif (args->nr == 2 && !strcmp(args->v[1], \"-h\"))\n \t\t\tfprintf_ln(stderr, _(\"'%s' is aliased to '%s'\"),\n \t\t\t\t   alias_command, alias_string);\n@@ -423,6 +425,24 @@ static int handle_alias(struct strvec *args)\n \t\tif (!strcmp(alias_command, new_argv[0]))\n \t\t\tdie(_(\"recursive alias: %s\"), alias_command);\n \n+\t\tseen = unsorted_string_list_lookup(expanded_aliases, args->v[0]);\n+\t\tif (seen) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tfor (size_t i = 0; i < expanded_aliases->nr; i++) {\n+\t\t\t\tstruct string_list_item *item = &expanded_aliases->items[i];\n+\n+\t\t\t\tstrbuf_addf(&sb, \"\\n  %s\", item->string);\n+\t\t\t\tif (item == seen)\n+\t\t\t\t\tstrbuf_addstr(&sb, \" <==\");\n+\t\t\t\telse if (i == expanded_aliases->nr - 1)\n+\t\t\t\t\tstrbuf_addstr(&sb, \" ==>\");\n+\t\t\t}\n+\t\t\tdie(_(\"alias loop detected: expansion of '%s' does\"\n+\t\t\t      \" not terminate:%s\"), expanded_aliases->items[0].string, sb.buf);\n+\t\t}\n+\n+\t\tstring_list_append(expanded_aliases, args->v[0]);\n+\n \t\ttrace_argv_printf(new_argv,\n \t\t\t\t  \"trace: alias expansion: %s =>\",\n \t\t\t\t  alias_command);\n@@ -806,8 +826,7 @@ static void execv_dashed_external(const char **argv)\n static int run_argv(struct strvec *args)\n {\n \tint done_alias = 0;\n-\tstruct string_list cmd_list = STRING_LIST_INIT_DUP;\n-\tstruct string_list_item *seen;\n+\tstruct string_list expanded_aliases = STRING_LIST_INIT_DUP;\n \n \twhile (1) {\n \t\t/*\n@@ -859,35 +878,17 @@ static int run_argv(struct strvec *args)\n \t\t/* .. then try the external ones */\n \t\texecv_dashed_external(args->v);\n \n-\t\tseen = unsorted_string_list_lookup(&cmd_list, args->v[0]);\n-\t\tif (seen) {\n-\t\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\t\tfor (size_t i = 0; i < cmd_list.nr; i++) {\n-\t\t\t\tstruct string_list_item *item = &cmd_list.items[i];\n-\n-\t\t\t\tstrbuf_addf(&sb, \"\\n  %s\", item->string);\n-\t\t\t\tif (item == seen)\n-\t\t\t\t\tstrbuf_addstr(&sb, \" <==\");\n-\t\t\t\telse if (i == cmd_list.nr - 1)\n-\t\t\t\t\tstrbuf_addstr(&sb, \" ==>\");\n-\t\t\t}\n-\t\t\tdie(_(\"alias loop detected: expansion of '%s' does\"\n-\t\t\t      \" not terminate:%s\"), cmd_list.items[0].string, sb.buf);\n-\t\t}\n-\n-\t\tstring_list_append(&cmd_list, args->v[0]);\n-\n \t\t/*\n \t\t * It could be an alias -- this works around the insanity\n \t\t * of overriding \"git log\" with \"git show\" by having\n \t\t * alias.log = show\n \t\t */\n-\t\tif (!handle_alias(args))\n+\t\tif (!handle_alias(args, &expanded_aliases))\n \t\t\tbreak;\n \t\tdone_alias = 1;\n \t}\n \n-\tstring_list_clear(&cmd_list, 0);\n+\tstring_list_clear(&expanded_aliases, 0);\n \n \treturn done_alias;\n }\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"526288","messageId":"4bfe36cccd03fd822fc40c24bb46d4e237d11ac4.1757879060.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757879060.git.code@khaugsbakk.name","subject":"[PATCH v5 3/8] git: allow alias-shadowing deprecated builtins","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-14T19:49:37Z","receivedAt":"2025-09-14T19:51:23Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\ngit-whatchanged(1) is deprecated and you need to pass\n`--i-still-use-this` in order to force it to work as before.\nThere are two affected users, or usages:\n\n1. people who use the command in scripts; and\n2. people who are used to using it interactively.\n\nFor (1) the replacement is straightforward.[1]  But people in (2) might\nlike the name or be really used to typing it.[3]\n\nAn obvious first thought is to suggest aliasing `whatchanged` to the\ngit-log(1) equivalent.[1]  But this doesn’t work and is awkward since you\ncannot shadow builtins via aliases.\n\nNow you are left in an uncomfortable limbo; your alias won’t work until\nthe command is removed for good.\n\nLet’s lift this limitation by allowing *deprecated* builtins to be\nshadowed by aliases.[4]\n\nThe only observed demand for aliasing has been for git-whatchanged(1),\nnot for git-pack-redundant(1).  But let’s be consistent and treat all\ndeprecated commands the same.\n\n[1]:\n\n        git log --raw --no-merges\n\n     With a minor caveat: you get different outputs if you happen to\n     have empty commits (no changes)[2]\n[2]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n[3]: https://lore.kernel.org/git/BL3P221MB0449288C8B0FA448A227FD48833AA@BL3P221MB0449.NAMP221.PROD.OUTLOOK.COM/\n[4]: The test “looping aliases - deprecated builtins” covers the\n    case of the extra output mentioned in footnote number 3 of the\n    previous commit\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v5:\n    \n    Fix a leak caused by calling `alias_lookup(...)` without freeing the\n    return value by not calling that function.\n    \n    Add tests for deprecated builtins for infinite alias looping as well as\n    simple chaining.  This is to exercise all code paths that we could think\n    of.\n    \n    Expand on the infinite alias looping test on the whole error\n    output (compared to the one above).  Link this change to the\n    previous patch/commit by mentioning it in a footnote.\n    \n    See the last footnote and the note on the previous commit/patch.  I have\n    opted not to get rid of this redundant line.  But this documents the\n    change and future changes that affect it will trigger this\n    assertion (say if they remove the redundant line).\n    \n    v4:\n    \n    Better `is_deprecated_command` implementation.\n    \n    v3 (new):\n    \n    Prerequisite for telling the user that they can alias `whatchanged` to\n    `git log --raw --no-merged`.\n    \n    Link: https://lore.kernel.org/git/cover.1756311355.git.code@khaugsbakk.name/T/#md434b0968f499263262fb1805d82b788b8349d9a\n    \n    > I think that is good advice, but... it won't do anything until we\n    > actually drop the whatchanged command, since until then we'll refuse to\n    > override the command (even the crippled --i-still-use-this one).\n    >\n    > We'd need something like the patch here:\n    \n    ❦\n    \n    The\n    \n        test_file_not_empty expect\n    \n    is here because the git(1) command could fail.  Make sure that it did\n    indeed output anyhing on stdout.  (Or if a previous redirect to `expect`\n    outputted something it should be completely different to `actual` in any\n    case)\n    \n    I don’t know if this is just a waste.\n\n Documentation/config/alias.adoc |  3 ++-\n git.c                           | 17 ++++++++++++++\n t/t0014-alias.sh                | 41 +++++++++++++++++++++++++++++++++\n 3 files changed, 60 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config/alias.adoc b/Documentation/config/alias.adoc\nindex 2c5db0ad842..3c8fab3a95c 100644\n--- a/Documentation/config/alias.adoc\n+++ b/Documentation/config/alias.adoc\n@@ -3,7 +3,8 @@ alias.*::\n \tafter defining `alias.last = cat-file commit HEAD`, the invocation\n \t`git last` is equivalent to `git cat-file commit HEAD`. To avoid\n \tconfusion and troubles with script usage, aliases that\n-\thide existing Git commands are ignored. Arguments are split by\n+\thide existing Git commands are ignored except for deprecated\n+\tcommands.  Arguments are split by\n \tspaces, the usual shell quoting and escaping are supported.\n \tA quote pair or a backslash can be used to quote them.\n +\ndiff --git a/git.c b/git.c\nindex f9c2d8c8d86..1310363e5c4 100644\n--- a/git.c\n+++ b/git.c\n@@ -823,12 +823,29 @@ static void execv_dashed_external(const char **argv)\n \t\texit(128);\n }\n \n+static int is_deprecated_command(const char *cmd)\n+{\n+\tstruct cmd_struct *builtin = get_builtin(cmd);\n+\treturn builtin && (builtin->option & DEPRECATED);\n+}\n+\n static int run_argv(struct strvec *args)\n {\n \tint done_alias = 0;\n \tstruct string_list expanded_aliases = STRING_LIST_INIT_DUP;\n \n \twhile (1) {\n+\t\t/*\n+\t\t * Allow deprecated commands to be overridden by aliases. This\n+\t\t * creates a seamless path forward for people who want to keep\n+\t\t * using the name after it is gone, but want to skip the\n+\t\t * deprecation complaint in the meantime.\n+\t\t */\n+\t\tif (is_deprecated_command(args->v[0]) &&\n+\t\t    handle_alias(args, &expanded_aliases)) {\n+\t\t\tdone_alias = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\t/*\n \t\t * If we tried alias and futzed with our environment,\n \t\t * it no longer is safe to invoke builtins directly in\ndiff --git a/t/t0014-alias.sh b/t/t0014-alias.sh\nindex 854d59ec58c..2f71c3265f0 100755\n--- a/t/t0014-alias.sh\n+++ b/t/t0014-alias.sh\n@@ -27,6 +27,21 @@ test_expect_success 'looping aliases - internal execution' '\n \ttest_grep \"^fatal: alias loop detected: expansion of\" output\n '\n \n+test_expect_success 'looping aliases - deprecated builtins' '\n+\ttest_config alias.whatchanged pack-redundant &&\n+\ttest_config alias.pack-redundant whatchanged &&\n+\tcat >expect <<-EOF &&\n+\t${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n+\t${SQ}pack-redundant${SQ} is aliased to ${SQ}whatchanged${SQ}\n+\t${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n+\tfatal: alias loop detected: expansion of ${SQ}whatchanged${SQ} does not terminate:\n+\t  whatchanged <==\n+\t  pack-redundant ==>\n+\tEOF\n+\ttest_must_fail git whatchanged -h 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n # This test is disabled until external loops are fixed, because would block\n # the test suite for a full minute.\n #\n@@ -55,4 +70,30 @@ test_expect_success 'tracing a shell alias with arguments shows trace of prepare\n \ttest_cmp expect actual\n '\n \n+can_alias_deprecated_builtin () {\n+\tcmd=\"$1\" &&\n+\t# some git(1) commands will fail for `-h` (the case for\n+\t# git-status as of 2025-09-07)\n+\ttest_might_fail git status -h >expect &&\n+\ttest_file_not_empty expect &&\n+\ttest_might_fail git -c alias.\"$cmd\"=status \"$cmd\" -h >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'can alias-shadow deprecated builtins' '\n+\tfor cmd in $(git --list-cmds=deprecated)\n+\tdo\n+\t\tcan_alias_deprecated_builtin \"$cmd\" || return 1\n+\tdone\n+'\n+\n+test_expect_success 'can alias-shadow via two deprecated builtins' '\n+\t# some git(1) commands will fail... (see above)\n+\ttest_might_fail git status -h >expect &&\n+\ttest_file_not_empty expect &&\n+\ttest_might_fail git -c alias.whatchanged=pack-redundant \\\n+\t\t-c alias.pack-redundant=status whatchanged -h >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"526289","messageId":"a04c6ae968e6423d59558671036f21e95b97bd8c.1757879060.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757879060.git.code@khaugsbakk.name","subject":"[PATCH v5 4/8] t0014: test shadowing of aliases for a sample of builtins","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-14T19:49:38Z","receivedAt":"2025-09-14T19:51:42Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThe previous commit added tests for shadowing deprecated builtins.\nLet’s make the test suite more complete by exercising a sample of\nthe builtins and in turn test the documentation for git-config(1):\n\n    To avoid confusion and troubles with script usage, aliases that hide\n    existing Git commands are ignored except for deprecated commands.\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v3 (new):\n    \n    Only a sample of builtins since the test file takes about 600ms longer\n    if I test all of them.\n\n t/t0014-alias.sh | 17 +++++++++++++++++\n 1 file changed, 17 insertions(+)\n\ndiff --git a/t/t0014-alias.sh b/t/t0014-alias.sh\nindex 2f71c3265f0..1ac739a2737 100755\n--- a/t/t0014-alias.sh\n+++ b/t/t0014-alias.sh\n@@ -96,4 +96,21 @@ test_expect_success 'can alias-shadow via two deprecated builtins' '\n \ttest_cmp expect actual\n '\n \n+cannot_alias_regular_builtin () {\n+\tcmd=\"$1\" &&\n+\t# some git(1) commands will fail... (see above)\n+\ttest_might_fail git \"$cmd\" -h >expect &&\n+\ttest_file_not_empty expect &&\n+\ttest_might_fail git -c alias.\"$cmd\"=status \"$cmd\" -h >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'cannot alias-shadow a sample of regular builtins' '\n+\tfor cmd in grep check-ref-format interpret-trailers \\\n+\t\tcheckout-index fast-import diagnose rev-list prune\n+\tdo\n+\t\tcannot_alias_regular_builtin \"$cmd\" || return 1\n+\tdone\n+'\n+\n test_done\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"526290","messageId":"2f78ab2e28c64f05d807cb9460fa62eb1011a57f.1757879060.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757879060.git.code@khaugsbakk.name","subject":"[PATCH v5 5/8] you-still-use-that??: help the user help themselves","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-14T19:49:39Z","receivedAt":"2025-09-14T19:52:00Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nGive the user a list of suggestions for what to do when they run a\ndeprecated command.\n\nThe first order of action will be to check the breaking changes\ndocument;[1] this short error message says nothing about why this\ncommand is deprecated, and in any case going into any kind of detail\nmight overwhelm the user.\n\nThen they can find out if this has been discussed on the mailing list.\nThen users who e.g. are using git-whatchanged(1) can learn that this is\narguably a plug-in replacement:\n\n    git log <opts> --raw --no-merges\n\nFinally they are invited to send an email to the mailing list.\n\nAlso drop the “please add” part in favor of just using the “refusing”\ndie-message; these two would have been right after each other in this\nnew version.\n\nAlso drop “Thanks” since it now would require a new paragraph.\n\n[1]: www.git-scm.com has a disclaimer for these internal documents that\n    says that “This information is specific to the Git project”.  That’s\n    misleading in this particular case.  But users are unlikely to get\n    discouraged from reading about why they (or their programs) cannot run a\n    command any more; it clearly concerns them.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v2:\n    \n    I had second thoughts about the bullet point about send-an-email.\n    Change it to the one Eric Sunshine proposed;[1] make sure to spell\n    out that you can send an email conditioned on not finding a suitable\n    replacement.\n    \n    Also change the area to something more pointed.\n    \n    And also use a clear URL to refer to www.git-scm.com.\n    \n    [1]: https://lore.kernel.org/git/CAPig+cQkVP57n_FE6dJ0uxvai-J7usxKFp8gzfEbPY=Ytsd6=Q@mail.gmail.com/\n    \n    • Change send-an-email bullet point\n    • Change the area\n    • Use www.git-scm.com, not simply git-scm\n    \n    v1:\n    \n    An alternative to linking to www.git-scm.com is to move this document to\n    a regular installed man page:\n    \n        gitbreaking-changes(7)\n    \n    What do you think?\n    \n    I would then have to base my topic on the in-flight\n    pw/3.0-commentchar-auto-deprecation, which in turn depends on\n    ps/config-wo-the-repository.\n    \n    Or just wait a bit for these to settle in.\n\n usage.c | 23 ++++++++++++++++++-----\n 1 file changed, 18 insertions(+), 5 deletions(-)\n\ndiff --git a/usage.c b/usage.c\nindex 81913236a4a..35dc57eb07e 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -7,6 +7,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"trace2.h\"\n+#include \"strbuf.h\"\n \n static void vfreportf(FILE *f, const char *prefix, const char *err, va_list params)\n {\n@@ -377,12 +378,24 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n \n NORETURN void you_still_use_that(const char *command_name)\n {\n+\tstruct strbuf percent_encoded = STRBUF_INIT;\n+\tstrbuf_add_percentencode(&percent_encoded,\n+\t\t\t\t command_name,\n+\t\t\t\t STRBUF_ENCODE_SLASH);\n+\n \tfprintf(stderr,\n \t\t_(\"'%s' is nominated for removal.\\n\"\n-\t\t  \"If you still use this command, please add an extra\\n\"\n-\t\t  \"option, '--i-still-use-this', on the command line\\n\"\n-\t\t  \"and let us know you still use it by sending an e-mail\\n\"\n-\t\t  \"to <git@vger.kernel.org>.  Thanks.\\n\"),\n-\t\tcommand_name);\n+\t\t  \"If you still use this command, here's what you can do:\\n\"\n+\t\t  \"\\n\"\n+\t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n+\t\t  \"- check if anyone has discussed this on the mailing\\n\"\n+\t\t  \"  list and if they came up with something that can\\n\"\n+\t\t  \"  help you: https://lore.kernel.org/git/?q=%s\\n\"\n+\t\t  \"- send an email to <git@vger.kernel.org> to let us\\n\"\n+\t\t  \"  know that you still use this command and were unable\\n\"\n+\t\t  \"  to determine a suitable replacement\\n\"\n+\t\t  \"\\n\"),\n+\t\tcommand_name, percent_encoded.buf);\n+\tstrbuf_release(&percent_encoded);\n \tdie(_(\"refusing to run without --i-still-use-this\"));\n }\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"526291","messageId":"3444306664589067a151fc4a50c7a51869bb18de.1757879060.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757879060.git.code@khaugsbakk.name","subject":"[PATCH v5 6/8] whatchanged: hint about git-log(1) and aliasing","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-14T19:49:40Z","receivedAt":"2025-09-14T19:52:19Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThere have been quite a few `--i-still-use-this` user reports since Git\n2.51.0 was released.[1][2]  And it doesn’t seem like they are reading\nthe man page about the git-log(1) equivalent.\n\nTell them what options to plug into git-log(1), either as a replacement\ncommand or as an alias.[3]  That template produces almost the same\noutput[4] and is arguably a plug-in replacement.  Concretely, add\nan optional `hint` argument so that we can use it right after the\ninitial error line.\n\nAlso mention the same concrete options in the documentation while we’re\nat it.\n\n[1]: E.g.,\n    • https://lore.kernel.org/git/e1a69dea-bcb6-45fc-83d3-9e50d32c410b@5y5.one/\n    • https://lore.kernel.org/git/1011073f-9930-4360-a42f-71eb7421fe3f@chrispalmer.uk/#t\n    • https://lore.kernel.org/git/9fcbfcc4-79f9-421f-b9a4-dc455f7db485@acm.org/#t\n    • https://lore.kernel.org/git/83241BDE-1E0D-489A-9181-C608E9FCC17B@gmail.com/\n[2]: The error message on 2.51.0 does tell them to report it, unconditionally\n[3]: We allow aliasing deprecated builtins now for people who are very\n    used to the command name or just like it a lot\n[4]: You only get different outputs if you happen to have empty\n     commits (no changes)[4]\n[5]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v5:\n    \n    Mention the aliasing hint in the commit message.\n    \n    v3:\n    \n    Add an alias hint now that that is possible.  Also prefix each hint-line\n    with `hint: `.\n    \n    v2:\n    \n    Review found a whitespace error in the prev. patch version.  I found a\n    broken footnote and wanted to expand on the last footnote.\n    \n    • Fix whitespace error (I should have used `ci/check-whitespace.sh\n      v2.51.0`)\n    • Add missing colon (:) to footnote\n    • Expand on footnote; a sentence is enough to summarize the difference\n\n Documentation/git-whatchanged.adoc |  6 +++++-\n builtin/log.c                      |  8 +++++++-\n builtin/pack-redundant.c           |  2 +-\n git-compat-util.h                  |  2 +-\n usage.c                            | 14 ++++++++++----\n 5 files changed, 24 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-whatchanged.adoc b/Documentation/git-whatchanged.adoc\nindex d21484026fe..e71d2aa2d27 100644\n--- a/Documentation/git-whatchanged.adoc\n+++ b/Documentation/git-whatchanged.adoc\n@@ -24,7 +24,11 @@ Shows commit logs and diff output each commit introduces.\n \n New users are encouraged to use linkgit:git-log[1] instead.  The\n `whatchanged` command is essentially the same as linkgit:git-log[1]\n-but defaults to showing the raw format diff output and skipping merges.\n+but defaults to showing the raw format diff output and skipping merges:\n+\n+----\n+git log --raw --no-merges\n+----\n \n The command is primarily kept for historical reasons; fingers of\n many people who learned Git long before `git log` was invented by\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c2f8bbf8630..1d1e6e9130a 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -543,7 +543,13 @@ int cmd_whatchanged(int argc,\n \tcmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);\n \n \tif (!cfg.i_still_use_this)\n-\t\tyou_still_use_that(\"git whatchanged\");\n+\t\tyou_still_use_that(\"git whatchanged\",\n+\t\t\t\t   _(\"\\n\"\n+\t\t\t\t     \"hint: You can replace 'git whatchanged <opts>' with:\\n\"\n+\t\t\t\t     \"hint:\\tgit log <opts> --raw --no-merges\\n\"\n+\t\t\t\t     \"hint: Or make an alias:\\n\"\n+\t\t\t\t     \"hint:\\tgit config set --global alias.whatchanged 'log --raw --no-merges'\\n\"\n+\t\t\t\t     \"\\n\"));\n \n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\ndiff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c\nindex fe81c293e3a..5d5ae4afa28 100644\n--- a/builtin/pack-redundant.c\n+++ b/builtin/pack-redundant.c\n@@ -626,7 +626,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED, s\n \t}\n \n \tif (!i_still_use_this)\n-\t\tyou_still_use_that(\"git pack-redundant\");\n+\t\tyou_still_use_that(\"git pack-redundant\", NULL);\n \n \tif (load_all_packs)\n \t\tload_all();\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 9408f463e31..398e0fac4fa 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -460,7 +460,7 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));\n \n void show_usage_if_asked(int ac, const char **av, const char *err);\n \n-NORETURN void you_still_use_that(const char *command_name);\n+NORETURN void you_still_use_that(const char *command_name, const char *hint);\n \n #ifndef NO_OPENSSL\n #ifdef APPLE_COMMON_CRYPTO\ndiff --git a/usage.c b/usage.c\nindex 35dc57eb07e..7545a616453 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -376,7 +376,8 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n \tva_end(ap);\n }\n \n-NORETURN void you_still_use_that(const char *command_name)\n+\n+NORETURN void you_still_use_that(const char *command_name, const char *hint)\n {\n \tstruct strbuf percent_encoded = STRBUF_INIT;\n \tstrbuf_add_percentencode(&percent_encoded,\n@@ -384,8 +385,13 @@ NORETURN void you_still_use_that(const char *command_name)\n \t\t\t\t STRBUF_ENCODE_SLASH);\n \n \tfprintf(stderr,\n-\t\t_(\"'%s' is nominated for removal.\\n\"\n-\t\t  \"If you still use this command, here's what you can do:\\n\"\n+\t\t_(\"'%s' is nominated for removal.\\n\"), command_name);\n+\n+\tif (hint)\n+\t\tfputs(hint, stderr);\n+\n+\tfprintf(stderr,\n+\t\t_(\"If you still use this command, here's what you can do:\\n\"\n \t\t  \"\\n\"\n \t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n \t\t  \"- check if anyone has discussed this on the mailing\\n\"\n@@ -395,7 +401,7 @@ NORETURN void you_still_use_that(const char *command_name)\n \t\t  \"  know that you still use this command and were unable\\n\"\n \t\t  \"  to determine a suitable replacement\\n\"\n \t\t  \"\\n\"),\n-\t\tcommand_name, percent_encoded.buf);\n+\t\tpercent_encoded.buf);\n \tstrbuf_release(&percent_encoded);\n \tdie(_(\"refusing to run without --i-still-use-this\"));\n }\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"526292","messageId":"b0aabd793f05e55193891b31485e4113e069109d.1757879060.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757879060.git.code@khaugsbakk.name","subject":"[PATCH v5 7/8] whatchanged: remove not-even-shorter clause","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-14T19:49:41Z","receivedAt":"2025-09-14T19:52:38Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThe closest equivalent is `git log --raw --no-merges`.\n\nAlso change to “defaults” (implicit plural).\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n Documentation/git-whatchanged.adoc | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-whatchanged.adoc b/Documentation/git-whatchanged.adoc\nindex e71d2aa2d27..436e219b7d0 100644\n--- a/Documentation/git-whatchanged.adoc\n+++ b/Documentation/git-whatchanged.adoc\n@@ -15,7 +15,7 @@ WARNING\n -------\n `git whatchanged` has been deprecated and is scheduled for removal in\n a future version of Git, as it is merely `git log` with different\n-default; `whatchanged` is not even shorter to type than `log --raw`.\n+defaults.\n \n DESCRIPTION\n -----------\n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"526293","messageId":"5021647c2450bc124af20e5d7aa8bd6dd859e57a.1757879060.git.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757879060.git.code@khaugsbakk.name","subject":"[PATCH v5 8/8] BreakingChanges: remove claim about whatchanged reports","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-14T19:49:42Z","receivedAt":"2025-09-14T19:52:57Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThis was written in e836757e14b (whatschanged: list it in\nBreakingChanges document, 2025-05-12) which was on the same\ntopic that added the `--i-still-use-this` requirement.[1]\n\nMaybe it was a work-in-progress comment/status.\n\n[1]: jc/you-still-use-whatchanged\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    Footnote solely to avoid awkward paragraph wrapping...\n\n Documentation/BreakingChanges.adoc | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc\nindex f8d2eba061c..c4985163c3c 100644\n--- a/Documentation/BreakingChanges.adoc\n+++ b/Documentation/BreakingChanges.adoc\n@@ -235,7 +235,7 @@ These features will be removed.\n   equivalent `git log --raw`.  We have nominated the command for\n   removal, have changed the command to refuse to work unless the\n   `--i-still-use-this` option is given, and asked the users to report\n-  when they do so.  So far there hasn't been a single complaint.\n+  when they do so.\n +\n The command will be removed.\n \n-- \n2.51.0.16.gcd94ab5bf81\n\n"},{"id":"526294","messageId":"20250915014451.GA44900@coredump.intra.peff.net","threadId":"64048","inReplyTo":"eb7e569a-b297-41d2-82b0-956210292b64@app.fastmail.com","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-15T01:44:51Z","receivedAt":"2025-09-15T01:51:35Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Sep 14, 2025 at 07:24:14PM +0200, Kristoffer Haugsbakk wrote:\n\n> > If you want to get rid of that last paragraph, I think it really is as\n> > simple as checking the expanded alias new_argv[0] as soon as we see it,\n> > like:\n> \n> I tried using both of these changes (the patches) but the `alias...`\n> test suite started failing.\n\nHmm, it still passes for me. If I do just this on top of your v5, it\nlikewise passes (except for your new test which expects the redundant\nline).\n\ndiff --git a/git.c b/git.c\nindex 10315742f8..c5fad56813 100644\n--- a/git.c\n+++ b/git.c\n@@ -425,7 +425,10 @@ static int handle_alias(struct strvec *args, struct string_list *expanded_aliase\n \t\tif (!strcmp(alias_command, new_argv[0]))\n \t\t\tdie(_(\"recursive alias: %s\"), alias_command);\n \n-\t\tseen = unsorted_string_list_lookup(expanded_aliases, args->v[0]);\n+\t\tstring_list_append(expanded_aliases, alias_command);\n+\t\tseen = unsorted_string_list_lookup(expanded_aliases,\n+\t\t\t\t\t\t   new_argv[0]);\n+\n \t\tif (seen) {\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n \t\t\tfor (size_t i = 0; i < expanded_aliases->nr; i++) {\n@@ -441,8 +444,6 @@ static int handle_alias(struct strvec *args, struct string_list *expanded_aliase\n \t\t\t      \" not terminate:%s\"), expanded_aliases->items[0].string, sb.buf);\n \t\t}\n \n-\t\tstring_list_append(expanded_aliases, args->v[0]);\n-\n \t\ttrace_argv_printf(new_argv,\n \t\t\t\t  \"trace: alias expansion: %s =>\",\n \t\t\t\t  alias_command);\n\n> I ended up with not changing it for v5.  I missed the first time around\n> that this informational message is only “logged” in the specific case of\n> `<git cmd> -h`.  In turn you only get one more line of output when you\n> are (1) chaining deprecated aliases, and (2) making a loop.\n\nI don't think (1) is necessary. With your v5 I get:\n\n  $ ./git -c alias.one=two -c alias.two=one one -h\n  'one' is aliased to 'two'\n  'two' is aliased to 'one'\n  'one' is aliased to 'two'\n  fatal: alias loop detected: expansion of 'one' does not terminate:\n    one <==\n    two ==>\n\nSo the extra line is printed even without deprecated aliases.\n\n-Peff\n"},{"id":"526302","messageId":"7d63af22-3886-4dcf-863e-dec333601b3b@app.fastmail.com","threadId":"64048","inReplyTo":"20250915014451.GA44900@coredump.intra.peff.net","subject":"Re: [PATCH v4 2/7] git: allow alias-shadowing deprecated builtins","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-15T06:27:44Z","receivedAt":"2025-09-15T06:28:05Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Mon, Sep 15, 2025, at 03:44, Jeff King wrote:\n> On Sun, Sep 14, 2025 at 07:24:14PM +0200, Kristoffer Haugsbakk wrote:\n>\n>> > If you want to get rid of that last paragraph, I think it really is as\n>> > simple as checking the expanded alias new_argv[0] as soon as we see it,\n>> > like:\n>>\n>> I tried using both of these changes (the patches) but the `alias...`\n>> test suite started failing.\n>\n> Hmm, it still passes for me. If I do just this on top of your v5, it\n> likewise passes (except for your new test which expects the redundant\n> line).\n>\n>[snip diff]\n\nThat works for me.  Thanks!  I will send the next round this evening\nor tomorrow.\n\nIt will be good to get rid of those footnotes.\n\n>> I ended up with not changing it for v5.  I missed the first time around\n>> that this informational message is only “logged” in the specific case of\n>> `<git cmd> -h`.  In turn you only get one more line of output when you\n>> are (1) chaining deprecated aliases, and (2) making a loop.\n>\n> I don't think (1) is necessary. With your v5 I get:\n>\n>   $ ./git -c alias.one=two -c alias.two=one one -h\n>   'one' is aliased to 'two'\n>   'two' is aliased to 'one'\n>   'one' is aliased to 'two'\n>   fatal: alias loop detected: expansion of 'one' does not terminate:\n>     one <==\n>     two ==>\n>\n> So the extra line is printed even without deprecated aliases.\n\nThat’s right, that part’s in error.\n"},{"id":"526384","messageId":"xmqqy0qffrri.fsf@gitster.g","threadId":"64048","inReplyTo":"cover.1757879060.git.code@khaugsbakk.name","subject":"Re: [PATCH v5 0/8] you-still-use-that??: improve breaking changes troubleshooting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-15T19:19:13Z","receivedAt":"2025-09-15T19:19:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"kristofferhaugsbakk@fastmail.com writes:\n\n> § Changes in v5\n>\n> Fix the leak that Peff found (with Peff’s suggestion).  Also fix (with\n> Peff’s help) a regression where you were able to get into an infinite\n> alias loop specifically when using deprecated builtin names.\n\nWhen merged to 'seen' and run with WITH_BREAKING_CHANGES=YesPlease,\nlooping alias test would chage the behaviour, and the test needs to\nbe prepared for it, I think.  Here is how t0014 fails\n\nexpecting success of 0014.4 'looping aliases - deprecated builtins':\n        test_config alias.whatchanged pack-redundant &&\n        test_config alias.pack-redundant whatchanged &&\n        cat >expect <<-EOF &&\n        ${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n        ${SQ}pack-redundant${SQ} is aliased to ${SQ}whatchanged${SQ}\n        ${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n        fatal: alias loop detected: expansion of ${SQ}whatchanged${SQ} does not terminate:\n          whatchanged <==\n          pack-redundant ==>\n        EOF\n        test_must_fail git whatchanged -h 2>actual &&\n        test_cmp expect actual\n\n--- expect      2025-09-15 19:16:26.340366359 +0000\n+++ actual      2025-09-15 19:16:26.360366313 +0000\n@@ -1,6 +1 @@\n-'whatchanged' is aliased to 'pack-redundant'\n-'pack-redundant' is aliased to 'whatchanged'\n-'whatchanged' is aliased to 'pack-redundant'\n-fatal: alias loop detected: expansion of 'whatchanged' does not terminate:\n-  whatchanged <==\n-  pack-redundant ==>\n+fatal: cannot handle whatchanged as a builtin\nnot ok 4 - looping aliases - deprecated builtins\n"},{"id":"526499","messageId":"e4fc3b83-15ca-4844-92c6-1f41169161ed@app.fastmail.com","threadId":"64048","inReplyTo":"xmqqy0qffrri.fsf@gitster.g","subject":"Re: [PATCH v5 0/8] you-still-use-that??: improve breaking changes troubleshooting","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2025-09-16T20:47:58Z","receivedAt":"2025-09-16T20:48:20Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Mon, Sep 15, 2025, at 21:19, Junio C Hamano wrote:\n> kristofferhaugsbakk@fastmail.com writes:\n>\n>> § Changes in v5\n>>\n>> Fix the leak that Peff found (with Peff’s suggestion).  Also fix (with\n>> Peff’s help) a regression where you were able to get into an infinite\n>> alias loop specifically when using deprecated builtin names.\n>\n> When merged to 'seen' and run with WITH_BREAKING_CHANGES=YesPlease,\n> looping alias test would chage the behaviour, and the test needs to\n> be prepared for it, I think.  Here is how t0014 fails\n>\n> expecting success of 0014.4 'looping aliases - deprecated builtins':\n>         test_config alias.whatchanged pack-redundant &&\n>         test_config alias.pack-redundant whatchanged &&\n>         cat >expect <<-EOF &&\n>         ${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n>         ${SQ}pack-redundant${SQ} is aliased to ${SQ}whatchanged${SQ}\n>         ${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n>         fatal: alias loop detected: expansion of ${SQ}whatchanged${SQ}\n> does not terminate:\n>           whatchanged <==\n>           pack-redundant ==>\n>         EOF\n>         test_must_fail git whatchanged -h 2>actual &&\n>         test_cmp expect actual\n>\n> --- expect      2025-09-15 19:16:26.340366359 +0000\n> +++ actual      2025-09-15 19:16:26.360366313 +0000\n> @@ -1,6 +1 @@\n> -'whatchanged' is aliased to 'pack-redundant'\n> -'pack-redundant' is aliased to 'whatchanged'\n> -'whatchanged' is aliased to 'pack-redundant'\n> -fatal: alias loop detected: expansion of 'whatchanged' does not terminate:\n> -  whatchanged <==\n> -  pack-redundant ==>\n> +fatal: cannot handle whatchanged as a builtin\n> not ok 4 - looping aliases - deprecated builtins\n\nThanks.  This is (maybe you surmise or know already but) a\nproblem when on the branch itself as well.  I did some compiling\n`WITH_BREAKING_CHANGES=YesPlease` and it seems that 07572f220a8\n(whatchanged: remove when built with WITH_BREAKING_CHANGES, 2025-05-12)\nmade it a hard error to call `whatchanged` after its death:\n\n    $ ./bin-wrappers/git whatchanged\n    fatal: cannot handle whatchanged as a builtin\n\nThis seems fine, although I would have expected it to say something more\nneutral like “command not found”.\n\nWhat I don’t understand though is this:\n\n    $ ./bin-wrappers/git -c alias.whatchanged=status whatchanged\n    fatal: cannot handle whatchanged as a builtin\n\nPeople have been saying that people can at least alias `whatchanged` to\n`log <something>` and then it will work after the removal.  But now that\nit has been removed (according to the commit message) its spirit lives\non and prevents reusing the name?\n\nAm I missing something here?\n"},{"id":"526516","messageId":"20250916232446.GA1189675@coredump.intra.peff.net","threadId":"64048","inReplyTo":"e4fc3b83-15ca-4844-92c6-1f41169161ed@app.fastmail.com","subject":"Re: [PATCH v5 0/8] you-still-use-that??: improve breaking changes troubleshooting","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-16T23:24:46Z","receivedAt":"2025-09-16T23:24:48Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Sep 16, 2025 at 10:47:58PM +0200, Kristoffer Haugsbakk wrote:\n\n> Thanks.  This is (maybe you surmise or know already but) a\n> problem when on the branch itself as well.  I did some compiling\n> `WITH_BREAKING_CHANGES=YesPlease` and it seems that 07572f220a8\n> (whatchanged: remove when built with WITH_BREAKING_CHANGES, 2025-05-12)\n> made it a hard error to call `whatchanged` after its death:\n> \n>     $ ./bin-wrappers/git whatchanged\n>     fatal: cannot handle whatchanged as a builtin\n> \n> This seems fine, although I would have expected it to say something more\n> neutral like “command not found”.\n\nI think the breaking-changes code for whatchanged did not update the\nMakefile appropriately. If I build with that flag:\n\n  [make sure it is not here]\n  $ rm git-whatchanged\n\n  [now build]\n  $ make WITH_BREAKING_CHANGES=YesPlease\n    [...]\n    BUILTIN git-whatchanged\n    SUBDIR templates\n\n  $ ls -l git-whatchanged\n  -rwxrwxr-x 140 peff peff 12104368 Sep 16 19:15 git-whatchanged*\n\nSo we have linked it in the usual way we do for builtins, so that\ngit-whatchanged would \"work\". But of course since it is not in the\nbuiltin table, it does not:\n\n  $ ./git-whatchanged\n  fatal: cannot handle whatchanged as a builtin\n\nAnd then because it exists in the exec-path, \"git whatchanged\" thinks\nthat it can run the dashed form, so it produces the same message.\n\nWe probably need this:\n\ndiff --git a/Makefile b/Makefile\nindex e11340c1ae..9311da5bb1 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -883,7 +883,9 @@ BUILT_INS += git-stage$X\n BUILT_INS += git-status$X\n BUILT_INS += git-switch$X\n BUILT_INS += git-version$X\n+ifndef WITH_BREAKING_CHANGES\n BUILT_INS += git-whatchanged$X\n+endif\n \n # what 'all' will build but not install in gitexecdir\n OTHER_PROGRAMS += git$X\n\nbut annoyingly an old build will leave git-whatchanged in place and\ncause the same confusion. And git-status won't even notice that it's\nleftover because .gitignore mentions it. But we can't drop it from\n.gitignore until the non-breaking-changes builds go away entirely. Yuck.\n\n> What I don’t understand though is this:\n> \n>     $ ./bin-wrappers/git -c alias.whatchanged=status whatchanged\n>     fatal: cannot handle whatchanged as a builtin\n> \n> People have been saying that people can at least alias `whatchanged` to\n> `log <something>` and then it will work after the removal.  But now that\n> it has been removed (according to the commit message) its spirit lives\n> on and prevents reusing the name?\n\nI'd have thought the alias took precedence over the stale executable\nfile. But it can't, because \"whatchanged\" is no longer marked as\ndeprecated in the builtin list. Because it's not in the builtin list at\nall! So it is just a \"normal\" non-deprecated external command from the\nperspective of git.c, and you are not allowed to override it.\n\nSo I think the alias code is working as expected, but the stale file is\ncausing the same confusion as above. Deleting it makes everything work.\n\n-Peff\n"},{"id":"526596","messageId":"8673bb92-6bc5-47b8-bef8-bd59669f095f@app.fastmail.com","threadId":"64048","inReplyTo":"20250916232446.GA1189675@coredump.intra.peff.net","subject":"Re: [PATCH v5 0/8] you-still-use-that??: improve breaking changes troubleshooting","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2025-09-17T15:41:18Z","receivedAt":"2025-09-17T15:41:59Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Wed, Sep 17, 2025, at 01:24, Jeff King wrote:\n> On Tue, Sep 16, 2025 at 10:47:58PM +0200, Kristoffer Haugsbakk wrote:\n>\n>> Thanks.  This is (maybe you surmise or know already but) a\n>> problem when on the branch itself as well.  I did some compiling\n>> `WITH_BREAKING_CHANGES=YesPlease` and it seems that 07572f220a8\n>> (whatchanged: remove when built with WITH_BREAKING_CHANGES, 2025-05-12)\n>> made it a hard error to call `whatchanged` after its death:\n>>\n>>     $ ./bin-wrappers/git whatchanged\n>>     fatal: cannot handle whatchanged as a builtin\n>>\n>> This seems fine, although I would have expected it to say something more\n>> neutral like “command not found”.\n>\n> I think the breaking-changes code for whatchanged did not update the\n> Makefile appropriately. If I build with that flag:\n>\n>   [make sure it is not here]\n>   $ rm git-whatchanged\n>\n>   [now build]\n>   $ make WITH_BREAKING_CHANGES=YesPlease\n>     [...]\n>     BUILTIN git-whatchanged\n>     SUBDIR templates\n>\n>   $ ls -l git-whatchanged\n>   -rwxrwxr-x 140 peff peff 12104368 Sep 16 19:15 git-whatchanged*\n>\n> So we have linked it in the usual way we do for builtins, so that\n> git-whatchanged would \"work\". But of course since it is not in the\n> builtin table, it does not:\n>\n>   $ ./git-whatchanged\n>   fatal: cannot handle whatchanged as a builtin\n>\n> And then because it exists in the exec-path, \"git whatchanged\" thinks\n> that it can run the dashed form, so it produces the same message.\n>\n> We probably need this:\n>\n> diff --git a/Makefile b/Makefile\n> index e11340c1ae..9311da5bb1 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -883,7 +883,9 @@ BUILT_INS += git-stage$X\n>  BUILT_INS += git-status$X\n>  BUILT_INS += git-switch$X\n>  BUILT_INS += git-version$X\n> +ifndef WITH_BREAKING_CHANGES\n>  BUILT_INS += git-whatchanged$X\n> +endif\n>\n>  # what 'all' will build but not install in gitexecdir\n>  OTHER_PROGRAMS += git$X\n>\n> but annoyingly an old build will leave git-whatchanged in place and\n> cause the same confusion. And git-status won't even notice that it's\n> leftover because .gitignore mentions it. But we can't drop it from\n> .gitignore until the non-breaking-changes builds go away entirely. Yuck.\n>\n>> What I don’t understand though is this:\n>>\n>>     $ ./bin-wrappers/git -c alias.whatchanged=status whatchanged\n>>     fatal: cannot handle whatchanged as a builtin\n>>\n>> People have been saying that people can at least alias `whatchanged` to\n>> `log <something>` and then it will work after the removal.  But now that\n>> it has been removed (according to the commit message) its spirit lives\n>> on and prevents reusing the name?\n>\n> I'd have thought the alias took precedence over the stale executable\n> file. But it can't, because \"whatchanged\" is no longer marked as\n> deprecated in the builtin list. Because it's not in the builtin list at\n> all! So it is just a \"normal\" non-deprecated external command from the\n> perspective of git.c, and you are not allowed to override it.\n>\n> So I think the alias code is working as expected, but the stale file is\n> causing the same confusion as above. Deleting it makes everything work.\n\nThat’s great, I think I understand.  It is my understanding that the\nchange to `Makefile` is needed for the test to not break when\n`WITH_BREAKING_CHANGES` is active.  Well I tried now with a *make* after\n\n    git fsck -d -x -f\n\nAnd it fails the same way.  So I’ll just steal this change... (as well)\nand include it in preparations for the others.\n\n(Thanks so much!)\n\n-----\n\nDraft of the commit message:\n\n-----\n\nMakefile: don’t add whatchanged after it has been removed\n\n07572f220a8 (whatchanged: remove when built with WITH_BREAKING_CHANGES,\n2025-05-12) set up the removal of git-whatchanged(1) when\n`WITH_BREAKING_CHANGES` is active.  Part of that work was removing it\nfrom `commands` in `git.c`.  But the the Makefile still lists it as a\nbuiltin .  That leaves it in the limbo of being linked but not being\ncallable; you get the generic error about not being able to call it as\na *builtin*:\n\n    $ git whatchanged\n    fatal: cannot handle whatchanged as a builtin\n\ninstead of the expected:\n\n    $ git whatchanged\n    git: 'whatchanged' is not a git command. See 'git --help'.\n"},{"id":"526602","messageId":"xmqqa52tdp1g.fsf@gitster.g","threadId":"64048","inReplyTo":"8673bb92-6bc5-47b8-bef8-bd59669f095f@app.fastmail.com","subject":"Re: [PATCH v5 0/8] you-still-use-that??: improve breaking changes troubleshooting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-17T16:25:31Z","receivedAt":"2025-09-17T16:25:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Kristoffer Haugsbakk\" <code@khaugsbakk.name> writes:\n\n> Draft of the commit message:\n>\n> -----\n>\n> Makefile: don’t add whatchanged after it has been removed\n>\n> 07572f220a8 (whatchanged: remove when built with WITH_BREAKING_CHANGES,\n> 2025-05-12) set up the removal of git-whatchanged(1) when\n> `WITH_BREAKING_CHANGES` is active.  Part of that work was removing it\n> from `commands` in `git.c`.  But the the Makefile still lists it as a\n> builtin .  That leaves it in the limbo of being linked but not being\n> callable; you get the generic error about not being able to call it as\n> a *builtin*:\n>\n>     $ git whatchanged\n>     fatal: cannot handle whatchanged as a builtin\n>\n> instead of the expected:\n>\n>     $ git whatchanged\n>     git: 'whatchanged' is not a git command. See 'git --help'.\n\nA change very well thought out.  Thanks for fixing my earlier\nbreakage.\n"},{"id":"526619","messageId":"cover.1758139856.short.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1757879060.git.code@khaugsbakk.name","subject":"[PATCH v6 0/9] you-still-use-that??: improve breaking changes troubleshooting","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-17T20:24:10Z","receivedAt":"2025-09-17T20:25:18Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nBased on the recent i-still-use-that reports about whatchanged, improve\nthe error reporting with this command in mind:\n\n1. Give more possible actions instead of just (only) asking them to send\n   an email\n2. Hint how to replace their git-whatchanged(1) use with git-log(1) or\n   an alias `whatchanged` (you can alias deprecated commands now)\n3. Minor documentation changes\n4. Add `deprecated` to `git --list-cmds`\n5. (new in v6) Don’t list git-whatchanged(1) as a builtin when\n   `WITH_BREAKING_CHANGES`\n\n§ What the errors now look like\n\n    $ ./git whatchanged\n    'git whatchanged' is nominated for removal.\n\n    hint: You can replace 'git whatchanged <opts>' with:\n    hint:   git log <opts> --raw --no-merges\n    hint: Or make an alias:\n    hint:   git config set --global alias.whatchanged 'log --raw --no-merges'\n\n    If you still use this command, here's what you can do:\n\n    - read https://git-scm.com/docs/BreakingChanges.html\n    - check if anyone has discussed this on the mailing\n      list and if they came up with something that can\n      help you: https://lore.kernel.org/git/?q=git%20whatchanged\n    - send an email to <git@vger.kernel.org> to let us\n      know that you still use this command and were unable\n      to determine a suitable replacement\n\n    fatal: refusing to run without --i-still-use-this\n    $ ./git pack-redundant\n    'git pack-redundant' is nominated for removal.\n    If you still use this command, here's what you can do:\n\n    - read https://git-scm.com/docs/BreakingChanges.html\n    - check if anyone has discussed this on the mailing\n      list and if they came up with something that can\n      help you: https://lore.kernel.org/git/?q=git%20pack-redundant\n    - send an email to <git@vger.kernel.org> to let us\n      know that you still use this command and were unable\n      to determine a suitable replacement\n\n    fatal: refusing to run without --i-still-use-this\n\n§ Changes in v6\n\nUse Peff’s patch to get rid of “the extra output”.[1]  Update commit\nmessages to not mention that.\n\nAlso use another fix from Peff (patch 1); don’t list git-whatchanged(1)\nas a builtin when `WITH_BREAKING_CHANGES`.\n\n🔗 1: https://lore.kernel.org/git/cover.1757446619.git.code@khaugsbakk.name/T/#mb3ac9c32c8527675a1f9cdf7709f2c74b3dd2a6c\n\nThanks\n\nKristoffer Haugsbakk (9):\n  Makefile: don’t add whatchanged after it has been removed\n  git: add `deprecated` category to --list-cmds\n  git: move seen-alias bookkeeping into handle_alias(...)\n  git: allow alias-shadowing deprecated builtins\n  t0014: test shadowing of aliases for a sample of builtins\n  you-still-use-that??: help the user help themselves\n  whatchanged: hint about git-log(1) and aliasing\n  whatchanged: remove not-even-shorter clause\n  BreakingChanges: remove claim about whatchanged reports\n\n Documentation/BreakingChanges.adoc |  2 +-\n Documentation/config/alias.adoc    |  3 +-\n Documentation/git-whatchanged.adoc |  8 ++-\n Documentation/git.adoc             |  3 +-\n Makefile                           |  2 +\n builtin/log.c                      |  8 ++-\n builtin/pack-redundant.c           |  2 +-\n git-compat-util.h                  |  2 +-\n git.c                              | 91 ++++++++++++++++++++----------\n t/t0014-alias.sh                   | 57 +++++++++++++++++++\n usage.c                            | 33 ++++++++---\n 11 files changed, 165 insertions(+), 46 deletions(-)\n\nInterdiff against v5:\ndiff --git a/Makefile b/Makefile\nindex e11340c1ae7..9311da5bb15 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -883,7 +883,9 @@ BUILT_INS += git-stage$X\n BUILT_INS += git-status$X\n BUILT_INS += git-switch$X\n BUILT_INS += git-version$X\n+ifndef WITH_BREAKING_CHANGES\n BUILT_INS += git-whatchanged$X\n+endif\n \n # what 'all' will build but not install in gitexecdir\n OTHER_PROGRAMS += git$X\ndiff --git a/git.c b/git.c\nindex 1310363e5c4..8c85da84c30 100644\n--- a/git.c\n+++ b/git.c\n@@ -425,7 +425,10 @@ static int handle_alias(struct strvec *args, struct string_list *expanded_aliase\n \t\tif (!strcmp(alias_command, new_argv[0]))\n \t\t\tdie(_(\"recursive alias: %s\"), alias_command);\n \n-\t\tseen = unsorted_string_list_lookup(expanded_aliases, args->v[0]);\n+\t\tstring_list_append(expanded_aliases, alias_command);\n+\t\tseen = unsorted_string_list_lookup(expanded_aliases,\n+\t\t\t\t\t\t   new_argv[0]);\n+\n \t\tif (seen) {\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n \t\t\tfor (size_t i = 0; i < expanded_aliases->nr; i++) {\n@@ -441,8 +444,6 @@ static int handle_alias(struct strvec *args, struct string_list *expanded_aliase\n \t\t\t      \" not terminate:%s\"), expanded_aliases->items[0].string, sb.buf);\n \t\t}\n \n-\t\tstring_list_append(expanded_aliases, args->v[0]);\n-\n \t\ttrace_argv_printf(new_argv,\n \t\t\t\t  \"trace: alias expansion: %s =>\",\n \t\t\t\t  alias_command);\ndiff --git a/t/t0014-alias.sh b/t/t0014-alias.sh\nindex 1ac739a2737..07a53e7366e 100755\n--- a/t/t0014-alias.sh\n+++ b/t/t0014-alias.sh\n@@ -33,7 +33,6 @@ test_expect_success 'looping aliases - deprecated builtins' '\n \tcat >expect <<-EOF &&\n \t${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n \t${SQ}pack-redundant${SQ} is aliased to ${SQ}whatchanged${SQ}\n-\t${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n \tfatal: alias loop detected: expansion of ${SQ}whatchanged${SQ} does not terminate:\n \t  whatchanged <==\n \t  pack-redundant ==>\nRange-diff against v5:\n -:  ----------- >  1:  8b34edf876e Makefile: don’t add whatchanged after it has been removed\n 1:  13682553018 =  2:  c10a24018ce git: add `deprecated` category to --list-cmds\n 2:  7f82ef6e96f !  3:  90e35bfc063 git: move seen-alias bookkeeping into handle_alias(...)\n    @@ Commit message\n         The code in the `while(1)` speculatively adds commands to the list\n         before finding out if it’s an alias.  Let’s instead move it inside\n         `handle_alias(...)`—where it conceptually belongs anyway—and in turn\n    -    only run this logic when we have found an alias.[2][3]\n    +    only run this logic when we have found an alias.[2]\n     \n         [1]: We will do that with an additional call to `handle_alias(1)` inside\n             the loop.  *Not* moving this code leaves a blind spot; we will miss\n             alias looping crafted via deprecated builtin names\n         [2]: Also rename the list to a more descriptive name\n    -    [3]: The error output `'<cmd1>' is aliased to '<cmd2>'` is used for the\n    -        `<cmd2> -h` case.  We will get one more (redundant) line of output\n    -        in the case of alias looping; that is covered in the test “looping\n    -        aliases - deprecated builtins” in the next commit\n     \n         Based-on-patch-by: Jeff King <peff@peff.net>\n         Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n     \n     \n      ## Notes (series) ##\n    +    v6:\n    +\n    +    Apply Peff’s patch[1] in order to get rid of the extra output line.\n    +\n    +    Drop the footnote from the commit message now that we don’t have that\n    +    problem any more.\n    +\n    +    🔗 1: https://lore.kernel.org/git/cover.1757446619.git.code@khaugsbakk.name/T/#mb3ac9c32c8527675a1f9cdf7709f2c74b3dd2a6c\n    +\n         v5 (new):\n     \n         Do a preliminary code movement in order to avoid loop aliasing in the\n    @@ git.c: static int handle_alias(struct strvec *args)\n      \t\tif (!strcmp(alias_command, new_argv[0]))\n      \t\t\tdie(_(\"recursive alias: %s\"), alias_command);\n      \n    -+\t\tseen = unsorted_string_list_lookup(expanded_aliases, args->v[0]);\n    ++\t\tstring_list_append(expanded_aliases, alias_command);\n    ++\t\tseen = unsorted_string_list_lookup(expanded_aliases,\n    ++\t\t\t\t\t\t   new_argv[0]);\n    ++\n     +\t\tif (seen) {\n     +\t\t\tstruct strbuf sb = STRBUF_INIT;\n     +\t\t\tfor (size_t i = 0; i < expanded_aliases->nr; i++) {\n    @@ git.c: static int handle_alias(struct strvec *args)\n     +\t\t\tdie(_(\"alias loop detected: expansion of '%s' does\"\n     +\t\t\t      \" not terminate:%s\"), expanded_aliases->items[0].string, sb.buf);\n     +\t\t}\n    -+\n    -+\t\tstring_list_append(expanded_aliases, args->v[0]);\n     +\n      \t\ttrace_argv_printf(new_argv,\n      \t\t\t\t  \"trace: alias expansion: %s =>\",\n 3:  4bfe36cccd0 !  4:  2cac74d731c git: allow alias-shadowing deprecated builtins\n    @@ Commit message\n         the command is removed for good.\n     \n         Let’s lift this limitation by allowing *deprecated* builtins to be\n    -    shadowed by aliases.[4]\n    +    shadowed by aliases.\n     \n         The only observed demand for aliasing has been for git-whatchanged(1),\n         not for git-pack-redundant(1).  But let’s be consistent and treat all\n    @@ Commit message\n              have empty commits (no changes)[2]\n         [2]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n         [3]: https://lore.kernel.org/git/BL3P221MB0449288C8B0FA448A227FD48833AA@BL3P221MB0449.NAMP221.PROD.OUTLOOK.COM/\n    -    [4]: The test “looping aliases - deprecated builtins” covers the\n    -        case of the extra output mentioned in footnote number 3 of the\n    -        previous commit\n     \n         Based-on-patch-by: Jeff King <peff@peff.net>\n         Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n     \n     \n      ## Notes (series) ##\n    +    v6:\n    +\n    +    Drop footnote now that we don’t have the “extra output line” problem.\n    +\n         v5:\n     \n         Fix a leak caused by calling `alias_lookup(...)` without freeing the\n    @@ t/t0014-alias.sh: test_expect_success 'looping aliases - internal execution' '\n     +\tcat >expect <<-EOF &&\n     +\t${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n     +\t${SQ}pack-redundant${SQ} is aliased to ${SQ}whatchanged${SQ}\n    -+\t${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n     +\tfatal: alias loop detected: expansion of ${SQ}whatchanged${SQ} does not terminate:\n     +\t  whatchanged <==\n     +\t  pack-redundant ==>\n 4:  a04c6ae968e =  5:  3576439add5 t0014: test shadowing of aliases for a sample of builtins\n 5:  2f78ab2e28c =  6:  7499890ed49 you-still-use-that??: help the user help themselves\n 6:  34443066645 =  7:  b0831e8967a whatchanged: hint about git-log(1) and aliasing\n 7:  b0aabd793f0 =  8:  2f281944fbf whatchanged: remove not-even-shorter clause\n 8:  5021647c245 =  9:  7dc3d03d626 BreakingChanges: remove claim about whatchanged reports\n\nbase-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0\n-- \n2.51.0.274.gdcb64e51a0f\n\n"},{"id":"526620","messageId":"8b34e.1758139856.short.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1758139856.short.code@khaugsbakk.name","subject":"[PATCH v6 1/9] Makefile: don’t add whatchanged after it has been removed","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-17T20:24:11Z","receivedAt":"2025-09-17T20:25:37Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\n07572f220a8 (whatchanged: remove when built with WITH_BREAKING_CHANGES,\n2025-05-12) set up the removal of git-whatchanged(1) when\n`WITH_BREAKING_CHANGES` is active.  Part of that work was removing it\nfrom `commands` in `git.c`.  But the Makefile still lists it as a\nbuiltin.  That leaves it in the limbo of being linked but not being\ncallable; you get the generic error about not being able to call it as\na *builtin*:\n\n    $ git whatchanged\n    fatal: cannot handle whatchanged as a builtin\n\ninstead of the expected:\n\n    $ git whatchanged\n    git: 'whatchanged' is not a git command. See 'git --help'.\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v6 (new):\n    \n    Link/Source: https://lore.kernel.org/git/cover.1757446619.git.code@khaugsbakk.name/T/#mfa023b1c1626df68d25714a5981d8662a66199dc\n\n Makefile | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/Makefile b/Makefile\nindex e11340c1ae7..9311da5bb15 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -883,7 +883,9 @@ BUILT_INS += git-stage$X\n BUILT_INS += git-status$X\n BUILT_INS += git-switch$X\n BUILT_INS += git-version$X\n+ifndef WITH_BREAKING_CHANGES\n BUILT_INS += git-whatchanged$X\n+endif\n \n # what 'all' will build but not install in gitexecdir\n OTHER_PROGRAMS += git$X\n-- \n2.51.0.274.gdcb64e51a0f\n\n"},{"id":"526621","messageId":"c10a2.1758139856.short.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1758139856.short.code@khaugsbakk.name","subject":"[PATCH v6 2/9] git: add `deprecated` category to --list-cmds","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-17T20:24:12Z","receivedAt":"2025-09-17T20:25:56Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nWith 145 builtin commands (according to `git --list-cmds=builtins`),\nusers are probably not keeping on top of which ones (if any) are\ndeprecated.\n\nLet’s expand the experimental `--list-cmds`[1] to allow users and\nprograms to query for this information.  We will also use this in an\nupcoming commit to implement `is_deprecated_command`.\n\n[1]: Using something which is experimental to query for deprecations is\n    perhaps not the most ideal approach, but it is simple to implement\n    and better than having to scan the documentation\n\nAcked-by: Patrick Steinhardt <ps@pks.im>\nHelped-by: Patrick Steinhardt <ps@pks.im>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v5:\n    \n    Add back the `BUG` check from v3 because I think it makes sense to only\n    populate one of the two options.\n    \n    Link: https://lore.kernel.org/git/cover.1757345711.git.code@khaugsbakk.name/T/#m922b852384911511c45afd458051f52b50dce62f\n    \n    v4:\n    \n    Incorporate Patrick’s suggestions about the for-loop refactor and\n    formatting the overlong lines.  Now drop the function doc since it\n    doesn’t apply anymore.\n    \n    Also adjust the commit message now that we use it in the C source in an\n    upcoming commit, not just/only as an assert-helper (in an upcoming\n    commit).\n    \n    v3 (new):\n    \n    This is something I wanted to submit independently until the point about\n    aliasing builtins was brought up.[1]  It will help (in a small way) with\n    the upcoming patch “git: allow alias-shadowing deprecated builtins”.\n    \n    By the way: should `command-list.txt` be updated in some way (I didn’t\n    know what way?)\n    \n    🔗 1: https://lore.kernel.org/git/cover.1756311355.git.code@khaugsbakk.name/T/#mee19f8d39572f9021f9d3000758e87b6c32c967c\n\n Documentation/git.adoc |  3 ++-\n git.c                  | 26 ++++++++++++++++++--------\n 2 files changed, 20 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git.adoc b/Documentation/git.adoc\nindex 743b7b00e4d..a2f0838b168 100644\n--- a/Documentation/git.adoc\n+++ b/Documentation/git.adoc\n@@ -219,7 +219,8 @@ If you just want to run git as if it was started in `<path>` then use\n \tList commands by group. This is an internal/experimental\n \toption and may change or be removed in the future. Supported\n \tgroups are: builtins, parseopt (builtin commands that use\n-\tparse-options), main (all commands in libexec directory),\n+\tparse-options), deprecated (deprecated builtins),\n+\tmain (all commands in libexec directory),\n \tothers (all other commands in `$PATH` that have git- prefix),\n \tlist-<category> (see categories in command-list.txt),\n \tnohelpers (exclude helper commands), alias and config\ndiff --git a/git.c b/git.c\nindex 83eac0aeab7..511efdf2056 100644\n--- a/git.c\n+++ b/git.c\n@@ -28,6 +28,7 @@\n #define NEED_WORK_TREE\t\t(1<<3)\n #define DELAY_PAGER_CONFIG\t(1<<4)\n #define NO_PARSEOPT\t\t(1<<5) /* parse-options is not used */\n+#define DEPRECATED\t\t(1<<6)\n \n struct cmd_struct {\n \tconst char *cmd;\n@@ -51,7 +52,9 @@ const char git_more_info_string[] =\n \n static int use_pager = -1;\n \n-static void list_builtins(struct string_list *list, unsigned int exclude_option);\n+static void list_builtins(struct string_list *list,\n+\t\t\t  unsigned int include_option,\n+\t\t\t  unsigned int exclude_option);\n \n static void exclude_helpers_from_list(struct string_list *list)\n {\n@@ -88,7 +91,7 @@ static int list_cmds(const char *spec)\n \t\tint len = sep - spec;\n \n \t\tif (match_token(spec, len, \"builtins\"))\n-\t\t\tlist_builtins(&list, 0);\n+\t\t\tlist_builtins(&list, 0, 0);\n \t\telse if (match_token(spec, len, \"main\"))\n \t\t\tlist_all_main_cmds(&list);\n \t\telse if (match_token(spec, len, \"others\"))\n@@ -99,6 +102,8 @@ static int list_cmds(const char *spec)\n \t\t\tlist_aliases(&list);\n \t\telse if (match_token(spec, len, \"config\"))\n \t\t\tlist_cmds_by_config(&list);\n+\t\telse if (match_token(spec, len, \"deprecated\"))\n+\t\t\tlist_builtins(&list, DEPRECATED, 0);\n \t\telse if (len > 5 && !strncmp(spec, \"list-\", 5)) {\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n \n@@ -322,7 +327,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\tif (!strcmp(cmd, \"parseopt\")) {\n \t\t\t\tstruct string_list list = STRING_LIST_INIT_DUP;\n \n-\t\t\t\tlist_builtins(&list, NO_PARSEOPT);\n+\t\t\t\tlist_builtins(&list, 0, NO_PARSEOPT);\n \t\t\t\tfor (size_t i = 0; i < list.nr; i++)\n \t\t\t\t\tprintf(\"%s \", list.items[i].string);\n \t\t\t\tstring_list_clear(&list, 0);\n@@ -590,7 +595,7 @@ static struct cmd_struct commands[] = {\n \t{ \"notes\", cmd_notes, RUN_SETUP },\n \t{ \"pack-objects\", cmd_pack_objects, RUN_SETUP },\n #ifndef WITH_BREAKING_CHANGES\n-\t{ \"pack-redundant\", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT },\n+\t{ \"pack-redundant\", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT | DEPRECATED },\n #endif\n \t{ \"pack-refs\", cmd_pack_refs, RUN_SETUP },\n \t{ \"patch-id\", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },\n@@ -647,7 +652,7 @@ static struct cmd_struct commands[] = {\n \t{ \"verify-tag\", cmd_verify_tag, RUN_SETUP },\n \t{ \"version\", cmd_version },\n #ifndef WITH_BREAKING_CHANGES\n-\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP },\n+\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP | DEPRECATED },\n #endif\n \t{ \"worktree\", cmd_worktree, RUN_SETUP },\n \t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n@@ -668,11 +673,16 @@ int is_builtin(const char *s)\n \treturn !!get_builtin(s);\n }\n \n-static void list_builtins(struct string_list *out, unsigned int exclude_option)\n+static void list_builtins(struct string_list *out,\n+\t\t\t  unsigned int include_option,\n+\t\t\t  unsigned int exclude_option)\n {\n+\tif (include_option && exclude_option)\n+\t\tBUG(\"'include_option' and 'exclude_option' are mutually exclusive\");\n \tfor (size_t i = 0; i < ARRAY_SIZE(commands); i++) {\n-\t\tif (exclude_option &&\n-\t\t    (commands[i].option & exclude_option))\n+\t\tif (include_option && !(commands[i].option & include_option))\n+\t\t\tcontinue;\n+\t\tif (exclude_option && (commands[i].option & exclude_option))\n \t\t\tcontinue;\n \t\tstring_list_append(out, commands[i].cmd);\n \t}\n-- \n2.51.0.274.gdcb64e51a0f\n\n"},{"id":"526622","messageId":"90e35.1758139856.short.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1758139856.short.code@khaugsbakk.name","subject":"[PATCH v6 3/9] git: move seen-alias bookkeeping into handle_alias(...)","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-17T20:24:13Z","receivedAt":"2025-09-17T20:26:15Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nWe are about to complicate the command handling by allowing *deprecated*\nbuiltins to be shadowed by aliases.  We need to organize the code in\norder to facilitate that.[1]\n\nThe code in the `while(1)` speculatively adds commands to the list\nbefore finding out if it’s an alias.  Let’s instead move it inside\n`handle_alias(...)`—where it conceptually belongs anyway—and in turn\nonly run this logic when we have found an alias.[2]\n\n[1]: We will do that with an additional call to `handle_alias(1)` inside\n    the loop.  *Not* moving this code leaves a blind spot; we will miss\n    alias looping crafted via deprecated builtin names\n[2]: Also rename the list to a more descriptive name\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v6:\n    \n    Apply Peff’s patch[1] in order to get rid of the extra output line.\n    \n    Drop the footnote from the commit message now that we don’t have that\n    problem any more.\n    \n    🔗 1: https://lore.kernel.org/git/cover.1757446619.git.code@khaugsbakk.name/T/#mb3ac9c32c8527675a1f9cdf7709f2c74b3dd2a6c\n    \n    v5 (new):\n    \n    Do a preliminary code movement in order to avoid loop aliasing in the\n    specific case of alias-shadowing deprecated builtins.\n    \n    Link: https://lore.kernel.org/git/20250911204302.GA1907101@coredump.intra.peff.net/\n    \n    As noted in footnote 3: you get one more line of output before the loop\n    aliasing is detected (and see the next commit):\n    \n        'whatchanged' is aliased to 'pack-redundant'\n        'pack-redundant' is aliased to 'whatchanged'\n        'whatchanged' is aliased to 'pack-redundant'\n        fatal: alias loop detected: expansion of 'whatchanged' does not terminate:\n          whatchanged <==\n          pack-redundant ==>\n    \n    This is an informational message specifically for `<git cmd> -h`.\n    This output can be gotten after the next commit.\n    \n    The output in this case is very incidental:\n    \n    1. You are aliasing deprecated commands in a chain\n    2. You are using `-h`\n    3. You are going to get caught by the alias loop checker\n\n git.c | 48 +++++++++++++++++++++++++-----------------------\n 1 file changed, 25 insertions(+), 23 deletions(-)\n\ndiff --git a/git.c b/git.c\nindex 511efdf2056..ef1e7b205aa 100644\n--- a/git.c\n+++ b/git.c\n@@ -365,7 +365,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \treturn (*argv) - orig_argv;\n }\n \n-static int handle_alias(struct strvec *args)\n+static int handle_alias(struct strvec *args, struct string_list *expanded_aliases)\n {\n \tint envchanged = 0, ret = 0, saved_errno = errno;\n \tint count, option_count;\n@@ -376,6 +376,8 @@ static int handle_alias(struct strvec *args)\n \talias_command = args->v[0];\n \talias_string = alias_lookup(alias_command);\n \tif (alias_string) {\n+\t\tstruct string_list_item *seen;\n+\n \t\tif (args->nr == 2 && !strcmp(args->v[1], \"-h\"))\n \t\t\tfprintf_ln(stderr, _(\"'%s' is aliased to '%s'\"),\n \t\t\t\t   alias_command, alias_string);\n@@ -423,6 +425,25 @@ static int handle_alias(struct strvec *args)\n \t\tif (!strcmp(alias_command, new_argv[0]))\n \t\t\tdie(_(\"recursive alias: %s\"), alias_command);\n \n+\t\tstring_list_append(expanded_aliases, alias_command);\n+\t\tseen = unsorted_string_list_lookup(expanded_aliases,\n+\t\t\t\t\t\t   new_argv[0]);\n+\n+\t\tif (seen) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\t\tfor (size_t i = 0; i < expanded_aliases->nr; i++) {\n+\t\t\t\tstruct string_list_item *item = &expanded_aliases->items[i];\n+\n+\t\t\t\tstrbuf_addf(&sb, \"\\n  %s\", item->string);\n+\t\t\t\tif (item == seen)\n+\t\t\t\t\tstrbuf_addstr(&sb, \" <==\");\n+\t\t\t\telse if (i == expanded_aliases->nr - 1)\n+\t\t\t\t\tstrbuf_addstr(&sb, \" ==>\");\n+\t\t\t}\n+\t\t\tdie(_(\"alias loop detected: expansion of '%s' does\"\n+\t\t\t      \" not terminate:%s\"), expanded_aliases->items[0].string, sb.buf);\n+\t\t}\n+\n \t\ttrace_argv_printf(new_argv,\n \t\t\t\t  \"trace: alias expansion: %s =>\",\n \t\t\t\t  alias_command);\n@@ -806,8 +827,7 @@ static void execv_dashed_external(const char **argv)\n static int run_argv(struct strvec *args)\n {\n \tint done_alias = 0;\n-\tstruct string_list cmd_list = STRING_LIST_INIT_DUP;\n-\tstruct string_list_item *seen;\n+\tstruct string_list expanded_aliases = STRING_LIST_INIT_DUP;\n \n \twhile (1) {\n \t\t/*\n@@ -859,35 +879,17 @@ static int run_argv(struct strvec *args)\n \t\t/* .. then try the external ones */\n \t\texecv_dashed_external(args->v);\n \n-\t\tseen = unsorted_string_list_lookup(&cmd_list, args->v[0]);\n-\t\tif (seen) {\n-\t\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\t\tfor (size_t i = 0; i < cmd_list.nr; i++) {\n-\t\t\t\tstruct string_list_item *item = &cmd_list.items[i];\n-\n-\t\t\t\tstrbuf_addf(&sb, \"\\n  %s\", item->string);\n-\t\t\t\tif (item == seen)\n-\t\t\t\t\tstrbuf_addstr(&sb, \" <==\");\n-\t\t\t\telse if (i == cmd_list.nr - 1)\n-\t\t\t\t\tstrbuf_addstr(&sb, \" ==>\");\n-\t\t\t}\n-\t\t\tdie(_(\"alias loop detected: expansion of '%s' does\"\n-\t\t\t      \" not terminate:%s\"), cmd_list.items[0].string, sb.buf);\n-\t\t}\n-\n-\t\tstring_list_append(&cmd_list, args->v[0]);\n-\n \t\t/*\n \t\t * It could be an alias -- this works around the insanity\n \t\t * of overriding \"git log\" with \"git show\" by having\n \t\t * alias.log = show\n \t\t */\n-\t\tif (!handle_alias(args))\n+\t\tif (!handle_alias(args, &expanded_aliases))\n \t\t\tbreak;\n \t\tdone_alias = 1;\n \t}\n \n-\tstring_list_clear(&cmd_list, 0);\n+\tstring_list_clear(&expanded_aliases, 0);\n \n \treturn done_alias;\n }\n-- \n2.51.0.274.gdcb64e51a0f\n\n"},{"id":"526623","messageId":"2cac7.1758139856.short.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1758139856.short.code@khaugsbakk.name","subject":"[PATCH v6 4/9] git: allow alias-shadowing deprecated builtins","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-17T20:24:14Z","receivedAt":"2025-09-17T20:26:34Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\ngit-whatchanged(1) is deprecated and you need to pass\n`--i-still-use-this` in order to force it to work as before.\nThere are two affected users, or usages:\n\n1. people who use the command in scripts; and\n2. people who are used to using it interactively.\n\nFor (1) the replacement is straightforward.[1]  But people in (2) might\nlike the name or be really used to typing it.[3]\n\nAn obvious first thought is to suggest aliasing `whatchanged` to the\ngit-log(1) equivalent.[1]  But this doesn’t work and is awkward since you\ncannot shadow builtins via aliases.\n\nNow you are left in an uncomfortable limbo; your alias won’t work until\nthe command is removed for good.\n\nLet’s lift this limitation by allowing *deprecated* builtins to be\nshadowed by aliases.\n\nThe only observed demand for aliasing has been for git-whatchanged(1),\nnot for git-pack-redundant(1).  But let’s be consistent and treat all\ndeprecated commands the same.\n\n[1]:\n\n        git log --raw --no-merges\n\n     With a minor caveat: you get different outputs if you happen to\n     have empty commits (no changes)[2]\n[2]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n[3]: https://lore.kernel.org/git/BL3P221MB0449288C8B0FA448A227FD48833AA@BL3P221MB0449.NAMP221.PROD.OUTLOOK.COM/\n\nBased-on-patch-by: Jeff King <peff@peff.net>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v6:\n    \n    Drop footnote now that we don’t have the “extra output line” problem.\n    \n    v5:\n    \n    Fix a leak caused by calling `alias_lookup(...)` without freeing the\n    return value by not calling that function.\n    \n    Add tests for deprecated builtins for infinite alias looping as well as\n    simple chaining.  This is to exercise all code paths that we could think\n    of.\n    \n    Expand on the infinite alias looping test on the whole error\n    output (compared to the one above).  Link this change to the\n    previous patch/commit by mentioning it in a footnote.\n    \n    See the last footnote and the note on the previous commit/patch.  I have\n    opted not to get rid of this redundant line.  But this documents the\n    change and future changes that affect it will trigger this\n    assertion (say if they remove the redundant line).\n    \n    v4:\n    \n    Better `is_deprecated_command` implementation.\n    \n    v3 (new):\n    \n    Prerequisite for telling the user that they can alias `whatchanged` to\n    `git log --raw --no-merged`.\n    \n    Link: https://lore.kernel.org/git/cover.1756311355.git.code@khaugsbakk.name/T/#md434b0968f499263262fb1805d82b788b8349d9a\n    \n    > I think that is good advice, but... it won't do anything until we\n    > actually drop the whatchanged command, since until then we'll refuse to\n    > override the command (even the crippled --i-still-use-this one).\n    >\n    > We'd need something like the patch here:\n    \n    ❦\n    \n    The\n    \n        test_file_not_empty expect\n    \n    is here because the git(1) command could fail.  Make sure that it did\n    indeed output anyhing on stdout.  (Or if a previous redirect to `expect`\n    outputted something it should be completely different to `actual` in any\n    case)\n    \n    I don’t know if this is just a waste.\n\n Documentation/config/alias.adoc |  3 ++-\n git.c                           | 17 ++++++++++++++\n t/t0014-alias.sh                | 40 +++++++++++++++++++++++++++++++++\n 3 files changed, 59 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config/alias.adoc b/Documentation/config/alias.adoc\nindex 2c5db0ad842..3c8fab3a95c 100644\n--- a/Documentation/config/alias.adoc\n+++ b/Documentation/config/alias.adoc\n@@ -3,7 +3,8 @@ alias.*::\n \tafter defining `alias.last = cat-file commit HEAD`, the invocation\n \t`git last` is equivalent to `git cat-file commit HEAD`. To avoid\n \tconfusion and troubles with script usage, aliases that\n-\thide existing Git commands are ignored. Arguments are split by\n+\thide existing Git commands are ignored except for deprecated\n+\tcommands.  Arguments are split by\n \tspaces, the usual shell quoting and escaping are supported.\n \tA quote pair or a backslash can be used to quote them.\n +\ndiff --git a/git.c b/git.c\nindex ef1e7b205aa..8c85da84c30 100644\n--- a/git.c\n+++ b/git.c\n@@ -824,12 +824,29 @@ static void execv_dashed_external(const char **argv)\n \t\texit(128);\n }\n \n+static int is_deprecated_command(const char *cmd)\n+{\n+\tstruct cmd_struct *builtin = get_builtin(cmd);\n+\treturn builtin && (builtin->option & DEPRECATED);\n+}\n+\n static int run_argv(struct strvec *args)\n {\n \tint done_alias = 0;\n \tstruct string_list expanded_aliases = STRING_LIST_INIT_DUP;\n \n \twhile (1) {\n+\t\t/*\n+\t\t * Allow deprecated commands to be overridden by aliases. This\n+\t\t * creates a seamless path forward for people who want to keep\n+\t\t * using the name after it is gone, but want to skip the\n+\t\t * deprecation complaint in the meantime.\n+\t\t */\n+\t\tif (is_deprecated_command(args->v[0]) &&\n+\t\t    handle_alias(args, &expanded_aliases)) {\n+\t\t\tdone_alias = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\t/*\n \t\t * If we tried alias and futzed with our environment,\n \t\t * it no longer is safe to invoke builtins directly in\ndiff --git a/t/t0014-alias.sh b/t/t0014-alias.sh\nindex 854d59ec58c..1b196ed9d6d 100755\n--- a/t/t0014-alias.sh\n+++ b/t/t0014-alias.sh\n@@ -27,6 +27,20 @@ test_expect_success 'looping aliases - internal execution' '\n \ttest_grep \"^fatal: alias loop detected: expansion of\" output\n '\n \n+test_expect_success 'looping aliases - deprecated builtins' '\n+\ttest_config alias.whatchanged pack-redundant &&\n+\ttest_config alias.pack-redundant whatchanged &&\n+\tcat >expect <<-EOF &&\n+\t${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}\n+\t${SQ}pack-redundant${SQ} is aliased to ${SQ}whatchanged${SQ}\n+\tfatal: alias loop detected: expansion of ${SQ}whatchanged${SQ} does not terminate:\n+\t  whatchanged <==\n+\t  pack-redundant ==>\n+\tEOF\n+\ttest_must_fail git whatchanged -h 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n # This test is disabled until external loops are fixed, because would block\n # the test suite for a full minute.\n #\n@@ -55,4 +69,30 @@ test_expect_success 'tracing a shell alias with arguments shows trace of prepare\n \ttest_cmp expect actual\n '\n \n+can_alias_deprecated_builtin () {\n+\tcmd=\"$1\" &&\n+\t# some git(1) commands will fail for `-h` (the case for\n+\t# git-status as of 2025-09-07)\n+\ttest_might_fail git status -h >expect &&\n+\ttest_file_not_empty expect &&\n+\ttest_might_fail git -c alias.\"$cmd\"=status \"$cmd\" -h >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'can alias-shadow deprecated builtins' '\n+\tfor cmd in $(git --list-cmds=deprecated)\n+\tdo\n+\t\tcan_alias_deprecated_builtin \"$cmd\" || return 1\n+\tdone\n+'\n+\n+test_expect_success 'can alias-shadow via two deprecated builtins' '\n+\t# some git(1) commands will fail... (see above)\n+\ttest_might_fail git status -h >expect &&\n+\ttest_file_not_empty expect &&\n+\ttest_might_fail git -c alias.whatchanged=pack-redundant \\\n+\t\t-c alias.pack-redundant=status whatchanged -h >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.51.0.274.gdcb64e51a0f\n\n"},{"id":"526624","messageId":"35764.1758139856.short.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1758139856.short.code@khaugsbakk.name","subject":"[PATCH v6 5/9] t0014: test shadowing of aliases for a sample of builtins","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-17T20:24:15Z","receivedAt":"2025-09-17T20:26:53Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThe previous commit added tests for shadowing deprecated builtins.\nLet’s make the test suite more complete by exercising a sample of\nthe builtins and in turn test the documentation for git-config(1):\n\n    To avoid confusion and troubles with script usage, aliases that hide\n    existing Git commands are ignored except for deprecated commands.\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v3 (new):\n    \n    Only a sample of builtins since the test file takes about 600ms longer\n    if I test all of them.\n\n t/t0014-alias.sh | 17 +++++++++++++++++\n 1 file changed, 17 insertions(+)\n\ndiff --git a/t/t0014-alias.sh b/t/t0014-alias.sh\nindex 1b196ed9d6d..07a53e7366e 100755\n--- a/t/t0014-alias.sh\n+++ b/t/t0014-alias.sh\n@@ -95,4 +95,21 @@ test_expect_success 'can alias-shadow via two deprecated builtins' '\n \ttest_cmp expect actual\n '\n \n+cannot_alias_regular_builtin () {\n+\tcmd=\"$1\" &&\n+\t# some git(1) commands will fail... (see above)\n+\ttest_might_fail git \"$cmd\" -h >expect &&\n+\ttest_file_not_empty expect &&\n+\ttest_might_fail git -c alias.\"$cmd\"=status \"$cmd\" -h >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'cannot alias-shadow a sample of regular builtins' '\n+\tfor cmd in grep check-ref-format interpret-trailers \\\n+\t\tcheckout-index fast-import diagnose rev-list prune\n+\tdo\n+\t\tcannot_alias_regular_builtin \"$cmd\" || return 1\n+\tdone\n+'\n+\n test_done\n-- \n2.51.0.274.gdcb64e51a0f\n\n"},{"id":"526625","messageId":"74998.1758139856.short.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1758139856.short.code@khaugsbakk.name","subject":"[PATCH v6 6/9] you-still-use-that??: help the user help themselves","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-17T20:24:16Z","receivedAt":"2025-09-17T20:27:12Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nGive the user a list of suggestions for what to do when they run a\ndeprecated command.\n\nThe first order of action will be to check the breaking changes\ndocument;[1] this short error message says nothing about why this\ncommand is deprecated, and in any case going into any kind of detail\nmight overwhelm the user.\n\nThen they can find out if this has been discussed on the mailing list.\nThen users who e.g. are using git-whatchanged(1) can learn that this is\narguably a plug-in replacement:\n\n    git log <opts> --raw --no-merges\n\nFinally they are invited to send an email to the mailing list.\n\nAlso drop the “please add” part in favor of just using the “refusing”\ndie-message; these two would have been right after each other in this\nnew version.\n\nAlso drop “Thanks” since it now would require a new paragraph.\n\n[1]: www.git-scm.com has a disclaimer for these internal documents that\n    says that “This information is specific to the Git project”.  That’s\n    misleading in this particular case.  But users are unlikely to get\n    discouraged from reading about why they (or their programs) cannot run a\n    command any more; it clearly concerns them.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v2:\n    \n    I had second thoughts about the bullet point about send-an-email.\n    Change it to the one Eric Sunshine proposed;[1] make sure to spell\n    out that you can send an email conditioned on not finding a suitable\n    replacement.\n    \n    Also change the area to something more pointed.\n    \n    And also use a clear URL to refer to www.git-scm.com.\n    \n    [1]: https://lore.kernel.org/git/CAPig+cQkVP57n_FE6dJ0uxvai-J7usxKFp8gzfEbPY=Ytsd6=Q@mail.gmail.com/\n    \n    • Change send-an-email bullet point\n    • Change the area\n    • Use www.git-scm.com, not simply git-scm\n    \n    v1:\n    \n    An alternative to linking to www.git-scm.com is to move this document to\n    a regular installed man page:\n    \n        gitbreaking-changes(7)\n    \n    What do you think?\n    \n    I would then have to base my topic on the in-flight\n    pw/3.0-commentchar-auto-deprecation, which in turn depends on\n    ps/config-wo-the-repository.\n    \n    Or just wait a bit for these to settle in.\n\n usage.c | 23 ++++++++++++++++++-----\n 1 file changed, 18 insertions(+), 5 deletions(-)\n\ndiff --git a/usage.c b/usage.c\nindex 81913236a4a..35dc57eb07e 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -7,6 +7,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"trace2.h\"\n+#include \"strbuf.h\"\n \n static void vfreportf(FILE *f, const char *prefix, const char *err, va_list params)\n {\n@@ -377,12 +378,24 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n \n NORETURN void you_still_use_that(const char *command_name)\n {\n+\tstruct strbuf percent_encoded = STRBUF_INIT;\n+\tstrbuf_add_percentencode(&percent_encoded,\n+\t\t\t\t command_name,\n+\t\t\t\t STRBUF_ENCODE_SLASH);\n+\n \tfprintf(stderr,\n \t\t_(\"'%s' is nominated for removal.\\n\"\n-\t\t  \"If you still use this command, please add an extra\\n\"\n-\t\t  \"option, '--i-still-use-this', on the command line\\n\"\n-\t\t  \"and let us know you still use it by sending an e-mail\\n\"\n-\t\t  \"to <git@vger.kernel.org>.  Thanks.\\n\"),\n-\t\tcommand_name);\n+\t\t  \"If you still use this command, here's what you can do:\\n\"\n+\t\t  \"\\n\"\n+\t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n+\t\t  \"- check if anyone has discussed this on the mailing\\n\"\n+\t\t  \"  list and if they came up with something that can\\n\"\n+\t\t  \"  help you: https://lore.kernel.org/git/?q=%s\\n\"\n+\t\t  \"- send an email to <git@vger.kernel.org> to let us\\n\"\n+\t\t  \"  know that you still use this command and were unable\\n\"\n+\t\t  \"  to determine a suitable replacement\\n\"\n+\t\t  \"\\n\"),\n+\t\tcommand_name, percent_encoded.buf);\n+\tstrbuf_release(&percent_encoded);\n \tdie(_(\"refusing to run without --i-still-use-this\"));\n }\n-- \n2.51.0.274.gdcb64e51a0f\n\n"},{"id":"526626","messageId":"b0831.1758139856.short.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1758139856.short.code@khaugsbakk.name","subject":"[PATCH v6 7/9] whatchanged: hint about git-log(1) and aliasing","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-17T20:24:17Z","receivedAt":"2025-09-17T20:27:30Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThere have been quite a few `--i-still-use-this` user reports since Git\n2.51.0 was released.[1][2]  And it doesn’t seem like they are reading\nthe man page about the git-log(1) equivalent.\n\nTell them what options to plug into git-log(1), either as a replacement\ncommand or as an alias.[3]  That template produces almost the same\noutput[4] and is arguably a plug-in replacement.  Concretely, add\nan optional `hint` argument so that we can use it right after the\ninitial error line.\n\nAlso mention the same concrete options in the documentation while we’re\nat it.\n\n[1]: E.g.,\n    • https://lore.kernel.org/git/e1a69dea-bcb6-45fc-83d3-9e50d32c410b@5y5.one/\n    • https://lore.kernel.org/git/1011073f-9930-4360-a42f-71eb7421fe3f@chrispalmer.uk/#t\n    • https://lore.kernel.org/git/9fcbfcc4-79f9-421f-b9a4-dc455f7db485@acm.org/#t\n    • https://lore.kernel.org/git/83241BDE-1E0D-489A-9181-C608E9FCC17B@gmail.com/\n[2]: The error message on 2.51.0 does tell them to report it, unconditionally\n[3]: We allow aliasing deprecated builtins now for people who are very\n    used to the command name or just like it a lot\n[4]: You only get different outputs if you happen to have empty\n     commits (no changes)[4]\n[5]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    v5:\n    \n    Mention the aliasing hint in the commit message.\n    \n    v3:\n    \n    Add an alias hint now that that is possible.  Also prefix each hint-line\n    with `hint: `.\n    \n    v2:\n    \n    Review found a whitespace error in the prev. patch version.  I found a\n    broken footnote and wanted to expand on the last footnote.\n    \n    • Fix whitespace error (I should have used `ci/check-whitespace.sh\n      v2.51.0`)\n    • Add missing colon (:) to footnote\n    • Expand on footnote; a sentence is enough to summarize the difference\n\n Documentation/git-whatchanged.adoc |  6 +++++-\n builtin/log.c                      |  8 +++++++-\n builtin/pack-redundant.c           |  2 +-\n git-compat-util.h                  |  2 +-\n usage.c                            | 14 ++++++++++----\n 5 files changed, 24 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-whatchanged.adoc b/Documentation/git-whatchanged.adoc\nindex d21484026fe..e71d2aa2d27 100644\n--- a/Documentation/git-whatchanged.adoc\n+++ b/Documentation/git-whatchanged.adoc\n@@ -24,7 +24,11 @@ Shows commit logs and diff output each commit introduces.\n \n New users are encouraged to use linkgit:git-log[1] instead.  The\n `whatchanged` command is essentially the same as linkgit:git-log[1]\n-but defaults to showing the raw format diff output and skipping merges.\n+but defaults to showing the raw format diff output and skipping merges:\n+\n+----\n+git log --raw --no-merges\n+----\n \n The command is primarily kept for historical reasons; fingers of\n many people who learned Git long before `git log` was invented by\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c2f8bbf8630..1d1e6e9130a 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -543,7 +543,13 @@ int cmd_whatchanged(int argc,\n \tcmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);\n \n \tif (!cfg.i_still_use_this)\n-\t\tyou_still_use_that(\"git whatchanged\");\n+\t\tyou_still_use_that(\"git whatchanged\",\n+\t\t\t\t   _(\"\\n\"\n+\t\t\t\t     \"hint: You can replace 'git whatchanged <opts>' with:\\n\"\n+\t\t\t\t     \"hint:\\tgit log <opts> --raw --no-merges\\n\"\n+\t\t\t\t     \"hint: Or make an alias:\\n\"\n+\t\t\t\t     \"hint:\\tgit config set --global alias.whatchanged 'log --raw --no-merges'\\n\"\n+\t\t\t\t     \"\\n\"));\n \n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\ndiff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c\nindex fe81c293e3a..5d5ae4afa28 100644\n--- a/builtin/pack-redundant.c\n+++ b/builtin/pack-redundant.c\n@@ -626,7 +626,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED, s\n \t}\n \n \tif (!i_still_use_this)\n-\t\tyou_still_use_that(\"git pack-redundant\");\n+\t\tyou_still_use_that(\"git pack-redundant\", NULL);\n \n \tif (load_all_packs)\n \t\tload_all();\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 9408f463e31..398e0fac4fa 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -460,7 +460,7 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));\n \n void show_usage_if_asked(int ac, const char **av, const char *err);\n \n-NORETURN void you_still_use_that(const char *command_name);\n+NORETURN void you_still_use_that(const char *command_name, const char *hint);\n \n #ifndef NO_OPENSSL\n #ifdef APPLE_COMMON_CRYPTO\ndiff --git a/usage.c b/usage.c\nindex 35dc57eb07e..7545a616453 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -376,7 +376,8 @@ void bug_fl(const char *file, int line, const char *fmt, ...)\n \tva_end(ap);\n }\n \n-NORETURN void you_still_use_that(const char *command_name)\n+\n+NORETURN void you_still_use_that(const char *command_name, const char *hint)\n {\n \tstruct strbuf percent_encoded = STRBUF_INIT;\n \tstrbuf_add_percentencode(&percent_encoded,\n@@ -384,8 +385,13 @@ NORETURN void you_still_use_that(const char *command_name)\n \t\t\t\t STRBUF_ENCODE_SLASH);\n \n \tfprintf(stderr,\n-\t\t_(\"'%s' is nominated for removal.\\n\"\n-\t\t  \"If you still use this command, here's what you can do:\\n\"\n+\t\t_(\"'%s' is nominated for removal.\\n\"), command_name);\n+\n+\tif (hint)\n+\t\tfputs(hint, stderr);\n+\n+\tfprintf(stderr,\n+\t\t_(\"If you still use this command, here's what you can do:\\n\"\n \t\t  \"\\n\"\n \t\t  \"- read https://git-scm.com/docs/BreakingChanges.html\\n\"\n \t\t  \"- check if anyone has discussed this on the mailing\\n\"\n@@ -395,7 +401,7 @@ NORETURN void you_still_use_that(const char *command_name)\n \t\t  \"  know that you still use this command and were unable\\n\"\n \t\t  \"  to determine a suitable replacement\\n\"\n \t\t  \"\\n\"),\n-\t\tcommand_name, percent_encoded.buf);\n+\t\tpercent_encoded.buf);\n \tstrbuf_release(&percent_encoded);\n \tdie(_(\"refusing to run without --i-still-use-this\"));\n }\n-- \n2.51.0.274.gdcb64e51a0f\n\n"},{"id":"526627","messageId":"2f281.1758139856.short.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1758139856.short.code@khaugsbakk.name","subject":"[PATCH v6 8/9] whatchanged: remove not-even-shorter clause","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-17T20:24:18Z","receivedAt":"2025-09-17T20:27:49Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThe closest equivalent is `git log --raw --no-merges`.\n\nAlso change to “defaults” (implicit plural).\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n Documentation/git-whatchanged.adoc | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-whatchanged.adoc b/Documentation/git-whatchanged.adoc\nindex e71d2aa2d27..436e219b7d0 100644\n--- a/Documentation/git-whatchanged.adoc\n+++ b/Documentation/git-whatchanged.adoc\n@@ -15,7 +15,7 @@ WARNING\n -------\n `git whatchanged` has been deprecated and is scheduled for removal in\n a future version of Git, as it is merely `git log` with different\n-default; `whatchanged` is not even shorter to type than `log --raw`.\n+defaults.\n \n DESCRIPTION\n -----------\n-- \n2.51.0.274.gdcb64e51a0f\n\n"},{"id":"526628","messageId":"7dc3d.1758139856.short.code@khaugsbakk.name","threadId":"64048","inReplyTo":"cover.1758139856.short.code@khaugsbakk.name","subject":"[PATCH v6 9/9] BreakingChanges: remove claim about whatchanged reports","fromName":"","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-17T20:24:19Z","receivedAt":"2025-09-17T20:28:09Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"From: Kristoffer Haugsbakk <code@khaugsbakk.name>\n\nThis was written in e836757e14b (whatschanged: list it in\nBreakingChanges document, 2025-05-12) which was on the same\ntopic that added the `--i-still-use-this` requirement.[1]\n\nMaybe it was a work-in-progress comment/status.\n\n[1]: jc/you-still-use-whatchanged\n\nSigned-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>\n---\n\nNotes (series):\n    Footnote solely to avoid awkward paragraph wrapping...\n\n Documentation/BreakingChanges.adoc | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc\nindex f8d2eba061c..c4985163c3c 100644\n--- a/Documentation/BreakingChanges.adoc\n+++ b/Documentation/BreakingChanges.adoc\n@@ -235,7 +235,7 @@ These features will be removed.\n   equivalent `git log --raw`.  We have nominated the command for\n   removal, have changed the command to refuse to work unless the\n   `--i-still-use-this` option is given, and asked the users to report\n-  when they do so.  So far there hasn't been a single complaint.\n+  when they do so.\n +\n The command will be removed.\n \n-- \n2.51.0.274.gdcb64e51a0f\n\n"},{"id":"526716","messageId":"20250918183117.GB1184978@coredump.intra.peff.net","threadId":"64048","inReplyTo":"cover.1758139856.short.code@khaugsbakk.name","subject":"Re: [PATCH v6 0/9] you-still-use-that??: improve breaking changes troubleshooting","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-18T18:31:17Z","receivedAt":"2025-09-18T18:31:19Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Sep 17, 2025 at 10:24:10PM +0200, kristofferhaugsbakk@fastmail.com wrote:\n\n> § Changes in v6\n> \n> Use Peff’s patch to get rid of “the extra output”.[1]  Update commit\n> messages to not mention that.\n> \n> Also use another fix from Peff (patch 1); don’t list git-whatchanged(1)\n> as a builtin when `WITH_BREAKING_CHANGES`.\n\nThanks, I looked over the patches that touch the things we've been\ndiscussing, and it all looks good to me.\n\nI am a little troubled that even after your patch 1, the test suite will\nget confused by a stray git-whatchanged binary left over from an earlier\nbuild. That may become an irritation later, when the breaking change\nbecomes the norm. But maybe it won't be too bad. At that point the\nmatching entry in .gitignore should be dropped, so at least \"git clean\"\nwill find it. In the meantime, building with and without the\nbreaking-changes flag can cause confusion, but presumably people aren't\nswapping between them too often? And I don't have a clever idea of how\nto do better, short of having the breaking-changes flag explicitly\ndelete git-whatchanged from the build directory. Which feels a little\ngross.\n\nSo I'd say to call it good for now, and if it comes back to bite us\nlater, it is not harder to address then than it would be now.\n\n-Peff\n"},{"id":"526721","messageId":"xmqqsegj7bqg.fsf@gitster.g","threadId":"64048","inReplyTo":"20250918183117.GB1184978@coredump.intra.peff.net","subject":"Re: [PATCH v6 0/9] you-still-use-that??: improve breaking changes troubleshooting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-18T20:21:43Z","receivedAt":"2025-09-18T20:21:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> I am a little troubled that even after your patch 1, the test suite will\n> get confused by a stray git-whatchanged binary left over from an earlier\n> build. That may become an irritation later, when the breaking change\n> becomes the norm. But maybe it won't be too bad. At that point the\n> matching entry in .gitignore should be dropped, so at least \"git clean\"\n> will find it. In the meantime, building with and without the\n> breaking-changes flag can cause confusion, but presumably people aren't\n> swapping between them too often? And I don't have a clever idea of how\n> to do better, short of having the breaking-changes flag explicitly\n> delete git-whatchanged from the build directory. Which feels a little\n> gross.\n>\n> So I'd say to call it good for now, and if it comes back to bite us\n> later, it is not harder to address then than it would be now.\n\nYup, my \"before pushout\" local builds almost always hit a snag from\nleftover git-whatchanged, until I tweaked the procedure to begin\nwith \"make distclean\" before doing anything else X-<, but this may\nbe good enough for now.\n\nThanks, both of you.\n"},{"id":"526723","messageId":"c1392238-4869-4fcf-9d3d-5c7882167024@app.fastmail.com","threadId":"64048","inReplyTo":"20250918183117.GB1184978@coredump.intra.peff.net","subject":"Re: [PATCH v6 0/9] you-still-use-that??: improve breaking changes troubleshooting","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2025-09-18T20:28:32Z","receivedAt":"2025-09-18T20:28:54Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Thu, Sep 18, 2025, at 20:31, Jeff King wrote:\n> On Wed, Sep 17, 2025 at 10:24:10PM +0200,\n> kristofferhaugsbakk@fastmail.com wrote:\n>\n>> § Changes in v6\n>>\n>> Use Peff’s patch to get rid of “the extra output”.[1]  Update commit\n>> messages to not mention that.\n>>\n>> Also use another fix from Peff (patch 1); don’t list git-whatchanged(1)\n>> as a builtin when `WITH_BREAKING_CHANGES`.\n>\n> Thanks, I looked over the patches that touch the things we've been\n> discussing, and it all looks good to me.\n\nThanks for the review!\n\n> I am a little troubled that even after your patch 1, the test suite will\n> get confused by a stray git-whatchanged binary left over from an earlier\n> build. That may become an irritation later, when the breaking change\n> becomes the norm. But maybe it won't be too bad. At that point the\n> matching entry in .gitignore should be dropped, so at least \"git clean\"\n> will find it.\n\nYeah this seems tricky.  And this in-between space seems difficult to\ndeal with even with all the tools like gitignore and whatnot.\n\n> In the meantime, building with and without the breaking-changes flag\n> can cause confusion, but presumably people aren't swapping between\n> them too often?\n\nI for one (and honestly) only did it now when I was informed that it\nfailed the test.  I’ll keep it confined in its own worktree where I have\nno qualms doing git-clean(1) every run.\n\n> And I don't have a clever idea of how\n> to do better, short of having the breaking-changes flag explicitly\n> delete git-whatchanged from the build directory. Which feels a little\n> gross.\n\nNot that I know anything about build systems but that doesn’t sound like\na bad or unprincipled solution.\n\n> So I'd say to call it good for now, and if it comes back to bite us\n> later, it is not harder to address then than it would be now.\n\n-- \nKristoffer\n"}]}