]> git.ipfire.org Git - thirdparty/git.git/blobdiff - builtin/apply.c
builtin/apply: make find_header() return -128 instead of die()ing
[thirdparty/git.git] / builtin / apply.c
index 1a488f9e888b6e9ebb1357a6cf3f7f007cf732fe..434ba0c5429d53a196dbf9b8dd707181c6f13bac 100644 (file)
 #include "xdiff-interface.h"
 #include "ll-merge.h"
 #include "rerere.h"
-
-enum ws_error_action {
-       nowarn_ws_error,
-       warn_on_ws_error,
-       die_on_ws_error,
-       correct_ws_error
-};
-
-
-enum ws_ignore {
-       ignore_ws_none,
-       ignore_ws_change
-};
-
-/*
- * We need to keep track of how symlinks in the preimage are
- * manipulated by the patches.  A patch to add a/b/c where a/b
- * is a symlink should not be allowed to affect the directory
- * the symlink points at, but if the same patch removes a/b,
- * it is perfectly fine, as the patch removes a/b to make room
- * to create a directory a/b so that a/b/c can be created.
- *
- * See also "struct string_list symlink_changes" in "struct
- * apply_state".
- */
-#define SYMLINK_GOES_AWAY 01
-#define SYMLINK_IN_RESULT 02
-
-struct apply_state {
-       const char *prefix;
-       int prefix_length;
-
-       /* These are lock_file related */
-       struct lock_file *lock_file;
-       int newfd;
-
-       /* These control what gets looked at and modified */
-       int apply; /* this is not a dry-run */
-       int cached; /* apply to the index only */
-       int check; /* preimage must match working tree, don't actually apply */
-       int check_index; /* preimage must match the indexed version */
-       int update_index; /* check_index && apply */
-
-       /* These control cosmetic aspect of the output */
-       int diffstat; /* just show a diffstat, and don't actually apply */
-       int numstat; /* just show a numeric diffstat, and don't actually apply */
-       int summary; /* just report creation, deletion, etc, and don't actually apply */
-
-       /* These boolean parameters control how the apply is done */
-       int allow_overlap;
-       int apply_in_reverse;
-       int apply_with_reject;
-       int apply_verbosely;
-       int no_add;
-       int threeway;
-       int unidiff_zero;
-       int unsafe_paths;
-
-       /* Other non boolean parameters */
-       const char *fake_ancestor;
-       const char *patch_input_file;
-       int line_termination;
-       struct strbuf root;
-       int p_value;
-       int p_value_known;
-       unsigned int p_context;
-
-       /* Exclude and include path parameters */
-       struct string_list limit_by_name;
-       int has_include;
-
-       /* Various "current state" */
-       int linenr; /* current line number */
-       struct string_list symlink_changes; /* we have to track symlinks */
-
-       /*
-        * For "diff-stat" like behaviour, we keep track of the biggest change
-        * we've seen, and the longest filename. That allows us to do simple
-        * scaling.
-        */
-       int max_change;
-       int max_len;
-
-       /*
-        * Records filenames that have been touched, in order to handle
-        * the case where more than one patches touch the same file.
-        */
-       struct string_list fn_table;
-
-       /* These control whitespace errors */
-       enum ws_error_action ws_error_action;
-       enum ws_ignore ws_ignore_action;
-       const char *whitespace_option;
-       int whitespace_error;
-       int squelch_whitespace_errors;
-       int applied_after_fixing_ws;
-};
+#include "apply.h"
 
 static const char * const apply_usage[] = {
        N_("git apply [<options>] [<patch>...]"),
@@ -431,10 +335,10 @@ static void say_patch_name(FILE *output, const char *fmt, struct patch *patch)
 
 #define SLOP (16)
 
-static void read_patch_file(struct strbuf *sb, int fd)
+static int read_patch_file(struct strbuf *sb, int fd)
 {
        if (strbuf_read(sb, fd, 0) < 0)
-               die_errno("git apply: failed to read");
+               return error_errno("git apply: failed to read");
 
        /*
         * Make sure that we have some slop in the buffer
@@ -443,6 +347,7 @@ static void read_patch_file(struct strbuf *sb, int fd)
         */
        strbuf_grow(sb, SLOP);
        memset(sb->buf + sb->len, 0, SLOP);
+       return 0;
 }
 
 static unsigned long linelen(const char *buffer, unsigned long size)
@@ -1514,6 +1419,14 @@ static int parse_fragment_header(const char *line, int len, struct fragment *fra
        return offset;
 }
 
+/*
+ * Find file diff header
+ *
+ * Returns:
+ *  -1 if no header was found
+ *  -128 in case of error
+ *   the size of the header in bytes (called "offset") otherwise
+ */
 static int find_header(struct apply_state *state,
                       const char *line,
                       unsigned long size,
@@ -1547,8 +1460,9 @@ static int find_header(struct apply_state *state,
                        struct fragment dummy;
                        if (parse_fragment_header(line, len, &dummy) < 0)
                                continue;
-                       die(_("patch fragment without header at line %d: %.*s"),
-                           state->linenr, (int)len-1, line);
+                       error(_("patch fragment without header at line %d: %.*s"),
+                                    state->linenr, (int)len-1, line);
+                       return -128;
                }
 
                if (size < len + 6)
@@ -1563,19 +1477,23 @@ static int find_header(struct apply_state *state,
                        if (git_hdr_len <= len)
                                continue;
                        if (!patch->old_name && !patch->new_name) {
-                               if (!patch->def_name)
-                                       die(Q_("git diff header lacks filename information when removing "
-                                              "%d leading pathname component (line %d)",
-                                              "git diff header lacks filename information when removing "
-                                              "%d leading pathname components (line %d)",
-                                              state->p_value),
-                                           state->p_value, state->linenr);
+                               if (!patch->def_name) {
+                                       error(Q_("git diff header lacks filename information when removing "
+                                                       "%d leading pathname component (line %d)",
+                                                       "git diff header lacks filename information when removing "
+                                                       "%d leading pathname components (line %d)",
+                                                       state->p_value),
+                                                    state->p_value, state->linenr);
+                                       return -128;
+                               }
                                patch->old_name = xstrdup(patch->def_name);
                                patch->new_name = xstrdup(patch->def_name);
                        }
-                       if (!patch->is_delete && !patch->new_name)
-                               die("git diff header lacks filename information "
-                                   "(line %d)", state->linenr);
+                       if (!patch->is_delete && !patch->new_name) {
+                               error("git diff header lacks filename information "
+                                            "(line %d)", state->linenr);
+                               return -128;
+                       }
                        patch->is_toplevel_relative = 1;
                        *hdrsize = git_hdr_len;
                        return offset;
@@ -2091,6 +2009,9 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si
        int hdrsize, patchsize;
        int offset = find_header(state, buffer, size, &hdrsize, patch);
 
+       if (offset == -128)
+               exit(128);
+
        if (offset < 0)
                return offset;
 
@@ -3750,11 +3671,11 @@ static void prepare_symlink_changes(struct apply_state *state, struct patch *pat
                if ((patch->old_name && S_ISLNK(patch->old_mode)) &&
                    (patch->is_rename || patch->is_delete))
                        /* the symlink at patch->old_name is removed */
-                       register_symlink_changes(state, patch->old_name, SYMLINK_GOES_AWAY);
+                       register_symlink_changes(state, patch->old_name, APPLY_SYMLINK_GOES_AWAY);
 
                if (patch->new_name && S_ISLNK(patch->new_mode))
                        /* the symlink at patch->new_name is created or remains */
-                       register_symlink_changes(state, patch->new_name, SYMLINK_IN_RESULT);
+                       register_symlink_changes(state, patch->new_name, APPLY_SYMLINK_IN_RESULT);
        }
 }
 
@@ -3769,9 +3690,9 @@ static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *na
                        break;
                name->buf[name->len] = '\0';
                change = check_symlink_changes(state, name->buf);
-               if (change & SYMLINK_IN_RESULT)
+               if (change & APPLY_SYMLINK_IN_RESULT)
                        return 1;
-               if (change & SYMLINK_GOES_AWAY)
+               if (change & APPLY_SYMLINK_GOES_AWAY)
                        /*
                         * This cannot be "return 0", because we may
                         * see a new one created at a higher level.
@@ -4500,6 +4421,15 @@ static struct lock_file lock_file;
 #define INACCURATE_EOF (1<<0)
 #define RECOUNT                (1<<1)
 
+/*
+ * Try to apply a patch.
+ *
+ * Returns:
+ *  -128 if a bad error happened (like patch unreadable)
+ *  -1 if patch did not apply and user cannot deal with it
+ *   0 if the patch applied
+ *   1 if the patch did not apply but user might fix it
+ */
 static int apply_patch(struct apply_state *state,
                       int fd,
                       const char *filename,
@@ -4509,9 +4439,11 @@ static int apply_patch(struct apply_state *state,
        struct strbuf buf = STRBUF_INIT; /* owns the patch text */
        struct patch *list = NULL, **listp = &list;
        int skipped_patch = 0;
+       int res = 0;
 
        state->patch_input_file = filename;
-       read_patch_file(&buf, fd);
+       if (read_patch_file(&buf, fd) < 0)
+               return -128;
        offset = 0;
        while (offset < buf.len) {
                struct patch *patch;
@@ -4541,8 +4473,11 @@ static int apply_patch(struct apply_state *state,
                offset += nr;
        }
 
-       if (!list && !skipped_patch)
-               die(_("unrecognized input"));
+       if (!list && !skipped_patch) {
+               error(_("unrecognized input"));
+               res = -128;
+               goto end;
+       }
 
        if (state->whitespace_error && (state->ws_error_action == die_on_ws_error))
                state->apply = 0;
@@ -4551,21 +4486,23 @@ static int apply_patch(struct apply_state *state,
        if (state->update_index && state->newfd < 0)
                state->newfd = hold_locked_index(state->lock_file, 1);
 
-       if (state->check_index) {
-               if (read_cache() < 0)
-                       die(_("unable to read index file"));
+       if (state->check_index && read_cache() < 0) {
+               error(_("unable to read index file"));
+               res = -128;
+               goto end;
        }
 
        if ((state->check || state->apply) &&
            check_patch_list(state, list) < 0 &&
-           !state->apply_with_reject)
-               exit(1);
+           !state->apply_with_reject) {
+               res = -1;
+               goto end;
+       }
 
        if (state->apply && write_out_results(state, list)) {
-               if (state->apply_with_reject)
-                       exit(1);
                /* with --3way, we still need to write the index out */
-               return 1;
+               res = state->apply_with_reject ? -1 : 1;
+               goto end;
        }
 
        if (state->fake_ancestor)
@@ -4580,10 +4517,11 @@ static int apply_patch(struct apply_state *state,
        if (state->summary)
                summary_patch_list(list);
 
+end:
        free_patch_list(list);
        strbuf_release(&buf);
        string_list_clear(&state->fn_table, 0);
-       return 0;
+       return res;
 }
 
 static void git_apply_config(void)
@@ -4724,6 +4662,7 @@ static int apply_all_patches(struct apply_state *state,
                             int options)
 {
        int i;
+       int res;
        int errs = 0;
        int read_stdin = 1;
 
@@ -4732,7 +4671,10 @@ static int apply_all_patches(struct apply_state *state,
                int fd;
 
                if (!strcmp(arg, "-")) {
-                       errs |= apply_patch(state, 0, "<stdin>", options);
+                       res = apply_patch(state, 0, "<stdin>", options);
+                       if (res < 0)
+                               goto end;
+                       errs |= res;
                        read_stdin = 0;
                        continue;
                } else if (0 < state->prefix_length)
@@ -4745,12 +4687,19 @@ static int apply_all_patches(struct apply_state *state,
                        die_errno(_("can't open patch '%s'"), arg);
                read_stdin = 0;
                set_default_whitespace_mode(state);
-               errs |= apply_patch(state, fd, arg, options);
+               res = apply_patch(state, fd, arg, options);
+               if (res < 0)
+                       goto end;
+               errs |= res;
                close(fd);
        }
        set_default_whitespace_mode(state);
-       if (read_stdin)
-               errs |= apply_patch(state, 0, "<stdin>", options);
+       if (read_stdin) {
+               res = apply_patch(state, 0, "<stdin>", options);
+               if (res < 0)
+                       goto end;
+               errs |= res;
+       }
 
        if (state->whitespace_error) {
                if (state->squelch_whitespace_errors &&
@@ -4786,6 +4735,9 @@ static int apply_all_patches(struct apply_state *state,
        }
 
        return !!errs;
+
+end:
+       exit(res == -1 ? 1 : 128);
 }
 
 int cmd_apply(int argc, const char **argv, const char *prefix)