]> git.ipfire.org Git - thirdparty/git.git/commitdiff
git: allow alias-shadowing deprecated builtins
authorKristoffer Haugsbakk <code@khaugsbakk.name>
Wed, 17 Sep 2025 20:24:14 +0000 (22:24 +0200)
committerJunio C Hamano <gitster@pobox.com>
Wed, 17 Sep 2025 20:47:23 +0000 (13:47 -0700)
git-whatchanged(1) is deprecated and you need to pass
`--i-still-use-this` in order to force it to work as before.
There are two affected users, or usages:

1. people who use the command in scripts; and
2. people who are used to using it interactively.

For (1) the replacement is straightforward.[1]  But people in (2) might
like the name or be really used to typing it.[3]

An obvious first thought is to suggest aliasing `whatchanged` to the
git-log(1) equivalent.[1]  But this doesn’t work and is awkward since you
cannot shadow builtins via aliases.

Now you are left in an uncomfortable limbo; your alias won’t work until
the command is removed for good.

Let’s lift this limitation by allowing *deprecated* builtins to be
shadowed by aliases.

The only observed demand for aliasing has been for git-whatchanged(1),
not for git-pack-redundant(1).  But let’s be consistent and treat all
deprecated commands the same.

[1]:

        git log --raw --no-merges

     With a minor caveat: you get different outputs if you happen to
     have empty commits (no changes)[2]
[2]: https://lore.kernel.org/git/20250825085428.GA367101@coredump.intra.peff.net/
[3]: https://lore.kernel.org/git/BL3P221MB0449288C8B0FA448A227FD48833AA@BL3P221MB0449.NAMP221.PROD.OUTLOOK.COM/

Based-on-patch-by: Jeff King <peff@peff.net>
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config/alias.adoc
git.c
t/t0014-alias.sh

index 2c5db0ad8428e8badf42d402e79d045a8128a117..3c8fab3a95c59933092583ffddb1248dfde2c431 100644 (file)
@@ -3,7 +3,8 @@ alias.*::
        after defining `alias.last = cat-file commit HEAD`, the invocation
        `git last` is equivalent to `git cat-file commit HEAD`. To avoid
        confusion and troubles with script usage, aliases that
-       hide existing Git commands are ignored. Arguments are split by
+       hide existing Git commands are ignored except for deprecated
+       commands.  Arguments are split by
        spaces, the usual shell quoting and escaping are supported.
        A quote pair or a backslash can be used to quote them.
 +
diff --git a/git.c b/git.c
index ef1e7b205aa8e8d668947a8b2429a35c44402846..8c85da84c30534e1363d2b01fc01535ed39a86b1 100644 (file)
--- a/git.c
+++ b/git.c
@@ -824,12 +824,29 @@ static void execv_dashed_external(const char **argv)
                exit(128);
 }
 
+static int is_deprecated_command(const char *cmd)
+{
+       struct cmd_struct *builtin = get_builtin(cmd);
+       return builtin && (builtin->option & DEPRECATED);
+}
+
 static int run_argv(struct strvec *args)
 {
        int done_alias = 0;
        struct string_list expanded_aliases = STRING_LIST_INIT_DUP;
 
        while (1) {
+               /*
+                * Allow deprecated commands to be overridden by aliases. This
+                * creates a seamless path forward for people who want to keep
+                * using the name after it is gone, but want to skip the
+                * deprecation complaint in the meantime.
+                */
+               if (is_deprecated_command(args->v[0]) &&
+                   handle_alias(args, &expanded_aliases)) {
+                       done_alias = 1;
+                       continue;
+               }
                /*
                 * If we tried alias and futzed with our environment,
                 * it no longer is safe to invoke builtins directly in
index 854d59ec58c25ab2cfb58113bf5ea8d670829fe0..1b196ed9d6d1c8a0ead2b228eaebdf1c9e313b86 100755 (executable)
@@ -27,6 +27,20 @@ test_expect_success 'looping aliases - internal execution' '
        test_grep "^fatal: alias loop detected: expansion of" output
 '
 
+test_expect_success 'looping aliases - deprecated builtins' '
+       test_config alias.whatchanged pack-redundant &&
+       test_config alias.pack-redundant whatchanged &&
+       cat >expect <<-EOF &&
+       ${SQ}whatchanged${SQ} is aliased to ${SQ}pack-redundant${SQ}
+       ${SQ}pack-redundant${SQ} is aliased to ${SQ}whatchanged${SQ}
+       fatal: alias loop detected: expansion of ${SQ}whatchanged${SQ} does not terminate:
+         whatchanged <==
+         pack-redundant ==>
+       EOF
+       test_must_fail git whatchanged -h 2>actual &&
+       test_cmp expect actual
+'
+
 # This test is disabled until external loops are fixed, because would block
 # the test suite for a full minute.
 #
@@ -55,4 +69,30 @@ test_expect_success 'tracing a shell alias with arguments shows trace of prepare
        test_cmp expect actual
 '
 
+can_alias_deprecated_builtin () {
+       cmd="$1" &&
+       # some git(1) commands will fail for `-h` (the case for
+       # git-status as of 2025-09-07)
+       test_might_fail git status -h >expect &&
+       test_file_not_empty expect &&
+       test_might_fail git -c alias."$cmd"=status "$cmd" -h >actual &&
+       test_cmp expect actual
+}
+
+test_expect_success 'can alias-shadow deprecated builtins' '
+       for cmd in $(git --list-cmds=deprecated)
+       do
+               can_alias_deprecated_builtin "$cmd" || return 1
+       done
+'
+
+test_expect_success 'can alias-shadow via two deprecated builtins' '
+       # some git(1) commands will fail... (see above)
+       test_might_fail git status -h >expect &&
+       test_file_not_empty expect &&
+       test_might_fail git -c alias.whatchanged=pack-redundant \
+               -c alias.pack-redundant=status whatchanged -h >actual &&
+       test_cmp expect actual
+'
+
 test_done