Merge branch 'maint'
[gitweb.git] / builtin / add.c
index 2705f8d057a93f7b4a9351713b89fd9f4e041815..56a4e0af6b35ea9f6d86accad27a1c87bc5ad0d9 100644 (file)
@@ -117,7 +117,19 @@ static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
        }
 }
 
-static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
+static char *find_used_pathspec(const char **pathspec)
+{
+       char *seen;
+       int i;
+
+       for (i = 0; pathspec[i];  i++)
+               ; /* just counting */
+       seen = xcalloc(i, 1);
+       fill_pathspec_matches(pathspec, seen, i);
+       return seen;
+}
+
+static char *prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
 {
        char *seen;
        int i, specs;
@@ -137,13 +149,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
        }
        dir->nr = dst - dir->entries;
        fill_pathspec_matches(pathspec, seen, specs);
-
-       for (i = 0; i < specs; i++) {
-               if (!seen[i] && pathspec[i][0] && !file_exists(pathspec[i]))
-                       die("pathspec '%s' did not match any files",
-                                       pathspec[i]);
-       }
-        free(seen);
+       return seen;
 }
 
 static void treat_gitlinks(const char **pathspec)
@@ -255,12 +261,14 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
 {
        char *file = xstrdup(git_path("ADD_EDIT.patch"));
        const char *apply_argv[] = { "apply", "--recount", "--cached",
-               file, NULL };
+               NULL, NULL };
        struct child_process child;
        struct rev_info rev;
        int out;
        struct stat st;
 
+       apply_argv[3] = file;
+
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
 
        if (read_cache() < 0)
@@ -302,7 +310,7 @@ static const char ignore_error[] =
 "The following paths are ignored by one of your .gitignore files:\n";
 
 static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
-static int ignore_add_errors, addremove, intent_to_add;
+static int ignore_add_errors, addremove, intent_to_add, ignore_missing = 0;
 
 static struct option builtin_add_options[] = {
        OPT__DRY_RUN(&show_only),
@@ -317,6 +325,7 @@ static struct option builtin_add_options[] = {
        OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked 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_END(),
 };
 
@@ -359,6 +368,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        int flags;
        int add_new_files;
        int require_pathspec;
+       char *seen = NULL;
 
        git_config(add_config, NULL);
 
@@ -376,6 +386,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
        if (addremove && take_worktree_changes)
                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) {
                static const char *here[2] = { ".", NULL };
                argc = 1;
@@ -418,7 +430,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                /* This picks up the paths that are not tracked */
                baselen = fill_directory(&dir, pathspec);
                if (pathspec)
-                       prune_directory(&dir, pathspec, baselen);
+                       seen = prune_directory(&dir, pathspec, baselen);
        }
 
        if (refresh_only) {
@@ -426,6 +438,24 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                goto finish;
        }
 
+       if (pathspec) {
+               int i;
+               if (!seen)
+                       seen = find_used_pathspec(pathspec);
+               for (i = 0; pathspec[i]; i++) {
+                       if (!seen[i] && pathspec[i][0]
+                           && !file_exists(pathspec[i])) {
+                               if (ignore_missing) {
+                                       if (excluded(&dir, pathspec[i], DT_UNKNOWN))
+                                               dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
+                               } else
+                                       die("pathspec '%s' did not match any files",
+                                           pathspec[i]);
+                       }
+               }
+               free(seen);
+       }
+
        exit_status |= add_files_to_cache(prefix, pathspec, flags);
 
        if (add_new_files)