Merge branch 'ms/maint-config-error-at-eol-linecount'
authorJunio C Hamano <gitster@pobox.com>
Tue, 13 Mar 2012 19:35:53 +0000 (12:35 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 13 Mar 2012 19:35:53 +0000 (12:35 -0700)
When "git config" diagnoses an error in a configuration file and
shows the line number for the offending line, it miscounted if the
error was at the end of line.

By Martin Stenberg
* ms/maint-config-error-at-eol-linecount:
config: report errors at the EOL with correct line number

Conflicts:
t/t1300-repo-config.sh

1  2 
config.c
t/t1300-repo-config.sh
diff --combined config.c
index ad0390819d2701d6153adf9db2947ee4908742ce,fc27913c3bb3646a6fd6592356be6a979e9cc7d2..68d32940f3f8f1512fa36702beb98c2457ca5d46
+++ b/config.c
@@@ -26,68 -26,7 +26,68 @@@ static config_file *cf
  
  static int zlib_compression_seen;
  
 -const char *config_exclusive_filename = NULL;
 +#define MAX_INCLUDE_DEPTH 10
 +static const char include_depth_advice[] =
 +"exceeded maximum include depth (%d) while including\n"
 +"     %s\n"
 +"from\n"
 +"     %s\n"
 +"Do you have circular includes?";
 +static int handle_path_include(const char *path, struct config_include_data *inc)
 +{
 +      int ret = 0;
 +      struct strbuf buf = STRBUF_INIT;
 +
 +      /*
 +       * Use an absolute path as-is, but interpret relative paths
 +       * based on the including config file.
 +       */
 +      if (!is_absolute_path(path)) {
 +              char *slash;
 +
 +              if (!cf || !cf->name)
 +                      return error("relative config includes must come from files");
 +
 +              slash = find_last_dir_sep(cf->name);
 +              if (slash)
 +                      strbuf_add(&buf, cf->name, slash - cf->name + 1);
 +              strbuf_addstr(&buf, path);
 +              path = buf.buf;
 +      }
 +
 +      if (!access(path, R_OK)) {
 +              if (++inc->depth > MAX_INCLUDE_DEPTH)
 +                      die(include_depth_advice, MAX_INCLUDE_DEPTH, path,
 +                          cf && cf->name ? cf->name : "the command line");
 +              ret = git_config_from_file(git_config_include, path, inc);
 +              inc->depth--;
 +      }
 +      strbuf_release(&buf);
 +      return ret;
 +}
 +
 +int git_config_include(const char *var, const char *value, void *data)
 +{
 +      struct config_include_data *inc = data;
 +      const char *type;
 +      int ret;
 +
 +      /*
 +       * Pass along all values, including "include" directives; this makes it
 +       * possible to query information on the includes themselves.
 +       */
 +      ret = inc->fn(var, value, inc->data);
 +      if (ret < 0)
 +              return ret;
 +
 +      type = skip_prefix(var, "include.");
 +      if (!type)
 +              return ret;
 +
 +      if (!strcmp(type, "path"))
 +              ret = handle_path_include(value, inc);
 +      return ret;
 +}
  
  static void lowercase(char *p)
  {
@@@ -196,8 -135,10 +196,10 @@@ static char *parse_value(void
        for (;;) {
                int c = get_next_char();
                if (c == '\n') {
-                       if (quote)
+                       if (quote) {
+                               cf->linenr--;
                                return NULL;
+                       }
                        return cf->value.buf;
                }
                if (comment)
@@@ -287,7 -228,7 +289,7 @@@ static int get_extended_base_var(char *
  {
        do {
                if (c == '\n')
-                       return -1;
+                       goto error_incomplete_line;
                c = get_next_char();
        } while (isspace(c));
  
        for (;;) {
                int c = get_next_char();
                if (c == '\n')
-                       return -1;
+                       goto error_incomplete_line;
                if (c == '"')
                        break;
                if (c == '\\') {
                        c = get_next_char();
                        if (c == '\n')
-                               return -1;
+                               goto error_incomplete_line;
                }
                name[baselen++] = c;
                if (baselen > MAXNAME / 2)
        if (get_next_char() != ']')
                return -1;
        return baselen;
+ error_incomplete_line:
+       cf->linenr--;
+       return -1;
  }
  
  static int get_base_var(char *name)
@@@ -394,7 -338,7 +399,7 @@@ static int git_parse_file(config_fn_t f
        die("bad config file line %d in %s", cf->linenr, cf->name);
  }
  
 -static int parse_unit_factor(const char *end, unsigned long *val)
 +static int parse_unit_factor(const char *end, uintmax_t *val)
  {
        if (!*end)
                return 1;
@@@ -417,23 -361,11 +422,23 @@@ static int git_parse_long(const char *v
  {
        if (value && *value) {
                char *end;
 -              long val = strtol(value, &end, 0);
 -              unsigned long factor = 1;
 +              intmax_t val;
 +              uintmax_t uval;
 +              uintmax_t factor = 1;
 +
 +              errno = 0;
 +              val = strtoimax(value, &end, 0);
 +              if (errno == ERANGE)
 +                      return 0;
                if (!parse_unit_factor(end, &factor))
                        return 0;
 -              *ret = val * factor;
 +              uval = abs(val);
 +              uval *= factor;
 +              if ((uval > maximum_signed_value_of_type(long)) ||
 +                  (abs(val) > uval))
 +                      return 0;
 +              val *= factor;
 +              *ret = val;
                return 1;
        }
        return 0;
@@@ -443,19 -375,9 +448,19 @@@ int git_parse_ulong(const char *value, 
  {
        if (value && *value) {
                char *end;
 -              unsigned long val = strtoul(value, &end, 0);
 +              uintmax_t val;
 +              uintmax_t oldval;
 +
 +              errno = 0;
 +              val = strtoumax(value, &end, 0);
 +              if (errno == ERANGE)
 +                      return 0;
 +              oldval = val;
                if (!parse_unit_factor(end, &val))
                        return 0;
 +              if ((val > maximum_unsigned_value_of_type(long)) ||
 +                  (oldval > val))
 +                      return 0;
                *ret = val;
                return 1;
        }
@@@ -636,7 -558,7 +641,7 @@@ static int git_default_core_config(cons
  
        if (!strcmp(var, "core.packedgitwindowsize")) {
                int pgsz_x2 = getpagesize() * 2;
 -              packed_git_window_size = git_config_int(var, value);
 +              packed_git_window_size = git_config_ulong(var, value);
  
                /* This value must be multiple of (pagesize * 2) */
                packed_git_window_size /= pgsz_x2;
        }
  
        if (!strcmp(var, "core.bigfilethreshold")) {
 -              long n = git_config_int(var, value);
 -              big_file_threshold = 0 < n ? n : 0;
 +              big_file_threshold = git_config_ulong(var, value);
                return 0;
        }
  
        if (!strcmp(var, "core.packedgitlimit")) {
 -              packed_git_limit = git_config_int(var, value);
 +              packed_git_limit = git_config_ulong(var, value);
                return 0;
        }
  
        if (!strcmp(var, "core.deltabasecachelimit")) {
 -              delta_base_cache_limit = git_config_int(var, value);
 +              delta_base_cache_limit = git_config_ulong(var, value);
                return 0;
        }
  
@@@ -879,10 -802,6 +884,10 @@@ int git_default_config(const char *var
                return 0;
        }
  
 +      if (!strcmp(var, "pack.packsizelimit")) {
 +              pack_size_limit_cfg = git_config_ulong(var, value);
 +              return 0;
 +      }
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
  }
@@@ -940,6 -859,9 +945,6 @@@ int git_config_early(config_fn_t fn, vo
        int ret = 0, found = 0;
        const char *home = NULL;
  
 -      /* Setting $GIT_CONFIG makes git read _only_ the given config file. */
 -      if (config_exclusive_filename)
 -              return git_config_from_file(fn, config_exclusive_filename, data);
        if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {
                ret += git_config_from_file(fn, git_etc_gitconfig(),
                                            data);
  
        home = getenv("HOME");
        if (home) {
 -              char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
 +              char buf[PATH_MAX];
 +              char *user_config = mksnpath(buf, sizeof(buf), "%s/.gitconfig", home);
                if (!access(user_config, R_OK)) {
                        ret += git_config_from_file(fn, user_config, data);
                        found += 1;
                }
 -              free(user_config);
        }
  
        if (repo_config && !access(repo_config, R_OK)) {
        return ret == 0 ? found : ret;
  }
  
 -int git_config(config_fn_t fn, void *data)
 +int git_config_with_options(config_fn_t fn, void *data,
 +                          const char *filename, int respect_includes)
  {
        char *repo_config = NULL;
        int ret;
 +      struct config_include_data inc = CONFIG_INCLUDE_INIT;
 +
 +      if (respect_includes) {
 +              inc.fn = fn;
 +              inc.data = data;
 +              fn = git_config_include;
 +              data = &inc;
 +      }
 +
 +      /*
 +       * If we have a specific filename, use it. Otherwise, follow the
 +       * regular lookup sequence.
 +       */
 +      if (filename)
 +              return git_config_from_file(fn, filename, data);
  
        repo_config = git_pathdup("config");
        ret = git_config_early(fn, data, repo_config);
        return ret;
  }
  
 +int git_config(config_fn_t fn, void *data)
 +{
 +      return git_config_with_options(fn, data, NULL, 1);
 +}
 +
  /*
   * Find all the stuff for git_config_set() below.
   */
@@@ -1202,12 -1103,6 +1207,12 @@@ contline
        return offset;
  }
  
 +int git_config_set_in_file(const char *config_filename,
 +                      const char *key, const char *value)
 +{
 +      return git_config_set_multivar_in_file(config_filename, key, value, NULL, 0);
 +}
 +
  int git_config_set(const char *key, const char *value)
  {
        return git_config_set_multivar(key, value, NULL, 0);
@@@ -1305,14 -1200,18 +1310,14 @@@ out_free_ret_1
   * - the config file is removed and the lock file rename()d to it.
   *
   */
 -int git_config_set_multivar(const char *key, const char *value,
 -      const char *value_regex, int multi_replace)
 +int git_config_set_multivar_in_file(const char *config_filename,
 +                              const char *key, const char *value,
 +                              const char *value_regex, int multi_replace)
  {
        int fd = -1, in_fd;
        int ret;
 -      char *config_filename;
        struct lock_file *lock = NULL;
 -
 -      if (config_exclusive_filename)
 -              config_filename = xstrdup(config_exclusive_filename);
 -      else
 -              config_filename = git_pathdup("config");
 +      char *filename_buf = NULL;
  
        /* parse-key returns negative; flip the sign to feed exit(3) */
        ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
  
        store.multi_replace = multi_replace;
  
 +      if (!config_filename)
 +              config_filename = filename_buf = git_pathdup("config");
  
        /*
         * The lock serves a purpose in addition to locking: the new
  out_free:
        if (lock)
                rollback_lock_file(lock);
 -      free(config_filename);
 +      free(filename_buf);
        return ret;
  
  write_err_out:
  
  }
  
 +int git_config_set_multivar(const char *key, const char *value,
 +                      const char *value_regex, int multi_replace)
 +{
 +      return git_config_set_multivar_in_file(NULL, key, value, value_regex,
 +                                             multi_replace);
 +}
 +
  static int section_name_match (const char *buf, const char *name)
  {
        int i = 0, j = 0, dot = 0;
  }
  
  /* if new_name == NULL, the section is removed instead */
 -int git_config_rename_section(const char *old_name, const char *new_name)
 +int git_config_rename_section_in_file(const char *config_filename,
 +                                    const char *old_name, const char *new_name)
  {
        int ret = 0, remove = 0;
 -      char *config_filename;
 +      char *filename_buf = NULL;
        struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
        int out_fd;
        char buf[1024];
        FILE *config_file;
  
 -      if (config_exclusive_filename)
 -              config_filename = xstrdup(config_exclusive_filename);
 -      else
 -              config_filename = git_pathdup("config");
 +      if (!config_filename)
 +              config_filename = filename_buf = git_pathdup("config");
 +
        out_fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (out_fd < 0) {
                ret = error("could not lock config file %s", config_filename);
@@@ -1624,15 -1514,10 +1629,15 @@@ unlock_and_out
        if (commit_lock_file(lock) < 0)
                ret = error("could not commit config file %s", config_filename);
  out:
 -      free(config_filename);
 +      free(filename_buf);
        return ret;
  }
  
 +int git_config_rename_section(const char *old_name, const char *new_name)
 +{
 +      return git_config_rename_section_in_file(NULL, old_name, new_name);
 +}
 +
  /*
   * Call this to report error for your variable that should not
   * get a boolean value (i.e. "[my] var" means "true").
diff --combined t/t1300-repo-config.sh
index 5f249f681e9324d7d35e3aee4dc9a834beff6c75,23c8711d714eefa34f2bfc14b92818953aeafa49..36e227b3bb25cb17dabc5e205e63056e7fd0b370
@@@ -7,28 -7,28 +7,28 @@@ test_description='Test git config in di
  
  . ./test-lib.sh
  
 -test -f .git/config && rm .git/config
 -
 -git config core.penguin "little blue"
 +test_expect_success 'clear default config' '
 +      rm -f .git/config
 +'
  
  cat > expect << EOF
  [core]
        penguin = little blue
  EOF
 -
 -test_expect_success 'initial' 'cmp .git/config expect'
 -
 -git config Core.Movie BadPhysics
 +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' 'cmp .git/config expect'
 -
 -git config Cores.WhatEver Second
 +test_expect_success 'mixed case' '
 +      git config Core.Movie BadPhysics &&
 +      test_cmp expect .git/config
 +'
  
  cat > expect << EOF
  [core]
  [Cores]
        WhatEver = Second
  EOF
 -
 -test_expect_success 'similar section' 'cmp .git/config expect'
 -
 -git config CORE.UPPERCASE true
 +test_expect_success 'similar section' '
 +      git config Cores.WhatEver Second &&
 +      test_cmp expect .git/config
 +'
  
  cat > expect << EOF
  [core]
  [Cores]
        WhatEver = Second
  EOF
 -
 -test_expect_success 'similar section' 'cmp .git/config expect'
 +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'
@@@ -71,34 -69,7 +71,34 @@@ cat > expect << EO
        WhatEver = Second
  EOF
  
 -test_expect_success 'non-match result' 'cmp .git/config expect'
 +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]
@@@ -117,7 -88,7 +117,7 @@@ bar = fo
  [beta]
  EOF
  
 -test_expect_success 'unset with cont. lines is correct' 'cmp .git/config expect'
 +test_expect_success 'unset with cont. lines is correct' 'test_cmp expect .git/config'
  
  cat > .git/config << EOF
  [beta] ; silly comment # another comment
@@@ -145,7 -116,7 +145,7 @@@ noIndent= sillyValue ; 'nother silly co
  [nextSection] noNewline = ouch
  EOF
  
 -test_expect_success 'multiple unset is correct' 'cmp .git/config expect'
 +test_expect_success 'multiple unset is correct' 'test_cmp expect .git/config'
  
  cp .git/config2 .git/config
  
@@@ -169,7 -140,9 +169,7 @@@ noIndent= sillyValue ; 'nother silly co
  [nextSection] noNewline = ouch
  EOF
  
 -test_expect_success 'all replaced' 'cmp .git/config expect'
 -
 -git config beta.haha alpha
 +test_expect_success 'all replaced' 'test_cmp expect .git/config'
  
  cat > expect << EOF
  [beta] ; silly comment # another comment
@@@ -180,10 -153,10 +180,10 @@@ noIndent= sillyValue ; 'nother silly co
        haha = alpha
  [nextSection] noNewline = ouch
  EOF
 -
 -test_expect_success 'really mean test' 'cmp .git/config expect'
 -
 -git config nextsection.nonewline wow
 +test_expect_success 'really mean test' '
 +      git config beta.haha alpha &&
 +      test_cmp expect .git/config
 +'
  
  cat > expect << EOF
  [beta] ; silly comment # another comment
@@@ -195,12 -168,11 +195,12 @@@ noIndent= sillyValue ; 'nother silly co
  [nextSection]
        nonewline = wow
  EOF
 -
 -test_expect_success 'really really mean test' 'cmp .git/config expect'
 +test_expect_success 'really really mean test' '
 +      git config nextsection.nonewline wow &&
 +      test_cmp expect .git/config
 +'
  
  test_expect_success 'get value' 'test alpha = $(git config beta.haha)'
 -git config --unset beta.haha
  
  cat > expect << EOF
  [beta] ; silly comment # another comment
@@@ -211,10 -183,10 +211,10 @@@ noIndent= sillyValue ; 'nother silly co
  [nextSection]
        nonewline = wow
  EOF
 -
 -test_expect_success 'unset' 'cmp .git/config expect'
 -
 -git config nextsection.NoNewLine "wow2 for me" "for me$"
 +test_expect_success 'unset' '
 +      git config --unset beta.haha &&
 +      test_cmp expect .git/config
 +'
  
  cat > expect << EOF
  [beta] ; silly comment # another comment
@@@ -226,10 -198,8 +226,10 @@@ noIndent= sillyValue ; 'nother silly co
        nonewline = wow
        NoNewLine = wow2 for me
  EOF
 -
 -test_expect_success 'multivar' 'cmp .git/config expect'
 +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'
@@@ -244,6 -214,8 +244,6 @@@ test_expect_success 'ambiguous get' 
  test_expect_success 'get multivar' \
        'git config --get-all nextsection.nonewline'
  
 -git config nextsection.nonewline "wow3" "wow$"
 -
  cat > expect << EOF
  [beta] ; silly comment # another comment
  noIndent= sillyValue ; 'nother silly comment
        nonewline = wow3
        NoNewLine = wow2 for me
  EOF
 -
 -test_expect_success 'multivar replace' 'cmp .git/config expect'
 +test_expect_success 'multivar replace' '
 +      git config nextsection.nonewline "wow3" "wow$" &&
 +      test_cmp expect .git/config
 +'
  
  test_expect_success 'ambiguous value' '
        test_must_fail git config nextsection.nonewline
@@@ -271,6 -241,8 +271,6 @@@ test_expect_success 'invalid unset' 
        test_must_fail git config --unset somesection.nonewline
  '
  
 -git config --unset nextsection.nonewline "wow3$"
 -
  cat > expect << EOF
  [beta] ; silly comment # another comment
  noIndent= sillyValue ; 'nother silly comment
        NoNewLine = wow2 for me
  EOF
  
 -test_expect_success 'multivar unset' 'cmp .git/config expect'
 +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'
  
@@@ -307,7 -276,7 +307,7 @@@ noIndent= sillyValue ; 'nother silly co
        Alpha = beta
  EOF
  
 -test_expect_success 'hierarchical section value' 'cmp .git/config expect'
 +test_expect_success 'hierarchical section value' 'test_cmp expect .git/config'
  
  cat > expect << EOF
  beta.noindent=sillyValue
@@@ -335,16 -304,15 +335,16 @@@ EO
  test_expect_success '--get-regexp' \
        'git config --get-regexp in > output && cmp output expect'
  
 -git config --add nextsection.nonewline "wow4 for you"
 -
  cat > expect << EOF
  wow2 for me
  wow4 for you
  EOF
  
 -test_expect_success '--add' \
 -      'git config --get-all nextsection.nonewline > output && cmp output expect'
 +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]
@@@ -399,6 -367,8 +399,6 @@@ cat > .git/config << EO
        c = d
  EOF
  
 -git config a.x y
 -
  cat > expect << EOF
  [a.b]
        c = d
        x = y
  EOF
  
 -test_expect_success 'new section is partial match of another' 'cmp .git/config expect'
 -
 -git config b.x y
 -git config a.b c
 +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]
        x = y
  EOF
  
 -test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect'
 +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 GIT_CONFIG (non-existing file should fail)' \
        'test_must_fail git config --file non-existing-config -l'
@@@ -439,10 -405,9 +439,10 @@@ cat > expect << EO
  ein.bahn=strasse
  EOF
  
 -GIT_CONFIG=other-config git config -l > output
 -
 -test_expect_success 'alternative GIT_CONFIG' 'cmp output expect'
 +test_expect_success 'alternative GIT_CONFIG' '
 +      GIT_CONFIG=other-config git config -l >output &&
 +      test_cmp expect output
 +'
  
  test_expect_success 'alternative GIT_CONFIG (--file)' \
        'git config --file other-config -l > output && cmp output expect'
@@@ -451,20 -416,14 +451,20 @@@ test_expect_success 'refer config from 
        mkdir x &&
        (
                cd x &&
 -              echo strasse >expect
 +              echo strasse >expect &&
                git config --get --file ../other-config ein.bahn >actual &&
                test_cmp expect actual
        )
  
  '
  
 -GIT_CONFIG=other-config git config anwohner.park ausweis
 +test_expect_success 'refer config from subdirectory via GIT_CONFIG' '
 +      (
 +              cd x &&
 +              GIT_CONFIG=../other-config git config --get ein.bahn >actual &&
 +              test_cmp expect actual
 +      )
 +'
  
  cat > expect << EOF
  [ein]
        park = ausweis
  EOF
  
 -test_expect_success '--set in alternative GIT_CONFIG' 'cmp other-config expect'
 +test_expect_success '--set in alternative GIT_CONFIG' '
 +      GIT_CONFIG=other-config git config anwohner.park ausweis &&
 +      test_cmp expect other-config
 +'
  
  cat > .git/config << EOF
  # Hallo
@@@ -566,6 -522,8 +566,6 @@@ EO
  test_expect_success "section was removed properly" \
        "test_cmp expect .git/config"
  
 -rm .git/config
 -
  cat > expect << EOF
  [gitcvs]
        enabled = true
@@@ -576,11 -534,10 +576,11 @@@ EO
  
  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 &&
 -      cmp .git/config expect
 +      test_cmp expect .git/config
  
  '
  
@@@ -649,6 -606,8 +649,6 @@@ test_expect_success 'invalid bool (set)
  
        test_must_fail git config --bool bool.nobool foobar'
  
 -rm .git/config
 -
  cat > expect <<\EOF
  [bool]
        true1 = true
@@@ -663,7 -622,6 +663,7 @@@ EO
  
  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.false4 FALSE &&
        cmp expect .git/config'
  
 -rm .git/config
 -
  cat > expect <<\EOF
  [int]
        val1 = 1
@@@ -683,12 -643,13 +683,12 @@@ EO
  
  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 &&
        cmp expect .git/config'
  
 -rm .git/config
 -
  cat >expect <<\EOF
  [bool]
        true1 = true
  EOF
  
  test_expect_success 'get --bool-or-int' '
 +      rm -f .git/config &&
        (
                echo "[bool]"
                echo true1
  
  '
  
 -rm .git/config
  cat >expect <<\EOF
  [bool]
        true1 = true
  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 &&
        test_cmp expect .git/config
  '
  
 -rm .git/config
 -
  cat >expect <<\EOF
  [path]
        home = ~/
  EOF
  
  test_expect_success NOT_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~" &&
@@@ -795,6 -756,13 +795,6 @@@ test_expect_success NOT_MINGW 'get --pa
        test_cmp expect result
  '
  
 -rm .git/config
 -
 -git config quote.leading " test"
 -git config quote.ending "test "
 -git config quote.semicolon "test;test"
 -git config quote.hash "test#test"
 -
  cat > expect << EOF
  [quote]
        leading = " test"
        semicolon = "test;test"
        hash = "test#test"
  EOF
 -
 -test_expect_success 'quoting' 'cmp .git/config expect'
 +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
@@@ -834,10 -796,9 +834,10 @@@ section.noncont=not continue
  section.quotecont=cont;inued
  EOF
  
 -git config --list > result
 -
 -test_expect_success 'value continued on next line' 'cmp result expect'
 +test_expect_success 'value continued on next line' '
 +      git config --list > result &&
 +      cmp result expect
 +'
  
  cat > .git/config <<\EOF
  [section "sub=section"]
@@@ -858,17 -819,16 +858,17 @@@ barQsection.sub=section.val
  Qsection.sub=section.val4
  Qsection.sub=section.val5Q
  EOF
 +test_expect_success '--null --list' '
 +      git config --null --list | nul_to_q >result &&
 +      echo >>result &&
 +      test_cmp expect result
 +'
  
 -git config --null --list | perl -pe 'y/\000/Q/' > result
 -echo >>result
 -
 -test_expect_success '--null --list' 'cmp result expect'
 -
 -git config --null --get-regexp 'val[0-9]' | perl -pe 'y/\000/Q/' > result
 -echo >>result
 -
 -test_expect_success '--null --get-regexp' 'cmp result expect'
 +test_expect_success '--null --get-regexp' '
 +      git config --null --get-regexp "val[0-9]" | nul_to_q >result &&
 +      echo >>result &&
 +      test_cmp expect result
 +'
  
  test_expect_success 'inner whitespace kept verbatim' '
        git config section.val "foo       bar" &&
@@@ -968,21 -928,35 +968,52 @@@ test_expect_success 'git -c complains a
        test_must_fail git -c "" rev-parse
  '
  
 +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 &&
+       grep " 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 &&
+       grep " 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 &&
+       grep " line 3 " error
+ '
  test_done