Add global and system-wide gitattributes
authorPetr Onderka <gsvick@gmail.com>
Tue, 31 Aug 2010 22:42:43 +0000 (00:42 +0200)
committerJunio C Hamano <gitster@pobox.com>
Wed, 1 Sep 2010 19:19:36 +0000 (12:19 -0700)
Allow gitattributes to be set globally and system wide. This way, settings
for particular file types can be set in one place and apply for all user's
repositories.

The location of system-wide attributes file is $(prefix)/etc/gitattributes.
The location of the global file can be configured by setting
core.attributesfile.

Some parts of the code were copied from the implementation of the same
functionality in config.c.

Signed-off-by: Petr Onderka <gsvick@gmail.com>
Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config.txt
Documentation/gitattributes.txt
Makefile
attr.c
configure.ac
t/t0003-attributes.sh

index e75434b3ef2d81fa8dafd552d4b689eb84eea441..a043d55459424cc02d4ec63637c523bdfc011894 100644 (file)
@@ -450,6 +450,12 @@ core.excludesfile::
        to the value of `$HOME` and "{tilde}user/" to the specified user's
        home directory.  See linkgit:gitignore[5].
 
+core.attributesfile::
+       In addition to '.gitattributes' (per-directory) and
+       '.git/info/attributes', git looks into this file for attributes
+       (see linkgit:gitattributes[5]). Path expansions are made the same
+       way as for `core.excludesfile`.
+
 core.editor::
        Commands such as `commit` and `tag` that lets you edit
        messages by launching an editor uses the value of this
index 564586b943f439cb5ae04c4e76bc19d093c625da..c6bdeae66da33d21993022b4e2a8561b863c3f1e 100644 (file)
@@ -62,14 +62,21 @@ consults `$GIT_DIR/info/attributes` file (which has the highest
 precedence), `.gitattributes` file in the same directory as the
 path in question, and its parent directories up to the toplevel of the
 work tree (the further the directory that contains `.gitattributes`
-is from the path in question, the lower its precedence).
+is from the path in question, the lower its precedence). Finally
+global and system-wide files are considered (they have the lowest
+precedence).
 
 If you wish to affect only a single repository (i.e., to assign
-attributes to files that are particular to one user's workflow), then
+attributes to files that are particular to
+one user's workflow for that repository), then
 attributes should be placed in the `$GIT_DIR/info/attributes` file.
 Attributes which should be version-controlled and distributed to other
 repositories (i.e., attributes of interest to all users) should go into
-`.gitattributes` files.
+`.gitattributes` files. Attributes that should affect all repositories
+for a single user should be placed in a file specified by the
+`core.attributesfile` configuration option (see linkgit:git-config[1]).
+Attributes for all users on a system should be placed in the
+`$(prefix)/etc/gitattributes` file.
 
 Sometimes you would need to override an setting of an attribute
 for a path to `unspecified` state.  This can be done by listing
index bc3c57058faba66f6a7a947e1e9642f47053b5bb..eadd2d734bc701cdfeed8ab21e1a2981fb0cefa3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -268,6 +268,7 @@ STRIP ?= strip
 #   infodir
 #   htmldir
 #   ETC_GITCONFIG (but not sysconfdir)
+#   ETC_GITATTRIBUTES
 # can be specified as a relative path some/where/else;
 # this is interpreted as relative to $(prefix) and "git" at
 # runtime figures out where they are based on the path to the executable.
@@ -286,9 +287,11 @@ htmldir = share/doc/git-doc
 ifeq ($(prefix),/usr)
 sysconfdir = /etc
 ETC_GITCONFIG = $(sysconfdir)/gitconfig
+ETC_GITATTRIBUTES = $(sysconfdir)/gitattributes
 else
 sysconfdir = $(prefix)/etc
 ETC_GITCONFIG = etc/gitconfig
+ETC_GITATTRIBUTES = etc/gitattributes
 endif
 lib = lib
 # DESTDIR=
@@ -1502,6 +1505,7 @@ endif
 
 SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
 ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC_GITCONFIG))
+ETC_GITATTRIBUTES_SQ = $(subst ','\'',$(ETC_GITATTRIBUTES))
 
 DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
 bindir_SQ = $(subst ','\'',$(bindir))
@@ -1872,6 +1876,8 @@ builtin/init-db.s builtin/init-db.o: EXTRA_CPPFLAGS = \
 
 config.s config.o: EXTRA_CPPFLAGS = -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
 
+attr.s attr.o: EXTRA_CPPFLAGS = -DETC_GITATTRIBUTES='"$(ETC_GITATTRIBUTES_SQ)"'
+
 http.s http.o: EXTRA_CPPFLAGS = -DGIT_USER_AGENT='"git/$(GIT_VERSION)"'
 
 ifdef NO_EXPAT
diff --git a/attr.c b/attr.c
index 8ba606c933088e27ac08aabb546b764745f8187e..6aff6951d38d7f672aeb9851eca88c6e5a26b950 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -1,5 +1,6 @@
 #define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
+#include "exec_cmd.h"
 #include "attr.h"
 
 const char git_attr__true[] = "(builtin)true";
@@ -10,6 +11,8 @@ static const char git_attr__unknown[] = "(builtin)unknown";
 #define ATTR__UNSET NULL
 #define ATTR__UNKNOWN git_attr__unknown
 
+static const char *attributes_file;
+
 /*
  * The basic design decision here is that we are not going to have
  * insanely large number of attributes.
@@ -462,6 +465,32 @@ static void drop_attr_stack(void)
        }
 }
 
+const char *git_etc_gitattributes(void)
+{
+       static const char *system_wide;
+       if (!system_wide)
+               system_wide = system_path(ETC_GITATTRIBUTES);
+       return system_wide;
+}
+
+int git_attr_system(void)
+{
+       return !git_env_bool("GIT_ATTR_NOSYSTEM", 0);
+}
+
+int git_attr_global(void)
+{
+       return !git_env_bool("GIT_ATTR_NOGLOBAL", 0);
+}
+
+static int git_attr_config(const char *var, const char *value, void *dummy)
+{
+       if (!strcmp(var, "core.attributesfile"))
+               return git_config_pathname(&attributes_file, var, value);
+
+       return 0;
+}
+
 static void bootstrap_attr_stack(void)
 {
        if (!attr_stack) {
@@ -472,6 +501,25 @@ static void bootstrap_attr_stack(void)
                elem->prev = attr_stack;
                attr_stack = elem;
 
+               if (git_attr_system()) {
+                       elem = read_attr_from_file(git_etc_gitattributes(), 1);
+                       if (elem) {
+                               elem->origin = NULL;
+                               elem->prev = attr_stack;
+                               attr_stack = elem;
+                       }
+               }
+
+               git_config(git_attr_config, NULL);
+               if (git_attr_global() && attributes_file) {
+                       elem = read_attr_from_file(attributes_file, 1);
+                       if (elem) {
+                               elem->origin = NULL;
+                               elem->prev = attr_stack;
+                               attr_stack = elem;
+                       }
+               }
+
                if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
                        elem = read_attr(GITATTRIBUTES_FILE, 1);
                        elem->origin = strdup("");
@@ -499,7 +547,9 @@ static void prepare_attr_stack(const char *path, int dirlen)
 
        /*
         * At the bottom of the attribute stack is the built-in
-        * set of attribute definitions.  Then, contents from
+        * set of attribute definitions, followed by the contents
+        * of $(prefix)/etc/gitattributes and a file specified by
+        * core.attributesfile.  Then, contents from
         * .gitattribute files from directories closer to the
         * root to the ones in deeper directories are pushed
         * to the stack.  Finally, at the very top of the stack
index 5601e8bac953c670e35f32ffe48d157dd5694ce7..c5b3a4145fc7c15d4f3beba793864fb60560588f 100644 (file)
@@ -282,7 +282,15 @@ GIT_PARSE_WITH(iconv))
 GIT_PARSE_WITH_SET_MAKE_VAR(gitconfig, ETC_GITCONFIG,
                        Use VALUE instead of /etc/gitconfig as the
                        global git configuration file.
-                       If VALUE is not fully qualified it will be interpretted
+                       If VALUE is not fully qualified it will be interpreted
+                       as a path relative to the computed prefix at runtime.)
+
+#
+# Allow user to set ETC_GITATTRIBUTES variable
+GIT_PARSE_WITH_SET_MAKE_VAR(gitattributes, ETC_GITATTRIBUTES,
+                       Use VALUE instead of /etc/gitattributes as the
+                       global git attributes file.
+                       If VALUE is not fully qualified it will be interpreted
                        as a path relative to the computed prefix at runtime.)
 
 #
index 53bd7fcc4abbf6cb7db73b43d5dde477f5115f90..a7e24edf5b0ac3157ab2772f3cd94db176e810c1 100755 (executable)
@@ -36,6 +36,9 @@ test_expect_success 'setup' '
                echo "d/* test=a/b/d/*"
                echo "d/yes notest"
        ) >a/b/.gitattributes
+       (
+               echo "global test=global"
+       ) >$HOME/global-gitattributes
 
 '
 
@@ -57,6 +60,16 @@ test_expect_success 'attribute test' '
 
 '
 
+test_expect_success 'core.attributesfile' '
+       attr_check global unspecified &&
+       git config core.attributesfile "$HOME/global-gitattributes" &&
+       attr_check global global &&
+       git config core.attributesfile "~/global-gitattributes" &&
+       attr_check global global &&
+       echo "global test=precedence" >> .gitattributes &&
+       attr_check global precedence
+'
+
 test_expect_success 'attribute test: read paths from stdin' '
 
        cat <<EOF > expect