Skip to content

Commit 5906d5d

Browse files
dschogitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 47dc4fd commit 5906d5d

File tree

1 file changed

+104
-5
lines changed

1 file changed

+104
-5
lines changed

add-patch.c

Lines changed: 104 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ struct add_p_state {
3333
struct hunk head;
3434
struct hunk *hunk;
3535
size_t hunk_nr, hunk_alloc;
36-
unsigned deleted:1;
36+
unsigned deleted:1, mode_change:1;
3737
} *file_diff;
3838
size_t file_diff_nr;
3939
};
@@ -129,6 +129,17 @@ static int parse_hunk_header(struct add_p_state *s, struct hunk *hunk)
129129
return 0;
130130
}
131131

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+
132143
static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
133144
{
134145
struct argv_array args = ARGV_ARRAY_INIT;
@@ -181,7 +192,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
181192
pend = p + plain->len;
182193
while (p != pend) {
183194
char *eol = memchr(p, '\n', pend - p);
184-
const char *deleted = NULL;
195+
const char *deleted = NULL, *mode_change = NULL;
185196

186197
if (!eol)
187198
eol = pend;
@@ -218,8 +229,53 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
218229
file_diff->deleted = 1;
219230
else if (parse_hunk_header(s, hunk) < 0)
220231
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);
221272
}
222273

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+
223279
p = eol == pend ? pend : eol + 1;
224280
hunk->end = p - plain->buf;
225281

@@ -233,6 +289,16 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
233289

234290
hunk->colored_end = colored_p - colored->buf;
235291
}
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+
}
236302
}
237303

238304
return 0;
@@ -284,16 +350,49 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk,
284350
hunk->end - hunk->start);
285351
}
286352

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+
287386
static void reassemble_patch(struct add_p_state *s,
288387
struct file_diff *file_diff, struct strbuf *out)
289388
{
290389
struct hunk *hunk;
291390
size_t i;
292391
ssize_t delta = 0;
293392

294-
render_hunk(s, &file_diff->head, 0, 0, out);
393+
render_diff_header(s, file_diff, 0, out);
295394

296-
for (i = 0; i < file_diff->hunk_nr; i++) {
395+
for (i = file_diff->mode_change; i < file_diff->hunk_nr; i++) {
297396
hunk = file_diff->hunk + i;
298397
if (hunk->use != USE_HUNK)
299398
delta += hunk->header.old_count
@@ -328,7 +427,7 @@ static int patch_update_file(struct add_p_state *s,
328427
return 0;
329428

330429
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);
332431
fputs(s->buf.buf, stdout);
333432
for (;;) {
334433
if (hunk_index >= file_diff->hunk_nr)

0 commit comments

Comments
 (0)