public inbox for git@vger.kernel.org
 help / color / mirror / Atom feed
From: Junio C Hamano <gitster@pobox.com>
To: git@vger.kernel.org
Subject: [PATCH] whitespace: symbolic links usually lack LF at the end
Date: Wed, 04 Feb 2026 13:23:06 -0800	[thread overview]
Message-ID: <xmqqecn0nqyt.fsf@gitster.g> (raw)

For a patch that touches a symbolic link, it is perfectly normal
that the payload 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.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 apply.c                    |  5 +++++
 diff.c                     | 20 ++++++++++++++++++--
 t/t4015-diff-whitespace.sh | 13 +++++++++++++
 t/t4124-apply-ws-rule.sh   | 31 +++++++++++++++++++++++++++++++
 4 files changed, 67 insertions(+), 2 deletions(-)

diff --git a/apply.c b/apply.c
index 3de4aa4d2e..581aafb8be 100644
--- a/apply.c
+++ b/apply.c
@@ -2193,6 +2193,11 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si
 		patch->ws_rule = whitespace_rule(state->repo->index,
 						 patch->old_name);
 
+	/* being an incomplete line is the norm for a symbolic link */
+	if ((patch->old_mode && S_ISLNK(patch->old_mode)) ||
+	    (patch->new_mode && S_ISLNK(patch->new_mode)))
+		patch->ws_rule &= ~WS_INCOMPLETE_LINE;
+
 	patchsize = parse_single_patch(state,
 				       buffer + offset + hdrsize,
 				       size - offset - hdrsize,
diff --git a/diff.c b/diff.c
index a68ddd2168..2b37432eed 100644
--- a/diff.c
+++ b/diff.c
@@ -1837,6 +1837,7 @@ static void emit_rewrite_diff(const char *name_a,
 	const char *a_prefix, *b_prefix;
 	char *data_one, *data_two;
 	size_t size_one, size_two;
+	unsigned ws_rule;
 	struct emit_callback ecbdata;
 	struct strbuf out = STRBUF_INIT;
 
@@ -1859,9 +1860,14 @@ static void emit_rewrite_diff(const char *name_a,
 	size_one = fill_textconv(o->repo, textconv_one, one, &data_one);
 	size_two = fill_textconv(o->repo, textconv_two, two, &data_two);
 
+	ws_rule = whitespace_rule(o->repo->index, name_b);
+	if ((DIFF_FILE_VALID(one) && S_ISLNK(one->mode)) ||
+	    (DIFF_FILE_VALID(two) && S_ISLNK(two->mode)))
+		ws_rule &= ~WS_INCOMPLETE_LINE;
+
 	memset(&ecbdata, 0, sizeof(ecbdata));
 	ecbdata.color_diff = o->use_color;
-	ecbdata.ws_rule = whitespace_rule(o->repo->index, name_b);
+	ecbdata.ws_rule = ws_rule;
 	ecbdata.opt = o;
 	if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
 		mmfile_t mf1, mf2;
@@ -3764,6 +3770,7 @@ static void builtin_diff(const char *name_a,
 		xpparam_t xpp;
 		xdemitconf_t xecfg;
 		struct emit_callback ecbdata;
+		unsigned ws_rule;
 		const struct userdiff_funcname *pe;
 
 		if (must_show_header) {
@@ -3775,6 +3782,11 @@ static void builtin_diff(const char *name_a,
 		mf1.size = fill_textconv(o->repo, textconv_one, one, &mf1.ptr);
 		mf2.size = fill_textconv(o->repo, textconv_two, two, &mf2.ptr);
 
+		ws_rule = whitespace_rule(o->repo->index, name_b);
+		if ((DIFF_FILE_VALID(one) && S_ISLNK(one->mode)) ||
+		    (DIFF_FILE_VALID(two) && S_ISLNK(two->mode)))
+			ws_rule &= ~WS_INCOMPLETE_LINE;
+
 		pe = diff_funcname_pattern(o, one);
 		if (!pe)
 			pe = diff_funcname_pattern(o, two);
@@ -3786,7 +3798,7 @@ static void builtin_diff(const char *name_a,
 			lbl[0] = NULL;
 		ecbdata.label_path = lbl;
 		ecbdata.color_diff = o->use_color;
-		ecbdata.ws_rule = whitespace_rule(o->repo->index, name_b);
+		ecbdata.ws_rule = ws_rule;
 		if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
 			check_blank_at_eof(&mf1, &mf2, &ecbdata);
 		ecbdata.opt = o;
@@ -3993,6 +4005,10 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
 	data.ws_rule = whitespace_rule(o->repo->index, attr_path);
 	data.conflict_marker_size = ll_merge_marker_size(o->repo->index, attr_path);
 
+	if ((DIFF_FILE_VALID(one) && S_ISLNK(one->mode)) ||
+	    (DIFF_FILE_VALID(two) && S_ISLNK(two->mode)))
+		data.ws_rule &= ~WS_INCOMPLETE_LINE;
+
 	if (fill_mmfile(o->repo, &mf1, one) < 0 ||
 	    fill_mmfile(o->repo, &mf2, two) < 0)
 		die("unable to read files to diff");
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 3c8eb02e4f..903128f1d2 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -90,6 +90,19 @@ test_expect_success "new incomplete line in post-image" '
 	git -c core.whitespace=incomplete diff -R --check x
 '
 
+test_expect_success SYMLINKS "incomplete-line error is disabled for symlinks" '
+	test_when_finished "git reset --hard" &&
+	test_when_finished "rm -f mylink" &&
+	ln -s one mylink &&
+	git add mylink &&
+	ln -s -f two mylink &&
+
+	git -c core.whitespace=incomplete diff mylink &&
+	git -c core.whitespace=incomplete diff -R mylink &&
+	git -c core.whitespace=incomplete diff --check mylink &&
+	git -c core.whitespace=incomplete diff -R --check mylink
+'
+
 test_expect_success "Ray Lehtiniemi's example" '
 	cat <<-\EOF >x &&
 	do {
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
index 115a0f8579..f48d8bbf49 100755
--- a/t/t4124-apply-ws-rule.sh
+++ b/t/t4124-apply-ws-rule.sh
@@ -743,4 +743,35 @@ test_expect_success 'incomplete line modified at the end (error)' '
 	test_cmp sample target
 '
 
+test_expect_success "incomplete-line error is disabled for symlinks" '
+	test_when_finished "git reset" &&
+	test_when_finished "rm -f patch.txt" &&
+	oneblob=$(printf "one" | git hash-object --stdin -w -t blob) &&
+	twoblob=$(printf "two" | git hash-object --stdin -w -t blob) &&
+
+	git update-index --add --cacheinfo "120000,$oneblob,mylink" &&
+
+	oneshort=$(git rev-parse --short $oneblob) &&
+	twoshort=$(git rev-parse --short $twoblob) &&
+	cat >patch.txt <<-EOF &&
+	diff --git a/mylink b/mylink
+	index $oneshort..$twoshort 120000
+	--- a/mylink
+	+++ b/mylink
+	@@ -1 +1 @@
+	-one
+	\ No newline at end of file
+	+two
+	\ No newline at end of file
+	EOF
+
+	git -c core.whitespace=incomplete apply --cached --check patch.txt &&
+
+	git -c core.whitespace=incomplete apply --cached --whitespace=error \
+		patch.txt &&
+
+	git -c core.whitespace=incomplete apply --cached -R --whitespace=error \
+		patch.txt
+'
+
 test_done
-- 
2.53.0-169-ga09cd4eb64


             reply	other threads:[~2026-02-04 21:23 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-04 21:23 Junio C Hamano [this message]
2026-02-05 12:21 ` [PATCH] whitespace: symbolic links usually lack LF at the end Patrick Steinhardt
2026-02-05 15:50   ` Junio C Hamano
2026-02-06  6:31     ` Patrick Steinhardt
2026-02-06 16:25       ` Junio C Hamano
2026-02-06 16:58         ` Patrick Steinhardt
2026-02-06 16:25 ` [PATCH v2] " Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=xmqqecn0nqyt.fsf@gitster.g \
    --to=gitster@pobox.com \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox