"Fossies" - the Fresh Open Source Software Archive

Member "git-2.23.0.windows.1/add-patch.c" (16 Aug 2019, 46837 Bytes) of package /windows/misc/git-2.23.0.windows.1.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "add-patch.c" see the Fossies "Dox" file reference documentation.

    1 #include "cache.h"
    2 #include "add-interactive.h"
    3 #include "strbuf.h"
    4 #include "run-command.h"
    5 #include "argv-array.h"
    6 #include "pathspec.h"
    7 #include "color.h"
    8 #include "diff.h"
    9 #include "compat/terminal.h"
   10 
   11 enum prompt_mode_type {
   12     PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK
   13 };
   14 
   15 struct patch_mode {
   16     const char *diff[4], *apply[4], *apply_check[4];
   17     unsigned is_reverse:1, apply_for_checkout:1;
   18     const char *prompt_mode[PROMPT_HUNK + 1];
   19     const char *edit_hunk_hint, *help_patch_text;
   20 };
   21 
   22 static struct patch_mode patch_mode_stage = {
   23     .diff = { "diff-files", NULL },
   24     .apply = { "--cached", NULL },
   25     .apply_check = { "--cached", NULL },
   26     .is_reverse = 0,
   27     .prompt_mode = {
   28         N_("Stage mode change [y,n,q,a,d%s,?]? "),
   29         N_("Stage deletion [y,n,q,a,d%s,?]? "),
   30         N_("Stage this hunk [y,n,q,a,d%s,?]? ")
   31     },
   32     .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
   33                  "will immediately be marked for staging."),
   34     .help_patch_text =
   35         N_("y - stage this hunk\n"
   36            "n - do not stage this hunk\n"
   37            "q - quit; do not stage this hunk or any of the remaining "
   38             "ones\n"
   39            "a - stage this hunk and all later hunks in the file\n"
   40            "d - do not stage this hunk or any of the later hunks in "
   41             "the file\n")
   42 };
   43 
   44 static struct patch_mode patch_mode_stash = {
   45     .diff = { "diff-index", "HEAD", NULL },
   46     .apply = { "--cached", NULL },
   47     .apply_check = { "--cached", NULL },
   48     .is_reverse = 0,
   49     .prompt_mode = {
   50         N_("Stash mode change [y,n,q,a,d%s,?]? "),
   51         N_("Stash deletion [y,n,q,a,d%s,?]? "),
   52         N_("Stash this hunk [y,n,q,a,d%s,?]? "),
   53     },
   54     .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
   55                  "will immediately be marked for stashing."),
   56     .help_patch_text =
   57         N_("y - stash this hunk\n"
   58            "n - do not stash this hunk\n"
   59            "q - quit; do not stash this hunk or any of the remaining "
   60             "ones\n"
   61            "a - stash this hunk and all later hunks in the file\n"
   62            "d - do not stash this hunk or any of the later hunks in "
   63             "the file\n"),
   64 };
   65 
   66 static struct patch_mode patch_mode_reset_head = {
   67     .diff = { "diff-index", "--cached", NULL },
   68     .apply = { "-R", "--cached", NULL },
   69     .apply_check = { "-R", "--cached", NULL },
   70     .is_reverse = 1,
   71     .prompt_mode = {
   72         N_("Unstage mode change [y,n,q,a,d%s,?]? "),
   73         N_("Unstage deletion [y,n,q,a,d%s,?]? "),
   74         N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
   75     },
   76     .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
   77                  "will immediately be marked for unstaging."),
   78     .help_patch_text =
   79         N_("y - unstage this hunk\n"
   80            "n - do not unstage this hunk\n"
   81            "q - quit; do not unstage this hunk or any of the remaining "
   82             "ones\n"
   83            "a - unstage this hunk and all later hunks in the file\n"
   84            "d - do not unstage this hunk or any of the later hunks in "
   85             "the file\n"),
   86 };
   87 
   88 static struct patch_mode patch_mode_reset_nothead = {
   89     .diff = { "diff-index", "-R", "--cached", NULL },
   90     .apply = { "--cached", NULL },
   91     .apply_check = { "--cached", NULL },
   92     .is_reverse = 0,
   93     .prompt_mode = {
   94         N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
   95         N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
   96         N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
   97     },
   98     .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
   99                  "will immediately be marked for applying."),
  100     .help_patch_text =
  101         N_("y - apply this hunk to index\n"
  102            "n - do not apply this hunk to index\n"
  103            "q - quit; do not apply this hunk or any of the remaining "
  104             "ones\n"
  105            "a - apply this hunk and all later hunks in the file\n"
  106            "d - do not apply this hunk or any of the later hunks in "
  107             "the file\n"),
  108 };
  109 
  110 static struct patch_mode patch_mode_checkout_index = {
  111     .diff = { "diff-files", NULL },
  112     .apply = { "-R", NULL },
  113     .apply_check = { "-R", NULL },
  114     .is_reverse = 1,
  115     .prompt_mode = {
  116         N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
  117         N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
  118         N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
  119     },
  120     .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
  121                  "will immediately be marked for discarding."),
  122     .help_patch_text =
  123         N_("y - discard this hunk from worktree\n"
  124            "n - do not discard this hunk from worktree\n"
  125            "q - quit; do not discard this hunk or any of the remaining "
  126             "ones\n"
  127            "a - discard this hunk and all later hunks in the file\n"
  128            "d - do not discard this hunk or any of the later hunks in "
  129             "the file\n"),
  130 };
  131 
  132 static struct patch_mode patch_mode_checkout_head = {
  133     .diff = { "diff-index", NULL },
  134     .apply_for_checkout = 1,
  135     .apply_check = { "-R", NULL },
  136     .is_reverse = 1,
  137     .prompt_mode = {
  138         N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
  139         N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
  140         N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
  141     },
  142     .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
  143                  "will immediately be marked for discarding."),
  144     .help_patch_text =
  145         N_("y - discard this hunk from index and worktree\n"
  146            "n - do not discard this hunk from index and worktree\n"
  147            "q - quit; do not discard this hunk or any of the remaining "
  148             "ones\n"
  149            "a - discard this hunk and all later hunks in the file\n"
  150            "d - do not discard this hunk or any of the later hunks in "
  151             "the file\n"),
  152 };
  153 
  154 static struct patch_mode patch_mode_checkout_nothead = {
  155     .diff = { "diff-index", "-R", NULL },
  156     .apply_for_checkout = 1,
  157     .apply_check = { NULL },
  158     .is_reverse = 0,
  159     .prompt_mode = {
  160         N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
  161         N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
  162         N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
  163     },
  164     .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
  165                  "will immediately be marked for applying."),
  166     .help_patch_text =
  167         N_("y - apply this hunk to index and worktree\n"
  168            "n - do not apply this hunk to index and worktree\n"
  169            "q - quit; do not apply this hunk or any of the remaining "
  170             "ones\n"
  171            "a - apply this hunk and all later hunks in the file\n"
  172            "d - do not apply this hunk or any of the later hunks in "
  173             "the file\n"),
  174 };
  175 
  176 static struct patch_mode patch_mode_worktree_head = {
  177     .diff = { "diff-index", NULL },
  178     .apply = { "-R", NULL },
  179     .apply_check = { "-R", NULL },
  180     .is_reverse = 1,
  181     .prompt_mode = {
  182         N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
  183         N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
  184         N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
  185     },
  186     .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
  187                  "will immediately be marked for discarding."),
  188     .help_patch_text =
  189         N_("y - discard this hunk from worktree\n"
  190            "n - do not discard this hunk from worktree\n"
  191            "q - quit; do not discard this hunk or any of the remaining "
  192             "ones\n"
  193            "a - discard this hunk and all later hunks in the file\n"
  194            "d - do not discard this hunk or any of the later hunks in "
  195             "the file\n"),
  196 };
  197 
  198 static struct patch_mode patch_mode_worktree_nothead = {
  199     .diff = { "diff-index", "-R", NULL },
  200     .apply = { NULL },
  201     .apply_check = { NULL },
  202     .is_reverse = 0,
  203     .prompt_mode = {
  204         N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
  205         N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
  206         N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
  207     },
  208     .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
  209                  "will immediately be marked for applying."),
  210     .help_patch_text =
  211         N_("y - apply this hunk to worktree\n"
  212            "n - do not apply this hunk to worktree\n"
  213            "q - quit; do not apply this hunk or any of the remaining "
  214             "ones\n"
  215            "a - apply this hunk and all later hunks in the file\n"
  216            "d - do not apply this hunk or any of the later hunks in "
  217             "the file\n"),
  218 };
  219 
  220 struct hunk_header {
  221     unsigned long old_offset, old_count, new_offset, new_count;
  222     /*
  223      * Start/end offsets to the extra text after the second `@@` in the
  224      * hunk header, e.g. the function signature. This is expected to
  225      * include the newline.
  226      */
  227     size_t extra_start, extra_end, colored_extra_start, colored_extra_end;
  228 };
  229 
  230 struct hunk {
  231     size_t start, end, colored_start, colored_end, splittable_into;
  232     ssize_t delta;
  233     enum { UNDECIDED_HUNK = 0, SKIP_HUNK, USE_HUNK } use;
  234     struct hunk_header header;
  235 };
  236 
  237 struct add_p_state {
  238     struct add_i_state s;
  239     struct strbuf answer, buf;
  240 
  241     /* parsed diff */
  242     struct strbuf plain, colored;
  243     struct file_diff {
  244         struct hunk head;
  245         struct hunk *hunk;
  246         size_t hunk_nr, hunk_alloc;
  247         unsigned deleted:1, mode_change:1,binary:1;
  248     } *file_diff;
  249     size_t file_diff_nr;
  250 
  251     /* patch mode */
  252     struct patch_mode *mode;
  253     const char *revision;
  254 };
  255 
  256 static void err(struct add_p_state *s, const char *fmt, ...)
  257 {
  258     va_list args;
  259 
  260     va_start(args, fmt);
  261     fputs(s->s.error_color, stderr);
  262     vfprintf(stderr, fmt, args);
  263     fputs(s->s.reset_color, stderr);
  264     fputc('\n', stderr);
  265     va_end(args);
  266 }
  267 
  268 static void setup_child_process(struct child_process *cp,
  269                 struct add_p_state *s, ...)
  270 {
  271     va_list ap;
  272     const char *arg;
  273 
  274     va_start(ap, s);
  275     while((arg = va_arg(ap, const char *)))
  276         argv_array_push(&cp->args, arg);
  277     va_end(ap);
  278 
  279     cp->git_cmd = 1;
  280     argv_array_pushf(&cp->env_array,
  281              INDEX_ENVIRONMENT "=%s", s->s.r->index_file);
  282 }
  283 
  284 static int parse_range(const char **p,
  285                unsigned long *offset, unsigned long *count)
  286 {
  287     char *pend;
  288 
  289     *offset = strtoul(*p, &pend, 10);
  290     if (pend == *p)
  291         return -1;
  292     if (*pend != ',') {
  293         *count = 1;
  294         *p = pend;
  295         return 0;
  296     }
  297     *count = strtoul(pend + 1, (char **)p, 10);
  298     return *p == pend + 1 ? -1 : 0;
  299 }
  300 
  301 static int parse_hunk_header(struct add_p_state *s, struct hunk *hunk)
  302 {
  303     struct hunk_header *header = &hunk->header;
  304     const char *line = s->plain.buf + hunk->start, *p = line;
  305     char *eol = memchr(p, '\n', s->plain.len - hunk->start);
  306 
  307     if (!eol)
  308         eol = s->plain.buf + s->plain.len;
  309 
  310     if (!skip_prefix(p, "@@ -", &p) ||
  311         parse_range(&p, &header->old_offset, &header->old_count) < 0 ||
  312         !skip_prefix(p, " +", &p) ||
  313         parse_range(&p, &header->new_offset, &header->new_count) < 0 ||
  314         !skip_prefix(p, " @@", &p))
  315         return error(_("could not parse hunk header '%.*s'"),
  316                  (int)(eol - line), line);
  317 
  318     hunk->start = eol - s->plain.buf + (*eol == '\n');
  319     header->extra_start = p - s->plain.buf;
  320     header->extra_end = hunk->start;
  321 
  322     if (!s->colored.len) {
  323         header->colored_extra_start = header->colored_extra_end = 0;
  324         return 0;
  325     }
  326 
  327     /* Now find the extra text in the colored diff */
  328     line = s->colored.buf + hunk->colored_start;
  329     eol = memchr(line, '\n', s->colored.len - hunk->colored_start);
  330     if (!eol)
  331         eol = s->colored.buf + s->colored.len;
  332     p = memmem(line, eol - line, "@@ -", 4);
  333     if (!p)
  334         return error(_("could not parse colored hunk header '%.*s'"),
  335                  (int)(eol - line), line);
  336     p = memmem(p + 4, eol - p - 4, " @@", 3);
  337     if (!p)
  338         return error(_("could not parse colored hunk header '%.*s'"),
  339                  (int)(eol - line), line);
  340     hunk->colored_start = eol - s->colored.buf + (*eol == '\n');
  341     header->colored_extra_start = p + 3 - s->colored.buf;
  342     header->colored_extra_end = hunk->colored_start;
  343 
  344     return 0;
  345 }
  346 
  347 static int is_octal(const char *p, size_t len)
  348 {
  349     while (len--)
  350         if (*p < '0' || *(p++) > '7')
  351             return 0;
  352     return 1;
  353 }
  354 
  355 static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
  356 {
  357     struct argv_array args = ARGV_ARRAY_INIT;
  358     const char *diff_algorithm = s->s.interactive_diff_algorithm;
  359     struct strbuf *plain = &s->plain, *colored = NULL;
  360     struct child_process cp = CHILD_PROCESS_INIT;
  361     char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0';
  362     size_t file_diff_alloc = 0, i, color_arg_index;
  363     struct file_diff *file_diff = NULL;
  364     struct hunk *hunk = NULL;
  365     int res;
  366 
  367     argv_array_pushv(&args, s->mode->diff);
  368     if (diff_algorithm)
  369         argv_array_pushf(&args, "--diff-algorithm=%s", diff_algorithm);
  370     if (s->revision) {
  371         struct object_id oid;
  372         argv_array_push(&args,
  373                 /* could be on an unborn branch */
  374                 !strcmp("HEAD", s->revision) &&
  375                 get_oid("HEAD", &oid) ?
  376                 empty_tree_oid_hex() : s->revision);
  377     }
  378     color_arg_index = args.argc;
  379     /* Use `--no-color` explicitly, just in case `diff.color = always`. */
  380     argv_array_pushl(&args, "--no-color", "-p", "--", NULL);
  381     for (i = 0; i < ps->nr; i++)
  382         argv_array_push(&args, ps->items[i].original);
  383 
  384     setup_child_process(&cp, s, NULL);
  385     cp.argv = args.argv;
  386     res = capture_command(&cp, plain, 0);
  387     if (res) {
  388         argv_array_clear(&args);
  389         return error(_("could not parse diff"));
  390     }
  391     if (!plain->len) {
  392         argv_array_clear(&args);
  393         return 0;
  394     }
  395     strbuf_complete_line(plain);
  396 
  397     if (want_color_fd(1, -1)) {
  398         struct child_process colored_cp = CHILD_PROCESS_INIT;
  399         const char *diff_filter = s->s.interactive_diff_filter;
  400 
  401         setup_child_process(&colored_cp, s, NULL);
  402         xsnprintf((char *)args.argv[color_arg_index], 8, "--color");
  403         colored_cp.argv = args.argv;
  404         colored = &s->colored;
  405         res = capture_command(&colored_cp, colored, 0);
  406         argv_array_clear(&args);
  407         if (res)
  408             return error(_("could not parse colored diff"));
  409 
  410         if (diff_filter) {
  411             struct child_process filter_cp = CHILD_PROCESS_INIT;
  412 
  413             setup_child_process(&filter_cp, s,
  414                         diff_filter, NULL);
  415             filter_cp.git_cmd = 0;
  416             filter_cp.use_shell = 1;
  417             strbuf_reset(&s->buf);
  418             if (pipe_command(&filter_cp,
  419                      colored->buf, colored->len,
  420                      &s->buf, colored->len,
  421                      NULL, 0) < 0)
  422                 return error(_("failed to run '%s'"),
  423                          diff_filter);
  424             strbuf_swap(colored, &s->buf);
  425         }
  426 
  427         strbuf_complete_line(colored);
  428         colored_p = colored->buf;
  429         colored_pend = colored_p + colored->len;
  430     }
  431     argv_array_clear(&args);
  432 
  433     /* parse files and hunks */
  434     p = plain->buf;
  435     pend = p + plain->len;
  436     while (p != pend) {
  437         char *eol = memchr(p, '\n', pend - p);
  438         const char *deleted = NULL, *mode_change = NULL;
  439 
  440         if (!eol)
  441             eol = pend;
  442 
  443         if (starts_with(p, "diff ")) {
  444             s->file_diff_nr++;
  445             ALLOC_GROW(s->file_diff, s->file_diff_nr,
  446                    file_diff_alloc);
  447             file_diff = s->file_diff + s->file_diff_nr - 1;
  448             memset(file_diff, 0, sizeof(*file_diff));
  449             hunk = &file_diff->head;
  450             hunk->start = p - plain->buf;
  451             if (colored_p)
  452                 hunk->colored_start = colored_p - colored->buf;
  453         } else if (p == plain->buf)
  454             BUG("diff starts with unexpected line:\n"
  455                 "%.*s\n", (int)(eol - p), p);
  456         else if (file_diff->deleted)
  457             ; /* keep the rest of the file in a single "hunk" */
  458         else if (starts_with(p, "@@ ") ||
  459              (hunk == &file_diff->head &&
  460               skip_prefix(p, "deleted file", &deleted))) {
  461             if (marker == '-' || marker == '+')
  462                 /*
  463                  * Should not happen; previous hunk did not end
  464                  * in a context line? Handle it anyway.
  465                  */
  466                 hunk->splittable_into++;
  467 
  468             file_diff->hunk_nr++;
  469             ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
  470                    file_diff->hunk_alloc);
  471             hunk = file_diff->hunk + file_diff->hunk_nr - 1;
  472             memset(hunk, 0, sizeof(*hunk));
  473 
  474             hunk->start = p - plain->buf;
  475             if (colored)
  476                 hunk->colored_start = colored_p - colored->buf;
  477 
  478             if (deleted)
  479                 file_diff->deleted = 1;
  480             else if (parse_hunk_header(s, hunk) < 0)
  481                 return -1;
  482 
  483             /*
  484              * Start counting into how many hunks this one can be
  485              * split
  486              */
  487             marker = *p;
  488         } else if (hunk == &file_diff->head &&
  489                ((skip_prefix(p, "old mode ", &mode_change) ||
  490                  skip_prefix(p, "new mode ", &mode_change)) &&
  491                 is_octal(mode_change, eol - mode_change))) {
  492             if (!file_diff->mode_change) {
  493                 if (file_diff->hunk_nr++)
  494                     BUG("mode change before first hunk");
  495                 ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
  496                        file_diff->hunk_alloc);
  497                 memset(file_diff->hunk, 0, sizeof(struct hunk));
  498                 file_diff->hunk->start = p - plain->buf;
  499                 if (colored_p)
  500                     file_diff->hunk->colored_start =
  501                         colored_p - colored->buf;
  502                 file_diff->mode_change = 1;
  503             } else if (file_diff->hunk_nr != 1)
  504                 BUG("mode change after first hunk?");
  505         } else if (hunk == &file_diff->head &&
  506                starts_with(p, "Binary files "))
  507             file_diff->binary = 1;
  508 
  509         if (file_diff->deleted && file_diff->mode_change)
  510             BUG("diff contains delete *and* a mode change?!?\n%.*s",
  511                 (int)(eol - (plain->buf + file_diff->head.start)),
  512                 plain->buf + file_diff->head.start);
  513 
  514         if ((marker == '-' || marker == '+') &&
  515             (*p == ' ' || *p == '\\'))
  516             hunk->splittable_into++;
  517         if (marker)
  518             marker = *p;
  519 
  520         p = eol == pend ? pend : eol + 1;
  521         hunk->end = p - plain->buf;
  522 
  523         if (colored) {
  524             char *colored_eol = memchr(colored_p, '\n',
  525                            colored_pend - colored_p);
  526             if (colored_eol)
  527                 colored_p = colored_eol + 1;
  528             else if (p != pend)
  529                 /* colored shorter than non-colored? */
  530                 goto mismatched_output;
  531             else
  532                 colored_p = colored_pend;
  533 
  534             hunk->colored_end = colored_p - colored->buf;
  535         }
  536 
  537         if (mode_change) {
  538             file_diff->hunk->end = hunk->end;
  539             if (colored_p)
  540                 file_diff->hunk->colored_end =
  541                     hunk->colored_end;
  542         }
  543     }
  544 
  545     if (marker == '-' || marker == '+')
  546         /*
  547          * Last hunk ended in non-context line (i.e. it appended lines
  548          * to the file, so there are no trailing context lines).
  549          */
  550         hunk->splittable_into++;
  551 
  552     /* non-colored shorter than colored? */
  553     if (colored_p != colored_pend) {
  554 mismatched_output:
  555         error(_("mismatched output from interactive.diffFilter"));
  556         advise(_("Your filter must maintain a one-to-one correspondence\n"
  557              "between its input and output lines."));
  558         return -1;
  559     }
  560 
  561     return 0;
  562 }
  563 
  564 static size_t find_next_line(struct strbuf *sb, size_t offset)
  565 {
  566     char *eol = memchr(sb->buf + offset, '\n', sb->len - offset);
  567 
  568     if (!eol)
  569         return sb->len;
  570     return eol - sb->buf + 1;
  571 }
  572 
  573 static void render_hunk(struct add_p_state *s, struct hunk *hunk,
  574             ssize_t delta, int colored, struct strbuf *out)
  575 {
  576     struct hunk_header *header = &hunk->header;
  577 
  578     if (hunk->header.old_offset != 0 || hunk->header.new_offset != 0) {
  579         /*
  580          * Generate the hunk header dynamically, except for special
  581          * hunks (such as the diff header).
  582          */
  583         const char *p;
  584         size_t len;
  585         unsigned long old_offset = header->old_offset;
  586         unsigned long new_offset = header->new_offset;
  587 
  588         if (!colored) {
  589             p = s->plain.buf + header->extra_start;
  590             len = header->extra_end - header->extra_start;
  591         } else {
  592             strbuf_addstr(out, s->s.fraginfo_color);
  593             p = s->colored.buf + header->colored_extra_start;
  594             len = header->colored_extra_end
  595                 - header->colored_extra_start;
  596         }
  597 
  598         if (s->mode->is_reverse)
  599             old_offset -= delta;
  600         else
  601             new_offset += delta;
  602 
  603         strbuf_addf(out, "@@ -%lu,%lu +%lu,%lu @@",
  604                 old_offset, header->old_count,
  605                 new_offset, header->new_count);
  606         if (len)
  607             strbuf_add(out, p, len);
  608         else if (colored)
  609             strbuf_addf(out, "%s\n", GIT_COLOR_RESET);
  610         else
  611             strbuf_addch(out, '\n');
  612     }
  613 
  614     if (colored)
  615         strbuf_add(out, s->colored.buf + hunk->colored_start,
  616                hunk->colored_end - hunk->colored_start);
  617     else
  618         strbuf_add(out, s->plain.buf + hunk->start,
  619                hunk->end - hunk->start);
  620 }
  621 
  622 static void render_diff_header(struct add_p_state *s,
  623                    struct file_diff *file_diff, int colored,
  624                    struct strbuf *out)
  625 {
  626     /*
  627      * If there was a mode change, the first hunk is a pseudo hunk that
  628      * corresponds to the mode line in the header. If the user did not want
  629      * to stage that "hunk", we actually have to cut it out from the header.
  630      */
  631     int skip_mode_change =
  632         file_diff->mode_change && file_diff->hunk->use != USE_HUNK;
  633     struct hunk *head = &file_diff->head, *first = file_diff->hunk;
  634 
  635     if (!skip_mode_change) {
  636         render_hunk(s, head, 0, colored, out);
  637         return;
  638     }
  639 
  640     if (colored) {
  641         const char *p = s->colored.buf;
  642 
  643         strbuf_add(out, p + head->colored_start,
  644                 first->colored_start - head->colored_start);
  645         strbuf_add(out, p + first->colored_end,
  646                 head->colored_end - first->colored_end);
  647     } else {
  648         const char *p = s->plain.buf;
  649 
  650         strbuf_add(out, p + head->start, first->start - head->start);
  651         strbuf_add(out, p + first->end, head->end - first->end);
  652     }
  653 }
  654 
  655 /* Coalesce hunks again that were split */
  656 static int merge_hunks(struct add_p_state *s, struct file_diff *file_diff,
  657                size_t *hunk_index, int use_all, struct hunk *temp)
  658 {
  659     size_t i = *hunk_index, delta;
  660     struct hunk *hunk = file_diff->hunk + i;
  661     struct hunk_header *header = &temp->header, *next;
  662 
  663     if (!use_all && hunk->use != USE_HUNK)
  664         return 0;
  665 
  666     memcpy(temp, hunk, sizeof(*temp));
  667     /* We simply skip the colored part (if any) when merging hunks */
  668     temp->colored_start = temp->colored_end = 0;
  669 
  670     for (; i + 1 < file_diff->hunk_nr; i++) {
  671         hunk++;
  672         next = &hunk->header;
  673 
  674         if ((!use_all && hunk->use != USE_HUNK) ||
  675             header->new_offset >= next->new_offset + temp->delta ||
  676             header->new_offset + header->new_count
  677             < next->new_offset + temp->delta)
  678             break;
  679 
  680         if (temp->start < hunk->start && temp->end > hunk->start) {
  681             temp->end = hunk->end;
  682             temp->colored_end = hunk->colored_end;
  683             delta = 0;
  684         } else {
  685             const char *plain = s->plain.buf;
  686             size_t  overlapping_line_count = header->new_offset
  687                 + header->new_count - temp->delta
  688                 - next->new_offset;
  689             size_t overlap_end = hunk->start;
  690             size_t overlap_start = overlap_end;
  691             size_t overlap_next, len, i;
  692 
  693             /*
  694              * One of the hunks was edited; let's ensure that at
  695              * least the last context line of the first hunk
  696              * overlaps with the corresponding line of the second
  697              * hunk, and then merge.
  698              */
  699 
  700             for (i = 0; i < overlapping_line_count; i++) {
  701                 overlap_next = find_next_line(&s->plain,
  702                                   overlap_end);
  703 
  704                 if (overlap_next > hunk->end)
  705                     BUG("failed to find %d context lines "
  706                         "in:\n%.*s",
  707                         (int)overlapping_line_count,
  708                         (int)(hunk->end - hunk->start),
  709                         plain + hunk->start);
  710 
  711                 if (plain[overlap_end] != ' ')
  712                     return error(_("expected context line "
  713                                "#%d in\n%.*s"),
  714                              (int)(i + 1),
  715                              (int)(hunk->end
  716                                - hunk->start),
  717                              plain + hunk->start);
  718 
  719                 overlap_start = overlap_end;
  720                 overlap_end = overlap_next;
  721             }
  722             len = overlap_end - overlap_start;
  723 
  724             if (len > temp->end - temp->start ||
  725                 memcmp(plain + temp->end - len,
  726                    plain + overlap_start, len))
  727                 return error(_("hunks do not overlap:\n%.*s\n"
  728                            "\tdoes not end with:\n%.*s"),
  729                          (int)(temp->end - temp->start),
  730                          plain + temp->start,
  731                          (int)len, plain + overlap_start);
  732 
  733             /*
  734              * Since the start-end ranges are not adjacent, we
  735              * cannot simply take the union of the ranges. To
  736              * address that, we temporarily append the union of the
  737              * lines to the `plain` strbuf.
  738              */
  739             if (temp->end != s->plain.len) {
  740                 size_t start = s->plain.len;
  741 
  742                 strbuf_add(&s->plain, plain + temp->start,
  743                        temp->end - temp->start);
  744                 plain = s->plain.buf;
  745                 temp->start = start;
  746                 temp->end = s->plain.len;
  747             }
  748 
  749             strbuf_add(&s->plain,
  750                    plain + overlap_end,
  751                    hunk->end - overlap_end);
  752             temp->end = s->plain.len;
  753             temp->splittable_into += hunk->splittable_into;
  754             delta = temp->delta;
  755             temp->delta += hunk->delta;
  756         }
  757 
  758         header->old_count = next->old_offset + next->old_count
  759             - header->old_offset;
  760         header->new_count = next->new_offset + delta
  761             + next->new_count - header->new_offset;
  762     }
  763 
  764     if (i == *hunk_index)
  765         return 0;
  766 
  767     *hunk_index = i;
  768     return 1;
  769 }
  770 
  771 static void reassemble_patch(struct add_p_state *s,
  772                  struct file_diff *file_diff, int use_all,
  773                  struct strbuf *out)
  774 {
  775     struct hunk *hunk;
  776     size_t save_len = s->plain.len, i;
  777     ssize_t delta = 0;
  778 
  779     render_diff_header(s, file_diff, 0, out);
  780 
  781     for (i = file_diff->mode_change; i < file_diff->hunk_nr; i++) {
  782         struct hunk temp = { 0 };
  783 
  784         hunk = file_diff->hunk + i;
  785         if (!use_all && hunk->use != USE_HUNK)
  786             delta += hunk->header.old_count
  787                 - hunk->header.new_count;
  788         else {
  789             /* merge overlapping hunks into a temporary hunk */
  790             if (merge_hunks(s, file_diff, &i, use_all, &temp))
  791                 hunk = &temp;
  792 
  793             render_hunk(s, hunk, delta, 0, out);
  794 
  795             /*
  796              * In case `merge_hunks()` used `plain` as a scratch
  797              * pad (this happens when an edited hunk had to be
  798              * coalesced with another hunk).
  799              */
  800             strbuf_setlen(&s->plain, save_len);
  801 
  802             delta += hunk->delta;
  803         }
  804     }
  805 }
  806 
  807 static int split_hunk(struct add_p_state *s, struct file_diff *file_diff,
  808                size_t hunk_index)
  809 {
  810     int colored = !!s->colored.len, first = 1;
  811     struct hunk *hunk = file_diff->hunk + hunk_index;
  812     size_t splittable_into;
  813     size_t end, colored_end, current, colored_current = 0, context_line_count;
  814     struct hunk_header remaining, *header;
  815     char marker, ch;
  816 
  817     if (hunk_index >= file_diff->hunk_nr)
  818         BUG("invalid hunk index: %d (must be >= 0 and < %d)",
  819             (int)hunk_index, (int)file_diff->hunk_nr);
  820 
  821     if (hunk->splittable_into < 2)
  822         return 0;
  823     splittable_into = hunk->splittable_into;
  824 
  825     end = hunk->end;
  826     colored_end = hunk->colored_end;
  827 
  828     memcpy(&remaining, &hunk->header, sizeof(remaining));
  829 
  830     file_diff->hunk_nr += splittable_into - 1;
  831     ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr, file_diff->hunk_alloc);
  832     if (hunk_index + splittable_into < file_diff->hunk_nr)
  833         memmove(file_diff->hunk + hunk_index + splittable_into,
  834             file_diff->hunk + hunk_index + 1,
  835             (file_diff->hunk_nr - hunk_index - splittable_into)
  836             * sizeof(*hunk));
  837     hunk = file_diff->hunk + hunk_index;
  838     hunk->splittable_into = 1;
  839     memset(hunk + 1, 0, (splittable_into - 1) * sizeof(*hunk));
  840 
  841     header = &hunk->header;
  842     header->old_count = header->new_count = 0;
  843 
  844     current = hunk->start;
  845     if (colored)
  846         colored_current = hunk->colored_start;
  847     marker = '\0';
  848     context_line_count = 0;
  849 
  850     while (splittable_into > 1) {
  851         ch = s->plain.buf[current];
  852         if ((marker == '-' || marker == '+') && ch == ' ') {
  853             first = 0;
  854             hunk[1].start = current;
  855             if (colored)
  856                 hunk[1].colored_start = colored_current;
  857             context_line_count = 0;
  858         }
  859 
  860         if (marker != ' ' || (ch != '-' && ch != '+')) {
  861 next_hunk_line:
  862             /* current hunk not done yet */
  863             if (ch == ' ')
  864                 context_line_count++;
  865             else if (ch == '-')
  866                 header->old_count++;
  867             else if (ch == '+')
  868                 header->new_count++;
  869             else
  870                 BUG("unhandled diff marker: '%c'", ch);
  871             marker = ch;
  872             current = find_next_line(&s->plain, current);
  873             if (colored)
  874                 colored_current =
  875                     find_next_line(&s->colored,
  876                                colored_current);
  877             continue;
  878         }
  879 
  880         if (first) {
  881             if (header->old_count || header->new_count)
  882                 BUG("counts are off: %d/%d",
  883                     (int)header->old_count,
  884                     (int)header->new_count);
  885 
  886             header->old_count = context_line_count;
  887             header->new_count = context_line_count;
  888             context_line_count = 0;
  889             first = 0;
  890             goto next_hunk_line;
  891         }
  892 
  893         remaining.old_offset += header->old_count;
  894         remaining.old_count -= header->old_count;
  895         remaining.new_offset += header->new_count;
  896         remaining.new_count -= header->new_count;
  897 
  898         /* initialize next hunk header's offsets */
  899         hunk[1].header.old_offset =
  900             header->old_offset + header->old_count;
  901         hunk[1].header.new_offset =
  902             header->new_offset + header->new_count;
  903 
  904         /* add one split hunk */
  905         header->old_count += context_line_count;
  906         header->new_count += context_line_count;
  907 
  908         hunk->end = current;
  909         if (colored)
  910             hunk->colored_end = colored_current;
  911 
  912         hunk++;
  913         hunk->splittable_into = 1;
  914         hunk->use = hunk[-1].use;
  915         header = &hunk->header;
  916 
  917         header->old_count = header->new_count = context_line_count;
  918         context_line_count = 0;
  919 
  920         splittable_into--;
  921         marker = ch;
  922     }
  923 
  924     /* last hunk simply gets the rest */
  925     if (header->old_offset != remaining.old_offset)
  926         BUG("miscounted old_offset: %lu != %lu",
  927             header->old_offset, remaining.old_offset);
  928     if (header->new_offset != remaining.new_offset)
  929         BUG("miscounted new_offset: %lu != %lu",
  930             header->new_offset, remaining.new_offset);
  931     header->old_count = remaining.old_count;
  932     header->new_count = remaining.new_count;
  933     hunk->end = end;
  934     if (colored)
  935         hunk->colored_end = colored_end;
  936 
  937     return 0;
  938 }
  939 
  940 static void recolor_hunk(struct add_p_state *s, struct hunk *hunk)
  941 {
  942     const char *plain = s->plain.buf;
  943     size_t current, eol, next;
  944 
  945     if (!s->colored.len)
  946         return;
  947 
  948     hunk->colored_start = s->colored.len;
  949     for (current = hunk->start; current < hunk->end; ) {
  950         for (eol = current; eol < hunk->end; eol++)
  951             if (plain[eol] == '\n')
  952                 break;
  953         next = eol + (eol < hunk->end);
  954         if (eol > current && plain[eol - 1] == '\r')
  955             eol--;
  956 
  957         strbuf_addstr(&s->colored,
  958                   plain[current] == '-' ?
  959                   s->s.file_old_color :
  960                   plain[current] == '+' ?
  961                   s->s.file_new_color :
  962                   s->s.context_color);
  963         strbuf_add(&s->colored, plain + current, eol - current);
  964         strbuf_addstr(&s->colored, GIT_COLOR_RESET);
  965         if (next > eol)
  966             strbuf_add(&s->colored, plain + eol, next - eol);
  967         current = next;
  968     }
  969     hunk->colored_end = s->colored.len;
  970 }
  971 
  972 static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
  973 {
  974     char *path = xstrdup(git_path("addp-hunk-edit.diff"));
  975     int fd = xopen(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
  976     struct strbuf buf = STRBUF_INIT;
  977     size_t i, j;
  978     int res, copy;
  979 
  980     if (fd < 0) {
  981         res = error_errno(_("could not open '%s' for writing"), path);
  982         goto edit_hunk_manually_finish;
  983     }
  984 
  985     strbuf_commented_addf(&buf, _("Manual hunk edit mode -- see bottom for "
  986                       "a quick guide.\n"));
  987     render_hunk(s, hunk, 0, 0, &buf);
  988     strbuf_commented_addf(&buf,
  989                   _("---\n"
  990                 "To remove '%c' lines, make them ' ' lines "
  991                 "(context).\n"
  992                 "To remove '%c' lines, delete them.\n"
  993                 "Lines starting with %c will be removed.\n"),
  994                   s->mode->is_reverse ? '+' : '-',
  995                   s->mode->is_reverse ? '-' : '+',
  996                   comment_line_char);
  997     strbuf_commented_addf(&buf, "%s", _(s->mode->edit_hunk_hint));
  998     /*
  999      * TRANSLATORS: 'it' refers to the patch mentioned in the previous
 1000      * messages.
 1001      */
 1002     strbuf_commented_addf(&buf,
 1003                   _("If it does not apply cleanly, you will be "
 1004                 "given an opportunity to\n"
 1005                 "edit again.  If all lines of the hunk are "
 1006                 "removed, then the edit is\n"
 1007                 "aborted and the hunk is left unchanged.\n"));
 1008     if (write_in_full(fd, buf.buf, buf.len) < 0) {
 1009         res = error_errno(_("could not write to '%s'"), path);
 1010         goto edit_hunk_manually_finish;
 1011     }
 1012 
 1013     res = close(fd);
 1014     fd = -1;
 1015     if (res < 0)
 1016         goto edit_hunk_manually_finish;
 1017 
 1018     hunk->start = s->plain.len;
 1019     if (launch_editor(path, &s->plain, NULL) < 0) {
 1020         res = error_errno(_("could not edit '%s'"), path);
 1021         goto edit_hunk_manually_finish;
 1022     }
 1023     unlink(path);
 1024 
 1025     /* strip out commented lines */
 1026     copy = s->plain.buf[hunk->start] != comment_line_char;
 1027     for (i = j = hunk->start; i < s->plain.len; ) {
 1028         if (copy)
 1029             s->plain.buf[j++] = s->plain.buf[i];
 1030         if (s->plain.buf[i++] == '\n')
 1031             copy = s->plain.buf[i] != comment_line_char;
 1032     }
 1033 
 1034     if (j == hunk->start)
 1035         /* User aborted by deleting everything */
 1036         goto edit_hunk_manually_finish;
 1037 
 1038     res = 1;
 1039     strbuf_setlen(&s->plain, j);
 1040     hunk->end = j;
 1041     recolor_hunk(s, hunk);
 1042     if (s->plain.buf[hunk->start] == '@' &&
 1043         /* If the hunk header was deleted, simply use the original one. */
 1044         parse_hunk_header(s, hunk) < 0)
 1045         res = -1;
 1046 
 1047 edit_hunk_manually_finish:
 1048     if (fd >= 0)
 1049         close(fd);
 1050     free(path);
 1051     strbuf_release(&buf);
 1052 
 1053     return res;
 1054 }
 1055 
 1056 static ssize_t recount_edited_hunk(struct add_p_state *s, struct hunk *hunk,
 1057                    size_t orig_old_count, size_t orig_new_count)
 1058 {
 1059     struct hunk_header *header = &hunk->header;
 1060     size_t i;
 1061 
 1062     header->old_count = header->new_count = 0;
 1063     for (i = hunk->start; i < hunk->end; ) {
 1064         switch (s->plain.buf[i]) {
 1065         case '-':
 1066             header->old_count++;
 1067             break;
 1068         case '+':
 1069             header->new_count++;
 1070             break;
 1071         case ' ': case '\r': case '\n':
 1072             header->old_count++;
 1073             header->new_count++;
 1074             break;
 1075         }
 1076 
 1077         i = find_next_line(&s->plain, i);
 1078     }
 1079 
 1080     return orig_old_count - orig_new_count
 1081         - header->old_count + header->new_count;
 1082 }
 1083 
 1084 static int run_apply_check(struct add_p_state *s,
 1085                struct file_diff *file_diff)
 1086 {
 1087     struct child_process cp = CHILD_PROCESS_INIT;
 1088 
 1089     strbuf_reset(&s->buf);
 1090     reassemble_patch(s, file_diff, 1, &s->buf);
 1091 
 1092     setup_child_process(&cp, s,
 1093                 "apply", "--check", NULL);
 1094     argv_array_pushv(&cp.args, s->mode->apply_check);
 1095     if (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))
 1096         return error(_("'git apply --cached' failed"));
 1097 
 1098     return 0;
 1099 }
 1100 
 1101 static int read_single_character(struct add_p_state *s)
 1102 {
 1103     if (s->s.use_single_key) {
 1104         int res = read_key_without_echo(&s->answer);
 1105         printf("%s\n", res == EOF ? "" : s->answer.buf);
 1106         return res;
 1107     }
 1108 
 1109     if (strbuf_getline(&s->answer, stdin) == EOF)
 1110         return EOF;
 1111     strbuf_trim_trailing_newline(&s->answer);
 1112     return 0;
 1113 }
 1114 
 1115 static int prompt_yesno(struct add_p_state *s, const char *prompt)
 1116 {
 1117     for (;;) {
 1118         color_fprintf(stdout, s->s.prompt_color, "%s", _(prompt));
 1119         fflush(stdout);
 1120         if (read_single_character(s) == EOF)
 1121             return -1;
 1122         switch (tolower(s->answer.buf[0])) {
 1123         case 'n': return 0;
 1124         case 'y': return 1;
 1125         }
 1126     }
 1127 }
 1128 
 1129 static int edit_hunk_loop(struct add_p_state *s,
 1130               struct file_diff *file_diff, struct hunk *hunk)
 1131 {
 1132     size_t plain_len = s->plain.len, colored_len = s->colored.len;
 1133     struct hunk backup;
 1134 
 1135     memcpy(&backup, hunk, sizeof(backup));
 1136 
 1137     for (;;) {
 1138         int res = edit_hunk_manually(s, hunk);
 1139         if (res == 0) {
 1140             /* abandonded */
 1141             memcpy(hunk, &backup, sizeof(backup));
 1142             return -1;
 1143         }
 1144 
 1145         if (res > 0) {
 1146             hunk->delta +=
 1147                 recount_edited_hunk(s, hunk,
 1148                             backup.header.old_count,
 1149                             backup.header.new_count);
 1150             if (!run_apply_check(s, file_diff))
 1151                 return 0;
 1152         }
 1153 
 1154         /* Drop edits (they were appended to s->plain) */
 1155         strbuf_setlen(&s->plain, plain_len);
 1156         strbuf_setlen(&s->colored, colored_len);
 1157         memcpy(hunk, &backup, sizeof(backup));
 1158 
 1159         /*
 1160          * TRANSLATORS: do not translate [y/n]
 1161          * The program will only accept that input at this point.
 1162          * Consider translating (saying "no" discards!) as
 1163          * (saying "n" for "no" discards!) if the translation
 1164          * of the word "no" does not start with n.
 1165          */
 1166         res = prompt_yesno(s, _("Your edited hunk does not apply. "
 1167                     "Edit again (saying \"no\" discards!) "
 1168                     "[y/n]? "));
 1169         if (res < 1)
 1170             return -1;
 1171     }
 1172 }
 1173 
 1174 static int apply_for_checkout(struct add_p_state *s, struct strbuf *diff,
 1175                   int is_reverse)
 1176 {
 1177     const char *reverse = is_reverse ? "-R" : NULL;
 1178     struct child_process check_index = CHILD_PROCESS_INIT;
 1179     struct child_process check_worktree = CHILD_PROCESS_INIT;
 1180     struct child_process apply_index = CHILD_PROCESS_INIT;
 1181     struct child_process apply_worktree = CHILD_PROCESS_INIT;
 1182     int applies_index, applies_worktree;
 1183 
 1184     setup_child_process(&check_index, s,
 1185                 "apply", "--cached", "--check", reverse, NULL);
 1186     applies_index = !pipe_command(&check_index, diff->buf, diff->len,
 1187                       NULL, 0, NULL, 0);
 1188 
 1189     setup_child_process(&check_worktree, s,
 1190                 "apply", "--check", reverse, NULL);
 1191     applies_worktree = !pipe_command(&check_worktree, diff->buf, diff->len,
 1192                      NULL, 0, NULL, 0);
 1193 
 1194     if (applies_worktree && applies_index) {
 1195         setup_child_process(&apply_index, s,
 1196                     "apply", "--cached", reverse, NULL);
 1197         pipe_command(&apply_index, diff->buf, diff->len,
 1198                  NULL, 0, NULL, 0);
 1199 
 1200         setup_child_process(&apply_worktree, s,
 1201                     "apply", reverse, NULL);
 1202         pipe_command(&apply_worktree, diff->buf, diff->len,
 1203                  NULL, 0, NULL, 0);
 1204 
 1205         return 1;
 1206     }
 1207 
 1208     if (!applies_index) {
 1209         err(s, _("The selected hunks do not apply to the index!"));
 1210         if (prompt_yesno(s, _("Apply them to the worktree "
 1211                       "anyway? ")) > 0) {
 1212             setup_child_process(&apply_worktree, s,
 1213                         "apply", reverse, NULL);
 1214             return pipe_command(&apply_worktree, diff->buf,
 1215                         diff->len, NULL, 0, NULL, 0);
 1216         }
 1217         err(s, _("Nothing was applied.\n"));
 1218     } else
 1219         /* As a last resort, show the diff to the user */
 1220         fwrite(diff->buf, diff->len, 1, stderr);
 1221 
 1222     return 0;
 1223 }
 1224 
 1225 #define SUMMARY_HEADER_WIDTH 20
 1226 #define SUMMARY_LINE_WIDTH 80
 1227 static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
 1228                struct strbuf *out)
 1229 {
 1230     struct hunk_header *header = &hunk->header;
 1231     struct strbuf *plain = &s->plain;
 1232     size_t len = out->len, i;
 1233 
 1234     strbuf_addf(out, " -%lu,%lu +%lu,%lu ",
 1235             header->old_offset, header->old_count,
 1236             header->new_offset, header->new_count);
 1237     if (out->len - len < SUMMARY_HEADER_WIDTH)
 1238         strbuf_addchars(out, ' ',
 1239                 SUMMARY_HEADER_WIDTH + len - out->len);
 1240     for (i = hunk->start; i < hunk->end; i = find_next_line(plain, i))
 1241         if (plain->buf[i] != ' ')
 1242             break;
 1243     if (i < hunk->end)
 1244         strbuf_add(out, plain->buf + i, find_next_line(plain, i) - i);
 1245     if (out->len - len > SUMMARY_LINE_WIDTH)
 1246         strbuf_setlen(out, len + SUMMARY_LINE_WIDTH);
 1247     strbuf_complete_line(out);
 1248 }
 1249 
 1250 #define DISPLAY_HUNKS_LINES 20
 1251 static size_t display_hunks(struct add_p_state *s,
 1252                 struct file_diff *file_diff, size_t start_index)
 1253 {
 1254     size_t end_index = start_index + DISPLAY_HUNKS_LINES;
 1255 
 1256     if (end_index > file_diff->hunk_nr)
 1257         end_index = file_diff->hunk_nr;
 1258 
 1259     while (start_index < end_index) {
 1260         struct hunk *hunk = file_diff->hunk + start_index++;
 1261 
 1262         strbuf_reset(&s->buf);
 1263         strbuf_addf(&s->buf, "%c%2d: ", hunk->use == USE_HUNK ? '+'
 1264                 : hunk->use == SKIP_HUNK ? '-' : ' ',
 1265                 (int)start_index);
 1266         summarize_hunk(s, hunk, &s->buf);
 1267         fputs(s->buf.buf, stdout);
 1268     }
 1269 
 1270     return end_index;
 1271 }
 1272 
 1273 static const char help_patch_remainder[] =
 1274 N_("j - leave this hunk undecided, see next undecided hunk\n"
 1275    "J - leave this hunk undecided, see next hunk\n"
 1276    "k - leave this hunk undecided, see previous undecided hunk\n"
 1277    "K - leave this hunk undecided, see previous hunk\n"
 1278    "g - select a hunk to go to\n"
 1279    "/ - search for a hunk matching the given regex\n"
 1280    "s - split the current hunk into smaller hunks\n"
 1281    "e - manually edit the current hunk\n"
 1282    "? - print help\n");
 1283 
 1284 static int patch_update_file(struct add_p_state *s,
 1285                  struct file_diff *file_diff)
 1286 {
 1287     size_t hunk_index = 0;
 1288     ssize_t i, undecided_previous, undecided_next;
 1289     struct hunk *hunk;
 1290     char ch;
 1291     struct child_process cp = CHILD_PROCESS_INIT;
 1292     int colored = !!s->colored.len, quit = 0;
 1293     enum prompt_mode_type prompt_mode_type;
 1294 
 1295     if (!file_diff->hunk_nr)
 1296         return 0;
 1297 
 1298     strbuf_reset(&s->buf);
 1299     render_diff_header(s, file_diff, colored, &s->buf);
 1300     fputs(s->buf.buf, stdout);
 1301     for (;;) {
 1302         if (hunk_index >= file_diff->hunk_nr)
 1303             hunk_index = 0;
 1304         hunk = file_diff->hunk + hunk_index;
 1305 
 1306         undecided_previous = -1;
 1307         for (i = hunk_index - 1; i >= 0; i--)
 1308             if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
 1309                 undecided_previous = i;
 1310                 break;
 1311             }
 1312 
 1313         undecided_next = -1;
 1314         for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
 1315             if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
 1316                 undecided_next = i;
 1317                 break;
 1318             }
 1319 
 1320         /* Everything decided? */
 1321         if (undecided_previous < 0 && undecided_next < 0 &&
 1322             hunk->use != UNDECIDED_HUNK)
 1323             break;
 1324 
 1325         strbuf_reset(&s->buf);
 1326         render_hunk(s, hunk, 0, colored, &s->buf);
 1327         fputs(s->buf.buf, stdout);
 1328 
 1329         strbuf_reset(&s->buf);
 1330         if (undecided_previous >= 0)
 1331             strbuf_addstr(&s->buf, ",k");
 1332         if (hunk_index)
 1333             strbuf_addstr(&s->buf, ",K");
 1334         if (undecided_next >= 0)
 1335             strbuf_addstr(&s->buf, ",j");
 1336         if (hunk_index + 1 < file_diff->hunk_nr)
 1337             strbuf_addstr(&s->buf, ",J");
 1338         if (file_diff->hunk_nr > 1)
 1339             strbuf_addstr(&s->buf, ",g,/");
 1340         if (hunk->splittable_into > 1)
 1341             strbuf_addstr(&s->buf, ",s");
 1342         if (hunk_index + 1 > file_diff->mode_change &&
 1343             !file_diff->deleted)
 1344             strbuf_addstr(&s->buf, ",e");
 1345 
 1346         if (file_diff->deleted)
 1347             prompt_mode_type = PROMPT_DELETION;
 1348         else if (file_diff->mode_change && !hunk_index)
 1349             prompt_mode_type = PROMPT_MODE_CHANGE;
 1350         else
 1351             prompt_mode_type = PROMPT_HUNK;
 1352 
 1353         color_fprintf(stdout, s->s.prompt_color,
 1354                   _(s->mode->prompt_mode[prompt_mode_type]),
 1355                   s->buf.buf);
 1356         fflush(stdout);
 1357         if (read_single_character(s) == EOF)
 1358             break;
 1359 
 1360         if (!s->answer.len)
 1361             continue;
 1362         ch = tolower(s->answer.buf[0]);
 1363         if (ch == 'y') {
 1364             hunk->use = USE_HUNK;
 1365 soft_increment:
 1366             while (++hunk_index < file_diff->hunk_nr &&
 1367                    file_diff->hunk[hunk_index].use
 1368                    != UNDECIDED_HUNK)
 1369                 ; /* continue looking */
 1370         } else if (ch == 'n') {
 1371             hunk->use = SKIP_HUNK;
 1372             goto soft_increment;
 1373         } else if (ch == 'a') {
 1374             for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
 1375                 hunk = file_diff->hunk + hunk_index;
 1376                 if (hunk->use == UNDECIDED_HUNK)
 1377                     hunk->use = USE_HUNK;
 1378             }
 1379         } else if (ch == 'd' || ch == 'q') {
 1380             for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
 1381                 hunk = file_diff->hunk + hunk_index;
 1382                 if (hunk->use == UNDECIDED_HUNK)
 1383                     hunk->use = SKIP_HUNK;
 1384             }
 1385             if (ch == 'q') {
 1386                 quit = 1;
 1387                 break;
 1388             }
 1389         } else if (s->answer.buf[0] == 'K') {
 1390             if (hunk_index)
 1391                 hunk_index--;
 1392             else
 1393                 err(s, _("No previous hunk"));
 1394         } else if (s->answer.buf[0] == 'J') {
 1395             if (hunk_index + 1 < file_diff->hunk_nr)
 1396                 hunk_index++;
 1397             else
 1398                 err(s, _("No next hunk"));
 1399         } else if (s->answer.buf[0] == 'k') {
 1400             if (undecided_previous >= 0)
 1401                 hunk_index = undecided_previous;
 1402             else
 1403                 err(s, _("No previous hunk"));
 1404         } else if (s->answer.buf[0] == 'j') {
 1405             if (undecided_next >= 0)
 1406                 hunk_index = undecided_next;
 1407             else
 1408                 err(s, _("No next hunk"));
 1409         } else if (s->answer.buf[0] == 'g') {
 1410             char *pend;
 1411             unsigned long response;
 1412 
 1413             if (file_diff->hunk_nr < 2) {
 1414                 err(s, _("No other hunks to goto"));
 1415                 continue;
 1416             }
 1417             strbuf_remove(&s->answer, 0, 1);
 1418             strbuf_trim(&s->answer);
 1419             i = hunk_index > 10 ? hunk_index - 10 : 0;
 1420             while (s->answer.len == 0) {
 1421                 i = display_hunks(s, file_diff, i);
 1422                 printf("%s", i < file_diff->hunk_nr ?
 1423                        _("go to which hunk (<ret> to see "
 1424                      "more)? ") : _("go to which hunk? "));
 1425                 fflush(stdout);
 1426                 if (strbuf_getline(&s->answer,
 1427                            stdin) == EOF)
 1428                     break;
 1429                 strbuf_trim_trailing_newline(&s->answer);
 1430             }
 1431 
 1432             strbuf_trim(&s->answer);
 1433             response = strtoul(s->answer.buf, &pend, 10);
 1434             if (*pend || pend == s->answer.buf)
 1435                 err(s, _("Invalid number: '%s'"),
 1436                     s->answer.buf);
 1437             else if (0 < response && response <= file_diff->hunk_nr)
 1438                 hunk_index = response - 1;
 1439             else
 1440                 err(s, Q_("Sorry, only %d hunk available.",
 1441                       "Sorry, only %d hunks available.",
 1442                       file_diff->hunk_nr),
 1443                     (int)file_diff->hunk_nr);
 1444         } else if (s->answer.buf[0] == '/') {
 1445             regex_t regex;
 1446             int ret;
 1447 
 1448             if (file_diff->hunk_nr < 2) {
 1449                 err(s, _("No other hunks to search"));
 1450                 continue;
 1451             }
 1452             strbuf_remove(&s->answer, 0, 1);
 1453             strbuf_trim_trailing_newline(&s->answer);
 1454             if (s->answer.len == 0) {
 1455                 printf("%s", _("search for regex? "));
 1456                 fflush(stdout);
 1457                 if (strbuf_getline(&s->answer,
 1458                            stdin) == EOF)
 1459                     break;
 1460                 strbuf_trim_trailing_newline(&s->answer);
 1461                 if (s->answer.len == 0)
 1462                     continue;
 1463             }
 1464             ret = regcomp(&regex, s->answer.buf,
 1465                       REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
 1466             if (ret) {
 1467                 char errbuf[1024];
 1468 
 1469                 regerror(ret, &regex, errbuf, sizeof(errbuf));
 1470                 err(s, _("Malformed search regexp %s: %s"),
 1471                     s->answer.buf, errbuf);
 1472                 continue;
 1473             }
 1474             i = hunk_index;
 1475             for (;;) {
 1476                 /* render the hunk into a scratch buffer */
 1477                 render_hunk(s, file_diff->hunk + i, 0, 0,
 1478                         &s->buf);
 1479                 if (regexec(&regex, s->buf.buf, 0, NULL, 0)
 1480                     != REG_NOMATCH)
 1481                     break;
 1482                 i++;
 1483                 if (i == file_diff->hunk_nr)
 1484                     i = 0;
 1485                 if (i != hunk_index)
 1486                     continue;
 1487                 err(s, _("No hunk matches the given pattern"));
 1488                 break;
 1489             }
 1490             hunk_index = i;
 1491         } else if (s->answer.buf[0] == 's') {
 1492             size_t splittable_into = hunk->splittable_into;
 1493             if (splittable_into < 2)
 1494                 err(s, _("Sorry, cannot split this hunk"));
 1495             else if (!split_hunk(s, file_diff,
 1496                          hunk - file_diff->hunk))
 1497                 color_fprintf_ln(stdout, s->s.header_color,
 1498                          _("Split into %d hunks."),
 1499                          (int)splittable_into);
 1500         } else if (s->answer.buf[0] == 'e') {
 1501             if (hunk_index + 1 == file_diff->mode_change)
 1502                 err(s, _("Sorry, cannot edit this hunk"));
 1503             else if (edit_hunk_loop(s, file_diff, hunk) >= 0) {
 1504                 hunk->use = USE_HUNK;
 1505                 goto soft_increment;
 1506             }
 1507         } else {
 1508             const char *p = _(help_patch_remainder), *eol = p;
 1509 
 1510             color_fprintf(stdout, s->s.help_color, "%s",
 1511                       _(s->mode->help_patch_text));
 1512 
 1513             /*
 1514              * Show only those lines of the remainder that are
 1515              * actually applicable with the current hunk.
 1516              */
 1517             for (; *p; p = eol + (*eol == '\n')) {
 1518                 eol = strchrnul(p, '\n');
 1519 
 1520                 /*
 1521                  * `s->buf` still contains the part of the
 1522                  * commands shown in the prompt that are not
 1523                  * always available.
 1524                  */
 1525                 if (*p != '?' && !strchr(s->buf.buf, *p))
 1526                     continue;
 1527 
 1528                 color_fprintf_ln(stdout, s->s.help_color,
 1529                          "%.*s", (int)(eol - p), p);
 1530             }
 1531         }
 1532     }
 1533 
 1534     /* Any hunk to be used? */
 1535     for (i = 0; i < file_diff->hunk_nr; i++)
 1536         if (file_diff->hunk[i].use == USE_HUNK)
 1537             break;
 1538 
 1539     if (i < file_diff->hunk_nr) {
 1540         /* At least one hunk selected: apply */
 1541         strbuf_reset(&s->buf);
 1542         reassemble_patch(s, file_diff, 0, &s->buf);
 1543 
 1544         if (s->mode->apply_for_checkout)
 1545             apply_for_checkout(s, &s->buf,
 1546                        s->mode->is_reverse);
 1547         else {
 1548             setup_child_process(&cp, s, "apply", NULL);
 1549             argv_array_pushv(&cp.args, s->mode->apply);
 1550             if (pipe_command(&cp, s->buf.buf, s->buf.len,
 1551                      NULL, 0, NULL, 0))
 1552                 error(_("'git apply' failed"));
 1553         }
 1554         repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0);
 1555     }
 1556 
 1557     putchar('\n');
 1558     return quit;
 1559 }
 1560 
 1561 int run_add_p(struct repository *r, enum add_p_mode mode,
 1562           const char *revision, const struct pathspec *ps)
 1563 {
 1564     struct add_p_state s = {
 1565         { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
 1566     };
 1567     size_t i, binary_count = 0;
 1568 
 1569     if (init_add_i_state(r, &s.s))
 1570         return error("Could not read `add -i` config");
 1571 
 1572     if (mode == ADD_P_STASH)
 1573         s.mode = &patch_mode_stash;
 1574     else if (mode == ADD_P_RESET) {
 1575         if (!revision || !strcmp(revision, "HEAD"))
 1576             s.mode = &patch_mode_reset_head;
 1577         else
 1578             s.mode = &patch_mode_reset_nothead;
 1579     } else if (mode == ADD_P_CHECKOUT) {
 1580         if (!revision)
 1581             s.mode = &patch_mode_checkout_index;
 1582         else if (!strcmp(revision, "HEAD"))
 1583             s.mode = &patch_mode_checkout_head;
 1584         else
 1585             s.mode = &patch_mode_checkout_nothead;
 1586     } else if (mode == ADD_P_WORKTREE) {
 1587         if (!revision)
 1588             s.mode = &patch_mode_checkout_index;
 1589         else if (!strcmp(revision, "HEAD"))
 1590             s.mode = &patch_mode_worktree_head;
 1591         else
 1592             s.mode = &patch_mode_worktree_nothead;
 1593     } else
 1594         s.mode = &patch_mode_stage;
 1595     s.revision = revision;
 1596 
 1597     if (repo_refresh_and_write_index(r, REFRESH_QUIET, 0) < 0 ||
 1598         parse_diff(&s, ps) < 0) {
 1599         strbuf_release(&s.plain);
 1600         strbuf_release(&s.colored);
 1601         return -1;
 1602     }
 1603 
 1604     for (i = 0; i < s.file_diff_nr; i++)
 1605         if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr)
 1606             binary_count++;
 1607         else if (patch_update_file(&s, s.file_diff + i))
 1608             break;
 1609 
 1610     if (s.file_diff_nr == 0)
 1611         fprintf(stderr, _("No changes.\n"));
 1612     else if (binary_count == s.file_diff_nr)
 1613         fprintf(stderr, _("Only binary files changed.\n"));
 1614 
 1615     strbuf_release(&s.answer);
 1616     strbuf_release(&s.buf);
 1617     strbuf_release(&s.plain);
 1618     strbuf_release(&s.colored);
 1619     return 0;
 1620 }