threads / discuss / 6813

/etc/gitconfig

Subject: /etc/gitconfig

## tl;dr

30 messages between Feb 14, 2007 and Feb 19, 2007.

replies: 29people: 10as markdown or json

Andy Parkins· Feb 14, 2007, 09:09 UTC · lore
Hello

With the release of 1.5.0 I added legacyheaders=false and usedelatbaseoffset=true to my repositories.

While doing that though, it felt wrong to be editing every single config. Is there a justification for having three config files?

 /etc/gitconfig
 $HOME/.gitconfig
 $GIT_DIR/config
With /etc/gitconfig coming in at lowest priority?
Andy
-- 
Dr Andy Parkins, M Eng (hons), MIEE
andyparkins@gmail.com
Peter Baumann· Feb 14, 2007, 10:30 UTC · re: Andy Parkins · lore

Re: /etc/gitconfig

Andy Parkins <andyparkins@gmail.com> schrieb:
Show 15 quoted lines
> Hello
>
> With the release of 1.5.0 I added legacyheaders=false and 
> usedelatbaseoffset=true to my repositories.
>
> While doing that though, it felt wrong to be editing every single config.  Is 
> there a justification for having three config files?
>
>  /etc/gitconfig
>  $HOME/.gitconfig
>  $GIT_DIR/config
>
> With /etc/gitconfig coming in at lowest priority?
>
> Andy

I would like this, too. This would allow me to set e.g. "diff.color = auto" for everyone one a machine. We use git there to only manage some config files and several simple perl scripts. As it is mostly myself who does the editing, ~/.gitconfig works for *ME*.

But I'd like to set this globally so that my colluagues which don't have much clue about git don't need to bother about config settings. And yes, editing every single repo doesn't seem like an elegant solution.

-Peter
Johannes Schindelin· Feb 14, 2007, 11:48 UTC · re: Peter Baumann · lore

[PATCH] config: read system-wide defaults from /etc/gitconfig

The settings in /etc/gitconfig can be overridden in ~/.gitconfig, which in turn can be overridden in .git/config.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
	On Wed, 14 Feb 2007, Peter Baumann wrote:
	> Andy Parkins <andyparkins@gmail.com> schrieb:
	> >
	> > With the release of 1.5.0 I added legacyheaders=false and
	> > usedelatbaseoffset=true to my repositories.
	> >
	> > While doing that though, it felt wrong to be editing every 
	> > single config.  Is there a justification for having three 
	> > config files?
	> >
	> >  /etc/gitconfig
	> >  $HOME/.gitconfig
	> >  $GIT_DIR/config
	> >
	> > With /etc/gitconfig coming in at lowest priority?
	> 
	> I would like this, too. This would allow me to set e.g.
	> "diff.color = auto" for everyone one a machine. We use git there 
	> to only manage some config files and several simple perl 
	> scripts. As it is mostly myself who does the editing, 
	> ~/.gitconfig works for *ME*.
	> 
	> But I'd like to set this globally so that my colluagues which 
	> don't have much clue about git don't need to bother about config 
	> settings. And yes, editing every single repo doesn't seem like 
	> an elegant solution.
	Voilà.
 Documentation/config.txt |    3 ++-
 cache.h                  |    1 +
 config.c                 |    2 ++
 3 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 3865535..1dd90d8 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -5,7 +5,8 @@ The git configuration file contains a number of variables that affect
 the git command's behavior. `.git/config` file for each repository
 is used to store the information for that repository, and
 `$HOME/.gitconfig` is used to store per user information to give
-fallback values for `.git/config` file.
+fallback values for `.git/config` file. The file `/etc/gitconfig`
+can be used to store system-wide defaults.
 
 They can be used by both the git plumbing
 and the porcelains. The variables are divided into sections, where
diff --git a/cache.h b/cache.h
index 44941c0..e316f66 100644
--- a/cache.h
+++ b/cache.h
@@ -123,6 +123,7 @@ extern int cache_errno;
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
 #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
 #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
+#define ETC_GITCONFIG "/etc/gitconfig"
 #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 #define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
 #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
diff --git a/config.c b/config.c
index d821071..b0c0948 100644
--- a/config.c
+++ b/config.c
@@ -383,6 +383,8 @@ int git_config(config_fn_t fn)
 	 * config file otherwise. */
 	filename = getenv(CONFIG_ENVIRONMENT);
 	if (!filename) {
+		if (!access(ETC_GITCONFIG, R_OK))
+			ret += git_config_from_file(fn, ETC_GITCONFIG);
 		home = getenv("HOME");
 		filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
 		if (!filename)
-- 
1.5.0.rc4.2447.g06287-dirty
Junio C Hamano· Feb 14, 2007, 16:30 UTC · re: Johannes Schindelin · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 7 quoted lines
> The settings in /etc/gitconfig can be overridden in ~/.gitconfig,
> which in turn can be overridden in .git/config.
>
> Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
> ---
> ...
> 	Voilà.

I knew this can be done, and you are capable of doing this, but I was wondering if it is a good thing to do in the first place.

Site-wide configuration for options that are potentially compatibility-breaking is a bad idea on a multi-user machines, and it was certainly the case back when our machines hosted many diverse set of people.

But these days many machines are practically single-user and many more are owned by a single group/project that share the same policy. So in such a setting, /etc/gitconfig might not be too bad.

Johannes Schindelin· Feb 14, 2007, 17:45 UTC · re: Junio C Hamano · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Hi,
On Wed, 14 Feb 2007, Junio C Hamano wrote:
Show 17 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > The settings in /etc/gitconfig can be overridden in ~/.gitconfig,
> > which in turn can be overridden in .git/config.
> >
> > Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
> > ---
> > ...
> > 	Voilà.
> 
> I knew this can be done, and you are capable of doing this, but I was 
> wondering if it is a good thing to do in the first place.
> 
> Site-wide configuration for options that are potentially 
> compatibility-breaking is a bad idea on a multi-user machines, and it 
> was certainly the case back when our machines hosted many diverse set of 
> people.

That is why /etc is only writable by the administrator. And contrary to the Microsoft-infected world, Unix and Linux administrators are expected to have real know how. If they don't care about breaking things or not, they'll find other ways to screw you.

> But these days many machines are practically single-user and many more 
> are owned by a single group/project that share the same policy.  So in 
> such a setting, /etc/gitconfig might not be too bad.

Well, I don't use it myself (other than testing that the patch does what it says). Let the list decide?

Ciao, Dscho

Junio C Hamano· Feb 14, 2007, 17:57 UTC · re: Johannes Schindelin · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Well, I don't use it myself (other than testing that the patch does what 
> it says). Let the list decide?

I am slightly worried about stupid distros screwing people over by shipping with a bad default config in /etc, which would make diagnosing their problems harder without knowing what they have there. But other than that I do not have a problem with it.

Johannes Schindelin· Feb 14, 2007, 18:02 UTC · re: Junio C Hamano · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Hi,
On Wed, 14 Feb 2007, Junio C Hamano wrote:
Show 9 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > Well, I don't use it myself (other than testing that the patch does what 
> > it says). Let the list decide?
> 
> I am slightly worried about stupid distros screwing people over
> by shipping with a bad default config in /etc, which would make
> diagnosing their problems harder without knowing what they have
> there.  But other than that I do not have a problem with it.

They can do that already by shipping with a bad default /etc/skel/ directory.

Ciao, Dscho

Junio C Hamano· Feb 14, 2007, 18:12 UTC · re: Johannes Schindelin · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 16 quoted lines
> Hi,
>
> On Wed, 14 Feb 2007, Junio C Hamano wrote:
>
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> 
>> > Well, I don't use it myself (other than testing that the patch does what 
>> > it says). Let the list decide?
>> 
>> I am slightly worried about stupid distros screwing people over
>> by shipping with a bad default config in /etc, which would make
>> diagnosing their problems harder without knowing what they have
>> there.  But other than that I do not have a problem with it.
>
> They can do that already by shipping with a bad default /etc/skel/ 
> directory.

That's slightly different. I can ask "Do you have anything special in your $HOME/.gitconfig?" Now I have to ask two questions (and I would not know if a particular distro compiled git to use /etc/git/default.config or /etc/gitconfig or whatever).

Nicolas Pitre· Feb 14, 2007, 18:19 UTC · re: Junio C Hamano · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

On Wed, 14 Feb 2007, Junio C Hamano wrote:
> That's slightly different.  I can ask "Do you have anything
> special in your $HOME/.gitconfig?"  Now I have to ask two
> questions (and I would not know if a particular distro compiled
> git to use /etc/git/default.config or /etc/gitconfig or whatever).
Why not simplifying the question to only:
	git config -l
then?
Nicolas
Junio C Hamano· Feb 14, 2007, 19:06 UTC · re: Nicolas Pitre · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Nicolas Pitre <nico@cam.org> writes:
Show 5 quoted lines
> Why not simplifying the question to only:
>
> 	git config -l
>
> then?
Fair enough.
Andy Parkins· Feb 15, 2007, 10:19 UTC · re: Junio C Hamano · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

On Wednesday 2007 February 14 16:30, Junio C Hamano wrote:
> Site-wide configuration for options that are potentially
> compatibility-breaking is a bad idea on a multi-user machines,
> and it was certainly the case back when our machines hosted many
> diverse set of people.

Isn't it more likely that on a multi-user machine all users are sharing one install of git - in which case you do want the upgrade of facilities to be system-wide. Otherwise you have to tell every user to edit their .gitconfig to enable the new facilities - users shouldn't have to care about that sort of thing, that's what admins are for.

Andy
-- 
Dr Andy Parkins, M Eng (hons), MIEE
andyparkins@gmail.com
Junio C Hamano· Feb 15, 2007, 11:26 UTC · re: Andy Parkins · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Andy Parkins <andyparkins@gmail.com> writes:
Show 10 quoted lines
> On Wednesday 2007 February 14 16:30, Junio C Hamano wrote:
>
>> Site-wide configuration for options that are potentially
>> compatibility-breaking is a bad idea on a multi-user machines,
>> and it was certainly the case back when our machines hosted many
>> diverse set of people.
>
> Isn't it more likely that on a multi-user machine all users are sharing one 
> install of git - in which case you do want the upgrade of facilities to be 
> system-wide.

Not really. Some repositories would need to be accessible by people with older git coming over the network. Some don't.

Han-Wen Nienhuys· Feb 14, 2007, 18:10 UTC · re: Johannes Schindelin · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Johannes Schindelin escreveu:
> The settings in /etc/gitconfig can be overridden in ~/.gitconfig,
> which in turn can be overridden in .git/config.
> +#define ETC_GITCONFIG "/etc/gitconfig"
> +		if (!access(ETC_GITCONFIG, R_OK))
> +			ret += git_config_from_file(fn, ETC_GITCONFIG);
this is a stupid idea.  

This should use the path given as the --sysconfdir argument to configure. If the configure result is not available, it should default to $prefix/etc/

It should be possible to install private copy of Git without being affected by system wide defaults.

-- 
 Han-Wen Nienhuys - hanwen@xs4all.nl - http://www.xs4all.nl/~hanwen
Junio C Hamano· Feb 14, 2007, 19:07 UTC · re: Han-Wen Nienhuys · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Han-Wen Nienhuys <hanwen@xs4all.nl> writes:
Show 10 quoted lines
> Johannes Schindelin escreveu:
>> The settings in /etc/gitconfig can be overridden in ~/.gitconfig,
>> which in turn can be overridden in .git/config.
>
>> +#define ETC_GITCONFIG "/etc/gitconfig"
>
>> +		if (!access(ETC_GITCONFIG, R_OK))
>> +			ret += git_config_from_file(fn, ETC_GITCONFIG);
>
> this is a stupid idea.  

Stupid probably is too strong a word, but I think I'd agree we should default it to $(prefix)/etc and have distros override it in the Makefile.

Johannes Schindelin· Feb 14, 2007, 19:25 UTC · re: Junio C Hamano · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Hi,
On Wed, 14 Feb 2007, Junio C Hamano wrote:
Show 16 quoted lines
> Han-Wen Nienhuys <hanwen@xs4all.nl> writes:
> 
> > Johannes Schindelin escreveu:
> >> The settings in /etc/gitconfig can be overridden in ~/.gitconfig,
> >> which in turn can be overridden in .git/config.
> >
> >> +#define ETC_GITCONFIG "/etc/gitconfig"
> >
> >> +		if (!access(ETC_GITCONFIG, R_OK))
> >> +			ret += git_config_from_file(fn, ETC_GITCONFIG);
> >
> > this is a stupid idea.  
> 
> Stupid probably is too strong a word, but I think I'd agree we
> should default it to $(prefix)/etc and have distros override it
> in the Makefile.

Fair enough. And my patch was not really complete. Please amend to spare me eternal shame: -- [BROWN PAPERBAG PATCH ON TOP OF OTHER PATCH]

It is $prefix/etc/gitconfig now, and works also if you do not `git config -l`. D'oh.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
 Makefile         |    5 ++++-
 builtin-config.c |    7 ++++++-
 cache.h          |    1 -
 3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/Makefile b/Makefile
index 6f05abb..617908c 100644
--- a/Makefile
+++ b/Makefile
@@ -128,6 +128,7 @@ prefix = $(HOME)
 bindir = $(prefix)/bin
 gitexecdir = $(bindir)
 template_dir = $(prefix)/share/git-core/templates/
+ETC_GITCONFIG = $(prefix)/etc/gitconfig
 # DESTDIR=
 
 # default configuration for gitweb
@@ -591,6 +592,7 @@ endif
 # Shell quote (do not use $(call) to accommodate ancient setups);
 
 SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
+ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC_GITCONFIG))
 
 DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
 bindir_SQ = $(subst ','\'',$(bindir))
@@ -603,7 +605,8 @@ PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 
 LIBS = $(GITLIBS) $(EXTLIBS)
 
-BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS)
+BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
+	-DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $(COMPAT_CFLAGS)
 LIB_OBJS += $(COMPAT_OBJS)
 
 ALL_CFLAGS += $(BASIC_CFLAGS)
diff --git a/builtin-config.c b/builtin-config.c
index 0f9051d..34470c4 100644
--- a/builtin-config.c
+++ b/builtin-config.c
@@ -64,7 +64,7 @@ static int get_value(const char* key_, const char* regex_)
 	int ret = -1;
 	char *tl;
 	char *global = NULL, *repo_config = NULL;
-	const char *local;
+	const char *system_wide = NULL, *local;
 
 	local = getenv(CONFIG_ENVIRONMENT);
 	if (!local) {
@@ -74,6 +74,7 @@ static int get_value(const char* key_, const char* regex_)
 			local = repo_config = xstrdup(git_path("config"));
 		if (home)
 			global = xstrdup(mkpath("%s/.gitconfig", home));
+		system_wide = ETC_GITCONFIG;
 	}
 
 	key = xstrdup(key_);
@@ -103,11 +104,15 @@ static int get_value(const char* key_, const char* regex_)
 		}
 	}
 
+	if (do_all && system_wide)
+		git_config_from_file(show_config, system_wide);
 	if (do_all && global)
 		git_config_from_file(show_config, global);
 	git_config_from_file(show_config, local);
 	if (!do_all && !seen && global)
 		git_config_from_file(show_config, global);
+	if (!do_all && !seen && system_wide)
+		git_config_from_file(show_config, system_wide);
 
 	free(key);
 	if (regexp) {
diff --git a/cache.h b/cache.h
index e316f66..44941c0 100644
--- a/cache.h
+++ b/cache.h
@@ -123,7 +123,6 @@ extern int cache_errno;
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
 #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
 #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
-#define ETC_GITCONFIG "/etc/gitconfig"
 #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 #define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
 #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
Junio C Hamano· Feb 14, 2007, 19:43 UTC · re: Johannes Schindelin · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 11 quoted lines
>> Stupid probably is too strong a word, but I think I'd agree we
>> should default it to $(prefix)/etc and have distros override it
>> in the Makefile.
>
> Fair enough. And my patch was not really complete. Please amend to spare 
> me eternal shame:
> --
> [BROWN PAPERBAG PATCH ON TOP OF OTHER PATCH] 
>
> It is $prefix/etc/gitconfig now, and works also if you do not
> `git config -l`. D'oh.

How would we run our tests without getting screwed by whatever non-standard values system-wide configuration may have? Currently we disable tester's ~/.gitconfig by exporting HOME from t/test-lib.sh, but that will not work for this.

I think we need to have a way to disable this setting, perhaps via an environment variable.

I do not think we need to support 'git config --system --set foo.bar baz' but some people might have twisted minds ;-).

Johannes Schindelin· Feb 14, 2007, 19:54 UTC · re: Junio C Hamano · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Hi,
On Wed, 14 Feb 2007, Junio C Hamano wrote:
Show 21 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> >> Stupid probably is too strong a word, but I think I'd agree we
> >> should default it to $(prefix)/etc and have distros override it
> >> in the Makefile.
> >
> > Fair enough. And my patch was not really complete. Please amend to spare 
> > me eternal shame:
> > --
> > [BROWN PAPERBAG PATCH ON TOP OF OTHER PATCH] 
> >
> > It is $prefix/etc/gitconfig now, and works also if you do not
> > `git config -l`. D'oh.
> 
> How would we run our tests without getting screwed by whatever
> non-standard values system-wide configuration may have?
> Currently we disable tester's ~/.gitconfig by exporting HOME
> from t/test-lib.sh, but that will not work for this.
> 
> I think we need to have a way to disable this setting, perhaps
> via an environment variable.

Yes. Just set GIT_CONFIG=/what/ever, and it no longer reads from ~/.gitconfig or $prefix/etc/gitconfig.

But this possibly breaks down when creating a repository in a subdirectory of trash. Hmm.

> I do not think we need to support 'git config --system --set foo.bar 
> baz' but some people might have twisted minds ;-).

;-) Yes, I'd rather not have an administrator run git directly. But it _would_ be easy enough:

diff --git a/builtin-config.c b/builtin-config.c
index 0f9051d..a42d251 100644
--- a/builtin-config.c
+++ b/builtin-config.c
@@ -147,6 +152,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 			} else {
 				die("$HOME not set");
 			}
+		else if (!strcmp(argv[1], "--system-wide")) {
+			setenv("GIT_CONFIG", ETC_GITCONFIG, 1);
 		} else if (!strcmp(argv[1], "--rename-section")) {
 			int ret;
 			if (argc != 4)
Johannes Schindelin· Feb 14, 2007, 22:39 UTC · re: Johannes Schindelin · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Hi,
On Wed, 14 Feb 2007, Johannes Schindelin wrote:
Show 9 quoted lines
> diff --git a/builtin-config.c b/builtin-config.c
> index 0f9051d..a42d251 100644
> --- a/builtin-config.c
> +++ b/builtin-config.c
> @@ -147,6 +152,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
>  			} else {
>  				die("$HOME not set");
>  			}
> +		else if (!strcmp(argv[1], "--system-wide")) {
Ooops. There is a "}" missing before "else".

Sorry, Dscho

Junio C Hamano· Feb 15, 2007, 05:27 UTC · re: Johannes Schindelin · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 5 quoted lines
>> I think we need to have a way to disable this setting, perhaps
>> via an environment variable.
>
> Yes. Just set GIT_CONFIG=/what/ever, and it no longer reads from 
> ~/.gitconfig or $prefix/etc/gitconfig.

I think GIT_CONFIG and GIT_LOCAL_CONFIG environment variables are seriously misdesigned. At least, I do not think of a sane way to make use of them. For one thing, when they are set, truly per-repository variables such as repositoryformatversion, legacyheaders and sharedrepository are all ignored.

What I would think is sensible would be simply to:
 - $GIT_CONFIG_SYSTEM environment, if set, names a file to be
   read first.  It defaults to /etc/gitconfig.  You can set it
   to /dev/null to avoid using /etc/gitconfig if you have
   specific need.
 - Then $HOME/.gitconfig is read.
 - Then $GIT_DIR/config is read.

Information read from later files overrides the earlier ones, as before.

I am not quite sure how $GIT_CONFIG and $GIT_CONFIG_LOCAL were meant to be used. Are there any *real* users? With lack of information on the intended uses of these two environment variables, I hacked up the following tweaks on top of the above defined semantics, to imitate what I _think_ was the original intention. The hacked one goes like this:

 - $GIT_CONFIG environment, if set, names a file to be read
   first.  We read it first.
 - If $GIT_CONFIG is unset, then $GIT_CONFIG_SYSTEM (or
   /etc/gitconfig) and $HOME/.gitconfig are read, as above.
 - Next file to be read is $GIT_CONFIG_LOCAL (if set) or
   $GIT_DIR/config.  When reading this file, if we read from
   $GIT_CONFIG earlier, we read only "core.*" section from it.

The difference from the original, aside from the additional business with /etc/gitconfig, is that this updated one does read from $GIT_DIR/config (or $GIT_CONFIG_LOCAL) to avoid missing more important per-repo variables. Since I did not understand why $GIT_CONFIG makes the remaining configuration files to be totally skipped in the original, I added hacks to make the last step to read only minimally.

I'd rather not to have the hacks to deal with GIT_CONFIG and GIT_CONFIG_LOCAL I did in this patch, but I do not know enough to tell if they are meaningless hacks to imitate ill-defined original semantics, or if they still cover useful use cases these two environment variables were originally invented to satisfy.

---
 Makefile |   10 +++++--
 cache.h  |    4 +++
 config.c |   84 +++++++++++++++++++++++++++++++++++++++----------------------
 3 files changed, 65 insertions(+), 33 deletions(-)
diff --git a/Makefile b/Makefile
index ebecbbd..4232465 100644
--- a/Makefile
+++ b/Makefile
@@ -122,6 +122,7 @@ STRIP ?= strip
 
 prefix = $(HOME)
 bindir = $(prefix)/bin
+etcdir = $(prefix)/etc
 gitexecdir = $(bindir)
 template_dir = $(prefix)/share/git-core/templates/
 # DESTDIR=
@@ -142,7 +143,7 @@ GITWEB_FAVICON = git-favicon.png
 GITWEB_SITE_HEADER =
 GITWEB_SITE_FOOTER =
 
-export prefix bindir gitexecdir template_dir
+export prefix bindir etcdir gitexecdir template_dir
 
 CC = gcc
 AR = ar
@@ -587,6 +588,7 @@ SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
 
 DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
 bindir_SQ = $(subst ','\'',$(bindir))
+etcdir_SQ = $(subst ','\'',$(etcdir))
 gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
 prefix_SQ = $(subst ','\'',$(prefix))
@@ -596,7 +598,9 @@ PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 
 LIBS = $(GITLIBS) $(EXTLIBS)
 
-BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS)
+BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)'
+BASIC_CFLAGS += -DGIT_CONFIG_SYSTEM='"$(etcdir_SQ)/gitconfig"'
+BASIC_CFLAGS += $(COMPAT_CFLAGS)
 LIB_OBJS += $(COMPAT_OBJS)
 
 ALL_CFLAGS += $(BASIC_CFLAGS)
diff --git a/cache.h b/cache.h
index c62b0b0..2b4e26d 100644
--- a/cache.h
+++ b/cache.h
@@ -125,6 +125,10 @@ extern int cache_errno;
 #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
 #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 #define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
+#define CONFIG_SYSTEM_ENVIRONMENT "GIT_CONFIG_SYSTEM"
+#ifndef GIT_CONFIG_SYSTEM
+#define GIT_CONFIG_SYSTEM "/etc/gitconfig"
+#endif
 #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
 
 extern int is_bare_repository_cfg;
diff --git a/config.c b/config.c
index c938aa0..7ec0d9a 100644
--- a/config.c
+++ b/config.c
@@ -107,7 +107,7 @@ static inline int iskeychar(int c)
 	return isalnum(c) || c == '-';
 }
 
-static int get_value(config_fn_t fn, char *name, unsigned int len)
+static int get_value(config_fn_t fn, char *name, unsigned int len, int core_only)
 {
 	int c;
 	char *value;
@@ -135,6 +135,8 @@ static int get_value(config_fn_t fn, char *name, unsigned int len)
 		if (!value)
 			return -1;
 	}
+	if (core_only && strncmp(name, "core.", 5))
+		return 0;
 	return fn(name, value);
 }
 
@@ -193,7 +195,7 @@ static int get_base_var(char *name)
 	}
 }
 
-static int git_parse_file(config_fn_t fn)
+static int git_parse_file(config_fn_t fn, int core_only)
 {
 	int comment = 0;
 	int baselen = 0;
@@ -225,7 +227,8 @@ static int git_parse_file(config_fn_t fn)
 		if (!isalpha(c))
 			break;
 		var[baselen] = tolower(c);
-		if (get_value(fn, var, baselen+1) < 0)
+
+		if (get_value(fn, var, baselen+1, core_only) < 0)
 			break;
 	}
 	die("bad config file line %d in %s", config_linenr, config_file_name);
@@ -356,50 +359,71 @@ int git_default_config(const char *var, const char *value)
 	return 0;
 }
 
-int git_config_from_file(config_fn_t fn, const char *filename)
+static int git_config_from_file_1(config_fn_t fn, const char *filename, int core_only, int missing_ok)
 {
 	int ret;
 	FILE *f = fopen(filename, "r");
 
-	ret = -1;
+	ret = missing_ok ? 0 : -1;
 	if (f) {
 		config_file = f;
 		config_file_name = filename;
 		config_linenr = 1;
-		ret = git_parse_file(fn);
+		ret = git_parse_file(fn, core_only);
 		fclose(f);
 		config_file_name = NULL;
 	}
 	return ret;
 }
 
+int git_config_from_file(config_fn_t fn, const char *filename)
+{
+	return git_config_from_file_1(fn, filename, 0, 0);
+}
+
 int git_config(config_fn_t fn)
 {
+	/*
+	 * If GIT_CONFIG environment is set, then it is read first.
+	 * If GIT_CONFIG environment is not set,
+	 *    GIT_CONFIG_SYSTEM (default /etc/gitconfig) is read next.
+	 *    Then $HOME/.gitconfig is read.
+	 *
+	 * Then, if GIT_CONFIG_LOCAL is set, it is read.
+	 * Otherwise "$GIT_DIR/config" is read.
+	 * 
+	 * However, if we read from GIT_CONFIG environment earlier, we
+	 * only read "core.*" section in the last step.
+	 */
 	int ret = 0;
-	char *repo_config = NULL;
-	const char *home = NULL, *filename;
-
-	/* $GIT_CONFIG makes git read _only_ the given config file,
-	 * $GIT_CONFIG_LOCAL will make it process it in addition to the
-	 * global config file, the same way it would the per-repository
-	 * config file otherwise. */
-	filename = getenv(CONFIG_ENVIRONMENT);
-	if (!filename) {
-		home = getenv("HOME");
-		filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
-		if (!filename)
-			filename = repo_config = xstrdup(git_path("config"));
-	}
-
-	if (home) {
-		char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
-		if (!access(user_config, R_OK))
-			ret = git_config_from_file(fn, user_config);
-		free(user_config);
-	}
-
-	ret += git_config_from_file(fn, filename);
-	free(repo_config);
+	int core_only;
+	char *cfg;
+	char path[PATH_MAX];
+
+	cfg = getenv(CONFIG_ENVIRONMENT);
+	if (cfg) {
+		ret += git_config_from_file_1(fn, cfg, 0, 1);
+		core_only = 1;
+	}
+	else {
+		core_only = 0;
+		cfg = getenv(CONFIG_SYSTEM_ENVIRONMENT);
+		if (!cfg)
+			cfg = GIT_CONFIG_SYSTEM;
+		ret += git_config_from_file_1(fn, cfg, 0, 1);
+
+		cfg = getenv("HOME");
+		if (cfg) {
+			snprintf(path, sizeof(path), "%s/.gitconfig", cfg);
+			ret += git_config_from_file_1(fn, path, 0, 1);
+		}
+	}
+	cfg = getenv(CONFIG_LOCAL_ENVIRONMENT);
+	if (!cfg) {
+		strcpy(path, git_path("config"));
+		cfg = path;
+	}
+	ret += git_config_from_file_1(fn, cfg, core_only, 1);
 	return ret;
 }
 
Junio C Hamano· Feb 15, 2007, 08:46 UTC · re: Junio C Hamano · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Junio C Hamano <junkio@cox.net> writes:
Show 17 quoted lines
> I am not quite sure how $GIT_CONFIG and $GIT_CONFIG_LOCAL were
> meant to be used.  Are there any *real* users?  With lack of
> information on the intended uses of these two environment
> variables, I hacked up the following tweaks on top of the above
> defined semantics, to imitate what I _think_ was the original
> intention.  The hacked one goes like this:
>
>  - $GIT_CONFIG environment, if set, names a file to be read
>    first.  We read it first.
>
>  - If $GIT_CONFIG is unset, then $GIT_CONFIG_SYSTEM (or
>    /etc/gitconfig) and $HOME/.gitconfig are read, as above.
>
>  - Next file to be read is $GIT_CONFIG_LOCAL (if set) or
>    $GIT_DIR/config.  When reading this file, if we read from
>    $GIT_CONFIG earlier, we read only "core.*" section from it.
> ...
Addendum.

Judging from the way builtin-config.c::get_value() uses it, I think whoever invented $GIT_CONFIG_LOCAL as a parallel to $GIT_INDEX_FILE and $GIT_OBJECT_DIRECTORY, in other words, what usually appear under $GIT_DIR can be placed somewhere completely different. So I think the above "hack" I described still honors its intended use.

I am still not sure what good GIT_CONFIG would be to completely override everything else, though.

Eric Wong· Feb 15, 2007, 09:59 UTC · re: Junio C Hamano · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Junio C Hamano <junkio@cox.net> wrote:
Show 16 quoted lines
> Junio C Hamano <junkio@cox.net> writes:
> 
> > I am not quite sure how $GIT_CONFIG and $GIT_CONFIG_LOCAL were
> > meant to be used.  Are there any *real* users?  With lack of
> 
> Addendum.
> 
> Judging from the way builtin-config.c::get_value() uses it, I
> think whoever invented $GIT_CONFIG_LOCAL as a parallel to
> $GIT_INDEX_FILE and $GIT_OBJECT_DIRECTORY, in other words, what
> usually appear under $GIT_DIR can be placed somewhere completely
> different.  So I think the above "hack" I described still honors
> its intended use.
> 
> I am still not sure what good GIT_CONFIG would be to completely
> override everything else, though.

I just started using GIT_CONFIG in the experimental version of git-svn[1] which allows me to use .git/svn/config for tracking some metadata bits.

I didn't want to pollute the users' .git/config with automatically read and state data in .git/config; so I started using .git/svn/config to avoid littering .git/svn/ with any tiny pieces of data I might need.

-- 
Eric Wong
[1] - git://bogomips.org/git-svn
Eric Wong· Feb 15, 2007, 10:03 UTC · re: Eric Wong · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Eric Wong <normalperson@yhbt.net> wrote:
> Junio C Hamano <junkio@cox.net> wrote:
> > Junio C Hamano <junkio@cox.net> writes:
> > different.  So I think the above "hack" I described still honors
> > its intended use.
GIT_CONFIG continues to work here with the latest git-svn + your hack
-- 
Eric Wong
Junio C Hamano· Feb 15, 2007, 10:36 UTC · re: Eric Wong · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Eric Wong <normalperson@yhbt.net> writes:
Show 12 quoted lines
> Junio C Hamano <junkio@cox.net> wrote:
> ...
>> I am still not sure what good GIT_CONFIG would be to completely
>> override everything else, though.
>
> I just started using GIT_CONFIG in the experimental version of
> git-svn[1] which allows me to use .git/svn/config for tracking some
> metadata bits.
>
> I didn't want to pollute the users' .git/config with automatically read
> and state data in .git/config; so I started using .git/svn/config to
> avoid littering .git/svn/ with any tiny pieces of data I might need.

Ah, so the intended usage is to use git-config to store in and retrieve from a file that does not have anything to do with the core git data, and the using script would say:

	value=$(GIT_CONFIG=$that_extra_file git-config --get some.variable)
        ... do whatever with $value which does not have
        ... anything to do with git core itself
        gitvalue=$(git-config --get some.git.variable)
	... use gitvalue which has some relevance to git core
	GIT_CONFIG=$that_extra_file git-config some.variable $newvalue

I was more worried about the fact that the current GIT_CONFIG totally *ignores* the variables in .git/config. With a narrower-than-usual umask, not reading core.sharedrepository would result in quite an undesired result. But as long as the intended usage follows the patter like above, in other words, the user (a script) will never export GIT_CONFIG when using core git plumbing or porcelain, I think it is Ok.

Johannes Schindelin· Feb 15, 2007, 10:43 UTC · re: Junio C Hamano · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Hi,
On Wed, 14 Feb 2007, Junio C Hamano wrote:
Show 10 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> >> I think we need to have a way to disable this setting, perhaps
> >> via an environment variable.
> >
> > Yes. Just set GIT_CONFIG=/what/ever, and it no longer reads from 
> > ~/.gitconfig or $prefix/etc/gitconfig.
> 
> I think GIT_CONFIG and GIT_LOCAL_CONFIG environment variables are 
> seriously misdesigned.

Okay for GIT_LOCAL_CONFIG. I do not remember off-hand who wanted it (Jakub? Pasky?), but it was in the context of gitweb.

However, GIT_CONFIG is meant to parse arbitrary config files. You can literally say

	GIT_CONFIG=/windows/C/WINNT/system.ini git config drivers.wave
and get the value for "wave" in system.ini, section [drivers].
BTW I find the HOME mangling in test-lib.sh insane. Here's a replacement:

-- snip -- [PATCH] Make tests independent of global config files

This was done by setting $HOME to somewhere bogus. A better method is to reuse $GIT_CONFIG, which was invented for ignoring the global config file explicitely.

Technically, setting GIT_CONFIG=.git/config could be wrong, but it passes all the tests, and we can keep the tests that way.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
 t/test-lib.sh |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 37822fc..a403fe0 100755
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -255,8 +255,8 @@ test_done () {
 PATH=$(pwd)/..:$PATH
 GIT_EXEC_PATH=$(pwd)/..
 GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
-HOME=$(pwd)/trash
-export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR HOME
+GIT_CONFIG=.git/config
+export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG
 
 GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
 export GITPERLLIB
-- snap --

>  - $GIT_CONFIG_SYSTEM environment, if set, names a file to be
>    read first.  It defaults to /etc/gitconfig.  You can set it
>    to /dev/null to avoid using /etc/gitconfig if you have
>    specific need.
> 
>  - Then $HOME/.gitconfig is read.
> 
>  - Then $GIT_DIR/config is read.

Yes, you can. But I'd rather not. The contents of /etc/gitconfig should be 
changed with care, and _if_ they are changed, you should be forced to 
override the settings explicitely in $HOME/.gitconfig.

Of course, this assumes a sensible administrator.

> I am not quite sure how $GIT_CONFIG and $GIT_CONFIG_LOCAL were meant to 
> be used.

As I said, I _think_ it was the gitweb people, who wanted to have this to 
point at some gitweb specific, global (i.e. repo-independent) config.

>  - $GIT_CONFIG environment, if set, names a file to be read
>    first.  We read it first.
> 
>  - If $GIT_CONFIG is unset, then $GIT_CONFIG_SYSTEM (or
>    /etc/gitconfig) and $HOME/.gitconfig are read, as above.
> 
>  - Next file to be read is $GIT_CONFIG_LOCAL (if set) or
>    $GIT_DIR/config.  When reading this file, if we read from
>    $GIT_CONFIG earlier, we read only "core.*" section from it.

I am all for removing support for $GIT_CONFIG_LOCAL. IMHO it really does 
not make sense, for the reasons you told in your email: important per-repo 
information would be ignored, but not global or system-wide settings.

GIT_CONFIG should be used when reading a config which is totally unrelated 
to a repository. (Or for testing.)

But this "core.*" stuff is insane. Please no.

BTW just to clarify a subtlety in the code (which looks ugly at first, but 
nothing else makes sense):

When using --get-all, (with the patch I sent out earlier) git-config reads 
/etc/gitconfig first, then ~/.gitconfig, and then .git/config.

When not using --get-all, it reads .git/config first, and if nothing was 
found yet, reads ~/.gitconfig, and if still nothing was found, 
/etc/gitconfig.

It seems convoluted at first, but is the only way we can check for unique 
settings. For example, if the system-wide default for "color.diff" is 
"false", but in your repo it is "auto", it should _not_ warn of ambiguous 
settings, while it should warn if you have both settings in .git/config.

Outside of git-config, such unique variables are always handled in a 
last-wins manner, so we are safe.

I would feel much better, though, if supporters of /etc/gitconfig spoke a 
little bit louder, before actually doing something about it.

Ciao,
Dscho
Junio C Hamano· Feb 15, 2007, 11:25 UTC · re: Johannes Schindelin · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 6 quoted lines
> Okay for GIT_LOCAL_CONFIG. I do not remember off-hand who wanted it 
> (Jakub? Pasky?), but it was in the context of gitweb.
>
> However, GIT_CONFIG is meant to parse arbitrary config files.
> ...
> But this "core.*" stuff is insane. Please no.

Ok, Eric's example and yours made it clear that GIT_CONFIG is an interface meant to reuse (or abuse) git-config to read some file that is not at all related to git, and should never be used by other plumbing. As long as that is clear (could we have that in the documentation, by the way, please?), I have no problem with that.

In fact, I am very happy that we do not have to do that insane "core.*" stuff, which I thought was needed purely because somebody was trying to use GIT_CONFIG to prevent plumbing and porcelain from reading core configuration variables that are per repository in nature.

As I said in my other message, GIT_LOCAL_CONFIG is parallel to GIT_OBJECT_DIRECTORY and GIT_INDEX_FILE, and I am Ok with the way it is handled by the current code.

I mildly disagree with you on having an ability to disable /etc/gitconfig. This is necessary in the real world (in the same sense as "adduser" can be told not to copy skeltons by creating an empty home directory beforehand), even if we do not consider the fact that it would help gaining repeatable results from our test scripts (remember, using GIT_CONFIG to make plumbing and porcelain read from there would set a bad example, even when it is pointing at .git/config).

I've queued that insane "core.*" stuff in 'pu' and pushed out, but I'll drop that topic altogether. But before doing that, it's past my bedtime ;-).

Johannes Schindelin· Feb 15, 2007, 12:05 UTC · re: Junio C Hamano · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Hi,
On Thu, 15 Feb 2007, Junio C Hamano wrote:
Show 15 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > Okay for GIT_LOCAL_CONFIG. I do not remember off-hand who wanted it 
> > (Jakub? Pasky?), but it was in the context of gitweb.
> >
> > However, GIT_CONFIG is meant to parse arbitrary config files.
> > ...
> > But this "core.*" stuff is insane. Please no.
> 
> Ok, Eric's example and yours made it clear that GIT_CONFIG is an
> interface meant to reuse (or abuse) git-config to read some file
> that is not at all related to git, and should never be used by
> other plumbing.  As long as that is clear (could we have that in
> the documentation, by the way, please?), I have no problem with
> that.

I am no particularly good with documentation, but the good people who wanted this feature in the first place are.

> I mildly disagree with you on having an ability to disable
> /etc/gitconfig.

I'm okay either way. But I thought /etc/gitconfig was not so much like /etc/skel/, but more like /etc/profile.

> I've queued that insane "core.*" stuff in 'pu' and pushed out,
> but I'll drop that topic altogether.  But before doing that,
> it's past my bedtime ;-).
Hey, take it easy, now that Git "snog" is out!

Ciao, Dscho

Jakub Narebski· Feb 19, 2007, 01:47 UTC · re: Junio C Hamano · lore

Re: [PATCH] config: read system-wide defaults from /etc/gitconfig

Junio C Hamano wrote:
Show 34 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
>> Okay for GIT_LOCAL_CONFIG. I do not remember off-hand who wanted it 
>> (Jakub? Pasky?), but it was in the context of gitweb.
>>
>> However, GIT_CONFIG is meant to parse arbitrary config files.
>> ...
>> But this "core.*" stuff is insane. Please no.
> 
> Ok, Eric's example and yours made it clear that GIT_CONFIG is an
> interface meant to reuse (or abuse) git-config to read some file
> that is not at all related to git, and should never be used by
> other plumbing.  As long as that is clear (could we have that in
> the documentation, by the way, please?), I have no problem with
> that.
> 
> In fact, I am very happy that we do not have to do that insane
> "core.*" stuff, which I thought was needed purely because
> somebody was trying to use GIT_CONFIG to prevent plumbing and
> porcelain from reading core configuration variables that are per
> repository in nature.
> 
> As I said in my other message, GIT_LOCAL_CONFIG is parallel to
> GIT_OBJECT_DIRECTORY and GIT_INDEX_FILE, and I am Ok with the
> way it is handled by the current code.
> 
> I mildly disagree with you on having an ability to disable
> /etc/gitconfig.  This is necessary in the real world (in the
> same sense as "adduser" can be told not to copy skeltons by
> creating an empty home directory beforehand), even if we do not
> consider the fact that it would help gaining repeatable results
> from our test scripts (remember, using GIT_CONFIG to make
> plumbing and porcelain read from there would set a bad example,
> even when it is pointing at .git/config).

Hmmm... documentation of GIT_CONFIG and GIT_LOCAL_CONFIG is a bit behind, I guess:

git-repo-config(1):
ENVIRONMENT
       GIT_CONFIG
              Take  the  configuration from the given file instead of .git/config.
              Using the "--global" option forces this to ~/.gitconfig.
       GIT_CONFIG_LOCAL
              Currently the same as $GIT_CONFIG; when Git will support global con-
              figuration  files, this will cause it to take the configuration from
              the global configuration file in addition to the given file.

In my opionoin the logic should be: first read /etc/gitconfig, or GIT_SYSTEM_CONFIG, if it exists, then ~/.gitconfig, if it exists, then $GIT_DIR/config or GIT_LOCAL_CONFIG; if GIT_CONFIG is set, read only this.

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git
Uwe Kleine-König· Feb 14, 2007, 10:40 UTC · re: Andy Parkins · lore

Re: /etc/gitconfig

Andy Parkins wrote:
Show 11 quoted lines
> With the release of 1.5.0 I added legacyheaders=false and 
> usedelatbaseoffset=true to my repositories.
> 
> While doing that though, it felt wrong to be editing every single config.  Is 
> there a justification for having three config files?
> 
>  /etc/gitconfig
>  $HOME/.gitconfig
>  $GIT_DIR/config
> 
> With /etc/gitconfig coming in at lowest priority?

I didn't try it, but maybe you could place your desired config to /usr/share/git-core/templates/config and then at least newly created repos get it?

Best regards Uwe

-- 
Uwe Kleine-König

$ dc -e "5735816763073014741799356604682P"
Alex Riesen· Feb 16, 2007, 14:42 UTC · lore

[PATCH] Allow config files to be included

The syntax is:
    [include "filename"]
which is somewhat branch/remote-alike. There are a few differences, though:
filenames happen to be long, so a backslash is supported to split the
names into multiple lines. No bare LF allowed, so this is illegal:
    [include "path/
    name"]
On the other hand, this is allowed:
    [include "path/\
    name"]

Backslash is just to quote characters (as in shell). Only one quoted string allowed, so this is bad too:

    [include "path/" "name"]
Signed-off-by: Alex Riesen <raa.lkml@gmail.com>
---

This is a resend, I have no idea what happened to original, but it never appeared on vger. And this will probably horribly garbled by gmail. Will see.

Alex Riesen, Thu, Feb 15, 2007 12:35:57 +0100:
> I suggest to ignore /etc/gitconfig completely if ~/.gitconfig exists,
> but allow inclusion of /etc/gitconfig from ~/.gitconfig (there are
> very singular and preciuos exceptions).
 config.c |  103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 100 insertions(+), 3 deletions(-)
diff --git a/config.c b/config.c
index d821071..b42a7ea 100644
--- a/config.c
+++ b/config.c
@@ -12,6 +12,57 @@
 static FILE *config_file;
 static const char *config_file_name;
 static int config_linenr;
+
+struct fileinfo
+{
+       char *name;
+       int linenr;
+       FILE *file;
+       struct fileinfo *prev;
+};
+static struct fileinfo *config_stack = NULL;
+
+static void include_file(const char *filename)
+{
+       struct fileinfo *prev;
+       FILE *file;
+       file = fopen(filename, "r");
+       if (!file) {
+               error("ignored \"%s\": %s", filename, strerror(errno));
+               return;
+       }
+       prev = malloc(sizeof(*prev));
+       prev->name = (char *)config_file_name;
+       prev->linenr = config_linenr;
+       prev->file = config_file;
+       prev->prev = config_stack;
+       config_stack = prev;
+
+       config_file = file;
+       config_file_name = xstrdup(filename);
+       config_linenr = 0;
+}
+
+static FILE *pop_file(void)
+{
+       struct fileinfo *prev;
+
+       if (!config_stack)
+               /* The last file on stack does not belong to us.
+                * Free the names and close all included files. */
+               return NULL;
+
+       free((void*)config_file_name);
+       fclose(config_file);
+       config_file = config_stack->file;
+       config_file_name = config_stack->name;
+       config_linenr = 0;
+       prev = config_stack->prev;
+       free(config_stack);
+       config_stack = prev;
+       return config_file;
+}
+
 static int get_next_char(void)
 {
        int c;
@@ -31,13 +82,51 @@ static int get_next_char(void)
                if (c == '\n')
                        config_linenr++;
                if (c == EOF) {
-                       config_file = NULL;
+                       config_file = pop_file();
                        c = '\n';
                }
        }
        return c;
 }

+static int parse_include(void)
+{
+       char name[PATH_MAX];
+       int quote = 0, len = 0;
+
+       for (;;) {
+               int c = get_next_char();
+               if (len >= sizeof(name))
+                       return -1;
+               if (c == '"') {
+                       quote++;
+                       continue;
+               }
+               if (c == '\n')
+                       /* do not allow bare \n anywhere in path */
+                       return -1;
+               if (quote == 1) {
+                       if (c == '\\') {
+                               c = get_next_char();
+                               if (c == '\n')
+                                       continue;
+                       }
+                       name[len++] = c;
+               }
+               if (quote == 2 && c == ']') {
+                       do
+                               c = get_next_char();
+                       while (c != '\n');
+                       break;
+               }
+               if ((quote < 1 || quote >= 2) && !isspace(c) )
+                       return -1;
+       }
+       name[len] = '\0';
+       include_file(name);
+       return 0;
+}
+
 static char *parse_value(void)
 {
        static char value[1024];
@@ -181,8 +270,13 @@ static int get_base_var(char *name)
                int c = get_next_char();
                if (c == EOF)
                        return -1;
+               if (!isalpha(c) && !strncmp(name, "include", baselen) &&
+                   config_file) {
+                       ungetc(c, config_file);
+                       return parse_include();
+               }
                if (c == ']')
-                       return baselen;
+                       return baselen ? baselen: -1;
                if (isspace(c))
                        return get_extended_base_var(name, baselen, c);
                if (!iskeychar(c) && c != '.')
@@ -216,8 +310,11 @@ static int git_parse_file(config_fn_t fn)
                }
                if (c == '[') {
                        baselen = get_base_var(var);
-                       if (baselen <= 0)
+                       if (baselen < 0)
                                break;
+                       if (!baselen)
+                               /* [include "..."]*/
+                               continue;
                        var[baselen++] = '.';
                        var[baselen] = 0;
                        continue;
--
1.5.0.138.g36f81
Alex Riesen· Feb 16, 2007, 14:45 UTC · re: Alex Riesen · lore

Re: [PATCH] Allow config files to be included

The syntax is:
    [include "filename"]
which is somewhat branch/remote-alike. There are a few differences, though:
filenames happen to be long, so a backslash is supported to split the
names into multiple lines. No bare LF allowed, so this is illegal:
    [include "path/
    name"]
On the other hand, this is allowed:
    [include "path/\
    name"]

Backslash is just to quote characters (as in shell). Only one quoted string allowed, so this is bad too:

    [include "path/" "name"]
Signed-off-by: Alex Riesen <raa.lkml@gmail.com>
---
> This is a resend, I have no idea what happened to original,
> but it never appeared on vger. And this will probably horribly
> garbled by gmail. Will see.
It was horribly garbled by gmail. Resending as attachment.
Show 5 quoted lines
> Alex Riesen, Thu, Feb 15, 2007 12:35:57 +0100:
> > I suggest to ignore /etc/gitconfig completely if ~/.gitconfig exists,
> > but allow inclusion of /etc/gitconfig from ~/.gitconfig (there are
> > very singular and preciuos exceptions).
>
config.c |  103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 100 insertions(+), 3 deletions(-)
From 10c6dc89128fda0b4de89d69bd0ca2087f531a29 Mon Sep 17 00:00:00 2001
From: Alex Riesen <raa@steel.home>
Date: Thu, 15 Feb 2007 18:34:30 +0100
Subject: [PATCH] Allow config files to be included
The syntax is:
    [include "filename"]
which is somewhat branch/remote-alike. There are a few differences, though:
filenames happen to be long, so a backslash is supported to split the
names into multiple lines. No bare LF allowed, so this is illegal:
    [include "path/
    name"]
On the other hand, this is allowed:
    [include "path/\
    name"]

Backslash is just to quote characters (as in shell). Only one quoted string allowed, so this is bad too:

    [include "path/" "name"]
Signed-off-by: Alex Riesen <raa.lkml@gmail.com>
---
 config.c |  103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 100 insertions(+), 3 deletions(-)
diff --git a/config.c b/config.c
index d821071..b42a7ea 100644
--- a/config.c
+++ b/config.c
@@ -12,6 +12,57 @@
 static FILE *config_file;
 static const char *config_file_name;
 static int config_linenr;
+
+struct fileinfo
+{
+	char *name;
+	int linenr;
+	FILE *file;
+	struct fileinfo *prev;
+};
+static struct fileinfo *config_stack = NULL;
+
+static void include_file(const char *filename)
+{
+	struct fileinfo *prev;
+	FILE *file;
+	file = fopen(filename, "r");
+	if (!file) {
+		error("ignored \"%s\": %s", filename, strerror(errno));
+		return;
+	}
+	prev = malloc(sizeof(*prev));
+	prev->name = (char *)config_file_name;
+	prev->linenr = config_linenr;
+	prev->file = config_file;
+	prev->prev = config_stack;
+	config_stack = prev;
+
+	config_file = file;
+	config_file_name = xstrdup(filename);
+	config_linenr = 0;
+}
+
+static FILE *pop_file(void)
+{
+	struct fileinfo *prev;
+
+	if (!config_stack)
+		/* The last file on stack does not belong to us.
+		 * Free the names and close all included files. */
+		return NULL;
+
+	free((void*)config_file_name);
+	fclose(config_file);
+	config_file = config_stack->file;
+	config_file_name = config_stack->name;
+	config_linenr = 0;
+	prev = config_stack->prev;
+	free(config_stack);
+	config_stack = prev;
+	return config_file;
+}
+
 static int get_next_char(void)
 {
 	int c;
@@ -31,13 +82,51 @@ static int get_next_char(void)
 		if (c == '\n')
 			config_linenr++;
 		if (c == EOF) {
-			config_file = NULL;
+			config_file = pop_file();
 			c = '\n';
 		}
 	}
 	return c;
 }
 
+static int parse_include(void)
+{
+	char name[PATH_MAX];
+	int quote = 0, len = 0;
+
+	for (;;) {
+		int c = get_next_char();
+		if (len >= sizeof(name))
+			return -1;
+		if (c == '"') {
+			quote++;
+			continue;
+		}
+		if (c == '\n')
+			/* do not allow bare \n anywhere in path */
+			return -1;
+		if (quote == 1) {
+			if (c == '\\') {
+				c = get_next_char();
+				if (c == '\n')
+					continue;
+			}
+			name[len++] = c;
+		}
+		if (quote == 2 && c == ']') {
+			do
+				c = get_next_char();
+			while (c != '\n');
+			break;
+		}
+		if ((quote < 1 || quote >= 2) && !isspace(c) )
+			return -1;
+	}
+	name[len] = '\0';
+	include_file(name);
+	return 0;
+}
+
 static char *parse_value(void)
 {
 	static char value[1024];
@@ -181,8 +270,13 @@ static int get_base_var(char *name)
 		int c = get_next_char();
 		if (c == EOF)
 			return -1;
+		if (!isalpha(c) && !strncmp(name, "include", baselen) &&
+		    config_file) {
+			ungetc(c, config_file);
+			return parse_include();
+		}
 		if (c == ']')
-			return baselen;
+			return baselen ? baselen: -1;
 		if (isspace(c))
 			return get_extended_base_var(name, baselen, c);
 		if (!iskeychar(c) && c != '.')
@@ -216,8 +310,11 @@ static int git_parse_file(config_fn_t fn)
 		}
 		if (c == '[') {
 			baselen = get_base_var(var);
-			if (baselen <= 0)
+			if (baselen < 0)
 				break;
+			if (!baselen)
+				/* [include "..."]*/
+				continue;
 			var[baselen++] = '.';
 			var[baselen] = 0;
 			continue;
-- 
1.5.0.138.g36f81

← back to recent threads