Merge branch 'maint'
[gitweb.git] / builtin-apply.c
index 872c8005a27c0085d47680a8e638b8cf6f3d70a3..38f647510a13f51e63a35aaf442aeb154e34915c 100644 (file)
@@ -6,7 +6,6 @@
  * This applies patches on top of some (arbitrary) version of the SCM.
  *
  */
-#include <fnmatch.h>
 #include "cache.h"
 #include "cache-tree.h"
 #include "quote.h"
@@ -27,8 +26,9 @@ 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;
+static int p_value_known;
 static int check_index;
 static int write_index;
 static int cached;
@@ -43,7 +43,7 @@ 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 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|error|error-all|strip>] <patch>...";
 
@@ -140,12 +140,16 @@ struct fragment {
 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;
+       int is_new, is_delete;  /* -1 = unknown, 0 = false, 1 = true */
        int rejected;
        unsigned long deflate_origlen;
        int lines_added, lines_deleted;
        int score;
-       int inaccurate_eof:1;
+       unsigned int is_toplevel_relative:1;
+       unsigned int inaccurate_eof:1;
+       unsigned int is_binary:1;
+       unsigned int is_copy:1;
+       unsigned int is_rename:1;
        struct fragment *fragments;
        char *result;
        unsigned long resultsize;
@@ -236,7 +240,7 @@ static int name_terminate(const char *name, int namelen, int c, int terminate)
        return 1;
 }
 
-static char * find_name(const char *line, char *def, int p_value, int terminate)
+static char *find_name(const char *line, char *def, int p_value, int terminate)
 {
        int len;
        const char *start = line;
@@ -309,11 +313,54 @@ static char * find_name(const char *line, char *def, int p_value, int terminate)
        return name;
 }
 
+static int count_slashes(const char *cp)
+{
+       int cnt = 0;
+       char ch;
+
+       while ((ch = *cp++))
+               if (ch == '/')
+                       cnt++;
+       return cnt;
+}
+
+/*
+ * Given the string after "--- " or "+++ ", guess the appropriate
+ * p_value for the given patch.
+ */
+static int guess_p_value(const char *nameline)
+{
+       char *name, *cp;
+       int val = -1;
+
+       if (is_dev_null(nameline))
+               return -1;
+       name = find_name(nameline, NULL, 0, TERM_SPACE | TERM_TAB);
+       if (!name)
+               return -1;
+       cp = strchr(name, '/');
+       if (!cp)
+               val = 0;
+       else if (prefix) {
+               /*
+                * Does it begin with "a/$our-prefix" and such?  Then this is
+                * very likely to apply to our directory.
+                */
+               if (!strncmp(name, prefix, prefix_length))
+                       val = count_slashes(prefix);
+               else {
+                       cp++;
+                       if (!strncmp(cp, prefix, prefix_length))
+                               val = count_slashes(prefix) + 1;
+               }
+       }
+       free(name);
+       return val;
+}
+
 /*
  * Get the name etc info from the --/+++ lines of a traditional patch header
  *
- * NOTE! This hardcodes "-p1" behaviour in filename detection.
- *
  * FIXME! The end-of-filename heuristics are kind of screwy. For existing
  * files, we can happily check the index for a match, but for creating a
  * new file we should try to match whatever "patch" does. I have no idea.
@@ -324,6 +371,16 @@ static void parse_traditional_patch(const char *first, const char *second, struc
 
        first += 4;     /* skip "--- " */
        second += 4;    /* skip "+++ " */
+       if (!p_value_known) {
+               int p, q;
+               p = guess_p_value(first);
+               q = guess_p_value(second);
+               if (p < 0) p = q;
+               if (0 <= p && p == q) {
+                       p_value = p;
+                       p_value_known = 1;
+               }
+       }
        if (is_dev_null(first)) {
                patch->is_new = 1;
                patch->is_delete = 0;
@@ -360,7 +417,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;
@@ -370,7 +427,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);
@@ -785,6 +842,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
 {
        unsigned long offset, len;
 
+       patch->is_toplevel_relative = 0;
        patch->is_rename = patch->is_copy = 0;
        patch->is_new = patch->is_delete = -1;
        patch->old_mode = patch->new_mode = 0;
@@ -809,7 +867,8 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
                        struct fragment dummy;
                        if (parse_fragment_header(line, len, &dummy) < 0)
                                continue;
-                       error("patch fragment without header at line %d: %.*s", linenr, (int)len-1, line);
+                       die("patch fragment without header at line %d: %.*s",
+                           linenr, (int)len-1, line);
                }
 
                if (size < len + 6)
@@ -828,6 +887,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
                                        die("git diff header lacks filename information (line %d)", linenr);
                                patch->old_name = patch->new_name = patch->def_name;
                        }
+                       patch->is_toplevel_relative = 1;
                        *hdrsize = git_hdr_len;
                        return offset;
                }
@@ -854,12 +914,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)
 {
@@ -876,31 +978,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);
@@ -909,6 +994,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--;
@@ -922,25 +1008,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;
@@ -973,12 +1042,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)) {
@@ -989,9 +1064,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;
@@ -1000,6 +1077,50 @@ 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;
+                       patch->old_name = NULL;
+               }
+               if (patch->is_delete < 0 && !newlines) {
+                       patch->is_delete = 1;
+                       patch->new_name = NULL;
+               }
+       }
+
+       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;
 }
 
@@ -1065,11 +1186,11 @@ static struct fragment *parse_binary_hunk(char **buf_p,
 
        *status_p = 0;
 
-       if (!strncmp(buffer, "delta ", 6)) {
+       if (!prefixcmp(buffer, "delta ")) {
                patch_method = BINARY_DELTA_DEFLATED;
                origlen = strtoul(buffer + 6, NULL, 10);
        }
-       else if (!strncmp(buffer, "literal ", 8)) {
+       else if (!prefixcmp(buffer, "literal ")) {
                patch_method = BINARY_LITERAL_DEFLATED;
                origlen = strtoul(buffer + 8, NULL, 10);
        }
@@ -1228,14 +1349,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);
        }
 
@@ -1331,28 +1450,39 @@ static void show_stats(struct patch *patch)
        free(qname);
 }
 
-static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
+static int read_old_data(struct stat *st, const char *path, char **buf_p, unsigned long *alloc_p, unsigned long *size_p)
 {
        int fd;
        unsigned long got;
+       unsigned long nsize;
+       char *nbuf;
+       unsigned long size = *size_p;
+       char *buf = *buf_p;
 
        switch (st->st_mode & S_IFMT) {
        case S_IFLNK:
-               return readlink(path, buf, size);
+               return readlink(path, buf, size) != size;
        case S_IFREG:
                fd = open(path, O_RDONLY);
                if (fd < 0)
                        return error("unable to open %s", path);
                got = 0;
                for (;;) {
-                       int ret = xread(fd, (char *) buf + got, size - got);
+                       int ret = xread(fd, buf + got, size - got);
                        if (ret <= 0)
                                break;
                        got += ret;
                }
                close(fd);
-               return got;
-
+               nsize = got;
+               nbuf = buf;
+               if (convert_to_git(path, &nbuf, &nsize)) {
+                       free(buf);
+                       *buf_p = nbuf;
+                       *alloc_p = nsize;
+                       *size_p = nsize;
+               }
+               return got != size;
        default:
                return -1;
        }
@@ -1466,22 +1596,69 @@ 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 ||
+           *patch != '+') {
+               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;
+       }
+
+       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;
        }
-       memcpy(output, patch + 1, plen);
+       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 inaccurate_eof)
@@ -1523,6 +1700,14 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                                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);
@@ -1539,6 +1724,8 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                        /* Ignore it, we already handled it */
                        break;
                default:
+                       if (apply_verbosely)
+                               error("invalid start of line: '%c'", first);
                        return -1;
                }
                patch += len;
@@ -1559,9 +1746,19 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
        /*
         * 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;
@@ -1626,6 +1823,9 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                }
        }
 
+       if (offset && apply_verbosely)
+               error("while searching for:\n%.*s", oldsize, oldlines);
+
        free(old);
        free(new);
        return offset;
@@ -1673,13 +1873,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.
@@ -1695,8 +1888,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 "
@@ -1720,11 +1912,11 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
 
        if (has_sha1_file(sha1)) {
                /* We already have the postimage */
-               char type[10];
+               enum object_type type;
                unsigned long size;
 
                free(desc->buffer);
-               desc->buffer = read_sha1_file(sha1, type, &size);
+               desc->buffer = read_sha1_file(sha1, &type, &size);
                if (!desc->buffer)
                        return error("the necessary postimage %s for "
                                     "'%s' cannot be read",
@@ -1741,8 +1933,7 @@ 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 (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1));
        }
@@ -1781,8 +1972,8 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
        buf = NULL;
        if (cached) {
                if (ce) {
-                       char type[20];
-                       buf = read_sha1_file(ce->sha1, type, &size);
+                       enum object_type type;
+                       buf = read_sha1_file(ce->sha1, &type, &size);
                        if (!buf)
                                return error("read of %s failed",
                                             patch->old_name);
@@ -1793,7 +1984,7 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
                size = st->st_size;
                alloc = size + 8192;
                buf = xmalloc(alloc);
-               if (read_old_data(st, patch->old_name, buf, alloc) != size)
+               if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
                        return error("read of %s failed", patch->old_name);
        }
 
@@ -1812,7 +2003,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;
@@ -1871,7 +2062,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
                        return error("%s: %s", old_name, strerror(errno));
 
                if (!cached)
-                       st_mode = ntohl(create_ce_mode(st.st_mode));
+                       st_mode = ntohl(ce_mode_from_stat(ce, st.st_mode));
 
                if (patch->is_new < 0)
                        patch->is_new = 0;
@@ -1884,7 +2075,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
@@ -1897,7 +2088,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)
@@ -1914,7 +2106,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;
@@ -1965,7 +2157,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).",
@@ -2001,12 +2193,16 @@ static void numstat_patch_list(struct patch *patch)
        for ( ; patch; patch = patch->next) {
                const char *name;
                name = patch->new_name ? patch->new_name : patch->old_name;
-               printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
+               if (patch->is_binary)
+                       printf("-\t-\t");
+               else
+                       printf("%d\t%d\t",
+                              patch->lines_added, patch->lines_deleted);
                if (line_termination && quote_c_style(name, NULL, NULL, 0))
                        quote_c_style(name, NULL, stdout, 0);
                else
                        fputs(name, stdout);
-               putchar('\n');
+               putchar(line_termination);
        }
 }
 
@@ -2110,15 +2306,26 @@ static void patch_stats(struct patch *patch)
        }
 }
 
-static void remove_file(struct patch *patch)
+static void remove_file(struct patch *patch, int rmdir_empty)
 {
        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);
        }
-       if (!cached)
-               unlink(patch->old_name);
+       if (!cached) {
+               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);
+               }
+       }
 }
 
 static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)
@@ -2149,12 +2356,22 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
 static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
 {
        int fd;
+       char *nbuf;
+       unsigned long nsize;
 
        if (S_ISLNK(mode))
                /* Although buf:size is counted string, it also is NUL
                 * terminated.
                 */
                return symlink(buf, path);
+       nsize = size;
+       nbuf = (char *) buf;
+       if (convert_to_working_tree(path, &nbuf, &nsize)) {
+               free((char *) buf);
+               buf = nbuf;
+               size = nsize;
+       }
+
        fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
        if (fd < 0)
                return -1;
@@ -2240,7 +2457,7 @@ static void write_out_one_result(struct patch *patch, int phase)
 {
        if (patch->is_delete > 0) {
                if (phase == 0)
-                       remove_file(patch);
+                       remove_file(patch, 1);
                return;
        }
        if (patch->is_new > 0 || patch->is_copy) {
@@ -2253,7 +2470,7 @@ static void write_out_one_result(struct patch *patch, int phase)
         * thing: remove the old, write the new
         */
        if (phase == 0)
-               remove_file(patch);
+               remove_file(patch, 0);
        if (phase == 1)
                create_file(patch);
 }
@@ -2375,6 +2592,32 @@ static int use_patch(struct patch *p)
        return 1;
 }
 
+static void prefix_one(char **name)
+{
+       char *old_name = *name;
+       if (!old_name)
+               return;
+       *name = xstrdup(prefix_filename(prefix, prefix_length, *name));
+       free(old_name);
+}
+
+static void prefix_patches(struct patch *p)
+{
+       if (!prefix || p->is_toplevel_relative)
+               return;
+       for ( ; p; p = p->next) {
+               if (p->new_name == p->old_name) {
+                       char *prefixed = p->new_name;
+                       prefix_one(&prefixed);
+                       p->new_name = p->old_name = prefixed;
+               }
+               else {
+                       prefix_one(&p->new_name);
+                       prefix_one(&p->old_name);
+               }
+       }
+}
+
 static int apply_patch(int fd, const char *filename, int inaccurate_eof)
 {
        unsigned long offset, size;
@@ -2397,11 +2640,14 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
                        break;
                if (apply_in_reverse)
                        reverse_patches(patch);
+               if (prefix)
+                       prefix_patches(patch);
                if (use_patch(patch)) {
                        patch_stats(patch);
                        *listp = patch;
                        listp = &patch->next;
-               } else {
+               }
+               else {
                        /* perhaps free it a bit better? */
                        free(patch);
                        skipped_patch++;
@@ -2456,15 +2702,22 @@ static int git_apply_config(const char *var, const char *value)
 }
 
 
-int cmd_apply(int argc, const char **argv, const char *prefix)
+int cmd_apply(int argc, const char **argv, const char *unused_prefix)
 {
        int i;
        int read_stdin = 1;
        int inaccurate_eof = 0;
        int errs = 0;
+       int is_not_gitdir = 0;
 
        const char *whitespace_option = NULL;
 
+       prefix = setup_git_directory_gently(&is_not_gitdir);
+       prefix_length = prefix ? strlen(prefix) : 0;
+       git_config(git_apply_config);
+       if (apply_default_whitespace)
+               parse_whitespace_option(apply_default_whitespace);
+
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
                char *end;
@@ -2475,15 +2728,16 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        read_stdin = 0;
                        continue;
                }
-               if (!strncmp(arg, "--exclude=", 10)) {
+               if (!prefixcmp(arg, "--exclude=")) {
                        struct excludes *x = xmalloc(sizeof(*x));
                        x->path = arg + 10;
                        x->next = excludes;
                        excludes = x;
                        continue;
                }
-               if (!strncmp(arg, "-p", 2)) {
+               if (!prefixcmp(arg, "-p")) {
                        p_value = atoi(arg + 2);
+                       p_value_known = 1;
                        continue;
                }
                if (!strcmp(arg, "--no-add")) {
@@ -2497,8 +2751,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;
@@ -2516,10 +2769,14 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        continue;
                }
                if (!strcmp(arg, "--index")) {
+                       if (is_not_gitdir)
+                               die("--index outside a repository");
                        check_index = 1;
                        continue;
                }
                if (!strcmp(arg, "--cached")) {
+                       if (is_not_gitdir)
+                               die("--cached outside a repository");
                        check_index = 1;
                        cached = 1;
                        continue;
@@ -2537,13 +2794,13 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        line_termination = 0;
                        continue;
                }
-               if (!strncmp(arg, "-C", 2)) {
+               if (!prefixcmp(arg, "-C")) {
                        p_context = strtoul(arg + 2, &end, 0);
                        if (*end != '\0')
                                die("unrecognized context count '%s'", arg + 2);
                        continue;
                }
-               if (!strncmp(arg, "--whitespace=", 13)) {
+               if (!prefixcmp(arg, "--whitespace=")) {
                        whitespace_option = arg + 13;
                        parse_whitespace_option(arg + 13);
                        continue;
@@ -2552,11 +2809,15 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        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")) {
+               if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose")) {
                        apply_verbosely = 1;
                        continue;
                }
@@ -2564,14 +2825,6 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        inaccurate_eof = 1;
                        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);