diff options
author | Johannes Schindelin <johannes.schindelin@gmx.de> | 2019-12-13 08:07:59 +0000 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2019-12-13 12:37:14 -0800 |
commit | 11f2c0dae8f8889b533455d700121d437f4be19f (patch) | |
tree | c0b3e13141205d2c1948fa1d1e5f988fa003fd10 /add-patch.c | |
parent | 510aeca199c4feeb38d318cc151ecf5464a3a865 (diff) | |
download | git-11f2c0dae8f8889b533455d700121d437f4be19f.tar.gz |
built-in add -p: coalesce hunks after splitting them
This is considered "the right thing to do", according to 933e44d3a0
("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 <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'add-patch.c')
-rw-r--r-- | add-patch.c | 58 |
1 files changed, 57 insertions, 1 deletions
diff --git a/add-patch.c b/add-patch.c index 2d34ddd7f4..c8d84aec68 100644 --- a/add-patch.c +++ b/add-patch.c @@ -433,6 +433,55 @@ static void render_diff_header(struct add_p_state *s, } } +/* Coalesce hunks again that were split */ +static int merge_hunks(struct add_p_state *s, struct file_diff *file_diff, + size_t *hunk_index, struct hunk *merged) +{ + size_t i = *hunk_index; + struct hunk *hunk = file_diff->hunk + i; + /* `header` corresponds to the merged hunk */ + struct hunk_header *header = &merged->header, *next; + + if (hunk->use != USE_HUNK) + return 0; + + *merged = *hunk; + /* We simply skip the colored part (if any) when merging hunks */ + merged->colored_start = merged->colored_end = 0; + + for (; i + 1 < file_diff->hunk_nr; i++) { + hunk++; + next = &hunk->header; + + /* + * Stop merging hunks when: + * + * - the hunk is not selected for use, or + * - the hunk does not overlap with the already-merged hunk(s) + */ + if (hunk->use != USE_HUNK || + header->new_offset >= next->new_offset || + header->new_offset + header->new_count < next->new_offset || + merged->start >= hunk->start || + merged->end < hunk->start) + break; + + merged->end = hunk->end; + merged->colored_end = hunk->colored_end; + + header->old_count = next->old_offset + next->old_count + - header->old_offset; + header->new_count = next->new_offset + next->new_count + - header->new_offset; + } + + if (i == *hunk_index) + return 0; + + *hunk_index = i; + return 1; +} + static void reassemble_patch(struct add_p_state *s, struct file_diff *file_diff, struct strbuf *out) { @@ -443,12 +492,19 @@ static void reassemble_patch(struct add_p_state *s, render_diff_header(s, file_diff, 0, out); for (i = file_diff->mode_change; i < file_diff->hunk_nr; i++) { + struct hunk merged = { 0 }; + hunk = file_diff->hunk + i; if (hunk->use != USE_HUNK) delta += hunk->header.old_count - hunk->header.new_count; - else + else { + /* merge overlapping hunks into a temporary hunk */ + if (merge_hunks(s, file_diff, &i, &merged)) + hunk = &merged; + render_hunk(s, hunk, delta, 0, out); + } } } |