Skip to content

Commit 11f2c0d

Browse files
dschogitster
authored andcommitted
built-in add -p: coalesce hunks after splitting them
This is considered "the right thing to do", according to 933e44d ("add -p": work-around an old laziness that does not coalesce hunks, 2011-04-06). Note: we cannot simply modify the hunks while merging them; Once we implement hunk editing, we will call `reassemble_patch()` whenever a hunk is edited, therefore we must not modify the hunks (because the user might e.g. hit `K` and change their mind whether to stage the previous hunk). Signed-off-by: Johannes Schindelin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 510aeca commit 11f2c0d

File tree

1 file changed

+57
-1
lines changed

1 file changed

+57
-1
lines changed

add-patch.c

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,55 @@ static void render_diff_header(struct add_p_state *s,
433433
}
434434
}
435435

436+
/* Coalesce hunks again that were split */
437+
static int merge_hunks(struct add_p_state *s, struct file_diff *file_diff,
438+
size_t *hunk_index, struct hunk *merged)
439+
{
440+
size_t i = *hunk_index;
441+
struct hunk *hunk = file_diff->hunk + i;
442+
/* `header` corresponds to the merged hunk */
443+
struct hunk_header *header = &merged->header, *next;
444+
445+
if (hunk->use != USE_HUNK)
446+
return 0;
447+
448+
*merged = *hunk;
449+
/* We simply skip the colored part (if any) when merging hunks */
450+
merged->colored_start = merged->colored_end = 0;
451+
452+
for (; i + 1 < file_diff->hunk_nr; i++) {
453+
hunk++;
454+
next = &hunk->header;
455+
456+
/*
457+
* Stop merging hunks when:
458+
*
459+
* - the hunk is not selected for use, or
460+
* - the hunk does not overlap with the already-merged hunk(s)
461+
*/
462+
if (hunk->use != USE_HUNK ||
463+
header->new_offset >= next->new_offset ||
464+
header->new_offset + header->new_count < next->new_offset ||
465+
merged->start >= hunk->start ||
466+
merged->end < hunk->start)
467+
break;
468+
469+
merged->end = hunk->end;
470+
merged->colored_end = hunk->colored_end;
471+
472+
header->old_count = next->old_offset + next->old_count
473+
- header->old_offset;
474+
header->new_count = next->new_offset + next->new_count
475+
- header->new_offset;
476+
}
477+
478+
if (i == *hunk_index)
479+
return 0;
480+
481+
*hunk_index = i;
482+
return 1;
483+
}
484+
436485
static void reassemble_patch(struct add_p_state *s,
437486
struct file_diff *file_diff, struct strbuf *out)
438487
{
@@ -443,12 +492,19 @@ static void reassemble_patch(struct add_p_state *s,
443492
render_diff_header(s, file_diff, 0, out);
444493

445494
for (i = file_diff->mode_change; i < file_diff->hunk_nr; i++) {
495+
struct hunk merged = { 0 };
496+
446497
hunk = file_diff->hunk + i;
447498
if (hunk->use != USE_HUNK)
448499
delta += hunk->header.old_count
449500
- hunk->header.new_count;
450-
else
501+
else {
502+
/* merge overlapping hunks into a temporary hunk */
503+
if (merge_hunks(s, file_diff, &i, &merged))
504+
hunk = &merged;
505+
451506
render_hunk(s, hunk, delta, 0, out);
507+
}
452508
}
453509
}
454510

0 commit comments

Comments
 (0)