From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: "Junio C Hamano" <gitster@pobox.com>, "Jeff King" <peff@peff.net>,
sschuberth@gmail.com, "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH v2 2/2] config: add conditional include
Date: Tue, 28 Jun 2016 19:26:41 +0200 [thread overview]
Message-ID: <20160628172641.26381-3-pclouds@gmail.com> (raw)
In-Reply-To: <20160628172641.26381-1-pclouds@gmail.com>
Main description is already in config.txt. Here is a dev-only note
about Windows support.
While prepare_include_condition_pattern() is Windows-friendly (because
it does not hard code '/'). The reality could be uglier because
internally get_git_dir() may return a path with '/' only or worse, a
mix of '/' and '\\'.
At some point, we need to teach wildmatch() that '/' and '\' should be
treated the same way (via a flag) as well. Then we could care less
about '/' vs '\\'. But a Windows dev probably has to do it.
Helped-by: Jeff King <peff@peff.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/config.txt | 40 +++++++++++++++++++++
config.c | 89 +++++++++++++++++++++++++++++++++++++++++++++--
t/t1305-config-include.sh | 45 ++++++++++++++++++++++++
3 files changed, 172 insertions(+), 2 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 58673cf..c8ad0bf 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -91,6 +91,46 @@ found at the location of the include directive. If the value of the
relative to the configuration file in which the include directive was
found. See below for examples.
+Included files can be grouped into subsections where subsectino name
+is the condition when the files are included. The condition starts
+with an condition type string, followed by a colon and a pattern.
+
+Only "gitdir" type is supported, where files are included if
+`$GIT_DIR` matches the specified pattern. For example,
+
+
+ [include "gitdir:/path/to/foo.git"]
+ path = /path/to/foo.inc
+
+would only include "/path/to/foo.inc" if `$GIT_DIR` is
+/path/to/foo.git.
+
+The following pattern is a wildcard pattern with two additional
+wildcards `**/` and `/**`. See linkgit:gitignore[5] for more
+information. For convenience:
+
+ * If the pattern ends with '/', '**' will be automatically added. For
+ example, the pattern 'foo/' becomes 'foo/**'. In other words, it
+ matches "foo" and everything inside, recursively.
+
+ * If the pattern starts with `~/`, `~` will be substitued with the
+ environment variable `HOME`.
+
+ * If the pattern starts with `./`, it is replaced with the directory
+ where the current config file is. For example if the config file
+ that contains the "include" subsection is `$HOME/.gitconfig` then
+ the pattern `./foo` would match the path `$HOME/foo`
+
+A few more notes:
+
+ * Symlinks in `$GIT_DIR` are not resolved before matching.
+
+ * Note that "../" is not special and will match literally, which is
+ unlikely what you want.
+
+ * On case-insensitive file systems, you may need to specify
+ core.ignoreCase before the `include` subsections in order to match
+ case-insensitively if core.ignoreCase is declared in the same file.
Example
~~~~~~~
diff --git a/config.c b/config.c
index f51c56b..97c450e 100644
--- a/config.c
+++ b/config.c
@@ -13,6 +13,7 @@
#include "hashmap.h"
#include "string-list.h"
#include "utf8.h"
+#include "dir.h"
struct config_source {
struct config_source *prev;
@@ -140,9 +141,89 @@ static int handle_path_include(const char *path, struct config_include_data *inc
return ret;
}
+static int prepare_include_condition_pattern(struct strbuf *pat)
+{
+ struct strbuf path = STRBUF_INIT;
+ int prefix = 0;
+
+ /* TODO: maybe support ~user/ too */
+ if (pat->buf[0] == '~' && is_dir_sep(pat->buf[1])) {
+ const char *home = getenv("HOME");
+
+ if (!home)
+ return error(_("$HOME is not defined"));
+
+ strbuf_add_absolute_path(&path, home);
+ strbuf_splice(pat, 0, 1, path.buf, path.len);
+ prefix = path.len + 1 /*slash*/;
+ } else if (pat->buf[0] == '.' && is_dir_sep(pat->buf[1])) {
+ const char *slash;
+
+ if (!cf || !cf->path)
+ return error(_("relative config include "
+ "conditionals must come from files"));
+
+ /* TODO: escape wildcards */
+ strbuf_add_absolute_path(&path, cf->path);
+ slash = find_last_dir_sep(path.buf);
+ if (!slash)
+ die("BUG: how is this possible?");
+ strbuf_splice(pat, 0, 1, path.buf, slash - path.buf);
+ prefix = slash - path.buf + 1 /* slash */;
+ } else if (!is_absolute_path(pat->buf))
+ strbuf_insert(pat, 0, "**/", 3);
+
+ if (pat->len && is_dir_sep(pat->buf[pat->len - 1]))
+ strbuf_addstr(pat, "**");
+
+ strbuf_release(&path);
+ return prefix;
+}
+
+static int include_condition_is_true(const char *cond, int cond_len)
+{
+ const char *value;
+ size_t value_len;
+
+ /* no condition (i.e., "include.path") is always true */
+ if (!cond)
+ return 1;
+
+ if (skip_prefix_mem(cond, cond_len, "gitdir:", &value, &value_len)) {
+ struct strbuf text = STRBUF_INIT;
+ struct strbuf pattern = STRBUF_INIT;
+ int ret, prefix;
+
+ strbuf_add_absolute_path(&text, get_git_dir());
+ strbuf_add(&pattern, value, value_len);
+ prefix = prepare_include_condition_pattern(&pattern);
+
+ if (prefix < 0)
+ return 0;
+
+ if (prefix > 0 &&
+ (text.len < prefix ||
+ fspathncmp(pattern.buf, text.buf, prefix)))
+ return 0;
+
+ ret = !wildmatch(pattern.buf + prefix, text.buf + prefix,
+ ignore_case ? WM_CASEFOLD : 0,
+ NULL);
+ strbuf_release(&pattern);
+ strbuf_release(&text);
+ return ret;
+ }
+
+ error(_("unrecognized include condition: %.*s"), cond_len, cond);
+ /* unknown conditionals are always false */
+ return 0;
+}
+
int git_config_include(const char *var, const char *value, void *data)
{
struct config_include_data *inc = data;
+ const char *cond, *key;
+ int cond_len;
int ret;
/*
@@ -153,8 +234,12 @@ int git_config_include(const char *var, const char *value, void *data)
if (ret < 0)
return ret;
- if (!strcmp(var, "include.path"))
- ret = handle_path_include(value, inc);
+ if (!parse_config_key(var, "include", &cond, &cond_len, &key) &&
+ include_condition_is_true(cond, cond_len)) {
+ if (!strcmp(key, "path"))
+ ret = handle_path_include(value, inc);
+ /* else we do not know about this type of include; ignore */
+ }
return ret;
}
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index 9ba2ba1..30351f2 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -152,6 +152,51 @@ test_expect_success 'relative includes from stdin line fail' '
test_must_fail git config --file - test.one
'
+test_expect_success 'conditional include, both unanchored' '
+ git init foo &&
+ (
+ cd foo &&
+ echo "[include \"gitdir:foo/\"]path=bar" >>.git/config &&
+ echo "[test]one=1" >.git/bar &&
+ echo 1 >expect &&
+ git config test.one >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'conditional include, $HOME expansion' '
+ (
+ cd foo &&
+ echo "[include \"gitdir:~/foo/\"]path=bar2" >>.git/config &&
+ echo "[test]two=2" >.git/bar2 &&
+ echo 2 >expect &&
+ git config test.two >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'conditional include, full pattern' '
+ (
+ cd foo &&
+ echo "[include \"gitdir:**/foo/**\"]path=bar3" >>.git/config &&
+ echo "[test]three=3" >.git/bar3 &&
+ echo 3 >expect &&
+ git config test.three >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'conditional include, relative path' '
+ echo "[include \"gitdir:./foo/.git\"]path=bar4" >>.gitconfig &&
+ echo "[test]four=4" >bar4 &&
+ (
+ cd foo &&
+ echo 4 >expect &&
+ git config test.four >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'include cycles are detected' '
cat >.gitconfig <<-\EOF &&
[test]value = gitconfig
--
2.8.2.531.gd073806
next prev parent reply other threads:[~2016-06-28 17:41 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-06-26 7:06 [PATCH] config: add conditional include Nguyễn Thái Ngọc Duy
2016-06-26 18:27 ` Jeff King
2016-06-27 16:14 ` Duy Nguyen
2016-06-27 16:20 ` Jeff King
2016-06-27 16:32 ` Duy Nguyen
2016-06-27 16:35 ` Jeff King
2016-06-28 17:26 ` [PATCH v2 0/2] Config " Nguyễn Thái Ngọc Duy
2016-06-28 17:26 ` [PATCH v2 1/2] add skip_prefix_mem helper Nguyễn Thái Ngọc Duy
2016-06-28 17:26 ` Nguyễn Thái Ngọc Duy [this message]
2016-06-28 20:49 ` [PATCH v2 2/2] config: add conditional include Jeff King
2016-06-29 4:06 ` Duy Nguyen
2016-06-28 23:11 ` Eric Sunshine
2016-07-12 16:42 ` [PATCH v3] " Nguyễn Thái Ngọc Duy
2016-07-13 7:21 ` Matthieu Moy
2016-07-13 7:26 ` Jeff King
2016-07-13 12:48 ` Matthieu Moy
2016-07-13 15:57 ` Duy Nguyen
2016-07-14 15:33 ` [PATCH v4] " Nguyễn Thái Ngọc Duy
2016-07-14 15:53 ` Johannes Schindelin
2016-07-14 16:13 ` Duy Nguyen
2016-07-16 13:30 ` Johannes Schindelin
2016-07-16 14:48 ` Duy Nguyen
2016-07-16 15:08 ` Jeff King
2016-07-16 16:36 ` Johannes Schindelin
2016-07-16 16:47 ` Jeff King
2016-07-17 8:15 ` Johannes Schindelin
2016-07-20 13:31 ` Jeff King
2016-07-20 22:07 ` Junio C Hamano
2016-07-20 16:39 ` Jakub Narębski
2016-08-13 8:40 ` Duy Nguyen
2016-08-19 13:54 ` Jeff King
2016-08-20 21:08 ` Jakub Narębski
2016-08-22 12:43 ` Duy Nguyen
2016-08-22 12:59 ` Matthieu Moy
2016-08-22 13:09 ` Duy Nguyen
2016-08-22 13:22 ` Matthieu Moy
2016-08-22 13:32 ` Duy Nguyen
2016-08-23 13:42 ` Johannes Schindelin
2016-08-24 9:37 ` Duy Nguyen
2016-08-24 12:44 ` Jakub Narębski
2016-08-24 14:17 ` Jeff King
2016-06-28 20:28 ` [PATCH v2 0/2] Config " Jeff King
2016-06-28 20:51 ` Matthieu Moy
2016-06-28 21:03 ` Jeff King
2016-06-29 4:09 ` Duy Nguyen
2016-06-28 22:11 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20160628172641.26381-3-pclouds@gmail.com \
--to=pclouds@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=peff@peff.net \
--cc=sschuberth@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).