Skip to content

Commit 794a908

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 7c75f74 commit 794a908

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, ...)
@@ -130,7 +133,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
130133
struct strbuf *plain = &s->plain, *colored = NULL;
131134
struct child_process cp = CHILD_PROCESS_INIT;
132135
char *p, *pend, *colored_p = NULL, *colored_pend = NULL;
133-
size_t i, color_arg_index;
136+
size_t file_diff_alloc = 0, i, color_arg_index;
137+
struct file_diff *file_diff = NULL;
134138
struct hunk *hunk = NULL;
135139
int res;
136140

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

173-
/* parse hunks */
177+
/* parse files and hunks */
174178
p = plain->buf;
175179
pend = p + plain->len;
176180
while (p != pend) {
@@ -179,17 +183,23 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
179183
eol = pend;
180184

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

195205
hunk->start = p - plain->buf;
@@ -261,16 +271,17 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk,
261271
hunk->end - hunk->start);
262272
}
263273

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

270-
render_hunk(s, &s->head, 0, 0, out);
281+
render_hunk(s, &file_diff->head, 0, 0, out);
271282

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

293-
static int patch_update_file(struct add_p_state *s)
304+
static int patch_update_file(struct add_p_state *s,
305+
struct file_diff *file_diff)
294306
{
295307
size_t hunk_index = 0;
296308
ssize_t i, undecided_previous, undecided_next;
@@ -299,27 +311,27 @@ static int patch_update_file(struct add_p_state *s)
299311
struct child_process cp = CHILD_PROCESS_INIT;
300312
int colored = !!s->colored.len;
301313

302-
if (!s->hunk_nr)
314+
if (!file_diff->hunk_nr)
303315
return 0;
304316

305317
strbuf_reset(&s->buf);
306-
render_hunk(s, &s->head, 0, colored, &s->buf);
318+
render_hunk(s, &file_diff->head, 0, colored, &s->buf);
307319
fputs(s->buf.buf, stdout);
308320
for (;;) {
309-
if (hunk_index >= s->hunk_nr)
321+
if (hunk_index >= file_diff->hunk_nr)
310322
hunk_index = 0;
311-
hunk = s->hunk + hunk_index;
323+
hunk = file_diff->hunk + hunk_index;
312324

313325
undecided_previous = -1;
314326
for (i = hunk_index - 1; i >= 0; i--)
315-
if (s->hunk[i].use == UNDECIDED_HUNK) {
327+
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
316328
undecided_previous = i;
317329
break;
318330
}
319331

320332
undecided_next = -1;
321-
for (i = hunk_index + 1; i < s->hunk_nr; i++)
322-
if (s->hunk[i].use == UNDECIDED_HUNK) {
333+
for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
334+
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
323335
undecided_next = i;
324336
break;
325337
}
@@ -340,7 +352,7 @@ static int patch_update_file(struct add_p_state *s)
340352
strbuf_addstr(&s->buf, ",K");
341353
if (undecided_next >= 0)
342354
strbuf_addstr(&s->buf, ",j");
343-
if (hunk_index + 1 < s->hunk_nr)
355+
if (hunk_index + 1 < file_diff->hunk_nr)
344356
strbuf_addstr(&s->buf, ",J");
345357
color_fprintf(stdout, s->s.prompt_color,
346358
_("Stage this hunk [y,n,a,d%s,?]? "),
@@ -356,22 +368,22 @@ static int patch_update_file(struct add_p_state *s)
356368
if (ch == 'y') {
357369
hunk->use = USE_HUNK;
358370
soft_increment:
359-
while (++hunk_index < s->hunk_nr &&
360-
s->hunk[hunk_index].use
371+
while (++hunk_index < file_diff->hunk_nr &&
372+
file_diff->hunk[hunk_index].use
361373
!= UNDECIDED_HUNK)
362374
; /* continue looking */
363375
} else if (ch == 'n') {
364376
hunk->use = SKIP_HUNK;
365377
goto soft_increment;
366378
} else if (ch == 'a') {
367-
for (; hunk_index < s->hunk_nr; hunk_index++) {
368-
hunk = s->hunk + hunk_index;
379+
for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
380+
hunk = file_diff->hunk + hunk_index;
369381
if (hunk->use == UNDECIDED_HUNK)
370382
hunk->use = USE_HUNK;
371383
}
372384
} else if (ch == 'd') {
373-
for (; hunk_index < s->hunk_nr; hunk_index++) {
374-
hunk = s->hunk + hunk_index;
385+
for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
386+
hunk = file_diff->hunk + hunk_index;
375387
if (hunk->use == UNDECIDED_HUNK)
376388
hunk->use = SKIP_HUNK;
377389
}
@@ -381,7 +393,7 @@ static int patch_update_file(struct add_p_state *s)
381393
else
382394
err(s, _("No previous hunk"));
383395
} else if (s->answer.buf[0] == 'J') {
384-
if (hunk_index + 1 < s->hunk_nr)
396+
if (hunk_index + 1 < file_diff->hunk_nr)
385397
hunk_index++;
386398
else
387399
err(s, _("No next hunk"));
@@ -401,14 +413,14 @@ static int patch_update_file(struct add_p_state *s)
401413
}
402414

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

408-
if (i < s->hunk_nr) {
420+
if (i < file_diff->hunk_nr) {
409421
/* At least one hunk selected: apply */
410422
strbuf_reset(&s->buf);
411-
reassemble_patch(s, &s->buf);
423+
reassemble_patch(s, file_diff, &s->buf);
412424

413425
setup_child_process(&cp, s, "apply", "--cached", NULL);
414426
if (pipe_command(&cp, s->buf.buf, s->buf.len,
@@ -426,6 +438,7 @@ int run_add_p(struct repository *r, const struct pathspec *ps)
426438
struct add_p_state s = {
427439
{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
428440
};
441+
size_t i;
429442

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

440-
if (s.hunk_nr)
441-
patch_update_file(&s);
453+
for (i = 0; i < s.file_diff_nr; i++)
454+
if (patch_update_file(&s, s.file_diff + i))
455+
break;
442456

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

0 commit comments

Comments
 (0)