From a13fc940415eb451b7a650cfde4bec45e9a1bff4 Mon Sep 17 00:00:00 2001 From: Isabella Caselli Date: Sun, 11 Oct 2026 22:58:44 -0300 Subject: [PATCH 1/3] var: add GIT_HOSTNAME Git asks the system for the name of the machine it runs on in a few places. For example, "git gc" records it in "gc.pid" to tell whether the gc holding the lock runs on the same machine. Users have no way to see which name Git uses, and depending on how the system is configured, it may be a short name ("myhost") or a fully qualified one ("myhost.example.com"). This matters more once the config can depend on the hostname, which a later commit adds as an includeIf condition. Without a way to see the value Git uses, users would have to guess what to write in their config file. Add a GIT_HOSTNAME variable to "git var" that prints the hostname Git sees. If the hostname can't be determined, "git var GIT_HOSTNAME" exits with a non-zero status, and "git var -l" omits the variable instead of failing. An option to "git config" to show the hostname was also discussed, but "git var" was chosen, since the hostname is not specific to the configuration code. The test only checks that a non-empty value is printed. The test has no independent way to know the expected hostname, as the "hostname" command may not agree with what Git sees on every platform. The includeIf tests added later check that the value matches what the configuration code uses. Suggested-by: Eric Sunshine Suggested-by: Jeff King Signed-off-by: Isabella Caselli Signed-off-by: Junio C Hamano --- Documentation/git-var.adoc | 3 +++ builtin/var.c | 13 +++++++++++++ t/t0007-git-var.sh | 5 +++++ 3 files changed, 21 insertions(+) diff --git a/Documentation/git-var.adoc b/Documentation/git-var.adoc index 697c10aded..e813ab4fd2 100644 --- a/Documentation/git-var.adoc +++ b/Documentation/git-var.adoc @@ -84,6 +84,9 @@ endif::git-default-pager[] `GIT_CONFIG_GLOBAL`:: The path to the global (per-user) configuration files, if any. +`GIT_HOSTNAME`:: + The hostname of the machine, as reported by the operating system. + Most path values contain only one value. However, some can contain multiple values, which are separated by newlines, and are listed in order from highest to lowest priority. Callers should be prepared for any such path value to contain diff --git a/builtin/var.c b/builtin/var.c index cc3a43cde2..b427ebd61e 100644 --- a/builtin/var.c +++ b/builtin/var.c @@ -115,6 +115,15 @@ static char *git_config_val_global(int ident_flag UNUSED) return strbuf_detach(&buf, &unused); } +static char *hostname(int ident_flag UNUSED) +{ + char buf[HOST_NAME_MAX + 1]; + + if (xgethostname(buf, sizeof(buf))) + return NULL; + return xstrdup(buf); +} + struct git_var { const char *name; char *(*read)(int); @@ -166,6 +175,10 @@ static struct git_var git_vars[] = { .read = git_config_val_global, .multivalued = 1, }, + { + .name = "GIT_HOSTNAME", + .read = hostname, + }, { .name = "", .read = NULL, diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh index 2b60317758..f50c62a25e 100755 --- a/t/t0007-git-var.sh +++ b/t/t0007-git-var.sh @@ -224,6 +224,11 @@ test_expect_success 'GIT_CONFIG_GLOBAL points to the correct location' ' ) ' +test_expect_success 'GIT_HOSTNAME is not empty' ' + hostname=$(git var GIT_HOSTNAME) && + test -n "$hostname" +' + # For git var -l, we check only a representative variable; # testing the whole output would make our test too brittle with # respect to unrelated changes in the test suite's environment. From a37cc0bf4904af1f03bc6153bc234beb5d4bb647 Mon Sep 17 00:00:00 2001 From: Isabella Caselli Date: Sun, 11 Oct 2026 22:58:45 -0300 Subject: [PATCH 2/3] t6500: get hostname from "git var" The test "background auto gc respects lock for all operations" simulates a concurrent gc by writing our shell's pid and hostname to "gc.pid". It gets the hostname from the "hostname" command, falling back to "unknown" if the command fails. "git gc" gets its hostname from the system and only checks whether the pid is alive if the two hostnames match. Otherwise, it assumes the lock belongs to a gc running on another machine. Since the test passes in either case, a missing "hostname" command or a mismatch between its output and the system hostname can cause the test to silently exercise the wrong case. Use "git var GIT_HOSTNAME" to get the same hostname as "git gc". Keep the "unknown" fallback, which is also used by "git gc" when it cannot determine the hostname. Helped-by: Jeff King Signed-off-by: Isabella Caselli Signed-off-by: Junio C Hamano --- t/t6500-gc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh index a7142ab815..f928089bf0 100755 --- a/t/t6500-gc.sh +++ b/t/t6500-gc.sh @@ -413,7 +413,7 @@ test_expect_success 'background auto gc respects lock for all operations' ' # now fake a concurrent gc that holds the lock; we can use our # shell pid so that it looks valid. - hostname=$(hostname || echo unknown) && + hostname=$(git var GIT_HOSTNAME || echo unknown) && shell_pid=$$ && if test_have_prereq MINGW && test -f /proc/$shell_pid/winpid then From b2f3c53e6c0a27e3584951b6b0e7372d4a80000a Mon Sep 17 00:00:00 2001 From: Isabella Caselli Date: Sun, 11 Oct 2026 22:58:46 -0300 Subject: [PATCH 3/3] config: learn the "hostname:" includeIf condition When the same home directory is shared across multiple machines, they all read the same ~/.gitconfig. Settings that should differ between machines therefore need a way to make that file behave differently on each host. The same problem affects people who keep their dotfiles in a repository and check them out on multiple machines. Add a "hostname:" keyword to includeIf. The value that follows is a pattern supporting standard glob wildcards, and the condition is met when the machine's hostname matches it. If the hostname cannot be determined, the condition is not met. Depending on the system configuration, the hostname may be either a short name or a fully qualified domain name. Making the name fully qualified ourselves was considered, but it could require a DNS lookup while reading the configuration. Instead, match the name as the system reports it, document this, and point to "git var GIT_HOSTNAME" as a way to check the value being matched. The tests obtain the hostname with "git var GIT_HOSTNAME" before writing the configuration, so that a failure to retrieve it makes the test fail instead of going unnoticed inside the here-document. Based-on-patch-by: Ignacio Encinas Signed-off-by: Isabella Caselli Signed-off-by: Junio C Hamano --- Documentation/config.adoc | 14 ++++++++++++ Documentation/git-var.adoc | 2 ++ config.c | 17 +++++++++++++++ t/t1305-config-include.sh | 44 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+) diff --git a/Documentation/config.adoc b/Documentation/config.adoc index f67dcd2f8e..aa6360e230 100644 --- a/Documentation/config.adoc +++ b/Documentation/config.adoc @@ -232,6 +232,16 @@ As for the naming of this keyword, it is for forwards compatibility with a naming scheme that supports more variable-based include conditions, but currently Git only supports the exact keyword described above. +`hostname`:: + The data that follows the keyword `hostname:` is taken to be a + pattern with standard globbing wildcards. If the hostname of + the machine matches the pattern, the include condition is met. ++ +Depending on how your system is configured, the hostname used for +matching may be short (e.g., "myhost") or a fully qualified domain +name ("myhost.example.com"). Run `git var GIT_HOSTNAME` to see the +hostname Git uses (see linkgit:git-var[1]). + A few more notes on matching via `gitdir` and `gitdir/i`: * Symlinks in `$GIT_DIR` are not resolved before matching. @@ -315,6 +325,10 @@ Example path = foo.inc [remote "origin"] url = https://example.com/git + +; include only if the hostname of the machine matches some-hostname +[includeIf "hostname:some-hostname"] + path = foo.inc ---- Values diff --git a/Documentation/git-var.adoc b/Documentation/git-var.adoc index e813ab4fd2..fc2def16d3 100644 --- a/Documentation/git-var.adoc +++ b/Documentation/git-var.adoc @@ -86,6 +86,8 @@ endif::git-default-pager[] `GIT_HOSTNAME`:: The hostname of the machine, as reported by the operating system. + This is the hostname matched by `includeIf "hostname:..."` (see + linkgit:git-config[1]). Most path values contain only one value. However, some can contain multiple values, which are separated by newlines, and are listed in order from highest to diff --git a/config.c b/config.c index e0bb29b53d..c891f63e60 100644 --- a/config.c +++ b/config.c @@ -316,6 +316,21 @@ static int include_by_branch(struct config_include_data *data, return ret; } +static int include_by_hostname(const char *cond, size_t cond_len) +{ + int ret; + char my_host[HOST_NAME_MAX + 1]; + char *pattern; + + if (xgethostname(my_host, sizeof(my_host))) + return 0; + + pattern = xmemdupz(cond, cond_len); + ret = !wildmatch(pattern, my_host, 0); + free(pattern); + return ret; +} + static int add_remote_url(const char *var, const char *value, const struct config_context *ctx UNUSED, void *data) { @@ -411,6 +426,8 @@ static int include_condition_is_true(const struct key_value_info *kvi, else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond, &cond_len)) return include_by_remote_url(inc, cond, cond_len); + else if (skip_prefix_mem(cond, cond_len, "hostname:", &cond, &cond_len)) + return include_by_hostname(cond, cond_len); /* unknown conditionals are always false */ return 0; diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh index 507b8ebd06..01069237ba 100755 --- a/t/t1305-config-include.sh +++ b/t/t1305-config-include.sh @@ -524,4 +524,48 @@ test_expect_success 'conditional include, worktree without repository but explic test_must_fail nongit git --git-dir=nonexistent config get foo.bar ' +test_expect_success 'conditional include, hostname' ' + hostname=$(git var GIT_HOSTNAME) && + cat >>.git/config <<-EOF && + [includeIf "hostname:${hostname}a"] + path = bar12 + EOF + cat >>.git/bar12 <<-EOF && + [test] + twelve=12 + EOF + + test_must_fail git config test.twelve && + + cat >>.git/config <<-EOF && + [includeIf "hostname:$hostname"] + path = bar12 + EOF + echo 12 >expect && + git config test.twelve >actual && + test_cmp expect actual +' + +test_expect_success 'conditional include, hostname, wildcard' ' + hostname=$(git var GIT_HOSTNAME) && + cat >>.git/config <<-EOF && + [includeIf "hostname:${hostname}a*"] + path = bar13 + EOF + cat >>.git/bar13 <<-EOF && + [test] + thirteen = 13 + EOF + + test_must_fail git config test.thirteen && + + cat >>.git/config <<-EOF && + [includeIf "hostname:$hostname*"] + path = bar13 + EOF + echo 13 >expect && + git config test.thirteen >actual && + test_cmp expect actual +' + test_done