From cc8f09baba94546a13087e923e9c2374284c8136 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 5 Feb 2020 13:01:33 +0100 Subject: [PATCH 1/5] tests: exercise the RUNTIME_PREFIX feature Originally, we refrained from adding a regression test in 7b6c6496374 (system_path(): Add prefix computation at runtime if RUNTIME_PREFIX set, 2008-08-10), and in 226c0ddd0d6 (exec_cmd: RUNTIME_PREFIX on some POSIX systems, 2018-04-10). The reason was that it was deemed too tricky to test. Turns out that it is not tricky to test at all: we simply create a pseudo-root, copy the `git` executable into the `git/` subdirectory of that pseudo-root, then copy a script into the `libexec/git-core/` directory and expect that to be picked up. As long as the trash directory is in a location where binaries can be executed, this works. Signed-off-by: Johannes Schindelin --- Makefile | 5 +++++ t/t0060-path-utils.sh | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/Makefile b/Makefile index c3565fc0f8f7df..4c3e4ccabcd826 100644 --- a/Makefile +++ b/Makefile @@ -2826,6 +2826,11 @@ ifdef GIT_TEST_INDEX_VERSION endif ifdef GIT_TEST_PERL_FATAL_WARNINGS @echo GIT_TEST_PERL_FATAL_WARNINGS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_PERL_FATAL_WARNINGS)))'\' >>$@+ +endif +ifdef RUNTIME_PREFIX + @echo RUNTIME_PREFIX=\'true\' >>$@+ +else + @echo RUNTIME_PREFIX=\'false\' >>$@+ endif @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index de4960783f071a..a76728c27bfa24 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -525,4 +525,22 @@ test_expect_success MINGW 'is_valid_path() on Windows' ' "PRN./abc" ' +test_lazy_prereq RUNTIME_PREFIX ' + test true = "$RUNTIME_PREFIX" +' + +test_lazy_prereq CAN_EXEC_IN_PWD ' + cp "$GIT_EXEC_PATH"/git$X ./ && + ./git rev-parse +' + +test_expect_success RUNTIME_PREFIX,CAN_EXEC_IN_PWD 'RUNTIME_PREFIX works' ' + mkdir -p pretend/bin pretend/libexec/git-core && + echo "echo HERE" | write_script pretend/libexec/git-core/git-here && + cp "$GIT_EXEC_PATH"/git$X pretend/bin/ && + GIT_EXEC_PATH= ./pretend/bin/git here >actual && + echo HERE >expect && + test_cmp expect actual +' + test_done From a4ff3a461bc42d11e5ef9e40deeb90d17919943d Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 24 Jul 2021 23:18:43 +0200 Subject: [PATCH 2/5] expand_user_path(): remove stale part of the comment In 395de250d9d (Expand ~ and ~user in core.excludesfile, commit.template, 2009-11-17), the `user_path()` function was refactored into the `expand_user_path()`. During that refactoring, the `buf` parameter was lost, but the code comment above said function still talks about it. Let's remove that stale part of the comment. Signed-off-by: Johannes Schindelin --- path.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/path.c b/path.c index 7bccd830e95890..3318ad24336ad1 100644 --- a/path.c +++ b/path.c @@ -719,9 +719,8 @@ static struct passwd *getpw_str(const char *username, size_t len) } /* - * Return a string with ~ and ~user expanded via getpw*. If buf != NULL, - * then it is a newly allocated string. Returns NULL on getpw failure or - * if path is NULL. + * Return a string with ~ and ~user expanded via getpw*. Returns NULL on getpw + * failure or if path is NULL. * * If real_home is true, strbuf_realpath($HOME) is used in the expansion. */ From 7b79ba66dd0d43f2565d2cbba268f01d3d36d052 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 24 Jul 2021 23:20:49 +0200 Subject: [PATCH 3/5] expand_user_path(): clarify the role of the `real_home` parameter The `real_home` parameter only has an effect when expanding paths starting with `~/`, not when expanding paths starting with `~/`. Let's make that clear. Signed-off-by: Johannes Schindelin --- path.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path.c b/path.c index 3318ad24336ad1..bf329e535cfddf 100644 --- a/path.c +++ b/path.c @@ -722,7 +722,7 @@ static struct passwd *getpw_str(const char *username, size_t len) * Return a string with ~ and ~user expanded via getpw*. Returns NULL on getpw * failure or if path is NULL. * - * If real_home is true, strbuf_realpath($HOME) is used in the expansion. + * If real_home is true, strbuf_realpath($HOME) is used in the `~/` expansion. */ char *expand_user_path(const char *path, int real_home) { From 19fd9c3c803a300b586c76736301a7379c4c6226 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 24 Jul 2021 23:09:33 +0200 Subject: [PATCH 4/5] Use a better name for the function interpolating paths It is not immediately clear what `expand_user_path()` means, so let's rename it to `interpolate_path()`. This also opens the path for interpolating more than just a home directory. Signed-off-by: Johannes Schindelin --- builtin/credential-cache.c | 2 +- builtin/credential-store.c | 2 +- builtin/gc.c | 2 +- cache.h | 2 +- config.c | 8 ++++---- path.c | 4 ++-- sequencer.c | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c index 76a6ba37223fa9..e8a74157471948 100644 --- a/builtin/credential-cache.c +++ b/builtin/credential-cache.c @@ -90,7 +90,7 @@ static char *get_socket_path(void) { struct stat sb; char *old_dir, *socket; - old_dir = expand_user_path("~/.git-credential-cache", 0); + old_dir = interpolate_path("~/.git-credential-cache", 0); if (old_dir && !stat(old_dir, &sb) && S_ISDIR(sb.st_mode)) socket = xstrfmt("%s/socket", old_dir); else diff --git a/builtin/credential-store.c b/builtin/credential-store.c index ae3c1ba75fe603..62a4f3c2653143 100644 --- a/builtin/credential-store.c +++ b/builtin/credential-store.c @@ -173,7 +173,7 @@ int cmd_credential_store(int argc, const char **argv, const char *prefix) if (file) { string_list_append(&fns, file); } else { - if ((file = expand_user_path("~/.git-credentials", 0))) + if ((file = interpolate_path("~/.git-credentials", 0))) string_list_append_nodup(&fns, file); file = xdg_config_home("credentials"); if (file) diff --git a/builtin/gc.c b/builtin/gc.c index f05d2f0a1ac9cd..6ce5ca45126599 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -1542,7 +1542,7 @@ static char *launchctl_service_filename(const char *name) struct strbuf filename = STRBUF_INIT; strbuf_addf(&filename, "~/Library/LaunchAgents/%s.plist", name); - expanded = expand_user_path(filename.buf, 1); + expanded = interpolate_path(filename.buf, 1); if (!expanded) die(_("failed to expand path '%s'"), filename.buf); diff --git a/cache.h b/cache.h index ba04ff8bd36b36..87e4cbe9c5f422 100644 --- a/cache.h +++ b/cache.h @@ -1246,7 +1246,7 @@ typedef int create_file_fn(const char *path, void *cb); int raceproof_create_file(const char *path, create_file_fn fn, void *cb); int mkdir_in_gitdir(const char *path); -char *expand_user_path(const char *path, int real_home); +char *interpolate_path(const char *path, int real_home); const char *enter_repo(const char *path, int strict); static inline int is_absolute_path(const char *path) { diff --git a/config.c b/config.c index f9c400ad3062d9..c17b9797292695 100644 --- a/config.c +++ b/config.c @@ -137,7 +137,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc if (!path) return config_error_nonbool("include.path"); - expanded = expand_user_path(path, 0); + expanded = interpolate_path(path, 0); if (!expanded) return error(_("could not expand include path '%s'"), path); path = expanded; @@ -185,7 +185,7 @@ static int prepare_include_condition_pattern(struct strbuf *pat) char *expanded; int prefix = 0; - expanded = expand_user_path(pat->buf, 1); + expanded = interpolate_path(pat->buf, 1); if (expanded) { strbuf_reset(pat); strbuf_addstr(pat, expanded); @@ -1270,7 +1270,7 @@ int git_config_pathname(const char **dest, const char *var, const char *value) { if (!value) return config_error_nonbool(var); - *dest = expand_user_path(value, 0); + *dest = interpolate_path(value, 0); if (!*dest) die(_("failed to expand user dir in: '%s'"), value); return 0; @@ -1844,7 +1844,7 @@ void git_global_config(char **user_out, char **xdg_out) char *xdg_config = NULL; if (!user_config) { - user_config = expand_user_path("~/.gitconfig", 0); + user_config = interpolate_path("~/.gitconfig", 0); xdg_config = xdg_config_home("config"); } diff --git a/path.c b/path.c index bf329e535cfddf..8dc5ad2cb55a01 100644 --- a/path.c +++ b/path.c @@ -724,7 +724,7 @@ static struct passwd *getpw_str(const char *username, size_t len) * * If real_home is true, strbuf_realpath($HOME) is used in the `~/` expansion. */ -char *expand_user_path(const char *path, int real_home) +char *interpolate_path(const char *path, int real_home) { struct strbuf user_path = STRBUF_INIT; const char *to_copy = path; @@ -811,7 +811,7 @@ const char *enter_repo(const char *path, int strict) strbuf_add(&validated_path, path, len); if (used_path.buf[0] == '~') { - char *newpath = expand_user_path(used_path.buf, 0); + char *newpath = interpolate_path(used_path.buf, 0); if (!newpath) return NULL; strbuf_attach(&used_path, newpath, strlen(newpath), diff --git a/sequencer.c b/sequencer.c index 0bec01cf38e817..007a851804dce6 100644 --- a/sequencer.c +++ b/sequencer.c @@ -1241,7 +1241,7 @@ N_("Your name and email address were configured automatically based\n" static const char *implicit_ident_advice(void) { - char *user_config = expand_user_path("~/.gitconfig", 0); + char *user_config = interpolate_path("~/.gitconfig", 0); char *xdg_config = xdg_config_home("config"); int config_exists = file_exists(user_config) || file_exists(xdg_config); From d286583082e13be0ed4e1d4d923b6bdcf0c234a6 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 11 Dec 2015 06:59:13 +0100 Subject: [PATCH 5/5] interpolate_path(): allow specifying paths relative to the runtime prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ever since Git learned to detect its install location at runtime, there was the slightly awkward problem that it was impossible to specify paths relative to said location. For example, if a version of Git was shipped with custom SSL certificates to use, there was no portable way to specify `http.sslCAInfo`. In Git for Windows, the problem was "solved" for years by interpreting paths starting with a slash as relative to the runtime prefix. However, this is not correct: such paths _are_ legal on Windows, and they are interpreted as absolute paths in the same drive as the current directory. After a lengthy discussion, and an even lengthier time to mull over the problem and its best solution, and then more discussions, we eventually decided to introduce support for the magic sequence `%(prefix)/`. If a path starts with this, the remainder is interpreted as relative to the detected (runtime) prefix. If built without runtime prefix support, Git will simply interpolate the compiled-in prefix. If a user _wants_ to specify a path starting with the magic sequence, they can prefix the magic sequence with `./` and voilĂ , the path won't be expanded. Signed-off-by: Johannes Schindelin --- Documentation/config.txt | 9 +++++++++ path.c | 8 ++++++++ t/t0060-path-utils.sh | 8 ++++++++ 3 files changed, 25 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index bf82766a6a272d..0c0e6b859f1ed2 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -298,6 +298,15 @@ pathname:: tilde expansion happens to such a string: `~/` is expanded to the value of `$HOME`, and `~user/` to the specified user's home directory. ++ +If a path starts with `%(prefix)/`, the remainder is interpreted as a +path relative to Git's "runtime prefix", i.e. relative to the location +where Git itself was installed. For example, `%(prefix)/bin/` refers to +the directory in which the Git executable itself lives. If Git was +compiled without runtime prefix support, the compiled-in prefix will be +subsituted instead. In the unlikely event that a literal path needs to +be specified that should _not_ be expanded, it needs to be prefixed by +`./`, like so: `./%(prefix)/bin`. Variables diff --git a/path.c b/path.c index 8dc5ad2cb55a01..0bc788ea40fb95 100644 --- a/path.c +++ b/path.c @@ -12,6 +12,7 @@ #include "packfile.h" #include "object-store.h" #include "lockfile.h" +#include "exec-cmd.h" static int get_st_mode_bits(const char *path, int *mode) { @@ -723,6 +724,9 @@ static struct passwd *getpw_str(const char *username, size_t len) * failure or if path is NULL. * * If real_home is true, strbuf_realpath($HOME) is used in the `~/` expansion. + * + * If the path starts with `%(prefix)/`, the remainder is interpreted as + * relative to where Git is installed, and expanded to the absolute path. */ char *interpolate_path(const char *path, int real_home) { @@ -731,6 +735,10 @@ char *interpolate_path(const char *path, int real_home) if (path == NULL) goto return_null; + + if (skip_prefix(path, "%(prefix)/", &path)) + return system_path(path); + if (path[0] == '~') { const char *first_slash = strchrnul(path, '/'); const char *username = path + 1; diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index a76728c27bfa24..34d1061f321fb4 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -540,6 +540,14 @@ test_expect_success RUNTIME_PREFIX,CAN_EXEC_IN_PWD 'RUNTIME_PREFIX works' ' cp "$GIT_EXEC_PATH"/git$X pretend/bin/ && GIT_EXEC_PATH= ./pretend/bin/git here >actual && echo HERE >expect && + test_cmp expect actual' + +test_expect_success RUNTIME_PREFIX,CAN_EXEC_IN_PWD '%(prefix)/ works' ' + mkdir -p pretend/bin && + cp "$GIT_EXEC_PATH"/git$X pretend/bin/ && + git config yes.path "%(prefix)/yes" && + GIT_EXEC_PATH= ./pretend/bin/git config --path yes.path >actual && + echo "$(pwd)/pretend/yes" >expect && test_cmp expect actual '