summaryrefslogtreecommitdiff
path: root/add-patch.c
diff options
context:
space:
mode:
authorJohannes Schindelin <johannes.schindelin@gmx.de>2019-12-13 08:07:59 +0000
committerJunio C Hamano <gitster@pobox.com>2019-12-13 12:37:14 -0800
commit11f2c0dae8f8889b533455d700121d437f4be19f (patch)
treec0b3e13141205d2c1948fa1d1e5f988fa003fd10 /add-patch.c
parent510aeca199c4feeb38d318cc151ecf5464a3a865 (diff)
downloadgit-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.c58
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);
+ }
}
}