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