Coverage Report

Created: 2024-09-08 06:24

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