Skip to content

Commit 6a41481

Browse files
committed
whitespace: symbolic links usually lack LF at the end
For a patch that touches a symbolic link, it is perfectly normal that the contents ends with "\ No newline at end of file". The checks introduced recently to detect incomplete lines (i.e., a text file that lack the newline on its final line) should not trigger. Disable the check early for symbolic links, both in "git apply" and "git diff" and test them. For "git apply", we check only when the postimage is a symbolic link regardless of the preimage, and we only care about preimage when applying in reverse. Similarly, "git diff" would warn only when the postimage is a symbolic link, or the preimage when running "git diff -R". Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 51358a1 commit 6a41481

File tree

4 files changed

+152
-2
lines changed

4 files changed

+152
-2
lines changed

apply.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,26 @@ static int parse_fragment(struct apply_state *state,
17251725
unsigned long oldlines, newlines;
17261726
unsigned long leading, trailing;
17271727

1728+
/* do not complain a symbolic link being an incomplete line */
1729+
if (patch->ws_rule & WS_INCOMPLETE_LINE) {
1730+
/*
1731+
* We want to figure out if the postimage is a
1732+
* symbolic link when applying the patch normally, or
1733+
* if the preimage is a symbolic link when applying
1734+
* the patch in reverse. A normal patch only has
1735+
* old_mode without new_mode. If it changes the
1736+
* filemode, new_mode has value, which is different
1737+
* from old_mode.
1738+
*/
1739+
unsigned mode = (state->apply_in_reverse
1740+
? patch->old_mode
1741+
: patch->new_mode
1742+
? patch->new_mode
1743+
: patch->old_mode);
1744+
if (mode && S_ISLNK(mode))
1745+
patch->ws_rule &= ~WS_INCOMPLETE_LINE;
1746+
}
1747+
17281748
offset = parse_fragment_header(line, len, fragment);
17291749
if (offset < 0)
17301750
return -1;

diff.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,6 +1834,7 @@ static void emit_rewrite_diff(const char *name_a,
18341834
const char *a_prefix, *b_prefix;
18351835
char *data_one, *data_two;
18361836
size_t size_one, size_two;
1837+
unsigned ws_rule;
18371838
struct emit_callback ecbdata;
18381839
struct strbuf out = STRBUF_INIT;
18391840

@@ -1856,9 +1857,15 @@ static void emit_rewrite_diff(const char *name_a,
18561857
size_one = fill_textconv(o->repo, textconv_one, one, &data_one);
18571858
size_two = fill_textconv(o->repo, textconv_two, two, &data_two);
18581859

1860+
ws_rule = whitespace_rule(o->repo->index, name_b);
1861+
1862+
/* symlink being an incomplete line is not a news */
1863+
if (DIFF_FILE_VALID(two) && S_ISLNK(two->mode))
1864+
ws_rule &= ~WS_INCOMPLETE_LINE;
1865+
18591866
memset(&ecbdata, 0, sizeof(ecbdata));
18601867
ecbdata.color_diff = o->use_color;
1861-
ecbdata.ws_rule = whitespace_rule(o->repo->index, name_b);
1868+
ecbdata.ws_rule = ws_rule;
18621869
ecbdata.opt = o;
18631870
if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
18641871
mmfile_t mf1, mf2;
@@ -3762,6 +3769,7 @@ static void builtin_diff(const char *name_a,
37623769
xpparam_t xpp;
37633770
xdemitconf_t xecfg;
37643771
struct emit_callback ecbdata;
3772+
unsigned ws_rule;
37653773
const struct userdiff_funcname *pe;
37663774

37673775
if (must_show_header) {
@@ -3773,6 +3781,12 @@ static void builtin_diff(const char *name_a,
37733781
mf1.size = fill_textconv(o->repo, textconv_one, one, &mf1.ptr);
37743782
mf2.size = fill_textconv(o->repo, textconv_two, two, &mf2.ptr);
37753783

3784+
ws_rule = whitespace_rule(o->repo->index, name_b);
3785+
3786+
/* symlink being an incomplete line is not a news */
3787+
if (DIFF_FILE_VALID(two) && S_ISLNK(two->mode))
3788+
ws_rule &= ~WS_INCOMPLETE_LINE;
3789+
37763790
pe = diff_funcname_pattern(o, one);
37773791
if (!pe)
37783792
pe = diff_funcname_pattern(o, two);
@@ -3784,7 +3798,7 @@ static void builtin_diff(const char *name_a,
37843798
lbl[0] = NULL;
37853799
ecbdata.label_path = lbl;
37863800
ecbdata.color_diff = o->use_color;
3787-
ecbdata.ws_rule = whitespace_rule(o->repo->index, name_b);
3801+
ecbdata.ws_rule = ws_rule;
37883802
if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
37893803
check_blank_at_eof(&mf1, &mf2, &ecbdata);
37903804
ecbdata.opt = o;
@@ -3991,6 +4005,10 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
39914005
data.ws_rule = whitespace_rule(o->repo->index, attr_path);
39924006
data.conflict_marker_size = ll_merge_marker_size(o->repo->index, attr_path);
39934007

4008+
/* symlink being an incomplete line is not a news */
4009+
if (DIFF_FILE_VALID(two) && S_ISLNK(two->mode))
4010+
data.ws_rule &= ~WS_INCOMPLETE_LINE;
4011+
39944012
if (fill_mmfile(o->repo, &mf1, one) < 0 ||
39954013
fill_mmfile(o->repo, &mf2, two) < 0)
39964014
die("unable to read files to diff");

t/t4015-diff-whitespace.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,32 @@ test_expect_success "new incomplete line in post-image" '
9090
git -c core.whitespace=incomplete diff -R --check x
9191
'
9292

93+
test_expect_success SYMLINKS "incomplete-line error is disabled for symlinks" '
94+
test_when_finished "git reset --hard" &&
95+
test_when_finished "rm -f mylink" &&
96+
97+
# a regular file with an incomplete line
98+
printf "%s" one >mylink &&
99+
git add mylink &&
100+
101+
# a symbolic link
102+
rm mylink &&
103+
ln -s two mylink &&
104+
105+
git -c diff.color=always -c core.whitespace=incomplete \
106+
diff mylink >forward.raw &&
107+
test_decode_color >forward <forward.raw &&
108+
test_grep ! "<BRED>\\\\ No newline at end of file<RESET>" forward &&
109+
110+
git -c diff.color=always -c core.whitespace=incomplete \
111+
diff -R mylink >reverse.raw &&
112+
test_decode_color >reverse <reverse.raw &&
113+
test_grep "<BRED>\\\\ No newline at end of file<RESET>" reverse &&
114+
115+
git -c core.whitespace=incomplete diff --check mylink &&
116+
test_must_fail git -c core.whitespace=incomplete diff --check -R mylink
117+
'
118+
93119
test_expect_success "Ray Lehtiniemi's example" '
94120
cat <<-\EOF >x &&
95121
do {

t/t4124-apply-ws-rule.sh

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,4 +743,90 @@ test_expect_success 'incomplete line modified at the end (error)' '
743743
test_cmp sample target
744744
'
745745

746+
test_expect_success "incomplete-line error is disabled for symlinks" '
747+
test_when_finished "git reset" &&
748+
test_when_finished "rm -f patch.txt" &&
749+
oneblob=$(printf "one" | git hash-object --stdin -w -t blob) &&
750+
twoblob=$(printf "two" | git hash-object --stdin -w -t blob) &&
751+
752+
oneshort=$(git rev-parse --short $oneblob) &&
753+
twoshort=$(git rev-parse --short $twoblob) &&
754+
755+
cat >patch0.txt <<-EOF &&
756+
diff --git a/mylink b/mylink
757+
index $oneshort..$twoshort 120000
758+
--- a/mylink
759+
+++ b/mylink
760+
@@ -1 +1 @@
761+
-one
762+
\ No newline at end of file
763+
+two
764+
\ No newline at end of file
765+
EOF
766+
767+
# the index has the preimage symlink
768+
git update-index --add --cacheinfo "120000,$oneblob,mylink" &&
769+
770+
# check the patch going forward and reverse
771+
git -c core.whitespace=incomplete apply --cached --check \
772+
--whitespace=error patch0.txt &&
773+
774+
git update-index --add --cacheinfo "120000,$twoblob,mylink" &&
775+
git -c core.whitespace=incomplete apply --cached --check \
776+
--whitespace=error -R patch0.txt &&
777+
778+
# the patch turns it into the postimage symlink
779+
git update-index --add --cacheinfo "120000,$oneblob,mylink" &&
780+
git -c core.whitespace=incomplete apply --cached --whitespace=error \
781+
patch0.txt &&
782+
783+
# and then back.
784+
git -c core.whitespace=incomplete apply --cached -R --whitespace=error \
785+
patch0.txt &&
786+
787+
# a text file turns into a symlink
788+
cat >patch1.txt <<-EOF &&
789+
diff --git a/mylink b/mylink
790+
deleted file mode 100644
791+
index $oneshort..0000000
792+
--- a/mylink
793+
+++ /dev/null
794+
@@ -1 +0,0 @@
795+
-one
796+
\ No newline at end of file
797+
diff --git a/mylink b/mylink
798+
new file mode 120000
799+
index 0000000..$twoshort
800+
--- /dev/null
801+
+++ b/mylink
802+
@@ -0,0 +1 @@
803+
+two
804+
\ No newline at end of file
805+
EOF
806+
807+
# the index has the preimage text
808+
git update-index --cacheinfo "100644,$oneblob,mylink" &&
809+
810+
# check
811+
git -c core.whitespace=incomplete apply --cached \
812+
--check --whitespace=error patch1.txt &&
813+
814+
# reverse, leaving an incomplete text file, should error
815+
git update-index --cacheinfo "120000,$twoblob,mylink" &&
816+
test_must_fail git -c core.whitespace=incomplete \
817+
apply --cached --check --whitespace=error -R patch1.txt &&
818+
819+
# apply to create a symbolic link
820+
git update-index --cacheinfo "100644,$oneblob,mylink" &&
821+
git -c core.whitespace=incomplete apply --cached --whitespace=error \
822+
patch1.txt &&
823+
824+
# turning it back into an incomplete text file is an error
825+
test_must_fail git -c core.whitespace=incomplete \
826+
apply --cached --whitespace=error -R patch1.txt
827+
828+
829+
830+
'
831+
746832
test_done

0 commit comments

Comments
 (0)