Commit 9733e80c2ae7517f44c658cd2914d99454470dd1

Russell Belfer 2013-03-22T10:44:45

Add has_cr_in_index check to CRLF filter This adds a check to the drop_crlf filter path to check it the file in the index already has a CR in it, in which case this will not drop the CRs from the workdir file contents. This uncovered a "bug" in `git_blob_create_fromworkdir` where the full path to the file was passed to look up the attributes instead of the relative path from the working directory root. This meant that the check in the index for a pre-existing entry of the same name was failing.

diff --git a/src/blob.c b/src/blob.c
index bcb6ac9..3ff141c 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -221,7 +221,9 @@ int git_blob_create_fromworkdir(git_oid *oid, git_repository *repo, const char *
 		return -1;
 	}
 
-	error = blob_create_internal(oid, repo, git_buf_cstr(&full_path), git_buf_cstr(&full_path), true);
+	error = blob_create_internal(
+		oid, repo, git_buf_cstr(&full_path),
+		git_buf_cstr(&full_path) + strlen(workdir), true);
 
 	git_buf_free(&full_path);
 	return error;
@@ -231,13 +233,21 @@ int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *pat
 {
 	int error;
 	git_buf full_path = GIT_BUF_INIT;
+	const char *workdir, *hintpath;
 
 	if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {
 		git_buf_free(&full_path);
 		return error;
 	}
 
-	error = blob_create_internal(oid, repo, git_buf_cstr(&full_path), git_buf_cstr(&full_path), true);
+	hintpath = git_buf_cstr(&full_path);
+	workdir  = git_repository_workdir(repo);
+
+	if (workdir && !git__prefixcmp(hintpath, workdir))
+		hintpath += strlen(workdir);
+
+	error = blob_create_internal(
+		oid, repo, git_buf_cstr(&full_path), hintpath, true);
 
 	git_buf_free(&full_path);
 	return error;
diff --git a/src/crlf.c b/src/crlf.c
index 060d39d..84347ac 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -10,8 +10,8 @@
 #include "hash.h"
 #include "filter.h"
 #include "repository.h"
-
 #include "git2/attr.h"
+#include "git2/blob.h"
 
 struct crlf_attrs {
 	int crlf_action;
@@ -21,6 +21,8 @@ struct crlf_attrs {
 struct crlf_filter {
 	git_filter f;
 	struct crlf_attrs attrs;
+	git_repository *repo;
+	char path[GIT_FLEX_ARRAY];
 };
 
 static int check_crlf(const char *value)
@@ -132,7 +134,46 @@ static int drop_crlf(git_buf *dest, const git_buf *source)
 	return 0;
 }
 
-static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *source)
+static int has_cr_in_index(git_filter *self)
+{
+	struct crlf_filter *filter = (struct crlf_filter *)self;
+	git_index *index;
+	const git_index_entry *entry;
+	git_blob *blob;
+	const void *blobcontent;
+	git_off_t blobsize;
+	bool found_cr;
+
+	if (git_repository_index__weakptr(&index, filter->repo) < 0) {
+		giterr_clear();
+		return false;
+	}
+
+	if (!(entry = git_index_get_bypath(index, filter->path, 0)) &&
+		!(entry = git_index_get_bypath(index, filter->path, 1)))
+		return false;
+
+	if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */
+		return true;
+
+	if (git_blob_lookup(&blob, filter->repo, &entry->oid) < 0)
+		return false;
+
+	blobcontent = git_blob_rawcontent(blob);
+	blobsize    = git_blob_rawsize(blob);
+	if (!git__is_sizet(blobsize))
+		blobsize = (size_t)-1;
+
+	found_cr = (blobcontent != NULL &&
+		blobsize > 0 &&
+		memchr(blobcontent, '\r', (size_t)blobsize) != NULL);
+
+	git_blob_free(blob);
+	return found_cr;
+}
+
+static int crlf_apply_to_odb(
+	git_filter *self, git_buf *dest, const git_buf *source)
 {
 	struct crlf_filter *filter = (struct crlf_filter *)self;
 
@@ -162,16 +203,14 @@ static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *sou
 		if (stats.cr != stats.crlf)
 			return -1;
 
-#if 0
-		if (crlf_action == CRLF_GUESS) {
+		if (filter->attrs.crlf_action == GIT_CRLF_GUESS) {
 			/*
 			 * If the file in the index has any CR in it, do not convert.
 			 * This is the new safer autocrlf handling.
 			 */
-			if (has_cr_in_index(path))
-				return 0;
+			if (has_cr_in_index(self))
+				return -1;
 		}
-#endif
 
 		if (!stats.cr)
 			return -1;
@@ -266,6 +305,7 @@ static int find_and_add_filter(
 {
 	struct crlf_attrs ca;
 	struct crlf_filter *filter;
+	size_t pathlen;
 	int error;
 
 	/* Load gitattributes for the path */
@@ -293,12 +333,15 @@ static int find_and_add_filter(
 
 	/* If we're good, we create a new filter object and push it
 	 * into the filters array */
-	filter = git__malloc(sizeof(struct crlf_filter));
+	pathlen = strlen(path);
+	filter = git__malloc(sizeof(struct crlf_filter) + pathlen + 1);
 	GITERR_CHECK_ALLOC(filter);
 
 	filter->f.apply = apply;
 	filter->f.do_free = NULL;
 	memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
+	filter->repo = repo;
+	memcpy(filter->path, path, pathlen + 1);
 
 	return git_vector_insert(filters, filter);
 }