apply --whitespace=warn/error: diagnose blank at EOF
[gitweb.git] / builtin-apply.c
index d13313f1051eb3f7374cf4119923b230fd6f81d8..37d3bc069b1618f81c14cf0c87d31a0f1a3b081c 100644 (file)
@@ -12,7 +12,8 @@
 #include "blob.h"
 #include "delta.h"
 #include "builtin.h"
-#include "path-list.h"
+#include "string-list.h"
+#include "dir.h"
 
 /*
  *  --check turns on checking that the working tree matches the
@@ -46,7 +47,7 @@ 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>...";
+"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 enum ws_error_action {
        nowarn_ws_error,
@@ -125,6 +126,7 @@ struct fragment {
        const char *patch;
        int size;
        int rejected;
+       int linenr;
        struct fragment *next;
 };
 
@@ -194,7 +196,7 @@ struct image {
  * the case where more than one patches touch the same file.
  */
 
-static struct path_list fn_table;
+static struct string_list fn_table;
 
 static uint32_t hash_line(const char *cp, size_t len)
 {
@@ -274,7 +276,7 @@ static void say_patch_name(FILE *output, const char *pre,
 static void read_patch_file(struct strbuf *sb, int fd)
 {
        if (strbuf_read(sb, fd, 0) < 0)
-               die("git-apply: read returned %s", strerror(errno));
+               die("git apply: read returned %s", strerror(errno));
 
        /*
         * Make sure that we have some slop in the buffer
@@ -506,17 +508,17 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name,
                name = orig_name;
                len = strlen(name);
                if (isnull)
-                       die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
+                       die("git apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
                another = find_name(line, NULL, p_value, TERM_TAB);
                if (!another || memcmp(another, name, len))
-                       die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
+                       die("git apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
                free(another);
                return orig_name;
        }
        else {
                /* expect "/dev/null" */
                if (memcmp("/dev/null", line, 9) || line[9] != '\n')
-                       die("git-apply: bad git-diff - expected /dev/null on line %d", linenr);
+                       die("git apply: bad git-diff - expected /dev/null on line %d", linenr);
                return NULL;
        }
 }
@@ -809,6 +811,13 @@ static int parse_git_header(char *line, int len, unsigned int size, struct patch
         * the default name from the header.
         */
        patch->def_name = git_header_name(line, len);
+       if (patch->def_name && root) {
+               char *s = xmalloc(root_len + strlen(patch->def_name) + 1);
+               strcpy(s, root);
+               strcpy(s + root_len, patch->def_name);
+               free(patch->def_name);
+               patch->def_name = s;
+       }
 
        line += len;
        size -= len;
@@ -1047,23 +1056,29 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
        return -1;
 }
 
-static void check_whitespace(const char *line, int len, unsigned ws_rule)
+static void record_ws_error(unsigned result, const char *line, int len, int linenr)
 {
        char *err;
-       unsigned result = ws_check(line + 1, len - 1, ws_rule);
+
        if (!result)
                return;
 
        whitespace_error++;
        if (squelch_whitespace_errors &&
            squelch_whitespace_errors < whitespace_error)
-               ;
-       else {
-               err = whitespace_error_string(result);
-               fprintf(stderr, "%s:%d: %s.\n%.*s\n",
-                       patch_input_file, linenr, err, len - 2, line + 1);
-               free(err);
-       }
+               return;
+
+       err = whitespace_error_string(result);
+       fprintf(stderr, "%s:%d: %s.\n%.*s\n",
+               patch_input_file, linenr, err, len, line);
+       free(err);
+}
+
+static void check_whitespace(const char *line, int len, unsigned ws_rule)
+{
+       unsigned result = ws_check(line + 1, len - 1, ws_rule);
+
+       record_ws_error(result, line + 1, len - 2, linenr);
 }
 
 /*
@@ -1179,6 +1194,7 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
                int len;
 
                fragment = xcalloc(1, sizeof(*fragment));
+               fragment->linenr = linenr;
                len = parse_fragment(line, size, patch, fragment);
                if (len <= 0)
                        die("corrupt patch at line %d", linenr);
@@ -1696,7 +1712,7 @@ static int match_fragment(struct image *img,
                fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
 
                /* Try fixing the line in the target */
-               if (sizeof(tgtfixbuf) < tgtlen)
+               if (sizeof(tgtfixbuf) > tgtlen)
                        tgtfix = tgtfixbuf;
                else
                        tgtfix = xmalloc(tgtlen);
@@ -1905,6 +1921,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                int len = linelen(patch, size);
                int plen, added;
                int added_blank_line = 0;
+               int is_blank_context = 0;
 
                if (!len)
                        break;
@@ -1937,8 +1954,11 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                        *new++ = '\n';
                        add_line_info(&preimage, "\n", 1, LINE_COMMON);
                        add_line_info(&postimage, "\n", 1, LINE_COMMON);
+                       is_blank_context = 1;
                        break;
                case ' ':
+                       if (plen && patch[1] == '\n')
+                               is_blank_context = 1;
                case '-':
                        memcpy(old, patch + 1, plen);
                        add_line_info(&preimage, old, plen,
@@ -1978,6 +1998,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                }
                if (added_blank_line)
                        new_blank_lines_at_end++;
+               else if (is_blank_context)
+                       ;
                else
                        new_blank_lines_at_end = 0;
                patch += len;
@@ -1996,6 +2018,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
        /*
         * A hunk to change lines at the beginning would begin with
         * @@ -1,L +N,M @@
+        * but we need to be careful.  -U0 that inserts before the second
+        * line also has this pattern.
         *
         * And a hunk to add to an empty file would begin with
         * @@ -0,0 +N,M @@
@@ -2003,7 +2027,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
         * In other words, a hunk that is (frag->oldpos <= 1) with or
         * without leading context must match at the beginning.
         */
-       match_beginning = frag->oldpos <= 1;
+       match_beginning = (!frag->oldpos ||
+                          (frag->oldpos == 1 && !unidiff_zero));
 
        /*
         * A hunk without trailing lines must match at the end.
@@ -2056,17 +2081,24 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
        }
 
        if (applied_pos >= 0) {
-               if (ws_error_action == correct_ws_error &&
-                   new_blank_lines_at_end &&
-                   postimage.nr + applied_pos == img->nr) {
+               if (new_blank_lines_at_end &&
+                   preimage.nr + applied_pos == img->nr &&
+                   (ws_rule & WS_BLANK_AT_EOF) &&
+                   ws_error_action != nowarn_ws_error) {
+                       record_ws_error(WS_BLANK_AT_EOF, "+", 1, frag->linenr);
+                       if (ws_error_action == correct_ws_error) {
+                               while (new_blank_lines_at_end--)
+                                       remove_last_line(&postimage);
+                       }
                        /*
-                        * If the patch application adds blank lines
-                        * at the end, and if the patch applies at the
-                        * end of the image, remove those added blank
-                        * lines.
+                        * We would want to prevent write_out_results()
+                        * from taking place in apply_patch() that follows
+                        * the callchain led us here, which is:
+                        * apply_patch->check_patch_list->check_patch->
+                        * apply_data->apply_fragments->apply_one_fragment
                         */
-                       while (new_blank_lines_at_end--)
-                               remove_last_line(&postimage);
+                       if (ws_error_action == die_on_ws_error)
+                               apply = 0;
                }
 
                /*
@@ -2250,12 +2282,12 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
 
 static struct patch *in_fn_table(const char *name)
 {
-       struct path_list_item *item;
+       struct string_list_item *item;
 
        if (name == NULL)
                return NULL;
 
-       item = path_list_lookup(name, &fn_table);
+       item = string_list_lookup(name, &fn_table);
        if (item != NULL)
                return (struct patch *)item->util;
 
@@ -2264,7 +2296,7 @@ static struct patch *in_fn_table(const char *name)
 
 static void add_to_fn_table(struct patch *patch)
 {
-       struct path_list_item *item;
+       struct string_list_item *item;
 
        /*
         * Always add new_name unless patch is a deletion
@@ -2272,7 +2304,7 @@ static void add_to_fn_table(struct patch *patch)
         * file creations and copies
         */
        if (patch->new_name != NULL) {
-               item = path_list_insert(patch->new_name, &fn_table);
+               item = string_list_insert(patch->new_name, &fn_table);
                item->util = patch;
        }
 
@@ -2281,7 +2313,7 @@ static void add_to_fn_table(struct patch *patch)
         * later chunks shouldn't patch old names
         */
        if ((patch->new_name == NULL) || (patch->is_rename)) {
-               item = path_list_insert(patch->old_name, &fn_table);
+               item = string_list_insert(patch->old_name, &fn_table);
                item->util = (struct patch *) -1;
        }
 }
@@ -2434,7 +2466,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)
@@ -2582,6 +2614,8 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
                        sha1_ptr = sha1;
 
                ce = make_cache_entry(patch->old_mode, sha1_ptr, name, 0, 0);
+               if (!ce)
+                       die("make_cache_entry failed for path '%s'", name);
                if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD))
                        die ("Could not add %s to temporary index", name);
        }
@@ -2732,15 +2766,7 @@ static void remove_file(struct patch *patch, int rmdir_empty)
                                warning("unable to remove submodule %s",
                                        patch->old_name);
                } else if (!unlink(patch->old_name) && rmdir_empty) {
-                       char *name = xstrdup(patch->old_name);
-                       char *end = strrchr(name, '/');
-                       while (end) {
-                               *end = 0;
-                               if (rmdir(name))
-                                       break;
-                               end = strrchr(name, '/');
-                       }
-                       free(name);
+                       remove_path(patch->old_name);
                }
        }
 }
@@ -2845,8 +2871,8 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
                unsigned int nr = getpid();
 
                for (;;) {
-                       const char *newpath;
-                       newpath = mkpath("%s~%u", path, nr);
+                       char newpath[PATH_MAX];
+                       mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr);
                        if (!try_create_file(newpath, mode, buf, size)) {
                                if (!rename(newpath, path))
                                        return;
@@ -3051,7 +3077,7 @@ static int apply_patch(int fd, const char *filename, int options)
        int skipped_patch = 0;
 
        /* FIXME - memory leak when using multiple patch files as inputs */
-       memset(&fn_table, 0, sizeof(struct path_list));
+       memset(&fn_table, 0, sizeof(struct string_list));
        strbuf_init(&buf, 0);
        patch_input_file = filename;
        read_patch_file(&buf, fd);