apply: handle type-changing patch correctly.
[gitweb.git] / builtin-apply.c
index 6dd0472ae07cac291d26b9e547a92c7c51d569df..8f7cf44c6945b8cfd41a36134374192e92b9789f 100644 (file)
 #include "delta.h"
 #include "builtin.h"
 
-//  --check turns on checking that the working tree matches the
-//    files that are being modified, but doesn't apply the patch
-//  --stat does just a diffstat, and doesn't actually apply
-//  --numstat does numeric diffstat, and doesn't actually apply
-//  --index-info shows the old and new index info for paths if available.
-//  --index updates the cache as well.
-//  --cached updates only the cache without ever touching the working tree.
-//
+/*
+ *  --check turns on checking that the working tree matches the
+ *    files that are being modified, but doesn't apply the patch
+ *  --stat does just a diffstat, and doesn't actually apply
+ *  --numstat does numeric diffstat, and doesn't actually apply
+ *  --index-info shows the old and new index info for paths if available.
+ *  --index updates the cache as well.
+ *  --cached updates only the cache without ever touching the working tree.
+ */
 static const char *prefix;
 static int prefix_length = -1;
 static int newfd = -1;
@@ -125,6 +126,7 @@ struct patch {
        unsigned long deflate_origlen;
        int lines_added, lines_deleted;
        int score;
+       int inaccurate_eof:1;
        struct fragment *fragments;
        char *result;
        unsigned long resultsize;
@@ -283,8 +285,8 @@ static void parse_traditional_patch(const char *first, const char *second, struc
 {
        char *name;
 
-       first += 4;     // skip "--- "
-       second += 4;    // skip "+++ "
+       first += 4;     /* skip "--- " */
+       second += 4;    /* skip "+++ " */
        if (is_dev_null(first)) {
                patch->is_new = 1;
                patch->is_delete = 0;
@@ -764,7 +766,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
                        continue;
 
                /*
-                * Make sure we don't find any unconnected patch fragmants.
+                * Make sure we don't find any unconnected patch fragments.
                 * That's a sign that we didn't find a header, and that a
                 * patch has become corrupted/broken up.
                 */
@@ -989,7 +991,7 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
         * 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 signalled with an empty line.
+        * The end of binary is signaled with an empty line.
         */
        int llen, used;
        struct fragment *fragment;
@@ -1333,7 +1335,8 @@ static int apply_line(char *output, const char *patch, int plen)
        return plen;
 }
 
-static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
+static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
+       int inaccurate_eof)
 {
        int match_beginning, match_end;
        char *buf = desc->buffer;
@@ -1386,13 +1389,11 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
                size -= len;
        }
 
-#ifdef NO_ACCURATE_DIFF
-       if (oldsize > 0 && old[oldsize - 1] == '\n' &&
+       if (inaccurate_eof && oldsize > 0 && old[oldsize - 1] == '\n' &&
                        newsize > 0 && new[newsize - 1] == '\n') {
                oldsize--;
                newsize--;
        }
-#endif
 
        oldlines = old;
        newlines = new;
@@ -1614,7 +1615,7 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
                return apply_binary(desc, patch);
 
        while (frag) {
-               if (apply_one_fragment(desc, frag) < 0)
+               if (apply_one_fragment(desc, frag, patch->inaccurate_eof) < 0)
                        return error("patch failed: %s:%ld",
                                     name, frag->oldpos);
                frag = frag->next;
@@ -1663,13 +1664,14 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
        return 0;
 }
 
-static int check_patch(struct patch *patch)
+static int check_patch(struct patch *patch, struct patch *prev_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;
        struct cache_entry *ce = NULL;
+       int ok_if_exists;
 
        if (old_name) {
                int changed = 0;
@@ -1727,13 +1729,33 @@ static int check_patch(struct patch *patch)
                                old_name, st_mode, patch->old_mode);
        }
 
+       if (new_name && prev_patch && 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
+                * create new (see diff.c::run_diff()); in such a case
+                * it is Ok that the entry to be deleted by the
+                * previous patch is still in the working tree and in
+                * the index.
+                */
+               ok_if_exists = 1;
+       else
+               ok_if_exists = 0;
+
        if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
-               if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0)
+               if (check_index &&
+                   cache_name_pos(new_name, strlen(new_name)) >= 0 &&
+                   !ok_if_exists)
                        return error("%s: already exists in index", new_name);
                if (!cached) {
-                       if (!lstat(new_name, &st))
-                               return error("%s: already exists in working directory", new_name);
-                       if (errno != ENOENT)
+                       struct stat nst;
+                       if (!lstat(new_name, &nst)) {
+                               if (S_ISDIR(nst.st_mode) || ok_if_exists)
+                                       ; /* ok */
+                               else
+                                       return error("%s: already exists in working directory", new_name);
+                       }
+                       else if ((errno != ENOENT) && (errno != ENOTDIR))
                                return error("%s: %s", new_name, strerror(errno));
                }
                if (!patch->new_mode) {
@@ -1761,10 +1783,13 @@ static int check_patch(struct patch *patch)
 
 static int check_patch_list(struct patch *patch)
 {
+       struct patch *prev_patch = NULL;
        int error = 0;
 
-       for (;patch ; patch = patch->next)
-               error |= check_patch(patch);
+       for (prev_patch = NULL; patch ; patch = patch->next) {
+               error |= check_patch(patch, prev_patch);
+               prev_patch = patch;
+       }
        return error;
 }
 
@@ -2009,6 +2034,16 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
                        return;
        }
 
+       if (errno == EEXIST) {
+               /* We may be trying to create a file where a directory
+                * used to be.
+                */
+               struct stat st;
+               errno = 0;
+               if (!lstat(path, &st) && S_ISDIR(st.st_mode) && !rmdir(path))
+                       errno = EEXIST;
+       }
+
        if (errno == EEXIST) {
                unsigned int nr = getpid();
 
@@ -2043,32 +2078,42 @@ static void create_file(struct patch *patch)
        cache_tree_invalidate_path(active_cache_tree, path);
 }
 
-static void write_out_one_result(struct patch *patch)
+/* phase zero is to remove, phase one is to create */
+static void write_out_one_result(struct patch *patch, int phase)
 {
        if (patch->is_delete > 0) {
-               remove_file(patch);
+               if (phase == 0)
+                       remove_file(patch);
                return;
        }
        if (patch->is_new > 0 || patch->is_copy) {
-               create_file(patch);
+               if (phase == 1)
+                       create_file(patch);
                return;
        }
        /*
         * Rename or modification boils down to the same
         * thing: remove the old, write the new
         */
-       remove_file(patch);
+       if (phase == 0)
+               remove_file(patch);
+       if (phase == 1)
        create_file(patch);
 }
 
 static void write_out_results(struct patch *list, int skipped_patch)
 {
+       int phase;
+
        if (!list && !skipped_patch)
                die("No changes");
 
-       while (list) {
-               write_out_one_result(list);
-               list = list->next;
+       for (phase = 0; phase < 2; phase++) {
+               struct patch *l = list;
+               while (l) {
+                       write_out_one_result(l, phase);
+                       l = l->next;
+               }
        }
 }
 
@@ -2097,7 +2142,7 @@ static int use_patch(struct patch *p)
        return 1;
 }
 
-static int apply_patch(int fd, const char *filename)
+static int apply_patch(int fd, const char *filename, int inaccurate_eof)
 {
        unsigned long offset, size;
        char *buffer = read_patch_file(fd, &size);
@@ -2113,6 +2158,7 @@ static int apply_patch(int fd, const char *filename)
                int nr;
 
                patch = xcalloc(1, sizeof(*patch));
+               patch->inaccurate_eof = inaccurate_eof;
                nr = parse_chunk(buffer + offset, size, patch);
                if (nr < 0)
                        break;
@@ -2180,6 +2226,8 @@ int cmd_apply(int argc, const char **argv, char **envp)
 {
        int i;
        int read_stdin = 1;
+       int inaccurate_eof = 0;
+
        const char *whitespace_option = NULL;
 
        for (i = 1; i < argc; i++) {
@@ -2188,7 +2236,7 @@ int cmd_apply(int argc, const char **argv, char **envp)
                int fd;
 
                if (!strcmp(arg, "-")) {
-                       apply_patch(0, "<stdin>");
+                       apply_patch(0, "<stdin>", inaccurate_eof);
                        read_stdin = 0;
                        continue;
                }
@@ -2265,6 +2313,10 @@ int cmd_apply(int argc, const char **argv, char **envp)
                        parse_whitespace_option(arg + 13);
                        continue;
                }
+               if (!strcmp(arg, "--inaccurate-eof")) {
+                       inaccurate_eof = 1;
+                       continue;
+               }
 
                if (check_index && prefix_length < 0) {
                        prefix = setup_git_directory();
@@ -2281,12 +2333,12 @@ int cmd_apply(int argc, const char **argv, char **envp)
                        usage(apply_usage);
                read_stdin = 0;
                set_default_whitespace_mode(whitespace_option);
-               apply_patch(fd, arg);
+               apply_patch(fd, arg, inaccurate_eof);
                close(fd);
        }
        set_default_whitespace_mode(whitespace_option);
        if (read_stdin)
-               apply_patch(0, "<stdin>");
+               apply_patch(0, "<stdin>", inaccurate_eof);
        if (whitespace_error) {
                if (squelch_whitespace_errors &&
                    squelch_whitespace_errors < whitespace_error) {
@@ -2316,7 +2368,7 @@ int cmd_apply(int argc, const char **argv, char **envp)
 
        if (write_index) {
                if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_lock_file(&lock_file))
+                   close(newfd) || commit_lock_file(&lock_file))
                        die("Unable to write new index file");
        }