@matheusbsbs / matheus-bagliano / commits / 5906d5de77
Fork 0

built-in app -p: allow selecting a mode change as a "hunk"

This imitates the way the Perl version treats mode changes: it offers the mode change up for the user to decide, as if it was a diff hunk. In contrast to the Perl version, we make use of the fact that the mode line is the first hunk, and explicitly strip out that line from the diff header if that "hunk" was not selected to be applied, and skipping that hunk while coalescing the diff. The Perl version plays some kind of diff line lego instead. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Dec 13, 2019 at 08:07 UTC 5906d5de77628e956d708e9a1796c12bee316b59
1 file changed +104 -5
add-patch.c
+104 -5
@@ -33,7 +33,7 @@ struct add_p_state {
33 struct hunk head;
34 struct hunk *hunk;
35 size_t hunk_nr, hunk_alloc;
36 - unsigned deleted:1;
36 + unsigned deleted:1, mode_change:1;
37 } *file_diff;
38 size_t file_diff_nr;
39 };
@@ -129,6 +129,17 @@ static int parse_hunk_header(struct add_p_state *s, struct hunk *hunk)
129 return 0;
130 }
131
132 +static int is_octal(const char *p, size_t len)
133 +{
134 + if (!len)
135 + return 0;
136 +
137 + while (len--)
138 + if (*p < '0' || *(p++) > '7')
139 + return 0;
140 + return 1;
141 +}
142 +
143 static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
144 {
145 struct argv_array args = ARGV_ARRAY_INIT;
@@ -181,7 +192,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
192 pend = p + plain->len;
193 while (p != pend) {
194 char *eol = memchr(p, '\n', pend - p);
184 - const char *deleted = NULL;
195 + const char *deleted = NULL, *mode_change = NULL;
196
197 if (!eol)
198 eol = pend;
@@ -218,8 +229,53 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
229 file_diff->deleted = 1;
230 else if (parse_hunk_header(s, hunk) < 0)
231 return -1;
232 + } else if (hunk == &file_diff->head &&
233 + skip_prefix(p, "old mode ", &mode_change) &&
234 + is_octal(mode_change, eol - mode_change)) {
235 + if (file_diff->mode_change)
236 + BUG("double mode change?\n\n%.*s",
237 + (int)(eol - plain->buf), plain->buf);
238 + if (file_diff->hunk_nr++)
239 + BUG("mode change in the middle?\n\n%.*s",
240 + (int)(eol - plain->buf), plain->buf);
241 +
242 + /*
243 + * Do *not* change `hunk`: the mode change pseudo-hunk
244 + * is _part of_ the header "hunk".
245 + */
246 + file_diff->mode_change = 1;
247 + ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
248 + file_diff->hunk_alloc);
249 + memset(file_diff->hunk, 0, sizeof(struct hunk));
250 + file_diff->hunk->start = p - plain->buf;
251 + if (colored_p)
252 + file_diff->hunk->colored_start =
253 + colored_p - colored->buf;
254 + } else if (hunk == &file_diff->head &&
255 + skip_prefix(p, "new mode ", &mode_change) &&
256 + is_octal(mode_change, eol - mode_change)) {
257 +
258 + /*
259 + * Extend the "mode change" pseudo-hunk to include also
260 + * the "new mode" line.
261 + */
262 + if (!file_diff->mode_change)
263 + BUG("'new mode' without 'old mode'?\n\n%.*s",
264 + (int)(eol - plain->buf), plain->buf);
265 + if (file_diff->hunk_nr != 1)
266 + BUG("mode change in the middle?\n\n%.*s",
267 + (int)(eol - plain->buf), plain->buf);
268 + if (p - plain->buf != file_diff->hunk->end)
269 + BUG("'new mode' does not immediately follow "
270 + "'old mode'?\n\n%.*s",
271 + (int)(eol - plain->buf), plain->buf);
272 }
273
274 + if (file_diff->deleted && file_diff->mode_change)
275 + BUG("diff contains delete *and* a mode change?!?\n%.*s",
276 + (int)(eol - (plain->buf + file_diff->head.start)),
277 + plain->buf + file_diff->head.start);
278 +
279 p = eol == pend ? pend : eol + 1;
280 hunk->end = p - plain->buf;
281
@@ -233,6 +289,16 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
289
290 hunk->colored_end = colored_p - colored->buf;
291 }
292 +
293 + if (mode_change) {
294 + if (file_diff->hunk_nr != 1)
295 + BUG("mode change in hunk #%d???",
296 + (int)file_diff->hunk_nr);
297 + /* Adjust the end of the "mode change" pseudo-hunk */
298 + file_diff->hunk->end = hunk->end;
299 + if (colored)
300 + file_diff->hunk->colored_end = hunk->colored_end;
301 + }
302 }
303
304 return 0;
@@ -284,6 +350,39 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk,
350 hunk->end - hunk->start);
351 }
352
353 +static void render_diff_header(struct add_p_state *s,
354 + struct file_diff *file_diff, int colored,
355 + struct strbuf *out)
356 +{
357 + /*
358 + * If there was a mode change, the first hunk is a pseudo hunk that
359 + * corresponds to the mode line in the header. If the user did not want
360 + * to stage that "hunk", we actually have to cut it out from the header.
361 + */
362 + int skip_mode_change =
363 + file_diff->mode_change && file_diff->hunk->use != USE_HUNK;
364 + struct hunk *head = &file_diff->head, *first = file_diff->hunk;
365 +
366 + if (!skip_mode_change) {
367 + render_hunk(s, head, 0, colored, out);
368 + return;
369 + }
370 +
371 + if (colored) {
372 + const char *p = s->colored.buf;
373 +
374 + strbuf_add(out, p + head->colored_start,
375 + first->colored_start - head->colored_start);
376 + strbuf_add(out, p + first->colored_end,
377 + head->colored_end - first->colored_end);
378 + } else {
379 + const char *p = s->plain.buf;
380 +
381 + strbuf_add(out, p + head->start, first->start - head->start);
382 + strbuf_add(out, p + first->end, head->end - first->end);
383 + }
384 +}
385 +
386 static void reassemble_patch(struct add_p_state *s,
387 struct file_diff *file_diff, struct strbuf *out)
388 {
@@ -291,9 +390,9 @@ static void reassemble_patch(struct add_p_state *s,
390 size_t i;
391 ssize_t delta = 0;
392
294 - render_hunk(s, &file_diff->head, 0, 0, out);
393 + render_diff_header(s, file_diff, 0, out);
394
296 - for (i = 0; i < file_diff->hunk_nr; i++) {
395 + for (i = file_diff->mode_change; i < file_diff->hunk_nr; i++) {
396 hunk = file_diff->hunk + i;
397 if (hunk->use != USE_HUNK)
398 delta += hunk->header.old_count
@@ -328,7 +427,7 @@ static int patch_update_file(struct add_p_state *s,
427 return 0;
428
429 strbuf_reset(&s->buf);
331 - render_hunk(s, &file_diff->head, 0, colored, &s->buf);
430 + render_diff_header(s, file_diff, colored, &s->buf);
431 fputs(s->buf.buf, stdout);
432 for (;;) {
433 if (hunk_index >= file_diff->hunk_nr)