Merge branch 'ks/rfc2047-one-char-at-a-time'
[gitweb.git] / builtin / add.c
index 8c3fdf9435bebac125005881f0a01660695dbb74..ab1c9e8fb7a0f7c6f60cc78ff0f345838210459a 100644 (file)
@@ -17,7 +17,7 @@
 #include "bulk-checkin.h"
 
 static const char * const builtin_add_usage[] = {
-       "git add [options] [--] <filepattern>...",
+       N_("git add [options] [--] <pathspec>..."),
        NULL
 };
 static int patch_interactive, add_interactive, edit_interactive;
@@ -121,6 +121,10 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, int
        return seen;
 }
 
+/*
+ * Checks the index to see whether any path in pathspec refers to
+ * something inside a submodule.  If so, dies with an error message.
+ */
 static void treat_gitlinks(const char **pathspec)
 {
        int i;
@@ -128,24 +132,8 @@ static void treat_gitlinks(const char **pathspec)
        if (!pathspec || !*pathspec)
                return;
 
-       for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
-               if (S_ISGITLINK(ce->ce_mode)) {
-                       int len = ce_namelen(ce), j;
-                       for (j = 0; pathspec[j]; j++) {
-                               int len2 = strlen(pathspec[j]);
-                               if (len2 <= len || pathspec[j][len] != '/' ||
-                                   memcmp(ce->name, pathspec[j], len))
-                                       continue;
-                               if (len2 == len + 1)
-                                       /* strip trailing slash */
-                                       pathspec[j] = xstrndup(ce->name, len);
-                               else
-                                       die (_("Path '%s' is in submodule '%.*s'"),
-                                               pathspec[j], len, ce->name);
-                       }
-               }
-       }
+       for (i = 0; pathspec[i]; i++)
+               pathspec[i] = check_path_for_gitlink(pathspec[i]);
 }
 
 static void refresh(int verbose, const char **pathspec)
@@ -165,6 +153,11 @@ static void refresh(int verbose, const char **pathspec)
         free(seen);
 }
 
+/*
+ * Normalizes argv relative to prefix, via get_pathspec(), and then
+ * runs die_if_path_beyond_symlink() on each path in the normalized
+ * list.
+ */
 static const char **validate_pathspec(const char **argv, const char *prefix)
 {
        const char **pathspec = get_pathspec(prefix, argv);
@@ -172,10 +165,7 @@ static const char **validate_pathspec(const char **argv, const char *prefix)
        if (pathspec) {
                const char **p;
                for (p = pathspec; *p; p++) {
-                       if (has_symlink_leading_path(*p, strlen(*p))) {
-                               int len = prefix ? strlen(prefix) : 0;
-                               die(_("'%s' is beyond a symbolic link"), *p + len);
-                       }
+                       die_if_path_beyond_symlink(*p, prefix);
                }
        }
 
@@ -228,7 +218,7 @@ int interactive_add(int argc, const char **argv, const char *prefix, int patch)
 
 static int edit_patch(int argc, const char **argv, const char *prefix)
 {
-       char *file = xstrdup(git_path("ADD_EDIT.patch"));
+       char *file = git_pathdup("ADD_EDIT.patch");
        const char *apply_argv[] = { "apply", "--recount", "--cached",
                NULL, NULL };
        struct child_process child;
@@ -271,6 +261,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
                die (_("Could not apply '%s'"), file);
 
        unlink(file);
+       free(file);
        return 0;
 }
 
@@ -283,19 +274,19 @@ static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
 static int ignore_add_errors, addremove, intent_to_add, ignore_missing = 0;
 
 static struct option builtin_add_options[] = {
-       OPT__DRY_RUN(&show_only, "dry run"),
-       OPT__VERBOSE(&verbose, "be verbose"),
+       OPT__DRY_RUN(&show_only, N_("dry run")),
+       OPT__VERBOSE(&verbose, N_("be verbose")),
        OPT_GROUP(""),
-       OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
-       OPT_BOOLEAN('p', "patch", &patch_interactive, "select hunks interactively"),
-       OPT_BOOLEAN('e', "edit", &edit_interactive, "edit current diff and apply"),
-       OPT__FORCE(&ignored_too, "allow adding otherwise ignored files"),
-       OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
-       OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
-       OPT_BOOLEAN('A', "all", &addremove, "add changes from all tracked and untracked files"),
-       OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
-       OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
-       OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, "check if - even missing - files are ignored in dry run"),
+       OPT_BOOLEAN('i', "interactive", &add_interactive, N_("interactive picking")),
+       OPT_BOOLEAN('p', "patch", &patch_interactive, N_("select hunks interactively")),
+       OPT_BOOLEAN('e', "edit", &edit_interactive, N_("edit current diff and apply")),
+       OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")),
+       OPT_BOOLEAN('u', "update", &take_worktree_changes, N_("update tracked files")),
+       OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
+       OPT_BOOLEAN('A', "all", &addremove, N_("add changes from all tracked and untracked files")),
+       OPT_BOOLEAN( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
+       OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
+       OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
        OPT_END(),
 };
 
@@ -330,6 +321,35 @@ static int add_files(struct dir_struct *dir, int flags)
        return exit_status;
 }
 
+static void warn_pathless_add(const char *option_name, const char *short_name) {
+       /*
+        * To be consistent with "git add -p" and most Git
+        * commands, we should default to being tree-wide, but
+        * this is not the original behavior and can't be
+        * changed until users trained themselves not to type
+        * "git add -u" or "git add -A". For now, we warn and
+        * keep the old behavior. Later, the behavior can be changed
+        * to tree-wide, keeping the warning for a while, and
+        * eventually we can drop the warning.
+        */
+       warning(_("The behavior of 'git add %s (or %s)' with no path argument from a\n"
+                 "subdirectory of the tree will change in Git 2.0 and should not be used anymore.\n"
+                 "To add content for the whole tree, run:\n"
+                 "\n"
+                 "  git add %s :/\n"
+                 "  (or git add %s :/)\n"
+                 "\n"
+                 "To restrict the command to the current directory, run:\n"
+                 "\n"
+                 "  git add %s .\n"
+                 "  (or git add %s .)\n"
+                 "\n"
+                 "With the current Git version, the command is restricted to the current directory."),
+               option_name, short_name,
+               option_name, short_name,
+               option_name, short_name);
+}
+
 int cmd_add(int argc, const char **argv, const char *prefix)
 {
        int exit_status = 0;
@@ -340,6 +360,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        int add_new_files;
        int require_pathspec;
        char *seen = NULL;
+       const char *option_with_implicit_dot = NULL;
+       const char *short_option_with_implicit_dot = NULL;
 
        git_config(add_config, NULL);
 
@@ -359,8 +381,19 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                die(_("-A and -u are mutually incompatible"));
        if (!show_only && ignore_missing)
                die(_("Option --ignore-missing can only be used together with --dry-run"));
-       if ((addremove || take_worktree_changes) && !argc) {
+       if (addremove) {
+               option_with_implicit_dot = "--all";
+               short_option_with_implicit_dot = "-A";
+       }
+       if (take_worktree_changes) {
+               option_with_implicit_dot = "--update";
+               short_option_with_implicit_dot = "-u";
+       }
+       if (option_with_implicit_dot && !argc) {
                static const char *here[2] = { ".", NULL };
+               if (prefix)
+                       warn_pathless_add(option_with_implicit_dot,
+                                         short_option_with_implicit_dot);
                argc = 1;
                argv = here;
        }