git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH/RFC] Hacky version of a glob() driven config include

From
Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Date
May 6, 2010, 21:14 UTC
Message-ID
<1273180440-8641-1-git-send-email-avarab@gmail.com>
In-Reply-To
<u2i51dd1af81004060115t5f837840z5adcf83622fa8882@mail.gmail.com>

This is not ready for inclusion in anything. Commiting for RFC on whether this way of doing it is sane in theory.

Known bugs:
  * Breaks the model of being able to *set* config values. That
    doesn't work for the included files. Maybe not a bug.
  * Errors in the git_config_from_file() call in glob_include_config()
    aren't passed upwards.
  * It relies on the GNU GLOB_TILDE extension with no
    alternative. That can be done by calling getenv("HOME") and
    s/~/$home/.
  * The whole bit with saving/restoring global state for config
    inclusion is evil, but then again so is the global state.
  * We don't check for recursion. But Git gives up eventually after
    after spewing a *lot* of duplicate entry errors. Not sure how to
    do this sanely w/symlinks.
Not-signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
Show 5 quoted lines
> On Sun, Apr 4, 2010 at 07:50, Eli Barzilay <eli@barzilay.org> wrote:
> > Isn't it better to have a way to include files instead?
>
> Probably yes. Programs like Apache HTTPD, rsyslog and others just use
> ${foo}conf.d by convention by supporting config inclusion.

Here's an evil implementation of this. I know the code is horrid & buggy (see above). But is the general idea sane. I thought it would be better to submit this for comments before I went further with it.

 config.c               |   55 +++++++++++++++++++++++++++++++++++++++++++++++-
 t/t1300-repo-config.sh |   43 +++++++++++++++++++++++++++++++++++++
 2 files changed, 97 insertions(+), 1 deletions(-)
diff --git a/config.c b/config.c
index 6963fbe..e7581b4 100644
--- a/config.c
+++ b/config.c
@@ -7,6 +7,7 @@
  */
 #include "cache.h"
 #include "exec_cmd.h"
+#include <glob.h>
 
 #define MAXNAME (256)
 
@@ -111,6 +112,52 @@ static inline int iskeychar(int c)
 	return isalnum(c) || c == '-';
 }
 
+static void glob_include_config(config_fn_t fn, void *data, char *pattern)
+{
+	glob_t globber;
+	int glob_ret;
+	int conf_ret;
+	size_t i = 0;
+	char *cfile = NULL;
+	FILE *saved_config_file;
+	char *saved_config_file_name;
+	int saved_config_linenr;
+	int saved_config_file_eof;
+#ifdef GLOB_TILDE
+	int glob_flags = GLOB_TILDE;
+#else
+	/* XXX: Non-GNU support for ~ expansion */
+	int glob_flags = 0;
+#endif
+
+	glob_ret = glob(pattern, glob_flags, NULL, &globber);
+
+	if (glob_ret == GLOB_NOSPACE || glob_ret == GLOB_ABORTED) {
+		globfree(&globber);
+		die("Unable to include config with pattern %s", pattern);
+	}
+
+	for (i = 0; i < globber.gl_pathc; i++) {
+		cfile = globber.gl_pathv[i];
+
+		/* Save away global state for including another file */
+		saved_config_file = config_file;
+		saved_config_file_name = config_file_name;
+		saved_config_linenr = config_linenr;
+		saved_config_file_eof = config_file_eof;
+
+		conf_ret = git_config_from_file(fn, cfile, data);
+
+		/* Restore it again */
+		config_file = saved_config_file;
+		config_file_name = saved_config_file_name;
+		config_linenr = saved_config_linenr;
+		config_file_eof = saved_config_file_eof;
+	}
+
+	globfree(&globber);
+}
+
 static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
 {
 	int c;
@@ -139,7 +186,13 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
 		if (!value)
 			return -1;
 	}
-	return fn(name, value, data);
+
+	if (!strcmp(name, "voodoo.include")) {
+		glob_include_config(fn, data, value);
+		return 0;
+	} else {
+		return fn(name, value, data);
+	}
 }
 
 static int get_extended_base_var(char *name, int baselen, int c)
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index f11f98c..4df6658 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -824,4 +824,47 @@ test_expect_success 'check split_cmdline return' "
 	test_must_fail git merge master
 	"
 
+cat > .git/config << EOF
+[some]
+	variable = blah
+[voodoo]
+	include = .git/more_config_*
+EOF
+
+cat > .git/more_config_1 << EOF
+[another]
+	variable = blah blah
+EOF
+
+cat > .git/more_config_2 << EOF
+[evenmore]
+	variable = blah bluh 
+EOF
+
+test_expect_success 'The voodoo include variable is hidden from us' \
+    'test_must_fail git config --get voodoo.include'
+test_expect_success 'get some included variable' \
+    'git config --get some.variable'
+test_expect_success 'get another included variable' \
+    'git config --get another.variable'
+test_expect_success 'get evenmore included variable' \
+    'git config --get evenmore.variable'
+
+rm .git/more_config*
+
+cat > .git/config << EOF
+[voodoo]
+	include = .git/more_config_*
+EOF
+
+cat > .git/more_config_1 << EOF
+[foo]
+    bar = zar
+[voodoo]
+	include = .git/more_config_*
+EOF
+
+test_expect_success 'circular config inclusion' \
+    'test_must_fail git config --get foo.bar'
+
 test_done
-- 
1.7.1.dirty
Previous: Jakub NarebskiNext: Bert Wesarg
Message 7 of 21 in “Is there interest in reading ~/.gitconfig.d/* and /etc/gitconfig.d/*?”
  1. Ævar Arnfjörð BjarmasonApr 1, 2010
  2. Heiko VoigtApr 1, 2010
  3. Peter KreftingApr 4, 2010
  4. Eli BarzilayApr 4, 2010
  5. Ævar Arnfjörð BjarmasonApr 6, 2010
  6. Jakub NarebskiApr 6, 2010
  7. Hacky version of a glob() driven config includeÆvar Arnfjörð Bjarmason, May 6, 2010
  8. Bert WesargMay 7, 2010
  9. Ævar Arnfjörð BjarmasonMay 7, 2010
  10. Bert WesargMay 7, 2010
  11. Ævar Arnfjörð BjarmasonMay 7, 2010
  12. Jacob HelwigMay 7, 2010
  13. Bert WesargMay 7, 2010
  14. Hacky version of a glob() driven config includeÆvar Arnfjörð Bjarmason, May 7, 2010
  15. Jakub NarebskiMay 7, 2010
  16. Ævar Arnfjörð BjarmasonMay 7, 2010
  17. Jakub NarebskiMay 7, 2010
  18. Ping YinMay 8, 2010
  19. Jakub NarebskiMay 8, 2010
  20. Ping YinMay 8, 2010
  21. Jeff KingMay 8, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.