From b7d11a0f5d2db1eff623150cdc2a2ddccd6845b3 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 24 Jul 2021 22:06:49 +0000 Subject: [PATCH 1/6] 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 Signed-off-by: Junio C Hamano --- Makefile | 5 +++++ t/t0060-path-utils.sh | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/Makefile b/Makefile index c3565fc0f8..4c3e4ccabc 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 de4960783f..a76728c27b 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 789f6f226b6c7a5b28468044c1705c62aef5eeb9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 24 Jul 2021 22:06:50 +0000 Subject: [PATCH 2/6] 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 Signed-off-by: Junio C Hamano --- path.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/path.c b/path.c index 7bccd830e9..3318ad2433 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 644e6b2c0f55fa52de88586d375e03ac7f7914b7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 24 Jul 2021 22:06:51 +0000 Subject: [PATCH 3/6] 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 Signed-off-by: Junio C Hamano --- path.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/path.c b/path.c index 3318ad2433..bf329e535c 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 a03b097d6307763c6778f5a1f194fbcbd158a5f7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 24 Jul 2021 22:06:52 +0000 Subject: [PATCH 4/6] 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 Signed-off-by: Junio C Hamano --- 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 76a6ba3722..e8a7415747 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 ae3c1ba75f..62a4f3c265 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 f05d2f0a1a..6ce5ca4512 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 ba04ff8bd3..87e4cbe9c5 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 f9c400ad30..c17b979729 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 bf329e535c..8dc5ad2cb5 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 0bec01cf38..007a851804 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 e394a16023cbb62784e380f70ad8a833fb960d68 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 24 Jul 2021 22:06:53 +0000 Subject: [PATCH 5/6] 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 Signed-off-by: Junio C Hamano --- 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 bf82766a6a..0c0e6b859f 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 8dc5ad2cb5..0bc788ea40 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 a76728c27b..34d1061f32 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 ' From 7ed37eb8ae485ff5590c656e871d6c739edfa067 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 26 Jul 2021 14:55:05 -0700 Subject: [PATCH 6/6] expand_user_path: allow in-flight topics to keep using the old name Signed-off-by: Junio C Hamano --- cache.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cache.h b/cache.h index 87e4cbe9c5..679a27e17c 100644 --- a/cache.h +++ b/cache.h @@ -1247,6 +1247,8 @@ int raceproof_create_file(const char *path, create_file_fn fn, void *cb); int mkdir_in_gitdir(const char *path); char *interpolate_path(const char *path, int real_home); +/* NEEDSWORK: remove this synonym once in-flight topics have migrated */ +#define expand_user_path interpolate_path const char *enter_repo(const char *path, int strict); static inline int is_absolute_path(const char *path) {