Merge branch 'kh/you-still-use-whatchanged-fix'
The "do you still use it?" message given by a command that is deeply deprecated and allow us to suggest alternatives has been updated. * kh/you-still-use-whatchanged-fix: BreakingChanges: remove claim about whatchanged reports whatchanged: remove not-even-shorter clause whatchanged: hint about git-log(1) and aliasing you-still-use-that??: help the user help themselves t0014: test shadowing of aliases for a sample of builtins git: allow alias-shadowing deprecated builtins git: move seen-alias bookkeeping into handle_alias(...) git: add `deprecated` category to --list-cmds Makefile: don’t add whatchanged after it has been removedmain
commit
7ae9eaf806
|
@ -241,7 +241,7 @@ These features will be removed.
|
|||
equivalent `git log --raw`. We have nominated the command for
|
||||
removal, have changed the command to refuse to work unless the
|
||||
`--i-still-use-this` option is given, and asked the users to report
|
||||
when they do so. So far there hasn't been a single complaint.
|
||||
when they do so.
|
||||
+
|
||||
The command will be removed.
|
||||
|
||||
|
|
|
@ -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.
|
||||
+
|
||||
|
|
|
@ -15,7 +15,7 @@ WARNING
|
|||
-------
|
||||
`git whatchanged` has been deprecated and is scheduled for removal in
|
||||
a future version of Git, as it is merely `git log` with different
|
||||
default; `whatchanged` is not even shorter to type than `log --raw`.
|
||||
defaults.
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
@ -24,7 +24,11 @@ Shows commit logs and diff output each commit introduces.
|
|||
|
||||
New users are encouraged to use linkgit:git-log[1] instead. The
|
||||
`whatchanged` command is essentially the same as linkgit:git-log[1]
|
||||
but defaults to showing the raw format diff output and skipping merges.
|
||||
but defaults to showing the raw format diff output and skipping merges:
|
||||
|
||||
----
|
||||
git log --raw --no-merges
|
||||
----
|
||||
|
||||
The command is primarily kept for historical reasons; fingers of
|
||||
many people who learned Git long before `git log` was invented by
|
||||
|
|
|
@ -219,7 +219,8 @@ If you just want to run git as if it was started in `<path>` then use
|
|||
List commands by group. This is an internal/experimental
|
||||
option and may change or be removed in the future. Supported
|
||||
groups are: builtins, parseopt (builtin commands that use
|
||||
parse-options), main (all commands in libexec directory),
|
||||
parse-options), deprecated (deprecated builtins),
|
||||
main (all commands in libexec directory),
|
||||
others (all other commands in `$PATH` that have git- prefix),
|
||||
list-<category> (see categories in command-list.txt),
|
||||
nohelpers (exclude helper commands), alias and config
|
||||
|
|
2
Makefile
2
Makefile
|
@ -883,7 +883,9 @@ BUILT_INS += git-stage$X
|
|||
BUILT_INS += git-status$X
|
||||
BUILT_INS += git-switch$X
|
||||
BUILT_INS += git-version$X
|
||||
ifndef WITH_BREAKING_CHANGES
|
||||
BUILT_INS += git-whatchanged$X
|
||||
endif
|
||||
|
||||
# what 'all' will build but not install in gitexecdir
|
||||
OTHER_PROGRAMS += git$X
|
||||
|
|
|
@ -543,7 +543,13 @@ int cmd_whatchanged(int argc,
|
|||
cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);
|
||||
|
||||
if (!cfg.i_still_use_this)
|
||||
you_still_use_that("git whatchanged");
|
||||
you_still_use_that("git whatchanged",
|
||||
_("\n"
|
||||
"hint: You can replace 'git whatchanged <opts>' with:\n"
|
||||
"hint:\tgit log <opts> --raw --no-merges\n"
|
||||
"hint: Or make an alias:\n"
|
||||
"hint:\tgit config set --global alias.whatchanged 'log --raw --no-merges'\n"
|
||||
"\n"));
|
||||
|
||||
if (!rev.diffopt.output_format)
|
||||
rev.diffopt.output_format = DIFF_FORMAT_RAW;
|
||||
|
|
|
@ -626,7 +626,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED, s
|
|||
}
|
||||
|
||||
if (!i_still_use_this)
|
||||
you_still_use_that("git pack-redundant");
|
||||
you_still_use_that("git pack-redundant", NULL);
|
||||
|
||||
if (load_all_packs)
|
||||
load_all();
|
||||
|
|
|
@ -460,7 +460,7 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
|
|||
|
||||
void show_usage_if_asked(int ac, const char **av, const char *err);
|
||||
|
||||
NORETURN void you_still_use_that(const char *command_name);
|
||||
NORETURN void you_still_use_that(const char *command_name, const char *hint);
|
||||
|
||||
#ifndef NO_OPENSSL
|
||||
#ifdef APPLE_COMMON_CRYPTO
|
||||
|
|
91
git.c
91
git.c
|
@ -28,6 +28,7 @@
|
|||
#define NEED_WORK_TREE (1<<3)
|
||||
#define DELAY_PAGER_CONFIG (1<<4)
|
||||
#define NO_PARSEOPT (1<<5) /* parse-options is not used */
|
||||
#define DEPRECATED (1<<6)
|
||||
|
||||
struct cmd_struct {
|
||||
const char *cmd;
|
||||
|
@ -51,7 +52,9 @@ const char git_more_info_string[] =
|
|||
|
||||
static int use_pager = -1;
|
||||
|
||||
static void list_builtins(struct string_list *list, unsigned int exclude_option);
|
||||
static void list_builtins(struct string_list *list,
|
||||
unsigned int include_option,
|
||||
unsigned int exclude_option);
|
||||
|
||||
static void exclude_helpers_from_list(struct string_list *list)
|
||||
{
|
||||
|
@ -88,7 +91,7 @@ static int list_cmds(const char *spec)
|
|||
int len = sep - spec;
|
||||
|
||||
if (match_token(spec, len, "builtins"))
|
||||
list_builtins(&list, 0);
|
||||
list_builtins(&list, 0, 0);
|
||||
else if (match_token(spec, len, "main"))
|
||||
list_all_main_cmds(&list);
|
||||
else if (match_token(spec, len, "others"))
|
||||
|
@ -99,6 +102,8 @@ static int list_cmds(const char *spec)
|
|||
list_aliases(&list);
|
||||
else if (match_token(spec, len, "config"))
|
||||
list_cmds_by_config(&list);
|
||||
else if (match_token(spec, len, "deprecated"))
|
||||
list_builtins(&list, DEPRECATED, 0);
|
||||
else if (len > 5 && !strncmp(spec, "list-", 5)) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
|
@ -322,7 +327,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
|
|||
if (!strcmp(cmd, "parseopt")) {
|
||||
struct string_list list = STRING_LIST_INIT_DUP;
|
||||
|
||||
list_builtins(&list, NO_PARSEOPT);
|
||||
list_builtins(&list, 0, NO_PARSEOPT);
|
||||
for (size_t i = 0; i < list.nr; i++)
|
||||
printf("%s ", list.items[i].string);
|
||||
string_list_clear(&list, 0);
|
||||
|
@ -360,7 +365,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
|
|||
return (*argv) - orig_argv;
|
||||
}
|
||||
|
||||
static int handle_alias(struct strvec *args)
|
||||
static int handle_alias(struct strvec *args, struct string_list *expanded_aliases)
|
||||
{
|
||||
int envchanged = 0, ret = 0, saved_errno = errno;
|
||||
int count, option_count;
|
||||
|
@ -371,6 +376,8 @@ static int handle_alias(struct strvec *args)
|
|||
alias_command = args->v[0];
|
||||
alias_string = alias_lookup(alias_command);
|
||||
if (alias_string) {
|
||||
struct string_list_item *seen;
|
||||
|
||||
if (args->nr == 2 && !strcmp(args->v[1], "-h"))
|
||||
fprintf_ln(stderr, _("'%s' is aliased to '%s'"),
|
||||
alias_command, alias_string);
|
||||
|
@ -418,6 +425,25 @@ static int handle_alias(struct strvec *args)
|
|||
if (!strcmp(alias_command, new_argv[0]))
|
||||
die(_("recursive alias: %s"), alias_command);
|
||||
|
||||
string_list_append(expanded_aliases, alias_command);
|
||||
seen = unsorted_string_list_lookup(expanded_aliases,
|
||||
new_argv[0]);
|
||||
|
||||
if (seen) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
for (size_t i = 0; i < expanded_aliases->nr; i++) {
|
||||
struct string_list_item *item = &expanded_aliases->items[i];
|
||||
|
||||
strbuf_addf(&sb, "\n %s", item->string);
|
||||
if (item == seen)
|
||||
strbuf_addstr(&sb, " <==");
|
||||
else if (i == expanded_aliases->nr - 1)
|
||||
strbuf_addstr(&sb, " ==>");
|
||||
}
|
||||
die(_("alias loop detected: expansion of '%s' does"
|
||||
" not terminate:%s"), expanded_aliases->items[0].string, sb.buf);
|
||||
}
|
||||
|
||||
trace_argv_printf(new_argv,
|
||||
"trace: alias expansion: %s =>",
|
||||
alias_command);
|
||||
|
@ -591,7 +617,7 @@ static struct cmd_struct commands[] = {
|
|||
{ "notes", cmd_notes, RUN_SETUP },
|
||||
{ "pack-objects", cmd_pack_objects, RUN_SETUP },
|
||||
#ifndef WITH_BREAKING_CHANGES
|
||||
{ "pack-redundant", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT },
|
||||
{ "pack-redundant", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT | DEPRECATED },
|
||||
#endif
|
||||
{ "pack-refs", cmd_pack_refs, RUN_SETUP },
|
||||
{ "patch-id", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },
|
||||
|
@ -649,7 +675,7 @@ static struct cmd_struct commands[] = {
|
|||
{ "verify-tag", cmd_verify_tag, RUN_SETUP },
|
||||
{ "version", cmd_version },
|
||||
#ifndef WITH_BREAKING_CHANGES
|
||||
{ "whatchanged", cmd_whatchanged, RUN_SETUP },
|
||||
{ "whatchanged", cmd_whatchanged, RUN_SETUP | DEPRECATED },
|
||||
#endif
|
||||
{ "worktree", cmd_worktree, RUN_SETUP },
|
||||
{ "write-tree", cmd_write_tree, RUN_SETUP },
|
||||
|
@ -670,11 +696,16 @@ int is_builtin(const char *s)
|
|||
return !!get_builtin(s);
|
||||
}
|
||||
|
||||
static void list_builtins(struct string_list *out, unsigned int exclude_option)
|
||||
static void list_builtins(struct string_list *out,
|
||||
unsigned int include_option,
|
||||
unsigned int exclude_option)
|
||||
{
|
||||
if (include_option && exclude_option)
|
||||
BUG("'include_option' and 'exclude_option' are mutually exclusive");
|
||||
for (size_t i = 0; i < ARRAY_SIZE(commands); i++) {
|
||||
if (exclude_option &&
|
||||
(commands[i].option & exclude_option))
|
||||
if (include_option && !(commands[i].option & include_option))
|
||||
continue;
|
||||
if (exclude_option && (commands[i].option & exclude_option))
|
||||
continue;
|
||||
string_list_append(out, commands[i].cmd);
|
||||
}
|
||||
|
@ -795,13 +826,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 cmd_list = STRING_LIST_INIT_DUP;
|
||||
struct string_list_item *seen;
|
||||
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
|
||||
|
@ -851,35 +898,17 @@ static int run_argv(struct strvec *args)
|
|||
/* .. then try the external ones */
|
||||
execv_dashed_external(args->v);
|
||||
|
||||
seen = unsorted_string_list_lookup(&cmd_list, args->v[0]);
|
||||
if (seen) {
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
for (size_t i = 0; i < cmd_list.nr; i++) {
|
||||
struct string_list_item *item = &cmd_list.items[i];
|
||||
|
||||
strbuf_addf(&sb, "\n %s", item->string);
|
||||
if (item == seen)
|
||||
strbuf_addstr(&sb, " <==");
|
||||
else if (i == cmd_list.nr - 1)
|
||||
strbuf_addstr(&sb, " ==>");
|
||||
}
|
||||
die(_("alias loop detected: expansion of '%s' does"
|
||||
" not terminate:%s"), cmd_list.items[0].string, sb.buf);
|
||||
}
|
||||
|
||||
string_list_append(&cmd_list, args->v[0]);
|
||||
|
||||
/*
|
||||
* It could be an alias -- this works around the insanity
|
||||
* of overriding "git log" with "git show" by having
|
||||
* alias.log = show
|
||||
*/
|
||||
if (!handle_alias(args))
|
||||
if (!handle_alias(args, &expanded_aliases))
|
||||
break;
|
||||
done_alias = 1;
|
||||
}
|
||||
|
||||
string_list_clear(&cmd_list, 0);
|
||||
string_list_clear(&expanded_aliases, 0);
|
||||
|
||||
return done_alias;
|
||||
}
|
||||
|
|
|
@ -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,47 @@ 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
|
||||
'
|
||||
|
||||
cannot_alias_regular_builtin () {
|
||||
cmd="$1" &&
|
||||
# some git(1) commands will fail... (see above)
|
||||
test_might_fail git "$cmd" -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 'cannot alias-shadow a sample of regular builtins' '
|
||||
for cmd in grep check-ref-format interpret-trailers \
|
||||
checkout-index fast-import diagnose rev-list prune
|
||||
do
|
||||
cannot_alias_regular_builtin "$cmd" || return 1
|
||||
done
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
33
usage.c
33
usage.c
|
@ -7,6 +7,7 @@
|
|||
#include "git-compat-util.h"
|
||||
#include "gettext.h"
|
||||
#include "trace2.h"
|
||||
#include "strbuf.h"
|
||||
|
||||
static void vfreportf(FILE *f, const char *prefix, const char *err, va_list params)
|
||||
{
|
||||
|
@ -376,14 +377,32 @@ void bug_fl(const char *file, int line, const char *fmt, ...)
|
|||
va_end(ap);
|
||||
}
|
||||
|
||||
NORETURN void you_still_use_that(const char *command_name)
|
||||
|
||||
NORETURN void you_still_use_that(const char *command_name, const char *hint)
|
||||
{
|
||||
struct strbuf percent_encoded = STRBUF_INIT;
|
||||
strbuf_add_percentencode(&percent_encoded,
|
||||
command_name,
|
||||
STRBUF_ENCODE_SLASH);
|
||||
|
||||
fprintf(stderr,
|
||||
_("'%s' is nominated for removal.\n"
|
||||
"If you still use this command, please add an extra\n"
|
||||
"option, '--i-still-use-this', on the command line\n"
|
||||
"and let us know you still use it by sending an e-mail\n"
|
||||
"to <git@vger.kernel.org>. Thanks.\n"),
|
||||
command_name);
|
||||
_("'%s' is nominated for removal.\n"), command_name);
|
||||
|
||||
if (hint)
|
||||
fputs(hint, stderr);
|
||||
|
||||
fprintf(stderr,
|
||||
_("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=%s\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"),
|
||||
percent_encoded.buf);
|
||||
strbuf_release(&percent_encoded);
|
||||
die(_("refusing to run without --i-still-use-this"));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue