GIT 1.5.2.5
[gitweb.git] / builtin-apply.c
index 27a182bfaa826711b2e6c4c775fbd6e83b243f7e..9ee93936627b4df97ee5fea3af74ba0739becb98 100644 (file)
@@ -30,7 +30,7 @@ static int unidiff_zero;
 static int p_value = 1;
 static int p_value_known;
 static int check_index;
-static int write_index;
+static int update_index;
 static int cached;
 static int diffstat;
 static int numstat;
@@ -185,7 +185,7 @@ static void *read_patch_file(int fd, unsigned long *sizep)
        void *buffer = xmalloc(alloc);
 
        for (;;) {
-               int nr = alloc - size;
+               ssize_t nr = alloc - size;
                if (nr < 1024) {
                        alloc += CHUNKSIZE;
                        buffer = xrealloc(buffer, alloc);
@@ -417,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, TERM_TAB);
+               return find_name(line, NULL, p_value, TERM_TAB);
 
        if (orig_name) {
                int len;
@@ -427,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, TERM_TAB);
+               another = find_name(line, NULL, p_value, TERM_TAB);
                if (!another || memcmp(another, name, len))
                        die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
                free(another);
@@ -1003,12 +1003,16 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
                        trailing++;
                        break;
                case '-':
+                       if (apply_in_reverse &&
+                                       new_whitespace != nowarn_whitespace)
+                               check_whitespace(line, len);
                        deleted++;
                        oldlines--;
                        trailing = 0;
                        break;
                case '+':
-                       if (new_whitespace != nowarn_whitespace)
+                       if (!apply_in_reverse &&
+                                       new_whitespace != nowarn_whitespace)
                                check_whitespace(line, len);
                        added++;
                        newlines--;
@@ -1468,15 +1472,15 @@ static int read_old_data(struct stat *st, const char *path, char **buf_p, unsign
                        return error("unable to open %s", path);
                got = 0;
                for (;;) {
-                       int ret = xread(fd, buf + got, size - got);
+                       ssize_t ret = xread(fd, buf + got, size - got);
                        if (ret <= 0)
                                break;
                        got += ret;
                }
                close(fd);
                nsize = got;
-               nbuf = buf;
-               if (convert_to_git(path, &nbuf, &nsize)) {
+               nbuf = convert_to_git(path, buf, &nsize);
+               if (nbuf) {
                        free(buf);
                        *buf_p = nbuf;
                        *alloc_p = nsize;
@@ -2009,6 +2013,29 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
        return 0;
 }
 
+static int check_to_create_blob(const char *new_name, int ok_if_exists)
+{
+       struct stat nst;
+       if (!lstat(new_name, &nst)) {
+               if (S_ISDIR(nst.st_mode) || ok_if_exists)
+                       return 0;
+               /*
+                * A leading component of new_name might be a symlink
+                * that is going to be removed with this patch, but
+                * still pointing at somewhere that has the path.
+                * In such a case, path "new_name" does not exist as
+                * far as git is concerned.
+                */
+               if (has_symlink_leading_path(new_name, NULL))
+                       return 0;
+
+               return error("%s: already exists in working directory", new_name);
+       }
+       else if ((errno != ENOENT) && (errno != ENOTDIR))
+               return error("%s: %s", new_name, strerror(errno));
+       return 0;
+}
+
 static int check_patch(struct patch *patch, struct patch *prev_patch)
 {
        struct stat st;
@@ -2095,15 +2122,9 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
                    !ok_if_exists)
                        return error("%s: already exists in index", new_name);
                if (!cached) {
-                       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));
+                       int err = check_to_create_blob(new_name, ok_if_exists);
+                       if (err)
+                               return err;
                }
                if (!patch->new_mode) {
                        if (0 < patch->is_new)
@@ -2308,7 +2329,7 @@ static void patch_stats(struct patch *patch)
 
 static void remove_file(struct patch *patch, int rmdir_empty)
 {
-       if (write_index) {
+       if (update_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);
@@ -2335,7 +2356,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
        int namelen = strlen(path);
        unsigned ce_size = cache_entry_size(namelen);
 
-       if (!write_index)
+       if (!update_index)
                return;
 
        ce = xcalloc(1, ce_size);
@@ -2355,9 +2376,8 @@ 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, converted;
+       int fd;
        char *nbuf;
-       unsigned long nsize;
 
        if (has_symlinks && S_ISLNK(mode))
                /* Although buf:size is counted string, it also is NUL
@@ -2369,13 +2389,10 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
        if (fd < 0)
                return -1;
 
-       nsize = size;
-       nbuf = (char *) buf;
-       converted = convert_to_working_tree(path, &nbuf, &nsize);
-       if (converted) {
+       nbuf = convert_to_working_tree(path, buf, &size);
+       if (nbuf)
                buf = nbuf;
-               size = nsize;
-       }
+
        while (size) {
                int written = xwrite(fd, buf, size);
                if (written < 0)
@@ -2387,7 +2404,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
        }
        if (close(fd) < 0)
                die("closing file %s: %s", path, strerror(errno));
-       if (converted)
+       if (nbuf)
                free(nbuf);
        return 0;
 }
@@ -2416,8 +2433,7 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
                 * used to be.
                 */
                struct stat st;
-               errno = 0;
-               if (!lstat(path, &st) && S_ISDIR(st.st_mode) && !rmdir(path))
+               if (!lstat(path, &st) && (!S_ISDIR(st.st_mode) || !rmdir(path)))
                        errno = EEXIST;
        }
 
@@ -2473,7 +2489,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, 0);
+               remove_file(patch, patch->is_rename);
        if (phase == 1)
                create_file(patch);
 }
@@ -2662,10 +2678,10 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
        if (whitespace_error && (new_whitespace == error_on_whitespace))
                apply = 0;
 
-       write_index = check_index && apply;
-       if (write_index && newfd < 0)
-               newfd = hold_lock_file_for_update(&lock_file,
-                                                 get_index_file(), 1);
+       update_index = check_index && apply;
+       if (update_index && newfd < 0)
+               newfd = hold_locked_index(&lock_file, 1);
+
        if (check_index) {
                if (read_cache() < 0)
                        die("unable to read index file");
@@ -2870,9 +2886,9 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
                                whitespace_error == 1 ? "s" : "");
        }
 
-       if (write_index) {
+       if (update_index) {
                if (write_cache(newfd, active_cache, active_nr) ||
-                   close(newfd) || commit_lock_file(&lock_file))
+                   close(newfd) || commit_locked_index(&lock_file))
                        die("Unable to write new index file");
        }