Merge branch 'js/empty-config-section-fix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 8 May 2018 06:59:18 +0000 (15:59 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 8 May 2018 06:59:18 +0000 (15:59 +0900)
"git config --unset a.b", when "a.b" is the last variable in an
otherwise empty section "a", left an empty section "a" behind, and
worse yet, a subsequent "git config a.c value" did not reuse that
empty shell and instead created a new one. These have been
(partially) corrected.

* js/empty-config-section-fix:
git_config_set: reuse empty sections
git config --unset: remove empty sections (in the common case)
git_config_set: make use of the config parser's event stream
git_config_set: do not use a state machine
config_set_store: rename some fields for consistency
config: avoid using the global variable `store`
config: introduce an optional event stream while parsing
t1300: `--unset-all` can leave an empty section behind (bug)
t1300: add a few more hairy examples of sections becoming empty
t1300: remove unreasonable expectation from TODO
t1300: avoid relying on a bug
config --replace-all: avoid extra line breaks
t1300: demonstrate that --replace-all can "invent" newlines
t1300: rename it to reflect that `repo-config` was deprecated
git_config_set: fix off-by-two

config.c
config.h
t/t1300-config.sh [new file with mode: 0755]
t/t1300-repo-config.sh [deleted file]
index 62c56099bf82c44aefc7f4dcdba207a66ad76cd7..5687e2d23e5ae1e3f2110875ee71318d1cf345a6 100644 (file)
--- a/config.c
+++ b/config.c
@@ -653,7 +653,45 @@ static int get_base_var(struct strbuf *name)
        }
 }
 
-static int git_parse_source(config_fn_t fn, void *data)
+struct parse_event_data {
+       enum config_event_t previous_type;
+       size_t previous_offset;
+       const struct config_options *opts;
+};
+
+static int do_event(enum config_event_t type, struct parse_event_data *data)
+{
+       size_t offset;
+
+       if (!data->opts || !data->opts->event_fn)
+               return 0;
+
+       if (type == CONFIG_EVENT_WHITESPACE &&
+           data->previous_type == type)
+               return 0;
+
+       offset = cf->do_ftell(cf);
+       /*
+        * At EOF, the parser always "inserts" an extra '\n', therefore
+        * the end offset of the event is the current file position, otherwise
+        * we will already have advanced to the next event.
+        */
+       if (type != CONFIG_EVENT_EOF)
+               offset--;
+
+       if (data->previous_type != CONFIG_EVENT_EOF &&
+           data->opts->event_fn(data->previous_type, data->previous_offset,
+                                offset, data->opts->event_fn_data) < 0)
+               return -1;
+
+       data->previous_type = type;
+       data->previous_offset = offset;
+
+       return 0;
+}
+
+static int git_parse_source(config_fn_t fn, void *data,
+                           const struct config_options *opts)
 {
        int comment = 0;
        int baselen = 0;
@@ -664,8 +702,15 @@ static int git_parse_source(config_fn_t fn, void *data)
        /* U+FEFF Byte Order Mark in UTF8 */
        const char *bomptr = utf8_bom;
 
+       /* For the parser event callback */
+       struct parse_event_data event_data = {
+               CONFIG_EVENT_EOF, 0, opts
+       };
+
        for (;;) {
-               int c = get_next_char();
+               int c;
+
+               c = get_next_char();
                if (bomptr && *bomptr) {
                        /* We are at the file beginning; skip UTF8-encoded BOM
                         * if present. Sane editors won't put this in on their
@@ -682,18 +727,33 @@ static int git_parse_source(config_fn_t fn, void *data)
                        }
                }
                if (c == '\n') {
-                       if (cf->eof)
+                       if (cf->eof) {
+                               if (do_event(CONFIG_EVENT_EOF, &event_data) < 0)
+                                       return -1;
                                return 0;
+                       }
+                       if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+                               return -1;
                        comment = 0;
                        continue;
                }
-               if (comment || isspace(c))
+               if (comment)
+                       continue;
+               if (isspace(c)) {
+                       if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+                                       return -1;
                        continue;
+               }
                if (c == '#' || c == ';') {
+                       if (do_event(CONFIG_EVENT_COMMENT, &event_data) < 0)
+                                       return -1;
                        comment = 1;
                        continue;
                }
                if (c == '[') {
+                       if (do_event(CONFIG_EVENT_SECTION, &event_data) < 0)
+                                       return -1;
+
                        /* Reset prior to determining a new stem */
                        strbuf_reset(var);
                        if (get_base_var(var) < 0 || var->len < 1)
@@ -704,6 +764,10 @@ static int git_parse_source(config_fn_t fn, void *data)
                }
                if (!isalpha(c))
                        break;
+
+               if (do_event(CONFIG_EVENT_ENTRY, &event_data) < 0)
+                       return -1;
+
                /*
                 * Truncate the var name back to the section header
                 * stem prior to grabbing the suffix part of the name
@@ -715,6 +779,9 @@ static int git_parse_source(config_fn_t fn, void *data)
                        break;
        }
 
+       if (do_event(CONFIG_EVENT_ERROR, &event_data) < 0)
+               return -1;
+
        switch (cf->origin_type) {
        case CONFIG_ORIGIN_BLOB:
                error_msg = xstrfmt(_("bad config line %d in blob %s"),
@@ -1398,7 +1465,8 @@ int git_default_config(const char *var, const char *value, void *dummy)
  * fgetc, ungetc, ftell of top need to be initialized before calling
  * this function.
  */
-static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
+static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
+                         const struct config_options *opts)
 {
        int ret;
 
@@ -1410,7 +1478,7 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
        strbuf_init(&top->var, 1024);
        cf = top;
 
-       ret = git_parse_source(fn, data);
+       ret = git_parse_source(fn, data, opts);
 
        /* pop config-file parsing state stack */
        strbuf_release(&top->value);
@@ -1423,7 +1491,7 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
 static int do_config_from_file(config_fn_t fn,
                const enum config_origin_type origin_type,
                const char *name, const char *path, FILE *f,
-               void *data)
+               void *data, const struct config_options *opts)
 {
        struct config_source top;
        int ret;
@@ -1438,29 +1506,38 @@ static int do_config_from_file(config_fn_t fn,
        top.do_ftell = config_file_ftell;
 
        flockfile(f);
-       ret = do_config_from(&top, fn, data);
+       ret = do_config_from(&top, fn, data, opts);
        funlockfile(f);
        return ret;
 }
 
 static int git_config_from_stdin(config_fn_t fn, void *data)
 {
-       return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin, data);
+       return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin,
+                                  data, NULL);
 }
 
-int git_config_from_file(config_fn_t fn, const char *filename, void *data)
+int git_config_from_file_with_options(config_fn_t fn, const char *filename,
+                                     void *data,
+                                     const struct config_options *opts)
 {
        int ret = -1;
        FILE *f;
 
        f = fopen_or_warn(filename, "r");
        if (f) {
-               ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, filename, f, data);
+               ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename,
+                                         filename, f, data, opts);
                fclose(f);
        }
        return ret;
 }
 
+int git_config_from_file(config_fn_t fn, const char *filename, void *data)
+{
+       return git_config_from_file_with_options(fn, filename, data, NULL);
+}
+
 int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_type,
                        const char *name, const char *buf, size_t len, void *data)
 {
@@ -1477,7 +1554,7 @@ int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_typ
        top.do_ungetc = config_buf_ungetc;
        top.do_ftell = config_buf_ftell;
 
-       return do_config_from(&top, fn, data);
+       return do_config_from(&top, fn, data, NULL);
 }
 
 int git_config_from_blob_oid(config_fn_t fn,
@@ -2221,96 +2298,98 @@ void git_die_config(const char *key, const char *err, ...)
  * Find all the stuff for git_config_set() below.
  */
 
-static struct {
+struct config_store_data {
        int baselen;
        char *key;
        int do_not_match;
        regex_t *value_regex;
        int multi_replace;
-       size_t *offset;
-       unsigned int offset_alloc;
-       enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
-       unsigned int seen;
-} store;
+       struct {
+               size_t begin, end;
+               enum config_event_t type;
+               int is_keys_section;
+       } *parsed;
+       unsigned int parsed_nr, parsed_alloc, *seen, seen_nr, seen_alloc;
+       unsigned int key_seen:1, section_seen:1, is_keys_section:1;
+};
 
-static int matches(const char *key, const char *value)
+static int matches(const char *key, const char *value,
+                  const struct config_store_data *store)
 {
-       if (strcmp(key, store.key))
+       if (strcmp(key, store->key))
                return 0; /* not ours */
-       if (!store.value_regex)
+       if (!store->value_regex)
                return 1; /* always matches */
-       if (store.value_regex == CONFIG_REGEX_NONE)
+       if (store->value_regex == CONFIG_REGEX_NONE)
                return 0; /* never matches */
 
-       return store.do_not_match ^
-               (value && !regexec(store.value_regex, value, 0, NULL, 0));
+       return store->do_not_match ^
+               (value && !regexec(store->value_regex, value, 0, NULL, 0));
+}
+
+static int store_aux_event(enum config_event_t type,
+                          size_t begin, size_t end, void *data)
+{
+       struct config_store_data *store = data;
+
+       ALLOC_GROW(store->parsed, store->parsed_nr + 1, store->parsed_alloc);
+       store->parsed[store->parsed_nr].begin = begin;
+       store->parsed[store->parsed_nr].end = end;
+       store->parsed[store->parsed_nr].type = type;
+
+       if (type == CONFIG_EVENT_SECTION) {
+               if (cf->var.len < 2 || cf->var.buf[cf->var.len - 1] != '.')
+                       BUG("Invalid section name '%s'", cf->var.buf);
+
+               /* Is this the section we were looking for? */
+               store->is_keys_section =
+                       store->parsed[store->parsed_nr].is_keys_section =
+                       cf->var.len - 1 == store->baselen &&
+                       !strncasecmp(cf->var.buf, store->key, store->baselen);
+               if (store->is_keys_section) {
+                       store->section_seen = 1;
+                       ALLOC_GROW(store->seen, store->seen_nr + 1,
+                                  store->seen_alloc);
+                       store->seen[store->seen_nr] = store->parsed_nr;
+               }
+       }
+
+       store->parsed_nr++;
+
+       return 0;
 }
 
 static int store_aux(const char *key, const char *value, void *cb)
 {
-       const char *ep;
-       size_t section_len;
+       struct config_store_data *store = cb;
 
-       switch (store.state) {
-       case KEY_SEEN:
-               if (matches(key, value)) {
-                       if (store.seen == 1 && store.multi_replace == 0) {
+       if (store->key_seen) {
+               if (matches(key, value, store)) {
+                       if (store->seen_nr == 1 && store->multi_replace == 0) {
                                warning(_("%s has multiple values"), key);
                        }
 
-                       ALLOC_GROW(store.offset, store.seen + 1,
-                                  store.offset_alloc);
+                       ALLOC_GROW(store->seen, store->seen_nr + 1,
+                                  store->seen_alloc);
 
-                       store.offset[store.seen] = cf->do_ftell(cf);
-                       store.seen++;
+                       store->seen[store->seen_nr] = store->parsed_nr;
+                       store->seen_nr++;
                }
-               break;
-       case SECTION_SEEN:
+       } else if (store->is_keys_section) {
                /*
-                * What we are looking for is in store.key (both
-                * section and var), and its section part is baselen
-                * long.  We found key (again, both section and var).
-                * We would want to know if this key is in the same
-                * section as what we are looking for.  We already
-                * know we are in the same section as what should
-                * hold store.key.
+                * Do not increment matches yet: this may not be a match, but we
+                * are in the desired section.
                 */
-               ep = strrchr(key, '.');
-               section_len = ep - key;
+               ALLOC_GROW(store->seen, store->seen_nr + 1, store->seen_alloc);
+               store->seen[store->seen_nr] = store->parsed_nr;
+               store->section_seen = 1;
 
-               if ((section_len != store.baselen) ||
-                   memcmp(key, store.key, section_len+1)) {
-                       store.state = SECTION_END_SEEN;
-                       break;
-               }
-
-               /*
-                * Do not increment matches: this is no match, but we
-                * just made sure we are in the desired section.
-                */
-               ALLOC_GROW(store.offset, store.seen + 1,
-                          store.offset_alloc);
-               store.offset[store.seen] = cf->do_ftell(cf);
-               /* fallthru */
-       case SECTION_END_SEEN:
-       case START:
-               if (matches(key, value)) {
-                       ALLOC_GROW(store.offset, store.seen + 1,
-                                  store.offset_alloc);
-                       store.offset[store.seen] = cf->do_ftell(cf);
-                       store.state = KEY_SEEN;
-                       store.seen++;
-               } else {
-                       if (strrchr(key, '.') - key == store.baselen &&
-                             !strncmp(key, store.key, store.baselen)) {
-                                       store.state = SECTION_SEEN;
-                                       ALLOC_GROW(store.offset,
-                                                  store.seen + 1,
-                                                  store.offset_alloc);
-                                       store.offset[store.seen] = cf->do_ftell(cf);
-                       }
+               if (matches(key, value, store)) {
+                       store->seen_nr++;
+                       store->key_seen = 1;
                }
        }
+
        return 0;
 }
 
@@ -2322,31 +2401,33 @@ static int write_error(const char *filename)
        return 4;
 }
 
-static struct strbuf store_create_section(const char *key)
+static struct strbuf store_create_section(const char *key,
+                                         const struct config_store_data *store)
 {
        const char *dot;
        int i;
        struct strbuf sb = STRBUF_INIT;
 
-       dot = memchr(key, '.', store.baselen);
+       dot = memchr(key, '.', store->baselen);
        if (dot) {
                strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key);
-               for (i = dot - key + 1; i < store.baselen; i++) {
+               for (i = dot - key + 1; i < store->baselen; i++) {
                        if (key[i] == '"' || key[i] == '\\')
                                strbuf_addch(&sb, '\\');
                        strbuf_addch(&sb, key[i]);
                }
                strbuf_addstr(&sb, "\"]\n");
        } else {
-               strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
+               strbuf_addf(&sb, "[%.*s]\n", store->baselen, key);
        }
 
        return sb;
 }
 
-static ssize_t write_section(int fd, const char *key)
+static ssize_t write_section(int fd, const char *key,
+                            const struct config_store_data *store)
 {
-       struct strbuf sb = store_create_section(key);
+       struct strbuf sb = store_create_section(key, store);
        ssize_t ret;
 
        ret = write_in_full(fd, sb.buf, sb.len);
@@ -2355,11 +2436,12 @@ static ssize_t write_section(int fd, const char *key)
        return ret;
 }
 
-static ssize_t write_pair(int fd, const char *key, const char *value)
+static ssize_t write_pair(int fd, const char *key, const char *value,
+                         const struct config_store_data *store)
 {
        int i;
        ssize_t ret;
-       int length = strlen(key + store.baselen + 1);
+       int length = strlen(key + store->baselen + 1);
        const char *quote = "";
        struct strbuf sb = STRBUF_INIT;
 
@@ -2379,7 +2461,7 @@ static ssize_t write_pair(int fd, const char *key, const char *value)
                quote = "\"";
 
        strbuf_addf(&sb, "\t%.*s = %s",
-                   length, key + store.baselen + 1, quote);
+                   length, key + store->baselen + 1, quote);
 
        for (i = 0; value[i]; i++)
                switch (value[i]) {
@@ -2405,30 +2487,85 @@ static ssize_t write_pair(int fd, const char *key, const char *value)
        return ret;
 }
 
-static ssize_t find_beginning_of_line(const char *contents, size_t size,
-       size_t offset_, int *found_bracket)
+/*
+ * If we are about to unset the last key(s) in a section, and if there are
+ * no comments surrounding (or included in) the section, we will want to
+ * extend begin/end to remove the entire section.
+ *
+ * Note: the parameter `seen_ptr` points to the index into the store.seen
+ * array.  * This index may be incremented if a section has more than one
+ * entry (which all are to be removed).
+ */
+static void maybe_remove_section(struct config_store_data *store,
+                                const char *contents,
+                                size_t *begin_offset, size_t *end_offset,
+                                int *seen_ptr)
 {
-       size_t equal_offset = size, bracket_offset = size;
-       ssize_t offset;
+       size_t begin;
+       int i, seen, section_seen = 0;
 
-contline:
-       for (offset = offset_-2; offset > 0
-                       && contents[offset] != '\n'; offset--)
-               switch (contents[offset]) {
-                       case '=': equal_offset = offset; break;
-                       case ']': bracket_offset = offset; break;
+       /*
+        * First, ensure that this is the first key, and that there are no
+        * comments before the entry nor before the section header.
+        */
+       seen = *seen_ptr;
+       for (i = store->seen[seen]; i > 0; i--) {
+               enum config_event_t type = store->parsed[i - 1].type;
+
+               if (type == CONFIG_EVENT_COMMENT)
+                       /* There is a comment before this entry or section */
+                       return;
+               if (type == CONFIG_EVENT_ENTRY) {
+                       if (!section_seen)
+                               /* This is not the section's first entry. */
+                               return;
+                       /* We encountered no comment before the section. */
+                       break;
+               }
+               if (type == CONFIG_EVENT_SECTION) {
+                       if (!store->parsed[i - 1].is_keys_section)
+                               break;
+                       section_seen = 1;
                }
-       if (offset > 0 && contents[offset-1] == '\\') {
-               offset_ = offset;
-               goto contline;
        }
-       if (bracket_offset < equal_offset) {
-               *found_bracket = 1;
-               offset = bracket_offset+1;
-       } else
-               offset++;
+       begin = store->parsed[i].begin;
+
+       /*
+        * Next, make sure that we are removing he last key(s) in the section,
+        * and that there are no comments that are possibly about the current
+        * section.
+        */
+       for (i = store->seen[seen] + 1; i < store->parsed_nr; i++) {
+               enum config_event_t type = store->parsed[i].type;
 
-       return offset;
+               if (type == CONFIG_EVENT_COMMENT)
+                       return;
+               if (type == CONFIG_EVENT_SECTION) {
+                       if (store->parsed[i].is_keys_section)
+                               continue;
+                       break;
+               }
+               if (type == CONFIG_EVENT_ENTRY) {
+                       if (++seen < store->seen_nr &&
+                           i == store->seen[seen])
+                               /* We want to remove this entry, too */
+                               continue;
+                       /* There is another entry in this section. */
+                       return;
+               }
+       }
+
+       /*
+        * We are really removing the last entry/entries from this section, and
+        * there are no enclosed or surrounding comments. Remove the entire,
+        * now-empty section.
+        */
+       *seen_ptr = seen;
+       *begin_offset = begin;
+       if (i < store->parsed_nr)
+               *end_offset = store->parsed[i].begin;
+       else
+               *end_offset = store->parsed[store->parsed_nr - 1].end;
 }
 
 int git_config_set_in_file_gently(const char *config_filename,
@@ -2489,6 +2626,9 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
        char *filename_buf = NULL;
        char *contents = NULL;
        size_t contents_sz;
+       struct config_store_data store;
+
+       memset(&store, 0, sizeof(store));
 
        /* parse-key returns negative; flip the sign to feed exit(3) */
        ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
@@ -2531,13 +2671,14 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                }
 
                store.key = (char *)key;
-               if (write_section(fd, key) < 0 ||
-                   write_pair(fd, key, value) < 0)
+               if (write_section(fd, key, &store) < 0 ||
+                   write_pair(fd, key, value, &store) < 0)
                        goto write_err_out;
        } else {
                struct stat st;
                size_t copy_begin, copy_end;
                int i, new_line = 0;
+               struct config_options opts;
 
                if (value_regex == NULL)
                        store.value_regex = NULL;
@@ -2560,18 +2701,24 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                        }
                }
 
-               ALLOC_GROW(store.offset, 1, store.offset_alloc);
-               store.offset[0] = 0;
-               store.state = START;
-               store.seen = 0;
+               ALLOC_GROW(store.parsed, 1, store.parsed_alloc);
+               store.parsed[0].end = 0;
+
+               memset(&opts, 0, sizeof(opts));
+               opts.event_fn = store_aux_event;
+               opts.event_fn_data = &store;
 
                /*
-                * After this, store.offset will contain the *end* offset
-                * of the last match, or remain at 0 if no match was found.
+                * After this, store.parsed will contain offsets of all the
+                * parsed elements, and store.seen will contain a list of
+                * matches, as indices into store.parsed.
+                *
                 * As a side effect, we make sure to transform only a valid
                 * existing config file.
                 */
-               if (git_config_from_file(store_aux, config_filename, NULL)) {
+               if (git_config_from_file_with_options(store_aux,
+                                                     config_filename,
+                                                     &store, &opts)) {
                        error("invalid config file %s", config_filename);
                        free(store.key);
                        if (store.value_regex != NULL &&
@@ -2591,8 +2738,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                }
 
                /* if nothing to unset, or too many matches, error out */
-               if ((store.seen == 0 && value == NULL) ||
-                               (store.seen > 1 && multi_replace == 0)) {
+               if ((store.seen_nr == 0 && value == NULL) ||
+                   (store.seen_nr > 1 && multi_replace == 0)) {
                        ret = CONFIG_NOTHING_SET;
                        goto out_free;
                }
@@ -2623,18 +2770,49 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                        goto out_free;
                }
 
-               if (store.seen == 0)
-                       store.seen = 1;
+               if (store.seen_nr == 0) {
+                       if (!store.seen_alloc) {
+                               /* Did not see key nor section */
+                               ALLOC_GROW(store.seen, 1, store.seen_alloc);
+                               store.seen[0] = store.parsed_nr
+                                       - !!store.parsed_nr;
+                       }
+                       store.seen_nr = 1;
+               }
 
-               for (i = 0, copy_begin = 0; i < store.seen; i++) {
-                       if (store.offset[i] == 0) {
-                               store.offset[i] = copy_end = contents_sz;
-                       } else if (store.state != KEY_SEEN) {
-                               copy_end = store.offset[i];
-                       } else
-                               copy_end = find_beginning_of_line(
-                                       contents, contents_sz,
-                                       store.offset[i]-2, &new_line);
+               for (i = 0, copy_begin = 0; i < store.seen_nr; i++) {
+                       size_t replace_end;
+                       int j = store.seen[i];
+
+                       new_line = 0;
+                       if (!store.key_seen) {
+                               copy_end = store.parsed[j].end;
+                               /* include '\n' when copying section header */
+                               if (copy_end > 0 && copy_end < contents_sz &&
+                                   contents[copy_end - 1] != '\n' &&
+                                   contents[copy_end] == '\n')
+                                       copy_end++;
+                               replace_end = copy_end;
+                       } else {
+                               replace_end = store.parsed[j].end;
+                               copy_end = store.parsed[j].begin;
+                               if (!value)
+                                       maybe_remove_section(&store, contents,
+                                                            &copy_end,
+                                                            &replace_end, &i);
+                               /*
+                                * Swallow preceding white-space on the same
+                                * line.
+                                */
+                               while (copy_end > 0 ) {
+                                       char c = contents[copy_end - 1];
+
+                                       if (isspace(c) && c != '\n')
+                                               copy_end--;
+                                       else
+                                               break;
+                               }
+                       }
 
                        if (copy_end > 0 && contents[copy_end-1] != '\n')
                                new_line = 1;
@@ -2648,16 +2826,16 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                                    write_str_in_full(fd, "\n") < 0)
                                        goto write_err_out;
                        }
-                       copy_begin = store.offset[i];
+                       copy_begin = replace_end;
                }
 
                /* write the pair (value == NULL means unset) */
                if (value != NULL) {
-                       if (store.state == START) {
-                               if (write_section(fd, key) < 0)
+                       if (!store.section_seen) {
+                               if (write_section(fd, key, &store) < 0)
                                        goto write_err_out;
                        }
-                       if (write_pair(fd, key, value) < 0)
+                       if (write_pair(fd, key, value, &store) < 0)
                                goto write_err_out;
                }
 
@@ -2781,7 +2959,8 @@ static int section_name_is_ok(const char *name)
 
 /* if new_name == NULL, the section is removed instead */
 static int git_config_copy_or_rename_section_in_file(const char *config_filename,
-                                     const char *old_name, const char *new_name, int copy)
+                                     const char *old_name,
+                                     const char *new_name, int copy)
 {
        int ret = 0, remove = 0;
        char *filename_buf = NULL;
@@ -2791,6 +2970,9 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
        FILE *config_file = NULL;
        struct stat st;
        struct strbuf copystr = STRBUF_INIT;
+       struct config_store_data store;
+
+       memset(&store, 0, sizeof(store));
 
        if (new_name && !section_name_is_ok(new_name)) {
                ret = error("invalid section name: %s", new_name);
@@ -2860,7 +3042,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
                                }
                                store.baselen = strlen(new_name);
                                if (!copy) {
-                                       if (write_section(out_fd, new_name) < 0) {
+                                       if (write_section(out_fd, new_name, &store) < 0) {
                                                ret = write_error(get_lock_file_path(&lock));
                                                goto out;
                                        }
@@ -2881,7 +3063,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
                                                output[0] = '\t';
                                        }
                                } else {
-                                       copystr = store_create_section(new_name);
+                                       copystr = store_create_section(new_name, &store);
                                }
                        }
                        remove = 0;
index ef70a9cac1e6dc67df24d157a4e5de38edd8c984..5a2394daae21d68ad0e44f6ec47ccac9afb6284a 100644 (file)
--- a/config.h
+++ b/config.h
@@ -28,15 +28,40 @@ enum config_origin_type {
        CONFIG_ORIGIN_CMDLINE
 };
 
+enum config_event_t {
+       CONFIG_EVENT_SECTION,
+       CONFIG_EVENT_ENTRY,
+       CONFIG_EVENT_WHITESPACE,
+       CONFIG_EVENT_COMMENT,
+       CONFIG_EVENT_EOF,
+       CONFIG_EVENT_ERROR
+};
+
+/*
+ * The parser event function (if not NULL) is called with the event type and
+ * the begin/end offsets of the parsed elements.
+ *
+ * Note: for CONFIG_EVENT_ENTRY (i.e. config variables), the trailing newline
+ * character is considered part of the element.
+ */
+typedef int (*config_parser_event_fn_t)(enum config_event_t type,
+                                       size_t begin_offset, size_t end_offset,
+                                       void *event_fn_data);
+
 struct config_options {
        unsigned int respect_includes : 1;
        const char *commondir;
        const char *git_dir;
+       config_parser_event_fn_t event_fn;
+       void *event_fn_data;
 };
 
 typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
+extern int git_config_from_file_with_options(config_fn_t fn, const char *,
+                                            void *,
+                                            const struct config_options *);
 extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
                                        const char *name, const char *buf, size_t len, void *data);
 extern int git_config_from_blob_oid(config_fn_t fn, const char *name,
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
new file mode 100755 (executable)
index 0000000..5acf12f
--- /dev/null
@@ -0,0 +1,1710 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Johannes Schindelin
+#
+
+test_description='Test git config in different settings'
+
+. ./test-lib.sh
+
+test_expect_success 'clear default config' '
+       rm -f .git/config
+'
+
+cat > expect << EOF
+[core]
+       penguin = little blue
+EOF
+test_expect_success 'initial' '
+       git config core.penguin "little blue" &&
+       test_cmp expect .git/config
+'
+
+cat > expect << EOF
+[core]
+       penguin = little blue
+       Movie = BadPhysics
+EOF
+test_expect_success 'mixed case' '
+       git config Core.Movie BadPhysics &&
+       test_cmp expect .git/config
+'
+
+cat > expect << EOF
+[core]
+       penguin = little blue
+       Movie = BadPhysics
+[Cores]
+       WhatEver = Second
+EOF
+test_expect_success 'similar section' '
+       git config Cores.WhatEver Second &&
+       test_cmp expect .git/config
+'
+
+cat > expect << EOF
+[core]
+       penguin = little blue
+       Movie = BadPhysics
+       UPPERCASE = true
+[Cores]
+       WhatEver = Second
+EOF
+test_expect_success 'uppercase section' '
+       git config CORE.UPPERCASE true &&
+       test_cmp expect .git/config
+'
+
+test_expect_success 'replace with non-match' '
+       git config core.penguin kingpin !blue
+'
+
+test_expect_success 'replace with non-match (actually matching)' '
+       git config core.penguin "very blue" !kingpin
+'
+
+cat > expect << EOF
+[core]
+       penguin = very blue
+       Movie = BadPhysics
+       UPPERCASE = true
+       penguin = kingpin
+[Cores]
+       WhatEver = Second
+EOF
+
+test_expect_success 'non-match result' 'test_cmp expect .git/config'
+
+test_expect_success 'find mixed-case key by canonical name' '
+       echo Second >expect &&
+       git config cores.whatever >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'find mixed-case key by non-canonical name' '
+       echo Second >expect &&
+       git config CoReS.WhAtEvEr >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'subsections are not canonicalized by git-config' '
+       cat >>.git/config <<-\EOF &&
+       [section.SubSection]
+       key = one
+       [section "SubSection"]
+       key = two
+       EOF
+       echo one >expect &&
+       git config section.subsection.key >actual &&
+       test_cmp expect actual &&
+       echo two >expect &&
+       git config section.SubSection.key >actual &&
+       test_cmp expect actual
+'
+
+cat > .git/config <<\EOF
+[alpha]
+bar = foo
+[beta]
+baz = multiple \
+lines
+foo = bar
+EOF
+
+test_expect_success 'unset with cont. lines' '
+       git config --unset beta.baz
+'
+
+cat > expect <<\EOF
+[alpha]
+bar = foo
+[beta]
+foo = bar
+EOF
+
+test_expect_success 'unset with cont. lines is correct' 'test_cmp expect .git/config'
+
+cat > .git/config << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+               ; comment
+               haha   ="beta" # last silly comment
+haha = hello
+       haha = bello
+[nextSection] noNewline = ouch
+EOF
+
+cp .git/config .git/config2
+
+test_expect_success 'multiple unset' '
+       git config --unset-all beta.haha
+'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+               ; comment
+[nextSection] noNewline = ouch
+EOF
+
+test_expect_success 'multiple unset is correct' '
+       test_cmp expect .git/config
+'
+
+cp .git/config2 .git/config
+
+test_expect_success '--replace-all missing value' '
+       test_must_fail git config --replace-all beta.haha &&
+       test_cmp .git/config2 .git/config
+'
+
+rm .git/config2
+
+test_expect_success '--replace-all' '
+       git config --replace-all beta.haha gamma
+'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+               ; comment
+       haha = gamma
+[nextSection] noNewline = ouch
+EOF
+
+test_expect_success 'all replaced' '
+       test_cmp expect .git/config
+'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+               ; comment
+       haha = alpha
+[nextSection] noNewline = ouch
+EOF
+test_expect_success 'really mean test' '
+       git config beta.haha alpha &&
+       test_cmp expect .git/config
+'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+               ; comment
+       haha = alpha
+[nextSection]
+       nonewline = wow
+EOF
+test_expect_success 'really really mean test' '
+       git config nextsection.nonewline wow &&
+       test_cmp expect .git/config
+'
+
+test_expect_success 'get value' '
+       echo alpha >expect &&
+       git config beta.haha >actual &&
+       test_cmp expect actual
+'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+               ; comment
+[nextSection]
+       nonewline = wow
+EOF
+test_expect_success 'unset' '
+       git config --unset beta.haha &&
+       test_cmp expect .git/config
+'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+               ; comment
+[nextSection]
+       nonewline = wow
+       NoNewLine = wow2 for me
+EOF
+test_expect_success 'multivar' '
+       git config nextsection.NoNewLine "wow2 for me" "for me$" &&
+       test_cmp expect .git/config
+'
+
+test_expect_success 'non-match' '
+       git config --get nextsection.nonewline !for
+'
+
+test_expect_success 'non-match value' '
+       echo wow >expect &&
+       git config --get nextsection.nonewline !for >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'multi-valued get returns final one' '
+       echo "wow2 for me" >expect &&
+       git config --get nextsection.nonewline >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'multi-valued get-all returns all' '
+       cat >expect <<-\EOF &&
+       wow
+       wow2 for me
+       EOF
+       git config --get-all nextsection.nonewline >actual &&
+       test_cmp expect actual
+'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+               ; comment
+[nextSection]
+       nonewline = wow3
+       NoNewLine = wow2 for me
+EOF
+test_expect_success 'multivar replace' '
+       git config nextsection.nonewline "wow3" "wow$" &&
+       test_cmp expect .git/config
+'
+
+test_expect_success 'ambiguous unset' '
+       test_must_fail git config --unset nextsection.nonewline
+'
+
+test_expect_success 'invalid unset' '
+       test_must_fail git config --unset somesection.nonewline
+'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+               ; comment
+[nextSection]
+       NoNewLine = wow2 for me
+EOF
+
+test_expect_success 'multivar unset' '
+       git config --unset nextsection.nonewline "wow3$" &&
+       test_cmp expect .git/config
+'
+
+test_expect_success 'invalid key' 'test_must_fail git config inval.2key blabla'
+
+test_expect_success 'correct key' 'git config 123456.a123 987'
+
+test_expect_success 'hierarchical section' '
+       git config Version.1.2.3eX.Alpha beta
+'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+               ; comment
+[nextSection]
+       NoNewLine = wow2 for me
+[123456]
+       a123 = 987
+[Version "1.2.3eX"]
+       Alpha = beta
+EOF
+
+test_expect_success 'hierarchical section value' '
+       test_cmp expect .git/config
+'
+
+cat > expect << EOF
+beta.noindent=sillyValue
+nextsection.nonewline=wow2 for me
+123456.a123=987
+version.1.2.3eX.alpha=beta
+EOF
+
+test_expect_success 'working --list' '
+       git config --list > output &&
+       test_cmp expect output
+'
+cat > expect << EOF
+EOF
+
+test_expect_success '--list without repo produces empty output' '
+       git --git-dir=nonexistent config --list >output &&
+       test_cmp expect output
+'
+
+cat > expect << EOF
+beta.noindent
+nextsection.nonewline
+123456.a123
+version.1.2.3eX.alpha
+EOF
+
+test_expect_success '--name-only --list' '
+       git config --name-only --list >output &&
+       test_cmp expect output
+'
+
+cat > expect << EOF
+beta.noindent sillyValue
+nextsection.nonewline wow2 for me
+EOF
+
+test_expect_success '--get-regexp' '
+       git config --get-regexp in >output &&
+       test_cmp expect output
+'
+
+cat > expect << EOF
+beta.noindent
+nextsection.nonewline
+EOF
+
+test_expect_success '--name-only --get-regexp' '
+       git config --name-only --get-regexp in >output &&
+       test_cmp expect output
+'
+
+cat > expect << EOF
+wow2 for me
+wow4 for you
+EOF
+
+test_expect_success '--add' '
+       git config --add nextsection.nonewline "wow4 for you" &&
+       git config --get-all nextsection.nonewline > output &&
+       test_cmp expect output
+'
+
+cat > .git/config << EOF
+[novalue]
+       variable
+[emptyvalue]
+       variable =
+EOF
+
+test_expect_success 'get variable with no value' '
+       git config --get novalue.variable ^$
+'
+
+test_expect_success 'get variable with empty value' '
+       git config --get emptyvalue.variable ^$
+'
+
+echo novalue.variable > expect
+
+test_expect_success 'get-regexp variable with no value' '
+       git config --get-regexp novalue > output &&
+       test_cmp expect output
+'
+
+echo 'novalue.variable true' > expect
+
+test_expect_success 'get-regexp --bool variable with no value' '
+       git config --bool --get-regexp novalue > output &&
+       test_cmp expect output
+'
+
+echo 'emptyvalue.variable ' > expect
+
+test_expect_success 'get-regexp variable with empty value' '
+       git config --get-regexp emptyvalue > output &&
+       test_cmp expect output
+'
+
+echo true > expect
+
+test_expect_success 'get bool variable with no value' '
+       git config --bool novalue.variable > output &&
+       test_cmp expect output
+'
+
+echo false > expect
+
+test_expect_success 'get bool variable with empty value' '
+       git config --bool emptyvalue.variable > output &&
+       test_cmp expect output
+'
+
+test_expect_success 'no arguments, but no crash' '
+       test_must_fail git config >output 2>&1 &&
+       test_i18ngrep usage output
+'
+
+cat > .git/config << EOF
+[a.b]
+       c = d
+EOF
+
+cat > expect << EOF
+[a.b]
+       c = d
+[a]
+       x = y
+EOF
+
+test_expect_success 'new section is partial match of another' '
+       git config a.x y &&
+       test_cmp expect .git/config
+'
+
+cat > expect << EOF
+[a.b]
+       c = d
+[a]
+       x = y
+       b = c
+[b]
+       x = y
+EOF
+
+test_expect_success 'new variable inserts into proper section' '
+       git config b.x y &&
+       git config a.b c &&
+       test_cmp expect .git/config
+'
+
+test_expect_success 'alternative --file (non-existing file should fail)' '
+       test_must_fail git config --file non-existing-config -l
+'
+
+cat > other-config << EOF
+[ein]
+       bahn = strasse
+EOF
+
+cat > expect << EOF
+ein.bahn=strasse
+EOF
+
+test_expect_success 'alternative GIT_CONFIG' '
+       GIT_CONFIG=other-config git config --list >output &&
+       test_cmp expect output
+'
+
+test_expect_success 'alternative GIT_CONFIG (--file)' '
+       git config --file other-config --list >output &&
+       test_cmp expect output
+'
+
+test_expect_success 'alternative GIT_CONFIG (--file=-)' '
+       git config --file - --list <other-config >output &&
+       test_cmp expect output
+'
+
+test_expect_success 'setting a value in stdin is an error' '
+       test_must_fail git config --file - some.value foo
+'
+
+test_expect_success 'editing stdin is an error' '
+       test_must_fail git config --file - --edit
+'
+
+test_expect_success 'refer config from subdirectory' '
+       mkdir x &&
+       (
+               cd x &&
+               echo strasse >expect &&
+               git config --get --file ../other-config ein.bahn >actual &&
+               test_cmp expect actual
+       )
+
+'
+
+test_expect_success 'refer config from subdirectory via --file' '
+       (
+               cd x &&
+               git config --file=../other-config --get ein.bahn >actual &&
+               test_cmp expect actual
+       )
+'
+
+cat > expect << EOF
+[ein]
+       bahn = strasse
+[anwohner]
+       park = ausweis
+EOF
+
+test_expect_success '--set in alternative file' '
+       git config --file=other-config anwohner.park ausweis &&
+       test_cmp expect other-config
+'
+
+cat > .git/config << EOF
+# Hallo
+       #Bello
+[branch "eins"]
+       x = 1
+[branch.eins]
+       y = 1
+       [branch "1 234 blabl/a"]
+weird
+EOF
+
+test_expect_success 'rename section' '
+       git config --rename-section branch.eins branch.zwei
+'
+
+cat > expect << EOF
+# Hallo
+       #Bello
+[branch "zwei"]
+       x = 1
+[branch "zwei"]
+       y = 1
+       [branch "1 234 blabl/a"]
+weird
+EOF
+
+test_expect_success 'rename succeeded' '
+       test_cmp expect .git/config
+'
+
+test_expect_success 'rename non-existing section' '
+       test_must_fail git config --rename-section \
+               branch."world domination" branch.drei
+'
+
+test_expect_success 'rename succeeded' '
+       test_cmp expect .git/config
+'
+
+test_expect_success 'rename another section' '
+       git config --rename-section branch."1 234 blabl/a" branch.drei
+'
+
+cat > expect << EOF
+# Hallo
+       #Bello
+[branch "zwei"]
+       x = 1
+[branch "zwei"]
+       y = 1
+[branch "drei"]
+weird
+EOF
+
+test_expect_success 'rename succeeded' '
+       test_cmp expect .git/config
+'
+
+cat >> .git/config << EOF
+[branch "vier"] z = 1
+EOF
+
+test_expect_success 'rename a section with a var on the same line' '
+       git config --rename-section branch.vier branch.zwei
+'
+
+cat > expect << EOF
+# Hallo
+       #Bello
+[branch "zwei"]
+       x = 1
+[branch "zwei"]
+       y = 1
+[branch "drei"]
+weird
+[branch "zwei"]
+       z = 1
+EOF
+
+test_expect_success 'rename succeeded' '
+       test_cmp expect .git/config
+'
+
+test_expect_success 'renaming empty section name is rejected' '
+       test_must_fail git config --rename-section branch.zwei ""
+'
+
+test_expect_success 'renaming to bogus section is rejected' '
+       test_must_fail git config --rename-section branch.zwei "bogus name"
+'
+
+cat >> .git/config << EOF
+  [branch "zwei"] a = 1 [branch "vier"]
+EOF
+
+test_expect_success 'remove section' '
+       git config --remove-section branch.zwei
+'
+
+cat > expect << EOF
+# Hallo
+       #Bello
+[branch "drei"]
+weird
+EOF
+
+test_expect_success 'section was removed properly' '
+       test_cmp expect .git/config
+'
+
+cat > expect << EOF
+[gitcvs]
+       enabled = true
+       dbname = %Ggitcvs2.%a.%m.sqlite
+[gitcvs "ext"]
+       dbname = %Ggitcvs1.%a.%m.sqlite
+EOF
+
+test_expect_success 'section ending' '
+       rm -f .git/config &&
+       git config gitcvs.enabled true &&
+       git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
+       git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
+       test_cmp expect .git/config
+
+'
+
+test_expect_success numbers '
+       git config kilo.gram 1k &&
+       git config mega.ton 1m &&
+       echo 1024 >expect &&
+       echo 1048576 >>expect &&
+       git config --int --get kilo.gram >actual &&
+       git config --int --get mega.ton >>actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--int is at least 64 bits' '
+       git config giga.watts 121g &&
+       echo 129922760704 >expect &&
+       git config --int --get giga.watts >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'invalid unit' '
+       git config aninvalid.unit "1auto" &&
+       echo 1auto >expect &&
+       git config aninvalid.unit >actual &&
+       test_cmp expect actual &&
+       test_must_fail git config --int --get aninvalid.unit 2>actual &&
+       test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
+'
+
+test_expect_success 'line number is reported correctly' '
+       printf "[bool]\n\tvar\n" >invalid &&
+       test_must_fail git config -f invalid --path bool.var 2>actual &&
+       test_i18ngrep "line 2" actual
+'
+
+test_expect_success 'invalid stdin config' '
+       echo "[broken" | test_must_fail git config --list --file - >output 2>&1 &&
+       test_i18ngrep "bad config line 1 in standard input" output
+'
+
+cat > expect << EOF
+true
+false
+true
+false
+true
+false
+true
+false
+EOF
+
+test_expect_success bool '
+
+       git config bool.true1 01 &&
+       git config bool.true2 -1 &&
+       git config bool.true3 YeS &&
+       git config bool.true4 true &&
+       git config bool.false1 000 &&
+       git config bool.false2 "" &&
+       git config bool.false3 nO &&
+       git config bool.false4 FALSE &&
+       rm -f result &&
+       for i in 1 2 3 4
+       do
+           git config --bool --get bool.true$i >>result
+           git config --bool --get bool.false$i >>result
+        done &&
+       test_cmp expect result'
+
+test_expect_success 'invalid bool (--get)' '
+
+       git config bool.nobool foobar &&
+       test_must_fail git config --bool --get bool.nobool'
+
+test_expect_success 'invalid bool (set)' '
+
+       test_must_fail git config --bool bool.nobool foobar'
+
+cat > expect <<\EOF
+[bool]
+       true1 = true
+       true2 = true
+       true3 = true
+       true4 = true
+       false1 = false
+       false2 = false
+       false3 = false
+       false4 = false
+EOF
+
+test_expect_success 'set --bool' '
+
+       rm -f .git/config &&
+       git config --bool bool.true1 01 &&
+       git config --bool bool.true2 -1 &&
+       git config --bool bool.true3 YeS &&
+       git config --bool bool.true4 true &&
+       git config --bool bool.false1 000 &&
+       git config --bool bool.false2 "" &&
+       git config --bool bool.false3 nO &&
+       git config --bool bool.false4 FALSE &&
+       test_cmp expect .git/config'
+
+cat > expect <<\EOF
+[int]
+       val1 = 1
+       val2 = -1
+       val3 = 5242880
+EOF
+
+test_expect_success 'set --int' '
+
+       rm -f .git/config &&
+       git config --int int.val1 01 &&
+       git config --int int.val2 -1 &&
+       git config --int int.val3 5m &&
+       test_cmp expect .git/config
+'
+
+test_expect_success 'get --bool-or-int' '
+       cat >.git/config <<-\EOF &&
+       [bool]
+       true1
+       true2 = true
+       false = false
+       [int]
+       int1 = 0
+       int2 = 1
+       int3 = -1
+       EOF
+       cat >expect <<-\EOF &&
+       true
+       true
+       false
+       0
+       1
+       -1
+       EOF
+       {
+               git config --bool-or-int bool.true1 &&
+               git config --bool-or-int bool.true2 &&
+               git config --bool-or-int bool.false &&
+               git config --bool-or-int int.int1 &&
+               git config --bool-or-int int.int2 &&
+               git config --bool-or-int int.int3
+       } >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<\EOF
+[bool]
+       true1 = true
+       false1 = false
+       true2 = true
+       false2 = false
+[int]
+       int1 = 0
+       int2 = 1
+       int3 = -1
+EOF
+
+test_expect_success 'set --bool-or-int' '
+       rm -f .git/config &&
+       git config --bool-or-int bool.true1 true &&
+       git config --bool-or-int bool.false1 false &&
+       git config --bool-or-int bool.true2 yes &&
+       git config --bool-or-int bool.false2 no &&
+       git config --bool-or-int int.int1 0 &&
+       git config --bool-or-int int.int2 1 &&
+       git config --bool-or-int int.int3 -1 &&
+       test_cmp expect .git/config
+'
+
+cat >expect <<\EOF
+[path]
+       home = ~/
+       normal = /dev/null
+       trailingtilde = foo~
+EOF
+
+test_expect_success !MINGW 'set --path' '
+       rm -f .git/config &&
+       git config --path path.home "~/" &&
+       git config --path path.normal "/dev/null" &&
+       git config --path path.trailingtilde "foo~" &&
+       test_cmp expect .git/config'
+
+if test_have_prereq !MINGW && test "${HOME+set}"
+then
+       test_set_prereq HOMEVAR
+fi
+
+cat >expect <<EOF
+$HOME/
+/dev/null
+foo~
+EOF
+
+test_expect_success HOMEVAR 'get --path' '
+       git config --get --path path.home > result &&
+       git config --get --path path.normal >> result &&
+       git config --get --path path.trailingtilde >> result &&
+       test_cmp expect result
+'
+
+cat >expect <<\EOF
+/dev/null
+foo~
+EOF
+
+test_expect_success !MINGW 'get --path copes with unset $HOME' '
+       (
+               unset HOME;
+               test_must_fail git config --get --path path.home \
+                       >result 2>msg &&
+               git config --get --path path.normal >>result &&
+               git config --get --path path.trailingtilde >>result
+       ) &&
+       test_i18ngrep "[Ff]ailed to expand.*~/" msg &&
+       test_cmp expect result
+'
+
+test_expect_success 'get --path barfs on boolean variable' '
+       echo "[path]bool" >.git/config &&
+       test_must_fail git config --get --path path.bool
+'
+
+test_expect_success 'get --expiry-date' '
+       rel="3.weeks.5.days.00:00" &&
+       rel_out="$rel ->" &&
+       cat >.git/config <<-\EOF &&
+       [date]
+       valid1 = "3.weeks.5.days 00:00"
+       valid2 = "Fri Jun 4 15:46:55 2010"
+       valid3 = "2017/11/11 11:11:11PM"
+       valid4 = "2017/11/10 09:08:07 PM"
+       valid5 = "never"
+       invalid1 = "abc"
+       EOF
+       cat >expect <<-EOF &&
+       $(test-tool date timestamp $rel)
+       1275666415
+       1510441871
+       1510348087
+       0
+       EOF
+       {
+               echo "$rel_out $(git config --expiry-date date.valid1)"
+               git config --expiry-date date.valid2 &&
+               git config --expiry-date date.valid3 &&
+               git config --expiry-date date.valid4 &&
+               git config --expiry-date date.valid5
+       } >actual &&
+       test_cmp expect actual &&
+       test_must_fail git config --expiry-date date.invalid1
+'
+
+cat > expect << EOF
+[quote]
+       leading = " test"
+       ending = "test "
+       semicolon = "test;test"
+       hash = "test#test"
+EOF
+test_expect_success 'quoting' '
+       rm -f .git/config &&
+       git config quote.leading " test" &&
+       git config quote.ending "test " &&
+       git config quote.semicolon "test;test" &&
+       git config quote.hash "test#test" &&
+       test_cmp expect .git/config
+'
+
+test_expect_success 'key with newline' '
+       test_must_fail git config "key.with
+newline" 123'
+
+test_expect_success 'value with newline' 'git config key.sub value.with\\\
+newline'
+
+cat > .git/config <<\EOF
+[section]
+       ; comment \
+       continued = cont\
+inued
+       noncont   = not continued ; \
+       quotecont = "cont;\
+inued"
+EOF
+
+cat > expect <<\EOF
+section.continued=continued
+section.noncont=not continued
+section.quotecont=cont;inued
+EOF
+
+test_expect_success 'value continued on next line' '
+       git config --list > result &&
+       test_cmp result expect
+'
+
+cat > .git/config <<\EOF
+[section "sub=section"]
+       val1 = foo=bar
+       val2 = foo\nbar
+       val3 = \n\n
+       val4 =
+       val5
+EOF
+
+cat > expect <<\EOF
+section.sub=section.val1
+foo=barQsection.sub=section.val2
+foo
+barQsection.sub=section.val3
+
+
+Qsection.sub=section.val4
+Qsection.sub=section.val5Q
+EOF
+test_expect_success '--null --list' '
+       git config --null --list >result.raw &&
+       nul_to_q <result.raw >result &&
+       echo >>result &&
+       test_cmp expect result
+'
+
+test_expect_success '--null --get-regexp' '
+       git config --null --get-regexp "val[0-9]" >result.raw &&
+       nul_to_q <result.raw >result &&
+       echo >>result &&
+       test_cmp expect result
+'
+
+test_expect_success 'inner whitespace kept verbatim' '
+       git config section.val "foo       bar" &&
+       echo "foo         bar" >expect &&
+       git config section.val >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success SYMLINKS 'symlinked configuration' '
+       ln -s notyet myconfig &&
+       git config --file=myconfig test.frotz nitfol &&
+       test -h myconfig &&
+       test -f notyet &&
+       test "z$(git config --file=notyet test.frotz)" = znitfol &&
+       git config --file=myconfig test.xyzzy rezrov &&
+       test -h myconfig &&
+       test -f notyet &&
+       cat >expect <<-\EOF &&
+       nitfol
+       rezrov
+       EOF
+       {
+               git config --file=notyet test.frotz &&
+               git config --file=notyet test.xyzzy
+       } >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'nonexistent configuration' '
+       test_must_fail git config --file=doesnotexist --list &&
+       test_must_fail git config --file=doesnotexist test.xyzzy
+'
+
+test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
+       ln -s doesnotexist linktonada &&
+       ln -s linktonada linktolinktonada &&
+       test_must_fail git config --file=linktonada --list &&
+       test_must_fail git config --file=linktolinktonada --list
+'
+
+test_expect_success 'check split_cmdline return' "
+       git config alias.split-cmdline-fix 'echo \"' &&
+       test_must_fail git split-cmdline-fix &&
+       echo foo > foo &&
+       git add foo &&
+       git commit -m 'initial commit' &&
+       git config branch.master.mergeoptions 'echo \"' &&
+       test_must_fail git merge master
+"
+
+test_expect_success 'git -c "key=value" support' '
+       cat >expect <<-\EOF &&
+       value
+       value
+       true
+       EOF
+       {
+               git -c core.name=value config core.name &&
+               git -c foo.CamelCase=value config foo.camelcase &&
+               git -c foo.flag config --bool foo.flag
+       } >actual &&
+       test_cmp expect actual &&
+       test_must_fail git -c name=value config core.name
+'
+
+# We just need a type-specifier here that cares about the
+# distinction internally between a NULL boolean and a real
+# string (because most of git's internal parsers do care).
+# Using "--path" works, but we do not otherwise care about
+# its semantics.
+test_expect_success 'git -c can represent empty string' '
+       echo >expect &&
+       git -c foo.empty= config --path foo.empty >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'key sanity-checking' '
+       test_must_fail git config foo=bar &&
+       test_must_fail git config foo=.bar &&
+       test_must_fail git config foo.ba=r &&
+       test_must_fail git config foo.1bar &&
+       test_must_fail git config foo."ba
+                               z".bar &&
+       test_must_fail git config . false &&
+       test_must_fail git config .foo false &&
+       test_must_fail git config foo. false &&
+       test_must_fail git config .foo. false &&
+       git config foo.bar true &&
+       git config foo."ba =z".bar false
+'
+
+test_expect_success 'git -c works with aliases of builtins' '
+       git config alias.checkconfig "-c foo.check=bar config foo.check" &&
+       echo bar >expect &&
+       git checkconfig >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'aliases can be CamelCased' '
+       test_config alias.CamelCased "rev-parse HEAD" &&
+       git CamelCased >out &&
+       git rev-parse HEAD >expect &&
+       test_cmp expect out
+'
+
+test_expect_success 'git -c does not split values on equals' '
+       echo "value with = in it" >expect &&
+       git -c core.foo="value with = in it" config core.foo >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git -c dies on bogus config' '
+       test_must_fail git -c core.bare=foo rev-parse
+'
+
+test_expect_success 'git -c complains about empty key' '
+       test_must_fail git -c "=foo" rev-parse
+'
+
+test_expect_success 'git -c complains about empty key and value' '
+       test_must_fail git -c "" rev-parse
+'
+
+test_expect_success 'multiple git -c appends config' '
+       test_config alias.x "!git -c x.two=2 config --get-regexp ^x\.*" &&
+       cat >expect <<-\EOF &&
+       x.one 1
+       x.two 2
+       EOF
+       git -c x.one=1 x >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'last one wins: two level vars' '
+
+       # sec.var and sec.VAR are the same variable, as the first
+       # and the last level of a configuration variable name is
+       # case insensitive.
+
+       echo VAL >expect &&
+
+       git -c sec.var=val -c sec.VAR=VAL config --get sec.var >actual &&
+       test_cmp expect actual &&
+       git -c SEC.var=val -c sec.var=VAL config --get sec.var >actual &&
+       test_cmp expect actual &&
+
+       git -c sec.var=val -c sec.VAR=VAL config --get SEC.var >actual &&
+       test_cmp expect actual &&
+       git -c SEC.var=val -c sec.var=VAL config --get sec.VAR >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'last one wins: three level vars' '
+
+       # v.a.r and v.A.r are not the same variable, as the middle
+       # level of a three-level configuration variable name is
+       # case sensitive.
+
+       echo val >expect &&
+       git -c v.a.r=val -c v.A.r=VAL config --get v.a.r >actual &&
+       test_cmp expect actual &&
+       git -c v.a.r=val -c v.A.r=VAL config --get V.a.R >actual &&
+       test_cmp expect actual &&
+
+       # v.a.r and V.a.R are the same variable, as the first
+       # and the last level of a configuration variable name is
+       # case insensitive.
+
+       echo VAL >expect &&
+       git -c v.a.r=val -c v.a.R=VAL config --get v.a.r >actual &&
+       test_cmp expect actual &&
+       git -c v.a.r=val -c V.a.r=VAL config --get v.a.r >actual &&
+       test_cmp expect actual &&
+       git -c v.a.r=val -c v.a.R=VAL config --get V.a.R >actual &&
+       test_cmp expect actual &&
+       git -c v.a.r=val -c V.a.r=VAL config --get V.a.R >actual &&
+       test_cmp expect actual
+'
+
+for VAR in a .a a. a.0b a."b c". a."b c".0d
+do
+       test_expect_success "git -c $VAR=VAL rejects invalid '$VAR'" '
+               test_must_fail git -c "$VAR=VAL" config -l
+       '
+done
+
+for VAR in a.b a."b c".d
+do
+       test_expect_success "git -c $VAR=VAL works with valid '$VAR'" '
+               echo VAL >expect &&
+               git -c "$VAR=VAL" config --get "$VAR" >actual &&
+               test_cmp expect actual
+       '
+done
+
+test_expect_success 'git -c is not confused by empty environment' '
+       GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
+'
+
+sq="'"
+test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
+       cat >expect <<-\EOF &&
+       env.one one
+       env.two two
+       EOF
+       GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq} ${sq}env.two=two${sq}" \
+               git config --get-regexp "env.*" >actual &&
+       test_cmp expect actual &&
+
+       cat >expect <<-EOF &&
+       env.one one${sq}
+       env.two two
+       EOF
+       GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq}\\$sq$sq$sq ${sq}env.two=two${sq}" \
+               git config --get-regexp "env.*" >actual &&
+       test_cmp expect actual &&
+
+       test_must_fail env \
+               GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq}\\$sq ${sq}env.two=two${sq}" \
+               git config --get-regexp "env.*"
+'
+
+test_expect_success 'git config --edit works' '
+       git config -f tmp test.value no &&
+       echo test.value=yes >expect &&
+       GIT_EDITOR="echo [test]value=yes >" git config -f tmp --edit &&
+       git config -f tmp --list >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git config --edit respects core.editor' '
+       git config -f tmp test.value no &&
+       echo test.value=yes >expect &&
+       test_config core.editor "echo [test]value=yes >" &&
+       git config -f tmp --edit &&
+       git config -f tmp --list >actual &&
+       test_cmp expect actual
+'
+
+# malformed configuration files
+test_expect_success 'barf on syntax error' '
+       cat >.git/config <<-\EOF &&
+       # broken section line
+       [section]
+       key garbage
+       EOF
+       test_must_fail git config --get section.key >actual 2>error &&
+       test_i18ngrep " line 3 " error
+'
+
+test_expect_success 'barf on incomplete section header' '
+       cat >.git/config <<-\EOF &&
+       # broken section line
+       [section
+       key = value
+       EOF
+       test_must_fail git config --get section.key >actual 2>error &&
+       test_i18ngrep " line 2 " error
+'
+
+test_expect_success 'barf on incomplete string' '
+       cat >.git/config <<-\EOF &&
+       # broken section line
+       [section]
+       key = "value string
+       EOF
+       test_must_fail git config --get section.key >actual 2>error &&
+       test_i18ngrep " line 3 " error
+'
+
+test_expect_success 'urlmatch' '
+       cat >.git/config <<-\EOF &&
+       [http]
+               sslVerify
+       [http "https://weak.example.com"]
+               sslVerify = false
+               cookieFile = /tmp/cookie.txt
+       EOF
+
+       test_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual &&
+       test_must_be_empty actual &&
+
+       echo true >expect &&
+       git config --bool --get-urlmatch http.SSLverify https://good.example.com >actual &&
+       test_cmp expect actual &&
+
+       echo false >expect &&
+       git config --bool --get-urlmatch http.sslverify https://weak.example.com >actual &&
+       test_cmp expect actual &&
+
+       {
+               echo http.cookiefile /tmp/cookie.txt &&
+               echo http.sslverify false
+       } >expect &&
+       git config --get-urlmatch HTTP https://weak.example.com >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'urlmatch favors more specific URLs' '
+       cat >.git/config <<-\EOF &&
+       [http "https://example.com/"]
+               cookieFile = /tmp/root.txt
+       [http "https://example.com/subdirectory"]
+               cookieFile = /tmp/subdirectory.txt
+       [http "https://user@example.com/"]
+               cookieFile = /tmp/user.txt
+       [http "https://averylonguser@example.com/"]
+               cookieFile = /tmp/averylonguser.txt
+       [http "https://preceding.example.com"]
+               cookieFile = /tmp/preceding.txt
+       [http "https://*.example.com"]
+               cookieFile = /tmp/wildcard.txt
+       [http "https://*.example.com/wildcardwithsubdomain"]
+               cookieFile = /tmp/wildcardwithsubdomain.txt
+       [http "https://trailing.example.com"]
+               cookieFile = /tmp/trailing.txt
+       [http "https://user@*.example.com/"]
+               cookieFile = /tmp/wildcardwithuser.txt
+       [http "https://sub.example.com/"]
+               cookieFile = /tmp/sub.txt
+       EOF
+
+       echo http.cookiefile /tmp/root.txt >expect &&
+       git config --get-urlmatch HTTP https://example.com >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/subdirectory.txt >expect &&
+       git config --get-urlmatch HTTP https://example.com/subdirectory >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/subdirectory.txt >expect &&
+       git config --get-urlmatch HTTP https://example.com/subdirectory/nested >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/user.txt >expect &&
+       git config --get-urlmatch HTTP https://user@example.com/ >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/subdirectory.txt >expect &&
+       git config --get-urlmatch HTTP https://averylonguser@example.com/subdirectory >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/preceding.txt >expect &&
+       git config --get-urlmatch HTTP https://preceding.example.com >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/wildcard.txt >expect &&
+       git config --get-urlmatch HTTP https://wildcard.example.com >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/sub.txt >expect &&
+       git config --get-urlmatch HTTP https://sub.example.com/wildcardwithsubdomain >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/trailing.txt >expect &&
+       git config --get-urlmatch HTTP https://trailing.example.com >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/sub.txt >expect &&
+       git config --get-urlmatch HTTP https://user@sub.example.com >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'urlmatch with wildcard' '
+       cat >.git/config <<-\EOF &&
+       [http]
+               sslVerify
+       [http "https://*.example.com"]
+               sslVerify = false
+               cookieFile = /tmp/cookie.txt
+       EOF
+
+       test_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual &&
+       test_must_be_empty actual &&
+
+       echo true >expect &&
+       git config --bool --get-urlmatch http.SSLverify https://example.com >actual &&
+       test_cmp expect actual &&
+
+       echo true >expect &&
+       git config --bool --get-urlmatch http.SSLverify https://good-example.com >actual &&
+       test_cmp expect actual &&
+
+       echo true >expect &&
+       git config --bool --get-urlmatch http.sslverify https://deep.nested.example.com >actual &&
+       test_cmp expect actual &&
+
+       echo false >expect &&
+       git config --bool --get-urlmatch http.sslverify https://good.example.com >actual &&
+       test_cmp expect actual &&
+
+       {
+               echo http.cookiefile /tmp/cookie.txt &&
+               echo http.sslverify false
+       } >expect &&
+       git config --get-urlmatch HTTP https://good.example.com >actual &&
+       test_cmp expect actual &&
+
+       echo http.sslverify >expect &&
+       git config --get-urlmatch HTTP https://more.example.com.au >actual &&
+       test_cmp expect actual
+'
+
+# good section hygiene
+test_expect_success '--unset last key removes section (except if commented)' '
+       cat >.git/config <<-\EOF &&
+       # some generic comment on the configuration file itself
+       # a comment specific to this "section" section.
+       [section]
+       # some intervening lines
+       # that should also be dropped
+
+       key = value
+       # please be careful when you update the above variable
+       EOF
+
+       cat >expect <<-\EOF &&
+       # some generic comment on the configuration file itself
+       # a comment specific to this "section" section.
+       [section]
+       # some intervening lines
+       # that should also be dropped
+
+       # please be careful when you update the above variable
+       EOF
+
+       git config --unset section.key &&
+       test_cmp expect .git/config &&
+
+       cat >.git/config <<-\EOF &&
+       [section]
+       key = value
+       [next-section]
+       EOF
+
+       cat >expect <<-\EOF &&
+       [next-section]
+       EOF
+
+       git config --unset section.key &&
+       test_cmp expect .git/config &&
+
+       q_to_tab >.git/config <<-\EOF &&
+       [one]
+       Qkey = "multiline \
+       QQ# with comment"
+       [two]
+       key = true
+       EOF
+       git config --unset two.key &&
+       ! grep two .git/config &&
+
+       q_to_tab >.git/config <<-\EOF &&
+       [one]
+       Qkey = "multiline \
+       QQ# with comment"
+       [one]
+       key = true
+       EOF
+       git config --unset-all one.key &&
+       test_line_count = 0 .git/config &&
+
+       q_to_tab >.git/config <<-\EOF &&
+       [one]
+       Qkey = true
+       Q# a comment not at the start
+       [two]
+       Qkey = true
+       EOF
+       git config --unset two.key &&
+       grep two .git/config &&
+
+       q_to_tab >.git/config <<-\EOF &&
+       [one]
+       Qkey = not [two "subsection"]
+       [two "subsection"]
+       [two "subsection"]
+       Qkey = true
+       [TWO "subsection"]
+       [one]
+       EOF
+       git config --unset two.subsection.key &&
+       test "not [two subsection]" = "$(git config one.key)" &&
+       test_line_count = 3 .git/config
+'
+
+test_expect_success '--unset-all removes section if empty & uncommented' '
+       cat >.git/config <<-\EOF &&
+       [section]
+       key = value1
+       key = value2
+       EOF
+
+       git config --unset-all section.key &&
+       test_line_count = 0 .git/config
+'
+
+test_expect_success 'adding a key into an empty section reuses header' '
+       cat >.git/config <<-\EOF &&
+       [section]
+       EOF
+
+       q_to_tab >expect <<-\EOF &&
+       [section]
+       Qkey = value
+       EOF
+
+       git config section.key value &&
+       test_cmp expect .git/config
+'
+
+test_expect_success POSIXPERM,PERL 'preserves existing permissions' '
+       chmod 0600 .git/config &&
+       git config imap.pass Hunter2 &&
+       perl -e \
+         "die q(badset) if ((stat(q(.git/config)))[2] & 07777) != 0600" &&
+       git config --rename-section imap pop &&
+       perl -e \
+         "die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
+'
+
+! test_have_prereq MINGW ||
+HOME="$(pwd)" # convert to Windows path
+
+test_expect_success 'set up --show-origin tests' '
+       INCLUDE_DIR="$HOME/include" &&
+       mkdir -p "$INCLUDE_DIR" &&
+       cat >"$INCLUDE_DIR"/absolute.include <<-\EOF &&
+               [user]
+                       absolute = include
+       EOF
+       cat >"$INCLUDE_DIR"/relative.include <<-\EOF &&
+               [user]
+                       relative = include
+       EOF
+       cat >"$HOME"/.gitconfig <<-EOF &&
+               [user]
+                       global = true
+                       override = global
+               [include]
+                       path = "$INCLUDE_DIR/absolute.include"
+       EOF
+       cat >.git/config <<-\EOF
+               [user]
+                       local = true
+                       override = local
+               [include]
+                       path = ../include/relative.include
+       EOF
+'
+
+test_expect_success '--show-origin with --list' '
+       cat >expect <<-EOF &&
+               file:$HOME/.gitconfig   user.global=true
+               file:$HOME/.gitconfig   user.override=global
+               file:$HOME/.gitconfig   include.path=$INCLUDE_DIR/absolute.include
+               file:$INCLUDE_DIR/absolute.include      user.absolute=include
+               file:.git/config        user.local=true
+               file:.git/config        user.override=local
+               file:.git/config        include.path=../include/relative.include
+               file:.git/../include/relative.include   user.relative=include
+               command line:   user.cmdline=true
+       EOF
+       git -c user.cmdline=true config --list --show-origin >output &&
+       test_cmp expect output
+'
+
+test_expect_success '--show-origin with --list --null' '
+       cat >expect <<-EOF &&
+               file:$HOME/.gitconfigQuser.global
+               trueQfile:$HOME/.gitconfigQuser.override
+               globalQfile:$HOME/.gitconfigQinclude.path
+               $INCLUDE_DIR/absolute.includeQfile:$INCLUDE_DIR/absolute.includeQuser.absolute
+               includeQfile:.git/configQuser.local
+               trueQfile:.git/configQuser.override
+               localQfile:.git/configQinclude.path
+               ../include/relative.includeQfile:.git/../include/relative.includeQuser.relative
+               includeQcommand line:Quser.cmdline
+               trueQ
+       EOF
+       git -c user.cmdline=true config --null --list --show-origin >output.raw &&
+       nul_to_q <output.raw >output &&
+       # The here-doc above adds a newline that the --null output would not
+       # include. Add it here to make the two comparable.
+       echo >>output &&
+       test_cmp expect output
+'
+
+test_expect_success '--show-origin with single file' '
+       cat >expect <<-\EOF &&
+               file:.git/config        user.local=true
+               file:.git/config        user.override=local
+               file:.git/config        include.path=../include/relative.include
+       EOF
+       git config --local --list --show-origin >output &&
+       test_cmp expect output
+'
+
+test_expect_success '--show-origin with --get-regexp' '
+       cat >expect <<-EOF &&
+               file:$HOME/.gitconfig   user.global true
+               file:.git/config        user.local true
+       EOF
+       git config --show-origin --get-regexp "user\.[g|l].*" >output &&
+       test_cmp expect output
+'
+
+test_expect_success '--show-origin getting a single key' '
+       cat >expect <<-\EOF &&
+               file:.git/config        local
+       EOF
+       git config --show-origin user.override >output &&
+       test_cmp expect output
+'
+
+test_expect_success 'set up custom config file' '
+       CUSTOM_CONFIG_FILE="file\" (dq) and spaces.conf" &&
+       cat >"$CUSTOM_CONFIG_FILE" <<-\EOF
+               [user]
+                       custom = true
+       EOF
+'
+
+test_expect_success !MINGW '--show-origin escape special file name characters' '
+       cat >expect <<-\EOF &&
+               file:"file\" (dq) and spaces.conf"      user.custom=true
+       EOF
+       git config --file "$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
+       test_cmp expect output
+'
+
+test_expect_success '--show-origin stdin' '
+       cat >expect <<-\EOF &&
+               standard input: user.custom=true
+       EOF
+       git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output &&
+       test_cmp expect output
+'
+
+test_expect_success '--show-origin stdin with file include' '
+       cat >"$INCLUDE_DIR"/stdin.include <<-EOF &&
+               [user]
+                       stdin = include
+       EOF
+       cat >expect <<-EOF &&
+               file:$INCLUDE_DIR/stdin.include include
+       EOF
+       echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" \
+               | git config --show-origin --includes --file - user.stdin >output &&
+       test_cmp expect output
+'
+
+test_expect_success !MINGW '--show-origin blob' '
+       blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
+       cat >expect <<-EOF &&
+               blob:$blob      user.custom=true
+       EOF
+       git config --blob=$blob --show-origin --list >output &&
+       test_cmp expect output
+'
+
+test_expect_success !MINGW '--show-origin blob ref' '
+       cat >expect <<-\EOF &&
+               blob:"master:file\" (dq) and spaces.conf"       user.custom=true
+       EOF
+       git add "$CUSTOM_CONFIG_FILE" &&
+       git commit -m "new config file" &&
+       git config --blob=master:"$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
+       test_cmp expect output
+'
+
+test_expect_success '--local requires a repo' '
+       # we expect 128 to ensure that we do not simply
+       # fail to find anything and return code "1"
+       test_expect_code 128 nongit git config --local foo.bar
+'
+
+test_expect_success '--replace-all does not invent newlines' '
+       q_to_tab >.git/config <<-\EOF &&
+       [abc]key
+       QkeepSection
+       [xyz]
+       Qkey = 1
+       [abc]
+       Qkey = a
+       EOF
+       q_to_tab >expect <<-\EOF &&
+       [abc]
+       QkeepSection
+       [xyz]
+       Qkey = 1
+       [abc]
+       Qkey = b
+       EOF
+       git config --replace-all abc.key b &&
+       test_cmp .git/config expect
+'
+
+test_done
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
deleted file mode 100755 (executable)
index e95b1e6..0000000
+++ /dev/null
@@ -1,1614 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Johannes Schindelin
-#
-
-test_description='Test git config in different settings'
-
-. ./test-lib.sh
-
-test_expect_success 'clear default config' '
-       rm -f .git/config
-'
-
-cat > expect << EOF
-[core]
-       penguin = little blue
-EOF
-test_expect_success 'initial' '
-       git config core.penguin "little blue" &&
-       test_cmp expect .git/config
-'
-
-cat > expect << EOF
-[core]
-       penguin = little blue
-       Movie = BadPhysics
-EOF
-test_expect_success 'mixed case' '
-       git config Core.Movie BadPhysics &&
-       test_cmp expect .git/config
-'
-
-cat > expect << EOF
-[core]
-       penguin = little blue
-       Movie = BadPhysics
-[Cores]
-       WhatEver = Second
-EOF
-test_expect_success 'similar section' '
-       git config Cores.WhatEver Second &&
-       test_cmp expect .git/config
-'
-
-cat > expect << EOF
-[core]
-       penguin = little blue
-       Movie = BadPhysics
-       UPPERCASE = true
-[Cores]
-       WhatEver = Second
-EOF
-test_expect_success 'uppercase section' '
-       git config CORE.UPPERCASE true &&
-       test_cmp expect .git/config
-'
-
-test_expect_success 'replace with non-match' '
-       git config core.penguin kingpin !blue
-'
-
-test_expect_success 'replace with non-match (actually matching)' '
-       git config core.penguin "very blue" !kingpin
-'
-
-cat > expect << EOF
-[core]
-       penguin = very blue
-       Movie = BadPhysics
-       UPPERCASE = true
-       penguin = kingpin
-[Cores]
-       WhatEver = Second
-EOF
-
-test_expect_success 'non-match result' 'test_cmp expect .git/config'
-
-test_expect_success 'find mixed-case key by canonical name' '
-       echo Second >expect &&
-       git config cores.whatever >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'find mixed-case key by non-canonical name' '
-       echo Second >expect &&
-       git config CoReS.WhAtEvEr >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'subsections are not canonicalized by git-config' '
-       cat >>.git/config <<-\EOF &&
-       [section.SubSection]
-       key = one
-       [section "SubSection"]
-       key = two
-       EOF
-       echo one >expect &&
-       git config section.subsection.key >actual &&
-       test_cmp expect actual &&
-       echo two >expect &&
-       git config section.SubSection.key >actual &&
-       test_cmp expect actual
-'
-
-cat > .git/config <<\EOF
-[alpha]
-bar = foo
-[beta]
-baz = multiple \
-lines
-EOF
-
-test_expect_success 'unset with cont. lines' '
-       git config --unset beta.baz
-'
-
-cat > expect <<\EOF
-[alpha]
-bar = foo
-[beta]
-EOF
-
-test_expect_success 'unset with cont. lines is correct' 'test_cmp expect .git/config'
-
-cat > .git/config << EOF
-[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
-
-# empty line
-               ; comment
-               haha   ="beta" # last silly comment
-haha = hello
-       haha = bello
-[nextSection] noNewline = ouch
-EOF
-
-cp .git/config .git/config2
-
-test_expect_success 'multiple unset' '
-       git config --unset-all beta.haha
-'
-
-cat > expect << EOF
-[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
-
-# empty line
-               ; comment
-[nextSection] noNewline = ouch
-EOF
-
-test_expect_success 'multiple unset is correct' '
-       test_cmp expect .git/config
-'
-
-cp .git/config2 .git/config
-
-test_expect_success '--replace-all missing value' '
-       test_must_fail git config --replace-all beta.haha &&
-       test_cmp .git/config2 .git/config
-'
-
-rm .git/config2
-
-test_expect_success '--replace-all' '
-       git config --replace-all beta.haha gamma
-'
-
-cat > expect << EOF
-[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
-
-# empty line
-               ; comment
-       haha = gamma
-[nextSection] noNewline = ouch
-EOF
-
-test_expect_success 'all replaced' '
-       test_cmp expect .git/config
-'
-
-cat > expect << EOF
-[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
-
-# empty line
-               ; comment
-       haha = alpha
-[nextSection] noNewline = ouch
-EOF
-test_expect_success 'really mean test' '
-       git config beta.haha alpha &&
-       test_cmp expect .git/config
-'
-
-cat > expect << EOF
-[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
-
-# empty line
-               ; comment
-       haha = alpha
-[nextSection]
-       nonewline = wow
-EOF
-test_expect_success 'really really mean test' '
-       git config nextsection.nonewline wow &&
-       test_cmp expect .git/config
-'
-
-test_expect_success 'get value' '
-       echo alpha >expect &&
-       git config beta.haha >actual &&
-       test_cmp expect actual
-'
-
-cat > expect << EOF
-[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
-
-# empty line
-               ; comment
-[nextSection]
-       nonewline = wow
-EOF
-test_expect_success 'unset' '
-       git config --unset beta.haha &&
-       test_cmp expect .git/config
-'
-
-cat > expect << EOF
-[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
-
-# empty line
-               ; comment
-[nextSection]
-       nonewline = wow
-       NoNewLine = wow2 for me
-EOF
-test_expect_success 'multivar' '
-       git config nextsection.NoNewLine "wow2 for me" "for me$" &&
-       test_cmp expect .git/config
-'
-
-test_expect_success 'non-match' '
-       git config --get nextsection.nonewline !for
-'
-
-test_expect_success 'non-match value' '
-       echo wow >expect &&
-       git config --get nextsection.nonewline !for >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'multi-valued get returns final one' '
-       echo "wow2 for me" >expect &&
-       git config --get nextsection.nonewline >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'multi-valued get-all returns all' '
-       cat >expect <<-\EOF &&
-       wow
-       wow2 for me
-       EOF
-       git config --get-all nextsection.nonewline >actual &&
-       test_cmp expect actual
-'
-
-cat > expect << EOF
-[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
-
-# empty line
-               ; comment
-[nextSection]
-       nonewline = wow3
-       NoNewLine = wow2 for me
-EOF
-test_expect_success 'multivar replace' '
-       git config nextsection.nonewline "wow3" "wow$" &&
-       test_cmp expect .git/config
-'
-
-test_expect_success 'ambiguous unset' '
-       test_must_fail git config --unset nextsection.nonewline
-'
-
-test_expect_success 'invalid unset' '
-       test_must_fail git config --unset somesection.nonewline
-'
-
-cat > expect << EOF
-[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
-
-# empty line
-               ; comment
-[nextSection]
-       NoNewLine = wow2 for me
-EOF
-
-test_expect_success 'multivar unset' '
-       git config --unset nextsection.nonewline "wow3$" &&
-       test_cmp expect .git/config
-'
-
-test_expect_success 'invalid key' 'test_must_fail git config inval.2key blabla'
-
-test_expect_success 'correct key' 'git config 123456.a123 987'
-
-test_expect_success 'hierarchical section' '
-       git config Version.1.2.3eX.Alpha beta
-'
-
-cat > expect << EOF
-[beta] ; silly comment # another comment
-noIndent= sillyValue ; 'nother silly comment
-
-# empty line
-               ; comment
-[nextSection]
-       NoNewLine = wow2 for me
-[123456]
-       a123 = 987
-[Version "1.2.3eX"]
-       Alpha = beta
-EOF
-
-test_expect_success 'hierarchical section value' '
-       test_cmp expect .git/config
-'
-
-cat > expect << EOF
-beta.noindent=sillyValue
-nextsection.nonewline=wow2 for me
-123456.a123=987
-version.1.2.3eX.alpha=beta
-EOF
-
-test_expect_success 'working --list' '
-       git config --list > output &&
-       test_cmp expect output
-'
-cat > expect << EOF
-EOF
-
-test_expect_success '--list without repo produces empty output' '
-       git --git-dir=nonexistent config --list >output &&
-       test_cmp expect output
-'
-
-cat > expect << EOF
-beta.noindent
-nextsection.nonewline
-123456.a123
-version.1.2.3eX.alpha
-EOF
-
-test_expect_success '--name-only --list' '
-       git config --name-only --list >output &&
-       test_cmp expect output
-'
-
-cat > expect << EOF
-beta.noindent sillyValue
-nextsection.nonewline wow2 for me
-EOF
-
-test_expect_success '--get-regexp' '
-       git config --get-regexp in >output &&
-       test_cmp expect output
-'
-
-cat > expect << EOF
-beta.noindent
-nextsection.nonewline
-EOF
-
-test_expect_success '--name-only --get-regexp' '
-       git config --name-only --get-regexp in >output &&
-       test_cmp expect output
-'
-
-cat > expect << EOF
-wow2 for me
-wow4 for you
-EOF
-
-test_expect_success '--add' '
-       git config --add nextsection.nonewline "wow4 for you" &&
-       git config --get-all nextsection.nonewline > output &&
-       test_cmp expect output
-'
-
-cat > .git/config << EOF
-[novalue]
-       variable
-[emptyvalue]
-       variable =
-EOF
-
-test_expect_success 'get variable with no value' '
-       git config --get novalue.variable ^$
-'
-
-test_expect_success 'get variable with empty value' '
-       git config --get emptyvalue.variable ^$
-'
-
-echo novalue.variable > expect
-
-test_expect_success 'get-regexp variable with no value' '
-       git config --get-regexp novalue > output &&
-       test_cmp expect output
-'
-
-echo 'novalue.variable true' > expect
-
-test_expect_success 'get-regexp --bool variable with no value' '
-       git config --bool --get-regexp novalue > output &&
-       test_cmp expect output
-'
-
-echo 'emptyvalue.variable ' > expect
-
-test_expect_success 'get-regexp variable with empty value' '
-       git config --get-regexp emptyvalue > output &&
-       test_cmp expect output
-'
-
-echo true > expect
-
-test_expect_success 'get bool variable with no value' '
-       git config --bool novalue.variable > output &&
-       test_cmp expect output
-'
-
-echo false > expect
-
-test_expect_success 'get bool variable with empty value' '
-       git config --bool emptyvalue.variable > output &&
-       test_cmp expect output
-'
-
-test_expect_success 'no arguments, but no crash' '
-       test_must_fail git config >output 2>&1 &&
-       test_i18ngrep usage output
-'
-
-cat > .git/config << EOF
-[a.b]
-       c = d
-EOF
-
-cat > expect << EOF
-[a.b]
-       c = d
-[a]
-       x = y
-EOF
-
-test_expect_success 'new section is partial match of another' '
-       git config a.x y &&
-       test_cmp expect .git/config
-'
-
-cat > expect << EOF
-[a.b]
-       c = d
-[a]
-       x = y
-       b = c
-[b]
-       x = y
-EOF
-
-test_expect_success 'new variable inserts into proper section' '
-       git config b.x y &&
-       git config a.b c &&
-       test_cmp expect .git/config
-'
-
-test_expect_success 'alternative --file (non-existing file should fail)' '
-       test_must_fail git config --file non-existing-config -l
-'
-
-cat > other-config << EOF
-[ein]
-       bahn = strasse
-EOF
-
-cat > expect << EOF
-ein.bahn=strasse
-EOF
-
-test_expect_success 'alternative GIT_CONFIG' '
-       GIT_CONFIG=other-config git config --list >output &&
-       test_cmp expect output
-'
-
-test_expect_success 'alternative GIT_CONFIG (--file)' '
-       git config --file other-config --list >output &&
-       test_cmp expect output
-'
-
-test_expect_success 'alternative GIT_CONFIG (--file=-)' '
-       git config --file - --list <other-config >output &&
-       test_cmp expect output
-'
-
-test_expect_success 'setting a value in stdin is an error' '
-       test_must_fail git config --file - some.value foo
-'
-
-test_expect_success 'editing stdin is an error' '
-       test_must_fail git config --file - --edit
-'
-
-test_expect_success 'refer config from subdirectory' '
-       mkdir x &&
-       (
-               cd x &&
-               echo strasse >expect &&
-               git config --get --file ../other-config ein.bahn >actual &&
-               test_cmp expect actual
-       )
-
-'
-
-test_expect_success 'refer config from subdirectory via --file' '
-       (
-               cd x &&
-               git config --file=../other-config --get ein.bahn >actual &&
-               test_cmp expect actual
-       )
-'
-
-cat > expect << EOF
-[ein]
-       bahn = strasse
-[anwohner]
-       park = ausweis
-EOF
-
-test_expect_success '--set in alternative file' '
-       git config --file=other-config anwohner.park ausweis &&
-       test_cmp expect other-config
-'
-
-cat > .git/config << EOF
-# Hallo
-       #Bello
-[branch "eins"]
-       x = 1
-[branch.eins]
-       y = 1
-       [branch "1 234 blabl/a"]
-weird
-EOF
-
-test_expect_success 'rename section' '
-       git config --rename-section branch.eins branch.zwei
-'
-
-cat > expect << EOF
-# Hallo
-       #Bello
-[branch "zwei"]
-       x = 1
-[branch "zwei"]
-       y = 1
-       [branch "1 234 blabl/a"]
-weird
-EOF
-
-test_expect_success 'rename succeeded' '
-       test_cmp expect .git/config
-'
-
-test_expect_success 'rename non-existing section' '
-       test_must_fail git config --rename-section \
-               branch."world domination" branch.drei
-'
-
-test_expect_success 'rename succeeded' '
-       test_cmp expect .git/config
-'
-
-test_expect_success 'rename another section' '
-       git config --rename-section branch."1 234 blabl/a" branch.drei
-'
-
-cat > expect << EOF
-# Hallo
-       #Bello
-[branch "zwei"]
-       x = 1
-[branch "zwei"]
-       y = 1
-[branch "drei"]
-weird
-EOF
-
-test_expect_success 'rename succeeded' '
-       test_cmp expect .git/config
-'
-
-cat >> .git/config << EOF
-[branch "vier"] z = 1
-EOF
-
-test_expect_success 'rename a section with a var on the same line' '
-       git config --rename-section branch.vier branch.zwei
-'
-
-cat > expect << EOF
-# Hallo
-       #Bello
-[branch "zwei"]
-       x = 1
-[branch "zwei"]
-       y = 1
-[branch "drei"]
-weird
-[branch "zwei"]
-       z = 1
-EOF
-
-test_expect_success 'rename succeeded' '
-       test_cmp expect .git/config
-'
-
-test_expect_success 'renaming empty section name is rejected' '
-       test_must_fail git config --rename-section branch.zwei ""
-'
-
-test_expect_success 'renaming to bogus section is rejected' '
-       test_must_fail git config --rename-section branch.zwei "bogus name"
-'
-
-cat >> .git/config << EOF
-  [branch "zwei"] a = 1 [branch "vier"]
-EOF
-
-test_expect_success 'remove section' '
-       git config --remove-section branch.zwei
-'
-
-cat > expect << EOF
-# Hallo
-       #Bello
-[branch "drei"]
-weird
-EOF
-
-test_expect_success 'section was removed properly' '
-       test_cmp expect .git/config
-'
-
-cat > expect << EOF
-[gitcvs]
-       enabled = true
-       dbname = %Ggitcvs2.%a.%m.sqlite
-[gitcvs "ext"]
-       dbname = %Ggitcvs1.%a.%m.sqlite
-EOF
-
-test_expect_success 'section ending' '
-       rm -f .git/config &&
-       git config gitcvs.enabled true &&
-       git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
-       git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
-       test_cmp expect .git/config
-
-'
-
-test_expect_success numbers '
-       git config kilo.gram 1k &&
-       git config mega.ton 1m &&
-       echo 1024 >expect &&
-       echo 1048576 >>expect &&
-       git config --int --get kilo.gram >actual &&
-       git config --int --get mega.ton >>actual &&
-       test_cmp expect actual
-'
-
-test_expect_success '--int is at least 64 bits' '
-       git config giga.watts 121g &&
-       echo 129922760704 >expect &&
-       git config --int --get giga.watts >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'invalid unit' '
-       git config aninvalid.unit "1auto" &&
-       echo 1auto >expect &&
-       git config aninvalid.unit >actual &&
-       test_cmp expect actual &&
-       test_must_fail git config --int --get aninvalid.unit 2>actual &&
-       test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
-'
-
-test_expect_success 'line number is reported correctly' '
-       printf "[bool]\n\tvar\n" >invalid &&
-       test_must_fail git config -f invalid --path bool.var 2>actual &&
-       test_i18ngrep "line 2" actual
-'
-
-test_expect_success 'invalid stdin config' '
-       echo "[broken" | test_must_fail git config --list --file - >output 2>&1 &&
-       test_i18ngrep "bad config line 1 in standard input" output
-'
-
-cat > expect << EOF
-true
-false
-true
-false
-true
-false
-true
-false
-EOF
-
-test_expect_success bool '
-
-       git config bool.true1 01 &&
-       git config bool.true2 -1 &&
-       git config bool.true3 YeS &&
-       git config bool.true4 true &&
-       git config bool.false1 000 &&
-       git config bool.false2 "" &&
-       git config bool.false3 nO &&
-       git config bool.false4 FALSE &&
-       rm -f result &&
-       for i in 1 2 3 4
-       do
-           git config --bool --get bool.true$i >>result
-           git config --bool --get bool.false$i >>result
-        done &&
-       test_cmp expect result'
-
-test_expect_success 'invalid bool (--get)' '
-
-       git config bool.nobool foobar &&
-       test_must_fail git config --bool --get bool.nobool'
-
-test_expect_success 'invalid bool (set)' '
-
-       test_must_fail git config --bool bool.nobool foobar'
-
-cat > expect <<\EOF
-[bool]
-       true1 = true
-       true2 = true
-       true3 = true
-       true4 = true
-       false1 = false
-       false2 = false
-       false3 = false
-       false4 = false
-EOF
-
-test_expect_success 'set --bool' '
-
-       rm -f .git/config &&
-       git config --bool bool.true1 01 &&
-       git config --bool bool.true2 -1 &&
-       git config --bool bool.true3 YeS &&
-       git config --bool bool.true4 true &&
-       git config --bool bool.false1 000 &&
-       git config --bool bool.false2 "" &&
-       git config --bool bool.false3 nO &&
-       git config --bool bool.false4 FALSE &&
-       test_cmp expect .git/config'
-
-cat > expect <<\EOF
-[int]
-       val1 = 1
-       val2 = -1
-       val3 = 5242880
-EOF
-
-test_expect_success 'set --int' '
-
-       rm -f .git/config &&
-       git config --int int.val1 01 &&
-       git config --int int.val2 -1 &&
-       git config --int int.val3 5m &&
-       test_cmp expect .git/config
-'
-
-test_expect_success 'get --bool-or-int' '
-       cat >.git/config <<-\EOF &&
-       [bool]
-       true1
-       true2 = true
-       false = false
-       [int]
-       int1 = 0
-       int2 = 1
-       int3 = -1
-       EOF
-       cat >expect <<-\EOF &&
-       true
-       true
-       false
-       0
-       1
-       -1
-       EOF
-       {
-               git config --bool-or-int bool.true1 &&
-               git config --bool-or-int bool.true2 &&
-               git config --bool-or-int bool.false &&
-               git config --bool-or-int int.int1 &&
-               git config --bool-or-int int.int2 &&
-               git config --bool-or-int int.int3
-       } >actual &&
-       test_cmp expect actual
-'
-
-cat >expect <<\EOF
-[bool]
-       true1 = true
-       false1 = false
-       true2 = true
-       false2 = false
-[int]
-       int1 = 0
-       int2 = 1
-       int3 = -1
-EOF
-
-test_expect_success 'set --bool-or-int' '
-       rm -f .git/config &&
-       git config --bool-or-int bool.true1 true &&
-       git config --bool-or-int bool.false1 false &&
-       git config --bool-or-int bool.true2 yes &&
-       git config --bool-or-int bool.false2 no &&
-       git config --bool-or-int int.int1 0 &&
-       git config --bool-or-int int.int2 1 &&
-       git config --bool-or-int int.int3 -1 &&
-       test_cmp expect .git/config
-'
-
-cat >expect <<\EOF
-[path]
-       home = ~/
-       normal = /dev/null
-       trailingtilde = foo~
-EOF
-
-test_expect_success !MINGW 'set --path' '
-       rm -f .git/config &&
-       git config --path path.home "~/" &&
-       git config --path path.normal "/dev/null" &&
-       git config --path path.trailingtilde "foo~" &&
-       test_cmp expect .git/config'
-
-if test_have_prereq !MINGW && test "${HOME+set}"
-then
-       test_set_prereq HOMEVAR
-fi
-
-cat >expect <<EOF
-$HOME/
-/dev/null
-foo~
-EOF
-
-test_expect_success HOMEVAR 'get --path' '
-       git config --get --path path.home > result &&
-       git config --get --path path.normal >> result &&
-       git config --get --path path.trailingtilde >> result &&
-       test_cmp expect result
-'
-
-cat >expect <<\EOF
-/dev/null
-foo~
-EOF
-
-test_expect_success !MINGW 'get --path copes with unset $HOME' '
-       (
-               unset HOME;
-               test_must_fail git config --get --path path.home \
-                       >result 2>msg &&
-               git config --get --path path.normal >>result &&
-               git config --get --path path.trailingtilde >>result
-       ) &&
-       test_i18ngrep "[Ff]ailed to expand.*~/" msg &&
-       test_cmp expect result
-'
-
-test_expect_success 'get --path barfs on boolean variable' '
-       echo "[path]bool" >.git/config &&
-       test_must_fail git config --get --path path.bool
-'
-
-test_expect_success 'get --expiry-date' '
-       rel="3.weeks.5.days.00:00" &&
-       rel_out="$rel ->" &&
-       cat >.git/config <<-\EOF &&
-       [date]
-       valid1 = "3.weeks.5.days 00:00"
-       valid2 = "Fri Jun 4 15:46:55 2010"
-       valid3 = "2017/11/11 11:11:11PM"
-       valid4 = "2017/11/10 09:08:07 PM"
-       valid5 = "never"
-       invalid1 = "abc"
-       EOF
-       cat >expect <<-EOF &&
-       $(test-tool date timestamp $rel)
-       1275666415
-       1510441871
-       1510348087
-       0
-       EOF
-       {
-               echo "$rel_out $(git config --expiry-date date.valid1)"
-               git config --expiry-date date.valid2 &&
-               git config --expiry-date date.valid3 &&
-               git config --expiry-date date.valid4 &&
-               git config --expiry-date date.valid5
-       } >actual &&
-       test_cmp expect actual &&
-       test_must_fail git config --expiry-date date.invalid1
-'
-
-cat > expect << EOF
-[quote]
-       leading = " test"
-       ending = "test "
-       semicolon = "test;test"
-       hash = "test#test"
-EOF
-test_expect_success 'quoting' '
-       rm -f .git/config &&
-       git config quote.leading " test" &&
-       git config quote.ending "test " &&
-       git config quote.semicolon "test;test" &&
-       git config quote.hash "test#test" &&
-       test_cmp expect .git/config
-'
-
-test_expect_success 'key with newline' '
-       test_must_fail git config "key.with
-newline" 123'
-
-test_expect_success 'value with newline' 'git config key.sub value.with\\\
-newline'
-
-cat > .git/config <<\EOF
-[section]
-       ; comment \
-       continued = cont\
-inued
-       noncont   = not continued ; \
-       quotecont = "cont;\
-inued"
-EOF
-
-cat > expect <<\EOF
-section.continued=continued
-section.noncont=not continued
-section.quotecont=cont;inued
-EOF
-
-test_expect_success 'value continued on next line' '
-       git config --list > result &&
-       test_cmp result expect
-'
-
-cat > .git/config <<\EOF
-[section "sub=section"]
-       val1 = foo=bar
-       val2 = foo\nbar
-       val3 = \n\n
-       val4 =
-       val5
-EOF
-
-cat > expect <<\EOF
-section.sub=section.val1
-foo=barQsection.sub=section.val2
-foo
-barQsection.sub=section.val3
-
-
-Qsection.sub=section.val4
-Qsection.sub=section.val5Q
-EOF
-test_expect_success '--null --list' '
-       git config --null --list >result.raw &&
-       nul_to_q <result.raw >result &&
-       echo >>result &&
-       test_cmp expect result
-'
-
-test_expect_success '--null --get-regexp' '
-       git config --null --get-regexp "val[0-9]" >result.raw &&
-       nul_to_q <result.raw >result &&
-       echo >>result &&
-       test_cmp expect result
-'
-
-test_expect_success 'inner whitespace kept verbatim' '
-       git config section.val "foo       bar" &&
-       echo "foo         bar" >expect &&
-       git config section.val >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success SYMLINKS 'symlinked configuration' '
-       ln -s notyet myconfig &&
-       git config --file=myconfig test.frotz nitfol &&
-       test -h myconfig &&
-       test -f notyet &&
-       test "z$(git config --file=notyet test.frotz)" = znitfol &&
-       git config --file=myconfig test.xyzzy rezrov &&
-       test -h myconfig &&
-       test -f notyet &&
-       cat >expect <<-\EOF &&
-       nitfol
-       rezrov
-       EOF
-       {
-               git config --file=notyet test.frotz &&
-               git config --file=notyet test.xyzzy
-       } >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'nonexistent configuration' '
-       test_must_fail git config --file=doesnotexist --list &&
-       test_must_fail git config --file=doesnotexist test.xyzzy
-'
-
-test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
-       ln -s doesnotexist linktonada &&
-       ln -s linktonada linktolinktonada &&
-       test_must_fail git config --file=linktonada --list &&
-       test_must_fail git config --file=linktolinktonada --list
-'
-
-test_expect_success 'check split_cmdline return' "
-       git config alias.split-cmdline-fix 'echo \"' &&
-       test_must_fail git split-cmdline-fix &&
-       echo foo > foo &&
-       git add foo &&
-       git commit -m 'initial commit' &&
-       git config branch.master.mergeoptions 'echo \"' &&
-       test_must_fail git merge master
-"
-
-test_expect_success 'git -c "key=value" support' '
-       cat >expect <<-\EOF &&
-       value
-       value
-       true
-       EOF
-       {
-               git -c core.name=value config core.name &&
-               git -c foo.CamelCase=value config foo.camelcase &&
-               git -c foo.flag config --bool foo.flag
-       } >actual &&
-       test_cmp expect actual &&
-       test_must_fail git -c name=value config core.name
-'
-
-# We just need a type-specifier here that cares about the
-# distinction internally between a NULL boolean and a real
-# string (because most of git's internal parsers do care).
-# Using "--path" works, but we do not otherwise care about
-# its semantics.
-test_expect_success 'git -c can represent empty string' '
-       echo >expect &&
-       git -c foo.empty= config --path foo.empty >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'key sanity-checking' '
-       test_must_fail git config foo=bar &&
-       test_must_fail git config foo=.bar &&
-       test_must_fail git config foo.ba=r &&
-       test_must_fail git config foo.1bar &&
-       test_must_fail git config foo."ba
-                               z".bar &&
-       test_must_fail git config . false &&
-       test_must_fail git config .foo false &&
-       test_must_fail git config foo. false &&
-       test_must_fail git config .foo. false &&
-       git config foo.bar true &&
-       git config foo."ba =z".bar false
-'
-
-test_expect_success 'git -c works with aliases of builtins' '
-       git config alias.checkconfig "-c foo.check=bar config foo.check" &&
-       echo bar >expect &&
-       git checkconfig >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'aliases can be CamelCased' '
-       test_config alias.CamelCased "rev-parse HEAD" &&
-       git CamelCased >out &&
-       git rev-parse HEAD >expect &&
-       test_cmp expect out
-'
-
-test_expect_success 'git -c does not split values on equals' '
-       echo "value with = in it" >expect &&
-       git -c core.foo="value with = in it" config core.foo >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'git -c dies on bogus config' '
-       test_must_fail git -c core.bare=foo rev-parse
-'
-
-test_expect_success 'git -c complains about empty key' '
-       test_must_fail git -c "=foo" rev-parse
-'
-
-test_expect_success 'git -c complains about empty key and value' '
-       test_must_fail git -c "" rev-parse
-'
-
-test_expect_success 'multiple git -c appends config' '
-       test_config alias.x "!git -c x.two=2 config --get-regexp ^x\.*" &&
-       cat >expect <<-\EOF &&
-       x.one 1
-       x.two 2
-       EOF
-       git -c x.one=1 x >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'last one wins: two level vars' '
-
-       # sec.var and sec.VAR are the same variable, as the first
-       # and the last level of a configuration variable name is
-       # case insensitive.
-
-       echo VAL >expect &&
-
-       git -c sec.var=val -c sec.VAR=VAL config --get sec.var >actual &&
-       test_cmp expect actual &&
-       git -c SEC.var=val -c sec.var=VAL config --get sec.var >actual &&
-       test_cmp expect actual &&
-
-       git -c sec.var=val -c sec.VAR=VAL config --get SEC.var >actual &&
-       test_cmp expect actual &&
-       git -c SEC.var=val -c sec.var=VAL config --get sec.VAR >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'last one wins: three level vars' '
-
-       # v.a.r and v.A.r are not the same variable, as the middle
-       # level of a three-level configuration variable name is
-       # case sensitive.
-
-       echo val >expect &&
-       git -c v.a.r=val -c v.A.r=VAL config --get v.a.r >actual &&
-       test_cmp expect actual &&
-       git -c v.a.r=val -c v.A.r=VAL config --get V.a.R >actual &&
-       test_cmp expect actual &&
-
-       # v.a.r and V.a.R are the same variable, as the first
-       # and the last level of a configuration variable name is
-       # case insensitive.
-
-       echo VAL >expect &&
-       git -c v.a.r=val -c v.a.R=VAL config --get v.a.r >actual &&
-       test_cmp expect actual &&
-       git -c v.a.r=val -c V.a.r=VAL config --get v.a.r >actual &&
-       test_cmp expect actual &&
-       git -c v.a.r=val -c v.a.R=VAL config --get V.a.R >actual &&
-       test_cmp expect actual &&
-       git -c v.a.r=val -c V.a.r=VAL config --get V.a.R >actual &&
-       test_cmp expect actual
-'
-
-for VAR in a .a a. a.0b a."b c". a."b c".0d
-do
-       test_expect_success "git -c $VAR=VAL rejects invalid '$VAR'" '
-               test_must_fail git -c "$VAR=VAL" config -l
-       '
-done
-
-for VAR in a.b a."b c".d
-do
-       test_expect_success "git -c $VAR=VAL works with valid '$VAR'" '
-               echo VAL >expect &&
-               git -c "$VAR=VAL" config --get "$VAR" >actual &&
-               test_cmp expect actual
-       '
-done
-
-test_expect_success 'git -c is not confused by empty environment' '
-       GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
-'
-
-sq="'"
-test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
-       cat >expect <<-\EOF &&
-       env.one one
-       env.two two
-       EOF
-       GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq} ${sq}env.two=two${sq}" \
-               git config --get-regexp "env.*" >actual &&
-       test_cmp expect actual &&
-
-       cat >expect <<-EOF &&
-       env.one one${sq}
-       env.two two
-       EOF
-       GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq}\\$sq$sq$sq ${sq}env.two=two${sq}" \
-               git config --get-regexp "env.*" >actual &&
-       test_cmp expect actual &&
-
-       test_must_fail env \
-               GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq}\\$sq ${sq}env.two=two${sq}" \
-               git config --get-regexp "env.*"
-'
-
-test_expect_success 'git config --edit works' '
-       git config -f tmp test.value no &&
-       echo test.value=yes >expect &&
-       GIT_EDITOR="echo [test]value=yes >" git config -f tmp --edit &&
-       git config -f tmp --list >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'git config --edit respects core.editor' '
-       git config -f tmp test.value no &&
-       echo test.value=yes >expect &&
-       test_config core.editor "echo [test]value=yes >" &&
-       git config -f tmp --edit &&
-       git config -f tmp --list >actual &&
-       test_cmp expect actual
-'
-
-# malformed configuration files
-test_expect_success 'barf on syntax error' '
-       cat >.git/config <<-\EOF &&
-       # broken section line
-       [section]
-       key garbage
-       EOF
-       test_must_fail git config --get section.key >actual 2>error &&
-       test_i18ngrep " line 3 " error
-'
-
-test_expect_success 'barf on incomplete section header' '
-       cat >.git/config <<-\EOF &&
-       # broken section line
-       [section
-       key = value
-       EOF
-       test_must_fail git config --get section.key >actual 2>error &&
-       test_i18ngrep " line 2 " error
-'
-
-test_expect_success 'barf on incomplete string' '
-       cat >.git/config <<-\EOF &&
-       # broken section line
-       [section]
-       key = "value string
-       EOF
-       test_must_fail git config --get section.key >actual 2>error &&
-       test_i18ngrep " line 3 " error
-'
-
-test_expect_success 'urlmatch' '
-       cat >.git/config <<-\EOF &&
-       [http]
-               sslVerify
-       [http "https://weak.example.com"]
-               sslVerify = false
-               cookieFile = /tmp/cookie.txt
-       EOF
-
-       test_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual &&
-       test_must_be_empty actual &&
-
-       echo true >expect &&
-       git config --bool --get-urlmatch http.SSLverify https://good.example.com >actual &&
-       test_cmp expect actual &&
-
-       echo false >expect &&
-       git config --bool --get-urlmatch http.sslverify https://weak.example.com >actual &&
-       test_cmp expect actual &&
-
-       {
-               echo http.cookiefile /tmp/cookie.txt &&
-               echo http.sslverify false
-       } >expect &&
-       git config --get-urlmatch HTTP https://weak.example.com >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'urlmatch favors more specific URLs' '
-       cat >.git/config <<-\EOF &&
-       [http "https://example.com/"]
-               cookieFile = /tmp/root.txt
-       [http "https://example.com/subdirectory"]
-               cookieFile = /tmp/subdirectory.txt
-       [http "https://user@example.com/"]
-               cookieFile = /tmp/user.txt
-       [http "https://averylonguser@example.com/"]
-               cookieFile = /tmp/averylonguser.txt
-       [http "https://preceding.example.com"]
-               cookieFile = /tmp/preceding.txt
-       [http "https://*.example.com"]
-               cookieFile = /tmp/wildcard.txt
-       [http "https://*.example.com/wildcardwithsubdomain"]
-               cookieFile = /tmp/wildcardwithsubdomain.txt
-       [http "https://trailing.example.com"]
-               cookieFile = /tmp/trailing.txt
-       [http "https://user@*.example.com/"]
-               cookieFile = /tmp/wildcardwithuser.txt
-       [http "https://sub.example.com/"]
-               cookieFile = /tmp/sub.txt
-       EOF
-
-       echo http.cookiefile /tmp/root.txt >expect &&
-       git config --get-urlmatch HTTP https://example.com >actual &&
-       test_cmp expect actual &&
-
-       echo http.cookiefile /tmp/subdirectory.txt >expect &&
-       git config --get-urlmatch HTTP https://example.com/subdirectory >actual &&
-       test_cmp expect actual &&
-
-       echo http.cookiefile /tmp/subdirectory.txt >expect &&
-       git config --get-urlmatch HTTP https://example.com/subdirectory/nested >actual &&
-       test_cmp expect actual &&
-
-       echo http.cookiefile /tmp/user.txt >expect &&
-       git config --get-urlmatch HTTP https://user@example.com/ >actual &&
-       test_cmp expect actual &&
-
-       echo http.cookiefile /tmp/subdirectory.txt >expect &&
-       git config --get-urlmatch HTTP https://averylonguser@example.com/subdirectory >actual &&
-       test_cmp expect actual &&
-
-       echo http.cookiefile /tmp/preceding.txt >expect &&
-       git config --get-urlmatch HTTP https://preceding.example.com >actual &&
-       test_cmp expect actual &&
-
-       echo http.cookiefile /tmp/wildcard.txt >expect &&
-       git config --get-urlmatch HTTP https://wildcard.example.com >actual &&
-       test_cmp expect actual &&
-
-       echo http.cookiefile /tmp/sub.txt >expect &&
-       git config --get-urlmatch HTTP https://sub.example.com/wildcardwithsubdomain >actual &&
-       test_cmp expect actual &&
-
-       echo http.cookiefile /tmp/trailing.txt >expect &&
-       git config --get-urlmatch HTTP https://trailing.example.com >actual &&
-       test_cmp expect actual &&
-
-       echo http.cookiefile /tmp/sub.txt >expect &&
-       git config --get-urlmatch HTTP https://user@sub.example.com >actual &&
-       test_cmp expect actual
-'
-
-test_expect_success 'urlmatch with wildcard' '
-       cat >.git/config <<-\EOF &&
-       [http]
-               sslVerify
-       [http "https://*.example.com"]
-               sslVerify = false
-               cookieFile = /tmp/cookie.txt
-       EOF
-
-       test_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual &&
-       test_must_be_empty actual &&
-
-       echo true >expect &&
-       git config --bool --get-urlmatch http.SSLverify https://example.com >actual &&
-       test_cmp expect actual &&
-
-       echo true >expect &&
-       git config --bool --get-urlmatch http.SSLverify https://good-example.com >actual &&
-       test_cmp expect actual &&
-
-       echo true >expect &&
-       git config --bool --get-urlmatch http.sslverify https://deep.nested.example.com >actual &&
-       test_cmp expect actual &&
-
-       echo false >expect &&
-       git config --bool --get-urlmatch http.sslverify https://good.example.com >actual &&
-       test_cmp expect actual &&
-
-       {
-               echo http.cookiefile /tmp/cookie.txt &&
-               echo http.sslverify false
-       } >expect &&
-       git config --get-urlmatch HTTP https://good.example.com >actual &&
-       test_cmp expect actual &&
-
-       echo http.sslverify >expect &&
-       git config --get-urlmatch HTTP https://more.example.com.au >actual &&
-       test_cmp expect actual
-'
-
-# good section hygiene
-test_expect_failure 'unsetting the last key in a section removes header' '
-       cat >.git/config <<-\EOF &&
-       # some generic comment on the configuration file itself
-       # a comment specific to this "section" section.
-       [section]
-       # some intervening lines
-       # that should also be dropped
-
-       key = value
-       # please be careful when you update the above variable
-       EOF
-
-       cat >expect <<-\EOF &&
-       # some generic comment on the configuration file itself
-       EOF
-
-       git config --unset section.key &&
-       test_cmp expect .git/config
-'
-
-test_expect_failure 'adding a key into an empty section reuses header' '
-       cat >.git/config <<-\EOF &&
-       [section]
-       EOF
-
-       q_to_tab >expect <<-\EOF &&
-       [section]
-       Qkey = value
-       EOF
-
-       git config section.key value &&
-       test_cmp expect .git/config
-'
-
-test_expect_success POSIXPERM,PERL 'preserves existing permissions' '
-       chmod 0600 .git/config &&
-       git config imap.pass Hunter2 &&
-       perl -e \
-         "die q(badset) if ((stat(q(.git/config)))[2] & 07777) != 0600" &&
-       git config --rename-section imap pop &&
-       perl -e \
-         "die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
-'
-
-! test_have_prereq MINGW ||
-HOME="$(pwd)" # convert to Windows path
-
-test_expect_success 'set up --show-origin tests' '
-       INCLUDE_DIR="$HOME/include" &&
-       mkdir -p "$INCLUDE_DIR" &&
-       cat >"$INCLUDE_DIR"/absolute.include <<-\EOF &&
-               [user]
-                       absolute = include
-       EOF
-       cat >"$INCLUDE_DIR"/relative.include <<-\EOF &&
-               [user]
-                       relative = include
-       EOF
-       cat >"$HOME"/.gitconfig <<-EOF &&
-               [user]
-                       global = true
-                       override = global
-               [include]
-                       path = "$INCLUDE_DIR/absolute.include"
-       EOF
-       cat >.git/config <<-\EOF
-               [user]
-                       local = true
-                       override = local
-               [include]
-                       path = ../include/relative.include
-       EOF
-'
-
-test_expect_success '--show-origin with --list' '
-       cat >expect <<-EOF &&
-               file:$HOME/.gitconfig   user.global=true
-               file:$HOME/.gitconfig   user.override=global
-               file:$HOME/.gitconfig   include.path=$INCLUDE_DIR/absolute.include
-               file:$INCLUDE_DIR/absolute.include      user.absolute=include
-               file:.git/config        user.local=true
-               file:.git/config        user.override=local
-               file:.git/config        include.path=../include/relative.include
-               file:.git/../include/relative.include   user.relative=include
-               command line:   user.cmdline=true
-       EOF
-       git -c user.cmdline=true config --list --show-origin >output &&
-       test_cmp expect output
-'
-
-test_expect_success '--show-origin with --list --null' '
-       cat >expect <<-EOF &&
-               file:$HOME/.gitconfigQuser.global
-               trueQfile:$HOME/.gitconfigQuser.override
-               globalQfile:$HOME/.gitconfigQinclude.path
-               $INCLUDE_DIR/absolute.includeQfile:$INCLUDE_DIR/absolute.includeQuser.absolute
-               includeQfile:.git/configQuser.local
-               trueQfile:.git/configQuser.override
-               localQfile:.git/configQinclude.path
-               ../include/relative.includeQfile:.git/../include/relative.includeQuser.relative
-               includeQcommand line:Quser.cmdline
-               trueQ
-       EOF
-       git -c user.cmdline=true config --null --list --show-origin >output.raw &&
-       nul_to_q <output.raw >output &&
-       # The here-doc above adds a newline that the --null output would not
-       # include. Add it here to make the two comparable.
-       echo >>output &&
-       test_cmp expect output
-'
-
-test_expect_success '--show-origin with single file' '
-       cat >expect <<-\EOF &&
-               file:.git/config        user.local=true
-               file:.git/config        user.override=local
-               file:.git/config        include.path=../include/relative.include
-       EOF
-       git config --local --list --show-origin >output &&
-       test_cmp expect output
-'
-
-test_expect_success '--show-origin with --get-regexp' '
-       cat >expect <<-EOF &&
-               file:$HOME/.gitconfig   user.global true
-               file:.git/config        user.local true
-       EOF
-       git config --show-origin --get-regexp "user\.[g|l].*" >output &&
-       test_cmp expect output
-'
-
-test_expect_success '--show-origin getting a single key' '
-       cat >expect <<-\EOF &&
-               file:.git/config        local
-       EOF
-       git config --show-origin user.override >output &&
-       test_cmp expect output
-'
-
-test_expect_success 'set up custom config file' '
-       CUSTOM_CONFIG_FILE="file\" (dq) and spaces.conf" &&
-       cat >"$CUSTOM_CONFIG_FILE" <<-\EOF
-               [user]
-                       custom = true
-       EOF
-'
-
-test_expect_success !MINGW '--show-origin escape special file name characters' '
-       cat >expect <<-\EOF &&
-               file:"file\" (dq) and spaces.conf"      user.custom=true
-       EOF
-       git config --file "$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
-       test_cmp expect output
-'
-
-test_expect_success '--show-origin stdin' '
-       cat >expect <<-\EOF &&
-               standard input: user.custom=true
-       EOF
-       git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output &&
-       test_cmp expect output
-'
-
-test_expect_success '--show-origin stdin with file include' '
-       cat >"$INCLUDE_DIR"/stdin.include <<-EOF &&
-               [user]
-                       stdin = include
-       EOF
-       cat >expect <<-EOF &&
-               file:$INCLUDE_DIR/stdin.include include
-       EOF
-       echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" \
-               | git config --show-origin --includes --file - user.stdin >output &&
-       test_cmp expect output
-'
-
-test_expect_success !MINGW '--show-origin blob' '
-       blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
-       cat >expect <<-EOF &&
-               blob:$blob      user.custom=true
-       EOF
-       git config --blob=$blob --show-origin --list >output &&
-       test_cmp expect output
-'
-
-test_expect_success !MINGW '--show-origin blob ref' '
-       cat >expect <<-\EOF &&
-               blob:"master:file\" (dq) and spaces.conf"       user.custom=true
-       EOF
-       git add "$CUSTOM_CONFIG_FILE" &&
-       git commit -m "new config file" &&
-       git config --blob=master:"$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
-       test_cmp expect output
-'
-
-test_expect_success '--local requires a repo' '
-       # we expect 128 to ensure that we do not simply
-       # fail to find anything and return code "1"
-       test_expect_code 128 nongit git config --local foo.bar
-'
-
-test_done