Commit 50e012163619a1808bb7e06e658f3d47a472f0d1

Edward Thomson 2021-03-20T16:39:29

repo: introduce git_repository_workdir_path Add a simple accessor for workdir paths to get an absolute on-disk path given a repository and a relative path within it. This is useful to avoid copy-pasta `git_buf_joinpath` and to ensure that we validate working directory paths while honoring `core.longpaths` settings.

diff --git a/src/repository.c b/src/repository.c
index de1a895..c243f1d 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -2494,6 +2494,22 @@ const char *git_repository_workdir(const git_repository *repo)
 	return repo->workdir;
 }
 
+int git_repository_workdir_path(
+	git_buf *out, git_repository *repo, const char *path)
+{
+	int error;
+
+	if (!repo->workdir) {
+		git_error_set(GIT_ERROR_REPOSITORY, "repository has no working directory");
+		return GIT_EBAREREPO;
+	}
+
+	if (!(error = git_buf_joinpath(out, repo->workdir, path)))
+		error = git_path_validate_workdir_buf(repo, out);
+
+	return error;
+}
+
 const char *git_repository_commondir(const git_repository *repo)
 {
 	GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL);
diff --git a/src/repository.h b/src/repository.h
index 4df8500..f48dd9e 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -241,4 +241,12 @@ bool git_repository__reserved_names(
  */
 int git_repository_initialbranch(git_buf *out, git_repository *repo);
 
+/*
+ * Given a relative `path`, this makes it absolute based on the
+ * repository's working directory.  This will perform validation
+ * to ensure that the path is not longer than MAX_PATH on Windows
+ * (unless `core.longpaths` is set in the repo config).
+ */
+int git_repository_workdir_path(git_buf *out, git_repository *repo, const char *path);
+
 #endif
diff --git a/tests/win32/longpath.c b/tests/win32/longpath.c
index 80ae08d..b2efba4 100644
--- a/tests/win32/longpath.c
+++ b/tests/win32/longpath.c
@@ -4,6 +4,7 @@
 #include "clone.h"
 #include "buffer.h"
 #include "futils.h"
+#include "repository.h"
 
 static git_buf path = GIT_BUF_INIT;
 
@@ -29,6 +30,7 @@ void test_win32_longpath__initialize(void)
 void test_win32_longpath__cleanup(void)
 {
 	git_buf_dispose(&path);
+	cl_git_sandbox_cleanup();
 }
 
 #ifdef GIT_WIN32
@@ -60,3 +62,17 @@ void test_win32_longpath__errmsg_on_checkout(void)
 	assert_name_too_long();
 #endif
 }
+
+void test_win32_longpath__workdir_path_validated(void)
+{
+#ifdef GIT_WIN32
+	git_repository *repo = cl_git_sandbox_init("testrepo");
+	git_buf out = GIT_BUF_INIT;
+
+	cl_git_pass(git_repository_workdir_path(&out, repo, "a.txt"));
+
+	/* even if the repo path is a drive letter, this is too long */
+	cl_git_fail(git_repository_workdir_path(&out, repo, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.txt"));
+	cl_assert(git__prefixcmp(git_error_last()->message, "path too long") == 0);
+#endif
+}