Commit e4bac3c4692834e6d0ca607aca229ddcae0ba2b7

Ben Straub 2012-07-31T15:38:12

Checkout: crlf filter.

diff --git a/src/crlf.c b/src/crlf.c
index f68938e..509e558 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -184,6 +184,85 @@ static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *sou
 	return drop_crlf(dest, source);
 }
 
+static int convert_line_endings(git_buf *dest, const git_buf *source, const char *ending)
+{
+	const char *scan = git_buf_cstr(source),
+				  *next,
+				  *scan_end = git_buf_cstr(source) + git_buf_len(source);
+
+	while ((next = memchr(scan, '\n', scan_end - scan)) != NULL) {
+		if (next > scan)
+			git_buf_put(dest, scan, next-scan);
+		git_buf_puts(dest, ending);
+		scan = next + 1;
+	}
+
+	git_buf_put(dest, scan, scan_end - scan);
+	return 0;
+}
+
+static const char *line_ending(struct crlf_filter *filter)
+{
+	switch (filter->attrs.crlf_action) {
+	case GIT_CRLF_BINARY:
+	case GIT_CRLF_INPUT:
+		return "\n";
+
+	case GIT_CRLF_CRLF:
+		return "\r\n";
+
+	case GIT_CRLF_AUTO:
+	case GIT_CRLF_TEXT:
+	case GIT_CRLF_GUESS:
+		break;
+
+	default:
+		goto line_ending_error;
+	}
+
+	switch (filter->attrs.eol) {
+	case GIT_EOL_UNSET:
+		return GIT_EOL_NATIVE == GIT_EOL_CRLF
+			? "\r\n"
+			: "\n";
+
+	case GIT_EOL_CRLF:
+		return "\r\n";
+
+	case GIT_EOL_LF:
+		return "\n";
+
+	default:
+		goto line_ending_error;
+	}
+
+line_ending_error:
+	giterr_set(GITERR_INVALID, "Invalid input to line ending filter");
+	return NULL;
+}
+
+static int crlf_apply_to_workdir(git_filter *self, git_buf *dest, const git_buf *source)
+{
+	struct crlf_filter *filter = (struct crlf_filter *)self;
+	const char *workdir_ending = NULL;
+
+	assert (self && dest && source);
+
+	/* Empty file? Nothing to do. */
+	if (git_buf_len(source) == 0)
+		return 0;
+
+	/* Determine proper line ending */
+	workdir_ending = line_ending(filter);
+	if (!workdir_ending) return -1;
+
+	/* If the line ending is '\n', just copy the input */
+	if (!strcmp(workdir_ending, "\n"))
+		return git_buf_puts(dest, git_buf_cstr(source));
+
+	return convert_line_endings(dest, source, workdir_ending);
+}
+
 static int find_and_add_filter(git_vector *filters, git_repository *repo, const char *path,
 										 int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source))
 {
@@ -207,8 +286,7 @@ static int find_and_add_filter(git_vector *filters, git_repository *repo, const 
 	if (ca.crlf_action == GIT_CRLF_GUESS) {
 		int auto_crlf;
 
-		if ((error = git_repository__cvar(
-			&auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < 0)
+		if ((error = git_repository__cvar(&auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < 0)
 			return error;
 
 		if (auto_crlf == GIT_AUTO_CRLF_FALSE)
@@ -227,12 +305,6 @@ static int find_and_add_filter(git_vector *filters, git_repository *repo, const 
 	return git_vector_insert(filters, filter);
 }
 
-static int crlf_apply_to_workdir(git_filter *self, git_buf *dest, const git_buf *source)
-{
-	/* TODO */
-	return -1;
-}
-
 int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path)
 {
 	return find_and_add_filter(filters, repo, path, &crlf_apply_to_odb);
diff --git a/tests-clar/checkout/checkout.c b/tests-clar/checkout/checkout.c
index 5099c4e..9551cba 100644
--- a/tests-clar/checkout/checkout.c
+++ b/tests-clar/checkout/checkout.c
@@ -58,15 +58,9 @@ void test_checkout_checkout__crlf(void)
 		"new.txt text eol=lf\n";
 	cl_git_mkfile("./testrepo/.gitattributes", attributes);
 	cl_git_pass(git_checkout_head(g_repo, NULL, NULL));
-	/* TODO: enable these when crlf is ready */
-	/* test_file_contents("./testrepo/README", "hey there\n"); */
-	/* test_file_contents("./testrepo/new.txt", "my new file\n"); */
-	/* test_file_contents("./testrepo/branch_file.txt", "hi\r\nbye!\r\n"); */
-}
-
-void test_checkout_checkout__stats(void)
-{
-	/* TODO */
+	 test_file_contents("./testrepo/README", "hey there\n"); 
+	 test_file_contents("./testrepo/new.txt", "my new file\n"); 
+	 test_file_contents("./testrepo/branch_file.txt", "hi\r\nbye!\r\n"); 
 }
 
 static void enable_symlinks(bool enable)