Merge branch 'ls/config-origin'
authorJunio C Hamano <gitster@pobox.com>
Fri, 26 Feb 2016 21:37:17 +0000 (13:37 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 26 Feb 2016 21:37:17 +0000 (13:37 -0800)
The configuration system has been taught to phrase where it found a
bad configuration variable in a better way in its error messages.
"git config" learnt a new "--show-origin" option to indicate where
the values come from.

* ls/config-origin:
config: add '--show-origin' option to print the origin of a config value
config: add 'origin_type' to config_source struct
rename git_config_from_buf to git_config_from_mem
t: do not hide Git's exit code in tests using 'nul_to_q'

1  2 
Documentation/git-config.txt
cache.h
config.c
index 242fa5d1bb2b7eff9645e140613640d94d28852f,499fc0f8a669646e68115e070db9f9f544046bff..153b2d89b551b5c249c6a56604726f8175b0b7c6
@@@ -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
@@@ -194,6 -194,12 +194,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 cache.h
index 83b688cca6902f81f920f92fcaff0b27c70c075e,ad7fcfc0f25df9feba14cb082f8d7e19fac55240..61c6e0abe39f0c0b0ff5991426853462f2f7eb24
+++ b/cache.h
@@@ -9,7 -9,6 +9,7 @@@
  #include "convert.h"
  #include "trace.h"
  #include "string-list.h"
 +#include "pack-revindex.h"
  
  #include SHA1_HEADER
  #ifndef platform_SHA_CTX
@@@ -215,7 -214,7 +215,7 @@@ struct cache_entry 
  #define CE_INTENT_TO_ADD     (1 << 29)
  #define CE_SKIP_WORKTREE     (1 << 30)
  /* CE_EXTENDED2 is for future extension */
 -#define CE_EXTENDED2         (1 << 31)
 +#define CE_EXTENDED2         (1U << 31)
  
  #define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
  
  #error "CE_EXTENDED_FLAGS out of range"
  #endif
  
 +/* Forward structure decls */
  struct pathspec;
 +struct child_process;
  
  /*
   * Copy the sha1 and stat state of a cache entry from one to
@@@ -262,7 -259,6 +262,7 @@@ static inline unsigned create_ce_flags(
  #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
  #define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
  #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
 +#define ce_intent_to_add(ce) ((ce)->ce_flags & CE_INTENT_TO_ADD)
  
  #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
  static inline unsigned int create_ce_mode(unsigned int mode)
@@@ -460,6 -456,7 +460,6 @@@ extern char *git_work_tree_cfg
  extern int is_inside_work_tree(void);
  extern const char *get_git_dir(void);
  extern const char *get_git_common_dir(void);
 -extern int is_git_directory(const char *path);
  extern char *get_object_directory(void);
  extern char *get_index_file(void);
  extern char *get_graft_file(void);
@@@ -470,25 -467,6 +470,25 @@@ extern const char *get_git_namespace(vo
  extern const char *strip_namespace(const char *namespaced_ref);
  extern const char *get_git_work_tree(void);
  
 +/*
 + * Return true if the given path is a git directory; note that this _just_
 + * looks at the directory itself. If you want to know whether "foo/.git"
 + * is a repository, you must feed that path, not just "foo".
 + */
 +extern int is_git_directory(const char *path);
 +
 +/*
 + * Return 1 if the given path is the root of a git repository or
 + * submodule, else 0. Will not return 1 for bare repositories with the
 + * exception of creating a bare repository in "foo/.git" and calling
 + * is_git_repository("foo").
 + *
 + * If we run into read errors, we err on the side of saying "yes, it is",
 + * as we usually consider sub-repos precious, and would prefer to err on the
 + * side of not disrupting or deleting them.
 + */
 +extern int is_nonbare_repository_dir(struct strbuf *path);
 +
  #define READ_GITFILE_ERR_STAT_FAILED 1
  #define READ_GITFILE_ERR_NOT_A_FILE 2
  #define READ_GITFILE_ERR_OPEN_FAILED 3
@@@ -1321,7 -1299,6 +1321,7 @@@ extern struct packed_git 
                 freshened:1,
                 do_not_close:1;
        unsigned char sha1[20];
 +      struct revindex_entry *revindex;
        /* something like ".git/objects/pack/xxxxx.pack" */
        char pack_name[FLEX_ARRAY]; /* more */
  } *packed_git;
@@@ -1508,8 -1485,8 +1508,8 @@@ struct git_config_source 
  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_buf(config_fn_t fn, const char *name,
-                              const char *buf, size_t len, void *data);
+ extern int git_config_from_mem(config_fn_t fn, const char *origin_type,
+                                       const char *name, const char *buf, size_t len, void *data);
  extern void git_config_push_parameter(const char *text);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern void git_config(config_fn_t fn, void *);
@@@ -1548,6 -1525,8 +1548,8 @@@ extern const char *get_log_output_encod
  extern const char *get_commit_output_encoding(void);
  
  extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
+ extern const char *current_config_origin_type(void);
+ extern const char *current_config_name(void);
  
  struct config_include_data {
        int depth;
@@@ -1626,14 -1605,6 +1628,14 @@@ extern int git_config_get_bool(const ch
  extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
  extern int git_config_get_maybe_bool(const char *key, int *dest);
  extern int git_config_get_pathname(const char *key, const char **dest);
 +extern int git_config_get_untracked_cache(void);
 +
 +/*
 + * This is a hack for test programs like test-dump-untracked-cache to
 + * ensure that they do not modify the untracked cache when reading it.
 + * Do not use it otherwise!
 + */
 +extern int ignore_untracked_cache_config;
  
  struct key_value_info {
        const char *filename;
@@@ -1687,7 -1658,6 +1689,7 @@@ extern int pager_use_color
  extern int term_columns(void);
  extern int decimal_width(uintmax_t);
  extern int check_pager_config(const char *cmd);
 +extern void prepare_pager_args(struct child_process *, const char *pager);
  
  extern const char *editor_program;
  extern const char *askpass_program;
diff --combined config.c
index e7b589a3a847133e6062fb5b2306508542959238,0fce371111ade3f708fd5f522c679bfb008bce46..d732e0b3d830a3c27b05fd8a7ad3e5ebc77a25e0
+++ b/config.c
@@@ -24,6 -24,7 +24,7 @@@ struct config_source 
                        size_t pos;
                } buf;
        } u;
+       const char *origin_type;
        const char *name;
        const char *path;
        int die_on_error;
@@@ -471,9 -472,9 +472,9 @@@ static int git_parse_source(config_fn_
                        break;
        }
        if (cf->die_on_error)
-               die(_("bad config file line %d in %s"), cf->linenr, cf->name);
+               die(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
        else
-               return error(_("bad config file line %d in %s"), cf->linenr, cf->name);
+               return error(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
  }
  
  static int parse_unit_factor(const char *end, uintmax_t *val)
@@@ -588,9 -589,9 +589,9 @@@ static void die_bad_number(const char *
        if (!value)
                value = "";
  
-       if (cf && cf->name)
-               die(_("bad numeric config value '%s' for '%s' in %s: %s"),
-                   value, name, cf->name, reason);
+       if (cf && cf->origin_type && cf->name)
+               die(_("bad numeric config value '%s' for '%s' in %s %s: %s"),
+                   value, name, cf->origin_type, cf->name, reason);
        die(_("bad numeric config value '%s' for '%s': %s"), value, name, reason);
  }
  
@@@ -1061,11 -1062,13 +1062,13 @@@ static int do_config_from(struct config
  }
  
  static int do_config_from_file(config_fn_t fn,
-               const char *name, const char *path, FILE *f, void *data)
+               const char *origin_type, const char *name, const char *path, FILE *f,
+               void *data)
  {
        struct config_source top;
  
        top.u.file = f;
+       top.origin_type = origin_type;
        top.name = name;
        top.path = path;
        top.die_on_error = 1;
  
  static int git_config_from_stdin(config_fn_t fn, void *data)
  {
-       return do_config_from_file(fn, "<stdin>", NULL, stdin, data);
+       return do_config_from_file(fn, "standard input", "", NULL, stdin, data);
  }
  
  int git_config_from_file(config_fn_t fn, const char *filename, void *data)
        f = fopen(filename, "r");
        if (f) {
                flockfile(f);
-               ret = do_config_from_file(fn, filename, filename, f, data);
+               ret = do_config_from_file(fn, "file", filename, filename, f, data);
                funlockfile(f);
                fclose(f);
        }
        return ret;
  }
  
- int git_config_from_buf(config_fn_t fn, const char *name, const char *buf,
-                       size_t len, void *data)
+ int git_config_from_mem(config_fn_t fn, const char *origin_type,
+                       const char *name, const char *buf, size_t len, void *data)
  {
        struct config_source top;
  
        top.u.buf.buf = buf;
        top.u.buf.len = len;
        top.u.buf.pos = 0;
+       top.origin_type = origin_type;
        top.name = name;
        top.path = NULL;
        top.die_on_error = 0;
@@@ -1132,7 -1136,7 +1136,7 @@@ static int git_config_from_blob_sha1(co
                return error("reference '%s' does not point to a blob", name);
        }
  
-       ret = git_config_from_buf(fn, name, buf, size, data);
+       ret = git_config_from_mem(fn, "blob", name, buf, size, data);
        free(buf);
  
        return ret;
@@@ -1594,30 -1598,6 +1598,30 @@@ int git_config_get_pathname(const char 
        return ret;
  }
  
 +int git_config_get_untracked_cache(void)
 +{
 +      int val = -1;
 +      const char *v;
 +
 +      /* Hack for test programs like test-dump-untracked-cache */
 +      if (ignore_untracked_cache_config)
 +              return -1;
 +
 +      if (!git_config_get_maybe_bool("core.untrackedcache", &val))
 +              return val;
 +
 +      if (!git_config_get_value("core.untrackedcache", &v)) {
 +              if (!strcasecmp(v, "keep"))
 +                      return -1;
 +
 +              error("unknown core.untrackedCache value '%s'; "
 +                    "using 'keep' default value", v);
 +              return -1;
 +      }
 +
 +      return -1; /* default value */
 +}
 +
  NORETURN
  void git_die_config_linenr(const char *key, const char *filename, int linenr)
  {
@@@ -1902,7 -1882,7 +1906,7 @@@ static int git_config_parse_key_1(cons
         * Validate the key and while at it, lower case it for matching.
         */
        if (store_key)
 -              *store_key = xmalloc(strlen(key) + 1);
 +              *store_key = xmallocz(strlen(key));
  
        dot = 0;
        for (i = 0; key[i]; i++) {
                if (store_key)
                        (*store_key)[i] = c;
        }
 -      if (store_key)
 -              (*store_key)[i] = 0;
  
        return 0;
  
@@@ -2407,3 -2389,13 +2411,13 @@@ int parse_config_key(const char *var
  
        return 0;
  }
+ const char *current_config_origin_type(void)
+ {
+       return cf && cf->origin_type ? cf->origin_type : "command line";
+ }
+ const char *current_config_name(void)
+ {
+       return cf && cf->name ? cf->name : "";
+ }