@matheusbsbs / matheus-bagliano / commits / 23707811c5
Fork 0

diff: do not chomp hunk-header in the middle of a character

We truncate hunk-header line at 80 bytes, but that 80th byte could be in the middle of a character, which is bad. This uses pick_one_utf8_char() function to make sure we do not cut a character in the middle. This assumes that the internal representation of the text is UTF-8. This needs to be extended in the future but the optimal direction has not been decided yet. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jan 2, 2008 at 01:50 UTC 23707811c56a7756cbd6188e510f0a486c35c929
2 files changed +69
diff.c
+25
@@ -10,6 +10,7 @@
10 #include "color.h"
11 #include "attr.h"
12 #include "run-command.h"
13 +#include "utf8.h"
14
15 #ifdef NO_FAST_WORKING_DIRECTORY
16 #define FAST_WORKING_DIRECTORY 0
@@ -469,10 +470,13 @@ static void diff_words_show(struct diff_words_data *diff_words)
470 }
471 }
472
473 +typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
474 +
475 struct emit_callback {
476 struct xdiff_emit_state xm;
477 int nparents, color_diff;
478 unsigned ws_rule;
479 + sane_truncate_fn truncate;
480 const char **label_path;
481 struct diff_words_data *diff_words;
482 int *found_changesp;
@@ -525,6 +529,24 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons
529 }
530 }
531
532 +static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, unsigned long len)
533 +{
534 + const char *cp;
535 + unsigned long allot;
536 + size_t l = len;
537 +
538 + if (ecb->truncate)
539 + return ecb->truncate(line, len);
540 + cp = line;
541 + allot = l;
542 + while (0 < l) {
543 + (void) utf8_width(&cp, &l);
544 + if (!cp)
545 + break; /* truncated in the middle? */
546 + }
547 + return allot - l;
548 +}
549 +
550 static void fn_out_consume(void *priv, char *line, unsigned long len)
551 {
552 int i;
@@ -555,8 +577,11 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
577 ;
578 if (2 <= i && i < len && line[i] == ' ') {
579 ecbdata->nparents = i - 1;
580 + len = sane_truncate_line(ecbdata, line, len);
581 emit_line(diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
582 reset, line, len);
583 + if (line[len-1] != '\n')
584 + putchar('\n');
585 return;
586 }
587
t/t4025-hunk-header.sh new
+44
@@ -0,0 +1,44 @@
1 +#!/bin/sh
2 +
3 +test_description='diff hunk header truncation'
4 +
5 +. ./test-lib.sh
6 +
7 +N='日本語'
8 +N1='日'
9 +N2='日本'
10 +NS="$N$N$N$N$N$N$N$N$N$N$N$N$N"
11 +
12 +test_expect_success setup '
13 +
14 + (
15 + echo "A $NS"
16 + for c in B C D E F G H I J K
17 + do
18 + echo " $c"
19 + done
20 + echo "L $NS"
21 + for c in M N O P Q R S T U V
22 + do
23 + echo " $c"
24 + done
25 + ) >file &&
26 + git add file &&
27 +
28 + sed -e "/^ [EP]/s/$/ modified/" <file >file+ &&
29 + mv file+ file
30 +
31 +'
32 +
33 +test_expect_success 'hunk header truncation with an overly long line' '
34 +
35 + git diff | sed -n -e "s/^.*@@//p" >actual &&
36 + (
37 + echo " A $N$N$N$N$N$N$N$N$N2"
38 + echo " L $N$N$N$N$N$N$N$N$N1"
39 + ) >expected &&
40 + diff -u actual expected
41 +
42 +'
43 +
44 +test_done