Merge branch 'master' into next
[gitweb.git] / apply.c
diff --git a/apply.c b/apply.c
index 590adc6afafa98e389dcafc2f8c432db09412d4e..e283df38aac6b5bb53e3f1673d5ea83493ba5a5f 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -8,7 +8,13 @@
  */
 #include <fnmatch.h>
 #include "cache.h"
+#include "cache-tree.h"
 #include "quote.h"
+#include "blob.h"
+
+static unsigned char active_cache_sha1[20];
+static struct cache_tree *active_cache_tree;
+
 
 //  --check turns on checking that the working tree matches the
 //    files that are being modified, but doesn't apply the patch
 //  --numstat does numeric diffstat, and doesn't actually apply
 //  --index-info shows the old and new index info for paths if available.
 //
+static const char *prefix;
+static int prefix_length = -1;
+
+static int p_value = 1;
+static int allow_binary_replacement = 0;
 static int check_index = 0;
 static int write_index = 0;
 static int diffstat = 0;
@@ -26,8 +37,59 @@ static int apply = 1;
 static int no_add = 0;
 static int show_index_info = 0;
 static int line_termination = '\n';
+static unsigned long p_context = -1;
 static const char apply_usage[] =
-"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [-z] <patch>...";
+"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
+
+static enum whitespace_eol {
+       nowarn_whitespace,
+       warn_on_whitespace,
+       error_on_whitespace,
+       strip_whitespace,
+} new_whitespace = warn_on_whitespace;
+static int whitespace_error = 0;
+static int squelch_whitespace_errors = 5;
+static int applied_after_stripping = 0;
+static const char *patch_input_file = NULL;
+
+static void parse_whitespace_option(const char *option)
+{
+       if (!option) {
+               new_whitespace = warn_on_whitespace;
+               return;
+       }
+       if (!strcmp(option, "warn")) {
+               new_whitespace = warn_on_whitespace;
+               return;
+       }
+       if (!strcmp(option, "nowarn")) {
+               new_whitespace = nowarn_whitespace;
+               return;
+       }
+       if (!strcmp(option, "error")) {
+               new_whitespace = error_on_whitespace;
+               return;
+       }
+       if (!strcmp(option, "error-all")) {
+               new_whitespace = error_on_whitespace;
+               squelch_whitespace_errors = 0;
+               return;
+       }
+       if (!strcmp(option, "strip")) {
+               new_whitespace = strip_whitespace;
+               return;
+       }
+       die("unrecognized whitespace option '%s'", option);
+}
+
+static void set_default_whitespace_mode(const char *whitespace_option)
+{
+       if (!whitespace_option && !apply_default_whitespace) {
+               new_whitespace = (apply
+                                 ? warn_on_whitespace
+                                 : nowarn_whitespace);
+       }
+}
 
 /*
  * For "diff-stat" like behaviour, we keep track of the biggest change
@@ -44,6 +106,7 @@ static int max_change, max_len;
 static int linenr = 1;
 
 struct fragment {
+       unsigned long leading, trailing;
        unsigned long oldpos, oldlines;
        unsigned long newpos, newlines;
        const char *patch;
@@ -80,14 +143,11 @@ static void *read_patch_file(int fd, unsigned long *sizep)
                        buffer = xrealloc(buffer, alloc);
                        nr = alloc - size;
                }
-               nr = read(fd, buffer + size, nr);
+               nr = xread(fd, buffer + size, nr);
                if (!nr)
                        break;
-               if (nr < 0) {
-                       if (errno == EAGAIN)
-                               continue;
+               if (nr < 0)
                        die("git-apply: read returned %s", strerror(errno));
-               }
                size += nr;
        }
        *sizep = size;
@@ -216,7 +276,6 @@ static char * find_name(const char *line, char *def, int p_value, int terminate)
  */
 static void parse_traditional_patch(const char *first, const char *second, struct patch *patch)
 {
-       int p_value = 1;
        char *name;
 
        first += 4;     // skip "--- "
@@ -600,7 +659,7 @@ static int parse_git_header(char *line, int len, unsigned int size, struct patch
                len = linelen(line, size);
                if (!len || line[len-1] != '\n')
                        break;
-               for (i = 0; i < sizeof(optable) / sizeof(optable[0]); i++) {
+               for (i = 0; i < ARRAY_SIZE(optable); i++) {
                        const struct opentry *p = optable + i;
                        int oplen = strlen(p->str);
                        if (len < oplen || memcmp(p->str, line, oplen))
@@ -642,7 +701,7 @@ static int parse_range(const char *line, int len, int offset, const char *expect
        line += digits;
        len -= digits;
 
-       *p2 = *p1;
+       *p2 = 1;
        if (*line == ',') {
                digits = parse_num(line+1, p2);
                if (!digits)
@@ -765,12 +824,15 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
        int added, deleted;
        int len = linelen(line, size), offset;
        unsigned long oldlines, newlines;
+       unsigned long leading, trailing;
 
        offset = parse_fragment_header(line, len, fragment);
        if (offset < 0)
                return -1;
        oldlines = fragment->oldlines;
        newlines = fragment->newlines;
+       leading = 0;
+       trailing = 0;
 
        if (patch->is_new < 0) {
                patch->is_new =  !oldlines;
@@ -783,7 +845,7 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
                        patch->new_name = NULL;
        }
 
-       if (patch->is_new != !oldlines)
+       if (patch->is_new && oldlines)
                return error("new file depends on old contents");
        if (patch->is_delete != !newlines) {
                if (newlines)
@@ -808,14 +870,38 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
                case ' ':
                        oldlines--;
                        newlines--;
+                       if (!deleted && !added)
+                               leading++;
+                       trailing++;
                        break;
                case '-':
                        deleted++;
                        oldlines--;
+                       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);
+                               }
+                       }
                        added++;
                        newlines--;
+                       trailing = 0;
                        break;
 
                 /* We allow "\ No newline at end of file". Depending
@@ -831,6 +917,11 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
                        break;
                }
        }
+       if (oldlines || newlines)
+               return -1;
+       fragment->leading = leading;
+       fragment->trailing = trailing;
+
        /* If a fragment ends with an incomplete line, we failed to include
         * it in the above loop because we hit oldlines == newlines == 0
         * before seeing it.
@@ -852,8 +943,7 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
                struct fragment *fragment;
                int len;
 
-               fragment = xmalloc(sizeof(*fragment));
-               memset(fragment, 0, sizeof(*fragment));
+               fragment = xcalloc(1, sizeof(*fragment));
                len = parse_fragment(line, size, patch, fragment);
                if (len <= 0)
                        die("corrupt patch at line %d", linenr);
@@ -891,17 +981,34 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
 
        patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch);
 
-       if (!patchsize && !metadata_changes(patch)) {
-               static const char binhdr[] = "Binary files ";
-
-               if (sizeof(binhdr) - 1 < size - offset - hdrsize &&
-                   !memcmp(binhdr, buffer + hdrsize + offset,
-                           sizeof(binhdr)-1))
-                       patch->is_binary = 1;
+       if (!patchsize) {
+               static const char *binhdr[] = {
+                       "Binary files ",
+                       "Files ",
+                       NULL,
+               };
+               int i;
+               int hd = hdrsize + offset;
+               unsigned long llen = linelen(buffer + hd, size - hd);
+
+               if (!memcmp(" differ\n", buffer + hd + llen - 8, 8))
+                       for (i = 0; binhdr[i]; i++) {
+                               int len = strlen(binhdr[i]);
+                               if (len < size - hd &&
+                                   !memcmp(binhdr[i], buffer + hd, len)) {
+                                       patch->is_binary = 1;
+                                       break;
+                               }
+                       }
 
-               if (patch->is_binary && !apply && !check)
-                       ;
-               else
+               /* Empty patch cannot be applied if:
+                * - it is a binary patch and we do not do binary_replace, or
+                * - text patch without metadata change
+                */
+               if ((apply || check) &&
+                   (patch->is_binary
+                    ? !allow_binary_replacement
+                    : !metadata_changes(patch)))
                        die("patch with only garbage at line %d", linenr);
        }
 
@@ -985,13 +1092,8 @@ static int read_old_data(struct stat *st, const char *path, void *buf, unsigned
                        return error("unable to open %s", path);
                got = 0;
                for (;;) {
-                       int ret = read(fd, buf + got, size - got);
-                       if (ret < 0) {
-                               if (errno == EAGAIN)
-                                       continue;
-                               break;
-                       }
-                       if (!ret)
+                       int ret = xread(fd, buf + got, size - got);
+                       if (ret <= 0)
                                break;
                        got += ret;
                }
@@ -1003,7 +1105,7 @@ static int read_old_data(struct stat *st, const char *path, void *buf, unsigned
        }
 }
 
-static int find_offset(const char *buf, unsigned long size, const char *fragment, unsigned long fragsize, int line)
+static int find_offset(const char *buf, unsigned long size, const char *fragment, unsigned long fragsize, int line, int *lines)
 {
        int i;
        unsigned long start, backwards, forwards;
@@ -1064,6 +1166,7 @@ static int find_offset(const char *buf, unsigned long size, const char *fragment
                n = (i >> 1)+1;
                if (i & 1)
                        n = -n;
+               *lines = n;
                return try;
        }
 
@@ -1073,12 +1176,61 @@ static int find_offset(const char *buf, unsigned long size, const char *fragment
        return -1;
 }
 
+static void remove_first_line(const char **rbuf, int *rsize)
+{
+       const char *buf = *rbuf;
+       int size = *rsize;
+       unsigned long offset;
+       offset = 0;
+       while (offset <= size) {
+               if (buf[offset++] == '\n')
+                       break;
+       }
+       *rsize = size - offset;
+       *rbuf = buf + offset;
+}
+
+static void remove_last_line(const char **rbuf, int *rsize)
+{
+       const char *buf = *rbuf;
+       int size = *rsize;
+       unsigned long offset;
+       offset = size - 1;
+       while (offset > 0) {
+               if (buf[--offset] == '\n')
+                       break;
+       }
+       *rsize = offset + 1;
+}
+
 struct buffer_desc {
        char *buffer;
        unsigned long size;
        unsigned long alloc;
 };
 
+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'.
+        */
+       int add_nl_to_tail = 0;
+       if ((new_whitespace == strip_whitespace) &&
+           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++;
+       }
+       memcpy(output, patch + 1, plen);
+       if (add_nl_to_tail)
+               output[plen++] = '\n';
+       return plen;
+}
+
 static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
 {
        char *buf = desc->buffer;
@@ -1086,7 +1238,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
        int offset, size = frag->size;
        char *old = xmalloc(size);
        char *new = xmalloc(size);
+       const char *oldlines, *newlines;
        int oldsize = 0, newsize = 0;
+       unsigned long leading, trailing;
+       int pos, lines;
 
        while (size > 0) {
                int len = linelen(patch, size);
@@ -1114,10 +1269,9 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
                                break;
                /* Fall-through for ' ' */
                case '+':
-                       if (*patch != '+' || !no_add) {
-                               memcpy(new + newsize, patch + 1, plen);
-                               newsize += plen;
-                       }
+                       if (*patch != '+' || !no_add)
+                               newsize += apply_line(new + newsize, patch,
+                                                     plen);
                        break;
                case '@': case '\\':
                        /* Ignore it, we already handled it */
@@ -1129,22 +1283,66 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
                size -= len;
        }
 
-       offset = find_offset(buf, desc->size, old, oldsize, frag->newpos);
-       if (offset >= 0) {
-               int diff = newsize - oldsize;
-               unsigned long size = desc->size + diff;
-               unsigned long alloc = desc->alloc;
+#ifdef NO_ACCURATE_DIFF
+       if (oldsize > 0 && old[oldsize - 1] == '\n' &&
+                       newsize > 0 && new[newsize - 1] == '\n') {
+               oldsize--;
+               newsize--;
+       }
+#endif
+
+       oldlines = old;
+       newlines = new;
+       leading = frag->leading;
+       trailing = frag->trailing;
+       lines = 0;
+       pos = frag->newpos;
+       for (;;) {
+               offset = find_offset(buf, desc->size, oldlines, oldsize, pos, &lines);
+               if (offset >= 0) {
+                       int diff = newsize - oldsize;
+                       unsigned long size = desc->size + diff;
+                       unsigned long alloc = desc->alloc;
+
+                       /* 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",
+                                       leading, trailing, pos + lines);
+
+                       if (size > alloc) {
+                               alloc = size + 8192;
+                               desc->alloc = alloc;
+                               buf = xrealloc(buf, alloc);
+                               desc->buffer = buf;
+                       }
+                       desc->size = size;
+                       memmove(buf + offset + newsize, buf + offset + oldsize, size - offset - newsize);
+                       memcpy(buf + offset, newlines, newsize);
+                       offset = 0;
+
+                       break;
+               }
 
-               if (size > alloc) {
-                       alloc = size + 8192;
-                       desc->alloc = alloc;
-                       buf = xrealloc(buf, alloc);
-                       desc->buffer = buf;
+               /* Am I at my context limits? */
+               if ((leading <= p_context) && (trailing <= p_context))
+                       break;
+               /* Reduce the number of context lines
+                * Reduce both leading and trailing if they are equal
+                * otherwise just reduce the larger context.
+                */
+               if (leading >= trailing) {
+                       remove_first_line(&oldlines, &oldsize);
+                       remove_first_line(&newlines, &newsize);
+                       pos--;
+                       leading--;
+               }
+               if (trailing > leading) {
+                       remove_last_line(&oldlines, &oldsize);
+                       remove_last_line(&newlines, &newsize);
+                       trailing--;
                }
-               desc->size = size;
-               memmove(buf + offset + newsize, buf + offset + oldsize, size - offset - newsize);
-               memcpy(buf + offset, new, newsize);
-               offset = 0;
        }
 
        free(old);
@@ -1155,10 +1353,77 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
 static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
 {
        struct fragment *frag = patch->fragments;
+       const char *name = patch->old_name ? patch->old_name : patch->new_name;
+
+       if (patch->is_binary) {
+               unsigned char sha1[20];
+
+               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.
+                */
+               if (strlen(patch->old_sha1_prefix) != 40 ||
+                   strlen(patch->new_sha1_prefix) != 40 ||
+                   get_sha1_hex(patch->old_sha1_prefix, sha1) ||
+                   get_sha1_hex(patch->new_sha1_prefix, sha1))
+                       return error("cannot apply binary patch to '%s' "
+                                    "without full index line", name);
+
+               if (patch->old_name) {
+                       unsigned char hdr[50];
+                       int hdrlen;
+
+                       /* See if the old one matches what the patch
+                        * applies to.
+                        */
+                       write_sha1_file_prepare(desc->buffer, desc->size,
+                                               blob_type, sha1, hdr, &hdrlen);
+                       if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
+                               return error("the patch applies to '%s' (%s), "
+                                            "which does not match the "
+                                            "current contents.",
+                                            name, sha1_to_hex(sha1));
+               }
+               else {
+                       /* Otherwise, the old one must be empty. */
+                       if (desc->size)
+                               return error("the patch applies to an empty "
+                                            "'%s' but it is not empty", name);
+               }
+
+               /* For now, we do not record post-image data in the patch,
+                * and require the object already present in the recipient's
+                * object database.
+                */
+               if (desc->buffer) {
+                       free(desc->buffer);
+                       desc->alloc = desc->size = 0;
+               }
+               get_sha1_hex(patch->new_sha1_prefix, sha1);
+
+               if (memcmp(sha1, null_sha1, 20)) {
+                       char type[10];
+                       unsigned long size;
+
+                       desc->buffer = read_sha1_file(sha1, type, &size);
+                       if (!desc->buffer)
+                               return error("the necessary postimage %s for "
+                                            "'%s' does not exist",
+                                            patch->new_sha1_prefix, name);
+                       desc->alloc = desc->size = size;
+               }
+
+               return 0;
+       }
 
        while (frag) {
                if (apply_one_fragment(desc, frag) < 0)
-                       return error("patch failed: %s:%ld", patch->old_name, frag->oldpos);
+                       return error("patch failed: %s:%ld",
+                                    name, frag->oldpos);
                frag = frag->next;
        }
        return 0;
@@ -1200,6 +1465,7 @@ static int check_patch(struct patch *patch)
        struct stat st;
        const char *old_name = patch->old_name;
        const char *new_name = patch->new_name;
+       const char *name = old_name ? old_name : new_name;
 
        if (old_name) {
                int changed;
@@ -1223,12 +1489,13 @@ static int check_patch(struct patch *patch)
                                costate.not_new = 0;
                                costate.refresh_cache = 1;
                                if (checkout_entry(active_cache[pos],
-                                                  &costate) ||
+                                                  &costate,
+                                                  NULL) ||
                                    lstat(old_name, &st))
                                        return -1;
                        }
 
-                       changed = ce_match_stat(active_cache[pos], &st);
+                       changed = ce_match_stat(active_cache[pos], &st, 1);
                        if (changed)
                                return error("%s: does not match index",
                                             old_name);
@@ -1274,7 +1541,7 @@ static int check_patch(struct patch *patch)
        }       
 
        if (apply_data(patch, &st) < 0)
-               return error("%s: patch does not apply", old_name);
+               return error("%s: patch does not apply", name);
        return 0;
 }
 
@@ -1455,6 +1722,7 @@ static void remove_file(struct patch *patch)
        if (write_index) {
                if (remove_file_from_cache(patch->old_name) < 0)
                        die("unable to remove %s from index", patch->old_name);
+               cache_tree_invalidate_path(active_cache_tree, patch->old_name);
        }
        unlink(patch->old_name);
 }
@@ -1469,54 +1737,32 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
        if (!write_index)
                return;
 
-       ce = xmalloc(ce_size);
-       memset(ce, 0, ce_size);
+       ce = xcalloc(1, ce_size);
        memcpy(ce->name, path, namelen);
        ce->ce_mode = create_ce_mode(mode);
        ce->ce_flags = htons(namelen);
        if (lstat(path, &st) < 0)
                die("unable to stat newly created file %s", path);
        fill_stat_cache_info(ce, &st);
-       if (write_sha1_file(buf, size, "blob", ce->sha1) < 0)
+       if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
                die("unable to create backing store for newly created file %s", path);
        if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
                die("unable to add cache entry for %s", path);
 }
 
-static void create_subdirectories(const char *path)
-{
-       int len = strlen(path);
-       char *buf = xmalloc(len + 1);
-       const char *slash = path;
-
-       while ((slash = strchr(slash+1, '/')) != NULL) {
-               len = slash - path;
-               memcpy(buf, path, len);
-               buf[len] = 0;
-               if (mkdir(buf, 0777) < 0) {
-                       if (errno != EEXIST)
-                               break;
-               }
-       }
-       free(buf);
-}
-
 static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
 {
        int fd;
 
        if (S_ISLNK(mode))
                return symlink(buf, path);
-       fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_TRUNC, (mode & 0100) ? 0777 : 0666);
+       fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
        if (fd < 0)
                return -1;
        while (size) {
-               int written = write(fd, buf, size);
-               if (written < 0) {
-                       if (errno == EINTR || errno == EAGAIN)
-                               continue;
+               int written = xwrite(fd, buf, size);
+               if (written < 0)
                        die("writing file %s: %s", path, strerror(errno));
-               }
                if (!written)
                        die("out of space writing file %s", path);
                buf += written;
@@ -1532,13 +1778,14 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
  * which is true 99% of the time anyway. If they don't,
  * we create them and try again.
  */
-static void create_one_file(const char *path, unsigned mode, const char *buf, unsigned long size)
+static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size)
 {
        if (!try_create_file(path, mode, buf, size))
                return;
 
        if (errno == ENOENT) {
-               create_subdirectories(path);
+               if (safe_create_leading_directories(path))
+                       return;
                if (!try_create_file(path, mode, buf, size))
                        return;
        }
@@ -1557,22 +1804,24 @@ static void create_one_file(const char *path, unsigned mode, const char *buf, un
                        }
                        if (errno != EEXIST)
                                break;
-               }                       
+                       ++nr;
+               }
        }
        die("unable to write file %s mode %o", path, mode);
 }
 
 static void create_file(struct patch *patch)
 {
-       const char *path = patch->new_name;
+       char *path = patch->new_name;
        unsigned mode = patch->new_mode;
        unsigned long size = patch->resultsize;
        char *buf = patch->result;
 
        if (!mode)
                mode = S_IFREG | 0644;
-       create_one_file(path, mode, buf, size); 
+       create_one_file(path, mode, buf, size);
        add_index_file(path, mode, buf, size);
+       cache_tree_invalidate_path(active_cache_tree, path);
 }
 
 static void write_out_one_result(struct patch *patch)
@@ -1620,10 +1869,16 @@ static int use_patch(struct patch *p)
                        return 0;
                x = x->next;
        }
+       if (0 < prefix_length) {
+               int pathlen = strlen(pathname);
+               if (pathlen <= prefix_length ||
+                   memcmp(prefix, pathname, prefix_length))
+                       return 0;
+       }
        return 1;
 }
 
-static int apply_patch(int fd)
+static int apply_patch(int fd, const char *filename)
 {
        int newfd;
        unsigned long offset, size;
@@ -1631,6 +1886,7 @@ static int apply_patch(int fd)
        struct patch *list = NULL, **listp = &list;
        int skipped_patch = 0;
 
+       patch_input_file = filename;
        if (!buffer)
                return -1;
        offset = 0;
@@ -1638,8 +1894,7 @@ static int apply_patch(int fd)
                struct patch *patch;
                int nr;
 
-               patch = xmalloc(sizeof(*patch));
-               memset(patch, 0, sizeof(*patch));
+               patch = xcalloc(1, sizeof(*patch));
                nr = parse_chunk(buffer + offset, size, patch);
                if (nr < 0)
                        break;
@@ -1657,12 +1912,16 @@ static int apply_patch(int fd)
        }
 
        newfd = -1;
+       if (whitespace_error && (new_whitespace == error_on_whitespace))
+               apply = 0;
+
        write_index = check_index && apply;
        if (write_index)
                newfd = hold_index_file_for_update(&cache_file, get_index_file());
        if (check_index) {
-               if (read_cache() < 0)
+               if (read_cache_1(active_cache_sha1) < 0)
                        die("unable to read index file");
+               active_cache_tree = read_cache_tree(active_cache_sha1);
        }
 
        if ((check || apply) && check_patch_list(list) < 0)
@@ -1672,9 +1931,11 @@ static int apply_patch(int fd)
                write_out_results(list, skipped_patch);
 
        if (write_index) {
-               if (write_cache(newfd, active_cache, active_nr) ||
+               if (write_cache_1(newfd, active_cache, active_nr,
+                                 active_cache_sha1) ||
                    commit_index_file(&cache_file))
                        die("Unable to write new cachefile");
+               write_cache_tree(active_cache_sha1, active_cache_tree);
        }
 
        if (show_index_info)
@@ -1693,17 +1954,29 @@ static int apply_patch(int fd)
        return 0;
 }
 
+static int git_apply_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "apply.whitespace")) {
+               apply_default_whitespace = strdup(value);
+               return 0;
+       }
+       return git_default_config(var, value);
+}
+
+
 int main(int argc, char **argv)
 {
        int i;
        int read_stdin = 1;
+       const char *whitespace_option = NULL;
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
+               char *end;
                int fd;
 
                if (!strcmp(arg, "-")) {
-                       apply_patch(0);
+                       apply_patch(0, "<stdin>");
                        read_stdin = 0;
                        continue;
                }
@@ -1714,6 +1987,10 @@ int main(int argc, char **argv)
                        excludes = x;
                        continue;
                }
+               if (!strncmp(arg, "-p", 2)) {
+                       p_value = atoi(arg + 2);
+                       continue;
+               }
                if (!strcmp(arg, "--no-add")) {
                        no_add = 1;
                        continue;
@@ -1723,6 +2000,10 @@ int main(int argc, char **argv)
                        diffstat = 1;
                        continue;
                }
+               if (!strcmp(arg, "--allow-binary-replacement")) {
+                       allow_binary_replacement = 1;
+                       continue;
+               }
                if (!strcmp(arg, "--numstat")) {
                        apply = 0;
                        numstat = 1;
@@ -1755,14 +2036,64 @@ int main(int argc, char **argv)
                        line_termination = 0;
                        continue;
                }
+               if (!strncmp(arg, "-C", 2)) {
+                       p_context = strtoul(arg + 2, &end, 0);
+                       if (*end != '\0')
+                               die("unrecognized context count '%s'", arg + 2);
+                       continue;
+               }
+               if (!strncmp(arg, "--whitespace=", 13)) {
+                       whitespace_option = arg + 13;
+                       parse_whitespace_option(arg + 13);
+                       continue;
+               }
+
+               if (check_index && prefix_length < 0) {
+                       prefix = setup_git_directory();
+                       prefix_length = prefix ? strlen(prefix) : 0;
+                       git_config(git_apply_config);
+                       if (!whitespace_option && apply_default_whitespace)
+                               parse_whitespace_option(apply_default_whitespace);
+               }
+               if (0 < prefix_length)
+                       arg = prefix_filename(prefix, prefix_length, arg);
+
                fd = open(arg, O_RDONLY);
                if (fd < 0)
                        usage(apply_usage);
                read_stdin = 0;
-               apply_patch(fd);
+               set_default_whitespace_mode(whitespace_option);
+               apply_patch(fd, arg);
                close(fd);
        }
+       set_default_whitespace_mode(whitespace_option);
        if (read_stdin)
-               apply_patch(0);
+               apply_patch(0, "<stdin>");
+       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",
+                               squelched,
+                               squelched == 1 ? "" : "s");
+               }
+               if (new_whitespace == error_on_whitespace)
+                       die("%d line%s add%s trailing whitespaces.",
+                           whitespace_error,
+                           whitespace_error == 1 ? "" : "s",
+                           whitespace_error == 1 ? "s" : "");
+               if (applied_after_stripping)
+                       fprintf(stderr, "warning: %d line%s applied after"
+                               " stripping trailing whitespaces.\n",
+                               applied_after_stripping,
+                               applied_after_stripping == 1 ? "" : "s");
+               else if (whitespace_error)
+                       fprintf(stderr, "warning: %d line%s add%s trailing"
+                               " whitespaces.\n",
+                               whitespace_error,
+                               whitespace_error == 1 ? "" : "s",
+                               whitespace_error == 1 ? "s" : "");
+       }
        return 0;
 }