git-push: documentation and tests for pushing only branches
[gitweb.git] / builtin-apply.c
index 490e23ef40c2b2d16272c67a67a14a1eb03b3877..5ad371424b8019a97d1fb34045bb8e32134870aa 100644 (file)
@@ -55,7 +55,7 @@ static enum whitespace_eol {
 } new_whitespace = warn_on_whitespace;
 static int whitespace_error;
 static int squelch_whitespace_errors = 5;
-static int applied_after_stripping;
+static int applied_after_fixing_ws;
 static const char *patch_input_file;
 
 static void parse_whitespace_option(const char *option)
@@ -1514,7 +1514,8 @@ static int find_offset(const char *buf, unsigned long size, const char *fragment
        }
 
        /* Exact line number? */
-       if (!memcmp(buf + start, fragment, fragsize))
+       if ((start + fragsize <= size) &&
+           !memcmp(buf + start, fragment, fragsize))
                return start;
 
        /*
@@ -1641,15 +1642,22 @@ static int apply_line(char *output, const char *patch, int plen)
 
        buf = output;
        if (need_fix_leading_space) {
+               int consecutive_spaces = 0;
                /* 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 != ' ')
+                       if (ch != ' ') {
+                               consecutive_spaces = 0;
                                *output++ = ch;
-                       else if ((i % 8) == 0)
-                               *output++ = '\t';
+                       } else {
+                               consecutive_spaces++;
+                               if (consecutive_spaces == 8) {
+                                       *output++ = '\t';
+                                       consecutive_spaces = 0;
+                               }
+                       }
                }
                fixed = 1;
                i = last_tab_in_indent;
@@ -1661,7 +1669,7 @@ static int apply_line(char *output, const char *patch, int plen)
        if (add_nl_to_tail)
                output[plen++] = '\n';
        if (fixed)
-               applied_after_stripping++;
+               applied_after_fixing_ws++;
        return output + plen - buf;
 }
 
@@ -1675,6 +1683,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
        char *new = xmalloc(size);
        const char *oldlines, *newlines;
        int oldsize = 0, newsize = 0;
+       int new_blank_lines_at_end = 0;
        unsigned long leading, trailing;
        int pos, lines;
 
@@ -1682,6 +1691,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                char first;
                int len = linelen(patch, size);
                int plen;
+               int added_blank_line = 0;
 
                if (!len)
                        break;
@@ -1703,6 +1713,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                        else if (first == '+')
                                first = '-';
                }
+
                switch (first) {
                case '\n':
                        /* Newer GNU diff, empty context line */
@@ -1720,9 +1731,14 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                                break;
                /* Fall-through for ' ' */
                case '+':
-                       if (first != '+' || !no_add)
-                               newsize += apply_line(new + newsize, patch,
-                                                     plen);
+                       if (first != '+' || !no_add) {
+                               int added = apply_line(new + newsize, patch,
+                                                      plen);
+                               newsize += added;
+                               if (first == '+' &&
+                                   added == 1 && new[newsize-1] == '\n')
+                                       added_blank_line = 1;
+                       }
                        break;
                case '@': case '\\':
                        /* Ignore it, we already handled it */
@@ -1732,6 +1748,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                                error("invalid start of line: '%c'", first);
                        return -1;
                }
+               if (added_blank_line)
+                       new_blank_lines_at_end++;
+               else
+                       new_blank_lines_at_end = 0;
                patch += len;
                size -= len;
        }
@@ -1774,9 +1794,16 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                if (match_beginning && offset)
                        offset = -1;
                if (offset >= 0) {
-                       int diff = newsize - oldsize;
-                       unsigned long size = desc->size + diff;
-                       unsigned long alloc = desc->alloc;
+                       int diff;
+                       unsigned long size, alloc;
+
+                       if (new_whitespace == strip_whitespace &&
+                           (desc->size - oldsize - offset == 0)) /* end of file? */
+                               newsize -= new_blank_lines_at_end;
+
+                       diff = newsize - oldsize;
+                       size = desc->size + diff;
+                       alloc = desc->alloc;
 
                        /* Warn if it was necessary to reduce the number
                         * of context lines.
@@ -1965,6 +1992,25 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
        return 0;
 }
 
+static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p,
+                               unsigned long *size_p)
+{
+       if (!ce)
+               return 0;
+
+       if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+               *buf_p = xmalloc(100);
+               *size_p = snprintf(*buf_p, 100,
+                       "Subproject commit %s\n", sha1_to_hex(ce->sha1));
+       } else {
+               enum object_type type;
+               *buf_p = read_sha1_file(ce->sha1, &type, size_p);
+               if (!*buf_p)
+                       return -1;
+       }
+       return 0;
+}
+
 static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
 {
        char *buf;
@@ -1975,22 +2021,32 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
        alloc = 0;
        buf = NULL;
        if (cached) {
-               if (ce) {
-                       enum object_type type;
-                       buf = read_sha1_file(ce->sha1, &type, &size);
-                       if (!buf)
+               if (read_file_or_gitlink(ce, &buf, &size))
+                       return error("read of %s failed", patch->old_name);
+               alloc = size;
+       } else if (patch->old_name) {
+               if (S_ISGITLINK(patch->old_mode)) {
+                       if (ce)
+                               read_file_or_gitlink(ce, &buf, &size);
+                       else {
+                               /*
+                                * There is no way to apply subproject
+                                * patch without looking at the index.
+                                */
+                               patch->fragments = NULL;
+                               size = 0;
+                       }
+               }
+               else {
+                       size = xsize_t(st->st_size);
+                       alloc = size + 8192;
+                       buf = xmalloc(alloc);
+                       if (read_old_data(st, patch->old_name,
+                                         &buf, &alloc, &size))
                                return error("read of %s failed",
                                             patch->old_name);
-                       alloc = size;
                }
        }
-       else if (patch->old_name) {
-               size = xsize_t(st->st_size);
-               alloc = size + 8192;
-               buf = xmalloc(alloc);
-               if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
-                       return error("read of %s failed", patch->old_name);
-       }
 
        desc.size = size;
        desc.alloc = alloc;
@@ -2036,6 +2092,16 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
        return 0;
 }
 
+static int verify_index_match(struct cache_entry *ce, struct stat *st)
+{
+       if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+               if (!S_ISDIR(st->st_mode))
+                       return -1;
+               return 0;
+       }
+       return ce_match_stat(ce, st, 1);
+}
+
 static int check_patch(struct patch *patch, struct patch *prev_patch)
 {
        struct stat st;
@@ -2046,8 +2112,14 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
        int ok_if_exists;
 
        patch->rejected = 1; /* we will drop this after we succeed */
+
+       /*
+        * Make sure that we do not have local modifications from the
+        * index when we are looking at the index.  Also make sure
+        * we have the preimage file to be patched in the work tree,
+        * unless --cached, which tells git to apply only in the index.
+        */
        if (old_name) {
-               int changed = 0;
                int stat_ret = 0;
                unsigned st_mode = 0;
 
@@ -2077,15 +2149,12 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
                                    lstat(old_name, &st))
                                        return -1;
                        }
-                       if (!cached)
-                               changed = ce_match_stat(ce, &st, 1);
-                       if (changed)
+                       if (!cached && verify_index_match(ce, &st))
                                return error("%s: does not match index",
                                             old_name);
                        if (cached)
                                st_mode = ntohl(ce->ce_mode);
-               }
-               else if (stat_ret < 0)
+               } else if (stat_ret < 0)
                        return error("%s: %s", old_name, strerror(errno));
 
                if (!cached)
@@ -2165,6 +2234,20 @@ static int check_patch_list(struct patch *patch)
        return err;
 }
 
+/* This function tries to read the sha1 from the current index */
+static int get_current_sha1(const char *path, unsigned char *sha1)
+{
+       int pos;
+
+       if (read_cache() < 0)
+               return -1;
+       pos = cache_name_pos(path, strlen(path));
+       if (pos < 0)
+               return -1;
+       hashcpy(sha1, active_cache[pos]->sha1);
+       return 0;
+}
+
 static void show_index_list(struct patch *list)
 {
        struct patch *patch;
@@ -2181,8 +2264,16 @@ static void show_index_list(struct patch *list)
                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).",
-                           name);
+                       /* git diff has no index line for mode/type changes */
+                       if (!patch->lines_added && !patch->lines_deleted) {
+                               if (get_current_sha1(patch->new_name, sha1) ||
+                                   get_current_sha1(patch->old_name, sha1))
+                                       die("mode change for %s, which is not "
+                                               "in current HEAD", name);
+                               sha1_ptr = sha1;
+                       } else
+                               die("sha1 information is lacking or useless "
+                                       "(%s).", name);
                else
                        sha1_ptr = sha1;
 
@@ -2335,7 +2426,11 @@ static void remove_file(struct patch *patch, int rmdir_empty)
                cache_tree_invalidate_path(active_cache_tree, patch->old_name);
        }
        if (!cached) {
-               if (!unlink(patch->old_name) && rmdir_empty) {
+               if (S_ISGITLINK(patch->old_mode)) {
+                       if (rmdir(patch->old_name))
+                               warning("unable to remove submodule %s",
+                                       patch->old_name);
+               } else if (!unlink(patch->old_name) && rmdir_empty) {
                        char *name = xstrdup(patch->old_name);
                        char *end = strrchr(name, '/');
                        while (end) {
@@ -2363,13 +2458,21 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
        memcpy(ce->name, path, namelen);
        ce->ce_mode = create_ce_mode(mode);
        ce->ce_flags = htons(namelen);
-       if (!cached) {
-               if (lstat(path, &st) < 0)
-                       die("unable to stat newly created file %s", path);
-               fill_stat_cache_info(ce, &st);
+       if (S_ISGITLINK(mode)) {
+               const char *s = buf;
+
+               if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
+                       die("corrupt patch for subproject %s", path);
+       } else {
+               if (!cached) {
+                       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_type, ce->sha1) < 0)
+                       die("unable to create backing store for newly created file %s", path);
        }
-       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);
 }
@@ -2379,6 +2482,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
        int fd;
        char *nbuf;
 
+       if (S_ISGITLINK(mode)) {
+               struct stat st;
+               if (!lstat(path, &st) && S_ISDIR(st.st_mode))
+                       return 0;
+               return mkdir(path, 0777);
+       }
+
        if (has_symlinks && S_ISLNK(mode))
                /* Although buf:size is counted string, it also is NUL
                 * terminated.
@@ -2489,7 +2599,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);
 }
@@ -2869,18 +2979,17 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
                                squelched == 1 ? "" : "s");
                }
                if (new_whitespace == error_on_whitespace)
-                       die("%d line%s add%s trailing whitespaces.",
+                       die("%d line%s add%s whitespace errors.",
                            whitespace_error,
                            whitespace_error == 1 ? "" : "s",
                            whitespace_error == 1 ? "s" : "");
-               if (applied_after_stripping)
+               if (applied_after_fixing_ws)
                        fprintf(stderr, "warning: %d line%s applied after"
-                               " stripping trailing whitespaces.\n",
-                               applied_after_stripping,
-                               applied_after_stripping == 1 ? "" : "s");
+                               " fixing whitespace errors.\n",
+                               applied_after_fixing_ws,
+                               applied_after_fixing_ws == 1 ? "" : "s");
                else if (whitespace_error)
-                       fprintf(stderr, "warning: %d line%s add%s trailing"
-                               " whitespaces.\n",
+                       fprintf(stderr, "warning: %d line%s add%s whitespace errors.\n",
                                whitespace_error,
                                whitespace_error == 1 ? "" : "s",
                                whitespace_error == 1 ? "s" : "");