lstat_cache(): print a warning if doing ping-pong between cache types
[gitweb.git] / builtin-apply.c
index a8f75ed3ed411d8cf7a3ec9dfefef7407c50f447..106be94105dbff4c150deac7854a50c67ecd974d 100644 (file)
@@ -14,6 +14,7 @@
 #include "builtin.h"
 #include "string-list.h"
 #include "dir.h"
+#include "parse-options.h"
 
 /*
  *  --check turns on checking that the working tree matches the
@@ -45,9 +46,11 @@ static int apply_verbosely;
 static int no_add;
 static const char *fake_ancestor;
 static int line_termination = '\n';
-static unsigned long p_context = ULONG_MAX;
-static const char apply_usage[] =
-"git apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|fix|error|error-all>] <patch>...";
+static unsigned int p_context = UINT_MAX;
+static const char * const apply_usage[] = {
+       "git apply [options] [<patch>...]",
+       NULL
+};
 
 static enum ws_error_action {
        nowarn_ws_error,
@@ -61,6 +64,8 @@ static int applied_after_fixing_ws;
 static const char *patch_input_file;
 static const char *root;
 static int root_len;
+static int read_stdin = 1;
+static int options;
 
 static void parse_whitespace_option(const char *option)
 {
@@ -1253,8 +1258,9 @@ static char *inflate_it(const void *data, unsigned long size,
        stream.avail_in = size;
        stream.next_out = out = xmalloc(inflated_size);
        stream.avail_out = inflated_size;
-       inflateInit(&stream);
-       st = inflate(&stream, Z_FINISH);
+       git_inflate_init(&stream);
+       st = git_inflate(&stream, Z_FINISH);
+       git_inflate_end(&stream);
        if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
                free(out);
                return NULL;
@@ -2354,7 +2360,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
                 * In such a case, path "new_name" does not exist as
                 * far as git is concerned.
                 */
-               if (has_symlink_leading_path(strlen(new_name), new_name))
+               if (has_symlink_leading_path(new_name, strlen(new_name)))
                        return 0;
 
                return error("%s: already exists in working directory", new_name);
@@ -2435,7 +2441,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
                return error("%s: %s", old_name, strerror(errno));
        }
 
-       if (!cached)
+       if (!cached && !tpatch)
                st_mode = ce_mode_from_stat(*ce, st->st_mode);
 
        if (patch->is_new < 0)
@@ -2447,7 +2453,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
        if (st_mode != patch->old_mode)
                fprintf(stderr, "warning: %s has type %o, expected %o\n",
                        old_name, st_mode, patch->old_mode);
-       if (!patch->new_mode)
+       if (!patch->new_mode && !patch->is_delete)
                patch->new_mode = st_mode;
        return 0;
 
@@ -3137,151 +3143,160 @@ static int git_apply_config(const char *var, const char *value, void *cb)
        return git_default_config(var, value, cb);
 }
 
+static int option_parse_exclude(const struct option *opt,
+                               const char *arg, int unset)
+{
+       add_name_limit(arg, 1);
+       return 0;
+}
+
+static int option_parse_include(const struct option *opt,
+                               const char *arg, int unset)
+{
+       add_name_limit(arg, 0);
+       has_include = 1;
+       return 0;
+}
+
+static int option_parse_p(const struct option *opt,
+                         const char *arg, int unset)
+{
+       p_value = atoi(arg);
+       p_value_known = 1;
+       return 0;
+}
+
+static int option_parse_z(const struct option *opt,
+                         const char *arg, int unset)
+{
+       if (unset)
+               line_termination = '\n';
+       else
+               line_termination = 0;
+       return 0;
+}
+
+static int option_parse_whitespace(const struct option *opt,
+                                  const char *arg, int unset)
+{
+       const char **whitespace_option = opt->value;
+
+       *whitespace_option = arg;
+       parse_whitespace_option(arg);
+       return 0;
+}
+
+static int option_parse_directory(const struct option *opt,
+                                 const char *arg, int unset)
+{
+       root_len = strlen(arg);
+       if (root_len && arg[root_len - 1] != '/') {
+               char *new_root;
+               root = new_root = xmalloc(root_len + 2);
+               strcpy(new_root, arg);
+               strcpy(new_root + root_len++, "/");
+       } else
+               root = arg;
+       return 0;
+}
 
 int cmd_apply(int argc, const char **argv, const char *unused_prefix)
 {
        int i;
-       int read_stdin = 1;
-       int options = 0;
        int errs = 0;
        int is_not_gitdir;
+       int binary;
+       int force_apply = 0;
 
        const char *whitespace_option = NULL;
 
+       struct option builtin_apply_options[] = {
+               { OPTION_CALLBACK, 0, "exclude", NULL, "path",
+                       "donĀ“t apply changes matching the given path",
+                       0, option_parse_exclude },
+               { OPTION_CALLBACK, 0, "include", NULL, "path",
+                       "apply changes matching the given path",
+                       0, option_parse_include },
+               { OPTION_CALLBACK, 'p', NULL, NULL, "num",
+                       "remove <num> leading slashes from traditional diff paths",
+                       0, option_parse_p },
+               OPT_BOOLEAN(0, "no-add", &no_add,
+                       "ignore additions made by the patch"),
+               OPT_BOOLEAN(0, "stat", &diffstat,
+                       "instead of applying the patch, output diffstat for the input"),
+               OPT_BOOLEAN(0, "allow-binary-replacement", &binary,
+                       "now no-op"),
+               OPT_BOOLEAN(0, "binary", &binary,
+                       "now no-op"),
+               OPT_BOOLEAN(0, "numstat", &numstat,
+                       "shows number of added and deleted lines in decimal notation"),
+               OPT_BOOLEAN(0, "summary", &summary,
+                       "instead of applying the patch, output a summary for the input"),
+               OPT_BOOLEAN(0, "check", &check,
+                       "instead of applying the patch, see if the patch is applicable"),
+               OPT_BOOLEAN(0, "index", &check_index,
+                       "make sure the patch is applicable to the current index"),
+               OPT_BOOLEAN(0, "cached", &cached,
+                       "apply a patch without touching the working tree"),
+               OPT_BOOLEAN(0, "apply", &force_apply,
+                       "also apply the patch (use with --stat/--summary/--check)"),
+               OPT_STRING(0, "build-fake-ancestor", &fake_ancestor, "file",
+                       "build a temporary index based on embedded index information"),
+               { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
+                       "paths are separated with NUL character",
+                       PARSE_OPT_NOARG, option_parse_z },
+               OPT_INTEGER('C', NULL, &p_context,
+                               "ensure at least <n> lines of context match"),
+               { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action",
+                       "detect new or modified lines that have whitespace errors",
+                       0, option_parse_whitespace },
+               OPT_BOOLEAN('R', "reverse", &apply_in_reverse,
+                       "apply the patch in reverse"),
+               OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero,
+                       "don't expect at least one line of context"),
+               OPT_BOOLEAN(0, "reject", &apply_with_reject,
+                       "leave the rejected hunks in corresponding *.rej files"),
+               OPT__VERBOSE(&apply_verbosely),
+               OPT_BIT(0, "inaccurate-eof", &options,
+                       "tolerate incorrectly detected missing new-line at the end of file",
+                       INACCURATE_EOF),
+               OPT_BIT(0, "recount", &options,
+                       "do not trust the line counts in the hunk headers",
+                       RECOUNT),
+               { OPTION_CALLBACK, 0, "directory", NULL, "root",
+                       "prepend <root> to all filenames",
+                       0, option_parse_directory },
+               OPT_END()
+       };
+
        prefix = setup_git_directory_gently(&is_not_gitdir);
        prefix_length = prefix ? strlen(prefix) : 0;
        git_config(git_apply_config, NULL);
        if (apply_default_whitespace)
                parse_whitespace_option(apply_default_whitespace);
 
-       for (i = 1; i < argc; i++) {
+       argc = parse_options(argc, argv, builtin_apply_options,
+                       apply_usage, 0);
+       if (apply_with_reject)
+               apply = apply_verbosely = 1;
+       if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
+               apply = 0;
+       if (check_index && is_not_gitdir)
+               die("--index outside a repository");
+       if (cached) {
+               if (is_not_gitdir)
+                       die("--cached outside a repository");
+               check_index = 1;
+       }
+       for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
-               char *end;
                int fd;
 
                if (!strcmp(arg, "-")) {
                        errs |= apply_patch(0, "<stdin>", options);
                        read_stdin = 0;
                        continue;
-               }
-               if (!prefixcmp(arg, "--exclude=")) {
-                       add_name_limit(arg + 10, 1);
-                       continue;
-               }
-               if (!prefixcmp(arg, "--include=")) {
-                       add_name_limit(arg + 10, 0);
-                       has_include = 1;
-                       continue;
-               }
-               if (!prefixcmp(arg, "-p")) {
-                       p_value = atoi(arg + 2);
-                       p_value_known = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--no-add")) {
-                       no_add = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--stat")) {
-                       apply = 0;
-                       diffstat = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--allow-binary-replacement") ||
-                   !strcmp(arg, "--binary")) {
-                       continue; /* now no-op */
-               }
-               if (!strcmp(arg, "--numstat")) {
-                       apply = 0;
-                       numstat = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--summary")) {
-                       apply = 0;
-                       summary = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--check")) {
-                       apply = 0;
-                       check = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--index")) {
-                       if (is_not_gitdir)
-                               die("--index outside a repository");
-                       check_index = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--cached")) {
-                       if (is_not_gitdir)
-                               die("--cached outside a repository");
-                       check_index = 1;
-                       cached = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--apply")) {
-                       apply = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--build-fake-ancestor")) {
-                       apply = 0;
-                       if (++i >= argc)
-                               die ("need a filename");
-                       fake_ancestor = argv[i];
-                       continue;
-               }
-               if (!strcmp(arg, "-z")) {
-                       line_termination = 0;
-                       continue;
-               }
-               if (!prefixcmp(arg, "-C")) {
-                       p_context = strtoul(arg + 2, &end, 0);
-                       if (*end != '\0')
-                               die("unrecognized context count '%s'", arg + 2);
-                       continue;
-               }
-               if (!prefixcmp(arg, "--whitespace=")) {
-                       whitespace_option = arg + 13;
-                       parse_whitespace_option(arg + 13);
-                       continue;
-               }
-               if (!strcmp(arg, "-R") || !strcmp(arg, "--reverse")) {
-                       apply_in_reverse = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--unidiff-zero")) {
-                       unidiff_zero = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--reject")) {
-                       apply = apply_with_reject = apply_verbosely = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose")) {
-                       apply_verbosely = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--inaccurate-eof")) {
-                       options |= INACCURATE_EOF;
-                       continue;
-               }
-               if (!strcmp(arg, "--recount")) {
-                       options |= RECOUNT;
-                       continue;
-               }
-               if (!prefixcmp(arg, "--directory=")) {
-                       arg += strlen("--directory=");
-                       root_len = strlen(arg);
-                       if (root_len && arg[root_len - 1] != '/') {
-                               char *new_root;
-                               root = new_root = xmalloc(root_len + 2);
-                               strcpy(new_root, arg);
-                               strcpy(new_root + root_len++, "/");
-                       } else
-                               root = arg;
-                       continue;
-               }
-               if (0 < prefix_length)
+               } else if (0 < prefix_length)
                        arg = prefix_filename(prefix, prefix_length, arg);
 
                fd = open(arg, O_RDONLY);