]> git.ipfire.org Git - thirdparty/git.git/blobdiff - add-patch.c
submodules: fix of regression on fetching of non-init subsub-repo
[thirdparty/git.git] / add-patch.c
index 87db599e9c873c1513f1f71079d0ccb79a22b45a..bd94bd3a7c9e86f4af6d9b887c824ff5be2db9a5 100644 (file)
@@ -2,7 +2,7 @@
 #include "add-interactive.h"
 #include "strbuf.h"
 #include "run-command.h"
-#include "argv-array.h"
+#include "strvec.h"
 #include "pathspec.h"
 #include "color.h"
 #include "diff.h"
@@ -300,12 +300,12 @@ static void setup_child_process(struct add_p_state *s,
 
        va_start(ap, cp);
        while ((arg = va_arg(ap, const char *)))
-               argv_array_push(&cp->args, arg);
+               strvec_push(&cp->args, arg);
        va_end(ap);
 
        cp->git_cmd = 1;
-       argv_array_pushf(&cp->env_array,
-                        INDEX_ENVIRONMENT "=%s", s->s.r->index_file);
+       strvec_pushf(&cp->env_array,
+                    INDEX_ENVIRONMENT "=%s", s->s.r->index_file);
 }
 
 static int parse_range(const char **p,
@@ -384,7 +384,7 @@ static int is_octal(const char *p, size_t len)
 
 static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
 {
-       struct argv_array args = ARGV_ARRAY_INIT;
+       struct strvec args = STRVEC_INIT;
        const char *diff_algorithm = s->s.interactive_diff_algorithm;
        struct strbuf *plain = &s->plain, *colored = NULL;
        struct child_process cp = CHILD_PROCESS_INIT;
@@ -394,32 +394,32 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
        struct hunk *hunk = NULL;
        int res;
 
-       argv_array_pushv(&args, s->mode->diff_cmd);
+       strvec_pushv(&args, s->mode->diff_cmd);
        if (diff_algorithm)
-               argv_array_pushf(&args, "--diff-algorithm=%s", diff_algorithm);
+               strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm);
        if (s->revision) {
                struct object_id oid;
-               argv_array_push(&args,
-                               /* could be on an unborn branch */
-                               !strcmp("HEAD", s->revision) &&
-                               get_oid("HEAD", &oid) ?
-                               empty_tree_oid_hex() : s->revision);
+               strvec_push(&args,
+                           /* could be on an unborn branch */
+                           !strcmp("HEAD", s->revision) &&
+                           get_oid("HEAD", &oid) ?
+                           empty_tree_oid_hex() : s->revision);
        }
-       color_arg_index = args.argc;
+       color_arg_index = args.nr;
        /* Use `--no-color` explicitly, just in case `diff.color = always`. */
-       argv_array_pushl(&args, "--no-color", "-p", "--", NULL);
+       strvec_pushl(&args, "--no-color", "-p", "--", NULL);
        for (i = 0; i < ps->nr; i++)
-               argv_array_push(&args, ps->items[i].original);
+               strvec_push(&args, ps->items[i].original);
 
        setup_child_process(s, &cp, NULL);
-       cp.argv = args.argv;
+       cp.argv = args.v;
        res = capture_command(&cp, plain, 0);
        if (res) {
-               argv_array_clear(&args);
+               strvec_clear(&args);
                return error(_("could not parse diff"));
        }
        if (!plain->len) {
-               argv_array_clear(&args);
+               strvec_clear(&args);
                return 0;
        }
        strbuf_complete_line(plain);
@@ -429,11 +429,11 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
                const char *diff_filter = s->s.interactive_diff_filter;
 
                setup_child_process(s, &colored_cp, NULL);
-               xsnprintf((char *)args.argv[color_arg_index], 8, "--color");
-               colored_cp.argv = args.argv;
+               xsnprintf((char *)args.v[color_arg_index], 8, "--color");
+               colored_cp.argv = args.v;
                colored = &s->colored;
                res = capture_command(&colored_cp, colored, 0);
-               argv_array_clear(&args);
+               strvec_clear(&args);
                if (res)
                        return error(_("could not parse colored diff"));
 
@@ -458,24 +458,22 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
                colored_p = colored->buf;
                colored_pend = colored_p + colored->len;
        }
-       argv_array_clear(&args);
+       strvec_clear(&args);
 
        /* parse files and hunks */
        p = plain->buf;
        pend = p + plain->len;
        while (p != pend) {
                char *eol = memchr(p, '\n', pend - p);
-               const char *deleted = NULL, *added = NULL, *mode_change = NULL;
+               const char *deleted = NULL, *mode_change = NULL;
 
                if (!eol)
                        eol = pend;
 
                if (starts_with(p, "diff ")) {
-                       s->file_diff_nr++;
-                       ALLOC_GROW(s->file_diff, s->file_diff_nr,
+                       ALLOC_GROW_BY(s->file_diff, s->file_diff_nr, 1,
                                   file_diff_alloc);
                        file_diff = s->file_diff + s->file_diff_nr - 1;
-                       memset(file_diff, 0, sizeof(*file_diff));
                        hunk = &file_diff->head;
                        hunk->start = p - plain->buf;
                        if (colored_p)
@@ -484,12 +482,11 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
                } else if (p == plain->buf)
                        BUG("diff starts with unexpected line:\n"
                            "%.*s\n", (int)(eol - p), p);
-               else if (file_diff->deleted || file_diff->added)
+               else if (file_diff->deleted)
                        ; /* keep the rest of the file in a single "hunk" */
                else if (starts_with(p, "@@ ") ||
                         (hunk == &file_diff->head &&
-                         (skip_prefix(p, "deleted file", &deleted) ||
-                          skip_prefix(p, "new file", &added)))) {
+                         (skip_prefix(p, "deleted file", &deleted)))) {
                        if (marker == '-' || marker == '+')
                                /*
                                 * Should not happen; previous hunk did not end
@@ -497,11 +494,9 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
                                 */
                                hunk->splittable_into++;
 
-                       file_diff->hunk_nr++;
-                       ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
+                       ALLOC_GROW_BY(file_diff->hunk, file_diff->hunk_nr, 1,
                                   file_diff->hunk_alloc);
                        hunk = file_diff->hunk + file_diff->hunk_nr - 1;
-                       memset(hunk, 0, sizeof(*hunk));
 
                        hunk->start = p - plain->buf;
                        if (colored)
@@ -509,8 +504,6 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
 
                        if (deleted)
                                file_diff->deleted = 1;
-                       else if (added)
-                               file_diff->added = 1;
                        else if (parse_hunk_header(s, hunk) < 0)
                                return -1;
 
@@ -519,13 +512,16 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
                         * split
                         */
                        marker = *p;
+               } else if (hunk == &file_diff->head &&
+                          starts_with(p, "new file")) {
+                       file_diff->added = 1;
                } else if (hunk == &file_diff->head &&
                           skip_prefix(p, "old mode ", &mode_change) &&
                           is_octal(mode_change, eol - mode_change)) {
                        if (file_diff->mode_change)
                                BUG("double mode change?\n\n%.*s",
                                    (int)(eol - plain->buf), plain->buf);
-                       if (file_diff->hunk_nr++)
+                       if (file_diff->hunk_nr)
                                BUG("mode change in the middle?\n\n%.*s",
                                    (int)(eol - plain->buf), plain->buf);
 
@@ -534,9 +530,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
                         * is _part of_ the header "hunk".
                         */
                        file_diff->mode_change = 1;
-                       ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
+                       ALLOC_GROW_BY(file_diff->hunk, file_diff->hunk_nr, 1,
                                   file_diff->hunk_alloc);
-                       memset(file_diff->hunk, 0, sizeof(struct hunk));
                        file_diff->hunk->start = p - plain->buf;
                        if (colored_p)
                                file_diff->hunk->colored_start =
@@ -1172,7 +1167,7 @@ static int run_apply_check(struct add_p_state *s,
 
        setup_child_process(s, &cp,
                            "apply", "--check", NULL);
-       argv_array_pushv(&cp.args, s->mode->apply_check_args);
+       strvec_pushv(&cp.args, s->mode->apply_check_args);
        if (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))
                return error(_("'git apply --cached' failed"));
 
@@ -1217,7 +1212,7 @@ static int edit_hunk_loop(struct add_p_state *s,
        for (;;) {
                int res = edit_hunk_manually(s, hunk);
                if (res == 0) {
-                       /* abandonded */
+                       /* abandoned */
                        *hunk = backup;
                        return -1;
                }
@@ -1371,8 +1366,18 @@ static int patch_update_file(struct add_p_state *s,
        struct child_process cp = CHILD_PROCESS_INIT;
        int colored = !!s->colored.len, quit = 0;
        enum prompt_mode_type prompt_mode_type;
-
-       if (!file_diff->hunk_nr)
+       enum {
+               ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
+               ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
+               ALLOW_GOTO_NEXT_HUNK = 1 << 2,
+               ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,
+               ALLOW_SEARCH_AND_GOTO = 1 << 4,
+               ALLOW_SPLIT = 1 << 5,
+               ALLOW_EDIT = 1 << 6
+       } permitted = 0;
+
+       /* Empty added files have no hunks */
+       if (!file_diff->hunk_nr && !file_diff->added)
                return 0;
 
        strbuf_reset(&s->buf);
@@ -1381,21 +1386,25 @@ static int patch_update_file(struct add_p_state *s,
        for (;;) {
                if (hunk_index >= file_diff->hunk_nr)
                        hunk_index = 0;
-               hunk = file_diff->hunk + hunk_index;
-
+               hunk = file_diff->hunk_nr
+                               ? file_diff->hunk + hunk_index
+                               : &file_diff->head;
                undecided_previous = -1;
-               for (i = hunk_index - 1; i >= 0; i--)
-                       if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
-                               undecided_previous = i;
-                               break;
-                       }
-
                undecided_next = -1;
-               for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
-                       if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
-                               undecided_next = i;
-                               break;
-                       }
+
+               if (file_diff->hunk_nr) {
+                       for (i = hunk_index - 1; i >= 0; i--)
+                               if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
+                                       undecided_previous = i;
+                                       break;
+                               }
+
+                       for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
+                               if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
+                                       undecided_next = i;
+                                       break;
+                               }
+               }
 
                /* Everything decided? */
                if (undecided_previous < 0 && undecided_next < 0 &&
@@ -1403,26 +1412,41 @@ static int patch_update_file(struct add_p_state *s,
                        break;
 
                strbuf_reset(&s->buf);
-               render_hunk(s, hunk, 0, colored, &s->buf);
-               fputs(s->buf.buf, stdout);
-
-               strbuf_reset(&s->buf);
-               if (undecided_previous >= 0)
-                       strbuf_addstr(&s->buf, ",k");
-               if (hunk_index)
-                       strbuf_addstr(&s->buf, ",K");
-               if (undecided_next >= 0)
-                       strbuf_addstr(&s->buf, ",j");
-               if (hunk_index + 1 < file_diff->hunk_nr)
-                       strbuf_addstr(&s->buf, ",J");
-               if (file_diff->hunk_nr > 1)
-                       strbuf_addstr(&s->buf, ",g,/");
-               if (hunk->splittable_into > 1)
-                       strbuf_addstr(&s->buf, ",s");
-               if (hunk_index + 1 > file_diff->mode_change &&
-                   !file_diff->deleted)
-                       strbuf_addstr(&s->buf, ",e");
+               if (file_diff->hunk_nr) {
+                       render_hunk(s, hunk, 0, colored, &s->buf);
+                       fputs(s->buf.buf, stdout);
 
+                       strbuf_reset(&s->buf);
+                       if (undecided_previous >= 0) {
+                               permitted |= ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK;
+                               strbuf_addstr(&s->buf, ",k");
+                       }
+                       if (hunk_index) {
+                               permitted |= ALLOW_GOTO_PREVIOUS_HUNK;
+                               strbuf_addstr(&s->buf, ",K");
+                       }
+                       if (undecided_next >= 0) {
+                               permitted |= ALLOW_GOTO_NEXT_UNDECIDED_HUNK;
+                               strbuf_addstr(&s->buf, ",j");
+                       }
+                       if (hunk_index + 1 < file_diff->hunk_nr) {
+                               permitted |= ALLOW_GOTO_NEXT_HUNK;
+                               strbuf_addstr(&s->buf, ",J");
+                       }
+                       if (file_diff->hunk_nr > 1) {
+                               permitted |= ALLOW_SEARCH_AND_GOTO;
+                               strbuf_addstr(&s->buf, ",g,/");
+                       }
+                       if (hunk->splittable_into > 1) {
+                               permitted |= ALLOW_SPLIT;
+                               strbuf_addstr(&s->buf, ",s");
+                       }
+                       if (hunk_index + 1 > file_diff->mode_change &&
+                           !file_diff->deleted) {
+                               permitted |= ALLOW_EDIT;
+                               strbuf_addstr(&s->buf, ",e");
+                       }
+               }
                if (file_diff->deleted)
                        prompt_mode_type = PROMPT_DELETION;
                else if (file_diff->added)
@@ -1435,7 +1459,9 @@ static int patch_update_file(struct add_p_state *s,
                color_fprintf(stdout, s->s.prompt_color,
                              "(%"PRIuMAX"/%"PRIuMAX") ",
                              (uintmax_t)hunk_index + 1,
-                             (uintmax_t)file_diff->hunk_nr);
+                             (uintmax_t)(file_diff->hunk_nr
+                                               ? file_diff->hunk_nr
+                                               : 1));
                color_fprintf(stdout, s->s.prompt_color,
                              _(s->mode->prompt_mode[prompt_mode_type]),
                              s->buf.buf);
@@ -1455,38 +1481,46 @@ soft_increment:
                        hunk->use = SKIP_HUNK;
                        goto soft_increment;
                } else if (ch == 'a') {
-                       for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
-                               hunk = file_diff->hunk + hunk_index;
-                               if (hunk->use == UNDECIDED_HUNK)
-                                       hunk->use = USE_HUNK;
+                       if (file_diff->hunk_nr) {
+                               for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
+                                       hunk = file_diff->hunk + hunk_index;
+                                       if (hunk->use == UNDECIDED_HUNK)
+                                               hunk->use = USE_HUNK;
+                               }
+                       } else if (hunk->use == UNDECIDED_HUNK) {
+                               hunk->use = USE_HUNK;
                        }
                } else if (ch == 'd' || ch == 'q') {
-                       for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
-                               hunk = file_diff->hunk + hunk_index;
-                               if (hunk->use == UNDECIDED_HUNK)
-                                       hunk->use = SKIP_HUNK;
+                       if (file_diff->hunk_nr) {
+                               for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
+                                       hunk = file_diff->hunk + hunk_index;
+                                       if (hunk->use == UNDECIDED_HUNK)
+                                               hunk->use = SKIP_HUNK;
+                               }
+                       } else if (hunk->use == UNDECIDED_HUNK) {
+                               hunk->use = SKIP_HUNK;
                        }
                        if (ch == 'q') {
                                quit = 1;
                                break;
                        }
                } else if (s->answer.buf[0] == 'K') {
-                       if (hunk_index)
+                       if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)
                                hunk_index--;
                        else
                                err(s, _("No previous hunk"));
                } else if (s->answer.buf[0] == 'J') {
-                       if (hunk_index + 1 < file_diff->hunk_nr)
+                       if (permitted & ALLOW_GOTO_NEXT_HUNK)
                                hunk_index++;
                        else
                                err(s, _("No next hunk"));
                } else if (s->answer.buf[0] == 'k') {
-                       if (undecided_previous >= 0)
+                       if (permitted & ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK)
                                hunk_index = undecided_previous;
                        else
                                err(s, _("No previous hunk"));
                } else if (s->answer.buf[0] == 'j') {
-                       if (undecided_next >= 0)
+                       if (permitted & ALLOW_GOTO_NEXT_UNDECIDED_HUNK)
                                hunk_index = undecided_next;
                        else
                                err(s, _("No next hunk"));
@@ -1494,7 +1528,7 @@ soft_increment:
                        char *pend;
                        unsigned long response;
 
-                       if (file_diff->hunk_nr < 2) {
+                       if (!(permitted & ALLOW_SEARCH_AND_GOTO)) {
                                err(s, _("No other hunks to goto"));
                                continue;
                        }
@@ -1531,7 +1565,7 @@ soft_increment:
                        regex_t regex;
                        int ret;
 
-                       if (file_diff->hunk_nr < 2) {
+                       if (!(permitted & ALLOW_SEARCH_AND_GOTO)) {
                                err(s, _("No other hunks to search"));
                                continue;
                        }
@@ -1576,7 +1610,7 @@ soft_increment:
                        hunk_index = i;
                } else if (s->answer.buf[0] == 's') {
                        size_t splittable_into = hunk->splittable_into;
-                       if (splittable_into < 2)
+                       if (!(permitted & ALLOW_SPLIT))
                                err(s, _("Sorry, cannot split this hunk"));
                        else if (!split_hunk(s, file_diff,
                                             hunk - file_diff->hunk))
@@ -1584,7 +1618,7 @@ soft_increment:
                                                 _("Split into %d hunks."),
                                                 (int)splittable_into);
                } else if (s->answer.buf[0] == 'e') {
-                       if (hunk_index + 1 == file_diff->mode_change)
+                       if (!(permitted & ALLOW_EDIT))
                                err(s, _("Sorry, cannot edit this hunk"));
                        else if (edit_hunk_loop(s, file_diff, hunk) >= 0) {
                                hunk->use = USE_HUNK;
@@ -1622,7 +1656,8 @@ soft_increment:
                if (file_diff->hunk[i].use == USE_HUNK)
                        break;
 
-       if (i < file_diff->hunk_nr) {
+       if (i < file_diff->hunk_nr ||
+           (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {
                /* At least one hunk selected: apply */
                strbuf_reset(&s->buf);
                reassemble_patch(s, file_diff, 0, &s->buf);
@@ -1633,12 +1668,12 @@ soft_increment:
                                           s->mode->is_reverse);
                else {
                        setup_child_process(s, &cp, "apply", NULL);
-                       argv_array_pushv(&cp.args, s->mode->apply_args);
+                       strvec_pushv(&cp.args, s->mode->apply_args);
                        if (pipe_command(&cp, s->buf.buf, s->buf.len,
                                         NULL, 0, NULL, 0))
                                error(_("'git apply' failed"));
                }
-               if (!repo_read_index(s->s.r))
+               if (repo_read_index(s->s.r) >= 0)
                        repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
                                                     1, NULL, NULL, NULL);
        }