Commit 5a76ad35aaf709503dfc939464715666919180f8

Carlos Martín Nieto 2014-06-19T11:45:46

crlf: pass-through mixed EOL buffers from LF->CRLF When checking out files, we're performing conversion into the user's native line endings, but we only want to do it for files which have consistent line endings. Refuse to perform the conversion for mixed-EOL files. The CRLF->LF filter is left as-is, as that conversion is considered to be normalization by git and should force a conversion of the line endings.

diff --git a/src/buf_text.c b/src/buf_text.c
index 631feb3..8d2b141 100644
--- a/src/buf_text.c
+++ b/src/buf_text.c
@@ -123,9 +123,13 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
 
 	for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
 		size_t copylen = next - scan;
-		/* don't convert existing \r\n to \r\r\n */
-		size_t extralen = (next > start && next[-1] == '\r') ? 1 : 2;
-		size_t needsize = tgt->size + copylen + extralen + 1;
+		size_t needsize = tgt->size + copylen + 2 + 1;
+
+		/* if we find mixed line endings, bail */
+		if (next > start && next[-1] == '\r') {
+			git_buf_free(tgt);
+			return GIT_PASSTHROUGH;
+		}
 
 		if (tgt->asize < needsize && git_buf_grow(tgt, needsize) < 0)
 			return -1;
@@ -134,8 +138,8 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
 			memcpy(tgt->ptr + tgt->size, scan, copylen);
 			tgt->size += copylen;
 		}
-		if (extralen == 2)
-			tgt->ptr[tgt->size++] = '\r';
+
+		tgt->ptr[tgt->size++] = '\r';
 		tgt->ptr[tgt->size++] = '\n';
 	}
 
diff --git a/src/buf_text.h b/src/buf_text.h
index 3ac9d14..e753a02 100644
--- a/src/buf_text.h
+++ b/src/buf_text.h
@@ -56,9 +56,10 @@ GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string)
 extern void git_buf_text_unescape(git_buf *buf);
 
 /**
- * Replace all \r\n with \n. Does not modify \r without trailing \n.
+ * Replace all \r\n with \n.
  *
- * @return 0 on success, -1 on memory error
+ * @return 0 on success, -1 on memory error, GIT_PASSTHROUGH if the
+ * source buffer has mixed line endings.
  */
 extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src);
 
diff --git a/tests/checkout/crlf.c b/tests/checkout/crlf.c
index 6b2c1b1..2c84a77 100644
--- a/tests/checkout/crlf.c
+++ b/tests/checkout/crlf.c
@@ -79,10 +79,7 @@ void test_checkout_crlf__more_lf_autocrlf_true(void)
 
 	git_checkout_head(g_repo, &opts);
 
-	if (GIT_EOL_NATIVE == GIT_EOL_LF)
-		check_file_contents("./crlf/more-lf", MORE_LF_TEXT_RAW);
-	else
-		check_file_contents("./crlf/more-lf", MORE_LF_TEXT_AS_CRLF);
+	check_file_contents("./crlf/more-lf", MORE_LF_TEXT_RAW);
 }
 
 void test_checkout_crlf__more_crlf_autocrlf_true(void)
@@ -94,10 +91,7 @@ void test_checkout_crlf__more_crlf_autocrlf_true(void)
 
 	git_checkout_head(g_repo, &opts);
 
-	if (GIT_EOL_NATIVE == GIT_EOL_LF)
-		check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_RAW);
-	else
-		check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_AS_CRLF);
+	check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_RAW);
 }
 
 void test_checkout_crlf__all_crlf_autocrlf_true(void)
diff --git a/tests/core/buffer.c b/tests/core/buffer.c
index da5ec60..8310dea 100644
--- a/tests/core/buffer.c
+++ b/tests/core/buffer.c
@@ -1034,18 +1034,14 @@ void test_core_buffer__lf_and_crlf_conversions(void)
 
 	git_buf_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n");
 
-	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
-	check_buf("crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n", tgt);
-	check_buf(src.ptr, tgt);
+	cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
 
 	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
 	check_buf("crlf\ncrlf\ncrlf\ncrlf\n", tgt);
 
 	git_buf_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf");
 
-	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
-	check_buf("\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf", tgt);
-	check_buf(src.ptr, tgt);
+	cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
 
 	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
 	check_buf("\ncrlf\ncrlf\ncrlf\ncrlf\ncrlf", tgt);
@@ -1054,8 +1050,7 @@ void test_core_buffer__lf_and_crlf_conversions(void)
 
 	git_buf_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n");
 
-	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
-	check_buf("\r\nlf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\ncrlf\r\n", tgt);
+	cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
 	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
 	check_buf("\nlf\nlf\ncrlf\nlf\nlf\ncrlf\n", tgt);
 
@@ -1063,8 +1058,7 @@ void test_core_buffer__lf_and_crlf_conversions(void)
 
 	git_buf_sets(&src, "\ncrlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf");
 
-	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
-	check_buf("\r\ncrlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf", tgt);
+	cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
 	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
 	check_buf("\ncrlf\ncrlf\nlf\ncrlf\ncrlf", tgt);
 
@@ -1072,8 +1066,7 @@ void test_core_buffer__lf_and_crlf_conversions(void)
 
 	git_buf_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r");
 
-	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
-	check_buf("\rcrlf\r\nlf\r\nlf\r\ncr\rcrlf\r\nlf\r\ncr\r", tgt);
+	cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
 	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
 	check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt);
 
@@ -1089,8 +1082,7 @@ void test_core_buffer__lf_and_crlf_conversions(void)
 	/* blob correspondence tests */
 
 	git_buf_sets(&src, ALL_CRLF_TEXT_RAW);
-	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
-	check_buf(ALL_CRLF_TEXT_AS_CRLF, tgt);
+	cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
 	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
 	check_buf(ALL_CRLF_TEXT_AS_LF, tgt);
 	git_buf_free(&src);
@@ -1105,16 +1097,14 @@ void test_core_buffer__lf_and_crlf_conversions(void)
 	git_buf_free(&tgt);
 
 	git_buf_sets(&src, MORE_CRLF_TEXT_RAW);
-	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
-	check_buf(MORE_CRLF_TEXT_AS_CRLF, tgt);
+	cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
 	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
 	check_buf(MORE_CRLF_TEXT_AS_LF, tgt);
 	git_buf_free(&src);
 	git_buf_free(&tgt);
 
 	git_buf_sets(&src, MORE_LF_TEXT_RAW);
-	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
-	check_buf(MORE_LF_TEXT_AS_CRLF, tgt);
+	cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
 	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
 	check_buf(MORE_LF_TEXT_AS_LF, tgt);
 	git_buf_free(&src);