Commit d4b1b76701cb0495993b31b1b0e98c0a4b49c0ce

Edward Thomson 2015-02-03T00:03:49

checkout: cache system attributes file location

diff --git a/src/attr.c b/src/attr.c
index df139e0..020bb1c 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -249,6 +249,46 @@ static int preload_attr_file(
 	return error;
 }
 
+static int system_attr_file(
+	git_buf *out,
+	git_repository *repo,
+	git_attr_session *attr_session)
+{
+	int error;
+
+	if (!attr_session) {
+		error = git_sysdir_find_system_file(out, GIT_ATTR_FILE_SYSTEM);
+
+		if (error == GIT_ENOTFOUND)
+			giterr_clear();
+
+		return error;
+	}
+
+	if (!attr_session->init_sysdir) {
+		error = git_sysdir_find_system_file(&attr_session->sysdir, GIT_ATTR_FILE_SYSTEM);
+
+		if (error == GIT_ENOTFOUND)
+			giterr_clear();
+		else if (error)
+			return error;
+
+		attr_session->init_sysdir = 1;
+	}
+
+	if (attr_session->sysdir.size == 0)
+		return GIT_ENOTFOUND;
+
+	/* We can safely provide a git_buf with no allocation (asize == 0) to
+	 * a consumer. This allows them to treat this as a regular `git_buf`,
+	 * but their call to `git_buf_free` will not attempt to free it.
+	 */
+	out->ptr = attr_session->sysdir.ptr;
+	out->size = attr_session->sysdir.size;
+	out->asize = 0;
+	return 0;
+}
+
 static int attr_setup(git_repository *repo, git_attr_session *attr_session)
 {
 	int error = 0;
@@ -256,7 +296,7 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session)
 	git_index *idx = NULL;
 	git_buf sys = GIT_BUF_INIT;
 
-	if (attr_session && attr_session->setup)
+	if (attr_session && attr_session->init_setup)
 		return 0;
 
 	if ((error = git_attr_cache__init(repo)) < 0)
@@ -266,18 +306,15 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session)
 	 * definitions will be available for later file parsing
 	 */
 
-	if (!(error = git_sysdir_find_system_file(&sys, GIT_ATTR_FILE_SYSTEM))) {
+	error = system_attr_file(&sys, repo, attr_session);
+
+	if (error == 0)
 		error = preload_attr_file(
 			repo, attr_session, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr);
-		git_buf_free(&sys);
-	}
-	if (error < 0) {
-		if (error == GIT_ENOTFOUND) {
-			giterr_clear();
-			error = 0;
-		} else
-			return error;
-	}
+	else if (error != GIT_ENOTFOUND)
+		return error;
+
+	git_buf_free(&sys);
 
 	if ((error = preload_attr_file(
 			repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
@@ -300,7 +337,7 @@ static int attr_setup(git_repository *repo, git_attr_session *attr_session)
 		return error;
 
 	if (attr_session)
-		attr_session->setup = 1;
+		attr_session->init_setup = 1;
 
 	return error;
 }
@@ -489,15 +526,14 @@ static int collect_attr_files(
 	}
 
 	if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
-		error = git_sysdir_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
+		error = system_attr_file(&dir, repo, attr_session);
+
 		if (!error)
 			error = push_attr_file(
 				repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
 				NULL, dir.ptr);
-		else if (error == GIT_ENOTFOUND) {
-			giterr_clear();
+		else if (error == GIT_ENOTFOUND)
 			error = 0;
-		}
 	}
 
  cleanup:
diff --git a/src/attr_file.c b/src/attr_file.c
index 9896df4..2ebd3b9 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -854,3 +854,13 @@ int git_attr_session__init(git_attr_session *session, git_repository *repo)
 
 	return 0;
 }
+
+void git_attr_session__free(git_attr_session *session)
+{
+	if (!session)
+		return;
+
+	git_buf_free(&session->sysdir);
+
+	memset(session, 0, sizeof(git_attr_session));
+}
diff --git a/src/attr_file.h b/src/attr_file.h
index b8e3725..5c64f7c 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -111,7 +111,9 @@ typedef struct {
 
 typedef struct {
 	int key;
-	unsigned int setup;
+	unsigned int init_setup:1,
+		init_sysdir:1;
+	git_buf sysdir;
 } git_attr_session;
 
 extern int git_attr_session__init(git_attr_session *attr_session, git_repository *repo);