Merge branch 'jk/config-get-urlmatch' into maint
authorJunio C Hamano <gitster@pobox.com>
Fri, 15 Apr 2016 01:57:43 +0000 (18:57 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 15 Apr 2016 01:57:43 +0000 (18:57 -0700)
"git config --get-urlmatch", unlike other variants of the "git
config --get" family, did not signal error with its exit status
when there was no matching configuration.

* jk/config-get-urlmatch:
Documentation/git-config: fix --get-all description
Documentation/git-config: use bulleted list for exit codes
config: fail if --get-urlmatch finds no value

1  2 
Documentation/git-config.txt
builtin/config.c
t/t1300-repo-config.sh
index 153b2d89b551b5c249c6a56604726f8175b0b7c6,409df406fa2eba8cd69658526a6cc0785d6d0577..6fc08e3d890007238528e60f843b6e01cda4f1fa
@@@ -9,18 -9,18 +9,18 @@@ git-config - Get and set repository or 
  SYNOPSIS
  --------
  [verse]
 -'git config' [<file-option>] [type] [-z|--null] name [value [value_regex]]
 +'git config' [<file-option>] [type] [--show-origin] [-z|--null] name [value [value_regex]]
  'git config' [<file-option>] [type] --add name value
  'git config' [<file-option>] [type] --replace-all name value [value_regex]
 -'git config' [<file-option>] [type] [-z|--null] --get name [value_regex]
 -'git config' [<file-option>] [type] [-z|--null] --get-all name [value_regex]
 -'git config' [<file-option>] [type] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]
 +'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get name [value_regex]
 +'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get-all name [value_regex]
 +'git config' [<file-option>] [type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]
  'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL
  'git config' [<file-option>] --unset name [value_regex]
  'git config' [<file-option>] --unset-all name [value_regex]
  'git config' [<file-option>] --rename-section old_name new_name
  'git config' [<file-option>] --remove-section name
 -'git config' [<file-option>] [-z|--null] [--name-only] -l | --list
 +'git config' [<file-option>] [--show-origin] [-z|--null] [--name-only] -l | --list
  'git config' [<file-option>] --get-color name [default]
  'git config' [<file-option>] --get-colorbool name [stdout-is-tty]
  'git config' [<file-option>] -e | --edit
@@@ -58,13 -58,13 +58,13 @@@ that location (you can say '--local' bu
  This command will fail with non-zero status upon error.  Some exit
  codes are:
  
. The config file is invalid (ret=3),
. can not write to the config file (ret=4),
. no section or name was provided (ret=2),
. the section or key is invalid (ret=1),
. you try to unset an option which does not exist (ret=5),
. you try to unset/set an option for which multiple lines match (ret=5), or
. you try to use an invalid regexp (ret=6).
- The config file is invalid (ret=3),
- can not write to the config file (ret=4),
- no section or name was provided (ret=2),
- the section or key is invalid (ret=1),
- you try to unset an option which does not exist (ret=5),
- you try to unset/set an option for which multiple lines match (ret=5), or
- you try to use an invalid regexp (ret=6).
  
  On success, the command returns the exit code 0.
  
@@@ -86,8 -86,7 +86,7 @@@ OPTION
        found and the last value if multiple key values were found.
  
  --get-all::
-       Like get, but does not fail if the number of values for the key
-       is not exactly one.
+       Like get, but returns all values for a multi-valued key.
  
  --get-regexp::
        Like --get-all, but interprets the name as a regular expression and
        given URL is returned (if no such key exists, the value for
        section.key is used as a fallback).  When given just the
        section as name, do so for all the keys in the section and
-       list them.
+       list them.  Returns error code 1 if no value is found.
  
  --global::
        For writing options: write to global `~/.gitconfig` file
@@@ -194,12 -193,6 +193,12 @@@ See also <<FILES>>
        Output only the names of config variables for `--list` or
        `--get-regexp`.
  
 +--show-origin::
 +      Augment the output of all queried config options with the
 +      origin type (file, standard input, blob, command line) and
 +      the actual origin (config file path, ref, or blob id if
 +      applicable).
 +
  --get-colorbool name [stdout-is-tty]::
  
        Find the color setting for `name` (e.g. `color.diff`) and output
  
  --[no-]includes::
        Respect `include.*` directives in config files when looking up
 -      values. Defaults to on.
 +      values. Defaults to `off` when a specific file is given (e.g.,
 +      using `--file`, `--global`, etc) and `on` when searching all
 +      config files.
  
  [[FILES]]
  FILES
diff --combined builtin/config.c
index ca9f834ae648177a67e1c9fea9481dfe58ff3972,b92abb74fe2facc9d79e53ab3c8344e40048674a..1d7c6ef558bf7adcfb94ea454966667f987f7701
@@@ -3,7 -3,6 +3,7 @@@
  #include "color.h"
  #include "parse-options.h"
  #include "urlmatch.h"
 +#include "quote.h"
  
  static const char *const builtin_config_usage[] = {
        N_("git config [<options>]"),
@@@ -28,7 -27,6 +28,7 @@@ static int actions, types
  static const char *get_color_slot, *get_colorbool_slot;
  static int end_null;
  static int respect_includes = -1;
 +static int show_origin;
  
  #define ACTION_GET (1<<0)
  #define ACTION_GET_ALL (1<<1)
@@@ -83,7 -81,6 +83,7 @@@ static struct option builtin_config_opt
        OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
        OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
        OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")),
 +      OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
        OPT_END(),
  };
  
@@@ -94,28 -91,8 +94,28 @@@ static void check_argc(int argc, int mi
        usage_with_options(builtin_config_usage, builtin_config_options);
  }
  
 +static void show_config_origin(struct strbuf *buf)
 +{
 +      const char term = end_null ? '\0' : '\t';
 +
 +      strbuf_addstr(buf, current_config_origin_type());
 +      strbuf_addch(buf, ':');
 +      if (end_null)
 +              strbuf_addstr(buf, current_config_name());
 +      else
 +              quote_c_style(current_config_name(), buf, NULL, 0);
 +      strbuf_addch(buf, term);
 +}
 +
  static int show_all_config(const char *key_, const char *value_, void *cb)
  {
 +      if (show_origin) {
 +              struct strbuf buf = STRBUF_INIT;
 +              show_config_origin(&buf);
 +              /* Use fwrite as "buf" can contain \0's if "end_null" is set. */
 +              fwrite(buf.buf, 1, buf.len, stdout);
 +              strbuf_release(&buf);
 +      }
        if (!omit_values && value_)
                printf("%s%c%s%c", key_, delim, value_, term);
        else
@@@ -131,8 -108,6 +131,8 @@@ struct strbuf_list 
  
  static int format_config(struct strbuf *buf, const char *key_, const char *value_)
  {
 +      if (show_origin)
 +              show_config_origin(buf);
        if (show_keys)
                strbuf_addstr(buf, key_);
        if (!omit_values) {
@@@ -377,9 -352,6 +377,9 @@@ static int get_colorbool(const char *va
  
  static void check_write(void)
  {
 +      if (!given_config_source.file && !startup_info->have_repository)
 +              die("not in a git directory");
 +
        if (given_config_source.use_stdin)
                die("writing to stdin is not supported");
  
@@@ -417,6 -389,7 +417,7 @@@ static int urlmatch_collect_fn(const ch
  
  static int get_urlmatch(const char *var, const char *url)
  {
+       int ret;
        char *section_tail;
        struct string_list_item *item;
        struct urlmatch_config config = { STRING_LIST_INIT_DUP };
        git_config_with_options(urlmatch_config_entry, &config,
                                &given_config_source, respect_includes);
  
+       ret = !values.nr;
        for_each_string_list_item(item, &values) {
                struct urlmatch_current_candidate_value *matched = item->util;
                struct strbuf buf = STRBUF_INIT;
        free(config.url.url);
  
        free((void *)config.section);
-       return 0;
+       return ret;
  }
  
  static char *default_user_config(void)
@@@ -566,14 -541,6 +569,14 @@@ int cmd_config(int argc, const char **a
                error("--name-only is only applicable to --list or --get-regexp");
                usage_with_options(builtin_config_usage, builtin_config_options);
        }
 +
 +      if (show_origin && !(actions &
 +              (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {
 +              error("--show-origin is only applicable to --get, --get-all, "
 +                        "--get-regexp, and --list.");
 +              usage_with_options(builtin_config_usage, builtin_config_options);
 +      }
 +
        if (actions == ACTION_LIST) {
                check_argc(argc, 0, 0);
                if (git_config_with_options(show_all_config, NULL,
                check_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
 -              ret = git_config_set_in_file(given_config_source.file, argv[0], value);
 +              ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
                if (ret == CONFIG_NOTHING_SET)
                        error("cannot overwrite multiple values with a single value\n"
                        "       Use a regexp, --add or --replace-all to change %s.", argv[0]);
                check_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
 -              return git_config_set_multivar_in_file(given_config_source.file,
 -                                                     argv[0], value, argv[2], 0);
 +              return git_config_set_multivar_in_file_gently(given_config_source.file,
 +                                                            argv[0], value, argv[2], 0);
        }
        else if (actions == ACTION_ADD) {
                check_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
 -              return git_config_set_multivar_in_file(given_config_source.file,
 -                                                     argv[0], value,
 -                                                     CONFIG_REGEX_NONE, 0);
 +              return git_config_set_multivar_in_file_gently(given_config_source.file,
 +                                                            argv[0], value,
 +                                                            CONFIG_REGEX_NONE, 0);
        }
        else if (actions == ACTION_REPLACE_ALL) {
                check_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
 -              return git_config_set_multivar_in_file(given_config_source.file,
 -                                                     argv[0], value, argv[2], 1);
 +              return git_config_set_multivar_in_file_gently(given_config_source.file,
 +                                                            argv[0], value, argv[2], 1);
        }
        else if (actions == ACTION_GET) {
                check_argc(argc, 1, 2);
                check_write();
                check_argc(argc, 1, 2);
                if (argc == 2)
 -                      return git_config_set_multivar_in_file(given_config_source.file,
 -                                                             argv[0], NULL, argv[1], 0);
 +                      return git_config_set_multivar_in_file_gently(given_config_source.file,
 +                                                                    argv[0], NULL, argv[1], 0);
                else
 -                      return git_config_set_in_file(given_config_source.file,
 -                                                    argv[0], NULL);
 +                      return git_config_set_in_file_gently(given_config_source.file,
 +                                                           argv[0], NULL);
        }
        else if (actions == ACTION_UNSET_ALL) {
                check_write();
                check_argc(argc, 1, 2);
 -              return git_config_set_multivar_in_file(given_config_source.file,
 -                                                     argv[0], NULL, argv[1], 1);
 +              return git_config_set_multivar_in_file_gently(given_config_source.file,
 +                                                            argv[0], NULL, argv[1], 1);
        }
        else if (actions == ACTION_RENAME_SECTION) {
                int ret;
diff --combined t/t1300-repo-config.sh
index 6767da87cba9589222b033c80b605b832f708baf,175e73cbb6e8e312b7e601a46c6047e079db1a1d..3d6f1db9da95cefa02391792500b9577d4ca9902
@@@ -699,13 -699,11 +699,13 @@@ test_expect_success 'invalid unit' 
        echo 1auto >expect &&
        git config aninvalid.unit >actual &&
        test_cmp expect actual &&
 -      cat >expect <<-\EOF &&
 -      fatal: bad numeric config value '\''1auto'\'' for '\''aninvalid.unit'\'' in .git/config: invalid unit
 -      EOF
        test_must_fail git config --int --get aninvalid.unit 2>actual &&
 -      test_i18ncmp expect actual
 +      test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" 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
@@@ -959,15 -957,13 +959,15 @@@ Qsection.sub=section.val
  Qsection.sub=section.val5Q
  EOF
  test_expect_success '--null --list' '
 -      git config --null --list | nul_to_q >result &&
 +      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]" | nul_to_q >result &&
 +      git config --null --get-regexp "val[0-9]" >result.raw &&
 +      nul_to_q <result.raw >result &&
        echo >>result &&
        test_cmp expect result
  '
@@@ -1144,6 -1140,9 +1144,9 @@@ test_expect_success 'urlmatch' 
                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 &&
@@@ -1205,154 -1204,4 +1208,154 @@@ test_expect_success POSIXPERM,PERL 'pre
          "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' '
 +      cat >expect <<-\EOF &&
 +              blob:a9d9f9e555b5c6f07cbe09d3f06fe3df11e09c08   user.custom=true
 +      EOF
 +      blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
 +      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_done