Skip to content

Commit 9aa3dd8

Browse files
committed
built-in add -p: support multi-file diffs
For simplicity, the initial implementation in C handled only a single modified file. Now it handles an arbitrary number of files. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 7458cd5 commit 9aa3dd8

File tree

1 file changed

+52
-38
lines changed

1 file changed

+52
-38
lines changed

add-patch.c

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@ struct add_p_state {
2929

3030
/* parsed diff */
3131
struct strbuf plain, colored;
32-
struct hunk head;
33-
struct hunk *hunk;
34-
size_t hunk_nr, hunk_alloc;
32+
struct file_diff {
33+
struct hunk head;
34+
struct hunk *hunk;
35+
size_t hunk_nr, hunk_alloc;
36+
} *file_diff;
37+
size_t file_diff_nr;
3538
};
3639

3740
static void err(struct add_p_state *s, const char *fmt, ...)
@@ -131,7 +134,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
131134
struct strbuf *plain = &s->plain, *colored = NULL;
132135
struct child_process cp = CHILD_PROCESS_INIT;
133136
char *p, *pend, *colored_p = NULL, *colored_pend = NULL;
134-
size_t i, color_arg_index;
137+
size_t file_diff_alloc = 0, i, color_arg_index;
138+
struct file_diff *file_diff = NULL;
135139
struct hunk *hunk = NULL;
136140
int res;
137141

@@ -171,7 +175,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
171175
}
172176
argv_array_clear(&args);
173177

174-
/* parse hunks */
178+
/* parse files and hunks */
175179
p = plain->buf;
176180
pend = p + plain->len;
177181
while (p != pend) {
@@ -180,17 +184,23 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
180184
eol = pend;
181185

182186
if (starts_with(p, "diff ")) {
183-
if (p != plain->buf)
184-
BUG("multi-file diff not yet handled");
185-
hunk = &s->head;
187+
s->file_diff_nr++;
188+
ALLOC_GROW(s->file_diff, s->file_diff_nr,
189+
file_diff_alloc);
190+
file_diff = s->file_diff + s->file_diff_nr - 1;
191+
memset(file_diff, 0, sizeof(*file_diff));
192+
hunk = &file_diff->head;
193+
hunk->start = p - plain->buf;
194+
if (colored_p)
195+
hunk->colored_start = colored_p - colored->buf;
186196
} else if (p == plain->buf)
187197
BUG("diff starts with unexpected line:\n"
188198
"%.*s\n", (int)(eol - p), p);
189199
else if (starts_with(p, "@@ ")) {
190-
s->hunk_nr++;
191-
ALLOC_GROW(s->hunk, s->hunk_nr,
192-
s->hunk_alloc);
193-
hunk = s->hunk + s->hunk_nr - 1;
200+
file_diff->hunk_nr++;
201+
ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
202+
file_diff->hunk_alloc);
203+
hunk = file_diff->hunk + file_diff->hunk_nr - 1;
194204
memset(hunk, 0, sizeof(*hunk));
195205

196206
hunk->start = p - plain->buf;
@@ -262,16 +272,17 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk,
262272
hunk->end - hunk->start);
263273
}
264274

265-
static void reassemble_patch(struct add_p_state *s, struct strbuf *out)
275+
static void reassemble_patch(struct add_p_state *s,
276+
struct file_diff *file_diff, struct strbuf *out)
266277
{
267278
struct hunk *hunk;
268279
size_t i;
269280
ssize_t delta = 0;
270281

271-
render_hunk(s, &s->head, 0, 0, out);
282+
render_hunk(s, &file_diff->head, 0, 0, out);
272283

273-
for (i = 0; i < s->hunk_nr; i++) {
274-
hunk = s->hunk + i;
284+
for (i = 0; i < file_diff->hunk_nr; i++) {
285+
hunk = file_diff->hunk + i;
275286
if (hunk->use != USE_HUNK)
276287
delta += hunk->header.old_count
277288
- hunk->header.new_count;
@@ -291,7 +302,8 @@ N_("y - stage this hunk\n"
291302
"K - leave this hunk undecided, see previous hunk\n"
292303
"? - print help\n");
293304

294-
static int patch_update_file(struct add_p_state *s)
305+
static int patch_update_file(struct add_p_state *s,
306+
struct file_diff *file_diff)
295307
{
296308
size_t hunk_index = 0;
297309
ssize_t i, undecided_previous, undecided_next;
@@ -300,27 +312,27 @@ static int patch_update_file(struct add_p_state *s)
300312
struct child_process cp = CHILD_PROCESS_INIT;
301313
int colored = !!s->colored.len;
302314

303-
if (!s->hunk_nr)
315+
if (!file_diff->hunk_nr)
304316
return 0;
305317

306318
strbuf_reset(&s->buf);
307-
render_hunk(s, &s->head, 0, colored, &s->buf);
319+
render_hunk(s, &file_diff->head, 0, colored, &s->buf);
308320
fputs(s->buf.buf, stdout);
309321
for (;;) {
310-
if (hunk_index >= s->hunk_nr)
322+
if (hunk_index >= file_diff->hunk_nr)
311323
hunk_index = 0;
312-
hunk = s->hunk + hunk_index;
324+
hunk = file_diff->hunk + hunk_index;
313325

314326
undecided_previous = -1;
315327
for (i = hunk_index - 1; i >= 0; i--)
316-
if (s->hunk[i].use == UNDECIDED_HUNK) {
328+
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
317329
undecided_previous = i;
318330
break;
319331
}
320332

321333
undecided_next = -1;
322-
for (i = hunk_index + 1; i < s->hunk_nr; i++)
323-
if (s->hunk[i].use == UNDECIDED_HUNK) {
334+
for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
335+
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
324336
undecided_next = i;
325337
break;
326338
}
@@ -341,7 +353,7 @@ static int patch_update_file(struct add_p_state *s)
341353
strbuf_addstr(&s->buf, ",K");
342354
if (undecided_next >= 0)
343355
strbuf_addstr(&s->buf, ",j");
344-
if (hunk_index + 1 < s->hunk_nr)
356+
if (hunk_index + 1 < file_diff->hunk_nr)
345357
strbuf_addstr(&s->buf, ",J");
346358
color_fprintf(stdout, s->s.prompt_color,
347359
_("Stage this hunk [y,n,a,d%s,?]? "),
@@ -357,22 +369,22 @@ static int patch_update_file(struct add_p_state *s)
357369
if (ch == 'y') {
358370
hunk->use = USE_HUNK;
359371
soft_increment:
360-
while (++hunk_index < s->hunk_nr &&
361-
s->hunk[hunk_index].use
372+
while (++hunk_index < file_diff->hunk_nr &&
373+
file_diff->hunk[hunk_index].use
362374
!= UNDECIDED_HUNK)
363375
; /* continue looking */
364376
} else if (ch == 'n') {
365377
hunk->use = SKIP_HUNK;
366378
goto soft_increment;
367379
} else if (ch == 'a') {
368-
for (; hunk_index < s->hunk_nr; hunk_index++) {
369-
hunk = s->hunk + hunk_index;
380+
for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
381+
hunk = file_diff->hunk + hunk_index;
370382
if (hunk->use == UNDECIDED_HUNK)
371383
hunk->use = USE_HUNK;
372384
}
373385
} else if (ch == 'd') {
374-
for (; hunk_index < s->hunk_nr; hunk_index++) {
375-
hunk = s->hunk + hunk_index;
386+
for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
387+
hunk = file_diff->hunk + hunk_index;
376388
if (hunk->use == UNDECIDED_HUNK)
377389
hunk->use = SKIP_HUNK;
378390
}
@@ -382,7 +394,7 @@ static int patch_update_file(struct add_p_state *s)
382394
else
383395
err(s, _("No previous hunk"));
384396
} else if (s->answer.buf[0] == 'J') {
385-
if (hunk_index + 1 < s->hunk_nr)
397+
if (hunk_index + 1 < file_diff->hunk_nr)
386398
hunk_index++;
387399
else
388400
err(s, _("No next hunk"));
@@ -402,14 +414,14 @@ static int patch_update_file(struct add_p_state *s)
402414
}
403415

404416
/* Any hunk to be used? */
405-
for (i = 0; i < s->hunk_nr; i++)
406-
if (s->hunk[i].use == USE_HUNK)
417+
for (i = 0; i < file_diff->hunk_nr; i++)
418+
if (file_diff->hunk[i].use == USE_HUNK)
407419
break;
408420

409-
if (i < s->hunk_nr) {
421+
if (i < file_diff->hunk_nr) {
410422
/* At least one hunk selected: apply */
411423
strbuf_reset(&s->buf);
412-
reassemble_patch(s, &s->buf);
424+
reassemble_patch(s, file_diff, &s->buf);
413425

414426
setup_child_process(&cp, s, "apply", "--cached", NULL);
415427
if (pipe_command(&cp, s->buf.buf, s->buf.len,
@@ -427,6 +439,7 @@ int run_add_p(struct repository *r, const struct pathspec *ps)
427439
struct add_p_state s = {
428440
{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
429441
};
442+
size_t i;
430443

431444
if (init_add_i_state(r, &s.s))
432445
return error("Could not read `add -i` config");
@@ -438,8 +451,9 @@ int run_add_p(struct repository *r, const struct pathspec *ps)
438451
return -1;
439452
}
440453

441-
if (s.hunk_nr)
442-
patch_update_file(&s);
454+
for (i = 0; i < s.file_diff_nr; i++)
455+
if (patch_update_file(&s, s.file_diff + i))
456+
break;
443457

444458
strbuf_release(&s.answer);
445459
strbuf_release(&s.buf);

0 commit comments

Comments
 (0)