add: warn when -u or -A is used without pathspec

Most Git commands that can be used with or without pathspec operate tree-wide by default, the pathspec being used to restrict their scope. A few exceptions are: 'git grep', 'git clean', 'git add -u' and 'git add -A'. When run in a subdirectory without pathspec, they operate only on paths in the current directory. The inconsistency of 'git add -u' and 'git add -A' is particularly problematic since other 'git add' subcommands (namely 'git add -p' and 'git add -e') are tree-wide by default. It also means that "git add -u && git commit" will record a state that is different from what is recorded with "git commit -a". Flipping the default now is unacceptable, so let's start training users to type 'git add -u|-A :/' or 'git add -u|-A .' explicitly, to prepare for the next steps: * forbid 'git add -u|-A' without pathspec (like 'git add' without option) * much later, maybe, re-allow 'git add -u|-A' without pathspec, that will add all tracked and modified files, or all files, tree-wide. A nice side effect of this patch is that it makes the :/ magic pathspec easier to discover for users. When the command is called from the root of the tree, there is no ambiguity and no need to change the behavior, hence no need to warn. Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Matthieu Moy committed Jan 28, 2013 at 10:16 UTC 0fa2eb530fb748774c5b2f309a471cf048b8d9d9
2 files changed +47 -4
Documentation/git-add.txt
+4 -3
@@ -107,9 +107,10 @@ apply to the index. See EDITING PATCHES below.
107 from the index if the corresponding files in the working tree
108 have been removed.
109 +
110 -If no <filepattern> is given, default to "."; in other words,
111 -update all tracked files in the current directory and its
112 -subdirectories.
110 +If no <filepattern> is given, the current version of Git defaults to
111 +"."; in other words, update all tracked files in the current directory
112 +and its subdirectories. This default will change in a future version
113 +of Git, hence the form without <filepattern> should not be used.
114
115 -A::
116 --all::
builtin/add.c
+43 -1
@@ -363,6 +363,35 @@ static int add_files(struct dir_struct *dir, int flags)
363 return exit_status;
364 }
365
366 +static void warn_pathless_add(const char *option_name, const char *short_name) {
367 + /*
368 + * To be consistent with "git add -p" and most Git
369 + * commands, we should default to being tree-wide, but
370 + * this is not the original behavior and can't be
371 + * changed until users trained themselves not to type
372 + * "git add -u" or "git add -A". For now, we warn and
373 + * keep the old behavior. Later, this warning can be
374 + * turned into a die(...), and eventually we may
375 + * reallow the command with a new behavior.
376 + */
377 + warning(_("The behavior of 'git add %s (or %s)' with no path argument from a\n"
378 + "subdirectory of the tree will change in Git 2.0 and should not be used anymore.\n"
379 + "To add content for the whole tree, run:\n"
380 + "\n"
381 + " git add %s :/\n"
382 + " (or git add %s :/)\n"
383 + "\n"
384 + "To restrict the command to the current directory, run:\n"
385 + "\n"
386 + " git add %s .\n"
387 + " (or git add %s .)\n"
388 + "\n"
389 + "With the current Git version, the command is restricted to the current directory."),
390 + option_name, short_name,
391 + option_name, short_name,
392 + option_name, short_name);
393 +}
394 +
395 int cmd_add(int argc, const char **argv, const char *prefix)
396 {
397 int exit_status = 0;
@@ -373,6 +402,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
402 int add_new_files;
403 int require_pathspec;
404 char *seen = NULL;
405 + const char *option_with_implicit_dot = NULL;
406 + const char *short_option_with_implicit_dot = NULL;
407
408 git_config(add_config, NULL);
409
@@ -392,8 +423,19 @@ int cmd_add(int argc, const char **argv, const char *prefix)
423 die(_("-A and -u are mutually incompatible"));
424 if (!show_only && ignore_missing)
425 die(_("Option --ignore-missing can only be used together with --dry-run"));
395 - if ((addremove || take_worktree_changes) && !argc) {
426 + if (addremove) {
427 + option_with_implicit_dot = "--all";
428 + short_option_with_implicit_dot = "-A";
429 + }
430 + if (take_worktree_changes) {
431 + option_with_implicit_dot = "--update";
432 + short_option_with_implicit_dot = "-u";
433 + }
434 + if (option_with_implicit_dot && !argc) {
435 static const char *here[2] = { ".", NULL };
436 + if (prefix)
437 + warn_pathless_add(option_with_implicit_dot,
438 + short_option_with_implicit_dot);
439 argc = 1;
440 argv = here;
441 }