gitweb: Secure against commit-ish/tree-ish with the same name as path
[gitweb.git] / builtin-apply.c
index 9cf477c701d3224efe86418113691768410beae9..11397f5504f98ccff47a90b228abc71b30327fb9 100644 (file)
@@ -27,22 +27,25 @@ static const char *prefix;
 static int prefix_length = -1;
 static int newfd = -1;
 
+static int unidiff_zero;
 static int p_value = 1;
-static int allow_binary_replacement = 0;
-static int check_index = 0;
-static int write_index = 0;
-static int cached = 0;
-static int diffstat = 0;
-static int numstat = 0;
-static int summary = 0;
-static int check = 0;
+static int check_index;
+static int write_index;
+static int cached;
+static int diffstat;
+static int numstat;
+static int summary;
+static int check;
 static int apply = 1;
-static int no_add = 0;
-static int show_index_info = 0;
+static int apply_in_reverse;
+static int apply_with_reject;
+static int apply_verbosely;
+static int no_add;
+static int show_index_info;
 static int line_termination = '\n';
 static unsigned long p_context = -1;
 static const char apply_usage[] =
-"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <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|error|error-all|strip>] <patch>...";
 
 static enum whitespace_eol {
        nowarn_whitespace,
@@ -50,10 +53,10 @@ static enum whitespace_eol {
        error_on_whitespace,
        strip_whitespace,
 } new_whitespace = warn_on_whitespace;
-static int whitespace_error = 0;
+static int whitespace_error;
 static int squelch_whitespace_errors = 5;
-static int applied_after_stripping = 0;
-static const char *patch_input_file = NULL;
+static int applied_after_stripping;
+static const char *patch_input_file;
 
 static void parse_whitespace_option(const char *option)
 {
@@ -108,21 +111,37 @@ static int max_change, max_len;
  */
 static int linenr = 1;
 
+/*
+ * This represents one "hunk" from a patch, starting with
+ * "@@ -oldpos,oldlines +newpos,newlines @@" marker.  The
+ * patch text is pointed at by patch, and its byte length
+ * is stored in size.  leading and trailing are the number
+ * of context lines.
+ */
 struct fragment {
        unsigned long leading, trailing;
        unsigned long oldpos, oldlines;
        unsigned long newpos, newlines;
        const char *patch;
        int size;
+       int rejected;
        struct fragment *next;
 };
 
+/*
+ * When dealing with a binary patch, we reuse "leading" field
+ * to store the type of the binary hunk, either deflated "delta"
+ * or deflated "literal".
+ */
+#define binary_patch_method leading
+#define BINARY_DELTA_DEFLATED  1
+#define BINARY_LITERAL_DEFLATED 2
+
 struct patch {
        char *new_name, *old_name, *def_name;
        unsigned int old_mode, new_mode;
-       int is_rename, is_copy, is_new, is_delete, is_binary, is_reverse;
-#define BINARY_DELTA_DEFLATED 1
-#define BINARY_LITERAL_DEFLATED 2
+       int is_rename, is_copy, is_new, is_delete, is_binary;
+       int rejected;
        unsigned long deflate_origlen;
        int lines_added, lines_deleted;
        int score;
@@ -135,6 +154,24 @@ struct patch {
        struct patch *next;
 };
 
+static void say_patch_name(FILE *output, const char *pre, struct patch *patch, const char *post)
+{
+       fputs(pre, output);
+       if (patch->old_name && patch->new_name &&
+           strcmp(patch->old_name, patch->new_name)) {
+               write_name_quoted(NULL, 0, patch->old_name, 1, output);
+               fputs(" => ", output);
+               write_name_quoted(NULL, 0, patch->new_name, 1, output);
+       }
+       else {
+               const char *n = patch->new_name;
+               if (!n)
+                       n = patch->old_name;
+               write_name_quoted(NULL, 0, n, 1, output);
+       }
+       fputs(post, output);
+}
+
 #define CHUNKSIZE (8192)
 #define SLOP (16)
 
@@ -323,7 +360,7 @@ static int gitdiff_hdrend(const char *line, struct patch *patch)
 static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
 {
        if (!orig_name && !isnull)
-               return find_name(line, NULL, 1, 0);
+               return find_name(line, NULL, 1, TERM_TAB);
 
        if (orig_name) {
                int len;
@@ -333,7 +370,7 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name,
                len = strlen(name);
                if (isnull)
                        die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
-               another = find_name(line, NULL, 1, 0);
+               another = find_name(line, NULL, 1, TERM_TAB);
                if (!another || memcmp(another, name, len))
                        die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
                free(another);
@@ -591,9 +628,7 @@ static char *git_header_name(char *line, int llen)
         * form.
         */
        for (len = 0 ; ; len++) {
-               char c = name[len];
-
-               switch (c) {
+               switch (name[len]) {
                default:
                        continue;
                case '\n':
@@ -819,12 +854,54 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
        return -1;
 }
 
+static void check_whitespace(const char *line, int len)
+{
+       const char *err = "Adds trailing whitespace";
+       int seen_space = 0;
+       int i;
+
+       /*
+        * We know len is at least two, since we have a '+' and we
+        * checked that the last character was a '\n' before calling
+        * this function.  That is, an addition of an empty line would
+        * check the '+' here.  Sneaky...
+        */
+       if (isspace(line[len-2]))
+               goto error;
+
+       /*
+        * Make sure that there is no space followed by a tab in
+        * indentation.
+        */
+       err = "Space in indent is followed by a tab";
+       for (i = 1; i < len; i++) {
+               if (line[i] == '\t') {
+                       if (seen_space)
+                               goto error;
+               }
+               else if (line[i] == ' ')
+                       seen_space = 1;
+               else
+                       break;
+       }
+       return;
+
+ error:
+       whitespace_error++;
+       if (squelch_whitespace_errors &&
+           squelch_whitespace_errors < whitespace_error)
+               ;
+       else
+               fprintf(stderr, "%s.\n%s:%d:%.*s\n",
+                       err, patch_input_file, linenr, len-2, line+1);
+}
+
+
 /*
- * Parse a unified diff. Note that this really needs
- * to parse each fragment separately, since the only
- * way to know the difference between a "---" that is
- * part of a patch, and a "---" that starts the next
- * patch is to look at the line counts..
+ * Parse a unified diff. Note that this really needs to parse each
+ * fragment separately, since the only way to know the difference
+ * between a "---" that is part of a patch, and a "---" that starts
+ * the next patch is to look at the line counts..
  */
 static int parse_fragment(char *line, unsigned long size, struct patch *patch, struct fragment *fragment)
 {
@@ -841,31 +918,14 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
        leading = 0;
        trailing = 0;
 
-       if (patch->is_new < 0) {
-               patch->is_new =  !oldlines;
-               if (!oldlines)
-                       patch->old_name = NULL;
-       }
-       if (patch->is_delete < 0) {
-               patch->is_delete = !newlines;
-               if (!newlines)
-                       patch->new_name = NULL;
-       }
-
-       if (patch->is_new && oldlines)
-               return error("new file depends on old contents");
-       if (patch->is_delete != !newlines) {
-               if (newlines)
-                       return error("deleted file still has contents");
-               fprintf(stderr, "** warning: file %s becomes empty but is not deleted\n", patch->new_name);
-       }
-
        /* Parse the thing.. */
        line += len;
        size -= len;
        linenr++;
        added = deleted = 0;
-       for (offset = len; size > 0; offset += len, size -= len, line += len, linenr++) {
+       for (offset = len;
+            0 < size;
+            offset += len, size -= len, line += len, linenr++) {
                if (!oldlines && !newlines)
                        break;
                len = linelen(line, size);
@@ -874,6 +934,7 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
                switch (*line) {
                default:
                        return -1;
+               case '\n': /* newer GNU diff, an empty context line */
                case ' ':
                        oldlines--;
                        newlines--;
@@ -887,25 +948,8 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
                        trailing = 0;
                        break;
                case '+':
-                       /*
-                        * We know len is at least two, since we have a '+' and
-                        * we checked that the last character was a '\n' above.
-                        * That is, an addition of an empty line would check
-                        * the '+' here.  Sneaky...
-                        */
-                       if ((new_whitespace != nowarn_whitespace) &&
-                           isspace(line[len-2])) {
-                               whitespace_error++;
-                               if (squelch_whitespace_errors &&
-                                   squelch_whitespace_errors <
-                                   whitespace_error)
-                                       ;
-                               else {
-                                       fprintf(stderr, "Adds trailing whitespace.\n%s:%d:%.*s\n",
-                                               patch_input_file,
-                                               linenr, len-2, line+1);
-                               }
-                       }
+                       if (new_whitespace != nowarn_whitespace)
+                               check_whitespace(line, len);
                        added++;
                        newlines--;
                        trailing = 0;
@@ -938,12 +982,18 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
 
        patch->lines_added += added;
        patch->lines_deleted += deleted;
+
+       if (0 < patch->is_new && oldlines)
+               return error("new file depends on old contents");
+       if (0 < patch->is_delete && newlines)
+               return error("deleted file still has contents");
        return offset;
 }
 
 static int parse_single_patch(char *line, unsigned long size, struct patch *patch)
 {
        unsigned long offset = 0;
+       unsigned long oldlines = 0, newlines = 0, context = 0;
        struct fragment **fragp = &patch->fragments;
 
        while (size > 4 && !memcmp(line, "@@ -", 4)) {
@@ -954,9 +1004,11 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
                len = parse_fragment(line, size, patch, fragment);
                if (len <= 0)
                        die("corrupt patch at line %d", linenr);
-
                fragment->patch = line;
                fragment->size = len;
+               oldlines += fragment->oldlines;
+               newlines += fragment->newlines;
+               context += fragment->leading + fragment->trailing;
 
                *fragp = fragment;
                fragp = &fragment->next;
@@ -965,6 +1017,46 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
                line += len;
                size -= len;
        }
+
+       /*
+        * If something was removed (i.e. we have old-lines) it cannot
+        * be creation, and if something was added it cannot be
+        * deletion.  However, the reverse is not true; --unified=0
+        * patches that only add are not necessarily creation even
+        * though they do not have any old lines, and ones that only
+        * delete are not necessarily deletion.
+        *
+        * Unfortunately, a real creation/deletion patch do _not_ have
+        * any context line by definition, so we cannot safely tell it
+        * apart with --unified=0 insanity.  At least if the patch has
+        * more than one hunk it is not creation or deletion.
+        */
+       if (patch->is_new < 0 &&
+           (oldlines || (patch->fragments && patch->fragments->next)))
+               patch->is_new = 0;
+       if (patch->is_delete < 0 &&
+           (newlines || (patch->fragments && patch->fragments->next)))
+               patch->is_delete = 0;
+       if (!unidiff_zero || context) {
+               /* If the user says the patch is not generated with
+                * --unified=0, or if we have seen context lines,
+                * then not having oldlines means the patch is creation,
+                * and not having newlines means the patch is deletion.
+                */
+               if (patch->is_new < 0 && !oldlines)
+                       patch->is_new = 1;
+               if (patch->is_delete < 0 && !newlines)
+                       patch->is_delete = 1;
+       }
+
+       if (0 < patch->is_new && oldlines)
+               die("new file %s depends on old contents", patch->new_name);
+       if (0 < patch->is_delete && newlines)
+               die("deleted file %s still has contents", patch->old_name);
+       if (!patch->is_delete && !newlines && context)
+               fprintf(stderr, "** warning: file %s becomes empty but "
+                       "is not deleted\n", patch->new_name);
+
        return offset;
 }
 
@@ -978,51 +1070,82 @@ static inline int metadata_changes(struct patch *patch)
                 patch->old_mode != patch->new_mode);
 }
 
-static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+static char *inflate_it(const void *data, unsigned long size,
+                       unsigned long inflated_size)
 {
-       /* We have read "GIT binary patch\n"; what follows is a line
-        * that says the patch method (currently, either "deflated
-        * literal" or "deflated delta") and the length of data before
-        * deflating; a sequence of 'length-byte' followed by base-85
-        * encoded data follows.
+       z_stream stream;
+       void *out;
+       int st;
+
+       memset(&stream, 0, sizeof(stream));
+
+       stream.next_in = (unsigned char *)data;
+       stream.avail_in = size;
+       stream.next_out = out = xmalloc(inflated_size);
+       stream.avail_out = inflated_size;
+       inflateInit(&stream);
+       st = inflate(&stream, Z_FINISH);
+       if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
+               free(out);
+               return NULL;
+       }
+       return out;
+}
+
+static struct fragment *parse_binary_hunk(char **buf_p,
+                                         unsigned long *sz_p,
+                                         int *status_p,
+                                         int *used_p)
+{
+       /* Expect a line that begins with binary patch method ("literal"
+        * or "delta"), followed by the length of data before deflating.
+        * a sequence of 'length-byte' followed by base-85 encoded data
+        * should follow, terminated by a newline.
         *
         * Each 5-byte sequence of base-85 encodes up to 4 bytes,
         * and we would limit the patch line to 66 characters,
         * so one line can fit up to 13 groups that would decode
         * to 52 bytes max.  The length byte 'A'-'Z' corresponds
         * to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes.
-        * The end of binary is signaled with an empty line.
         */
        int llen, used;
-       struct fragment *fragment;
+       unsigned long size = *sz_p;
+       char *buffer = *buf_p;
+       int patch_method;
+       unsigned long origlen;
        char *data = NULL;
+       int hunk_size = 0;
+       struct fragment *frag;
 
-       patch->fragments = fragment = xcalloc(1, sizeof(*fragment));
-
-       /* Grab the type of patch */
        llen = linelen(buffer, size);
        used = llen;
-       linenr++;
+
+       *status_p = 0;
 
        if (!strncmp(buffer, "delta ", 6)) {
-               patch->is_binary = BINARY_DELTA_DEFLATED;
-               patch->deflate_origlen = strtoul(buffer + 6, NULL, 10);
+               patch_method = BINARY_DELTA_DEFLATED;
+               origlen = strtoul(buffer + 6, NULL, 10);
        }
        else if (!strncmp(buffer, "literal ", 8)) {
-               patch->is_binary = BINARY_LITERAL_DEFLATED;
-               patch->deflate_origlen = strtoul(buffer + 8, NULL, 10);
+               patch_method = BINARY_LITERAL_DEFLATED;
+               origlen = strtoul(buffer + 8, NULL, 10);
        }
        else
-               return error("unrecognized binary patch at line %d: %.*s",
-                            linenr-1, llen-1, buffer);
+               return NULL;
+
+       linenr++;
        buffer += llen;
        while (1) {
                int byte_length, max_byte_length, newsize;
                llen = linelen(buffer, size);
                used += llen;
                linenr++;
-               if (llen == 1)
+               if (llen == 1) {
+                       /* consume the blank line */
+                       buffer++;
+                       size--;
                        break;
+               }
                /* Minimum line is "A00000\n" which is 7-byte long,
                 * and the line length must be multiple of 5 plus 2.
                 */
@@ -1043,21 +1166,78 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
                if (max_byte_length < byte_length ||
                    byte_length <= max_byte_length - 4)
                        goto corrupt;
-               newsize = fragment->size + byte_length;
+               newsize = hunk_size + byte_length;
                data = xrealloc(data, newsize);
-               if (decode_85(data + fragment->size,
-                             buffer + 1,
-                             byte_length))
+               if (decode_85(data + hunk_size, buffer + 1, byte_length))
                        goto corrupt;
-               fragment->size = newsize;
+               hunk_size = newsize;
                buffer += llen;
                size -= llen;
        }
-       fragment->patch = data;
-       return used;
+
+       frag = xcalloc(1, sizeof(*frag));
+       frag->patch = inflate_it(data, hunk_size, origlen);
+       if (!frag->patch)
+               goto corrupt;
+       free(data);
+       frag->size = origlen;
+       *buf_p = buffer;
+       *sz_p = size;
+       *used_p = used;
+       frag->binary_patch_method = patch_method;
+       return frag;
+
  corrupt:
-       return error("corrupt binary patch at line %d: %.*s",
-                    linenr-1, llen-1, buffer);
+       free(data);
+       *status_p = -1;
+       error("corrupt binary patch at line %d: %.*s",
+             linenr-1, llen-1, buffer);
+       return NULL;
+}
+
+static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+{
+       /* We have read "GIT binary patch\n"; what follows is a line
+        * that says the patch method (currently, either "literal" or
+        * "delta") and the length of data before deflating; a
+        * sequence of 'length-byte' followed by base-85 encoded data
+        * follows.
+        *
+        * When a binary patch is reversible, there is another binary
+        * hunk in the same format, starting with patch method (either
+        * "literal" or "delta") with the length of data, and a sequence
+        * of length-byte + base-85 encoded data, terminated with another
+        * empty line.  This data, when applied to the postimage, produces
+        * the preimage.
+        */
+       struct fragment *forward;
+       struct fragment *reverse;
+       int status;
+       int used, used_1;
+
+       forward = parse_binary_hunk(&buffer, &size, &status, &used);
+       if (!forward && !status)
+               /* there has to be one hunk (forward hunk) */
+               return error("unrecognized binary patch at line %d", linenr-1);
+       if (status)
+               /* otherwise we already gave an error message */
+               return status;
+
+       reverse = parse_binary_hunk(&buffer, &size, &status, &used_1);
+       if (reverse)
+               used += used_1;
+       else if (status) {
+               /* not having reverse hunk is not an error, but having
+                * a corrupt reverse hunk is.
+                */
+               free((void*) forward->patch);
+               free(forward);
+               return status;
+       }
+       forward->next = reverse;
+       patch->fragments = forward;
+       patch->is_binary = 1;
+       return used;
 }
 
 static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
@@ -1105,14 +1285,12 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
                        }
                }
 
-               /* Empty patch cannot be applied if:
-                * - it is a binary patch and we do not do binary_replace, or
-                * - text patch without metadata change
+               /* Empty patch cannot be applied if it is a text patch
+                * without metadata change.  A binary patch appears
+                * empty to us here.
                 */
                if ((apply || check) &&
-                   (patch->is_binary
-                    ? !allow_binary_replacement
-                    : !metadata_changes(patch)))
+                   (!patch->is_binary && !metadata_changes(patch)))
                        die("patch with only garbage at line %d", linenr);
        }
 
@@ -1143,7 +1321,6 @@ static void reverse_patches(struct patch *p)
                        swap(frag->newpos, frag->oldpos);
                        swap(frag->newlines, frag->oldlines);
                }
-               p->is_reverse = !p->is_reverse;
        }
 }
 
@@ -1206,8 +1383,7 @@ static void show_stats(struct patch *patch)
                printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
                       len, name, patch->lines_added + patch->lines_deleted,
                       add, pluses, del, minuses);
-       if (qname)
-               free(qname);
+       free(qname);
 }
 
 static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
@@ -1345,26 +1521,71 @@ static int apply_line(char *output, const char *patch, int plen)
 {
        /* plen is number of bytes to be copied from patch,
         * starting at patch+1 (patch[0] is '+').  Typically
-        * patch[plen] is '\n'.
+        * patch[plen] is '\n', unless this is the incomplete
+        * last line.
         */
+       int i;
        int add_nl_to_tail = 0;
-       if ((new_whitespace == strip_whitespace) &&
-           1 < plen && isspace(patch[plen-1])) {
+       int fixed = 0;
+       int last_tab_in_indent = -1;
+       int last_space_in_indent = -1;
+       int need_fix_leading_space = 0;
+       char *buf;
+
+       if ((new_whitespace != strip_whitespace) || !whitespace_error) {
+               memcpy(output, patch + 1, plen);
+               return plen;
+       }
+
+       if (1 < plen && isspace(patch[plen-1])) {
                if (patch[plen] == '\n')
                        add_nl_to_tail = 1;
                plen--;
                while (0 < plen && isspace(patch[plen]))
                        plen--;
-               applied_after_stripping++;
+               fixed = 1;
+       }
+
+       for (i = 1; i < plen; i++) {
+               char ch = patch[i];
+               if (ch == '\t') {
+                       last_tab_in_indent = i;
+                       if (0 <= last_space_in_indent)
+                               need_fix_leading_space = 1;
+               }
+               else if (ch == ' ')
+                       last_space_in_indent = i;
+               else
+                       break;
        }
-       memcpy(output, patch + 1, plen);
+
+       buf = output;
+       if (need_fix_leading_space) {
+               /* between patch[1..last_tab_in_indent] strip the
+                * funny spaces, updating them to tab as needed.
+                */
+               for (i = 1; i < last_tab_in_indent; i++, plen--) {
+                       char ch = patch[i];
+                       if (ch != ' ')
+                               *output++ = ch;
+                       else if ((i % 8) == 0)
+                               *output++ = '\t';
+               }
+               fixed = 1;
+               i = last_tab_in_indent;
+       }
+       else
+               i = 1;
+
+       memcpy(output, patch + i, plen);
        if (add_nl_to_tail)
                output[plen++] = '\n';
-       return plen;
+       if (fixed)
+               applied_after_stripping++;
+       return output + plen - buf;
 }
 
-static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
-       int reverse, int inaccurate_eof)
+static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, int inaccurate_eof)
 {
        int match_beginning, match_end;
        char *buf = desc->buffer;
@@ -1396,13 +1617,21 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
                if (len < size && patch[len] == '\\')
                        plen--;
                first = *patch;
-               if (reverse) {
+               if (apply_in_reverse) {
                        if (first == '-')
                                first = '+';
                        else if (first == '+')
                                first = '-';
                }
                switch (first) {
+               case '\n':
+                       /* Newer GNU diff, empty context line */
+                       if (plen < 0)
+                               /* ... followed by '\No newline'; nothing */
+                               break;
+                       old[oldsize++] = '\n';
+                       new[newsize++] = '\n';
+                       break;
                case ' ':
                case '-':
                        memcpy(old + oldsize, patch + 1, plen);
@@ -1439,14 +1668,25 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
        /*
         * If we don't have any leading/trailing data in the patch,
         * we want it to match at the beginning/end of the file.
+        *
+        * But that would break if the patch is generated with
+        * --unified=0; sane people wouldn't do that to cause us
+        * trouble, but we try to please not so sane ones as well.
         */
-       match_beginning = !leading && (frag->oldpos == 1);
-       match_end = !trailing;
+       if (unidiff_zero) {
+               match_beginning = (!leading && !frag->oldpos);
+               match_end = 0;
+       }
+       else {
+               match_beginning = !leading && (frag->oldpos == 1);
+               match_end = !trailing;
+       }
 
        lines = 0;
        pos = frag->newpos;
        for (;;) {
-               offset = find_offset(buf, desc->size, oldlines, oldsize, pos, &lines);
+               offset = find_offset(buf, desc->size,
+                                    oldlines, oldsize, pos, &lines);
                if (match_end && offset + oldsize != desc->size)
                        offset = -1;
                if (match_beginning && offset)
@@ -1459,8 +1699,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
                        /* Warn if it was necessary to reduce the number
                         * of context lines.
                         */
-                       if ((leading != frag->leading) || (trailing != frag->trailing))
-                               fprintf(stderr, "Context reduced to (%ld/%ld) to apply fragment at %d\n",
+                       if ((leading != frag->leading) ||
+                           (trailing != frag->trailing))
+                               fprintf(stderr, "Context reduced to (%ld/%ld)"
+                                       " to apply fragment at %d\n",
                                        leading, trailing, pos + lines);
 
                        if (size > alloc) {
@@ -1470,7 +1712,9 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
                                desc->buffer = buf;
                        }
                        desc->size = size;
-                       memmove(buf + offset + newsize, buf + offset + oldsize, size - offset - newsize);
+                       memmove(buf + offset + newsize,
+                               buf + offset + oldsize,
+                               size - offset - newsize);
                        memcpy(buf + offset, newlines, newsize);
                        offset = 0;
 
@@ -1506,28 +1750,6 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
        return offset;
 }
 
-static char *inflate_it(const void *data, unsigned long size,
-                       unsigned long inflated_size)
-{
-       z_stream stream;
-       void *out;
-       int st;
-
-       memset(&stream, 0, sizeof(stream));
-
-       stream.next_in = (unsigned char *)data;
-       stream.avail_in = size;
-       stream.next_out = out = xmalloc(inflated_size);
-       stream.avail_out = inflated_size;
-       inflateInit(&stream);
-       st = inflate(&stream, Z_FINISH);
-       if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
-               free(out);
-               return NULL;
-       }
-       return out;
-}
-
 static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
 {
        unsigned long dst_size;
@@ -1535,30 +1757,29 @@ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
        void *data;
        void *result;
 
-       /* Binary patch is irreversible */
-       if (patch->is_reverse)
-               return error("cannot reverse-apply a binary patch to '%s'",
-                            patch->new_name
-                            ? patch->new_name : patch->old_name);
-
-       data = inflate_it(fragment->patch, fragment->size,
-                         patch->deflate_origlen);
-       if (!data)
-               return error("corrupt patch data");
-       switch (patch->is_binary) {
+       /* Binary patch is irreversible without the optional second hunk */
+       if (apply_in_reverse) {
+               if (!fragment->next)
+                       return error("cannot reverse-apply a binary patch "
+                                    "without the reverse hunk to '%s'",
+                                    patch->new_name
+                                    ? patch->new_name : patch->old_name);
+               fragment = fragment->next;
+       }
+       data = (void*) fragment->patch;
+       switch (fragment->binary_patch_method) {
        case BINARY_DELTA_DEFLATED:
                result = patch_delta(desc->buffer, desc->size,
                                     data,
-                                    patch->deflate_origlen,
+                                    fragment->size,
                                     &dst_size);
                free(desc->buffer);
                desc->buffer = result;
-               free(data);
                break;
        case BINARY_LITERAL_DEFLATED:
                free(desc->buffer);
                desc->buffer = data;
-               dst_size = patch->deflate_origlen;
+               dst_size = fragment->size;
                break;
        }
        if (!desc->buffer)
@@ -1571,13 +1792,6 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
 {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        unsigned char sha1[20];
-       unsigned char hdr[50];
-       int hdrlen;
-
-       if (!allow_binary_replacement)
-               return error("cannot apply binary patch to '%s' "
-                            "without --allow-binary-replacement",
-                            name);
 
        /* For safety, we require patch index line to contain
         * full 40-byte textual SHA1 for old and new, at least for now.
@@ -1593,8 +1807,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
                /* See if the old one matches what the patch
                 * applies to.
                 */
-               write_sha1_file_prepare(desc->buffer, desc->size,
-                                       blob_type, sha1, hdr, &hdrlen);
+               hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
                        return error("the patch applies to '%s' (%s), "
                                     "which does not match the "
@@ -1609,7 +1822,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
        }
 
        get_sha1_hex(patch->new_sha1_prefix, sha1);
-       if (!memcmp(sha1, null_sha1, 20)) {
+       if (is_null_sha1(sha1)) {
                free(desc->buffer);
                desc->alloc = desc->size = 0;
                desc->buffer = NULL;
@@ -1639,10 +1852,9 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
                                     name);
 
                /* verify that the result matches */
-               write_sha1_file_prepare(desc->buffer, desc->size, blob_type,
-                                       sha1, hdr, &hdrlen);
+               hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
-                       return error("binary patch to '%s' creates incorrect result", name);
+                       return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1));
        }
 
        return 0;
@@ -1657,10 +1869,12 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
                return apply_binary(desc, patch);
 
        while (frag) {
-               if (apply_one_fragment(desc, frag, patch->is_reverse,
-                                       patch->inaccurate_eof) < 0)
-                       return error("patch failed: %s:%ld",
-                                    name, frag->oldpos);
+               if (apply_one_fragment(desc, frag, patch->inaccurate_eof)) {
+                       error("patch failed: %s:%ld", name, frag->oldpos);
+                       if (!apply_with_reject)
+                               return -1;
+                       frag->rejected = 1;
+               }
                frag = frag->next;
        }
        return 0;
@@ -1696,8 +1910,9 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
        desc.size = size;
        desc.alloc = alloc;
        desc.buffer = buf;
+
        if (apply_fragments(&desc, patch) < 0)
-               return -1;
+               return -1; /* note with --reject this succeeds. */
 
        /* NUL terminate the result */
        if (desc.alloc <= desc.size)
@@ -1707,7 +1922,7 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
        patch->result = desc.buffer;
        patch->resultsize = desc.size;
 
-       if (patch->is_delete && patch->resultsize)
+       if (0 < patch->is_delete && patch->resultsize)
                return error("removal patch leaves file contents");
 
        return 0;
@@ -1722,6 +1937,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
        struct cache_entry *ce = NULL;
        int ok_if_exists;
 
+       patch->rejected = 1; /* we will drop this after we succeed */
        if (old_name) {
                int changed = 0;
                int stat_ret = 0;
@@ -1778,7 +1994,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
                                old_name, st_mode, patch->old_mode);
        }
 
-       if (new_name && prev_patch && prev_patch->is_delete &&
+       if (new_name && prev_patch && 0 < prev_patch->is_delete &&
            !strcmp(prev_patch->old_name, new_name))
                /* A type-change diff is always split into a patch to
                 * delete old, immediately followed by a patch to
@@ -1791,7 +2007,8 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
        else
                ok_if_exists = 0;
 
-       if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
+       if (new_name &&
+           ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) {
                if (check_index &&
                    cache_name_pos(new_name, strlen(new_name)) >= 0 &&
                    !ok_if_exists)
@@ -1808,7 +2025,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
                                return error("%s: %s", new_name, strerror(errno));
                }
                if (!patch->new_mode) {
-                       if (patch->is_new)
+                       if (0 < patch->is_new)
                                patch->new_mode = S_IFREG | 0644;
                        else
                                patch->new_mode = patch->old_mode;
@@ -1827,24 +2044,23 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
 
        if (apply_data(patch, &st, ce) < 0)
                return error("%s: patch does not apply", name);
+       patch->rejected = 0;
        return 0;
 }
 
 static int check_patch_list(struct patch *patch)
 {
        struct patch *prev_patch = NULL;
-       int error = 0;
+       int err = 0;
 
        for (prev_patch = NULL; patch ; patch = patch->next) {
-               error |= check_patch(patch, prev_patch);
+               if (apply_verbosely)
+                       say_patch_name(stderr,
+                                      "Checking patch ", patch, "...\n");
+               err |= check_patch(patch, prev_patch);
                prev_patch = patch;
        }
-       return error;
-}
-
-static inline int is_null_sha1(const unsigned char *sha1)
-{
-       return !memcmp(sha1, null_sha1, 20);
+       return err;
 }
 
 static void show_index_list(struct patch *list)
@@ -1860,7 +2076,7 @@ static void show_index_list(struct patch *list)
                const char *name;
 
                name = patch->old_name ? patch->old_name : patch->new_name;
-               if (patch->is_new)
+               if (0 < patch->is_new)
                        sha1_ptr = null_sha1;
                else if (get_sha1(patch->old_sha1_prefix, sha1))
                        die("sha1 information is lacking or useless (%s).",
@@ -1901,7 +2117,7 @@ static void numstat_patch_list(struct patch *patch)
                        quote_c_style(name, NULL, stdout, 0);
                else
                        fputs(name, stdout);
-               putchar('\n');
+               putchar(line_termination);
        }
 }
 
@@ -2150,23 +2366,99 @@ static void write_out_one_result(struct patch *patch, int phase)
        if (phase == 0)
                remove_file(patch);
        if (phase == 1)
-       create_file(patch);
+               create_file(patch);
 }
 
-static void write_out_results(struct patch *list, int skipped_patch)
+static int write_out_one_reject(struct patch *patch)
+{
+       FILE *rej;
+       char namebuf[PATH_MAX];
+       struct fragment *frag;
+       int cnt = 0;
+
+       for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
+               if (!frag->rejected)
+                       continue;
+               cnt++;
+       }
+
+       if (!cnt) {
+               if (apply_verbosely)
+                       say_patch_name(stderr,
+                                      "Applied patch ", patch, " cleanly.\n");
+               return 0;
+       }
+
+       /* This should not happen, because a removal patch that leaves
+        * contents are marked "rejected" at the patch level.
+        */
+       if (!patch->new_name)
+               die("internal error");
+
+       /* Say this even without --verbose */
+       say_patch_name(stderr, "Applying patch ", patch, " with");
+       fprintf(stderr, " %d rejects...\n", cnt);
+
+       cnt = strlen(patch->new_name);
+       if (ARRAY_SIZE(namebuf) <= cnt + 5) {
+               cnt = ARRAY_SIZE(namebuf) - 5;
+               fprintf(stderr,
+                       "warning: truncating .rej filename to %.*s.rej",
+                       cnt - 1, patch->new_name);
+       }
+       memcpy(namebuf, patch->new_name, cnt);
+       memcpy(namebuf + cnt, ".rej", 5);
+
+       rej = fopen(namebuf, "w");
+       if (!rej)
+               return error("cannot open %s: %s", namebuf, strerror(errno));
+
+       /* Normal git tools never deal with .rej, so do not pretend
+        * this is a git patch by saying --git nor give extended
+        * headers.  While at it, maybe please "kompare" that wants
+        * the trailing TAB and some garbage at the end of line ;-).
+        */
+       fprintf(rej, "diff a/%s b/%s\t(rejected hunks)\n",
+               patch->new_name, patch->new_name);
+       for (cnt = 1, frag = patch->fragments;
+            frag;
+            cnt++, frag = frag->next) {
+               if (!frag->rejected) {
+                       fprintf(stderr, "Hunk #%d applied cleanly.\n", cnt);
+                       continue;
+               }
+               fprintf(stderr, "Rejected hunk #%d.\n", cnt);
+               fprintf(rej, "%.*s", frag->size, frag->patch);
+               if (frag->patch[frag->size-1] != '\n')
+                       fputc('\n', rej);
+       }
+       fclose(rej);
+       return -1;
+}
+
+static int write_out_results(struct patch *list, int skipped_patch)
 {
        int phase;
+       int errs = 0;
+       struct patch *l;
 
        if (!list && !skipped_patch)
-               die("No changes");
+               return error("No changes");
 
        for (phase = 0; phase < 2; phase++) {
-               struct patch *l = list;
+               l = list;
                while (l) {
-                       write_out_one_result(l, phase);
+                       if (l->rejected)
+                               errs = 1;
+                       else {
+                               write_out_one_result(l, phase);
+                               if (phase == 1 && write_out_one_reject(l))
+                                       errs = 1;
+                       }
                        l = l->next;
                }
        }
+       return errs;
 }
 
 static struct lock_file lock_file;
@@ -2194,8 +2486,7 @@ static int use_patch(struct patch *p)
        return 1;
 }
 
-static int apply_patch(int fd, const char *filename,
-               int reverse, int inaccurate_eof)
+static int apply_patch(int fd, const char *filename, int inaccurate_eof)
 {
        unsigned long offset, size;
        char *buffer = read_patch_file(fd, &size);
@@ -2215,7 +2506,7 @@ static int apply_patch(int fd, const char *filename,
                nr = parse_chunk(buffer + offset, size, patch);
                if (nr < 0)
                        break;
-               if (reverse)
+               if (apply_in_reverse)
                        reverse_patches(patch);
                if (use_patch(patch)) {
                        patch_stats(patch);
@@ -2242,11 +2533,13 @@ static int apply_patch(int fd, const char *filename,
                        die("unable to read index file");
        }
 
-       if ((check || apply) && check_patch_list(list) < 0)
+       if ((check || apply) &&
+           check_patch_list(list) < 0 &&
+           !apply_with_reject)
                exit(1);
 
-       if (apply)
-               write_out_results(list, skipped_patch);
+       if (apply && write_out_results(list, skipped_patch))
+               exit(1);
 
        if (show_index_info)
                show_index_list(list);
@@ -2267,7 +2560,7 @@ static int apply_patch(int fd, const char *filename,
 static int git_apply_config(const char *var, const char *value)
 {
        if (!strcmp(var, "apply.whitespace")) {
-               apply_default_whitespace = strdup(value);
+               apply_default_whitespace = xstrdup(value);
                return 0;
        }
        return git_default_config(var, value);
@@ -2278,8 +2571,8 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
 {
        int i;
        int read_stdin = 1;
-       int reverse = 0;
        int inaccurate_eof = 0;
+       int errs = 0;
 
        const char *whitespace_option = NULL;
 
@@ -2289,7 +2582,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                int fd;
 
                if (!strcmp(arg, "-")) {
-                       apply_patch(0, "<stdin>", reverse, inaccurate_eof);
+                       errs |= apply_patch(0, "<stdin>", inaccurate_eof);
                        read_stdin = 0;
                        continue;
                }
@@ -2315,8 +2608,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                }
                if (!strcmp(arg, "--allow-binary-replacement") ||
                    !strcmp(arg, "--binary")) {
-                       allow_binary_replacement = 1;
-                       continue;
+                       continue; /* now no-op */
                }
                if (!strcmp(arg, "--numstat")) {
                        apply = 0;
@@ -2367,7 +2659,19 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        continue;
                }
                if (!strcmp(arg, "-R") || !strcmp(arg, "--reverse")) {
-                       reverse = 1;
+                       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, "--verbose")) {
+                       apply_verbosely = 1;
                        continue;
                }
                if (!strcmp(arg, "--inaccurate-eof")) {
@@ -2390,18 +2694,19 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        usage(apply_usage);
                read_stdin = 0;
                set_default_whitespace_mode(whitespace_option);
-               apply_patch(fd, arg, reverse, inaccurate_eof);
+               errs |= apply_patch(fd, arg, inaccurate_eof);
                close(fd);
        }
        set_default_whitespace_mode(whitespace_option);
        if (read_stdin)
-               apply_patch(0, "<stdin>", reverse, inaccurate_eof);
+               errs |= apply_patch(0, "<stdin>", inaccurate_eof);
        if (whitespace_error) {
                if (squelch_whitespace_errors &&
                    squelch_whitespace_errors < whitespace_error) {
                        int squelched =
                                whitespace_error - squelch_whitespace_errors;
-                       fprintf(stderr, "warning: squelched %d whitespace error%s\n",
+                       fprintf(stderr, "warning: squelched %d "
+                               "whitespace error%s\n",
                                squelched,
                                squelched == 1 ? "" : "s");
                }
@@ -2429,5 +2734,5 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        die("Unable to write new index file");
        }
 
-       return 0;
+       return !!errs;
 }