Skip to contentSkip to conversation

config: remove global state from config iteration

RFC PATCH Superseded Glen Choo via GitGitGadget 7 participants 115 messages Apr 21, 2023 – Jul 11, 2023 Page 2 of 3

lore
Share & tools
lore
#54 lore raw

v3 cover config: remove global state from config iteration

In reply to Glen Choo via GitGitGadget

Junio: I rebased this onto a newer "master". I assume this is a noop for you - I noticed that the RFC versions weren't applied anyway (good).

Thanks for the thoughtful review on the last round, all. This is the first non-RFC reroll, and hopefully the last ;) This is basically the RFC v2 with the post-RFC cleanups I've been talking about, plus review comments. I did a decent amount of experimentation with Phillip's alternative config_fn_t signatures, and while I think they make sense in the long term (when we add additional helpers, make structs opaque, etc), they introduce a lot of distracting churn but don't really change the status quo, so I've left them out of this series.

A major departure from the RFC is that the large refactoring patches have now been squashed together. If you'd would like to meaningfully range-diff those commits against v2, you can use:

git fetch https://github.com/chooglen/git/ config/no-global-unsquashed-cocci

which uses the same base as v1 and v2.

= Changes since v2

* Rebase onto a newer version of "master"

* To accomodate future changes to config_fn_t, use "const struct config_context *ctx" (which has a .kvi member) instead of directly referencing "kvi".

* Add appropriate comments to config.h.

* .kvi is now const.

* Introduce KVI_INIT as a safer default to "= { 0 }".

* Incorporate wording suggestions

= Description

This series removes all global state from config iteration, i.e. parsing config and iterating configsets, by passing config metadata as a "struct key_value_info" to the "config_fn_t" callbacks. This allows us to get rid of:

* "the_reader" (formerly "cf", "current_parsing_scope", "current_config_kvi"), and the config.c machinery that maintained it. This only needed to be global because it was read by...
* The "current_*" functions that read metadata about the 'current' config value ("current_config_scope", "current_config_name", etc), which are replaced by reading values from the "struct key_value_info" passed to the callbacks.

As a result:

* Config iterating code can be moved to into its own library with few modifications. C.f. libification efforts [1].
* The config iterating code can be safely called in parallel.
* We expose and fix instances where the "current_*" functions were being called outside of config callbacks.

We've had this idea of doing this "config_fn_t" refactor for a long time [2], but we've never attempted it because we wanted to avoid churn. After attempting it, though, I'm quite convinced that this is the right way forward for config, since the lack of global state makes things much easier to reason about. The churn is also quite manageable:

* The vast majority of changes can be handled with cocci.
* The few cases that aren't covered by cocci have obvious fixes.
* The change is simple enough for in-flight topics to perform and conflicts will be caught at build time anyway.

To avoid _future_ churn (if we want to change config_fn_t again), I opted to pass a config_context arg instead of using key_value_info directly. We can make changes we want to that arg while leaving the signature untouched.

= Patch overview

* 1-3/12 add the "ctx" parameter to the config_fn_t signature.

* 4-9/12 converts the config.c machinery off "config_reader.config_kvi" and "config_reader.source" and onto ctx->kvi. We convert the config.c machinery from "config_reader" to "kvi" one-by-one: configsets, then files, then CLI. To exercise the "kvi" arg as soon as possible, we convert from current_*() to "kvi" as soon as it is available. For example, in 4/12 "kvi" is available only in configsets, so we convert the current_*() call sites that are only reached via configsets and leave the others untouched. This means that we have a mix of current_*() and "kvi" in the middle, but auditing the changes is relatively easy, since you only need to verify that a callback isn't relying on the "kvi" arg before it is available, and that current_*() and "kvi" give the same value.

* 6-7/12 squashes some bugs where builtin/config.c was calling the current_*() API outside of config callbacks. The "kvi" plumbing doesn't just make the bugs apparent, it also provides an obvious way to fix the bugs (by injecting "kvi" into the right places in builtin/config.c). These would have been nontrivial to fix if we were still using global state.

* 10-12/12 remove config_reader by taking advantage of "kvi" and doing some other light plumbing.

= Alternatives considered

Ævar suggested in [3] that we might be able to do the refactor incrementally by having both the old "config_fn_t" and the new "config_fn_t with kvi", which lets us convert some of config iterating (e.g. configsets) without touching the others (e.g. config parsing). I experimented with that for a bit, and it turned out that doing it all at once is actually less work because we don't have to worry about the case where the same "config_fn_t" is used in both git_config() and git_config_from_file().

Jonathan Tan suggested in [4] that to reduce churn, we might be able to convert many of the config_fn_t-s to the config set API before attempting this refactor. But (hopefully), these patches show that the churn is manageable even without this preparatory step.

[1]
https://lore.kernel.org/git/CAJoAoZ=Cig_kLocxKGax31sU7Xe4==BGzC__Bg2_pr7krNq6MA@mail.gmail.com/
[2]
https://lore.kernel.org/git/CAPc5daV6bdUKS-ExHmpT4Ppy2S832NXoyPw7aOLP7fG=WrBPgg@mail.gmail.com/
[3]
https://lore.kernel.org/git/[email protected]
[4]
https://lore.kernel.org/git/[email protected]/

Glen Choo (12):
config: inline git_color_default_config urlmatch.h: use config_fn_t type
config: add ctx arg to config_fn_t config.c: pass ctx in configsets
config: pass ctx with config files builtin/config.c: test misuse of format_config() config.c: pass ctx with CLI config
trace2: plumb config kvi
config: pass kvi to die_bad_number() config.c: remove config_reader from configsets
config: add kvi.path, use it to evaluate includes
config: pass source to config_parser_event_fn_t

 alias.c                                       |   3 +-
 archive-tar.c                                 |   5 +-
 archive-zip.c                                 |   1 +
 builtin/add.c                                 |   5 +-
 builtin/blame.c                               |   5 +-
 builtin/branch.c                              |   8 +-
 builtin/cat-file.c                            |   5 +-
 builtin/checkout.c                            |  12 +-
 builtin/clean.c                               |   9 +-
 builtin/clone.c                               |  11 +-
 builtin/column.c                              |   3 +-
 builtin/commit-graph.c                        |   3 +-
 builtin/commit.c                              |  20 +-
 builtin/config.c                              |  72 ++-
 builtin/difftool.c                            |   5 +-
 builtin/fetch.c                               |  13 +-
 builtin/fsmonitor--daemon.c                   |  11 +-
 builtin/grep.c                                |  12 +-
 builtin/help.c                                |   5 +-
 builtin/index-pack.c                          |   9 +-
 builtin/log.c                                 |  12 +-
 builtin/merge.c                               |   7 +-
 builtin/multi-pack-index.c                    |   1 +
 builtin/pack-objects.c                        |  19 +-
 builtin/patch-id.c                            |   5 +-
 builtin/pull.c                                |   5 +-
 builtin/push.c                                |   5 +-
 builtin/read-tree.c                           |   5 +-
 builtin/rebase.c                              |   5 +-
 builtin/receive-pack.c                        |  15 +-
 builtin/reflog.c                              |   7 +-
 builtin/remote.c                              |  15 +-
 builtin/repack.c                              |   5 +-
 builtin/reset.c                               |   5 +-
 builtin/send-pack.c                           |   5 +-
 builtin/show-branch.c                         |   8 +-
 builtin/stash.c                               |   5 +-
 builtin/submodule--helper.c                   |   3 +-
 builtin/tag.c                                 |   9 +-
 builtin/var.c                                 |   5 +-
 builtin/worktree.c                            |   5 +-
 bundle-uri.c                                  |   9 +-
 color.c                                       |   8 -
 color.h                                       |   6 +-
 compat/mingw.c                                |   3 +-
 compat/mingw.h                                |   4 +-
 config.c                                      | 551 +++++++-----------
 config.h                                      |  80 ++-
 connect.c                                     |   4 +-
 .../coccinelle/config_fn_ctx.pending.cocci    | 144 +++++
 contrib/coccinelle/git_config_number.cocci    |  27 +
 convert.c                                     |   4 +-
 credential.c                                  |   1 +
 delta-islands.c                               |   4 +-
 diff.c                                        |  19 +-
 diff.h                                        |   7 +-
 fetch-pack.c                                  |   5 +-
 fmt-merge-msg.c                               |   7 +-
 fmt-merge-msg.h                               |   3 +-
 fsck.c                                        |  12 +-
 fsck.h                                        |   4 +-
 git-compat-util.h                             |   2 +
 gpg-interface.c                               |   7 +-
 grep.c                                        |   7 +-
 grep.h                                        |   4 +-
 help.c                                        |   9 +-
 http.c                                        |  15 +-
 ident.c                                       |   4 +-
 ident.h                                       |   4 +-
 imap-send.c                                   |   7 +-
 ll-merge.c                                    |   1 +
 ls-refs.c                                     |   1 +
 mailinfo.c                                    |   5 +-
 notes-utils.c                                 |   4 +-
 notes.c                                       |   4 +-
 pager.c                                       |   5 +-
 pretty.c                                      |   1 +
 promisor-remote.c                             |   4 +-
 remote.c                                      |   8 +-
 revision.c                                    |   4 +-
 scalar.c                                      |   4 +-
 sequencer.c                                   |  29 +-
 setup.c                                       |  18 +-
 submodule-config.c                            |  31 +-
 submodule-config.h                            |   3 +-
 t/helper/test-config.c                        |  24 +-
 t/helper/test-userdiff.c                      |   4 +-
 t/t1300-config.sh                             |  27 +
 trace2.c                                      |   4 +-
 trace2.h                                      |   3 +-
 trace2/tr2_cfg.c                              |  16 +-
 trace2/tr2_sysenv.c                           |   3 +-
 trace2/tr2_tgt.h                              |   4 +-
 trace2/tr2_tgt_event.c                        |   4 +-
 trace2/tr2_tgt_normal.c                       |   4 +-
 trace2/tr2_tgt_perf.c                         |   4 +-
 trailer.c                                     |   2 +
 upload-pack.c                                 |  18 +-
 urlmatch.c                                    |   7 +-
 urlmatch.h                                    |   8 +-
 worktree.c                                    |   2 +-
 xdiff-interface.c                             |   5 +-
 xdiff-interface.h                             |   4 +-
 103 files changed, 956 insertions(+), 638 deletions(-)
 create mode 100644 contrib/coccinelle/config_fn_ctx.pending.cocci
 create mode 100644 contrib/coccinelle/git_config_number.cocci

base-commit: d7d8841f67f29e6ecbad85a11805c907d0f00d5d
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1497%2Fchooglen%2Fconfig%2Fno-global-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1497/chooglen/config/no-global-v3
Pull-Request: https://github.com/git/git/pull/1497

Range-diff vs v2:

  1:  d5edf7e3fdd =  1:  26109b65142 config: inline git_color_default_config
  2:  821f0b90580 =  2:  1aeffcb1395 urlmatch.h: use config_fn_t type
  3:  6834e37066e <  -:  ----------- (RFC-only) config: add kvi arg to config_fn_t
  4:  bd52c6232ec !  3:  d3eb439aad2 (RFC-only) config: apply cocci to config_fn_t implementations
     @@ Metadata
      Author: Glen Choo <[email protected]>
      
       ## Commit message ##
     -    (RFC-only) config: apply cocci to config_fn_t implementations
     -
     -    Pass "struct key_value_info" to *most* functions that are invoked as
     -    "config_fn_t" callbacks by applying
     -    contrib/coccinelle/config_fn_kvi.pending.cocci. None of the functions
     -    actually use the "kvi" arg yet (besides propagating it to a function
     -    that now expects "kvi"), but this will be addressed in a later commit.
     -    When deciding whether or not to propagate "kvi" to an inner function,
     -    only propagate the "kvi" arg if the inner function is actually invoked
     -    elsewhere as a config callback; it does not matter whether the function
     -    happens have the same signature as config_fn_t.
     -
     -    This commit does not build and has several style issues (e.g. a lack of
     -    spacing around the "kvi" arg), but I've split this out for the RFC so
     -    that it's more obvious which changes are automatic vs manual. Post-RFC I
     -    will squash this with the rest of the refactor + cocci changes.
     +    config: add ctx arg to config_fn_t
     +
     +    Add a new "const struct config_context *ctx" arg to config_fn_t to hold
     +    additional information about the config iteration operation.
     +    config_context has a "struct key_value_info kvi" member that holds
     +    metadata about the config source being read (e.g. what kind of config
     +    source it is, the filename, etc). In this series, we're only interested
     +    in .kvi, so we could have just used "struct key_value_info" as an arg,
     +    but config_context makes it possible to add/adjust members in the future
     +    without changing the config_fn_t signature. We could also consider other
     +    ways of organizing the args (e.g. moving the config name and value into
     +    config_context or key_value_info), but in my experiments, the
     +    incremental benefit doesn't justify the added complexity (e.g. a
     +    config_fn_t will sometimes invoke another config_fn_t but with a
     +    different config value).
     +
     +    In subsequent commits, the .kvi member will replace the global "struct
     +    config_reader" in config.c, making config iteration a global-free
     +    operation. It requires much more work for the machinery to provide
     +    meaningful values of .kvi, so for now, merely change the signature and
     +    call sites, pass NULL as a placeholder value, and don't rely on the arg
     +    in any meaningful way.
     +
     +    Most of the changes are performed by
     +    contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every
     +    config_fn_t:
     +
     +    - Modifies the signature to accept "const struct config_context *ctx"
     +    - Passes "ctx" to any inner config_fn_t, if needed
     +    - Adds UNUSED attributes to "ctx", if needed
     +
     +    Most config_fn_t instances are easily identified by seeing if they are
     +    called by the various config functions. Most of the remaining ones are
     +    manually named in the .cocci patch. Manual cleanups are still needed,
     +    but the majority of it is trivial; it's either adjusting config_fn_t
     +    that the .cocci patch didn't catch, or adding forward declarations of
     +    "struct config_context ctx" to make the signatures make sense.
     +
     +    The non-trivial changes are in cases where we are invoking a config_fn_t
     +    outside of config machinery, and we now need to decide what value of
     +    "ctx" to pass. These cases are:
     +
     +    - trace2/tr2_cfg.c:tr2_cfg_set_fl()
     +
     +      This is indirectly called by git_config_set() so that the trace2
     +      machinery can notice the new config values and update its settings
     +      using the tr2 config parsing function, i.e. tr2_cfg_cb().
     +
     +    - builtin/checkout.c:checkout_main()
     +
     +      This calls git_xmerge_config() as a shorthand for parsing a CLI arg.
     +      This might be worth refactoring away in the future, since
     +      git_xmerge_config() can call git_default_config(), which can do much
     +      more than just parsing.
     +
     +    Handle them by creating a KVI_INIT macro that initializes "struct
     +    key_value_info" to a reasonable default, and use that to construct the
     +    "ctx" arg.
      
          Signed-off-by: Glen Choo <[email protected]>
      
     @@ alias.c: struct config_alias_data {
       
      -static int config_alias_cb(const char *key, const char *value, void *d)
      +static int config_alias_cb(const char *key, const char *value,
     -+			   struct key_value_info *kvi UNUSED, void *d)
     ++			   const struct config_context *ctx UNUSED, void *d)
       {
       	struct config_alias_data *data = d;
       	const char *p;
     @@ archive-tar.c: static int tar_filter_config(const char *var, const char *value,
       
      -static int git_tar_config(const char *var, const char *value, void *cb)
      +static int git_tar_config(const char *var, const char *value,
     -+			  struct key_value_info *kvi UNUSED, void *cb)
     ++			  const struct config_context *ctx UNUSED, void *cb)
       {
       	if (!strcmp(var, "tar.umask")) {
       		if (value && !strcmp(value, "user")) {
     @@ archive-zip.c: static void dos_time(timestamp_t *timestamp, int *dos_date, int *
       }
       
       static int archive_zip_config(const char *var, const char *value,
     -+			      struct key_value_info *kvi UNUSED,
     ++			      const struct config_context *ctx UNUSED,
       			      void *data UNUSED)
       {
       	return userdiff_config(var, value);
     @@ builtin/add.c: static struct option builtin_add_options[] = {
       
      -static int add_config(const char *var, const char *value, void *cb)
      +static int add_config(const char *var, const char *value,
     -+		      struct key_value_info *kvi, void *cb)
     ++		      const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "add.ignoreerrors") ||
       	    !strcmp(var, "add.ignore-errors")) {
     @@ builtin/add.c: static int add_config(const char *var, const char *value, void *c
       	}
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static const char embedded_advice[] = N_(
     @@ builtin/blame.c: static const char *add_prefix(const char *prefix, const char *p
       
      -static int git_blame_config(const char *var, const char *value, void *cb)
      +static int git_blame_config(const char *var, const char *value,
     -+			    struct key_value_info *kvi, void *cb)
     ++			    const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "blame.showroot")) {
       		show_root = git_config_bool(var, value);
     @@ builtin/blame.c: static int git_blame_config(const char *var, const char *value,
       		return -1;
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static int blame_copy_callback(const struct option *option, const char *arg, int unset)
     @@ builtin/branch.c: static unsigned int colopts;
       
      -static int git_branch_config(const char *var, const char *value, void *cb)
      +static int git_branch_config(const char *var, const char *value,
     -+			     struct key_value_info *kvi, void *cb)
     ++			     const struct config_context *ctx, void *cb)
       {
       	const char *slot_name;
       
     @@ builtin/branch.c: static int git_branch_config(const char *var, const char *valu
       		return -1;
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static const char *branch_get_color(enum color_branch ix)
     @@ builtin/cat-file.c: static int batch_objects(struct batch_options *opt)
       
      -static int git_cat_file_config(const char *var, const char *value, void *cb)
      +static int git_cat_file_config(const char *var, const char *value,
     -+			       struct key_value_info *kvi, void *cb)
     ++			       const struct config_context *ctx, void *cb)
       {
       	if (userdiff_config(var, value) < 0)
       		return -1;
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static int batch_option_callback(const struct option *opt,
     @@ builtin/checkout.c: static int switch_branches(const struct checkout_opts *opts,
       
      -static int git_checkout_config(const char *var, const char *value, void *cb)
      +static int git_checkout_config(const char *var, const char *value,
     -+			       struct key_value_info *kvi, void *cb)
     ++			       const struct config_context *ctx, void *cb)
       {
       	struct checkout_opts *opts = cb;
       
     @@ builtin/checkout.c: static int git_checkout_config(const char *var, const char *
       		return git_default_submodule_config(var, value, NULL);
       
      -	return git_xmerge_config(var, value, NULL);
     -+	return git_xmerge_config(var, value,kvi, NULL);
     ++	return git_xmerge_config(var, value, ctx, NULL);
       }
       
       static void setup_new_branch_info_and_source_tree(
     +@@ builtin/checkout.c: static int checkout_main(int argc, const char **argv, const char *prefix,
     + 	}
     + 
     + 	if (opts->conflict_style) {
     ++		struct key_value_info kvi = KVI_INIT;
     ++		struct config_context ctx = {
     ++			.kvi = &kvi,
     ++		};
     + 		opts->merge = 1; /* implied */
     +-		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
     ++		git_xmerge_config("merge.conflictstyle", opts->conflict_style,
     ++				  &ctx, NULL);
     + 	}
     + 	if (opts->force) {
     + 		opts->discard_changes = 1;
      
       ## builtin/clean.c ##
      @@ builtin/clean.c: struct menu_stuff {
     @@ builtin/clean.c: struct menu_stuff {
       
      -static int git_clean_config(const char *var, const char *value, void *cb)
      +static int git_clean_config(const char *var, const char *value,
     -+			    struct key_value_info *kvi, void *cb)
     ++			    const struct config_context *ctx, void *cb)
       {
       	const char *slot_name;
       
     @@ builtin/clean.c: static int git_clean_config(const char *var, const char *value,
       		return -1;
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static const char *clean_get_color(enum color_clean ix)
     @@ builtin/clone.c: static int checkout(int submodule_progress, int filter_submodul
       
      -static int git_clone_config(const char *k, const char *v, void *cb)
      +static int git_clone_config(const char *k, const char *v,
     -+			    struct key_value_info *kvi, void *cb)
     ++			    const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(k, "clone.defaultremotename")) {
       		free(remote_name);
     @@ builtin/clone.c: static int git_clone_config(const char *k, const char *v, void
       		config_filter_submodules = git_config_bool(k, v);
       
      -	return git_default_config(k, v, cb);
     -+	return git_default_config(k, v,kvi, cb);
     ++	return git_default_config(k, v, ctx, cb);
       }
       
      -static int write_one_config(const char *key, const char *value, void *data)
      +static int write_one_config(const char *key, const char *value,
     -+			    struct key_value_info *kvi UNUSED, void *data)
     ++			    const struct config_context *ctx,
     ++			    void *data)
       {
       	/*
       	 * give git_clone_config a chance to write config values back to the
     + 	 * environment, since git_config_set_multivar_gently only deals with
     + 	 * config-file writes
     + 	 */
     +-	int apply_failed = git_clone_config(key, value, data);
     ++	int apply_failed = git_clone_config(key, value, ctx, data);
     + 	if (apply_failed)
     + 		return apply_failed;
     + 
      
       ## builtin/column.c ##
      @@ builtin/column.c: static const char * const builtin_column_usage[] = {
     @@ builtin/column.c: static const char * const builtin_column_usage[] = {
       
      -static int column_config(const char *var, const char *value, void *cb)
      +static int column_config(const char *var, const char *value,
     -+			 struct key_value_info *kvi UNUSED, void *cb)
     ++			 const struct config_context *ctx UNUSED, void *cb)
       {
       	return git_column_config(var, value, cb, &colopts);
       }
     @@ builtin/commit-graph.c: static int write_option_max_new_filters(const struct opt
       }
       
       static int git_commit_graph_write_config(const char *var, const char *value,
     -+					 struct key_value_info *kvi UNUSED,
     ++					 const struct config_context *ctx UNUSED,
       					 void *cb UNUSED)
       {
       	if (!strcmp(var, "commitgraph.maxnewfilters"))
     @@ builtin/commit.c: static int parse_status_slot(const char *slot)
       
      -static int git_status_config(const char *k, const char *v, void *cb)
      +static int git_status_config(const char *k, const char *v,
     -+			     struct key_value_info *kvi, void *cb)
     ++			     const struct config_context *ctx, void *cb)
       {
       	struct wt_status *s = cb;
       	const char *slot_name;
     @@ builtin/commit.c: static int git_status_config(const char *k, const char *v, voi
       		return 0;
       	}
      -	return git_diff_ui_config(k, v, NULL);
     -+	return git_diff_ui_config(k, v,kvi, NULL);
     ++	return git_diff_ui_config(k, v, ctx, NULL);
       }
       
       int cmd_status(int argc, const char **argv, const char *prefix)
     @@ builtin/commit.c: int cmd_status(int argc, const char **argv, const char *prefix
       
      -static int git_commit_config(const char *k, const char *v, void *cb)
      +static int git_commit_config(const char *k, const char *v,
     -+			     struct key_value_info *kvi, void *cb)
     ++			     const struct config_context *ctx, void *cb)
       {
       	struct wt_status *s = cb;
       
     @@ builtin/commit.c: static int git_commit_config(const char *k, const char *v, voi
       	}
       
      -	return git_status_config(k, v, s);
     -+	return git_status_config(k, v,kvi, s);
     ++	return git_status_config(k, v, ctx, s);
       }
       
       int cmd_commit(int argc, const char **argv, const char *prefix)
     @@ builtin/config.c: static void show_config_scope(struct strbuf *buf)
       }
       
       static int show_all_config(const char *key_, const char *value_,
     --			   void *cb UNUSED)
     -+			   struct key_value_info *kvi UNUSED, void *cb UNUSED)
     ++			   const struct config_context *ctx UNUSED,
     + 			   void *cb UNUSED)
       {
       	if (show_origin || show_scope) {
     - 		struct strbuf buf = STRBUF_INIT;
      @@ builtin/config.c: static int format_config(struct strbuf *buf, const char *key_, const char *value
       	return 0;
       }
       
      -static int collect_config(const char *key_, const char *value_, void *cb)
      +static int collect_config(const char *key_, const char *value_,
     -+			  struct key_value_info *kvi UNUSED, void *cb)
     ++			  const struct config_context *ctx UNUSED, void *cb)
       {
       	struct strbuf_list *values = cb;
       
     @@ builtin/config.c: static const char *get_colorbool_slot;
       static char parsed_color[COLOR_MAXLEN];
       
       static int git_get_color_config(const char *var, const char *value,
     -+				struct key_value_info *kvi UNUSED,
     ++				const struct config_context *ctx UNUSED,
       				void *cb UNUSED)
       {
       	if (!strcmp(var, get_color_slot)) {
     @@ builtin/config.c: static int get_colorbool_found;
       static int get_diff_color_found;
       static int get_color_ui_found;
       static int git_get_colorbool_config(const char *var, const char *value,
     -+				    struct key_value_info *kvi UNUSED,
     ++				    const struct config_context *ctx UNUSED,
       				    void *data UNUSED)
       {
       	if (!strcmp(var, get_colorbool_slot))
     @@ builtin/config.c: struct urlmatch_current_candidate_value {
       
      -static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
      +static int urlmatch_collect_fn(const char *var, const char *value,
     -+			       struct key_value_info *kvi UNUSED, void *cb)
     ++			       const struct config_context *ctx UNUSED,
     ++			       void *cb)
       {
       	struct string_list *values = cb;
       	struct string_list_item *item = string_list_insert(values, var);
     @@ builtin/difftool.c: static const char *const builtin_difftool_usage[] = {
       
      -static int difftool_config(const char *var, const char *value, void *cb)
      +static int difftool_config(const char *var, const char *value,
     -+			   struct key_value_info *kvi, void *cb)
     ++			   const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "difftool.trustexitcode")) {
       		trust_exit_code = git_config_bool(var, value);
     @@ builtin/difftool.c: static const char *const builtin_difftool_usage[] = {
       	}
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static int print_tool_help(void)
      
       ## builtin/fetch.c ##
     -@@ builtin/fetch.c: static int fetch_write_commit_graph = -1;
     - static int stdin_refspecs = 0;
     - static int negotiate_only;
     +@@ builtin/fetch.c: struct fetch_config {
     + 	enum display_format display_format;
     + };
       
      -static int git_fetch_config(const char *k, const char *v, void *cb)
      +static int git_fetch_config(const char *k, const char *v,
     -+			    struct key_value_info *kvi, void *cb)
     ++			    const struct config_context *ctx, void *cb)
       {
     - 	if (!strcmp(k, "fetch.prune")) {
     - 		fetch_prune_config = git_config_bool(k, v);
     + 	struct fetch_config *fetch_config = cb;
     + 
      @@ builtin/fetch.c: static int git_fetch_config(const char *k, const char *v, void *cb)
     - 		return 0;
     + 			    "fetch.output", v);
       	}
       
      -	return git_default_config(k, v, cb);
     -+	return git_default_config(k, v,kvi, cb);
     ++	return git_default_config(k, v, ctx, cb);
       }
       
       static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
     @@ builtin/fetch.c: struct remote_group_data {
       
      -static int get_remote_group(const char *key, const char *value, void *priv)
      +static int get_remote_group(const char *key, const char *value,
     -+			    struct key_value_info *kvi UNUSED, void *priv)
     ++			    const struct config_context *ctx UNUSED,
     ++			    void *priv)
       {
       	struct remote_group_data *g = priv;
       
     @@ builtin/fsmonitor--daemon.c: static int fsmonitor__start_timeout_sec = 60;
       
      -static int fsmonitor_config(const char *var, const char *value, void *cb)
      +static int fsmonitor_config(const char *var, const char *value,
     -+			    struct key_value_info *kvi, void *cb)
     ++			    const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, FSMONITOR__IPC_THREADS)) {
       		int i = git_config_int(var, value);
     @@ builtin/fsmonitor--daemon.c: static int fsmonitor_config(const char *var, const
       	}
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       /*
     @@ builtin/grep.c: static int wait_all(void)
       
      -static int grep_cmd_config(const char *var, const char *value, void *cb)
      +static int grep_cmd_config(const char *var, const char *value,
     -+			   struct key_value_info *kvi, void *cb)
     ++			   const struct config_context *ctx, void *cb)
       {
     - 	int st = grep_config(var, value, cb);
     +-	int st = grep_config(var, value, cb);
     ++	int st = grep_config(var, value, ctx, cb);
       
       	if (git_color_config(var, value, cb) < 0)
       		st = -1;
      -	else if (git_default_config(var, value, cb) < 0)
     -+	else if (git_default_config(var, value,kvi, cb) < 0)
     ++	else if (git_default_config(var, value, ctx, cb) < 0)
       		st = -1;
       
       	if (!strcmp(var, "grep.threads")) {
     @@ builtin/help.c: static int add_man_viewer_info(const char *var, const char *valu
       
      -static int git_help_config(const char *var, const char *value, void *cb)
      +static int git_help_config(const char *var, const char *value,
     -+			   struct key_value_info *kvi, void *cb)
     ++			   const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "help.format")) {
       		if (!value)
     @@ builtin/help.c: static int git_help_config(const char *var, const char *value, v
       		return add_man_viewer_info(var, value);
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static struct cmdnames main_cmds, other_cmds;
     @@ builtin/index-pack.c: static void final(const char *final_pack_name, const char
       
      -static int git_index_pack_config(const char *k, const char *v, void *cb)
      +static int git_index_pack_config(const char *k, const char *v,
     -+				 struct key_value_info *kvi, void *cb)
     ++				 const struct config_context *ctx, void *cb)
       {
       	struct pack_idx_option *opts = cb;
       
     @@ builtin/index-pack.c: static int git_index_pack_config(const char *k, const char
       			opts->flags &= ~WRITE_REV;
       	}
      -	return git_default_config(k, v, cb);
     -+	return git_default_config(k, v,kvi, cb);
     ++	return git_default_config(k, v, ctx, cb);
       }
       
       static int cmp_uint32(const void *a_, const void *b_)
     @@ builtin/log.c: static int cmd_log_walk(struct rev_info *rev)
       
      -static int git_log_config(const char *var, const char *value, void *cb)
      +static int git_log_config(const char *var, const char *value,
     -+			  struct key_value_info *kvi, void *cb)
     ++			  const struct config_context *ctx, void *cb)
       {
       	const char *slot_name;
       
     @@ builtin/log.c: static int git_log_config(const char *var, const char *value, voi
       	}
       
      -	return git_diff_ui_config(var, value, cb);
     -+	return git_diff_ui_config(var, value,kvi, cb);
     ++	return git_diff_ui_config(var, value, ctx, cb);
       }
       
       int cmd_whatchanged(int argc, const char **argv, const char *prefix)
     @@ builtin/log.c: static enum cover_from_description parse_cover_from_description(c
       
      -static int git_format_config(const char *var, const char *value, void *cb)
      +static int git_format_config(const char *var, const char *value,
     -+			     struct key_value_info *kvi, void *cb)
     ++			     const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "format.headers")) {
       		if (!value)
     @@ builtin/log.c: static int git_format_config(const char *var, const char *value,
       		return 0;
       
      -	return git_log_config(var, value, cb);
     -+	return git_log_config(var, value,kvi, cb);
     ++	return git_log_config(var, value, ctx, cb);
       }
       
       static const char *output_directory = NULL;
     @@ builtin/merge.c: static void parse_branch_merge_options(char *bmo)
       
      -static int git_merge_config(const char *k, const char *v, void *cb)
      +static int git_merge_config(const char *k, const char *v,
     -+			    struct key_value_info *kvi, void *cb)
     ++			    const struct config_context *ctx, void *cb)
       {
       	int status;
       	const char *str;
     @@ builtin/merge.c: static int git_merge_config(const char *k, const char *v, void
       	}
       
      -	status = fmt_merge_msg_config(k, v, cb);
     -+	status = fmt_merge_msg_config(k, v,kvi, cb);
     ++	status = fmt_merge_msg_config(k, v, ctx, cb);
       	if (status)
       		return status;
      -	return git_diff_ui_config(k, v, cb);
     -+	return git_diff_ui_config(k, v,kvi, cb);
     ++	return git_diff_ui_config(k, v, ctx, cb);
       }
       
       static int read_tree_trivial(struct object_id *common, struct object_id *head,
     @@ builtin/multi-pack-index.c: static struct option *add_common_options(struct opti
       }
       
       static int git_multi_pack_index_write_config(const char *var, const char *value,
     -+					     struct key_value_info *kvi UNUSED,
     ++					     const struct config_context *ctx UNUSED,
       					     void *cb UNUSED)
       {
       	if (!strcmp(var, "pack.writebitmaphashcache")) {
     @@ builtin/pack-objects.c: static void prepare_pack(int window, int depth)
       
      -static int git_pack_config(const char *k, const char *v, void *cb)
      +static int git_pack_config(const char *k, const char *v,
     -+			   struct key_value_info *kvi, void *cb)
     ++			   const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(k, "pack.window")) {
       		window = git_config_int(k, v);
     @@ builtin/pack-objects.c: static int git_pack_config(const char *k, const char *v,
       		oidmap_put(&configured_exclusions, ex);
       	}
      -	return git_default_config(k, v, cb);
     -+	return git_default_config(k, v,kvi, cb);
     ++	return git_default_config(k, v, ctx, cb);
       }
       
       /* Counters for trace2 output when in --stdin-packs mode. */
     @@ builtin/patch-id.c: struct patch_id_opts {
       
      -static int git_patch_id_config(const char *var, const char *value, void *cb)
      +static int git_patch_id_config(const char *var, const char *value,
     -+			       struct key_value_info *kvi, void *cb)
     ++			       const struct config_context *ctx, void *cb)
       {
       	struct patch_id_opts *opts = cb;
       
     @@ builtin/patch-id.c: static int git_patch_id_config(const char *var, const char *
       	}
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       int cmd_patch_id(int argc, const char **argv, const char *prefix)
     @@ builtin/pull.c: static enum rebase_type config_get_rebase(int *rebase_unspecifie
        */
      -static int git_pull_config(const char *var, const char *value, void *cb)
      +static int git_pull_config(const char *var, const char *value,
     -+			   struct key_value_info *kvi, void *cb)
     ++			   const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "rebase.autostash")) {
       		config_autostash = git_config_bool(var, value);
     @@ builtin/pull.c: static int git_pull_config(const char *var, const char *value, v
       	}
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       /**
     @@ builtin/push.c: static void set_push_cert_flags(int *flags, int v)
       
      -static int git_push_config(const char *k, const char *v, void *cb)
      +static int git_push_config(const char *k, const char *v,
     -+			   struct key_value_info *kvi, void *cb)
     ++			   const struct config_context *ctx, void *cb)
       {
       	const char *slot_name;
       	int *flags = cb;
     @@ builtin/push.c: static int git_push_config(const char *k, const char *v, void *c
       	}
       
      -	return git_default_config(k, v, NULL);
     -+	return git_default_config(k, v,kvi, NULL);
     ++	return git_default_config(k, v, ctx, NULL);
       }
       
       int cmd_push(int argc, const char **argv, const char *prefix)
     @@ builtin/read-tree.c: static int debug_merge(const struct cache_entry * const *st
       
      -static int git_read_tree_config(const char *var, const char *value, void *cb)
      +static int git_read_tree_config(const char *var, const char *value,
     -+				struct key_value_info *kvi, void *cb)
     ++				const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "submodule.recurse"))
       		return git_default_submodule_config(var, value, cb);
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
     @@ builtin/rebase.c: static void parse_rebase_merges_value(struct rebase_options *o
       
      -static int rebase_config(const char *var, const char *value, void *data)
      +static int rebase_config(const char *var, const char *value,
     -+			 struct key_value_info *kvi, void *data)
     ++			 const struct config_context *ctx, void *data)
       {
       	struct rebase_options *opts = data;
       
     @@ builtin/rebase.c: static int rebase_config(const char *var, const char *value, v
       	}
       
      -	return git_default_config(var, value, data);
     -+	return git_default_config(var, value,kvi, data);
     ++	return git_default_config(var, value, ctx, data);
       }
       
       static int checkout_up_to_date(struct rebase_options *options)
     @@ builtin/receive-pack.c: static enum deny_action parse_deny_action(const char *va
       
      -static int receive_pack_config(const char *var, const char *value, void *cb)
      +static int receive_pack_config(const char *var, const char *value,
     -+			       struct key_value_info *kvi, void *cb)
     ++			       const struct config_context *ctx, void *cb)
       {
       	int status = parse_hide_refs_config(var, value, "receive", &hidden_refs);
       
     @@ builtin/receive-pack.c: static int receive_pack_config(const char *var, const ch
       	}
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static void show_ref(const char *path, const struct object_id *oid)
     @@ builtin/reflog.c: static struct reflog_expire_cfg *find_cfg_ent(const char *patt
       
      -static int reflog_expire_config(const char *var, const char *value, void *cb)
      +static int reflog_expire_config(const char *var, const char *value,
     -+				struct key_value_info *kvi, void *cb)
     ++				const struct config_context *ctx, void *cb)
       {
       	const char *pattern, *key;
       	size_t pattern_len;
     @@ builtin/reflog.c: static int reflog_expire_config(const char *var, const char *v
       
       	if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
      -		return git_default_config(var, value, cb);
     -+		return git_default_config(var, value,kvi, cb);
     ++		return git_default_config(var, value, ctx, cb);
       
       	if (!strcmp(key, "reflogexpire")) {
       		slot = EXPIRE_TOTAL;
     @@ builtin/reflog.c: static int reflog_expire_config(const char *var, const char *v
       			return -1;
       	} else
      -		return git_default_config(var, value, cb);
     -+		return git_default_config(var, value,kvi, cb);
     ++		return git_default_config(var, value, ctx, cb);
       
       	if (!pattern) {
       		switch (slot) {
     @@ builtin/remote.c: static const char *abbrev_ref(const char *name, const char *pr
       #define abbrev_branch(name) abbrev_ref((name), "refs/heads/")
       
       static int config_read_branches(const char *key, const char *value,
     -+				struct key_value_info *kvi UNUSED,
     ++				const struct config_context *ctx UNUSED,
       				void *data UNUSED)
       {
       	const char *orig_key = key;
     @@ builtin/remote.c: struct push_default_info
       
       static int config_read_push_default(const char *key, const char *value,
      -	void *cb)
     -+	struct key_value_info *kvi UNUSED, void *cb)
     ++	const struct config_context *ctx UNUSED, void *cb)
       {
       	struct push_default_info* info = cb;
       	if (strcmp(key, "remote.pushdefault") ||
     @@ builtin/remote.c: static int prune(int argc, const char **argv, const char *pref
       
      -static int get_remote_default(const char *key, const char *value UNUSED, void *priv)
      +static int get_remote_default(const char *key, const char *value UNUSED,
     -+			      struct key_value_info *kvi UNUSED, void *priv)
     ++			      const struct config_context *ctx UNUSED,
     ++			      void *priv)
       {
       	if (strcmp(key, "remotes.default") == 0) {
       		int *found = priv;
     @@ builtin/repack.c: struct pack_objects_args {
       
      -static int repack_config(const char *var, const char *value, void *cb)
      +static int repack_config(const char *var, const char *value,
     -+			 struct key_value_info *kvi, void *cb)
     ++			 const struct config_context *ctx, void *cb)
       {
       	struct pack_objects_args *cruft_po_args = cb;
       	if (!strcmp(var, "repack.usedeltabaseoffset")) {
     @@ builtin/repack.c: static int repack_config(const char *var, const char *value, v
       	if (!strcmp(var, "repack.cruftthreads"))
       		return git_config_string(&cruft_po_args->threads, var, value);
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       /*
     @@ builtin/reset.c: static int reset_refs(const char *rev, const struct object_id *
       
      -static int git_reset_config(const char *var, const char *value, void *cb)
      +static int git_reset_config(const char *var, const char *value,
     -+			    struct key_value_info *kvi, void *cb)
     ++			    const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "submodule.recurse"))
       		return git_default_submodule_config(var, value, cb);
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       int cmd_reset(int argc, const char **argv, const char *prefix)
     @@ builtin/send-pack.c: static void print_helper_status(struct ref *ref)
       
      -static int send_pack_config(const char *k, const char *v, void *cb)
      +static int send_pack_config(const char *k, const char *v,
     -+			    struct key_value_info *kvi, void *cb)
     ++			    const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(k, "push.gpgsign")) {
       		const char *value;
     @@ builtin/send-pack.c: static int send_pack_config(const char *k, const char *v, v
       		}
       	}
      -	return git_default_config(k, v, cb);
     -+	return git_default_config(k, v,kvi, cb);
     ++	return git_default_config(k, v, ctx, cb);
       }
       
       int cmd_send_pack(int argc, const char **argv, const char *prefix)
     @@ builtin/show-branch.c: static void append_one_rev(const char *av)
       
      -static int git_show_branch_config(const char *var, const char *value, void *cb)
      +static int git_show_branch_config(const char *var, const char *value,
     -+				  struct key_value_info *kvi, void *cb)
     ++				  const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "showbranch.default")) {
       		if (!value)
     @@ builtin/show-branch.c: static int git_show_branch_config(const char *var, const
       		return -1;
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
     @@ builtin/stash.c: static int show_stat = 1;
       
      -static int git_stash_config(const char *var, const char *value, void *cb)
      +static int git_stash_config(const char *var, const char *value,
     -+			    struct key_value_info *kvi, void *cb)
     ++			    const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "stash.showstat")) {
       		show_stat = git_config_bool(var, value);
     @@ builtin/stash.c: static int git_stash_config(const char *var, const char *value,
       		return 0;
       	}
      -	return git_diff_basic_config(var, value, cb);
     -+	return git_diff_basic_config(var, value,kvi, cb);
     ++	return git_diff_basic_config(var, value, ctx, cb);
       }
       
       static void diff_include_untracked(const struct stash_info *info, struct diff_options *diff_opt)
     @@ builtin/submodule--helper.c: static int update_clone_task_finished(int result,
       }
       
       static int git_update_clone_config(const char *var, const char *value,
     -+				   struct key_value_info *kvi UNUSED,
     ++				   const struct config_context *ctx UNUSED,
       				   void *cb)
       {
       	int *max_jobs = cb;
     @@ builtin/tag.c: static const char tag_template_nocleanup[] =
       
      -static int git_tag_config(const char *var, const char *value, void *cb)
      +static int git_tag_config(const char *var, const char *value,
     -+			  struct key_value_info *kvi, void *cb)
     ++			  const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "tag.gpgsign")) {
       		config_sign_tag = git_config_bool(var, value);
     @@ builtin/tag.c: static int git_tag_config(const char *var, const char *value, voi
       		return -1;
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static void write_tag_body(int fd, const struct object_id *oid)
     @@ builtin/var.c: static const struct git_var *get_git_var(const char *var)
       
      -static int show_config(const char *var, const char *value, void *cb)
      +static int show_config(const char *var, const char *value,
     -+		       struct key_value_info *kvi, void *cb)
     ++		       const struct config_context *ctx, void *cb)
       {
       	if (value)
       		printf("%s=%s\n", var, value);
       	else
       		printf("%s\n", var);
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       int cmd_var(int argc, const char **argv, const char *prefix UNUSED)
     @@ builtin/worktree.c: static int verbose;
       
      -static int git_worktree_config(const char *var, const char *value, void *cb)
      +static int git_worktree_config(const char *var, const char *value,
     -+			       struct key_value_info *kvi, void *cb)
     ++			       const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "worktree.guessremote")) {
       		guess_remote = git_config_bool(var, value);
     @@ builtin/worktree.c: static int verbose;
       	}
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static int delete_git_dir(const char *id)
     @@ bundle-uri.c: static int bundle_list_update(const char *key, const char *value,
       
      -static int config_to_bundle_list(const char *key, const char *value, void *data)
      +static int config_to_bundle_list(const char *key, const char *value,
     -+				 struct key_value_info *kvi UNUSED,
     ++				 const struct config_context *ctx UNUSED,
      +				 void *data)
       {
       	struct bundle_list *list = data;
     @@ bundle-uri.c: cached:
       
      -static int config_to_packet_line(const char *key, const char *value, void *data)
      +static int config_to_packet_line(const char *key, const char *value,
     -+				 struct key_value_info *kvi UNUSED,
     ++				 const struct config_context *ctx UNUSED,
      +				 void *data)
       {
       	struct packet_reader *writer = data;
       
      
     + ## compat/mingw.c ##
     +@@ compat/mingw.c: static int core_restrict_inherited_handles = -1;
     + static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
     + static char *unset_environment_variables;
     + 
     +-int mingw_core_config(const char *var, const char *value, void *cb)
     ++int mingw_core_config(const char *var, const char *value,
     ++		      const struct config_context *ctx, void *cb)
     + {
     + 	if (!strcmp(var, "core.hidedotfiles")) {
     + 		if (value && !strcasecmp(value, "dotgitonly"))
     +
     + ## compat/mingw.h ##
     +@@ compat/mingw.h: typedef _sigset_t sigset_t;
     + #undef _POSIX_THREAD_SAFE_FUNCTIONS
     + #endif
     + 
     +-int mingw_core_config(const char *var, const char *value, void *cb);
     ++struct config_context;
     ++int mingw_core_config(const char *var, const char *value,
     ++		      const struct config_context *ctx, void *cb);
     + #define platform_core_config mingw_core_config
     + 
     + /*
     +
       ## config.c ##
      @@ config.c: struct config_include_data {
       };
     @@ config.c: struct config_include_data {
       
      -static int git_config_include(const char *var, const char *value, void *data);
      +static int git_config_include(const char *var, const char *value,
     -+			      struct key_value_info *kvi, void *data);
     ++			      const struct config_context *ctx, void *data);
       
       #define MAX_INCLUDE_DEPTH 10
       static const char include_depth_advice[] = N_(
     @@ config.c: static int include_by_branch(const char *cond, size_t cond_len)
       
      -static int add_remote_url(const char *var, const char *value, void *data)
      +static int add_remote_url(const char *var, const char *value,
     -+			  struct key_value_info *kvi UNUSED, void *data)
     ++			  const struct config_context *ctx UNUSED, void *data)
       {
       	struct string_list *remote_urls = data;
       	const char *remote_name;
     @@ config.c: static void populate_remote_urls(struct config_include_data *inc)
       }
       
       static int forbid_remote_url(const char *var, const char *value UNUSED,
     -+			     struct key_value_info *kvi UNUSED,
     ++			     const struct config_context *ctx UNUSED,
       			     void *data UNUSED)
       {
       	const char *remote_name;
     @@ config.c: static int include_condition_is_true(struct config_source *cs,
       
      -static int git_config_include(const char *var, const char *value, void *data)
      +static int git_config_include(const char *var, const char *value,
     -+			      struct key_value_info *kvi UNUSED, void *data)
     ++			      const struct config_context *ctx,
     ++			      void *data)
       {
       	struct config_include_data *inc = data;
       	struct config_source *cs = inc->config_reader->source;
     +@@ config.c: static int git_config_include(const char *var, const char *value, void *data)
     + 	 * 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);
     ++	ret = inc->fn(var, value, NULL, inc->data);
     + 	if (ret < 0)
     + 		return ret;
     + 
     +@@ config.c: static int config_parse_pair(const char *key, const char *value,
     + 	if (git_config_parse_key(key, &canonical_name, NULL))
     + 		return -1;
     + 
     +-	ret = (fn(canonical_name, value, data) < 0) ? -1 : 0;
     ++	ret = (fn(canonical_name, value, NULL, data) < 0) ? -1 : 0;
     + 	free(canonical_name);
     + 	return ret;
     + }
     +@@ config.c: static int get_value(struct config_source *cs, config_fn_t fn, void *data,
     + 	 * accurate line number in error messages.
     + 	 */
     + 	cs->linenr--;
     +-	ret = fn(name->buf, value, data);
     ++	ret = fn(name->buf, value, NULL, data);
     + 	if (ret >= 0)
     + 		cs->linenr++;
     + 	return ret;
      @@ config.c: int git_config_color(char *dest, const char *var, const char *value)
       	return 0;
       }
       
      -static int git_default_core_config(const char *var, const char *value, void *cb)
      +static int git_default_core_config(const char *var, const char *value,
     -+				   struct key_value_info *kvi, void *cb)
     ++				   const struct config_context *ctx, void *cb)
       {
       	/* This needs a better name */
       	if (!strcmp(var, "core.filemode")) {
     @@ config.c: static int git_default_core_config(const char *var, const char *value,
       
       	/* Add other config variables here and to Documentation/config.txt. */
      -	return platform_core_config(var, value, cb);
     -+	return platform_core_config(var, value,kvi, cb);
     ++	return platform_core_config(var, value, ctx, cb);
       }
       
       static int git_default_sparse_config(const char *var, const char *value)
     @@ config.c: static int git_default_mailmap_config(const char *var, const char *val
       
      -int git_default_config(const char *var, const char *value, void *cb)
      +int git_default_config(const char *var, const char *value,
     -+		       struct key_value_info *kvi, void *cb)
     ++		       const struct config_context *ctx, void *cb)
       {
       	if (starts_with(var, "core."))
      -		return git_default_core_config(var, value, cb);
     -+		return git_default_core_config(var, value,kvi, cb);
     ++		return git_default_core_config(var, value, ctx, cb);
       
       	if (starts_with(var, "user.") ||
       	    starts_with(var, "author.") ||
       	    starts_with(var, "committer."))
      -		return git_ident_config(var, value, cb);
     -+		return git_ident_config(var, value,kvi, cb);
     ++		return git_ident_config(var, value, ctx, cb);
       
       	if (starts_with(var, "i18n."))
       		return git_default_i18n_config(var, value);
     +@@ config.c: static void configset_iter(struct config_reader *reader, struct config_set *set,
     + 
     + 		config_reader_set_kvi(reader, values->items[value_index].util);
     + 
     +-		if (fn(entry->key, values->items[value_index].string, data) < 0)
     ++		if (fn(entry->key, values->items[value_index].string, NULL, data) < 0)
     + 			git_die_config_linenr(entry->key,
     + 					      reader->config_kvi->filename,
     + 					      reader->config_kvi->linenr);
      @@ config.c: struct configset_add_data {
       };
       #define CONFIGSET_ADD_INIT { 0 }
       
      -static int config_set_callback(const char *key, const char *value, void *cb)
      +static int config_set_callback(const char *key, const char *value,
     -+			       struct key_value_info *kvi UNUSED, void *cb)
     ++			       const struct config_context *ctx UNUSED,
     ++			       void *cb)
       {
       	struct configset_add_data *data = cb;
       	configset_add_value(data->config_reader, data->config_set, key, value);
     @@ config.c: static int store_aux_event(enum config_event_t type,
       
      -static int store_aux(const char *key, const char *value, void *cb)
      +static int store_aux(const char *key, const char *value,
     -+		     struct key_value_info *kvi UNUSED, void *cb)
     ++		     const struct config_context *ctx UNUSED, void *cb)
       {
       	struct config_store_data *store = cb;
       
      
       ## config.h ##
     -@@ config.h: struct key_value_info {
     +@@ config.h: struct config_options {
     + 	} error_action;
     + };
     + 
     ++/* Config source metadata for a given config key-value pair */
     ++struct key_value_info {
     ++	const char *filename;
     ++	int linenr;
     ++	enum config_origin_type origin_type;
     ++	enum config_scope scope;
     ++};
     ++#define KVI_INIT { \
     ++	.filename = NULL, \
     ++	.linenr = -1, \
     ++	.origin_type = CONFIG_ORIGIN_UNKNOWN, \
     ++	.scope = CONFIG_SCOPE_UNKNOWN, \
     ++}
     ++
     ++/* Captures additional information that a config callback can use. */
     ++struct config_context {
     ++	/* Config source metadata for key and value. */
     ++	const struct key_value_info *kvi;
     ++};
     ++#define CONFIG_CONTEXT_INIT { 0 }
     ++
     + /**
     +- * A config callback function takes three parameters:
     ++ * A config callback function takes four parameters:
     +  *
     +  * - the name of the parsed variable. This is in canonical "flat" form: the
     +  *   section, subsection, and variable segments will be separated by dots,
     +@@ config.h: struct config_options {
     +  *   value specified, the value will be NULL (typically this means it
     +  *   should be interpreted as boolean true).
     +  *
     ++ * - the 'config context', that is, additional information about the config
     ++ *   iteration operation provided by the config machinery. For example, this
     ++ *   includes information about the config source being parsed (e.g. the
     ++ *   filename).
     ++ *
     +  * - a void pointer passed in by the caller of the config API; this can
     +  *   contain callback-specific data
     +  *
     +  * A config callback should return 0 for success, or -1 if the variable
     +  * could not be parsed properly.
        */
     - typedef int (*config_fn_t)(const char *, const char *, struct key_value_info *, void *);
     +-typedef int (*config_fn_t)(const char *, const char *, void *);
     ++typedef int (*config_fn_t)(const char *, const char *,
     ++			   const struct config_context *, void *);
       
      -int git_default_config(const char *, const char *, void *);
     -+int git_default_config(const char *, const char *, struct key_value_info *,
     -+		       void *);
     ++int git_default_config(const char *, const char *,
     ++		       const struct config_context *, void *);
       
       /**
        * Read a specific file in git-config format.
     +@@ config.h: int git_config_get_expiry(const char *key, const char **output);
     + /* parse either "this many days" integer, or "5.days.ago" approxidate */
     + int git_config_get_expiry_in_days(const char *key, timestamp_t *, timestamp_t now);
     + 
     +-struct key_value_info {
     +-	const char *filename;
     +-	int linenr;
     +-	enum config_origin_type origin_type;
     +-	enum config_scope scope;
     +-};
     +-
     + /**
     +  * First prints the error message specified by the caller in `err` and then
     +  * dies printing the line number and the file name of the highest priority
      
       ## connect.c ##
      @@ connect.c: static struct child_process *git_tcp_connect(int fd[2], char *host, int flags)
     @@ connect.c: static struct child_process *git_tcp_connect(int fd[2], char *host, i
       
       static int git_proxy_command_options(const char *var, const char *value,
      -		void *cb)
     -+		struct key_value_info *kvi, void *cb)
     ++		const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "core.gitproxy")) {
       		const char *for_pos;
     @@ connect.c: static int git_proxy_command_options(const char *var, const char *val
       	}
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static int git_use_proxy(const char *host)
      
     + ## contrib/coccinelle/config_fn_ctx.pending.cocci (new) ##
     +@@
     ++@ get_fn @
     ++identifier fn, R;
     ++@@
     ++(
     ++(
     ++git_config_from_file
     ++|
     ++git_config_from_file_with_options
     ++|
     ++git_config_from_mem
     ++|
     ++git_config_from_blob_oid
     ++|
     ++read_early_config
     ++|
     ++read_very_early_config
     ++|
     ++config_with_options
     ++|
     ++git_config
     ++|
     ++git_protected_config
     ++|
     ++config_from_gitmodules
     ++)
     ++  (fn, ...)
     ++|
     ++repo_config(R, fn, ...)
     ++)
     ++
     ++@ extends get_fn @
     ++identifier C1, C2, D;
     ++@@
     ++int fn(const char *C1, const char *C2,
     +++ const struct config_context *ctx,
     ++  void *D);
     ++
     ++@ extends get_fn @
     ++@@
     ++int fn(const char *, const char *,
     +++ const struct config_context *,
     ++  void *);
     ++
     ++@ extends get_fn @
     ++// Don't change fns that look like callback fns but aren't
     ++identifier fn2 != tar_filter_config && != git_diff_heuristic_config &&
     ++  != git_default_submodule_config && != git_color_config &&
     ++  != bundle_list_update && != parse_object_filter_config;
     ++identifier C1, C2, D1, D2, S;
     ++attribute name UNUSED;
     ++@@
     ++int fn(const char *C1, const char *C2,
     +++ const struct config_context *ctx,
     ++  void *D1) {
     ++<+...
     ++(
     ++fn2(C1, C2
     +++ , ctx
     ++, D2);
     ++|
     ++if(fn2(C1, C2
     +++ , ctx
     ++, D2) < 0) { ... }
     ++|
     ++return fn2(C1, C2
     +++ , ctx
     ++, D2);
     ++|
     ++S = fn2(C1, C2
     +++ , ctx
     ++, D2);
     ++)
     ++...+>
     ++  }
     ++
     ++@ extends get_fn@
     ++identifier C1, C2, D;
     ++attribute name UNUSED;
     ++@@
     ++int fn(const char *C1, const char *C2,
     +++ const struct config_context *ctx UNUSED,
     ++  void *D) {...}
     ++
     ++
     ++// The previous rules don't catch all callbacks, especially if they're defined
     ++// in a separate file from the git_config() call. Fix these manually.
     ++@@
     ++identifier C1, C2, D;
     ++attribute name UNUSED;
     ++@@
     ++int
     ++(
     ++git_ident_config
     ++|
     ++urlmatch_collect_fn
     ++|
     ++write_one_config
     ++|
     ++forbid_remote_url
     ++|
     ++credential_config_callback
     ++)
     ++  (const char *C1, const char *C2,
     +++ const struct config_context *ctx UNUSED,
     ++  void *D) {...}
     ++
     ++@@
     ++identifier C1, C2, D, D2, S, fn2;
     ++@@
     ++int
     ++(
     ++http_options
     ++|
     ++git_status_config
     ++|
     ++git_commit_config
     ++|
     ++git_default_core_config
     ++|
     ++grep_config
     ++)
     ++  (const char *C1, const char *C2,
     +++ const struct config_context *ctx,
     ++  void *D) {
     ++<+...
     ++(
     ++fn2(C1, C2
     +++ , ctx
     ++, D2);
     ++|
     ++if(fn2(C1, C2
     +++ , ctx
     ++, D2) < 0) { ... }
     ++|
     ++return fn2(C1, C2
     +++ , ctx
     ++, D2);
     ++|
     ++S = fn2(C1, C2
     +++ , ctx
     ++, D2);
     ++)
     ++...+>
     ++  }
     +
       ## convert.c ##
      @@ convert.c: static int apply_filter(const char *path, const char *src, size_t len,
       	return 0;
     @@ convert.c: static int apply_filter(const char *path, const char *src, size_t len
       
      -static int read_convert_config(const char *var, const char *value, void *cb UNUSED)
      +static int read_convert_config(const char *var, const char *value,
     -+			       struct key_value_info *kvi UNUSED,
     ++			       const struct config_context *ctx UNUSED,
      +			       void *cb UNUSED)
       {
       	const char *key, *name;
     @@ credential.c: static int credential_from_potentially_partial_url(struct credenti
       						   const char *url);
       
       static int credential_config_callback(const char *var, const char *value,
     -+				      struct key_value_info *kvi UNUSED,
     ++				      const struct config_context *ctx UNUSED,
       				      void *data)
       {
       	struct credential *c = data;
     @@ delta-islands.c: static void free_remote_islands(kh_str_t *remote_islands)
       
      -static int island_config_callback(const char *k, const char *v, void *cb)
      +static int island_config_callback(const char *k, const char *v,
     -+				  struct key_value_info *kvi UNUSED, void *cb)
     ++				  const struct config_context *ctx UNUSED,
     ++				  void *cb)
       {
       	struct island_load_data *ild = cb;
       
     @@ diff.c: static unsigned parse_color_moved_ws(const char *arg)
       
      -int git_diff_ui_config(const char *var, const char *value, void *cb)
      +int git_diff_ui_config(const char *var, const char *value,
     -+		       struct key_value_info *kvi, void *cb)
     ++		       const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
       		diff_use_color_default = git_config_colorbool(var, value);
     @@ diff.c: int git_diff_ui_config(const char *var, const char *value, void *cb)
       		return -1;
       
      -	return git_diff_basic_config(var, value, cb);
     -+	return git_diff_basic_config(var, value,kvi, cb);
     ++	return git_diff_basic_config(var, value, ctx, cb);
       }
       
      -int git_diff_basic_config(const char *var, const char *value, void *cb)
      +int git_diff_basic_config(const char *var, const char *value,
     -+			  struct key_value_info *kvi, void *cb)
     ++			  const struct config_context *ctx, void *cb)
       {
       	const char *name;
       
     @@ diff.c: int git_diff_basic_config(const char *var, const char *value, void *cb)
       		return -1;
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static char *quote_two(const char *one, const char *two)
     @@ diff.h: void free_diffstat_info(struct diffstat_t *diffstat);
       		   const char **optarg);
       
      -int git_diff_basic_config(const char *var, const char *value, void *cb);
     ++struct config_context;
      +int git_diff_basic_config(const char *var, const char *value,
     -+			  struct key_value_info *kvi, void *cb);
     ++			  const struct config_context *ctx, void *cb);
       int git_diff_heuristic_config(const char *var, const char *value, void *cb);
       void init_diff_ui_defaults(void);
      -int git_diff_ui_config(const char *var, const char *value, void *cb);
      +int git_diff_ui_config(const char *var, const char *value,
     -+		       struct key_value_info *kvi, void *cb);
     ++		       const struct config_context *ctx, void *cb);
       void repo_diff_setup(struct repository *, struct diff_options *);
       struct option *add_diff_options(const struct option *, struct diff_options *);
       int diff_opt_parse(struct diff_options *, const char **, int, const char *);
     @@ fetch-pack.c: static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
       
      -static int fetch_pack_config_cb(const char *var, const char *value, void *cb)
      +static int fetch_pack_config_cb(const char *var, const char *value,
     -+				struct key_value_info *kvi, void *cb)
     ++				const struct config_context *ctx, void *cb)
       {
       	if (strcmp(var, "fetch.fsck.skiplist") == 0) {
       		const char *path;
     @@ fetch-pack.c: static int fetch_pack_config_cb(const char *var, const char *value
       	}
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       static void fetch_pack_config(void)
     @@ fmt-merge-msg.c: static int use_branch_desc;
       
      -int fmt_merge_msg_config(const char *key, const char *value, void *cb)
      +int fmt_merge_msg_config(const char *key, const char *value,
     -+			 struct key_value_info *kvi, void *cb)
     ++			 const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
       		int is_bool;
     @@ fmt-merge-msg.c: int fmt_merge_msg_config(const char *key, const char *value, vo
       		suppress_dest_pattern_seen = 1;
       	} else {
      -		return git_default_config(key, value, cb);
     -+		return git_default_config(key, value,kvi, cb);
     ++		return git_default_config(key, value, ctx, cb);
       	}
       	return 0;
       }
     @@ fmt-merge-msg.h: struct fmt_merge_msg_opts {
       extern int merge_log_config;
      -int fmt_merge_msg_config(const char *key, const char *value, void *cb);
      +int fmt_merge_msg_config(const char *key, const char *value,
     -+			 struct key_value_info *kvi, void *cb);
     ++			 const struct config_context *ctx, void *cb);
       int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
       		  struct fmt_merge_msg_opts *);
       
     @@ fsck.c: struct fsck_gitmodules_data {
       
      -static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
      +static int fsck_gitmodules_fn(const char *var, const char *value,
     -+			      struct key_value_info *kvi UNUSED, void *vdata)
     ++			      const struct config_context *ctx UNUSED,
     ++			      void *vdata)
       {
       	struct fsck_gitmodules_data *data = vdata;
       	const char *subsection, *key;
     @@ fsck.c: int fsck_finish(struct fsck_options *options)
       
      -int git_fsck_config(const char *var, const char *value, void *cb)
      +int git_fsck_config(const char *var, const char *value,
     -+		    struct key_value_info *kvi, void *cb)
     ++		    const struct config_context *ctx, void *cb)
       {
       	struct fsck_options *options = cb;
       	if (strcmp(var, "fsck.skiplist") == 0) {
     @@ fsck.c: int git_fsck_config(const char *var, const char *value, void *cb)
       	}
       
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
       
       /*
      
       ## fsck.h ##
     -@@ fsck.h: const char *fsck_describe_object(struct fsck_options *options,
     +@@ fsck.h: void fsck_put_object_name(struct fsck_options *options,
     + const char *fsck_describe_object(struct fsck_options *options,
     + 				 const struct object_id *oid);
     + 
     ++struct key_value_info;
     + /*
        * git_config() callback for use by fsck-y tools that want to support
        * fsck.<msg> fsck.skipList etc.
        */
      -int git_fsck_config(const char *var, const char *value, void *cb);
      +int git_fsck_config(const char *var, const char *value,
     -+		    struct key_value_info *kvi, void *cb);
     ++		    const struct config_context *ctx, void *cb);
       
       #endif
      
     + ## git-compat-util.h ##
     +@@ git-compat-util.h: typedef uintmax_t timestamp_t;
     + #endif
     + 
     + #ifndef platform_core_config
     ++struct config_context;
     + static inline int noop_core_config(const char *var UNUSED,
     + 				   const char *value UNUSED,
     ++				   const struct config_context *ctx UNUSED,
     + 				   void *cb UNUSED)
     + {
     + 	return 0;
     +
       ## gpg-interface.c ##
      @@
       #include "alias.h"
       #include "wrapper.h"
       
      -static int git_gpg_config(const char *, const char *, void *);
     -+static int git_gpg_config(const char *, const char *, struct key_value_info *,
     -+			  void *);
     ++static int git_gpg_config(const char *, const char *,
     ++			  const struct config_context *, void *);
       
       static void gpg_interface_lazy_init(void)
       {
     @@ gpg-interface.c: void set_signing_key(const char *key)
       
      -static int git_gpg_config(const char *var, const char *value, void *cb UNUSED)
      +static int git_gpg_config(const char *var, const char *value,
     -+			  struct key_value_info *kvi UNUSED, void *cb UNUSED)
     ++			  const struct config_context *ctx UNUSED,
     ++			  void *cb UNUSED)
       {
       	struct gpg_format *fmt = NULL;
       	char *fmtname = NULL;
     @@ grep.c: define_list_config_array_extra(color_grep_slots, {"match"});
        */
      -int grep_config(const char *var, const char *value, void *cb)
      +int grep_config(const char *var, const char *value,
     -+		struct key_value_info *kvi UNUSED, void *cb)
     ++		const struct config_context *ctx, void *cb)
       {
       	struct grep_opt *opt = cb;
       	const char *slot;
     +@@ grep.c: int grep_config(const char *var, const char *value, void *cb)
     + 	if (!strcmp(var, "color.grep"))
     + 		opt->color = git_config_colorbool(var, value);
     + 	if (!strcmp(var, "color.grep.match")) {
     +-		if (grep_config("color.grep.matchcontext", value, cb) < 0)
     ++		if (grep_config("color.grep.matchcontext", value, ctx, cb) < 0)
     + 			return -1;
     +-		if (grep_config("color.grep.matchselected", value, cb) < 0)
     ++		if (grep_config("color.grep.matchselected", value, ctx, cb) < 0)
     + 			return -1;
     + 	} else if (skip_prefix(var, "color.grep.", &slot)) {
     + 		int i = LOOKUP_CONFIG(color_grep_slots, slot);
     +
     + ## grep.h ##
     +@@ grep.h: struct grep_opt {
     + 	.output = std_output, \
     + }
     + 
     +-int grep_config(const char *var, const char *value, void *);
     ++struct config_context;
     ++int grep_config(const char *var, const char *value,
     ++		const struct config_context *ctx, void *data);
     + void grep_init(struct grep_opt *, struct repository *repo);
     + 
     + void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
      
       ## help.c ##
      @@ help.c: void load_command_list(const char *prefix,
     @@ help.c: void load_command_list(const char *prefix,
       
      -static int get_colopts(const char *var, const char *value, void *data)
      +static int get_colopts(const char *var, const char *value,
     -+		       struct key_value_info *kvi UNUSED, void *data)
     ++		       const struct config_context *ctx UNUSED, void *data)
       {
       	unsigned int *colopts = data;
       
     @@ help.c: void list_developer_interfaces_help(void)
       
      -static int get_alias(const char *var, const char *value, void *data)
      +static int get_alias(const char *var, const char *value,
     -+		     struct key_value_info *kvi UNUSED, void *data)
     ++		     const struct config_context *ctx UNUSED, void *data)
       {
       	struct string_list *list = data;
       
     @@ help.c: static struct cmdnames aliases;
       #define AUTOCORRECT_IMMEDIATELY (-1)
       
       static int git_unknown_cmd_config(const char *var, const char *value,
     -+				  struct key_value_info *kvi UNUSED,
     ++				  const struct config_context *ctx UNUSED,
       				  void *cb UNUSED)
       {
       	const char *p;
      
     + ## http.c ##
     +@@ http.c: static void process_curl_messages(void)
     + 	}
     + }
     + 
     +-static int http_options(const char *var, const char *value, void *cb)
     ++static int http_options(const char *var, const char *value,
     ++			const struct config_context *ctx, void *data)
     + {
     + 	if (!strcmp("http.version", var)) {
     + 		return git_config_string(&curl_http_version, var, value);
     +@@ http.c: static int http_options(const char *var, const char *value, void *cb)
     + 	}
     + 
     + 	/* Fall back on the default ones */
     +-	return git_default_config(var, value, cb);
     ++	return git_default_config(var, value, ctx, data);
     + }
     + 
     + static int curl_empty_auth_enabled(void)
     +
       ## ident.c ##
      @@ ident.c: static int set_ident(const char *var, const char *value)
       	return 0;
     @@ ident.c: static int set_ident(const char *var, const char *value)
       
      -int git_ident_config(const char *var, const char *value, void *data UNUSED)
      +int git_ident_config(const char *var, const char *value,
     -+		     struct key_value_info *kvi UNUSED, void *data UNUSED)
     ++		     const struct config_context *ctx UNUSED,
     ++		     void *data UNUSED)
       {
       	if (!strcmp(var, "user.useconfigonly")) {
       		ident_use_config_only = git_config_bool(var, value);
     @@ ident.h: const char *fmt_name(enum want_ident);
       int author_ident_sufficiently_given(void);
       
      -int git_ident_config(const char *, const char *, void *);
     -+int git_ident_config(const char *, const char *,
     -+		     struct key_value_info *UNUSED, void *);
     ++struct config_context;
     ++int git_ident_config(const char *, const char *, const struct config_context *,
     ++		     void *);
       
       #endif
      
     @@ imap-send.c: static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, i
       
      -static int git_imap_config(const char *var, const char *val, void *cb)
      +static int git_imap_config(const char *var, const char *val,
     -+			   struct key_value_info *kvi, void *cb)
     ++			   const struct config_context *ctx, void *cb)
       {
       
       	if (!strcmp("imap.sslverify", var))
     @@ imap-send.c: static int git_imap_config(const char *var, const char *val, void *
       		}
       	} else
      -		return git_default_config(var, val, cb);
     -+		return git_default_config(var, val,kvi, cb);
     ++		return git_default_config(var, val, ctx, cb);
       
       	return 0;
       }
     @@ ll-merge.c: static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
       static const char *default_ll_merge;
       
       static int read_merge_config(const char *var, const char *value,
     -+			     struct key_value_info *kvi UNUSED,
     ++			     const struct config_context *ctx UNUSED,
       			     void *cb UNUSED)
       {
       	struct ll_merge_driver *fn;
     @@ ls-refs.c: static void send_possibly_unborn_head(struct ls_refs_data *data)
       }
       
       static int ls_refs_config(const char *var, const char *value,
     --			  void *cb_data)
     -+			  struct key_value_info *kvi UNUSED, void *cb_data)
     ++			  const struct config_context *ctx UNUSED,
     + 			  void *cb_data)
       {
       	struct ls_refs_data *data = cb_data;
     - 	/*
      
       ## mailinfo.c ##
      @@ mailinfo.c: int mailinfo_parse_quoted_cr_action(const char *actionstr, int *action)
     @@ mailinfo.c: int mailinfo_parse_quoted_cr_action(const char *actionstr, int *acti
       
      -static int git_mailinfo_config(const char *var, const char *value, void *mi_)
      +static int git_mailinfo_config(const char *var, const char *value,
     -+			       struct key_value_info *kvi, void *mi_)
     ++			       const struct config_context *ctx, void *mi_)
       {
       	struct mailinfo *mi = mi_;
       
       	if (!starts_with(var, "mailinfo."))
      -		return git_default_config(var, value, NULL);
     -+		return git_default_config(var, value,kvi, NULL);
     ++		return git_default_config(var, value, ctx, NULL);
       	if (!strcmp(var, "mailinfo.scissors")) {
       		mi->use_scissors = git_config_bool(var, value);
       		return 0;
     @@ notes-utils.c: static combine_notes_fn parse_combine_notes_fn(const char *v)
       
      -static int notes_rewrite_config(const char *k, const char *v, void *cb)
      +static int notes_rewrite_config(const char *k, const char *v,
     -+				struct key_value_info *kvi UNUSED, void *cb)
     ++				const struct config_context *ctx UNUSED,
     ++				void *cb)
       {
       	struct notes_rewrite_cfg *c = cb;
       	if (starts_with(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
     @@ notes.c: void string_list_add_refs_from_colon_sep(struct string_list *list,
       
      -static int notes_display_config(const char *k, const char *v, void *cb)
      +static int notes_display_config(const char *k, const char *v,
     -+				struct key_value_info *kvi UNUSED, void *cb)
     ++				const struct config_context *ctx UNUSED,
     ++				void *cb)
       {
       	int *load_refs = cb;
       
     @@ pager.c: static void wait_for_pager_signal(int signo)
       }
       
       static int core_pager_config(const char *var, const char *value,
     -+			     struct key_value_info *kvi UNUSED,
     ++			     const struct config_context *ctx UNUSED,
       			     void *data UNUSED)
       {
       	if (!strcmp(var, "core.pager"))
     @@ pager.c: struct pager_command_config_data {
       
      -static int pager_command_config(const char *var, const char *value, void *vdata)
      +static int pager_command_config(const char *var, const char *value,
     -+				struct key_value_info *kvi UNUSED,
     ++				const struct config_context *ctx UNUSED,
      +				void *vdata)
       {
       	struct pager_command_config_data *data = vdata;
     @@ pretty.c: static void save_user_format(struct rev_info *rev, const char *cp, int
       }
       
       static int git_pretty_formats_config(const char *var, const char *value,
     -+				     struct key_value_info *kvi UNUSED,
     ++				     const struct config_context *ctx UNUSED,
       				     void *cb UNUSED)
       {
       	struct cmt_fmt_map *commit_format = NULL;
     @@ promisor-remote.c: static void promisor_remote_move_to_tail(struct promisor_remo
       
      -static int promisor_remote_config(const char *var, const char *value, void *data)
      +static int promisor_remote_config(const char *var, const char *value,
     -+				  struct key_value_info *kvi UNUSED,
     ++				  const struct config_context *ctx UNUSED,
      +				  void *data)
       {
       	struct promisor_remote_config *config = data;
     @@ remote.c: static void read_branches_file(struct remote_state *remote_state,
       
      -static int handle_config(const char *key, const char *value, void *cb)
      +static int handle_config(const char *key, const char *value,
     -+			 struct key_value_info *kvi UNUSED, void *cb)
     ++			 const struct config_context *ctx UNUSED, void *cb)
       {
       	const char *name;
       	size_t namelen;
     @@ revision.c: struct exclude_hidden_refs_cb {
       
      -static int hide_refs_config(const char *var, const char *value, void *cb_data)
      +static int hide_refs_config(const char *var, const char *value,
     -+			    struct key_value_info *kvi UNUSED, void *cb_data)
     ++			    const struct config_context *ctx UNUSED,
     ++			    void *cb_data)
       {
       	struct exclude_hidden_refs_cb *cb = cb_data;
       	cb->exclusions->hidden_refs_configured = 1;
     @@ scalar.c: static int cmd_register(int argc, const char **argv)
       
      -static int get_scalar_repos(const char *key, const char *value, void *data)
      +static int get_scalar_repos(const char *key, const char *value,
     -+			    struct key_value_info *kvi UNUSED, void *data)
     ++			    const struct config_context *ctx UNUSED,
     ++			    void *data)
       {
       	struct string_list *list = data;
       
     @@ sequencer.c: static struct update_ref_record *init_update_ref_record(const char
       
      -static int git_sequencer_config(const char *k, const char *v, void *cb)
      +static int git_sequencer_config(const char *k, const char *v,
     -+				struct key_value_info *kvi, void *cb)
     ++				const struct config_context *ctx, void *cb)
       {
       	struct replay_opts *opts = cb;
       	int status;
     @@ sequencer.c: static int git_sequencer_config(const char *k, const char *v, void
       		opts->commit_use_reference = git_config_bool(k, v);
       
      -	return git_diff_basic_config(k, v, NULL);
     -+	return git_diff_basic_config(k, v,kvi, NULL);
     ++	return git_diff_basic_config(k, v, ctx, NULL);
       }
       
       void sequencer_init_config(struct replay_opts *opts)
     @@ sequencer.c: static int git_config_string_dup(char **dest,
       
      -static int populate_opts_cb(const char *key, const char *value, void *data)
      +static int populate_opts_cb(const char *key, const char *value,
     -+			    struct key_value_info *kvi UNUSED, void *data)
     ++			    const struct config_context *ctx UNUSED,
     ++			    void *data)
       {
       	struct replay_opts *opts = data;
       	int error_flag = 1;
     @@ setup.c: no_prevention_needed:
       
      -static int read_worktree_config(const char *var, const char *value, void *vdata)
      +static int read_worktree_config(const char *var, const char *value,
     -+				struct key_value_info *kvi UNUSED,
     ++				const struct config_context *ctx UNUSED,
      +				void *vdata)
       {
       	struct repository_format *data = vdata;
     @@ setup.c: static enum extension_result handle_extension(const char *var,
       
      -static int check_repo_format(const char *var, const char *value, void *vdata)
      +static int check_repo_format(const char *var, const char *value,
     -+			     struct key_value_info *kvi, void *vdata)
     ++			     const struct config_context *ctx, void *vdata)
       {
       	struct repository_format *data = vdata;
       	const char *ext;
     @@ setup.c: static int check_repo_format(const char *var, const char *value, void *
       	}
       
      -	return read_worktree_config(var, value, vdata);
     -+	return read_worktree_config(var, value,kvi, vdata);
     ++	return read_worktree_config(var, value, ctx, vdata);
       }
       
       static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)
     @@ setup.c: struct safe_directory_data {
       
      -static int safe_directory_cb(const char *key, const char *value, void *d)
      +static int safe_directory_cb(const char *key, const char *value,
     -+			     struct key_value_info *kvi UNUSED, void *d)
     ++			     const struct config_context *ctx UNUSED, void *d)
       {
       	struct safe_directory_data *data = d;
       
     @@ setup.c: static int ensure_valid_ownership(const char *gitfile,
       
      -static int allowed_bare_repo_cb(const char *key, const char *value, void *d)
      +static int allowed_bare_repo_cb(const char *key, const char *value,
     -+				struct key_value_info *kvi UNUSED, void *d)
     ++				const struct config_context *ctx UNUSED,
     ++				void *d)
       {
       	enum allowed_bare_repo *allowed_bare_repo = d;
       
     @@ submodule-config.c: struct parse_config_parameter {
        */
      -static int parse_config(const char *var, const char *value, void *data)
      +static int parse_config(const char *var, const char *value,
     -+			struct key_value_info *kvi UNUSED, void *data)
     ++			const struct config_context *ctx UNUSED, void *data)
       {
       	struct parse_config_parameter *me = data;
       	struct submodule *submodule;
     @@ submodule-config.c: out:
       
      -static int gitmodules_cb(const char *var, const char *value, void *data)
      +static int gitmodules_cb(const char *var, const char *value,
     -+			 struct key_value_info *kvi UNUSED, void *data)
     ++			 const struct config_context *ctx, void *data)
       {
       	struct repository *repo = data;
       	struct parse_config_parameter parameter;
     +@@ submodule-config.c: static int gitmodules_cb(const char *var, const char *value, void *data)
     + 	parameter.gitmodules_oid = null_oid();
     + 	parameter.overwrite = 1;
     + 
     +-	return parse_config(var, value, &parameter);
     ++	return parse_config(var, value, ctx, &parameter);
     + }
     + 
     + void repo_read_gitmodules(struct repository *repo, int skip_if_read)
      @@ submodule-config.c: void submodule_free(struct repository *r)
       		submodule_cache_clear(r->submodule_cache);
       }
       
      -static int config_print_callback(const char *var, const char *value, void *cb_data)
      +static int config_print_callback(const char *var, const char *value,
     -+				 struct key_value_info *kvi UNUSED,
     ++				 const struct config_context *ctx UNUSED,
      +				 void *cb_data)
       {
       	char *wanted_key = cb_data;
     @@ submodule-config.c: struct fetch_config {
       
      -static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
      +static int gitmodules_fetch_config(const char *var, const char *value,
     -+				   struct key_value_info *kvi UNUSED,
     ++				   const struct config_context *ctx UNUSED,
      +				   void *cb)
       {
       	struct fetch_config *config = cb;
     @@ submodule-config.c: void fetch_config_from_gitmodules(int *max_children, int *re
       }
       
       static int gitmodules_update_clone_config(const char *var, const char *value,
     -+					  struct key_value_info *kvi UNUSED,
     ++					  const struct config_context *ctx UNUSED,
       					  void *cb)
       {
       	int *max_jobs = cb;
     @@ t/helper/test-config.c
       
      -static int iterate_cb(const char *var, const char *value, void *data UNUSED)
      +static int iterate_cb(const char *var, const char *value,
     -+		      struct key_value_info *kvi UNUSED, void *data UNUSED)
     ++		      const struct config_context *ctx UNUSED,
     ++		      void *data UNUSED)
       {
       	static int nr;
       
     @@ t/helper/test-config.c: static int iterate_cb(const char *var, const char *value
       
      -static int parse_int_cb(const char *var, const char *value, void *data)
      +static int parse_int_cb(const char *var, const char *value,
     -+			struct key_value_info *kvi UNUSED, void *data)
     ++			const struct config_context *ctx UNUSED, void *data)
       {
       	const char *key_to_match = data;
       
     @@ t/helper/test-config.c: static int parse_int_cb(const char *var, const char *val
       
      -static int early_config_cb(const char *var, const char *value, void *vdata)
      +static int early_config_cb(const char *var, const char *value,
     -+			   struct key_value_info *kvi UNUSED, void *vdata)
     ++			   const struct config_context *ctx UNUSED,
     ++			   void *vdata)
       {
       	const char *key = vdata;
       
     @@ t/helper/test-userdiff.c: static int driver_cb(struct userdiff_driver *driver,
       
      -static int cmd__userdiff_config(const char *var, const char *value, void *cb UNUSED)
      +static int cmd__userdiff_config(const char *var, const char *value,
     -+				struct key_value_info *kvi UNUSED,
     ++				const struct config_context *ctx UNUSED,
      +				void *cb UNUSED)
       {
       	if (userdiff_config(var, value) < 0)
     @@ trace2/tr2_cfg.c: struct tr2_cfg_data {
        */
      -static int tr2_cfg_cb(const char *key, const char *value, void *d)
      +static int tr2_cfg_cb(const char *key, const char *value,
     -+		      struct key_value_info *kvi UNUSED, void *d)
     ++		      const struct config_context *ctx UNUSED, void *d)
       {
       	struct strbuf **s;
       	struct tr2_cfg_data *data = (struct tr2_cfg_data *)d;
     +@@ trace2/tr2_cfg.c: void tr2_list_env_vars_fl(const char *file, int line)
     + void tr2_cfg_set_fl(const char *file, int line, const char *key,
     + 		    const char *value)
     + {
     ++	struct key_value_info kvi = KVI_INIT;
     ++	struct config_context ctx = {
     ++		.kvi = &kvi,
     ++	};
     + 	struct tr2_cfg_data data = { file, line };
     + 
     + 	if (tr2_cfg_load_patterns() > 0)
     +-		tr2_cfg_cb(key, value, &data);
     ++		tr2_cfg_cb(key, value, &ctx, &data);
     + }
      
       ## trace2/tr2_sysenv.c ##
      @@ trace2/tr2_sysenv.c: static struct tr2_sysenv_entry tr2_sysenv_settings[] = {
     @@ trace2/tr2_sysenv.c: static struct tr2_sysenv_entry tr2_sysenv_settings[] = {
       
      -static int tr2_sysenv_cb(const char *key, const char *value, void *d)
      +static int tr2_sysenv_cb(const char *key, const char *value,
     -+			 struct key_value_info *kvi UNUSED, void *d)
     ++			 const struct config_context *ctx UNUSED, void *d)
       {
       	int k;
       
     @@ trailer.c: static struct {
       };
       
       static int git_trailer_default_config(const char *conf_key, const char *value,
     -+				      struct key_value_info *kvi UNUSED,
     ++				      const struct config_context *ctx UNUSED,
       				      void *cb UNUSED)
       {
       	const char *trailer_item, *variable_name;
     @@ trailer.c: static int git_trailer_default_config(const char *conf_key, const cha
       }
       
       static int git_trailer_config(const char *conf_key, const char *value,
     -+			      struct key_value_info *kvi UNUSED,
     ++			      const struct config_context *ctx UNUSED,
       			      void *cb UNUSED)
       {
       	const char *trailer_item, *variable_name;
     @@ upload-pack.c: static int parse_object_filter_config(const char *var, const char
       
      -static int upload_pack_config(const char *var, const char *value, void *cb_data)
      +static int upload_pack_config(const char *var, const char *value,
     -+			      struct key_value_info *kvi UNUSED,
     ++			      const struct config_context *ctx UNUSED,
      +			      void *cb_data)
       {
       	struct upload_pack_data *data = cb_data;
     @@ upload-pack.c: static int upload_pack_config(const char *var, const char *value,
       
      -static int upload_pack_protected_config(const char *var, const char *value, void *cb_data)
      +static int upload_pack_protected_config(const char *var, const char *value,
     -+					struct key_value_info *kvi UNUSED,
     ++					const struct config_context *ctx UNUSED,
      +					void *cb_data)
       {
       	struct upload_pack_data *data = cb_data;
     @@ urlmatch.c: static int cmp_matches(const struct urlmatch_item *a,
       
      -int urlmatch_config_entry(const char *var, const char *value, void *cb)
      +int urlmatch_config_entry(const char *var, const char *value,
     -+			  struct key_value_info *kvi UNUSED, void *cb)
     ++			  const struct config_context *ctx, void *cb)
       {
       	struct string_list_item *item;
       	struct urlmatch_config *collect = cb;
     +@@ urlmatch.c: int urlmatch_config_entry(const char *var, const char *value, void *cb)
     + 
     + 	if (!skip_prefix(var, collect->section, &key) || *(key++) != '.') {
     + 		if (collect->cascade_fn)
     +-			return collect->cascade_fn(var, value, cb);
     ++			return collect->cascade_fn(var, value, ctx, cb);
     + 		return 0; /* not interested */
     + 	}
     + 	dot = strrchr(key, '.');
     +@@ urlmatch.c: int urlmatch_config_entry(const char *var, const char *value, void *cb)
     + 	strbuf_addstr(&synthkey, collect->section);
     + 	strbuf_addch(&synthkey, '.');
     + 	strbuf_addstr(&synthkey, key);
     +-	retval = collect->collect_fn(synthkey.buf, value, collect->cb);
     ++	retval = collect->collect_fn(synthkey.buf, value, ctx, collect->cb);
     + 
     + 	strbuf_release(&synthkey);
     + 	return retval;
      
       ## urlmatch.h ##
      @@ urlmatch.h: struct urlmatch_config {
     @@ urlmatch.h: struct urlmatch_config {
       
      -int urlmatch_config_entry(const char *var, const char *value, void *cb);
      +int urlmatch_config_entry(const char *var, const char *value,
     -+			  struct key_value_info *kvi, void *cb);
     ++			  const struct config_context *ctx, void *cb);
       void urlmatch_config_release(struct urlmatch_config *config);
       
       #endif /* URL_MATCH_H */
     @@ xdiff-interface.c: int xdiff_compare_lines(const char *l1, long s1,
       
      -int git_xmerge_config(const char *var, const char *value, void *cb)
      +int git_xmerge_config(const char *var, const char *value,
     -+		      struct key_value_info *kvi, void *cb)
     ++		      const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "merge.conflictstyle")) {
       		if (!value)
     @@ xdiff-interface.c: int git_xmerge_config(const char *var, const char *value, voi
       		return 0;
       	}
      -	return git_default_config(var, value, cb);
     -+	return git_default_config(var, value,kvi, cb);
     ++	return git_default_config(var, value, ctx, cb);
       }
      
       ## xdiff-interface.h ##
     @@ xdiff-interface.h: int buffer_is_binary(const char *ptr, unsigned long size);
       void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
       void xdiff_clear_find_func(xdemitconf_t *xecfg);
      -int git_xmerge_config(const char *var, const char *value, void *cb);
     ++struct config_context;
      +int git_xmerge_config(const char *var, const char *value,
     -+		      struct key_value_info *kvi, void *cb);
     ++		      const struct config_context *ctx, void *cb);
       extern int git_xmerge_style;
       
       /*
  5:  f363b160259 <  -:  ----------- (RFC-only) config: finish config_fn_t refactor
  6:  f57c1007cad !  4:  c5051ddc10d config.c: pass kvi in configsets
     @@ Metadata
      Author: Glen Choo <[email protected]>
      
       ## Commit message ##
     -    config.c: pass kvi in configsets
     +    config.c: pass ctx in configsets
      
     -    Trivially pass "struct key_value_info" to config callbacks in
     -    configset_iter(). Then, in config callbacks that are only used with
     -    configsets, use the "kvi" arg to replace calls to current_config_*(),
     -    and delete current_config_line() because it has no remaining callers.
     +    Pass config_context to config callbacks in configset_iter(), trivially
     +    setting the .kvi member to the cached key_value_info. Then, in config
     +    callbacks that are only used with configsets, use the .kvi member to
     +    replace calls to current_config_*(), and delete current_config_line()
     +    because it has no remaining callers.
      
          This leaves builtin/config.c and config.c as the only remaining users of
          current_config_*().
     @@ builtin/remote.c: struct push_default_info
       };
       
       static int config_read_push_default(const char *key, const char *value,
     --	struct key_value_info *kvi UNUSED, void *cb)
     -+	struct key_value_info *kvi, void *cb)
     +-	const struct config_context *ctx UNUSED, void *cb)
     ++	const struct config_context *ctx, void *cb)
       {
     ++	const struct key_value_info *kvi = ctx->kvi;
     ++
       	struct push_default_info* info = cb;
       	if (strcmp(key, "remote.pushdefault") ||
       	    !value || strcmp(value, info->old_name))
     @@ config.c: static void configset_iter(struct config_reader *reader, struct config
       	struct string_list *values;
       	struct config_set_element *entry;
       	struct configset_list *list = &set->list;
     -+	struct key_value_info *kvi;
     ++	struct config_context ctx = CONFIG_CONTEXT_INIT;
       
       	for (i = 0; i < list->nr; i++) {
       		entry = list->items[i].e;
     - 		value_index = list->items[i].value_index;
     +@@ config.c: static void configset_iter(struct config_reader *reader, struct config_set *set,
       		values = &entry->value_list;
     -+		kvi = values->items[value_index].util;
       
       		config_reader_set_kvi(reader, values->items[value_index].util);
     - 
     +-
      -		if (fn(entry->key, values->items[value_index].string, NULL, data) < 0)
     --			git_die_config_linenr(entry->key,
     ++		ctx.kvi = values->items[value_index].util;
     ++		if (fn(entry->key, values->items[value_index].string, &ctx, data) < 0)
     + 			git_die_config_linenr(entry->key,
      -					      reader->config_kvi->filename,
      -					      reader->config_kvi->linenr);
      -
     -+		if (fn(entry->key, values->items[value_index].string, kvi, data) < 0)
     -+			git_die_config_linenr(entry->key, kvi->filename, kvi->linenr);
     ++					      ctx.kvi->filename,
     ++					      ctx.kvi->linenr);
       		config_reader_set_kvi(reader, NULL);
       	}
       }
     @@ remote.c: static void read_branches_file(struct remote_state *remote_state,
       }
       
       static int handle_config(const char *key, const char *value,
     --			 struct key_value_info *kvi UNUSED, void *cb)
     -+			 struct key_value_info *kvi, void *cb)
     +-			 const struct config_context *ctx UNUSED, void *cb)
     ++			 const struct config_context *ctx, void *cb)
       {
       	const char *name;
       	size_t namelen;
     +@@ remote.c: static int handle_config(const char *key, const char *value,
     + 	struct remote *remote;
     + 	struct branch *branch;
     + 	struct remote_state *remote_state = cb;
     ++	const struct key_value_info *kvi = ctx->kvi;
     + 
     + 	if (parse_config_key(key, "branch", &name, &namelen, &subkey) >= 0) {
     + 		/* There is no subsection. */
      @@ remote.c: static int handle_config(const char *key, const char *value,
       	}
       	remote = make_remote(remote_state, name, namelen);
     @@ t/helper/test-config.c
        */
       
       static int iterate_cb(const char *var, const char *value,
     --		      struct key_value_info *kvi UNUSED, void *data UNUSED)
     -+		      struct key_value_info *kvi, void *data UNUSED)
     +-		      const struct config_context *ctx UNUSED,
     ++		      const struct config_context *ctx,
     + 		      void *data UNUSED)
       {
     ++	const struct key_value_info *kvi = ctx->kvi;
       	static int nr;
       
     + 	if (nr++)
      @@ t/helper/test-config.c: static int iterate_cb(const char *var, const char *value,
       
       	printf("key=%s\n", var);

7: 641a56f0b40 ! 5: 595e7d2e163 config: provide kvi with config files
@@ Metadata
Author: Glen Choo <[email protected]>

## Commit message ##
- config: provide kvi with config files
+ config: pass ctx with config files

- Refactor out the configset logic that caches "struct config_source" and
- "enum config_scope" as a "struct key_value_info", and use it to pass the
- "kvi" arg to config callbacks when parsing config files. Get the "enum
- config_scope" value by plumbing an additional arg through
- git_config_from_file_with_options() and the underlying machinery.
+ Pass config_context to config_callbacks when parsing config files. To
+ provide the .kvi member, refactor out the configset logic that caches
+ "struct config_source" and "enum config_scope" as a "struct
+ key_value_info". Make the "enum config_scope" available to the config
+ file machinery by plumbing an additional arg through
+ git_config_from_file_with_options().

- We do not exercise the "kvi" arg yet because the remaining
- current_config_*() callers may be used with config_with_options(), which
- may read config from parameters, but parameters don't pass "kvi" yet.
+ We do not exercise ctx yet because the remaining current_config_*()
+ callers may be used with config_with_options(), which may read config
+ from parameters, but parameters don't pass ctx yet.

@@ config.c: static int handle_path_include(struct config_source *cs, const char *p
inc->depth--;
}
cleanup:
-@@ config.c: static int include_condition_is_true(struct config_source *cs,
- }
-
- static int git_config_include(const char *var, const char *value,
-- struct key_value_info *kvi UNUSED, void *data)
-+ struct key_value_info *kvi, void *data)
- {
- struct config_include_data *inc = data;
- struct config_source *cs = inc->config_reader->source;
@@ config.c: static int git_config_include(const char *var, const char *value,
* Pass along all values, including "include" directives; this makes it
* possible to query information on the includes themselves.
*/
- ret = inc->fn(var, value, NULL, inc->data);
-+ ret = inc->fn(var, value, kvi, inc->data);
++ ret = inc->fn(var, value, ctx, inc->data);
if (ret < 0)
return ret;

@@ config.c: static char *parse_value(struct config_source *cs)
{
int c;
char *value;
+ int ret;
++ struct config_context ctx = {
++ .kvi = kvi,
++ };
+
+ /* Get the full name */
+ for (;;) {
@@ config.c: static int get_value(struct config_source *cs, config_fn_t fn, void *data,
* accurate line number in error messages.
*/
cs->linenr--;
- ret = fn(name->buf, value, NULL, data);
+ kvi->linenr = cs->linenr;
-+ ret = fn(name->buf, value, kvi, data);
++ ret = fn(name->buf, value, &ctx, data);
if (ret >= 0)
cs->linenr++;
return ret;
@@ config.c: int git_default_config(const char *var, const char *value,
+ void *data, enum config_scope scope,
const struct config_options *opts)
{
-+ struct key_value_info kvi = { 0 };
++ struct key_value_info kvi = KVI_INIT;
int ret;

/* push config-file parsing state stack */
@@ config.c: int git_config_set_multivar_in_file_gently(const char *config_filename
goto out_free;

## config.h ##
-@@ config.h: int git_default_config(const char *, const char *, struct key_value_info *, +@@ config.h: int git_default_config(const char *, const char *,
int git_config_from_file(config_fn_t fn, const char *, void *);

int git_config_from_file_with_options(config_fn_t fn, const char *,

  8:  74f43fc727e =  6:  a2a891a069f builtin/config.c: test misuse of format_config()
  9:  3760015d2c0 !  7:  1fb1708bbd9 config.c: provide kvi with CLI config

@@ Metadata
Author: Glen Choo <[email protected]>

## Commit message ##
- config.c: provide kvi with CLI config
+ config.c: pass ctx with CLI config

- Refactor out kvi_from_param() from the logic that caches CLI config in
- configsets, and use it to pass the "kvi" arg to config callbacks when
- parsing CLI config. Now that "kvi" is always present when config
- machinery calls config callbacks, plumb "kvi" so that we can replace
- nearly all calls to current_config_*(). (The exception is an edge case
- where trace2/*.c calls current_config_scope(). That will be handled in a
- later commit.) Note that this results in "kvi" containing a different,
- more complete set of information than the mocked up "struct
- config_source" in git_config_from_parameters().
+ Pass config_context when parsing CLI config. To provide the .kvi member,
+ refactor out kvi_from_param() from the logic that caches CLI config in
+ configsets. Now that config_context and config_context.kvi is always
+ present when config machinery calls config callbacks, plumb "kvi" so
+ that we can remove all calls of current_config_scope() except for
+ trace2/*.c (which will be handled in a later commit), and remove all
+ other current_config_*() (the functions themselves and their calls).
+ Note that this results in .kvi containing a different, more complete
+ set of information than the mocked up "struct config_source" in
+ git_config_from_parameters().

Plumbing "kvi" reveals a few places where we've been doing the wrong thing:
@@ Commit message

* "git config --get-urlmatch --show-scope" iterates config to collect values, but then attempts to display the scope after config iteration.
- Fix this by copying the "kvi" arg in the collection phase so that it
+ Fix this by copying the "kvi" value in the collection phase so that it
can be read back later. This means that we can now support "git config --get-urlmatch --show-origin" (we don't allow this combination of args because of this bug), but that is left unchanged for now.
@@ builtin/config.c: static void check_argc(int argc, int min, int max)
}

-static void show_config_origin(struct strbuf *buf)
-+static void show_config_origin(struct key_value_info *kvi, struct strbuf *buf) ++static void show_config_origin(const struct key_value_info *kvi,
++ struct strbuf *buf)
{
const char term = end_nul ? '\0' : '\t';

@@ builtin/config.c: static void check_argc(int argc, int min, int max)
}

-static void show_config_scope(struct strbuf *buf)
-+static void show_config_scope(struct key_value_info *kvi, struct strbuf *buf) ++static void show_config_scope(const struct key_value_info *kvi,
++ struct strbuf *buf)
{
const char term = end_nul ? '\0' : '\t';
- const char *scope = config_scope_name(current_config_scope());
@@ builtin/config.c: static void check_argc(int argc, int min, int max)
}

static int show_all_config(const char *key_, const char *value_,
-- struct key_value_info *kvi UNUSED, void *cb UNUSED)
-+ struct key_value_info *kvi, void *cb UNUSED)
+- const struct config_context *ctx UNUSED,
++ const struct config_context *ctx,
+ void *cb UNUSED)
{
++ const struct key_value_info *kvi = ctx->kvi;
++
if (show_origin || show_scope) {
struct strbuf buf = STRBUF_INIT;
if (show_scope)
@@ builtin/config.c: struct strbuf_list {

-static int format_config(struct strbuf *buf, const char *key_, const char *value_) +static int format_config(struct strbuf *buf, const char *key_,
-+ const char *value_, struct key_value_info *kvi)
++ const char *value_, const struct key_value_info *kvi)
{
if (show_scope)
- show_config_scope(buf);
@@ builtin/config.c: static int format_config(struct strbuf *buf, const char *key_,
}

static int collect_config(const char *key_, const char *value_,
-- struct key_value_info *kvi UNUSED, void *cb)
-+ struct key_value_info *kvi, void *cb)
+- const struct config_context *ctx UNUSED, void *cb)
++ const struct config_context *ctx, void *cb)
{
struct strbuf_list *values = cb;
++ const struct key_value_info *kvi = ctx->kvi;

+ if (!use_key_regexp && strcmp(key_, key))
+ return 0;
@@ builtin/config.c: static int collect_config(const char *key_, const char *value_,
ALLOC_GROW(values->items, values->nr + 1, values->alloc); strbuf_init(&values->items[values->nr], 0);
@@ builtin/config.c: static int get_value(const char *key_, const char *regex_, uns
&given_config_source, &config_options);

if (!values.nr && default_value) {
-+ struct key_value_info kvi = { 0 };
++ struct key_value_info kvi = KVI_INIT;
struct strbuf *item;
+
+ kvi_from_param(&kvi);
@@ builtin/config.c: static void check_write(void)
};

static int urlmatch_collect_fn(const char *var, const char *value,
-- struct key_value_info *kvi UNUSED, void *cb)
-+ struct key_value_info *kvi, void *cb)
+- const struct config_context *ctx UNUSED,
++ const struct config_context *ctx,
+ void *cb)
{
struct string_list *values = cb;
struct string_list_item *item = string_list_insert(values, var);
+ struct urlmatch_current_candidate_value *matched = item->util;
++ const struct key_value_info *kvi = ctx->kvi;
+
+ if (!matched) {
+ matched = xmalloc(sizeof(*matched));
@@ builtin/config.c: static int urlmatch_collect_fn(const char *var, const char *value,
} else {
strbuf_reset(&matched->value);
}
-+ memcpy(&matched->kvi, kvi, sizeof(struct key_value_info));
++ matched->kvi = *kvi;

if (value) {
strbuf_addstr(&matched->value, value);
@@ config.c: static const char include_depth_advice[] = N_(
"This might be due to circular includes.");
-static int handle_path_include(struct config_source *cs, const char *path, +static int handle_path_include(struct config_source *cs,
-+ struct key_value_info *kvi,
++ const struct key_value_info *kvi,
+ const char *path,
struct config_include_data *inc)
{
@@ config.c: static int git_config_include(const char *var, const char *value,

if (!strcmp(var, "include.path"))
- ret = handle_path_include(cs, value, inc);
-+ ret = handle_path_include(cs, kvi, value, inc);
++ ret = handle_path_include(cs, ctx->kvi, value, inc);

if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
cond && include_condition_is_true(cs, inc, cond, cond_len) &&
@@ config.c: static int git_config_include(const char *var, const char *value,
if (inc->opts->unconditional_remote_url)
inc->fn = forbid_remote_url;
- ret = handle_path_include(cs, value, inc);
-+ ret = handle_path_include(cs, kvi, value, inc);
++ ret = handle_path_include(cs, ctx->kvi, value, inc);
inc->fn = old_fn;
}

@@ config.c: out_free_ret_1:
{
char *canonical_name;
int ret;
-@@ config.c: static int config_parse_pair(const char *key, const char *value,
++ struct config_context ctx = {
++ .kvi = kvi,
++ };
+
+ if (!strlen(key))
+ return error(_("empty config key"));
if (git_config_parse_key(key, &canonical_name, NULL))
return -1;

- ret = (fn(canonical_name, value, NULL, data) < 0) ? -1 : 0;
-+ ret = (fn(canonical_name, value, kvi, data) < 0) ? -1 : 0;
++ ret = (fn(canonical_name, value, &ctx, data) < 0) ? -1 : 0;
free(canonical_name);
return ret;
}
@@ config.c: static int config_parse_pair(const char *key, const char *value,
const char *value;
struct strbuf **pair;
int ret;
-+ struct key_value_info kvi = { 0 };
++ struct key_value_info kvi = KVI_INIT;
+
+ kvi_from_param(&kvi);

@@ config.c: int git_config_from_parameters(config_fn_t fn, void *data)
int ret = 0;
char *envw = NULL;
struct config_source source = CONFIG_SOURCE_INIT;
-+ struct key_value_info kvi = { 0 };
++ struct key_value_info kvi = KVI_INIT;

source.origin_type = CONFIG_ORIGIN_CMDLINE; config_reader_push_source(&the_reader, &source);
@@ config.c: static int configset_find_element(struct config_set *set, const char *
}

-static int configset_add_value(struct config_reader *reader,
-+static int configset_add_value(struct key_value_info *kvi_p, ++static int configset_add_value(const struct key_value_info *kvi_p,
+ struct config_reader *reader,
struct config_set *set, const char *key, const char *value)
@@ config.c: struct configset_add_data {
#define CONFIGSET_ADD_INIT { 0 }

static int config_set_callback(const char *key, const char *value,
-- struct key_value_info *kvi UNUSED, void *cb)
-+ struct key_value_info *kvi, void *cb)
+- const struct config_context *ctx UNUSED,
++ const struct config_context *ctx,
+ void *cb)
{
struct configset_add_data *data = cb;
- configset_add_value(data->config_reader, data->config_set, key, value);
-+ configset_add_value(kvi, data->config_reader, data->config_set, key, value);
++ configset_add_value(ctx->kvi, data->config_reader, data->config_set,
++ key, value);
return 0;
}

10: 7dc0c46b864 ! 8: 66572df7beb trace2: plumb config kvi

     @@ Commit message
          trace2_def_param_fl(), which gets called by two code paths:
      
          - Through tr2_cfg_cb(), which is a config callback, so it trivially
     -      receives "kvi".
     +      receives "kvi" via the "struct config_context ctx" parameter.
      
          - Through tr2_list_env_vars_fl(), which is a high level function that
            lists environment variables for tracing. This has been secretly
     @@ trace2.c: void trace2_thread_exit_fl(const char *file, int line)
       
       void trace2_def_param_fl(const char *file, int line, const char *param,
      -			 const char *value)
     -+			 const char *value, struct key_value_info *kvi)
     ++			 const char *value, const struct key_value_info *kvi)
       {
       	struct tr2_tgt *tgt_j;
       	int j;
     @@ trace2.h: void trace2_thread_exit_fl(const char *file, int line);
        */
       void trace2_def_param_fl(const char *file, int line, const char *param,
      -			 const char *value);
     -+			 const char *value, struct key_value_info *kvi);
     ++			 const char *value, const struct key_value_info *kvi);
       
       #define trace2_def_param(param, value) \
       	trace2_def_param_fl(__FILE__, __LINE__, (param), (value))
      
       ## trace2/tr2_cfg.c ##
     -@@ trace2/tr2_cfg.c: struct tr2_cfg_data {
     -  * See if the given config key matches any of our patterns of interest.
     -  */
     - static int tr2_cfg_cb(const char *key, const char *value,
     --		      struct key_value_info *kvi UNUSED, void *d)
     -+		      struct key_value_info *kvi, void *d)
     - {
     - 	struct strbuf **s;
     - 	struct tr2_cfg_data *data = (struct tr2_cfg_data *)d;
      @@ trace2/tr2_cfg.c: static int tr2_cfg_cb(const char *key, const char *value,
       		struct strbuf *buf = *s;
       		int wm = wildmatch(buf->buf, key, WM_CASEFOLD);
       		if (wm == WM_MATCH) {
      -			trace2_def_param_fl(data->file, data->line, key, value);
      +			trace2_def_param_fl(data->file, data->line, key, value,
     -+					    kvi);
     ++					    ctx->kvi);
       			return 0;
       		}
       	}
     @@ trace2/tr2_cfg.c: void tr2_cfg_list_config_fl(const char *file, int line)
       
       void tr2_list_env_vars_fl(const char *file, int line)
       {
     -+	struct key_value_info kvi = { 0 };
     ++	struct key_value_info kvi = KVI_INIT;
       	struct strbuf **s;
       
      +	kvi_from_param(&kvi);
     @@ trace2/tr2_tgt.h: typedef void(tr2_tgt_evt_exec_result_fl_t)(const char *file, i
       typedef void(tr2_tgt_evt_param_fl_t)(const char *file, int line,
      -				     const char *param, const char *value);
      +				     const char *param, const char *value,
     -+				     struct key_value_info *kvi);
     ++				     const struct key_value_info *kvi);
       
       typedef void(tr2_tgt_evt_repo_fl_t)(const char *file, int line,
       				    const struct repository *repo);
     @@ trace2/tr2_tgt_event.c: static void fn_exec_result_fl(const char *file, int line
       
       static void fn_param_fl(const char *file, int line, const char *param,
      -			const char *value)
     -+			const char *value, struct key_value_info *kvi)
     ++			const char *value, const struct key_value_info *kvi)
       {
       	const char *event_name = "def_param";
       	struct json_writer jw = JSON_WRITER_INIT;
     @@ trace2/tr2_tgt_normal.c: static void fn_exec_result_fl(const char *file, int lin
       
       static void fn_param_fl(const char *file, int line, const char *param,
      -			const char *value)
     -+			const char *value, struct key_value_info *kvi)
     ++			const char *value, const struct key_value_info *kvi)
       {
       	struct strbuf buf_payload = STRBUF_INIT;
      -	enum config_scope scope = current_config_scope();
     @@ trace2/tr2_tgt_perf.c: static void fn_exec_result_fl(const char *file, int line,
       
       static void fn_param_fl(const char *file, int line, const char *param,
      -			const char *value)
     -+			const char *value, struct key_value_info *kvi)
     ++			const char *value, const struct key_value_info *kvi)
       {
       	const char *event_name = "def_param";
       	struct strbuf buf_payload = STRBUF_INIT;
 11:  504eb206b5a !  9:  123e19dda4a config: pass kvi to die_bad_number()
     @@ Commit message
      
          In config.c, this requires changing the signature of
          git_configset_get_value() to 'return' "kvi" in an out parameter so that
     -    git_configset_get_<type>() can pass it to git_config_<type>().
     +    git_configset_get_<type>() can pass it to git_config_<type>(). Only
     +    numeric types will use "kvi", so for non-numeric types (e.g.
     +    git_configset_get_string()), pass NULL to indicate that the out
     +    parameter isn't needed.
      
     -    Outside of config.c, config callbacks now need to pass "kvi" to any of
     -    the git_config_<type>() functions that parse a config string into a
     -    number type. Included is a .cocci patch to make that refactor. In cases
     -    where "kvi" would never be used, pass NULL, e.g.:
     +    Outside of config.c, config callbacks now need to pass "ctx->kvi" to any
     +    of the git_config_<type>() functions that parse a config string into a
     +    number type. Included is a .cocci patch to make that refactor.
      
     -    - In config.c, when we are parsing a boolean instead of a number
     -    - In builtin/config.c, when calling normalize_value() before setting
     -      config to something the user gave us.
     +    The only exceptional case is builtin/config.c, where git_config_<type>()
     +    is called outside of a config callback (namely, on user-provided input),
     +    so config source information has never been available. In this case,
     +    die_bad_number() defaults to a generic, but perfectly descriptive
     +    message. Let's provide a safe, non-NULL for "kvi" anyway, but make sure
     +    not to change the message.
      
          Signed-off-by: Glen Choo <[email protected]>
      
     @@ archive-tar.c: static int tar_filter_config(const char *var, const char *value,
       }
       
       static int git_tar_config(const char *var, const char *value,
     --			  struct key_value_info *kvi UNUSED, void *cb)
     -+			  struct key_value_info *kvi, void *cb)
     +-			  const struct config_context *ctx UNUSED, void *cb)
     ++			  const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, "tar.umask")) {
       		if (value && !strcmp(value, "user")) {
     @@ archive-tar.c: static int tar_filter_config(const char *var, const char *value,
       			umask(tar_umask);
       		} else {
      -			tar_umask = git_config_int(var, value);
     -+			tar_umask = git_config_int(var, value, kvi);
     ++			tar_umask = git_config_int(var, value, ctx->kvi);
       		}
       		return 0;
       	}
     @@ builtin/commit-graph.c: static int write_option_max_new_filters(const struct opt
       }
       
       static int git_commit_graph_write_config(const char *var, const char *value,
     --					 struct key_value_info *kvi UNUSED,
     -+					 struct key_value_info *kvi,
     +-					 const struct config_context *ctx UNUSED,
     ++					 const struct config_context *ctx,
       					 void *cb UNUSED)
       {
       	if (!strcmp(var, "commitgraph.maxnewfilters"))
      -		write_opts.max_new_filters = git_config_int(var, value);
     -+		write_opts.max_new_filters = git_config_int(var, value, kvi);
     ++		write_opts.max_new_filters = git_config_int(var, value, ctx->kvi);
       	/*
       	 * No need to fall-back to 'git_default_config', since this was already
       	 * called in 'cmd_commit_graph()'.
     @@ builtin/commit.c: static int git_status_config(const char *k, const char *v,
       	if (!strcmp(k, "status.submodulesummary")) {
       		int is_bool;
      -		s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
     -+		s->submodule_summary = git_config_bool_or_int(k, v, kvi,
     ++		s->submodule_summary = git_config_bool_or_int(k, v, ctx->kvi,
      +							      &is_bool);
       		if (is_bool && s->submodule_summary)
       			s->submodule_summary = -1;
     @@ builtin/commit.c: static int git_status_config(const char *k, const char *v,
       	if (!strcmp(k, "diff.renamelimit")) {
       		if (s->rename_limit == -1)
      -			s->rename_limit = git_config_int(k, v);
     -+			s->rename_limit = git_config_int(k, v, kvi);
     ++			s->rename_limit = git_config_int(k, v, ctx->kvi);
       		return 0;
       	}
       	if (!strcmp(k, "status.renamelimit")) {
      -		s->rename_limit = git_config_int(k, v);
     -+		s->rename_limit = git_config_int(k, v, kvi);
     ++		s->rename_limit = git_config_int(k, v, ctx->kvi);
       		return 0;
       	}
       	if (!strcmp(k, "diff.renames")) {
     @@ builtin/commit.c: static int git_commit_config(const char *k, const char *v,
       	if (!strcmp(k, "commit.verbose")) {
       		int is_bool;
      -		config_commit_verbose = git_config_bool_or_int(k, v, &is_bool);
     -+		config_commit_verbose = git_config_bool_or_int(k, v, kvi,
     ++		config_commit_verbose = git_config_bool_or_int(k, v, ctx->kvi,
      +							       &is_bool);
       		return 0;
       	}
     @@ builtin/config.c: static char *normalize_value(const char *key, const char *valu
       		if (!is_bool)
       			return xstrfmt("%d", v);
       		else
     +@@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix)
     + 	char *value = NULL;
     + 	int flags = 0;
     + 	int ret = 0;
     ++	struct key_value_info default_kvi = KVI_INIT;
     + 
     + 	given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
     + 
      @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix)
       	else if (actions == ACTION_SET) {
       		check_write();
       		check_argc(argc, 2, 2);
      -		value = normalize_value(argv[0], argv[1]);
     -+		value = normalize_value(argv[0], argv[1], NULL);
     ++		value = normalize_value(argv[0], argv[1], &default_kvi);
       		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"
     @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix
       		check_write();
       		check_argc(argc, 2, 3);
      -		value = normalize_value(argv[0], argv[1]);
     -+		value = normalize_value(argv[0], argv[1], NULL);
     ++		value = normalize_value(argv[0], argv[1], &default_kvi);
       		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
       							     argv[0], value, argv[2],
       							     flags);
     @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix
       		check_write();
       		check_argc(argc, 2, 2);
      -		value = normalize_value(argv[0], argv[1]);
     -+		value = normalize_value(argv[0], argv[1], NULL);
     ++		value = normalize_value(argv[0], argv[1], &default_kvi);
       		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
       							     argv[0], value,
       							     CONFIG_REGEX_NONE,
     @@ builtin/config.c: int cmd_config(int argc, const char **argv, const char *prefix
       		check_write();
       		check_argc(argc, 2, 3);
      -		value = normalize_value(argv[0], argv[1]);
     -+		value = normalize_value(argv[0], argv[1], NULL);
     ++		value = normalize_value(argv[0], argv[1], &default_kvi);
       		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
       							     argv[0], value, argv[2],
       							     flags | CONFIG_FLAGS_MULTI_REPLACE);
     @@ builtin/fetch.c: static int git_fetch_config(const char *k, const char *v,
       
       	if (!strcmp(k, "submodule.fetchjobs")) {
      -		submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v);
     -+		submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v, kvi);
     ++		submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v, ctx->kvi);
       		return 0;
       	} else if (!strcmp(k, "fetch.recursesubmodules")) {
       		recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
     @@ builtin/fetch.c: static int git_fetch_config(const char *k, const char *v,
       
       	if (!strcmp(k, "fetch.parallel")) {
      -		fetch_parallel_config = git_config_int(k, v);
     -+		fetch_parallel_config = git_config_int(k, v, kvi);
     ++		fetch_parallel_config = git_config_int(k, v, ctx->kvi);
       		if (fetch_parallel_config < 0)
       			die(_("fetch.parallel cannot be negative"));
       		if (!fetch_parallel_config)
      
       ## builtin/fsmonitor--daemon.c ##
      @@ builtin/fsmonitor--daemon.c: static int fsmonitor_config(const char *var, const char *value,
     - 			    struct key_value_info *kvi, void *cb)
     + 			    const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(var, FSMONITOR__IPC_THREADS)) {
      -		int i = git_config_int(var, value);
     -+		int i = git_config_int(var, value, kvi);
     ++		int i = git_config_int(var, value, ctx->kvi);
       		if (i < 1)
       			return error(_("value of '%s' out of range: %d"),
       				     FSMONITOR__IPC_THREADS, i);
     @@ builtin/fsmonitor--daemon.c: static int fsmonitor_config(const char *var, const
       
       	if (!strcmp(var, FSMONITOR__START_TIMEOUT)) {
      -		int i = git_config_int(var, value);
     -+		int i = git_config_int(var, value, kvi);
     ++		int i = git_config_int(var, value, ctx->kvi);
       		if (i < 0)
       			return error(_("value of '%s' out of range: %d"),
       				     FSMONITOR__START_TIMEOUT, i);
     @@ builtin/fsmonitor--daemon.c: static int fsmonitor_config(const char *var, const
       	if (!strcmp(var, FSMONITOR__ANNOUNCE_STARTUP)) {
       		int is_bool;
      -		int i = git_config_bool_or_int(var, value, &is_bool);
     -+		int i = git_config_bool_or_int(var, value, kvi, &is_bool);
     ++		int i = git_config_bool_or_int(var, value, ctx->kvi, &is_bool);
       		if (i < 0)
       			return error(_("value of '%s' not bool or int: %d"),
       				     var, i);
     @@ builtin/grep.c: static int grep_cmd_config(const char *var, const char *value,
       
       	if (!strcmp(var, "grep.threads")) {
      -		num_threads = git_config_int(var, value);
     -+		num_threads = git_config_int(var, value, kvi);
     ++		num_threads = git_config_int(var, value, ctx->kvi);
       		if (num_threads < 0)
       			die(_("invalid number of threads specified (%d) for %s"),
       			    num_threads, var);
     @@ builtin/index-pack.c: static int git_index_pack_config(const char *k, const char
       
       	if (!strcmp(k, "pack.indexversion")) {
      -		opts->version = git_config_int(k, v);
     -+		opts->version = git_config_int(k, v, kvi);
     ++		opts->version = git_config_int(k, v, ctx->kvi);
       		if (opts->version > 2)
       			die(_("bad pack.indexVersion=%"PRIu32), opts->version);
       		return 0;
       	}
       	if (!strcmp(k, "pack.threads")) {
      -		nr_threads = git_config_int(k, v);
     -+		nr_threads = git_config_int(k, v, kvi);
     ++		nr_threads = git_config_int(k, v, ctx->kvi);
       		if (nr_threads < 0)
       			die(_("invalid number of threads specified (%d)"),
       			    nr_threads);
     @@ builtin/log.c: static int git_log_config(const char *var, const char *value,
       		return git_config_string(&fmt_patch_subject_prefix, var, value);
       	if (!strcmp(var, "format.filenamemaxlength")) {
      -		fmt_patch_name_max = git_config_int(var, value);
     -+		fmt_patch_name_max = git_config_int(var, value, kvi);
     ++		fmt_patch_name_max = git_config_int(var, value, ctx->kvi);
       		return 0;
       	}
       	if (!strcmp(var, "format.encodeemailheaders")) {
      
       ## builtin/pack-objects.c ##
      @@ builtin/pack-objects.c: static int git_pack_config(const char *k, const char *v,
     - 			   struct key_value_info *kvi, void *cb)
     + 			   const struct config_context *ctx, void *cb)
       {
       	if (!strcmp(k, "pack.window")) {
      -		window = git_config_int(k, v);
     -+		window = git_config_int(k, v, kvi);
     ++		window = git_config_int(k, v, ctx->kvi);
       		return 0;
       	}
       	if (!strcmp(k, "pack.windowmemory")) {
      -		window_memory_limit = git_config_ulong(k, v);
     -+		window_memory_limit = git_config_ulong(k, v, kvi);
     ++		window_memory_limit = git_config_ulong(k, v, ctx->kvi);
       		return 0;
       	}
       	if (!strcmp(k, "pack.depth")) {
      -		depth = git_config_int(k, v);
     -+		depth = git_config_int(k, v, kvi);
     ++		depth = git_config_int(k, v, ctx->kvi);
       		return 0;
       	}
       	if (!strcmp(k, "pack.deltacachesize")) {
      -		max_delta_cache_size = git_config_int(k, v);
     -+		max_delta_cache_size = git_config_int(k, v, kvi);
     ++		max_delta_cache_size = git_config_int(k, v, ctx->kvi);
       		return 0;
       	}
       	if (!strcmp(k, "pack.deltacachelimit")) {
      -		cache_max_small_delta_size = git_config_int(k, v);
     -+		cache_max_small_delta_size = git_config_int(k, v, kvi);
     ++		cache_max_small_delta_size = git_config_int(k, v, ctx->kvi);
       		return 0;
       	}
       	if (!strcmp(k, "pack.writebitmaphashcache")) {
     @@ builtin/pack-objects.c: static int git_pack_config(const char *k, const char *v,
       	}
       	if (!strcmp(k, "pack.threads")) {
      -		delta_search_threads = git_config_int(k, v);
     -+		delta_search_threads = git_config_int(k, v, kvi);
     ++		delta_search_threads = git_config_int(k, v, ctx->kvi);
       		if (delta_search_threads < 0)
       			die(_("invalid number of threads specified (%d)"),
       			    delta_search_threads);
     @@ builtin/pack-objects.c: static int git_pack_config(const char *k, const char *v,
       	}
       	if (!strcmp(k, "pack.indexversion")) {
      -		pack_idx_opts.version = git_config_int(k, v);
     -+		pack_idx_opts.version = git_config_int(k, v, kvi);
     ++		pack_idx_opts.version = git_config_int(k, v, ctx->kvi);
       		if (pack_idx_opts.version > 2)
       			die(_("bad pack.indexVersion=%"PRIu32),
       			    pack_idx_opts.version);
     @@ builtin/receive-pack.c: static int receive_pack_config(const char *var, const ch
       
       	if (strcmp(var, "receive.unpacklimit") == 0) {
      -		receive_unpack_limit = git_config_int(var, value);
     -+		receive_unpack_limit = git_config_int(var, value, kvi);
     ++		receive_unpack_limit = git_config_int(var, value, ctx->kvi);
       		return 0;
       	}
       
       	if (strcmp(var, "transfer.unpacklimit") == 0) {
      -		transfer_unpack_limit = git_config_int(var, value);
     -+		transfer_unpack_limit = git_config_int(var, value, kvi);
     ++		transfer_unpack_limit = git_config_int(var, value, ctx->kvi);
       		return 0;
       	}
       
     @@ builtin/receive-pack.c: static int receive_pack_config(const char *var, const ch
       
       	if (strcmp(var, "receive.certnonceslop") == 0) {
      -		nonce_stamp_slop_limit = git_config_ulong(var, value);
     -+		nonce_stamp_slop_limit = git_config_ulong(var, value, kvi);
     ++		nonce_stamp_slop_limit = git_config_ulong(var, value, ctx->kvi);
       		return 0;
       	}
       
     @@ builtin/receive-pack.c: static int receive_pack_config(const char *var, const ch
       
       	if (strcmp(var, "receive.keepalive") == 0) {
      -		keepalive_in_sec = git_config_int(var, value);
     -+		keepalive_in_sec = git_config_int(var, value, kvi);
     ++		keepalive_in_sec = git_config_int(var, value, ctx->kvi);
       		return 0;
       	}
       
       	if (strcmp(var, "receive.maxinputsize") == 0) {
      -		max_input_size = git_config_int64(var, value);
     -+		max_input_size = git_config_int64(var, value, kvi);
     ++		max_input_size = git_config_int64(var, value, ctx->kvi);
       		return 0;
       	}
       
     @@ builtin/submodule--helper.c: static int update_clone_task_finished(int result,
       }
       
       static int git_update_clone_config(const char *var, const char *value,
     --				   struct key_value_info *kvi UNUSED,
     -+				   struct key_value_info *kvi,
     +-				   const struct config_context *ctx UNUSED,
     ++				   const struct config_context *ctx,
       				   void *cb)
       {
       	int *max_jobs = cb;
       
       	if (!strcmp(var, "submodule.fetchjobs"))
      -		*max_jobs = parse_submodule_fetchjobs(var, value);
     -+		*max_jobs = parse_submodule_fetchjobs(var, value, kvi);
     ++		*max_jobs = parse_submodule_fetchjobs(var, value, ctx->kvi);
       	return 0;
       }
       
     @@ config.c: int git_parse_ssize_t(const char *value, ssize_t *ret)
      -static void die_bad_number(struct config_reader *reader, const char *name,
      -			   const char *value)
      +static void die_bad_number(const char *name, const char *value,
     -+			   struct key_value_info *kvi)
     ++			   const struct key_value_info *kvi)
       {
       	const char *error_type = (errno == ERANGE) ?
       		N_("out of range") : N_("invalid unit");
       	const char *bad_numeric = N_("bad numeric config value '%s' for '%s': %s");
      -	const char *config_name = NULL;
      -	enum config_origin_type config_origin = CONFIG_ORIGIN_UNKNOWN;
     ++
     ++	if (!kvi)
     ++		BUG("kvi should not be NULL");
       
       	if (!value)
       		value = "";
     @@ config.c: int git_parse_ssize_t(const char *value, ssize_t *ret)
      -	reader_origin_type(reader, &config_origin);
      -
      -	if (!config_name)
     -+	if (!kvi || !kvi->filename)
     ++	if (!kvi->filename)
       		die(_(bad_numeric), value, name, _(error_type));
       
      -	switch (config_origin) {
     @@ config.c: int git_parse_ssize_t(const char *value, ssize_t *ret)
       
      -int git_config_int(const char *name, const char *value)
      +int git_config_int(const char *name, const char *value,
     -+		   struct key_value_info *kvi)
     ++		   const struct key_value_info *kvi)
       {
       	int ret;
       	if (!git_parse_int(value, &ret))
     @@ config.c: int git_parse_ssize_t(const char *value, ssize_t *ret)
       }
       
      -int64_t git_config_int64(const char *name, const char *value)
     -+int64_t git_config_int64(const char *name, const char *value, struct key_value_info *kvi)
     ++int64_t git_config_int64(const char *name, const char *value,
     ++			 const struct key_value_info *kvi)
       {
       	int64_t ret;
       	if (!git_parse_int64(value, &ret))
     @@ config.c: int git_parse_ssize_t(const char *value, ssize_t *ret)
       
      -unsigned long git_config_ulong(const char *name, const char *value)
      +unsigned long git_config_ulong(const char *name, const char *value,
     -+			       struct key_value_info *kvi)
     ++			       const struct key_value_info *kvi)
       {
       	unsigned long ret;
       	if (!git_parse_ulong(value, &ret))
     @@ config.c: int git_parse_ssize_t(const char *value, ssize_t *ret)
       
      -ssize_t git_config_ssize_t(const char *name, const char *value)
      +ssize_t git_config_ssize_t(const char *name, const char *value,
     -+			   struct key_value_info *kvi)
     ++			   const struct key_value_info *kvi)
       {
       	ssize_t ret;
       	if (!git_parse_ssize_t(value, &ret))
     @@ config.c: int git_parse_maybe_bool(const char *value)
       
      -int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
      +int git_config_bool_or_int(const char *name, const char *value,
     -+			   struct key_value_info *kvi, int *is_bool)
     ++			   const struct key_value_info *kvi, int *is_bool)
       {
       	int v = git_parse_maybe_bool_text(value);
       	if (0 <= v) {
     @@ config.c: static int git_default_core_config(const char *var, const char *value,
       			default_abbrev = the_hash_algo->hexsz;
       		else {
      -			int abbrev = git_config_int(var, value);
     -+			int abbrev = git_config_int(var, value, kvi);
     ++			int abbrev = git_config_int(var, value, ctx->kvi);
       			if (abbrev < minimum_abbrev || abbrev > the_hash_algo->hexsz)
       				return error(_("abbrev length out of range: %d"), abbrev);
       			default_abbrev = abbrev;
     @@ config.c: static int git_default_core_config(const char *var, const char *value,
       
       	if (!strcmp(var, "core.loosecompression")) {
      -		int level = git_config_int(var, value);
     -+		int level = git_config_int(var, value, kvi);
     ++		int level = git_config_int(var, value, ctx->kvi);
       		if (level == -1)
       			level = Z_DEFAULT_COMPRESSION;
       		else if (level < 0 || level > Z_BEST_COMPRESSION)
     @@ config.c: static int git_default_core_config(const char *var, const char *value,
       
       	if (!strcmp(var, "core.compression")) {
      -		int level = git_config_int(var, value);
     -+		int level = git_config_int(var, value, kvi);
     ++		int level = git_config_int(var, value, ctx->kvi);
       		if (level == -1)
       			level = Z_DEFAULT_COMPRESSION;
       		else if (level < 0 || level > Z_BEST_COMPRESSION)
     @@ config.c: static int git_default_core_config(const char *var, const char *value,
       	if (!strcmp(var, "core.packedgitwindowsize")) {
       		int pgsz_x2 = getpagesize() * 2;
      -		packed_git_window_size = git_config_ulong(var, value);
     -+		packed_git_window_size = git_config_ulong(var, value, kvi);
     ++		packed_git_window_size = git_config_ulong(var, value, ctx->kvi);
       
       		/* This value must be multiple of (pagesize * 2) */
       		packed_git_window_size /= pgsz_x2;
     @@ config.c: static int git_default_core_config(const char *var, const char *value,
       
       	if (!strcmp(var, "core.bigfilethreshold")) {
      -		big_file_threshold = git_config_ulong(var, value);
     -+		big_file_threshold = git_config_ulong(var, value, kvi);
     ++		big_file_threshold = git_config_ulong(var, value, ctx->kvi);
       		return 0;
       	}
       
       	if (!strcmp(var, "core.packedgitlimit")) {
      -		packed_git_limit = git_config_ulong(var, value);
     -+		packed_git_limit = git_config_ulong(var, value, kvi);
     ++		packed_git_limit = git_config_ulong(var, value, ctx->kvi);
       		return 0;
       	}
       
       	if (!strcmp(var, "core.deltabasecachelimit")) {
      -		delta_base_cache_limit = git_config_ulong(var, value);
     -+		delta_base_cache_limit = git_config_ulong(var, value, kvi);
     ++		delta_base_cache_limit = git_config_ulong(var, value, ctx->kvi);
       		return 0;
       	}
       
     @@ config.c: int git_default_config(const char *var, const char *value,
       
       	if (!strcmp(var, "pack.packsizelimit")) {
      -		pack_size_limit_cfg = git_config_ulong(var, value);
     -+		pack_size_limit_cfg = git_config_ulong(var, value, kvi);
     ++		pack_size_limit_cfg = git_config_ulong(var, value, ctx->kvi);
       		return 0;
       	}
       
       	if (!strcmp(var, "pack.compression")) {
      -		int level = git_config_int(var, value);
     -+		int level = git_config_int(var, value, kvi);
     ++		int level = git_config_int(var, value, ctx->kvi);
       		if (level == -1)
       			level = Z_DEFAULT_COMPRESSION;
       		else if (level < 0 || level > Z_BEST_COMPRESSION)
      @@ config.c: static void configset_iter(struct config_reader *reader, struct config_set *set,
     + 		value_index = list->items[i].value_index;
       		values = &entry->value_list;
     - 		kvi = values->items[value_index].util;
       
      -		config_reader_set_kvi(reader, values->items[value_index].util);
     --
     - 		if (fn(entry->key, values->items[value_index].string, kvi, data) < 0)
     - 			git_die_config_linenr(entry->key, kvi->filename, kvi->linenr);
     + 		ctx.kvi = values->items[value_index].util;
     + 		if (fn(entry->key, values->items[value_index].string, &ctx, data) < 0)
     + 			git_die_config_linenr(entry->key,
     + 					      ctx.kvi->filename,
     + 					      ctx.kvi->linenr);
      -		config_reader_set_kvi(reader, NULL);
       	}
       }
     @@ config.h: int git_parse_maybe_bool(const char *);
        * otherwise, returns the parsed result.
        */
      -int git_config_int(const char *, const char *);
     -+int git_config_int(const char *, const char *, struct key_value_info *);
     ++int git_config_int(const char *, const char *, const struct key_value_info *);
       
      -int64_t git_config_int64(const char *, const char *);
     -+int64_t git_config_int64(const char *, const char *, struct key_value_info *);
     ++int64_t git_config_int64(const char *, const char *,
     ++			 const struct key_value_info *);
       
       /**
        * Identical to `git_config_int`, but for unsigned longs.
        */
      -unsigned long git_config_ulong(const char *, const char *);
     -+unsigned long git_config_ulong(const char *, const char *, struct key_value_info *);
     ++unsigned long git_config_ulong(const char *, const char *,
     ++			       const struct key_value_info *);
       
      -ssize_t git_config_ssize_t(const char *, const char *);
     -+ssize_t git_config_ssize_t(const char *, const char *, struct key_value_info *);
     ++ssize_t git_config_ssize_t(const char *, const char *,
     ++			   const struct key_value_info *);
       
       /**
        * Same as `git_config_bool`, except that integers are returned as-is, and
        * an `is_bool` flag is unset.
        */
      -int git_config_bool_or_int(const char *, const char *, int *);
     -+int git_config_bool_or_int(const char *, const char *, struct key_value_info *,
     -+			   int *);
     ++int git_config_bool_or_int(const char *, const char *,
     ++			   const struct key_value_info *, int *);
       
       /**
        * Parse a string into a boolean value, respecting keywords like "true" and
     @@ contrib/coccinelle/git_config_number.cocci (new)
      +git_config_ssize_t
      +)
      +  (C1, C2
     -++ , kvi
     +++ , ctx->kvi
      +  )
      +|
      +(
     @@ contrib/coccinelle/git_config_number.cocci (new)
      +|
      +git_config_bool_or_int
      +)
     -+  (C1, C2,
     -++ kvi,
     -+  C3
     ++  (C1, C2
     +++ , ctx->kvi
     ++ , C3
      +  )
      +)
      
     @@ diff.c: int git_diff_ui_config(const char *var, const char *value,
       	}
       	if (!strcmp(var, "diff.context")) {
      -		diff_context_default = git_config_int(var, value);
     -+		diff_context_default = git_config_int(var, value, kvi);
     ++		diff_context_default = git_config_int(var, value, ctx->kvi);
       		if (diff_context_default < 0)
       			return -1;
       		return 0;
     @@ diff.c: int git_diff_ui_config(const char *var, const char *value,
       	if (!strcmp(var, "diff.interhunkcontext")) {
      -		diff_interhunk_context_default = git_config_int(var, value);
      +		diff_interhunk_context_default = git_config_int(var, value,
     -+								kvi);
     ++								ctx->kvi);
       		if (diff_interhunk_context_default < 0)
       			return -1;
       		return 0;
     @@ diff.c: int git_diff_ui_config(const char *var, const char *value,
       	}
       	if (!strcmp(var, "diff.statgraphwidth")) {
      -		diff_stat_graph_width = git_config_int(var, value);
     -+		diff_stat_graph_width = git_config_int(var, value, kvi);
     ++		diff_stat_graph_width = git_config_int(var, value, ctx->kvi);
       		return 0;
       	}
       	if (!strcmp(var, "diff.external"))
     @@ diff.c: int git_diff_basic_config(const char *var, const char *value,
       
       	if (!strcmp(var, "diff.renamelimit")) {
      -		diff_rename_limit_default = git_config_int(var, value);
     -+		diff_rename_limit_default = git_config_int(var, value, kvi);
     ++		diff_rename_limit_default = git_config_int(var, value, ctx->kvi);
       		return 0;
       	}
       
     @@ fmt-merge-msg.c: int fmt_merge_msg_config(const char *key, const char *value,
       	if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
       		int is_bool;
      -		merge_log_config = git_config_bool_or_int(key, value, &is_bool);
     -+		merge_log_config = git_config_bool_or_int(key, value, kvi, &is_bool);
     ++		merge_log_config = git_config_bool_or_int(key, value, ctx->kvi, &is_bool);
       		if (!is_bool && merge_log_config < 0)
       			return error("%s: negative length %s", key, value);
       		if (is_bool && merge_log_config)
     @@ help.c: static struct cmdnames aliases;
       #define AUTOCORRECT_IMMEDIATELY (-1)
       
       static int git_unknown_cmd_config(const char *var, const char *value,
     --				  struct key_value_info *kvi UNUSED,
     --				  void *cb UNUSED)
     -+				  struct key_value_info *kvi, void *cb UNUSED)
     +-				  const struct config_context *ctx UNUSED,
     ++				  const struct config_context *ctx,
     + 				  void *cb UNUSED)
       {
       	const char *p;
     - 
      @@ help.c: static int git_unknown_cmd_config(const char *var, const char *value,
       		} else if (!strcmp(value, "prompt")) {
       			autocorrect = AUTOCORRECT_PROMPT;
       		} else {
      -			int v = git_config_int(var, value);
     -+			int v = git_config_int(var, value, kvi);
     ++			int v = git_config_int(var, value, ctx->kvi);
       			autocorrect = (v < 0)
       				? AUTOCORRECT_IMMEDIATELY : v;
       		}
     @@ http.c: static int http_options(const char *var, const char *value,
       
       	if (!strcmp("http.minsessions", var)) {
      -		min_curl_sessions = git_config_int(var, value);
     -+		min_curl_sessions = git_config_int(var, value, kvi);
     ++		min_curl_sessions = git_config_int(var, value, ctx->kvi);
       		if (min_curl_sessions > 1)
       			min_curl_sessions = 1;
       		return 0;
       	}
       	if (!strcmp("http.maxrequests", var)) {
      -		max_requests = git_config_int(var, value);
     -+		max_requests = git_config_int(var, value, kvi);
     ++		max_requests = git_config_int(var, value, ctx->kvi);
       		return 0;
       	}
       	if (!strcmp("http.lowspeedlimit", var)) {
      -		curl_low_speed_limit = (long)git_config_int(var, value);
     -+		curl_low_speed_limit = (long)git_config_int(var, value, kvi);
     ++		curl_low_speed_limit = (long)git_config_int(var, value, ctx->kvi);
       		return 0;
       	}
       	if (!strcmp("http.lowspeedtime", var)) {
      -		curl_low_speed_time = (long)git_config_int(var, value);
     -+		curl_low_speed_time = (long)git_config_int(var, value, kvi);
     ++		curl_low_speed_time = (long)git_config_int(var, value, ctx->kvi);
       		return 0;
       	}
       
     @@ http.c: static int http_options(const char *var, const char *value,
       
       	if (!strcmp("http.postbuffer", var)) {
      -		http_post_buffer = git_config_ssize_t(var, value);
     -+		http_post_buffer = git_config_ssize_t(var, value, kvi);
     ++		http_post_buffer = git_config_ssize_t(var, value, ctx->kvi);
       		if (http_post_buffer < 0)
       			warning(_("negative value for http.postBuffer; defaulting to %d"), LARGE_PACKET_MAX);
       		if (http_post_buffer < LARGE_PACKET_MAX)
     @@ imap-send.c: static int git_imap_config(const char *var, const char *val,
       		return git_config_string(&server.auth_method, var, val);
       	else if (!strcmp("imap.port", var))
      -		server.port = git_config_int(var, val);
     -+		server.port = git_config_int(var, val, kvi);
     ++		server.port = git_config_int(var, val, ctx->kvi);
       	else if (!strcmp("imap.host", var)) {
       		if (!val) {
       			git_die_config("imap.host", "Missing value for 'imap.host'");
     @@ sequencer.c: static int git_config_string_dup(char **dest,
       }
       
       static int populate_opts_cb(const char *key, const char *value,
     --			    struct key_value_info *kvi UNUSED, void *data)
     -+			    struct key_value_info *kvi, void *data)
     +-			    const struct config_context *ctx UNUSED,
     ++			    const struct config_context *ctx,
     + 			    void *data)
       {
       	struct replay_opts *opts = data;
     - 	int error_flag = 1;
      @@ sequencer.c: static int populate_opts_cb(const char *key, const char *value,
       	if (!value)
       		error_flag = 0;
       	else if (!strcmp(key, "options.no-commit"))
      -		opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
     -+		opts->no_commit = git_config_bool_or_int(key, value, kvi, &error_flag);
     ++		opts->no_commit = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
       	else if (!strcmp(key, "options.edit"))
      -		opts->edit = git_config_bool_or_int(key, value, &error_flag);
     -+		opts->edit = git_config_bool_or_int(key, value, kvi, &error_flag);
     ++		opts->edit = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
       	else if (!strcmp(key, "options.allow-empty"))
       		opts->allow_empty =
      -			git_config_bool_or_int(key, value, &error_flag);
     -+			git_config_bool_or_int(key, value, kvi, &error_flag);
     ++			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
       	else if (!strcmp(key, "options.allow-empty-message"))
       		opts->allow_empty_message =
      -			git_config_bool_or_int(key, value, &error_flag);
     -+			git_config_bool_or_int(key, value, kvi, &error_flag);
     ++			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
       	else if (!strcmp(key, "options.keep-redundant-commits"))
       		opts->keep_redundant_commits =
      -			git_config_bool_or_int(key, value, &error_flag);
     -+			git_config_bool_or_int(key, value, kvi, &error_flag);
     ++			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
       	else if (!strcmp(key, "options.signoff"))
      -		opts->signoff = git_config_bool_or_int(key, value, &error_flag);
     -+		opts->signoff = git_config_bool_or_int(key, value, kvi, &error_flag);
     ++		opts->signoff = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
       	else if (!strcmp(key, "options.record-origin"))
      -		opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
     -+		opts->record_origin = git_config_bool_or_int(key, value, kvi, &error_flag);
     ++		opts->record_origin = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
       	else if (!strcmp(key, "options.allow-ff"))
      -		opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
     -+		opts->allow_ff = git_config_bool_or_int(key, value, kvi, &error_flag);
     ++		opts->allow_ff = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
       	else if (!strcmp(key, "options.mainline"))
      -		opts->mainline = git_config_int(key, value);
     -+		opts->mainline = git_config_int(key, value, kvi);
     ++		opts->mainline = git_config_int(key, value, ctx->kvi);
       	else if (!strcmp(key, "options.strategy"))
       		git_config_string_dup(&opts->strategy, key, value);
       	else if (!strcmp(key, "options.gpg-sign"))
      @@ sequencer.c: static int populate_opts_cb(const char *key, const char *value,
     - 		opts->xopts[opts->xopts_nr++] = xstrdup(value);
     + 		strvec_push(&opts->xopts, value);
       	} else if (!strcmp(key, "options.allow-rerere-auto"))
       		opts->allow_rerere_auto =
      -			git_config_bool_or_int(key, value, &error_flag) ?
     -+			git_config_bool_or_int(key, value, kvi, &error_flag) ?
     ++			git_config_bool_or_int(key, value, ctx->kvi, &error_flag) ?
       				RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;
       	else if (!strcmp(key, "options.default-msg-cleanup")) {
       		opts->explicit_cleanup = 1;
     @@ setup.c: static int check_repo_format(const char *var, const char *value,
       
       	if (strcmp(var, "core.repositoryformatversion") == 0)
      -		data->version = git_config_int(var, value);
     -+		data->version = git_config_int(var, value, kvi);
     ++		data->version = git_config_int(var, value, ctx->kvi);
       	else if (skip_prefix(var, "extensions.", &ext)) {
       		switch (handle_extension_v0(var, value, ext, data)) {
       		case EXTENSION_ERROR:
     @@ submodule-config.c: static int parse_fetch_recurse(const char *opt, const char *
       
      -int parse_submodule_fetchjobs(const char *var, const char *value)
      +int parse_submodule_fetchjobs(const char *var, const char *value,
     -+			      struct key_value_info *kvi)
     ++			      const struct key_value_info *kvi)
       {
      -	int fetchjobs = git_config_int(var, value);
      +	int fetchjobs = git_config_int(var, value, kvi);
     @@ submodule-config.c: struct fetch_config {
       };
       
       static int gitmodules_fetch_config(const char *var, const char *value,
     --				   struct key_value_info *kvi UNUSED,
     --				   void *cb)
     -+				   struct key_value_info *kvi, void *cb)
     +-				   const struct config_context *ctx UNUSED,
     ++				   const struct config_context *ctx,
     + 				   void *cb)
       {
       	struct fetch_config *config = cb;
       	if (!strcmp(var, "submodule.fetchjobs")) {
       		if (config->max_children)
       			*(config->max_children) =
      -				parse_submodule_fetchjobs(var, value);
     -+				parse_submodule_fetchjobs(var, value, kvi);
     ++				parse_submodule_fetchjobs(var, value, ctx->kvi);
       		return 0;
       	} else if (!strcmp(var, "fetch.recursesubmodules")) {
       		if (config->recurse_submodules)
     @@ submodule-config.c: void fetch_config_from_gitmodules(int *max_children, int *re
       }
       
       static int gitmodules_update_clone_config(const char *var, const char *value,
     --					  struct key_value_info *kvi UNUSED,
     --					  void *cb)
     -+					  struct key_value_info *kvi, void *cb)
     +-					  const struct config_context *ctx UNUSED,
     ++					  const struct config_context *ctx,
     + 					  void *cb)
       {
       	int *max_jobs = cb;
       	if (!strcmp(var, "submodule.fetchjobs"))
      -		*max_jobs = parse_submodule_fetchjobs(var, value);
     -+		*max_jobs = parse_submodule_fetchjobs(var, value, kvi);
     ++		*max_jobs = parse_submodule_fetchjobs(var, value, ctx->kvi);
       	return 0;
       }
       
     @@ submodule-config.h: struct repository;
       
      -int parse_submodule_fetchjobs(const char *var, const char *value);
      +int parse_submodule_fetchjobs(const char *var, const char *value,
     -+			      struct key_value_info *kvi);
     ++			      const struct key_value_info *kvi);
       int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
       struct option;
       int option_fetch_parse_recurse_submodules(const struct option *opt,
     @@ t/helper/test-config.c: static int iterate_cb(const char *var, const char *value
       }
       
       static int parse_int_cb(const char *var, const char *value,
     --			struct key_value_info *kvi UNUSED, void *data)
     -+			struct key_value_info *kvi, void *data)
     +-			const struct config_context *ctx UNUSED, void *data)
     ++			const struct config_context *ctx, void *data)
       {
       	const char *key_to_match = data;
       
       	if (!strcmp(key_to_match, var)) {
      -		int parsed = git_config_int(value, value);
     -+		int parsed = git_config_int(value, value, kvi);
     ++		int parsed = git_config_int(value, value, ctx->kvi);
       		printf("%d\n", parsed);
       	}
       	return 0;
     @@ t/helper/test-config.c: int cmd__config(int argc, const char **argv)
       				printf("(NULL)\n");
       			else
      
     + ## trace2/tr2_cfg.c ##
     +@@ trace2/tr2_cfg.c: struct tr2_cfg_data {
     +  * See if the given config key matches any of our patterns of interest.
     +  */
     + static int tr2_cfg_cb(const char *key, const char *value,
     +-		      const struct config_context *ctx UNUSED, void *d)
     ++		      const struct config_context *ctx, void *d)
     + {
     + 	struct strbuf **s;
     + 	struct tr2_cfg_data *data = (struct tr2_cfg_data *)d;
     +
       ## upload-pack.c ##
      @@ upload-pack.c: static int find_symref(const char *refname,
       }
       
       static int parse_object_filter_config(const char *var, const char *value,
      -				       struct upload_pack_data *data)
     -+				      struct key_value_info *kvi,
     ++				      const struct key_value_info *kvi,
      +				      struct upload_pack_data *data)
       {
       	struct strbuf buf = STRBUF_INIT;
     @@ upload-pack.c: static int parse_object_filter_config(const char *var, const char
       }
       
       static int upload_pack_config(const char *var, const char *value,
     --			      struct key_value_info *kvi UNUSED,
     -+			      struct key_value_info *kvi,
     +-			      const struct config_context *ctx UNUSED,
     ++			      const struct config_context *ctx,
       			      void *cb_data)
       {
       	struct upload_pack_data *data = cb_data;
     @@ upload-pack.c: static int upload_pack_config(const char *var, const char *value,
       			data->allow_uor &= ~ALLOW_ANY_SHA1;
       	} else if (!strcmp("uploadpack.keepalive", var)) {
      -		data->keepalive = git_config_int(var, value);
     -+		data->keepalive = git_config_int(var, value, kvi);
     ++		data->keepalive = git_config_int(var, value, ctx->kvi);
       		if (!data->keepalive)
       			data->keepalive = -1;
       	} else if (!strcmp("uploadpack.allowfilter", var)) {
     @@ upload-pack.c: static int upload_pack_config(const char *var, const char *value,
       	}
       
      -	if (parse_object_filter_config(var, value, data) < 0)
     -+	if (parse_object_filter_config(var, value, kvi, data) < 0)
     ++	if (parse_object_filter_config(var, value, ctx->kvi, data) < 0)
       		return -1;
       
       	return parse_hide_refs_config(var, value, "uploadpack", &data->hidden_refs);
 12:  52db0d3be82 ! 10:  8ec24b018e9 config.c: remove config_reader from configsets
     @@ config.c: int config_with_options(config_fn_t fn, void *data,
      @@ config.c: static int configset_find_element(struct config_set *set, const char *key,
       }
       
     - static int configset_add_value(struct key_value_info *kvi_p,
     + static int configset_add_value(const struct key_value_info *kvi_p,
      -			       struct config_reader *reader,
       			       struct config_set *set, const char *key,
       			       const char *value)
       {
     -@@ config.c: static int configset_add_value(struct key_value_info *kvi_p,
     +@@ config.c: static int configset_add_value(const struct key_value_info *kvi_p,
       	l_item->e = e;
       	l_item->value_index = e->value_list.nr - 1;
       
     @@ config.c: void git_configset_clear(struct config_set *set)
      -#define CONFIGSET_ADD_INIT { 0 }
      -
       static int config_set_callback(const char *key, const char *value,
     - 			       struct key_value_info *kvi, void *cb)
     + 			       const struct config_context *ctx,
     + 			       void *cb)
       {
      -	struct configset_add_data *data = cb;
     --	configset_add_value(kvi, data->config_reader, data->config_set, key, value);
     +-	configset_add_value(ctx->kvi, data->config_reader, data->config_set,
     +-			    key, value);
      +	struct config_set *set = cb;
     -+	configset_add_value(kvi, set, key, value);
     ++	configset_add_value(ctx->kvi, set, key, value);
       	return 0;
       }

13: 7d9b9eefc78 ! 11: 8ae115cff88 config: add kvi.path, use it to evaluate includes
@@ Commit message
Include directives are evaluated using the path of the config file. To reduce the dependence on "config_reader.source", add a new "key_value_info.path" member and use that instead of
- "config_source.path".
+ "config_source.path". This allows us to remove a "struct config_reader
+ *" field from "struct config_include_data", which will subsequently
+ allow us to remove "struct config_reader" entirely.

@@ config.c: static const char include_depth_advice[] = N_(
" %s\n"
"This might be due to circular includes.");
-static int handle_path_include(struct config_source *cs,
-- struct key_value_info *kvi,
-- const char *path,
-+static int handle_path_include(struct key_value_info *kvi, const char *path,
+- const struct key_value_info *kvi,
++static int handle_path_include(const struct key_value_info *kvi,
+ const char *path,
struct config_include_data *inc)
{
- int ret = 0;
@@ config.c: static int handle_path_include(struct config_source *cs,
if (!is_absolute_path(path)) {
char *slash;
@@ config.c: static void add_trailing_starstar_for_dir(struct strbuf *pat)
}

-static int prepare_include_condition_pattern(struct config_source *cs,
-+static int prepare_include_condition_pattern(struct key_value_info *kvi, ++static int prepare_include_condition_pattern(const struct key_value_info *kvi,
struct strbuf *pat)
{
struct strbuf path = STRBUF_INIT;
@@ config.c: static int prepare_include_condition_pattern(struct config_source *cs,
}

-static int include_by_gitdir(struct config_source *cs,
-+static int include_by_gitdir(struct key_value_info *kvi, ++static int include_by_gitdir(const struct key_value_info *kvi,
const struct config_options *opts,
const char *cond, size_t cond_len, int icase)
{
@@ config.c: static int include_by_remote_url(struct config_include_data *inc,
}

-static int include_condition_is_true(struct config_source *cs,
-+static int include_condition_is_true(struct key_value_info *kvi, ++static int include_condition_is_true(const struct key_value_info *kvi,
struct config_include_data *inc, const char *cond, size_t cond_len)
{
@@ config.c: static int include_by_remote_url(struct config_include_data *inc,
return include_by_branch(cond, cond_len);
else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,
@@ config.c: static int git_config_include(const char *var, const char *value,
- struct key_value_info *kvi, void *data)
+ void *data)
{
struct config_include_data *inc = data;
- struct config_source *cs = inc->config_reader->source;
@@ config.c: static int git_config_include(const char *var, const char *value,
return ret;

if (!strcmp(var, "include.path"))
-- ret = handle_path_include(cs, kvi, value, inc);
-+ ret = handle_path_include(kvi, value, inc);
+- ret = handle_path_include(cs, ctx->kvi, value, inc);
++ ret = handle_path_include(ctx->kvi, value, inc);

if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
- cond && include_condition_is_true(cs, inc, cond, cond_len) &&
-+ cond && include_condition_is_true(kvi, inc, cond, cond_len) &&
++ cond && include_condition_is_true(ctx->kvi, inc, cond, cond_len) &&
!strcmp(key, "path")) {
config_fn_t old_fn = inc->fn;

if (inc->opts->unconditional_remote_url)
inc->fn = forbid_remote_url;
-- ret = handle_path_include(cs, kvi, value, inc);
-+ ret = handle_path_include(kvi, value, inc);
+- ret = handle_path_include(cs, ctx->kvi, value, inc);
++ ret = handle_path_include(ctx->kvi, value, inc);
inc->fn = old_fn;
}

@@ config.h: struct key_value_info {
enum config_scope scope;
+ const char *path;
};
+ #define KVI_INIT { \
+ .filename = NULL, \
+ .linenr = -1, \
+ .origin_type = CONFIG_ORIGIN_UNKNOWN, \
+ .scope = CONFIG_SCOPE_UNKNOWN, \
++ .path = NULL, \
+ }

- /**
+ /* Captures additional information that a config callback can use. */
14: 9e35b5b1f4d ! 12: 484d553cc7d config: pass source to config_parser_event_fn_t
@@ Commit message

..so that the callback can use a "struct config_source" parameter instead of "config_reader.source". "struct config_source" is internal to
- config.c, but this refactor is okay because this function has only ever
- been (and probably ever will be) used internally by config.c.
+ config.c, so we are adding a pointer to a struct defined in config.c
+ into a public function signature defined in config.h, but this is okay
+ because this function has only ever been (and probably ever will be)
+ used internally by config.c.

As a result, the_reader isn't used anywhere, so "struct config_reader" is obsolete (it was only intended to be used with the_reader). Remove
@@ config.c: int git_config_from_parameters(config_fn_t fn, void *data)
int ret = 0;
char *envw = NULL;
- struct config_source source = CONFIG_SOURCE_INIT;
- struct key_value_info kvi = { 0 };
+ struct key_value_info kvi = KVI_INIT;

- source.origin_type = CONFIG_ORIGIN_CMDLINE;
- config_reader_push_source(&the_reader, &source);

Signature · 1 line
gitgitgadget
#55 lore raw

PATCH v3 1/12 config: inline git_color_default_config

From: Glen Choo <[email protected]>

git_color_default_config() is a shorthand for calling two other config callbacks. There are no other non-static functions that do this and it will complicate our refactoring of config_fn_t so inline it instead.

---
 builtin/branch.c      | 5 ++++-
 builtin/clean.c       | 6 ++++--
 builtin/grep.c        | 5 ++++-
 builtin/show-branch.c | 5 ++++-
 builtin/tag.c         | 6 +++++-
 color.c               | 8 --------
 color.h               | 6 +-----
 7 files changed, 22 insertions(+), 19 deletions(-)
builtin/branch.c +4 -1
index e6c2655af68..df99e38847b 100644
@@ -117,7 +117,10 @@ static int git_branch_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_color_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
 }
 
 static const char *branch_get_color(enum color_branch ix)
builtin/clean.c +4 -2
index 78852d28cec..57e7f7cac64 100644
@@ -130,8 +130,10 @@ static int git_clean_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	/* inspect the color.ui config variable and others */
-	return git_color_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
 }
 
 static const char *clean_get_color(enum color_clean ix)
builtin/grep.c +4 -1
index b86c754defb..76cf999d310 100644
@@ -293,7 +293,10 @@ static int wait_all(void)
 static int grep_cmd_config(const char *var, const char *value, void *cb)
 {
 	int st = grep_config(var, value, cb);
-	if (git_color_default_config(var, value, NULL) < 0)
+
+	if (git_color_config(var, value, cb) < 0)
+		st = -1;
+	else if (git_default_config(var, value, cb) < 0)
 		st = -1;
 
 	if (!strcmp(var, "grep.threads")) {
builtin/show-branch.c +4 -1
index 7ef4a642c17..a2461270d4b 100644
@@ -579,7 +579,10 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_color_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
 }
 
 static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
builtin/tag.c +5 -1
index 49b64c7a288..1acf5f7a59f 100644
@@ -209,7 +209,11 @@ static int git_tag_config(const char *var, const char *value, void *cb)
 
 	if (starts_with(var, "column."))
 		return git_column_config(var, value, "tag", &colopts);
-	return git_color_default_config(var, value, cb);
+
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
 }
 
 static void write_tag_body(int fd, const struct object_id *oid)
color.c +0 -8
index 83abb11eda0..b24b19566b9 100644
@@ -430,14 +430,6 @@ int git_color_config(const char *var, const char *value, void *cb UNUSED)
 	return 0;
 }
 
-int git_color_default_config(const char *var, const char *value, void *cb)
-{
-	if (git_color_config(var, value, cb) < 0)
-		return -1;
-
-	return git_default_config(var, value, cb);
-}
-
 void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb)
 {
 	if (*color)
color.h +1 -5
index cfc8f841b23..bb28343be21 100644
@@ -88,12 +88,8 @@ extern const int column_colors_ansi_max;
  */
 extern int color_stdout_is_tty;
 
-/*
- * Use the first one if you need only color config; the second is a convenience
- * if you are just going to change to git_default_config, too.
- */
+/* Parse color config. */
 int git_color_config(const char *var, const char *value, void *cb);
-int git_color_default_config(const char *var, const char *value, void *cb);
 
 /*
  * Parse a config option, which can be a boolean or one of
Signature · 1 line
gitgitgadget
#56 lore raw

Re: [PATCH v3 01/12] config: inline git_color_default_config

Quoted 8 lines · Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

git_color_default_config() is a shorthand for calling two other config callbacks. There are no other non-static functions that do this and it will complicate our refactoring of config_fn_t so inline it instead.

---

We would need to deal with git_default_config() that is called from everywhere in exactly the same way, so I am not sure if the presence of git_color_default_config() will complicate so much to require this step while git_default_config() can be dealt with without inlining into its callers, but this change does not hurt anybody either.

OK.

Quoted 128 lines
 builtin/branch.c      | 5 ++++-
 builtin/clean.c       | 6 ++++--
 builtin/grep.c        | 5 ++++-
 builtin/show-branch.c | 5 ++++-
 builtin/tag.c         | 6 +++++-
 color.c               | 8 --------
 color.h               | 6 +-----
 7 files changed, 22 insertions(+), 19 deletions(-)
builtin/branch.c +4 -1
index e6c2655af68..df99e38847b 100644
@@ -117,7 +117,10 @@ static int git_branch_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_color_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
 }
 
 static const char *branch_get_color(enum color_branch ix)
builtin/clean.c +4 -2
index 78852d28cec..57e7f7cac64 100644
@@ -130,8 +130,10 @@ static int git_clean_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	/* inspect the color.ui config variable and others */
-	return git_color_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
 }
 
 static const char *clean_get_color(enum color_clean ix)
builtin/grep.c +4 -1
index b86c754defb..76cf999d310 100644
@@ -293,7 +293,10 @@ static int wait_all(void)
 static int grep_cmd_config(const char *var, const char *value, void *cb)
 {
 	int st = grep_config(var, value, cb);
-	if (git_color_default_config(var, value, NULL) < 0)
+
+	if (git_color_config(var, value, cb) < 0)
+		st = -1;
+	else if (git_default_config(var, value, cb) < 0)
 		st = -1;
 
 	if (!strcmp(var, "grep.threads")) {
builtin/show-branch.c +4 -1
index 7ef4a642c17..a2461270d4b 100644
@@ -579,7 +579,10 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_color_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
 }
 
 static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
builtin/tag.c +5 -1
index 49b64c7a288..1acf5f7a59f 100644
@@ -209,7 +209,11 @@ static int git_tag_config(const char *var, const char *value, void *cb)
 
 	if (starts_with(var, "column."))
 		return git_column_config(var, value, "tag", &colopts);
-	return git_color_default_config(var, value, cb);
+
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
 }
 
 static void write_tag_body(int fd, const struct object_id *oid)
color.c +0 -8
index 83abb11eda0..b24b19566b9 100644
@@ -430,14 +430,6 @@ int git_color_config(const char *var, const char *value, void *cb UNUSED)
 	return 0;
 }
 
-int git_color_default_config(const char *var, const char *value, void *cb)
-{
-	if (git_color_config(var, value, cb) < 0)
-		return -1;
-
-	return git_default_config(var, value, cb);
-}
-
 void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb)
 {
 	if (*color)
color.h +1 -5
index cfc8f841b23..bb28343be21 100644
@@ -88,12 +88,8 @@ extern const int column_colors_ansi_max;
  */
 extern int color_stdout_is_tty;
 
-/*
- * Use the first one if you need only color config; the second is a convenience
- * if you are just going to change to git_default_config, too.
- */
+/* Parse color config. */
 int git_color_config(const char *var, const char *value, void *cb);
-int git_color_default_config(const char *var, const char *value, void *cb);
 
 /*
  * Parse a config option, which can be a boolean or one of
#57 lore raw

PATCH v3 2/12 urlmatch.h: use config_fn_t type

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

These are actually used as config callbacks, so use the typedef-ed type and make future refactors easier.

---
 urlmatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
urlmatch.h +3 -2
index 9f40b00bfb8..bee374a642c 100644
@@ -2,6 +2,7 @@
 #define URL_MATCH_H
 
 #include "string-list.h"
+#include "config.h"
 
 struct url_info {
 	/* normalized url on success, must be freed, otherwise NULL */
@@ -48,8 +49,8 @@ struct urlmatch_config {
 	const char *key;
 
 	void *cb;
-	int (*collect_fn)(const char *var, const char *value, void *cb);
-	int (*cascade_fn)(const char *var, const char *value, void *cb);
+	config_fn_t collect_fn;
+	config_fn_t cascade_fn;
 	/*
 	 * Compare the two matches, the one just discovered and the existing
 	 * best match and return a negative value if the found item is to be
Signature · 1 line
gitgitgadget
#58 lore raw

Re: [PATCH v3 02/12] urlmatch.h: use config_fn_t type

Quoted 23 lines · Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

These are actually used as config callbacks, so use the typedef-ed type and make future refactors easier.

---
 urlmatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
urlmatch.h +1 -0
index 9f40b00bfb8..bee374a642c 100644
@@ -2,6 +2,7 @@
 #define URL_MATCH_H
 
 #include "string-list.h"
+#include "config.h"
 
 struct url_info {
 	/* normalized url on success, must be freed, otherwise NULL */

urlmatch.h struct urlmatch_config { · line 53

Quoted 7 lines
urlmatch.h +2 -2
@@ -48,8 +49,8 @@ struct urlmatch_config {
 	const char *key;
 
 	void *cb;
-	int (*collect_fn)(const char *var, const char *value, void *cb);
-	int (*cascade_fn)(const char *var, const char *value, void *cb);
+	config_fn_t collect_fn;
+	config_fn_t cascade_fn;

Makes sense.

Quoted 3 lines
urlmatch.h +0 -0
 	/*
 	 * Compare the two matches, the one just discovered and the existing
 	 * best match and return a negative value if the found item is to be
#59 lore raw

PATCH v3 3/12 config: add ctx arg to config_fn_t

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Add a new "const struct config_context *ctx" arg to config_fn_t to hold additional information about the config iteration operation. config_context has a "struct key_value_info kvi" member that holds metadata about the config source being read (e.g. what kind of config source it is, the filename, etc). In this series, we're only interested in .kvi, so we could have just used "struct key_value_info" as an arg, but config_context makes it possible to add/adjust members in the future without changing the config_fn_t signature. We could also consider other ways of organizing the args (e.g. moving the config name and value into config_context or key_value_info), but in my experiments, the incremental benefit doesn't justify the added complexity (e.g. a config_fn_t will sometimes invoke another config_fn_t but with a different config value).

In subsequent commits, the .kvi member will replace the global "struct config_reader" in config.c, making config iteration a global-free operation. It requires much more work for the machinery to provide meaningful values of .kvi, so for now, merely change the signature and call sites, pass NULL as a placeholder value, and don't rely on the arg in any meaningful way.

Most of the changes are performed by contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every config_fn_t:

- Modifies the signature to accept "const struct config_context *ctx"
- Passes "ctx" to any inner config_fn_t, if needed
- Adds UNUSED attributes to "ctx", if needed

Most config_fn_t instances are easily identified by seeing if they are called by the various config functions. Most of the remaining ones are manually named in the .cocci patch. Manual cleanups are still needed, but the majority of it is trivial; it's either adjusting config_fn_t that the .cocci patch didn't catch, or adding forward declarations of "struct config_context ctx" to make the signatures make sense.

The non-trivial changes are in cases where we are invoking a config_fn_t outside of config machinery, and we now need to decide what value of "ctx" to pass. These cases are:

- trace2/tr2_cfg.c:tr2_cfg_set_fl()

This is indirectly called by git_config_set() so that the trace2 machinery can notice the new config values and update its settings using the tr2 config parsing function, i.e. tr2_cfg_cb().

- builtin/checkout.c:checkout_main()

This calls git_xmerge_config() as a shorthand for parsing a CLI arg. This might be worth refactoring away in the future, since git_xmerge_config() can call git_default_config(), which can do much more than just parsing.

Handle them by creating a KVI_INIT macro that initializes "struct key_value_info" to a reasonable default, and use that to construct the "ctx" arg.

---
 alias.c                                       |   3 +-
 archive-tar.c                                 |   3 +-
 archive-zip.c                                 |   1 +
 builtin/add.c                                 |   5 +-
 builtin/blame.c                               |   5 +-
 builtin/branch.c                              |   5 +-
 builtin/cat-file.c                            |   5 +-
 builtin/checkout.c                            |  12 +-
 builtin/clean.c                               |   5 +-
 builtin/clone.c                               |  11 +-
 builtin/column.c                              |   3 +-
 builtin/commit-graph.c                        |   1 +
 builtin/commit.c                              |  10 +-
 builtin/config.c                              |  10 +-
 builtin/difftool.c                            |   5 +-
 builtin/fetch.c                               |   9 +-
 builtin/fsmonitor--daemon.c                   |   5 +-
 builtin/grep.c                                |   7 +-
 builtin/help.c                                |   5 +-
 builtin/index-pack.c                          |   5 +-
 builtin/log.c                                 |  10 +-
 builtin/merge.c                               |   7 +-
 builtin/multi-pack-index.c                    |   1 +
 builtin/pack-objects.c                        |   5 +-
 builtin/patch-id.c                            |   5 +-
 builtin/pull.c                                |   5 +-
 builtin/push.c                                |   5 +-
 builtin/read-tree.c                           |   5 +-
 builtin/rebase.c                              |   5 +-
 builtin/receive-pack.c                        |   5 +-
 builtin/reflog.c                              |   7 +-
 builtin/remote.c                              |   7 +-
 builtin/repack.c                              |   5 +-
 builtin/reset.c                               |   5 +-
 builtin/send-pack.c                           |   5 +-
 builtin/show-branch.c                         |   5 +-
 builtin/stash.c                               |   5 +-
 builtin/submodule--helper.c                   |   1 +
 builtin/tag.c                                 |   5 +-
 builtin/var.c                                 |   5 +-
 builtin/worktree.c                            |   5 +-
 bundle-uri.c                                  |   8 +-
 compat/mingw.c                                |   3 +-
 compat/mingw.h                                |   4 +-
 config.c                                      |  38 +++--
 config.h                                      |  41 +++--
 connect.c                                     |   4 +-
 .../coccinelle/config_fn_ctx.pending.cocci    | 144 ++++++++++++++++++
 convert.c                                     |   4 +-
 credential.c                                  |   1 +
 delta-islands.c                               |   4 +-
 diff.c                                        |  10 +-
 diff.h                                        |   7 +-
 fetch-pack.c                                  |   5 +-
 fmt-merge-msg.c                               |   5 +-
 fmt-merge-msg.h                               |   3 +-
 fsck.c                                        |   9 +-
 fsck.h                                        |   4 +-
 git-compat-util.h                             |   2 +
 gpg-interface.c                               |   7 +-
 grep.c                                        |   7 +-
 grep.h                                        |   4 +-
 help.c                                        |   7 +-
 http.c                                        |   5 +-
 ident.c                                       |   4 +-
 ident.h                                       |   4 +-
 imap-send.c                                   |   5 +-
 ll-merge.c                                    |   1 +
 ls-refs.c                                     |   1 +
 mailinfo.c                                    |   5 +-
 notes-utils.c                                 |   4 +-
 notes.c                                       |   4 +-
 pager.c                                       |   5 +-
 pretty.c                                      |   1 +
 promisor-remote.c                             |   4 +-
 remote.c                                      |   3 +-
 revision.c                                    |   4 +-
 scalar.c                                      |   4 +-
 sequencer.c                                   |   9 +-
 setup.c                                       |  16 +-
 submodule-config.c                            |  17 ++-
 t/helper/test-config.c                        |  11 +-
 t/helper/test-userdiff.c                      |   4 +-
 trace2/tr2_cfg.c                              |   9 +-
 trace2/tr2_sysenv.c                           |   3 +-
 trailer.c                                     |   2 +
 upload-pack.c                                 |   8 +-
 urlmatch.c                                    |   7 +-
 urlmatch.h                                    |   3 +-
 xdiff-interface.c                             |   5 +-
 xdiff-interface.h                             |   4 +-
 91 files changed, 515 insertions(+), 181 deletions(-)
 create mode 100644 contrib/coccinelle/config_fn_ctx.pending.cocci
alias.c +2 -1
index 54a1a23d2cf..910dd252a01 100644
@@ -12,7 +12,8 @@ struct config_alias_data {
 	struct string_list *list;
 };
 
-static int config_alias_cb(const char *key, const char *value, void *d)
+static int config_alias_cb(const char *key, const char *value,
+			   const struct config_context *ctx UNUSED, void *d)
 {
 	struct config_alias_data *data = d;
 	const char *p;
archive-tar.c +2 -1
index 4cd81d8161e..ef06e516b1f 100644
@@ -411,7 +411,8 @@ static int tar_filter_config(const char *var, const char *value,
 	return 0;
 }
 
-static int git_tar_config(const char *var, const char *value, void *cb)
+static int git_tar_config(const char *var, const char *value,
+			  const struct config_context *ctx UNUSED, void *cb)
 {
 	if (!strcmp(var, "tar.umask")) {
 		if (value && !strcmp(value, "user")) {
archive-zip.c +1 -0
index d0d065a312e..b6811951955 100644
@@ -617,6 +617,7 @@ static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
 }
 
 static int archive_zip_config(const char *var, const char *value,
+			      const struct config_context *ctx UNUSED,
 			      void *data UNUSED)
 {
 	return userdiff_config(var, value);
builtin/add.c +3 -2
index 76cc026a68a..23a93b71d93 100644
@@ -357,7 +357,8 @@ static struct option builtin_add_options[] = {
 	OPT_END(),
 };
 
-static int add_config(const char *var, const char *value, void *cb)
+static int add_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "add.ignoreerrors") ||
 	    !strcmp(var, "add.ignore-errors")) {
@@ -365,7 +366,7 @@ static int add_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static const char embedded_advice[] = N_(
builtin/blame.c +3 -2
index 2df6039a6e0..d0970a1ab13 100644
@@ -694,7 +694,8 @@ static const char *add_prefix(const char *prefix, const char *path)
 	return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
 }
 
-static int git_blame_config(const char *var, const char *value, void *cb)
+static int git_blame_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "blame.showroot")) {
 		show_root = git_config_bool(var, value);
@@ -767,7 +768,7 @@ static int git_blame_config(const char *var, const char *value, void *cb)
 	if (userdiff_config(var, value) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int blame_copy_callback(const struct option *option, const char *arg, int unset)
builtin/branch.c +3 -2
index df99e38847b..23bfb4a771a 100644
@@ -83,7 +83,8 @@ static unsigned int colopts;
 
 define_list_config_array(color_branch_slots);
 
-static int git_branch_config(const char *var, const char *value, void *cb)
+static int git_branch_config(const char *var, const char *value,
+			     const struct config_context *ctx, void *cb)
 {
 	const char *slot_name;
 
@@ -120,7 +121,7 @@ static int git_branch_config(const char *var, const char *value, void *cb)
 	if (git_color_config(var, value, cb) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static const char *branch_get_color(enum color_branch ix)
builtin/cat-file.c +3 -2
index 0bafc14e6c0..a7d88c055a0 100644
@@ -885,12 +885,13 @@ static int batch_objects(struct batch_options *opt)
 	return retval;
 }
 
-static int git_cat_file_config(const char *var, const char *value, void *cb)
+static int git_cat_file_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	if (userdiff_config(var, value) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int batch_option_callback(const struct option *opt,
builtin/checkout.c +9 -3
index 715eeb5048f..4e1f7dc26c2 100644
@@ -1186,7 +1186,8 @@ static int switch_branches(const struct checkout_opts *opts,
 	return ret || writeout_error;
 }
 
-static int git_checkout_config(const char *var, const char *value, void *cb)
+static int git_checkout_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	struct checkout_opts *opts = cb;
 
@@ -1202,7 +1203,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
 	if (starts_with(var, "submodule."))
 		return git_default_submodule_config(var, value, NULL);
 
-	return git_xmerge_config(var, value, NULL);
+	return git_xmerge_config(var, value, ctx, NULL);
 }
 
 static void setup_new_branch_info_and_source_tree(
@@ -1689,8 +1690,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	}
 
 	if (opts->conflict_style) {
+		struct key_value_info kvi = KVI_INIT;
+		struct config_context ctx = {
+			.kvi = &kvi,
+		};
 		opts->merge = 1; /* implied */
-		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
+		git_xmerge_config("merge.conflictstyle", opts->conflict_style,
+				  &ctx, NULL);
 	}
 	if (opts->force) {
 		opts->discard_changes = 1;
builtin/clean.c +3 -2
index 57e7f7cac64..5eff1b802a7 100644
@@ -103,7 +103,8 @@ struct menu_stuff {
 
 define_list_config_array(color_interactive_slots);
 
-static int git_clean_config(const char *var, const char *value, void *cb)
+static int git_clean_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	const char *slot_name;
 
@@ -133,7 +134,7 @@ static int git_clean_config(const char *var, const char *value, void *cb)
 	if (git_color_config(var, value, cb) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static const char *clean_get_color(enum color_clean ix)
builtin/clone.c +7 -4
index 15f9912b4ca..c0f6e067493 100644
@@ -790,7 +790,8 @@ static int checkout(int submodule_progress, int filter_submodules)
 	return err;
 }
 
-static int git_clone_config(const char *k, const char *v, void *cb)
+static int git_clone_config(const char *k, const char *v,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(k, "clone.defaultremotename")) {
 		free(remote_name);
@@ -801,17 +802,19 @@ static int git_clone_config(const char *k, const char *v, void *cb)
 	if (!strcmp(k, "clone.filtersubmodules"))
 		config_filter_submodules = git_config_bool(k, v);
 
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
-static int write_one_config(const char *key, const char *value, void *data)
+static int write_one_config(const char *key, const char *value,
+			    const struct config_context *ctx,
+			    void *data)
 {
 	/*
 	 * give git_clone_config a chance to write config values back to the
 	 * environment, since git_config_set_multivar_gently only deals with
 	 * config-file writes
 	 */
-	int apply_failed = git_clone_config(key, value, data);
+	int apply_failed = git_clone_config(key, value, ctx, data);
 	if (apply_failed)
 		return apply_failed;
 
builtin/column.c +2 -1
index de623a16c2d..4a6148ca479 100644
@@ -13,7 +13,8 @@ static const char * const builtin_column_usage[] = {
 };
 static unsigned int colopts;
 
-static int column_config(const char *var, const char *value, void *cb)
+static int column_config(const char *var, const char *value,
+			 const struct config_context *ctx UNUSED, void *cb)
 {
 	return git_column_config(var, value, cb, &colopts);
 }
builtin/commit-graph.c +1 -0
index a3d00fa232b..13d35b00ca8 100644
@@ -186,6 +186,7 @@ static int write_option_max_new_filters(const struct option *opt,
 }
 
 static int git_commit_graph_write_config(const char *var, const char *value,
+					 const struct config_context *ctx UNUSED,
 					 void *cb UNUSED)
 {
 	if (!strcmp(var, "commitgraph.maxnewfilters"))
builtin/commit.c +6 -4
index e67c4be2211..3bc87e5fc97 100644
@@ -1405,7 +1405,8 @@ static int parse_status_slot(const char *slot)
 	return LOOKUP_CONFIG(color_status_slots, slot);
 }
 
-static int git_status_config(const char *k, const char *v, void *cb)
+static int git_status_config(const char *k, const char *v,
+			     const struct config_context *ctx, void *cb)
 {
 	struct wt_status *s = cb;
 	const char *slot_name;
@@ -1490,7 +1491,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
 		s->detect_rename = git_config_rename(k, v);
 		return 0;
 	}
-	return git_diff_ui_config(k, v, NULL);
+	return git_diff_ui_config(k, v, ctx, NULL);
 }
 
 int cmd_status(int argc, const char **argv, const char *prefix)
@@ -1605,7 +1606,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static int git_commit_config(const char *k, const char *v, void *cb)
+static int git_commit_config(const char *k, const char *v,
+			     const struct config_context *ctx, void *cb)
 {
 	struct wt_status *s = cb;
 
@@ -1627,7 +1629,7 @@ static int git_commit_config(const char *k, const char *v, void *cb)
 		return 0;
 	}
 
-	return git_status_config(k, v, s);
+	return git_status_config(k, v, ctx, s);
 }
 
 int cmd_commit(int argc, const char **argv, const char *prefix)
builtin/config.c +8 -2
index ff2fe8ef125..e14f967bbea 100644
@@ -216,6 +216,7 @@ static void show_config_scope(struct strbuf *buf)
 }
 
 static int show_all_config(const char *key_, const char *value_,
+			   const struct config_context *ctx UNUSED,
 			   void *cb UNUSED)
 {
 	if (show_origin || show_scope) {
@@ -300,7 +301,8 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
 	return 0;
 }
 
-static int collect_config(const char *key_, const char *value_, void *cb)
+static int collect_config(const char *key_, const char *value_,
+			  const struct config_context *ctx UNUSED, void *cb)
 {
 	struct strbuf_list *values = cb;
 
@@ -468,6 +470,7 @@ static const char *get_colorbool_slot;
 static char parsed_color[COLOR_MAXLEN];
 
 static int git_get_color_config(const char *var, const char *value,
+				const struct config_context *ctx UNUSED,
 				void *cb UNUSED)
 {
 	if (!strcmp(var, get_color_slot)) {
@@ -500,6 +503,7 @@ static int get_colorbool_found;
 static int get_diff_color_found;
 static int get_color_ui_found;
 static int git_get_colorbool_config(const char *var, const char *value,
+				    const struct config_context *ctx UNUSED,
 				    void *data UNUSED)
 {
 	if (!strcmp(var, get_colorbool_slot))
@@ -557,7 +561,9 @@ struct urlmatch_current_candidate_value {
 	struct strbuf value;
 };
 
-static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
+static int urlmatch_collect_fn(const char *var, const char *value,
+			       const struct config_context *ctx UNUSED,
+			       void *cb)
 {
 	struct string_list *values = cb;
 	struct string_list_item *item = string_list_insert(values, var);
builtin/difftool.c +3 -2
index 0049342f5c0..f289530068b 100644
@@ -40,14 +40,15 @@ static const char *const builtin_difftool_usage[] = {
 	NULL
 };
 
-static int difftool_config(const char *var, const char *value, void *cb)
+static int difftool_config(const char *var, const char *value,
+			   const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "difftool.trustexitcode")) {
 		trust_exit_code = git_config_bool(var, value);
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int print_tool_help(void)
builtin/fetch.c +6 -3
index 849a9be421d..b607605b491 100644
@@ -111,7 +111,8 @@ struct fetch_config {
 	enum display_format display_format;
 };
 
-static int git_fetch_config(const char *k, const char *v, void *cb)
+static int git_fetch_config(const char *k, const char *v,
+			    const struct config_context *ctx, void *cb)
 {
 	struct fetch_config *fetch_config = cb;
 
@@ -165,7 +166,7 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
 			    "fetch.output", v);
 	}
 
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
 static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
@@ -1794,7 +1795,9 @@ struct remote_group_data {
 	struct string_list *list;
 };
 
-static int get_remote_group(const char *key, const char *value, void *priv)
+static int get_remote_group(const char *key, const char *value,
+			    const struct config_context *ctx UNUSED,
+			    void *priv)
 {
 	struct remote_group_data *g = priv;
 
builtin/fsmonitor--daemon.c +3 -2
index f6dd9a784c1..91a776e2f17 100644
@@ -37,7 +37,8 @@ static int fsmonitor__start_timeout_sec = 60;
 #define FSMONITOR__ANNOUNCE_STARTUP "fsmonitor.announcestartup"
 static int fsmonitor__announce_startup = 0;
 
-static int fsmonitor_config(const char *var, const char *value, void *cb)
+static int fsmonitor_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, FSMONITOR__IPC_THREADS)) {
 		int i = git_config_int(var, value);
@@ -67,7 +68,7 @@ static int fsmonitor_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 /*
builtin/grep.c +4 -3
index 76cf999d310..757d52b94ec 100644
@@ -290,13 +290,14 @@ static int wait_all(void)
 	return hit;
 }
 
-static int grep_cmd_config(const char *var, const char *value, void *cb)
+static int grep_cmd_config(const char *var, const char *value,
+			   const struct config_context *ctx, void *cb)
 {
-	int st = grep_config(var, value, cb);
+	int st = grep_config(var, value, ctx, cb);
 
 	if (git_color_config(var, value, cb) < 0)
 		st = -1;
-	else if (git_default_config(var, value, cb) < 0)
+	else if (git_default_config(var, value, ctx, cb) < 0)
 		st = -1;
 
 	if (!strcmp(var, "grep.threads")) {
builtin/help.c +3 -2
index d3cf4af3f6e..c348f201254 100644
@@ -398,7 +398,8 @@ static int add_man_viewer_info(const char *var, const char *value)
 	return 0;
 }
 
-static int git_help_config(const char *var, const char *value, void *cb)
+static int git_help_config(const char *var, const char *value,
+			   const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "help.format")) {
 		if (!value)
@@ -421,7 +422,7 @@ static int git_help_config(const char *var, const char *value, void *cb)
 	if (starts_with(var, "man."))
 		return add_man_viewer_info(var, value);
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static struct cmdnames main_cmds, other_cmds;
builtin/index-pack.c +3 -2
index bb67e166559..31914aac298 100644
@@ -1581,7 +1581,8 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
 	strbuf_release(&pack_name);
 }
 
-static int git_index_pack_config(const char *k, const char *v, void *cb)
+static int git_index_pack_config(const char *k, const char *v,
+				 const struct config_context *ctx, void *cb)
 {
 	struct pack_idx_option *opts = cb;
 
@@ -1608,7 +1609,7 @@ static int git_index_pack_config(const char *k, const char *v, void *cb)
 		else
 			opts->flags &= ~WRITE_REV;
 	}
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
 static int cmp_uint32(const void *a_, const void *b_)
builtin/log.c +6 -4
index 4c45a47ecf4..ac5f74b6b06 100644
@@ -564,7 +564,8 @@ static int cmd_log_walk(struct rev_info *rev)
 	return retval;
 }
 
-static int git_log_config(const char *var, const char *value, void *cb)
+static int git_log_config(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb)
 {
 	const char *slot_name;
 
@@ -613,7 +614,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_diff_ui_config(var, value, cb);
+	return git_diff_ui_config(var, value, ctx, cb);
 }
 
 int cmd_whatchanged(int argc, const char **argv, const char *prefix)
@@ -979,7 +980,8 @@ static enum cover_from_description parse_cover_from_description(const char *arg)
 		die(_("%s: invalid cover from description mode"), arg);
 }
 
-static int git_format_config(const char *var, const char *value, void *cb)
+static int git_format_config(const char *var, const char *value,
+			     const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "format.headers")) {
 		if (!value)
@@ -1108,7 +1110,7 @@ static int git_format_config(const char *var, const char *value, void *cb)
 	if (!strcmp(var, "diff.noprefix"))
 		return 0;
 
-	return git_log_config(var, value, cb);
+	return git_log_config(var, value, ctx, cb);
 }
 
 static const char *output_directory = NULL;
builtin/merge.c +4 -3
index 8da3e46abb0..cad624fb797 100644
@@ -623,7 +623,8 @@ static void parse_branch_merge_options(char *bmo)
 	free(argv);
 }
 
-static int git_merge_config(const char *k, const char *v, void *cb)
+static int git_merge_config(const char *k, const char *v,
+			    const struct config_context *ctx, void *cb)
 {
 	int status;
 	const char *str;
@@ -668,10 +669,10 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 		return 0;
 	}
 
-	status = fmt_merge_msg_config(k, v, cb);
+	status = fmt_merge_msg_config(k, v, ctx, cb);
 	if (status)
 		return status;
-	return git_diff_ui_config(k, v, cb);
+	return git_diff_ui_config(k, v, ctx, cb);
 }
 
 static int read_tree_trivial(struct object_id *common, struct object_id *head,
builtin/multi-pack-index.c +1 -0
index 1b5083f8b26..a0a7b82cc69 100644
@@ -82,6 +82,7 @@ static struct option *add_common_options(struct option *prev)
 }
 
 static int git_multi_pack_index_write_config(const char *var, const char *value,
+					     const struct config_context *ctx UNUSED,
 					     void *cb UNUSED)
 {
 	if (!strcmp(var, "pack.writebitmaphashcache")) {
builtin/pack-objects.c +3 -2
index 9cfc8801f9b..ad4d8fafb0b 100644
@@ -3135,7 +3135,8 @@ static void prepare_pack(int window, int depth)
 	free(delta_list);
 }
 
-static int git_pack_config(const char *k, const char *v, void *cb)
+static int git_pack_config(const char *k, const char *v,
+			   const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(k, "pack.window")) {
 		window = git_config_int(k, v);
@@ -3227,7 +3228,7 @@ static int git_pack_config(const char *k, const char *v, void *cb)
 		ex->uri = xstrdup(pack_end + 1);
 		oidmap_put(&configured_exclusions, ex);
 	}
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
 /* Counters for trace2 output when in --stdin-packs mode. */
builtin/patch-id.c +3 -2
index 9d5585d3a72..03eddd0fb82 100644
@@ -196,7 +196,8 @@ struct patch_id_opts {
 	int verbatim;
 };
 
-static int git_patch_id_config(const char *var, const char *value, void *cb)
+static int git_patch_id_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	struct patch_id_opts *opts = cb;
 
@@ -209,7 +210,7 @@ static int git_patch_id_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_patch_id(int argc, const char **argv, const char *prefix)
builtin/pull.c +3 -2
index 0c7bac97b75..83fca5b1d46 100644
@@ -361,7 +361,8 @@ static enum rebase_type config_get_rebase(int *rebase_unspecified)
 /**
  * Read config variables.
  */
-static int git_pull_config(const char *var, const char *value, void *cb)
+static int git_pull_config(const char *var, const char *value,
+			   const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "rebase.autostash")) {
 		config_autostash = git_config_bool(var, value);
@@ -374,7 +375,7 @@ static int git_pull_config(const char *var, const char *value, void *cb)
 		check_trust_level = 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 /**
builtin/push.c +3 -2
index dbdf609daf3..a2f68f77324 100644
@@ -510,7 +510,8 @@ static void set_push_cert_flags(int *flags, int v)
 }
 
 
-static int git_push_config(const char *k, const char *v, void *cb)
+static int git_push_config(const char *k, const char *v,
+			   const struct config_context *ctx, void *cb)
 {
 	const char *slot_name;
 	int *flags = cb;
@@ -577,7 +578,7 @@ static int git_push_config(const char *k, const char *v, void *cb)
 		return 0;
 	}
 
-	return git_default_config(k, v, NULL);
+	return git_default_config(k, v, ctx, NULL);
 }
 
 int cmd_push(int argc, const char **argv, const char *prefix)
builtin/read-tree.c +3 -2
index 440f19b1b87..8877dd6d4b5 100644
@@ -102,12 +102,13 @@ static int debug_merge(const struct cache_entry * const *stages,
 	return 0;
 }
 
-static int git_read_tree_config(const char *var, const char *value, void *cb)
+static int git_read_tree_config(const char *var, const char *value,
+				const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "submodule.recurse"))
 		return git_default_submodule_config(var, value, cb);
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
builtin/rebase.c +3 -2
index ace1d5e8d11..60930e2d8e0 100644
@@ -772,7 +772,8 @@ static void parse_rebase_merges_value(struct rebase_options *options, const char
 		die(_("Unknown rebase-merges mode: %s"), value);
 }
 
-static int rebase_config(const char *var, const char *value, void *data)
+static int rebase_config(const char *var, const char *value,
+			 const struct config_context *ctx, void *data)
 {
 	struct rebase_options *opts = data;
 
@@ -831,7 +832,7 @@ static int rebase_config(const char *var, const char *value, void *data)
 		return git_config_string(&opts->default_backend, var, value);
 	}
 
-	return git_default_config(var, value, data);
+	return git_default_config(var, value, ctx, data);
 }
 
 static int checkout_up_to_date(struct rebase_options *options)
builtin/receive-pack.c +3 -2
index 1a31a583674..94d9898aff7 100644
@@ -139,7 +139,8 @@ static enum deny_action parse_deny_action(const char *var, const char *value)
 	return DENY_IGNORE;
 }
 
-static int receive_pack_config(const char *var, const char *value, void *cb)
+static int receive_pack_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	int status = parse_hide_refs_config(var, value, "receive", &hidden_refs);
 
@@ -266,7 +267,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static void show_ref(const char *path, const struct object_id *oid)
builtin/reflog.c +4 -3
index a1fa0c855f4..84251cc9517 100644
@@ -108,7 +108,8 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
 #define EXPIRE_TOTAL   01
 #define EXPIRE_UNREACH 02
 
-static int reflog_expire_config(const char *var, const char *value, void *cb)
+static int reflog_expire_config(const char *var, const char *value,
+				const struct config_context *ctx, void *cb)
 {
 	const char *pattern, *key;
 	size_t pattern_len;
@@ -117,7 +118,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
 	struct reflog_expire_cfg *ent;
 
 	if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
-		return git_default_config(var, value, cb);
+		return git_default_config(var, value, ctx, cb);
 
 	if (!strcmp(key, "reflogexpire")) {
 		slot = EXPIRE_TOTAL;
@@ -128,7 +129,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
 		if (git_config_expiry_date(&expire, var, value))
 			return -1;
 	} else
-		return git_default_config(var, value, cb);
+		return git_default_config(var, value, ctx, cb);
 
 	if (!pattern) {
 		switch (slot) {
builtin/remote.c +5 -2
index 1e0b137d977..87de81105e2 100644
@@ -268,6 +268,7 @@ static const char *abbrev_ref(const char *name, const char *prefix)
 #define abbrev_branch(name) abbrev_ref((name), "refs/heads/")
 
 static int config_read_branches(const char *key, const char *value,
+				const struct config_context *ctx UNUSED,
 				void *data UNUSED)
 {
 	const char *orig_key = key;
@@ -645,7 +646,7 @@ struct push_default_info
 };
 
 static int config_read_push_default(const char *key, const char *value,
-	void *cb)
+	const struct config_context *ctx UNUSED, void *cb)
 {
 	struct push_default_info* info = cb;
 	if (strcmp(key, "remote.pushdefault") ||
@@ -1494,7 +1495,9 @@ static int prune(int argc, const char **argv, const char *prefix)
 	return result;
 }
 
-static int get_remote_default(const char *key, const char *value UNUSED, void *priv)
+static int get_remote_default(const char *key, const char *value UNUSED,
+			      const struct config_context *ctx UNUSED,
+			      void *priv)
 {
 	if (strcmp(key, "remotes.default") == 0) {
 		int *found = priv;
builtin/repack.c +3 -2
index 0541c3ce157..6f74570bf94 100644
@@ -59,7 +59,8 @@ struct pack_objects_args {
 	int local;
 };
 
-static int repack_config(const char *var, const char *value, void *cb)
+static int repack_config(const char *var, const char *value,
+			 const struct config_context *ctx, void *cb)
 {
 	struct pack_objects_args *cruft_po_args = cb;
 	if (!strcmp(var, "repack.usedeltabaseoffset")) {
@@ -91,7 +92,7 @@ static int repack_config(const char *var, const char *value, void *cb)
 		return git_config_string(&cruft_po_args->depth, var, value);
 	if (!strcmp(var, "repack.cruftthreads"))
 		return git_config_string(&cruft_po_args->threads, var, value);
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 /*
builtin/reset.c +3 -2
index f99f32d5802..1ae82f2e89c 100644
@@ -312,12 +312,13 @@ static int reset_refs(const char *rev, const struct object_id *oid)
 	return update_ref_status;
 }
 
-static int git_reset_config(const char *var, const char *value, void *cb)
+static int git_reset_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "submodule.recurse"))
 		return git_default_submodule_config(var, value, cb);
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_reset(int argc, const char **argv, const char *prefix)
builtin/send-pack.c +3 -2
index 4784143004d..cd6d9e41129 100644
@@ -131,7 +131,8 @@ static void print_helper_status(struct ref *ref)
 	strbuf_release(&buf);
 }
 
-static int send_pack_config(const char *k, const char *v, void *cb)
+static int send_pack_config(const char *k, const char *v,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(k, "push.gpgsign")) {
 		const char *value;
@@ -151,7 +152,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)
 			}
 		}
 	}
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
 int cmd_send_pack(int argc, const char **argv, const char *prefix)
builtin/show-branch.c +3 -2
index a2461270d4b..f2fd245b838 100644
@@ -559,7 +559,8 @@ static void append_one_rev(const char *av)
 	die("bad sha1 reference %s", av);
 }
 
-static int git_show_branch_config(const char *var, const char *value, void *cb)
+static int git_show_branch_config(const char *var, const char *value,
+				  const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "showbranch.default")) {
 		if (!value)
@@ -582,7 +583,7 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
 	if (git_color_config(var, value, cb) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
builtin/stash.c +3 -2
index a7e17ffe384..e5c4246d2d4 100644
@@ -837,7 +837,8 @@ static int show_stat = 1;
 static int show_patch;
 static int show_include_untracked;
 
-static int git_stash_config(const char *var, const char *value, void *cb)
+static int git_stash_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "stash.showstat")) {
 		show_stat = git_config_bool(var, value);
@@ -851,7 +852,7 @@ static int git_stash_config(const char *var, const char *value, void *cb)
 		show_include_untracked = git_config_bool(var, value);
 		return 0;
 	}
-	return git_diff_basic_config(var, value, cb);
+	return git_diff_basic_config(var, value, ctx, cb);
 }
 
 static void diff_include_untracked(const struct stash_info *info, struct diff_options *diff_opt)
builtin/submodule--helper.c +1 -0
index 6bf8d666ce9..3f8f3692937 100644
@@ -2189,6 +2189,7 @@ static int update_clone_task_finished(int result,
 }
 
 static int git_update_clone_config(const char *var, const char *value,
+				   const struct config_context *ctx UNUSED,
 				   void *cb)
 {
 	int *max_jobs = cb;
builtin/tag.c +3 -2
index 1acf5f7a59f..b7dfb5e2cad 100644
@@ -188,7 +188,8 @@ static const char tag_template_nocleanup[] =
 	"Lines starting with '%c' will be kept; you may remove them"
 	" yourself if you want to.\n");
 
-static int git_tag_config(const char *var, const char *value, void *cb)
+static int git_tag_config(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "tag.gpgsign")) {
 		config_sign_tag = git_config_bool(var, value);
@@ -213,7 +214,7 @@ static int git_tag_config(const char *var, const char *value, void *cb)
 	if (git_color_config(var, value, cb) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static void write_tag_body(int fd, const struct object_id *oid)
builtin/var.c +3 -2
index 21499989807..ae011bdf409 100644
@@ -71,13 +71,14 @@ static const struct git_var *get_git_var(const char *var)
 	return NULL;
 }
 
-static int show_config(const char *var, const char *value, void *cb)
+static int show_config(const char *var, const char *value,
+		       const struct config_context *ctx, void *cb)
 {
 	if (value)
 		printf("%s=%s\n", var, value);
 	else
 		printf("%s\n", var);
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_var(int argc, const char **argv, const char *prefix UNUSED)
builtin/worktree.c +3 -2
index f3180463be2..d6f6d82bbec 100644
@@ -107,14 +107,15 @@ static int verbose;
 static int guess_remote;
 static timestamp_t expire;
 
-static int git_worktree_config(const char *var, const char *value, void *cb)
+static int git_worktree_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "worktree.guessremote")) {
 		guess_remote = git_config_bool(var, value);
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int delete_git_dir(const char *id)
bundle-uri.c +6 -2
index 2a2db1a1d39..0d5acc3dc51 100644
@@ -224,7 +224,9 @@ static int bundle_list_update(const char *key, const char *value,
 	return 0;
 }
 
-static int config_to_bundle_list(const char *key, const char *value, void *data)
+static int config_to_bundle_list(const char *key, const char *value,
+				 const struct config_context *ctx UNUSED,
+				 void *data)
 {
 	struct bundle_list *list = data;
 	return bundle_list_update(key, value, list);
@@ -871,7 +873,9 @@ cached:
 	return advertise_bundle_uri;
 }
 
-static int config_to_packet_line(const char *key, const char *value, void *data)
+static int config_to_packet_line(const char *key, const char *value,
+				 const struct config_context *ctx UNUSED,
+				 void *data)
 {
 	struct packet_reader *writer = data;
 
compat/mingw.c +2 -1
index d06cdc6254f..4186bc7a417 100644
@@ -244,7 +244,8 @@ static int core_restrict_inherited_handles = -1;
 static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
 static char *unset_environment_variables;
 
-int mingw_core_config(const char *var, const char *value, void *cb)
+int mingw_core_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "core.hidedotfiles")) {
 		if (value && !strcasecmp(value, "dotgitonly"))
compat/mingw.h +3 -1
index 209cf7cebad..5e34c873473 100644
@@ -11,7 +11,9 @@ typedef _sigset_t sigset_t;
 #undef _POSIX_THREAD_SAFE_FUNCTIONS
 #endif
 
-int mingw_core_config(const char *var, const char *value, void *cb);
+struct config_context;
+int mingw_core_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb);
 #define platform_core_config mingw_core_config
 
 /*
config.c +24 -14
index b79baf83e35..670d92cd76b 100644
@@ -208,7 +208,8 @@ struct config_include_data {
 };
 #define CONFIG_INCLUDE_INIT { 0 }
 
-static int git_config_include(const char *var, const char *value, void *data);
+static int git_config_include(const char *var, const char *value,
+			      const struct config_context *ctx, void *data);
 
 #define MAX_INCLUDE_DEPTH 10
 static const char include_depth_advice[] = N_(
@@ -387,7 +388,8 @@ static int include_by_branch(const char *cond, size_t cond_len)
 	return ret;
 }
 
-static int add_remote_url(const char *var, const char *value, void *data)
+static int add_remote_url(const char *var, const char *value,
+			  const struct config_context *ctx UNUSED, void *data)
 {
 	struct string_list *remote_urls = data;
 	const char *remote_name;
@@ -421,6 +423,7 @@ static void populate_remote_urls(struct config_include_data *inc)
 }
 
 static int forbid_remote_url(const char *var, const char *value UNUSED,
+			     const struct config_context *ctx UNUSED,
 			     void *data UNUSED)
 {
 	const char *remote_name;
@@ -484,7 +487,9 @@ static int include_condition_is_true(struct config_source *cs,
 	return 0;
 }
 
-static int git_config_include(const char *var, const char *value, void *data)
+static int git_config_include(const char *var, const char *value,
+			      const struct config_context *ctx,
+			      void *data)
 {
 	struct config_include_data *inc = data;
 	struct config_source *cs = inc->config_reader->source;
@@ -496,7 +501,7 @@ static int git_config_include(const char *var, const char *value, void *data)
 	 * 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);
+	ret = inc->fn(var, value, NULL, inc->data);
 	if (ret < 0)
 		return ret;
 
@@ -678,7 +683,7 @@ static int config_parse_pair(const char *key, const char *value,
 	if (git_config_parse_key(key, &canonical_name, NULL))
 		return -1;
 
-	ret = (fn(canonical_name, value, data) < 0) ? -1 : 0;
+	ret = (fn(canonical_name, value, NULL, data) < 0) ? -1 : 0;
 	free(canonical_name);
 	return ret;
 }
@@ -966,7 +971,7 @@ static int get_value(struct config_source *cs, config_fn_t fn, void *data,
 	 * accurate line number in error messages.
 	 */
 	cs->linenr--;
-	ret = fn(name->buf, value, data);
+	ret = fn(name->buf, value, NULL, data);
 	if (ret >= 0)
 		cs->linenr++;
 	return ret;
@@ -1560,7 +1565,8 @@ int git_config_color(char *dest, const char *var, const char *value)
 	return 0;
 }
 
-static int git_default_core_config(const char *var, const char *value, void *cb)
+static int git_default_core_config(const char *var, const char *value,
+				   const struct config_context *ctx, void *cb)
 {
 	/* This needs a better name */
 	if (!strcmp(var, "core.filemode")) {
@@ -1845,7 +1851,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
 	}
 
 	/* Add other config variables here and to Documentation/config.txt. */
-	return platform_core_config(var, value, cb);
+	return platform_core_config(var, value, ctx, cb);
 }
 
 static int git_default_sparse_config(const char *var, const char *value)
@@ -1947,15 +1953,16 @@ static int git_default_mailmap_config(const char *var, const char *value)
 	return 0;
 }
 
-int git_default_config(const char *var, const char *value, void *cb)
+int git_default_config(const char *var, const char *value,
+		       const struct config_context *ctx, void *cb)
 {
 	if (starts_with(var, "core."))
-		return git_default_core_config(var, value, cb);
+		return git_default_core_config(var, value, ctx, cb);
 
 	if (starts_with(var, "user.") ||
 	    starts_with(var, "author.") ||
 	    starts_with(var, "committer."))
-		return git_ident_config(var, value, cb);
+		return git_ident_config(var, value, ctx, cb);
 
 	if (starts_with(var, "i18n."))
 		return git_default_i18n_config(var, value);
@@ -2310,7 +2317,7 @@ static void configset_iter(struct config_reader *reader, struct config_set *set,
 
 		config_reader_set_kvi(reader, values->items[value_index].util);
 
-		if (fn(entry->key, values->items[value_index].string, data) < 0)
+		if (fn(entry->key, values->items[value_index].string, NULL, data) < 0)
 			git_die_config_linenr(entry->key,
 					      reader->config_kvi->filename,
 					      reader->config_kvi->linenr);
@@ -2488,7 +2495,9 @@ struct configset_add_data {
 };
 #define CONFIGSET_ADD_INIT { 0 }
 
-static int config_set_callback(const char *key, const char *value, void *cb)
+static int config_set_callback(const char *key, const char *value,
+			       const struct config_context *ctx UNUSED,
+			       void *cb)
 {
 	struct configset_add_data *data = cb;
 	configset_add_value(data->config_reader, data->config_set, key, value);
@@ -3098,7 +3107,8 @@ static int store_aux_event(enum config_event_t type,
 	return 0;
 }
 
-static int store_aux(const char *key, const char *value, void *cb)
+static int store_aux(const char *key, const char *value,
+		     const struct config_context *ctx UNUSED, void *cb)
 {
 	struct config_store_data *store = cb;
 
config.h +31 -10

67 lines not on this page · raw message

connect.c +2 -2
index 3a0186280c4..cddac1d96b8 100644
@@ -964,7 +964,7 @@ static struct child_process *git_tcp_connect(int fd[2], char *host, int flags)
 static char *git_proxy_command;
 
 static int git_proxy_command_options(const char *var, const char *value,
-		void *cb)
+		const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "core.gitproxy")) {
 		const char *for_pos;
@@ -1010,7 +1010,7 @@ static int git_proxy_command_options(const char *var, const char *value,
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int git_use_proxy(const char *host)
contrib/coccinelle/config_fn_ctx.pending.cocci new file +144 -0

144 lines not on this page · raw message

convert.c +3 -1
index 9ee79fe4699..8e96cf83030 100644
@@ -1015,7 +1015,9 @@ static int apply_filter(const char *path, const char *src, size_t len,
 	return 0;
 }
 
-static int read_convert_config(const char *var, const char *value, void *cb UNUSED)
+static int read_convert_config(const char *var, const char *value,
+			       const struct config_context *ctx UNUSED,
+			       void *cb UNUSED)
 {
 	const char *key, *name;
 	size_t namelen;
credential.c +1 -0
index 023b59d5711..92b8d9707da 100644
@@ -48,6 +48,7 @@ static int credential_from_potentially_partial_url(struct credential *c,
 						   const char *url);
 
 static int credential_config_callback(const char *var, const char *value,
+				      const struct config_context *ctx UNUSED,
 				      void *data)
 {
 	struct credential *c = data;
delta-islands.c +3 -1
index c824a5f6a42..5fc6ea6ff55 100644
@@ -341,7 +341,9 @@ static void free_remote_islands(kh_str_t *remote_islands)
 	kh_destroy_str(remote_islands);
 }
 
-static int island_config_callback(const char *k, const char *v, void *cb)
+static int island_config_callback(const char *k, const char *v,
+				  const struct config_context *ctx UNUSED,
+				  void *cb)
 {
 	struct island_load_data *ild = cb;
 
diff.c +6 -4
index 1cdac6ed363..07be3bee137 100644
@@ -357,7 +357,8 @@ static unsigned parse_color_moved_ws(const char *arg)
 	return ret;
 }
 
-int git_diff_ui_config(const char *var, const char *value, void *cb)
+int git_diff_ui_config(const char *var, const char *value,
+		       const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
 		diff_use_color_default = git_config_colorbool(var, value);
@@ -440,10 +441,11 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
 	if (git_color_config(var, value, cb) < 0)
 		return -1;
 
-	return git_diff_basic_config(var, value, cb);
+	return git_diff_basic_config(var, value, ctx, cb);
 }
 
-int git_diff_basic_config(const char *var, const char *value, void *cb)
+int git_diff_basic_config(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb)
 {
 	const char *name;
 
@@ -495,7 +497,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
 	if (git_diff_heuristic_config(var, value, cb) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static char *quote_two(const char *one, const char *two)
diff.h +5 -2
index 3a7a9e8b888..1deea9cb64a 100644
@@ -531,10 +531,13 @@ void free_diffstat_info(struct diffstat_t *diffstat);
 int parse_long_opt(const char *opt, const char **argv,
 		   const char **optarg);
 
-int git_diff_basic_config(const char *var, const char *value, void *cb);
+struct config_context;
+int git_diff_basic_config(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb);
 int git_diff_heuristic_config(const char *var, const char *value, void *cb);
 void init_diff_ui_defaults(void);
-int git_diff_ui_config(const char *var, const char *value, void *cb);
+int git_diff_ui_config(const char *var, const char *value,
+		       const struct config_context *ctx, void *cb);
 void repo_diff_setup(struct repository *, struct diff_options *);
 struct option *add_diff_options(const struct option *, struct diff_options *);
 int diff_opt_parse(struct diff_options *, const char **, int, const char *);
fetch-pack.c +3 -2
index 0f71054fbae..5f3d40f3d09 100644
@@ -1860,7 +1860,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 	return ref;
 }
 
-static int fetch_pack_config_cb(const char *var, const char *value, void *cb)
+static int fetch_pack_config_cb(const char *var, const char *value,
+				const struct config_context *ctx, void *cb)
 {
 	if (strcmp(var, "fetch.fsck.skiplist") == 0) {
 		const char *path;
@@ -1882,7 +1883,7 @@ static int fetch_pack_config_cb(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static void fetch_pack_config(void)
fmt-merge-msg.c +3 -2
index 5af0d4715ba..10137444321 100644
@@ -20,7 +20,8 @@ static int use_branch_desc;
 static int suppress_dest_pattern_seen;
 static struct string_list suppress_dest_patterns = STRING_LIST_INIT_DUP;
 
-int fmt_merge_msg_config(const char *key, const char *value, void *cb)
+int fmt_merge_msg_config(const char *key, const char *value,
+			 const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
 		int is_bool;
@@ -40,7 +41,7 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)
 			string_list_append(&suppress_dest_patterns, value);
 		suppress_dest_pattern_seen = 1;
 	} else {
-		return git_default_config(key, value, cb);
+		return git_default_config(key, value, ctx, cb);
 	}
 	return 0;
 }
fmt-merge-msg.h +2 -1
index 99054042dc5..73ca3e44652 100644
@@ -13,7 +13,8 @@ struct fmt_merge_msg_opts {
 };
 
 extern int merge_log_config;
-int fmt_merge_msg_config(const char *key, const char *value, void *cb);
+int fmt_merge_msg_config(const char *key, const char *value,
+			 const struct config_context *ctx, void *cb);
 int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 		  struct fmt_merge_msg_opts *);
 
fsck.c +6 -3
index 3261ef9ec28..55b6a694853 100644
@@ -1163,7 +1163,9 @@ struct fsck_gitmodules_data {
 	int ret;
 };
 
-static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
+static int fsck_gitmodules_fn(const char *var, const char *value,
+			      const struct config_context *ctx UNUSED,
+			      void *vdata)
 {
 	struct fsck_gitmodules_data *data = vdata;
 	const char *subsection, *key;
@@ -1373,7 +1375,8 @@ int fsck_finish(struct fsck_options *options)
 	return ret;
 }
 
-int git_fsck_config(const char *var, const char *value, void *cb)
+int git_fsck_config(const char *var, const char *value,
+		    const struct config_context *ctx, void *cb)
 {
 	struct fsck_options *options = cb;
 	if (strcmp(var, "fsck.skiplist") == 0) {
@@ -1394,7 +1397,7 @@ int git_fsck_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 /*
fsck.h +3 -1
index e17730e9da9..6359ba359bd 100644
@@ -233,10 +233,12 @@ void fsck_put_object_name(struct fsck_options *options,
 const char *fsck_describe_object(struct fsck_options *options,
 				 const struct object_id *oid);
 
+struct key_value_info;
 /*
  * git_config() callback for use by fsck-y tools that want to support
  * fsck.<msg> fsck.skipList etc.
  */
-int git_fsck_config(const char *var, const char *value, void *cb);
+int git_fsck_config(const char *var, const char *value,
+		    const struct config_context *ctx, void *cb);
 
 #endif
git-compat-util.h +2 -0
index 5b2b99c17c5..14e8aacb957 100644
@@ -440,8 +440,10 @@ typedef uintmax_t timestamp_t;
 #endif
 
 #ifndef platform_core_config
+struct config_context;
 static inline int noop_core_config(const char *var UNUSED,
 				   const char *value UNUSED,
+				   const struct config_context *ctx UNUSED,
 				   void *cb UNUSED)
 {
 	return 0;
gpg-interface.c +5 -2
index 19a3471a0b5..57c862a3a22 100644
@@ -14,7 +14,8 @@
 #include "alias.h"
 #include "wrapper.h"
 
-static int git_gpg_config(const char *, const char *, void *);
+static int git_gpg_config(const char *, const char *,
+			  const struct config_context *, void *);
 
 static void gpg_interface_lazy_init(void)
 {
@@ -720,7 +721,9 @@ void set_signing_key(const char *key)
 	configured_signing_key = xstrdup(key);
 }
 
-static int git_gpg_config(const char *var, const char *value, void *cb UNUSED)
+static int git_gpg_config(const char *var, const char *value,
+			  const struct config_context *ctx UNUSED,
+			  void *cb UNUSED)
 {
 	struct gpg_format *fmt = NULL;
 	char *fmtname = NULL;
grep.c +4 -3
index f00986c451a..fc22c3e2afb 100644
@@ -56,7 +56,8 @@ define_list_config_array_extra(color_grep_slots, {"match"});
  * Read the configuration file once and store it in
  * the grep_defaults template.
  */
-int grep_config(const char *var, const char *value, void *cb)
+int grep_config(const char *var, const char *value,
+		const struct config_context *ctx, void *cb)
 {
 	struct grep_opt *opt = cb;
 	const char *slot;
@@ -91,9 +92,9 @@ int grep_config(const char *var, const char *value, void *cb)
 	if (!strcmp(var, "color.grep"))
 		opt->color = git_config_colorbool(var, value);
 	if (!strcmp(var, "color.grep.match")) {
-		if (grep_config("color.grep.matchcontext", value, cb) < 0)
+		if (grep_config("color.grep.matchcontext", value, ctx, cb) < 0)
 			return -1;
-		if (grep_config("color.grep.matchselected", value, cb) < 0)
+		if (grep_config("color.grep.matchselected", value, ctx, cb) < 0)
 			return -1;
 	} else if (skip_prefix(var, "color.grep.", &slot)) {
 		int i = LOOKUP_CONFIG(color_grep_slots, slot);
grep.h +3 -1
index c59592e3bdb..926c0875c42 100644
@@ -202,7 +202,9 @@ struct grep_opt {
 	.output = std_output, \
 }
 
-int grep_config(const char *var, const char *value, void *);
+struct config_context;
+int grep_config(const char *var, const char *value,
+		const struct config_context *ctx, void *data);
 void grep_init(struct grep_opt *, struct repository *repo);
 
 void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
help.c +5 -2
index 5d7637dce92..ac0ae5ac0dc 100644
@@ -309,7 +309,8 @@ void load_command_list(const char *prefix,
 	exclude_cmds(other_cmds, main_cmds);
 }
 
-static int get_colopts(const char *var, const char *value, void *data)
+static int get_colopts(const char *var, const char *value,
+		       const struct config_context *ctx UNUSED, void *data)
 {
 	unsigned int *colopts = data;
 
@@ -459,7 +460,8 @@ void list_developer_interfaces_help(void)
 	putchar('\n');
 }
 
-static int get_alias(const char *var, const char *value, void *data)
+static int get_alias(const char *var, const char *value,
+		     const struct config_context *ctx UNUSED, void *data)
 {
 	struct string_list *list = data;
 
@@ -543,6 +545,7 @@ static struct cmdnames aliases;
 #define AUTOCORRECT_IMMEDIATELY (-1)
 
 static int git_unknown_cmd_config(const char *var, const char *value,
+				  const struct config_context *ctx UNUSED,
 				  void *cb UNUSED)
 {
 	const char *p;
http.c +3 -2
index bb58bb3e6a3..762502828c9 100644
@@ -363,7 +363,8 @@ static void process_curl_messages(void)
 	}
 }
 
-static int http_options(const char *var, const char *value, void *cb)
+static int http_options(const char *var, const char *value,
+			const struct config_context *ctx, void *data)
 {
 	if (!strcmp("http.version", var)) {
 		return git_config_string(&curl_http_version, var, value);
@@ -534,7 +535,7 @@ static int http_options(const char *var, const char *value, void *cb)
 	}
 
 	/* Fall back on the default ones */
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, data);
 }
 
 static int curl_empty_auth_enabled(void)
ident.c +3 -1
index 8fad92d7007..08be4d0747d 100644
@@ -671,7 +671,9 @@ static int set_ident(const char *var, const char *value)
 	return 0;
 }
 
-int git_ident_config(const char *var, const char *value, void *data UNUSED)
+int git_ident_config(const char *var, const char *value,
+		     const struct config_context *ctx UNUSED,
+		     void *data UNUSED)
 {
 	if (!strcmp(var, "user.useconfigonly")) {
 		ident_use_config_only = git_config_bool(var, value);
ident.h +3 -1
index 96a64896a01..6a79febba15 100644
@@ -62,6 +62,8 @@ const char *fmt_name(enum want_ident);
 int committer_ident_sufficiently_given(void);
 int author_ident_sufficiently_given(void);
 
-int git_ident_config(const char *, const char *, void *);
+struct config_context;
+int git_ident_config(const char *, const char *, const struct config_context *,
+		     void *);
 
 #endif
imap-send.c +3 -2
index 7f5426177a1..47777e76861 100644
@@ -1323,7 +1323,8 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
 	return 1;
 }
 
-static int git_imap_config(const char *var, const char *val, void *cb)
+static int git_imap_config(const char *var, const char *val,
+			   const struct config_context *ctx, void *cb)
 {
 
 	if (!strcmp("imap.sslverify", var))
@@ -1357,7 +1358,7 @@ static int git_imap_config(const char *var, const char *val, void *cb)
 			server.host = xstrdup(val);
 		}
 	} else
-		return git_default_config(var, val, cb);
+		return git_default_config(var, val, ctx, cb);
 
 	return 0;
 }
ll-merge.c +1 -0
index 07ec16e8e5b..3936d112e00 100644
@@ -254,6 +254,7 @@ static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
 static const char *default_ll_merge;
 
 static int read_merge_config(const char *var, const char *value,
+			     const struct config_context *ctx UNUSED,
 			     void *cb UNUSED)
 {
 	struct ll_merge_driver *fn;
ls-refs.c +1 -0
index f385938b64c..a29c2364a59 100644
@@ -137,6 +137,7 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)
 }
 
 static int ls_refs_config(const char *var, const char *value,
+			  const struct config_context *ctx UNUSED,
 			  void *cb_data)
 {
 	struct ls_refs_data *data = cb_data;
mailinfo.c +3 -2
index 2aeb20e5e62..931505363cd 100644
@@ -1241,12 +1241,13 @@ int mailinfo_parse_quoted_cr_action(const char *actionstr, int *action)
 	return 0;
 }
 
-static int git_mailinfo_config(const char *var, const char *value, void *mi_)
+static int git_mailinfo_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *mi_)
 {
 	struct mailinfo *mi = mi_;
 
 	if (!starts_with(var, "mailinfo."))
-		return git_default_config(var, value, NULL);
+		return git_default_config(var, value, ctx, NULL);
 	if (!strcmp(var, "mailinfo.scissors")) {
 		mi->use_scissors = git_config_bool(var, value);
 		return 0;
notes-utils.c +3 -1
index 4a793eb347f..97c031c26ec 100644
@@ -94,7 +94,9 @@ static combine_notes_fn parse_combine_notes_fn(const char *v)
 		return NULL;
 }
 
-static int notes_rewrite_config(const char *k, const char *v, void *cb)
+static int notes_rewrite_config(const char *k, const char *v,
+				const struct config_context *ctx UNUSED,
+				void *cb)
 {
 	struct notes_rewrite_cfg *c = cb;
 	if (starts_with(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
notes.c +3 -1
index f51a2d3630e..e68645a4b89 100644
@@ -974,7 +974,9 @@ void string_list_add_refs_from_colon_sep(struct string_list *list,
 	free(globs_copy);
 }
 
-static int notes_display_config(const char *k, const char *v, void *cb)
+static int notes_display_config(const char *k, const char *v,
+				const struct config_context *ctx UNUSED,
+				void *cb)
 {
 	int *load_refs = cb;
 
pager.c +4 -1
index 63055d0873f..b8822a9381e 100644
@@ -43,6 +43,7 @@ static void wait_for_pager_signal(int signo)
 }
 
 static int core_pager_config(const char *var, const char *value,
+			     const struct config_context *ctx UNUSED,
 			     void *data UNUSED)
 {
 	if (!strcmp(var, "core.pager"))
@@ -228,7 +229,9 @@ struct pager_command_config_data {
 	char *value;
 };
 
-static int pager_command_config(const char *var, const char *value, void *vdata)
+static int pager_command_config(const char *var, const char *value,
+				const struct config_context *ctx UNUSED,
+				void *vdata)
 {
 	struct pager_command_config_data *data = vdata;
 	const char *cmd;
pretty.c +1 -0
index 0bb938021ba..87245353452 100644
@@ -56,6 +56,7 @@ static void save_user_format(struct rev_info *rev, const char *cp, int is_tforma
 }
 
 static int git_pretty_formats_config(const char *var, const char *value,
+				     const struct config_context *ctx UNUSED,
 				     void *cb UNUSED)
 {
 	struct cmt_fmt_map *commit_format = NULL;
promisor-remote.c +3 -1
index 1adcd6fb0a5..c22abb85b15 100644
@@ -100,7 +100,9 @@ static void promisor_remote_move_to_tail(struct promisor_remote_config *config,
 	config->promisors_tail = &r->next;
 }
 
-static int promisor_remote_config(const char *var, const char *value, void *data)
+static int promisor_remote_config(const char *var, const char *value,
+				  const struct config_context *ctx UNUSED,
+				  void *data)
 {
 	struct promisor_remote_config *config = data;
 	const char *name;
remote.c +2 -1
index 0764fca0db9..dafb1acdc38 100644
@@ -349,7 +349,8 @@ static void read_branches_file(struct remote_state *remote_state,
 	remote->fetch_tags = 1; /* always auto-follow */
 }
 
-static int handle_config(const char *key, const char *value, void *cb)
+static int handle_config(const char *key, const char *value,
+			 const struct config_context *ctx UNUSED, void *cb)
 {
 	const char *name;
 	size_t namelen;
revision.c +3 -1
index b33cc1d106a..87ed8ccd444 100644
@@ -1572,7 +1572,9 @@ struct exclude_hidden_refs_cb {
 	const char *section;
 };
 
-static int hide_refs_config(const char *var, const char *value, void *cb_data)
+static int hide_refs_config(const char *var, const char *value,
+			    const struct config_context *ctx UNUSED,
+			    void *cb_data)
 {
 	struct exclude_hidden_refs_cb *cb = cb_data;
 	cb->exclusions->hidden_refs_configured = 1;
scalar.c +3 -1
index 1326e1f6089..df7358f481c 100644
@@ -594,7 +594,9 @@ static int cmd_register(int argc, const char **argv)
 	return register_dir();
 }
 
-static int get_scalar_repos(const char *key, const char *value, void *data)
+static int get_scalar_repos(const char *key, const char *value,
+			    const struct config_context *ctx UNUSED,
+			    void *data)
 {
 	struct string_list *list = data;
 
sequencer.c +6 -3
index bceb6abcb6c..34754d17596 100644
@@ -219,7 +219,8 @@ static struct update_ref_record *init_update_ref_record(const char *ref)
 	return rec;
 }
 
-static int git_sequencer_config(const char *k, const char *v, void *cb)
+static int git_sequencer_config(const char *k, const char *v,
+				const struct config_context *ctx, void *cb)
 {
 	struct replay_opts *opts = cb;
 	int status;
@@ -274,7 +275,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)
 	if (opts->action == REPLAY_REVERT && !strcmp(k, "revert.reference"))
 		opts->commit_use_reference = git_config_bool(k, v);
 
-	return git_diff_basic_config(k, v, NULL);
+	return git_diff_basic_config(k, v, ctx, NULL);
 }
 
 void sequencer_init_config(struct replay_opts *opts)
@@ -2881,7 +2882,9 @@ static int git_config_string_dup(char **dest,
 	return 0;
 }
 
-static int populate_opts_cb(const char *key, const char *value, void *data)
+static int populate_opts_cb(const char *key, const char *value,
+			    const struct config_context *ctx UNUSED,
+			    void *data)
 {
 	struct replay_opts *opts = data;
 	int error_flag = 1;
setup.c +11 -5
index 458582207ea..e7f81151ef0 100644
@@ -517,7 +517,9 @@ no_prevention_needed:
 	startup_info->original_cwd = NULL;
 }
 
-static int read_worktree_config(const char *var, const char *value, void *vdata)
+static int read_worktree_config(const char *var, const char *value,
+				const struct config_context *ctx UNUSED,
+				void *vdata)
 {
 	struct repository_format *data = vdata;
 
@@ -588,7 +590,8 @@ static enum extension_result handle_extension(const char *var,
 	return EXTENSION_UNKNOWN;
 }
 
-static int check_repo_format(const char *var, const char *value, void *vdata)
+static int check_repo_format(const char *var, const char *value,
+			     const struct config_context *ctx, void *vdata)
 {
 	struct repository_format *data = vdata;
 	const char *ext;
@@ -617,7 +620,7 @@ static int check_repo_format(const char *var, const char *value, void *vdata)
 		}
 	}
 
-	return read_worktree_config(var, value, vdata);
+	return read_worktree_config(var, value, ctx, vdata);
 }
 
 static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)
@@ -1116,7 +1119,8 @@ struct safe_directory_data {
 	int is_safe;
 };
 
-static int safe_directory_cb(const char *key, const char *value, void *d)
+static int safe_directory_cb(const char *key, const char *value,
+			     const struct config_context *ctx UNUSED, void *d)
 {
 	struct safe_directory_data *data = d;
 
@@ -1172,7 +1176,9 @@ static int ensure_valid_ownership(const char *gitfile,
 	return data.is_safe;
 }
 
-static int allowed_bare_repo_cb(const char *key, const char *value, void *d)
+static int allowed_bare_repo_cb(const char *key, const char *value,
+				const struct config_context *ctx UNUSED,
+				void *d)
 {
 	enum allowed_bare_repo *allowed_bare_repo = d;
 
submodule-config.c +12 -5
index 58dfbde9ae5..b364244102e 100644
@@ -426,7 +426,8 @@ struct parse_config_parameter {
  * config store (.git/config, etc).  Callers are responsible for
  * checking for overrides in the main config store when appropriate.
  */
-static int parse_config(const char *var, const char *value, void *data)
+static int parse_config(const char *var, const char *value,
+			const struct config_context *ctx UNUSED, void *data)
 {
 	struct parse_config_parameter *me = data;
 	struct submodule *submodule;
@@ -675,7 +676,8 @@ out:
 	}
 }
 
-static int gitmodules_cb(const char *var, const char *value, void *data)
+static int gitmodules_cb(const char *var, const char *value,
+			 const struct config_context *ctx, void *data)
 {
 	struct repository *repo = data;
 	struct parse_config_parameter parameter;
@@ -685,7 +687,7 @@ static int gitmodules_cb(const char *var, const char *value, void *data)
 	parameter.gitmodules_oid = null_oid();
 	parameter.overwrite = 1;
 
-	return parse_config(var, value, &parameter);
+	return parse_config(var, value, ctx, &parameter);
 }
 
 void repo_read_gitmodules(struct repository *repo, int skip_if_read)
@@ -802,7 +804,9 @@ void submodule_free(struct repository *r)
 		submodule_cache_clear(r->submodule_cache);
 }
 
-static int config_print_callback(const char *var, const char *value, void *cb_data)
+static int config_print_callback(const char *var, const char *value,
+				 const struct config_context *ctx UNUSED,
+				 void *cb_data)
 {
 	char *wanted_key = cb_data;
 
@@ -844,7 +848,9 @@ struct fetch_config {
 	int *recurse_submodules;
 };
 
-static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
+static int gitmodules_fetch_config(const char *var, const char *value,
+				   const struct config_context *ctx UNUSED,
+				   void *cb)
 {
 	struct fetch_config *config = cb;
 	if (!strcmp(var, "submodule.fetchjobs")) {
@@ -872,6 +878,7 @@ void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules)
 }
 
 static int gitmodules_update_clone_config(const char *var, const char *value,
+					  const struct config_context *ctx UNUSED,
 					  void *cb)
 {
 	int *max_jobs = cb;
t/helper/test-config.c +8 -3
index ad78fc17683..85ad815358e 100644
@@ -42,7 +42,9 @@
  *
  */
 
-static int iterate_cb(const char *var, const char *value, void *data UNUSED)
+static int iterate_cb(const char *var, const char *value,
+		      const struct config_context *ctx UNUSED,
+		      void *data UNUSED)
 {
 	static int nr;
 
@@ -59,7 +61,8 @@ static int iterate_cb(const char *var, const char *value, void *data UNUSED)
 	return 0;
 }
 
-static int parse_int_cb(const char *var, const char *value, void *data)
+static int parse_int_cb(const char *var, const char *value,
+			const struct config_context *ctx UNUSED, void *data)
 {
 	const char *key_to_match = data;
 
@@ -70,7 +73,9 @@ static int parse_int_cb(const char *var, const char *value, void *data)
 	return 0;
 }
 
-static int early_config_cb(const char *var, const char *value, void *vdata)
+static int early_config_cb(const char *var, const char *value,
+			   const struct config_context *ctx UNUSED,
+			   void *vdata)
 {
 	const char *key = vdata;
 
t/helper/test-userdiff.c +3 -1
index 680124a6760..0ce31ce59f5 100644
@@ -12,7 +12,9 @@ static int driver_cb(struct userdiff_driver *driver,
 	return 0;
 }
 
-static int cmd__userdiff_config(const char *var, const char *value, void *cb UNUSED)
+static int cmd__userdiff_config(const char *var, const char *value,
+				const struct config_context *ctx UNUSED,
+				void *cb UNUSED)
 {
 	if (userdiff_config(var, value) < 0)
 		return -1;
trace2/tr2_cfg.c +7 -2
index 78cfc15d52d..83bc4fd109c 100644
@@ -99,7 +99,8 @@ struct tr2_cfg_data {
 /*
  * See if the given config key matches any of our patterns of interest.
  */
-static int tr2_cfg_cb(const char *key, const char *value, void *d)
+static int tr2_cfg_cb(const char *key, const char *value,
+		      const struct config_context *ctx UNUSED, void *d)
 {
 	struct strbuf **s;
 	struct tr2_cfg_data *data = (struct tr2_cfg_data *)d;
@@ -142,8 +143,12 @@ void tr2_list_env_vars_fl(const char *file, int line)
 void tr2_cfg_set_fl(const char *file, int line, const char *key,
 		    const char *value)
 {
+	struct key_value_info kvi = KVI_INIT;
+	struct config_context ctx = {
+		.kvi = &kvi,
+	};
 	struct tr2_cfg_data data = { file, line };
 
 	if (tr2_cfg_load_patterns() > 0)
-		tr2_cfg_cb(key, value, &data);
+		tr2_cfg_cb(key, value, &ctx, &data);
 }
trace2/tr2_sysenv.c +2 -1
index 069786cb927..f26ec95ab4d 100644
@@ -57,7 +57,8 @@ static struct tr2_sysenv_entry tr2_sysenv_settings[] = {
 };
 /* clang-format on */
 
-static int tr2_sysenv_cb(const char *key, const char *value, void *d)
+static int tr2_sysenv_cb(const char *key, const char *value,
+			 const struct config_context *ctx UNUSED, void *d)
 {
 	int k;
 
trailer.c +2 -0
index a2c3ed6f28c..06dc0b7f683 100644
@@ -482,6 +482,7 @@ static struct {
 };
 
 static int git_trailer_default_config(const char *conf_key, const char *value,
+				      const struct config_context *ctx UNUSED,
 				      void *cb UNUSED)
 {
 	const char *trailer_item, *variable_name;
@@ -514,6 +515,7 @@ static int git_trailer_default_config(const char *conf_key, const char *value,
 }
 
 static int git_trailer_config(const char *conf_key, const char *value,
+			      const struct config_context *ctx UNUSED,
 			      void *cb UNUSED)
 {
 	const char *trailer_item, *variable_name;
upload-pack.c +6 -2
index d3312006a32..951fd1f9c25 100644
@@ -1309,7 +1309,9 @@ static int parse_object_filter_config(const char *var, const char *value,
 	return 0;
 }
 
-static int upload_pack_config(const char *var, const char *value, void *cb_data)
+static int upload_pack_config(const char *var, const char *value,
+			      const struct config_context *ctx UNUSED,
+			      void *cb_data)
 {
 	struct upload_pack_data *data = cb_data;
 
@@ -1350,7 +1352,9 @@ static int upload_pack_config(const char *var, const char *value, void *cb_data)
 	return parse_hide_refs_config(var, value, "uploadpack", &data->hidden_refs);
 }
 
-static int upload_pack_protected_config(const char *var, const char *value, void *cb_data)
+static int upload_pack_protected_config(const char *var, const char *value,
+					const struct config_context *ctx UNUSED,
+					void *cb_data)
 {
 	struct upload_pack_data *data = cb_data;
 
urlmatch.c +4 -3
index eba0bdd77fe..1c45f23adf2 100644
@@ -551,7 +551,8 @@ static int cmp_matches(const struct urlmatch_item *a,
 	return 0;
 }
 
-int urlmatch_config_entry(const char *var, const char *value, void *cb)
+int urlmatch_config_entry(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb)
 {
 	struct string_list_item *item;
 	struct urlmatch_config *collect = cb;
@@ -565,7 +566,7 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
 
 	if (!skip_prefix(var, collect->section, &key) || *(key++) != '.') {
 		if (collect->cascade_fn)
-			return collect->cascade_fn(var, value, cb);
+			return collect->cascade_fn(var, value, ctx, cb);
 		return 0; /* not interested */
 	}
 	dot = strrchr(key, '.');
@@ -609,7 +610,7 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
 	strbuf_addstr(&synthkey, collect->section);
 	strbuf_addch(&synthkey, '.');
 	strbuf_addstr(&synthkey, key);
-	retval = collect->collect_fn(synthkey.buf, value, collect->cb);
+	retval = collect->collect_fn(synthkey.buf, value, ctx, collect->cb);
 
 	strbuf_release(&synthkey);
 	return retval;
urlmatch.h +2 -1
index bee374a642c..5ba85cea139 100644
@@ -71,7 +71,8 @@ struct urlmatch_config {
 	.vars = STRING_LIST_INIT_DUP, \
 }
 
-int urlmatch_config_entry(const char *var, const char *value, void *cb);
+int urlmatch_config_entry(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb);
 void urlmatch_config_release(struct urlmatch_config *config);
 
 #endif /* URL_MATCH_H */
xdiff-interface.c +3 -2
index 0460e03f5ed..dcbb5e09857 100644
@@ -307,7 +307,8 @@ int xdiff_compare_lines(const char *l1, long s1,
 
 int git_xmerge_style = -1;
 
-int git_xmerge_config(const char *var, const char *value, void *cb)
+int git_xmerge_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "merge.conflictstyle")) {
 		if (!value)
@@ -327,5 +328,5 @@ int git_xmerge_config(const char *var, const char *value, void *cb)
 			    value, var);
 		return 0;
 	}
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
xdiff-interface.h +3 -1
index 733c364d26c..e6f80df0462 100644
@@ -50,7 +50,9 @@ int buffer_is_binary(const char *ptr, unsigned long size);
 
 void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
 void xdiff_clear_find_func(xdemitconf_t *xecfg);
-int git_xmerge_config(const char *var, const char *value, void *cb);
+struct config_context;
+int git_xmerge_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb);
 extern int git_xmerge_style;
 
 /*
Signature · 1 line
gitgitgadget
#60 lore raw

PATCH v3 4/12 config.c: pass ctx in configsets

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Pass config_context to config callbacks in configset_iter(), trivially setting the .kvi member to the cached key_value_info. Then, in config callbacks that are only used with configsets, use the .kvi member to replace calls to current_config_*(), and delete current_config_line() because it has no remaining callers.

This leaves builtin/config.c and config.c as the only remaining users of current_config_*().

---
 builtin/remote.c       | 10 ++++++----
 config.c               | 35 ++++++++++++++++-------------------
 config.h               |  2 +-
 remote.c               |  7 ++++---
 t/helper/test-config.c | 11 ++++++-----
 5 files changed, 33 insertions(+), 32 deletions(-)
builtin/remote.c +6 -4
index 87de81105e2..d47f9ee21cf 100644
@@ -646,17 +646,19 @@ struct push_default_info
 };
 
 static int config_read_push_default(const char *key, const char *value,
-	const struct config_context *ctx UNUSED, void *cb)
+	const struct config_context *ctx, void *cb)
 {
+	const struct key_value_info *kvi = ctx->kvi;
+
 	struct push_default_info* info = cb;
 	if (strcmp(key, "remote.pushdefault") ||
 	    !value || strcmp(value, info->old_name))
 		return 0;
 
-	info->scope = current_config_scope();
+	info->scope = kvi->scope;
 	strbuf_reset(&info->origin);
-	strbuf_addstr(&info->origin, current_config_name());
-	info->linenr = current_config_line();
+	strbuf_addstr(&info->origin, config_origin_type_name(kvi->origin_type));
+	info->linenr = kvi->linenr;
 
 	return 0;
 }
config.c +16 -19
index 670d92cd76b..1e146c861b1 100644
@@ -2309,6 +2309,7 @@ static void configset_iter(struct config_reader *reader, struct config_set *set,
 	struct string_list *values;
 	struct config_set_element *entry;
 	struct configset_list *list = &set->list;
+	struct config_context ctx = CONFIG_CONTEXT_INIT;
 
 	for (i = 0; i < list->nr; i++) {
 		entry = list->items[i].e;
@@ -2316,12 +2317,11 @@ static void configset_iter(struct config_reader *reader, struct config_set *set,
 		values = &entry->value_list;
 
 		config_reader_set_kvi(reader, values->items[value_index].util);
-
-		if (fn(entry->key, values->items[value_index].string, NULL, data) < 0)
+		ctx.kvi = values->items[value_index].util;
+		if (fn(entry->key, values->items[value_index].string, &ctx, data) < 0)
 			git_die_config_linenr(entry->key,
-					      reader->config_kvi->filename,
-					      reader->config_kvi->linenr);
-
+					      ctx.kvi->filename,
+					      ctx.kvi->linenr);
 		config_reader_set_kvi(reader, NULL);
 	}
 }
@@ -3974,13 +3974,8 @@ static int reader_origin_type(struct config_reader *reader,
 	return 0;
 }
 
-const char *current_config_origin_type(void)
+const char *config_origin_type_name(enum config_origin_type type)
 {
-	enum config_origin_type type = CONFIG_ORIGIN_UNKNOWN;
-
-	if (reader_origin_type(&the_reader, &type))
-		BUG("current_config_origin_type called outside config callback");
-
 	switch (type) {
 	case CONFIG_ORIGIN_BLOB:
 		return "blob";
@@ -3997,6 +3992,16 @@ const char *current_config_origin_type(void)
 	}
 }
 
+const char *current_config_origin_type(void)
+{
+	enum config_origin_type type = CONFIG_ORIGIN_UNKNOWN;
+
+	if (reader_origin_type(&the_reader, &type))
+		BUG("current_config_origin_type called outside config callback");
+
+	return config_origin_type_name(type);
+}
+
 const char *config_scope_name(enum config_scope scope)
 {
 	switch (scope) {
@@ -4044,14 +4049,6 @@ enum config_scope current_config_scope(void)
 		return the_reader.parsing_scope;
 }
 
-int current_config_line(void)
-{
-	if (the_reader.config_kvi)
-		return the_reader.config_kvi->linenr;
-	else
-		return the_reader.source->linenr;
-}
-
 int lookup_config(const char **mapping, int nr_mapping, const char *var)
 {
 	int i;
config.h +1 -1
index bc52ceb72f2..b4236e120a8 100644
@@ -387,7 +387,7 @@ int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 enum config_scope current_config_scope(void);
 const char *current_config_origin_type(void);
 const char *current_config_name(void);
-int current_config_line(void);
+const char *config_origin_type_name(enum config_origin_type type);
 
 /*
  * Match and parse a config key of the form:
remote.c +4 -3
index dafb1acdc38..37d413e7892 100644
@@ -350,7 +350,7 @@ static void read_branches_file(struct remote_state *remote_state,
 }
 
 static int handle_config(const char *key, const char *value,
-			 const struct config_context *ctx UNUSED, void *cb)
+			 const struct config_context *ctx, void *cb)
 {
 	const char *name;
 	size_t namelen;
@@ -358,6 +358,7 @@ static int handle_config(const char *key, const char *value,
 	struct remote *remote;
 	struct branch *branch;
 	struct remote_state *remote_state = cb;
+	const struct key_value_info *kvi = ctx->kvi;
 
 	if (parse_config_key(key, "branch", &name, &namelen, &subkey) >= 0) {
 		/* There is no subsection. */
@@ -415,8 +416,8 @@ static int handle_config(const char *key, const char *value,
 	}
 	remote = make_remote(remote_state, name, namelen);
 	remote->origin = REMOTE_CONFIG;
-	if (current_config_scope() == CONFIG_SCOPE_LOCAL ||
-	    current_config_scope() == CONFIG_SCOPE_WORKTREE)
+	if (kvi->scope == CONFIG_SCOPE_LOCAL ||
+	    kvi->scope == CONFIG_SCOPE_WORKTREE)
 		remote->configured_in_repo = 1;
 	if (!strcmp(subkey, "mirror"))
 		remote->mirror = git_config_bool(key, value);
t/helper/test-config.c +6 -5
index 85ad815358e..3f4c3678318 100644
@@ -43,9 +43,10 @@
  */
 
 static int iterate_cb(const char *var, const char *value,
-		      const struct config_context *ctx UNUSED,
+		      const struct config_context *ctx,
 		      void *data UNUSED)
 {
+	const struct key_value_info *kvi = ctx->kvi;
 	static int nr;
 
 	if (nr++)
@@ -53,10 +54,10 @@ static int iterate_cb(const char *var, const char *value,
 
 	printf("key=%s\n", var);
 	printf("value=%s\n", value ? value : "(null)");
-	printf("origin=%s\n", current_config_origin_type());
-	printf("name=%s\n", current_config_name());
-	printf("lno=%d\n", current_config_line());
-	printf("scope=%s\n", config_scope_name(current_config_scope()));
+	printf("origin=%s\n", config_origin_type_name(kvi->origin_type));
+	printf("name=%s\n", kvi->filename ? kvi->filename : "");
+	printf("lno=%d\n", kvi->linenr);
+	printf("scope=%s\n", config_scope_name(kvi->scope));
 
 	return 0;
 }
Signature · 1 line
gitgitgadget
#61 lore raw

Re: [PATCH v3 04/12] config.c: pass ctx in configsets

Quoted 8 lines · Glen Choo via GitGitGadget

static int config_read_push_default(const char *key, const char *value,

builtin/remote.c +3 -1
-	const struct config_context *ctx UNUSED, void *cb)
+	const struct config_context *ctx, void *cb)
 {
+	const struct key_value_info *kvi = ctx->kvi;
+
 	struct push_default_info* info = cb;
 	if (strcmp(key, "remote.pushdefault") ||

builtin/remote.c struct push_default_info · line 661

Quoted 10 lines
builtin/remote.c +3 -3
 	    !value || strcmp(value, info->old_name))
 		return 0;
 
-	info->scope = current_config_scope();
+	info->scope = kvi->scope;
 	strbuf_reset(&info->origin);
-	strbuf_addstr(&info->origin, current_config_name());
-	info->linenr = current_config_line();
+	strbuf_addstr(&info->origin, config_origin_type_name(kvi->origin_type));
+	info->linenr = kvi->linenr;

Yay!

It is very pleasing to see these current_*() functions made unnecessary. This step only allows us to remove the _line() but not yet _scope(), but as long as we lose them at the end, going one step at a time is perfectly fine and readable.

config.c const char *current_config_origin_type(void) · line 4003

Quoted 9 lines
config.c +9 -0
+const char *current_config_origin_type(void)
+{
+	enum config_origin_type type = CONFIG_ORIGIN_UNKNOWN;
+
+	if (reader_origin_type(&the_reader, &type))
+		BUG("current_config_origin_type called outside config callback");
+
+	return config_origin_type_name(type);
+}

We still rely on the fact that the_reader is a global singleton, but that is of course OK in this early part of the series.

Looking nice.

#62 lore raw

PATCH v3 5/12 config: pass ctx with config files

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Pass config_context to config_callbacks when parsing config files. To provide the .kvi member, refactor out the configset logic that caches "struct config_source" and "enum config_scope" as a "struct key_value_info". Make the "enum config_scope" available to the config file machinery by plumbing an additional arg through git_config_from_file_with_options().

We do not exercise ctx yet because the remaining current_config_*() callers may be used with config_with_options(), which may read config from parameters, but parameters don't pass ctx yet.

---
 bundle-uri.c       |   1 +
 config.c           | 105 ++++++++++++++++++++++++++++++---------------
 config.h           |   8 ++--
 fsck.c             |   3 +-
 submodule-config.c |   5 ++-
 5 files changed, 81 insertions(+), 41 deletions(-)
bundle-uri.c +1 -0
index 0d5acc3dc51..64f32387745 100644
@@ -255,6 +255,7 @@ int bundle_uri_parse_config_format(const char *uri,
 	}
 	result = git_config_from_file_with_options(config_to_bundle_list,
 						   filename, list,
+						   CONFIG_SCOPE_UNKNOWN,
 						   &opts);
 
 	if (!result && list->mode == BUNDLE_MODE_NONE) {
config.c +70 -35

279 lines not on this page · raw message

config.h +5 -3
index b4236e120a8..1fc746c3a46 100644
@@ -170,16 +170,18 @@ int git_default_config(const char *, const char *,
 int git_config_from_file(config_fn_t fn, const char *, void *);
 
 int git_config_from_file_with_options(config_fn_t fn, const char *,
-				      void *,
+				      void *, enum config_scope,
 				      const struct config_options *);
 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, const struct config_options *opts);
+			void *data, enum config_scope scope,
+			const struct config_options *opts);
 int git_config_from_blob_oid(config_fn_t fn, const char *name,
 			     struct repository *repo,
-			     const struct object_id *oid, void *data);
+			     const struct object_id *oid, void *data,
+			     enum config_scope scope);
 void git_config_push_parameter(const char *text);
 void git_config_push_env(const char *spec);
 int git_config_from_parameters(config_fn_t fn, void *data);
fsck.c +2 -1
index 55b6a694853..f92c216fb5c 100644
@@ -1238,7 +1238,8 @@ static int fsck_blob(const struct object_id *oid, const char *buf,
 		data.ret = 0;
 		config_opts.error_action = CONFIG_ERROR_SILENT;
 		if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB,
-					".gitmodules", buf, size, &data, &config_opts))
+					".gitmodules", buf, size, &data,
+					CONFIG_SCOPE_UNKNOWN, &config_opts))
 			data.ret |= report(options, oid, OBJ_BLOB,
 					FSCK_MSG_GITMODULES_PARSE,
 					"could not parse gitmodules blob");
submodule-config.c +3 -2
index b364244102e..e19ab593aa4 100644
@@ -606,7 +606,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,
 	parameter.gitmodules_oid = &oid;
 	parameter.overwrite = 0;
 	git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
-			config, config_size, &parameter, NULL);
+			    config, config_size, &parameter, CONFIG_SCOPE_UNKNOWN, NULL);
 	strbuf_release(&rev);
 	free(config);
 
@@ -715,7 +715,8 @@ void gitmodules_config_oid(const struct object_id *commit_oid)
 
 	if (gitmodule_oid_from_commit(commit_oid, &oid, &rev)) {
 		git_config_from_blob_oid(gitmodules_cb, rev.buf,
-					 the_repository, &oid, the_repository);
+					 the_repository, &oid, the_repository,
+					 CONFIG_SCOPE_UNKNOWN);
 	}
 	strbuf_release(&rev);
 
Signature · 1 line
gitgitgadget
#63 lore raw

PATCH v3 6/12 builtin/config.c: test misuse of format_config()

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

current_config_*() functions aren't meant to be called outside of config callbacks because they read state that is only set when iterating through config. However, several sites in builtin/config.c are indirectly calling these functions outside of config callbacks thanks to the format_config() helper. Show the current, bad behavior via tests so that the fixes in a subsequent commit will be clearer.

The misbehaving cases are:

* "git config --get-urlmatch --show-scope" results in an "unknown" scope, where it arguably should show the config file's scope. It's clear that this wasn't intended, though: we knew that "--get-urlmatch" couldn't show config source metadata, which is why "--show-origin" was marked incompatible with "--get-urlmatch" when it was introduced [1]. It was most likely a mistake that we allowed "--show-scope" to sneak through.

* Similarly, "git config --default" doesn't set config source metadata , so "--show-scope" also results in "unknown", and "--show-origin" results in a BUG().

[1] https://lore.kernel.org/git/[email protected]/

---
 t/t1300-config.sh | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)
t/t1300-config.sh +25 -0
index 86bfbc2b364..fa6a8df2521 100755
@@ -1668,6 +1668,21 @@ test_expect_success 'urlmatch' '
 	test_cmp expect actual
 '
 
+test_expect_success 'urlmatch with --show-scope' '
+	cat >.git/config <<-\EOF &&
+	[http "https://weak.example.com"]
+		sslVerify = false
+		cookieFile = /tmp/cookie.txt
+	EOF
+
+	cat >expect <<-EOF &&
+	unknown	http.cookiefile /tmp/cookie.txt
+	unknown	http.sslverify false
+	EOF
+	git config --get-urlmatch --show-scope 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/"]
@@ -2055,6 +2070,10 @@ test_expect_success '--show-origin blob ref' '
 	test_cmp expect output
 '
 
+test_expect_success '--show-origin with --default' '
+	test_must_fail git config --show-origin --default foo some.key
+'
+
 test_expect_success '--show-scope with --list' '
 	cat >expect <<-EOF &&
 	global	user.global=true
@@ -2123,6 +2142,12 @@ test_expect_success '--show-scope with --show-origin' '
 	test_cmp expect output
 '
 
+test_expect_success '--show-scope with --default' '
+	git config --show-scope --default foo some.key >actual &&
+	echo "unknown	foo" >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success 'override global and system config' '
 	test_when_finished rm -f \"\$HOME\"/.gitconfig &&
 	cat >"$HOME"/.gitconfig <<-EOF &&
Signature · 1 line
gitgitgadget
#64 lore raw

Re: [PATCH v3 06/12] builtin/config.c: test misuse of format_config()

Quoted 5 lines · Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

current_config_*() functions aren't meant to be called outside of config callbacks because they read state that is only set when iterating through config.

Interesting. And config_context is about expressing the state of the iteration, so presumably some of these callers that do not have config_context to pass (because they happen outside iterations) will now (can|have to) pass NULL or something to say "I am asking format but from outside any iteration" or something?

Quoted 42 lines
t/t1300-config.sh +25 -0
+test_expect_success 'urlmatch with --show-scope' '
+	cat >.git/config <<-\EOF &&
+	[http "https://weak.example.com"]
+		sslVerify = false
+		cookieFile = /tmp/cookie.txt
+	EOF
+
+	cat >expect <<-EOF &&
+	unknown	http.cookiefile /tmp/cookie.txt
+	unknown	http.sslverify false
+	EOF
+	git config --get-urlmatch --show-scope 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/"]
@@ -2055,6 +2070,10 @@ test_expect_success '--show-origin blob ref' '
 	test_cmp expect output
 '
 
+test_expect_success '--show-origin with --default' '
+	test_must_fail git config --show-origin --default foo some.key
+'
+
 test_expect_success '--show-scope with --list' '
 	cat >expect <<-EOF &&
 	global	user.global=true
@@ -2123,6 +2142,12 @@ test_expect_success '--show-scope with --show-origin' '
 	test_cmp expect output
 '
 
+test_expect_success '--show-scope with --default' '
+	git config --show-scope --default foo some.key >actual &&
+	echo "unknown	foo" >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success 'override global and system config' '
 	test_when_finished rm -f \"\$HOME\"/.gitconfig &&
 	cat >"$HOME"/.gitconfig <<-EOF &&
#65 lore raw

Re: [PATCH v3 06/12] builtin/config.c: test misuse of format_config()

Quoted 9 lines · Junio C Hamano
Quoted 3 lines

current_config_*() functions aren't meant to be called outside of config callbacks because they read state that is only set when iterating through config.

Interesting. And config_context is about expressing the state of the iteration, so presumably some of these callers that do not have config_context to pass (because they happen outside iterations) will now (can|have to) pass NULL or something to say "I am asking format but from outside any iteration" or something?

Yup, they now have to pass a default value for config_context. For .kvi, it should be what's given by KVI_INIT (the next patch has an example).

#66 lore raw

Re: [PATCH v3 06/12] builtin/config.c: test misuse of format_config()

In reply to Glen Choo via GitGitGadget

t/t1300-config.sh test_expect_success '--show-origin blob ref' ' · line 2075

Quoted 3 lines · Glen Choo via GitGitGadget
t/t1300-config.sh +3 -0
+test_expect_success '--show-origin with --default' '
+	test_must_fail git config --show-origin --default foo some.key
+'

On my machine, this fails with

  BUG: config.c:4035: current_config_origin_type called outside config callback
  /usr/local/google/home/jonathantanmy/git/t/test-lib-functions.sh: line 1067: 3255109 Aborted                 "$@" 2>&7
  test_must_fail: died by signal 6: git config --show-origin --default foo some.key

(So it indeed fails, as expected, but test_must_fail seems to not like the exit code.)

Not sure if we need to be more precise with the exit code we're testing for.

#67 lore raw

Re: [PATCH v3 06/12] builtin/config.c: test misuse of format_config()

Quoted 11 lines · Jonathan Tan

t/t1300-config.sh test_expect_success '--show-origin blob ref' ' · line 2075

Quoted 3 lines · Glen Choo via GitGitGadget
t/t1300-config.sh +3 -0
+test_expect_success '--show-origin with --default' '
+	test_must_fail git config --show-origin --default foo some.key
+'

On my machine, this fails with

  BUG: config.c:4035: current_config_origin_type called outside config callback
  /usr/local/google/home/jonathantanmy/git/t/test-lib-functions.sh: line 1067: 3255109 Aborted                 "$@" 2>&7
  test_must_fail: died by signal 6: git config --show-origin --default foo some.key
Quoted 5 lines

(So it indeed fails, as expected, but test_must_fail seems to not like the exit code.)

Not sure if we need to be more precise with the exit code we're testing for.

That is test_must_fail operating as intended. We should _never_ hit a BUG() call, no matter what garbage we get from the user or the disk. The resolution is generally one of:

- there really is a bug, so we should fix it

- the invocation is garbage and expected to fail, but we should catch it sooner and give a nice error message, rather than getting as far as a BUG() call

But I didn't look at the patches to know which case this is.

-Peff

#68 lore raw

Re: [PATCH v3 06/12] builtin/config.c: test misuse of format_config()

In reply to Jonathan Tan
Quoted 7 lines · Jonathan Tan

t/t1300-config.sh test_expect_success '--show-origin blob ref' ' · line 2075

Quoted 3 lines · Glen Choo via GitGitGadget
t/t1300-config.sh +3 -0
+test_expect_success '--show-origin with --default' '
+	test_must_fail git config --show-origin --default foo some.key
+'

On my machine, this fails with

Quoted 6 lines
  BUG: config.c:4035: current_config_origin_type called outside config callback
  /usr/local/google/home/jonathantanmy/git/t/test-lib-functions.sh: line 1067: 3255109 Aborted                 "$@" 2>&7
  test_must_fail: died by signal 6: git config --show-origin --default foo some.key

(So it indeed fails, as expected, but test_must_fail seems to not like the exit code.)

Ah you're right. I was under the impression that this was doing the right thing on MacOS, but it doesn't work there either.

I think a good way to assert that we run into BUG() (Maybe test_match_signal on the numeric value of SIGABRT? But that sounds error prone), so I'll either use test_expect_failure or squash this patch.

#69 lore raw

PATCH v3 7/12 config.c: pass ctx with CLI config

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Pass config_context when parsing CLI config. To provide the .kvi member, refactor out kvi_from_param() from the logic that caches CLI config in configsets. Now that config_context and config_context.kvi is always present when config machinery calls config callbacks, plumb "kvi" so that we can remove all calls of current_config_scope() except for trace2/*.c (which will be handled in a later commit), and remove all other current_config_*() (the functions themselves and their calls). Note that this results in .kvi containing a different, more complete set of information than the mocked up "struct config_source" in git_config_from_parameters().

Plumbing "kvi" reveals a few places where we've been doing the wrong thing:

* git_config_parse_parameter() hasn't been setting config source information, so plumb "kvi" there too.

* "git config --get-urlmatch --show-scope" iterates config to collect values, but then attempts to display the scope after config iteration. Fix this by copying the "kvi" value in the collection phase so that it can be read back later. This means that we can now support "git config --get-urlmatch --show-origin" (we don't allow this combination of args because of this bug), but that is left unchanged for now.

* "git config --default" doesn't have config source metadata when displaying the default value. Fix this by treating the default value as if it came from the command line (e.g. like we do with "git -c" or "git config --file"), using kvi_from_param().

---
 builtin/config.c  | 47 +++++++++++++++++----------
 config.c          | 82 +++++++++++++++++++++++------------------------
 config.h          |  3 +-
 t/t1300-config.sh | 10 +++---
 4 files changed, 78 insertions(+), 64 deletions(-)
builtin/config.c +30 -17
index e14f967bbea..0c01ded5f05 100644
@@ -193,38 +193,42 @@ static void check_argc(int argc, int min, int max)
 	usage_builtin_config();
 }
 
-static void show_config_origin(struct strbuf *buf)
+static void show_config_origin(const struct key_value_info *kvi,
+			       struct strbuf *buf)
 {
 	const char term = end_nul ? '\0' : '\t';
 
-	strbuf_addstr(buf, current_config_origin_type());
+	strbuf_addstr(buf, config_origin_type_name(kvi->origin_type));
 	strbuf_addch(buf, ':');
 	if (end_nul)
-		strbuf_addstr(buf, current_config_name());
+		strbuf_addstr(buf, kvi->filename ? kvi->filename : "");
 	else
-		quote_c_style(current_config_name(), buf, NULL, 0);
+		quote_c_style(kvi->filename ? kvi->filename : "", buf, NULL, 0);
 	strbuf_addch(buf, term);
 }
 
-static void show_config_scope(struct strbuf *buf)
+static void show_config_scope(const struct key_value_info *kvi,
+			      struct strbuf *buf)
 {
 	const char term = end_nul ? '\0' : '\t';
-	const char *scope = config_scope_name(current_config_scope());
+	const char *scope = config_scope_name(kvi->scope);
 
 	strbuf_addstr(buf, N_(scope));
 	strbuf_addch(buf, term);
 }
 
 static int show_all_config(const char *key_, const char *value_,
-			   const struct config_context *ctx UNUSED,
+			   const struct config_context *ctx,
 			   void *cb UNUSED)
 {
+	const struct key_value_info *kvi = ctx->kvi;
+
 	if (show_origin || show_scope) {
 		struct strbuf buf = STRBUF_INIT;
 		if (show_scope)
-			show_config_scope(&buf);
+			show_config_scope(kvi, &buf);
 		if (show_origin)
-			show_config_origin(&buf);
+			show_config_origin(kvi, &buf);
 		/* Use fwrite as "buf" can contain \0's if "end_null" is set. */
 		fwrite(buf.buf, 1, buf.len, stdout);
 		strbuf_release(&buf);
@@ -242,12 +246,13 @@ struct strbuf_list {
 	int alloc;
 };
 
-static int format_config(struct strbuf *buf, const char *key_, const char *value_)
+static int format_config(struct strbuf *buf, const char *key_,
+			 const char *value_, const struct key_value_info *kvi)
 {
 	if (show_scope)
-		show_config_scope(buf);
+		show_config_scope(kvi, buf);
 	if (show_origin)
-		show_config_origin(buf);
+		show_config_origin(kvi, buf);
 	if (show_keys)
 		strbuf_addstr(buf, key_);
 	if (!omit_values) {
@@ -302,9 +307,10 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
 }
 
 static int collect_config(const char *key_, const char *value_,
-			  const struct config_context *ctx UNUSED, void *cb)
+			  const struct config_context *ctx, void *cb)
 {
 	struct strbuf_list *values = cb;
+	const struct key_value_info *kvi = ctx->kvi;
 
 	if (!use_key_regexp && strcmp(key_, key))
 		return 0;
@@ -319,7 +325,7 @@ static int collect_config(const char *key_, const char *value_,
 	ALLOC_GROW(values->items, values->nr + 1, values->alloc);
 	strbuf_init(&values->items[values->nr], 0);
 
-	return format_config(&values->items[values->nr++], key_, value_);
+	return format_config(&values->items[values->nr++], key_, value_, kvi);
 }
 
 static int get_value(const char *key_, const char *regex_, unsigned flags)
@@ -380,11 +386,14 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)
 			    &given_config_source, &config_options);
 
 	if (!values.nr && default_value) {
+		struct key_value_info kvi = KVI_INIT;
 		struct strbuf *item;
+
+		kvi_from_param(&kvi);
 		ALLOC_GROW(values.items, values.nr + 1, values.alloc);
 		item = &values.items[values.nr++];
 		strbuf_init(item, 0);
-		if (format_config(item, key_, default_value) < 0)
+		if (format_config(item, key_, default_value, &kvi) < 0)
 			die(_("failed to format default config value: %s"),
 				default_value);
 	}
@@ -559,15 +568,17 @@ static void check_write(void)
 struct urlmatch_current_candidate_value {
 	char value_is_null;
 	struct strbuf value;
+	struct key_value_info kvi;
 };
 
 static int urlmatch_collect_fn(const char *var, const char *value,
-			       const struct config_context *ctx UNUSED,
+			       const struct config_context *ctx,
 			       void *cb)
 {
 	struct string_list *values = cb;
 	struct string_list_item *item = string_list_insert(values, var);
 	struct urlmatch_current_candidate_value *matched = item->util;
+	const struct key_value_info *kvi = ctx->kvi;
 
 	if (!matched) {
 		matched = xmalloc(sizeof(*matched));
@@ -576,6 +587,7 @@ static int urlmatch_collect_fn(const char *var, const char *value,
 	} else {
 		strbuf_reset(&matched->value);
 	}
+	matched->kvi = *kvi;
 
 	if (value) {
 		strbuf_addstr(&matched->value, value);
@@ -622,7 +634,8 @@ static int get_urlmatch(const char *var, const char *url)
 		struct strbuf buf = STRBUF_INIT;
 
 		format_config(&buf, item->string,
-			      matched->value_is_null ? NULL : matched->value.buf);
+			      matched->value_is_null ? NULL : matched->value.buf,
+			      &matched->kvi);
 		fwrite(buf.buf, 1, buf.len, stdout);
 		strbuf_release(&buf);
 
config.c +41 -41

204 lines not on this page · raw message

config.h +1 -2
index 1fc746c3a46..7954a8cb104 100644
@@ -387,9 +387,8 @@ void git_global_config(char **user, char **xdg);
 int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 
 enum config_scope current_config_scope(void);
-const char *current_config_origin_type(void);
-const char *current_config_name(void);
 const char *config_origin_type_name(enum config_origin_type type);
+void kvi_from_param(struct key_value_info *out);
 
 /*
  * Match and parse a config key of the form:
t/t1300-config.sh +6 -4
index fa6a8df2521..387d336c91f 100755
@@ -1676,8 +1676,8 @@ test_expect_success 'urlmatch with --show-scope' '
 	EOF
 
 	cat >expect <<-EOF &&
-	unknown	http.cookiefile /tmp/cookie.txt
-	unknown	http.sslverify false
+	local	http.cookiefile /tmp/cookie.txt
+	local	http.sslverify false
 	EOF
 	git config --get-urlmatch --show-scope HTTP https://weak.example.com >actual &&
 	test_cmp expect actual
@@ -2071,7 +2071,9 @@ test_expect_success '--show-origin blob ref' '
 '
 
 test_expect_success '--show-origin with --default' '
-	test_must_fail git config --show-origin --default foo some.key
+	git config --show-origin --default foo some.key >actual &&
+	echo "command line:	foo" >expect &&
+	test_cmp expect actual
 '
 
 test_expect_success '--show-scope with --list' '
@@ -2144,7 +2146,7 @@ test_expect_success '--show-scope with --show-origin' '
 
 test_expect_success '--show-scope with --default' '
 	git config --show-scope --default foo some.key >actual &&
-	echo "unknown	foo" >expect &&
+	echo "command	foo" >expect &&
 	test_cmp expect actual
 '
 
Signature · 1 line
gitgitgadget
#70 lore raw

Re: [PATCH v3 07/12] config.c: pass ctx with CLI config

Quoted 9 lines · Glen Choo via GitGitGadget
config.c +1 -3
@@ -2465,16 +2488,10 @@ static int configset_add_value(struct config_reader *reader,
 	l_item->e = e;
 	l_item->value_index = e->value_list.nr - 1;
 
-	if (!reader->source)
-		BUG("configset_add_value has no source");
 	if (reader->source->name) {
-		kvi_from_source(reader->source, current_config_scope(), kv_info);
+		kvi_from_source(reader->source, kvi_p->scope, kv_info);

config.c static int configset_add_value(struct config_reader *reader, · line 2497

Quoted 10 lines
config.c +1 -5
 	} else {
-		/* for values read from `git_config_from_parameters()` */
-		kv_info->filename = NULL;
-		kv_info->linenr = -1;
-		kv_info->origin_type = CONFIG_ORIGIN_CMDLINE;
-		kv_info->scope = reader->parsing_scope;
+		kvi_from_param(kv_info);
 	}
 	si->util = kv_info;
 

Any reason to remove the "if (!reader->source)" guard? I don't think this patch does anything to ensure that reader is present. We can probably remove this once "reader" is removed.

The rest of the patch looks fine (and all patches prior look fine too.

#71 lore raw

Re: [PATCH v3 07/12] config.c: pass ctx with CLI config

Quoted 16 lines · Jonathan Tan
Quoted 15 lines · Glen Choo via GitGitGadget
config.c +1 -8
@@ -2465,16 +2488,10 @@ static int configset_add_value(struct config_reader *reader,
 	l_item->e = e;
 	l_item->value_index = e->value_list.nr - 1;
 
-	if (!reader->source)
-		BUG("configset_add_value has no source");
 	if (reader->source->name) {
-		kvi_from_source(reader->source, current_config_scope(), kv_info);
+		kvi_from_source(reader->source, kvi_p->scope, kv_info);
 	} else {
-		/* for values read from `git_config_from_parameters()` */
-		kv_info->filename = NULL;
-		kv_info->linenr = -1;
-		kv_info->origin_type = CONFIG_ORIGIN_CMDLINE;
-		kv_info->scope = reader->parsing_scope;
Quoted 8 lines

config.c static int configset_add_value(struct config_reader *reader, · line 2497

Quoted 4 lines
config.c +1 -0
+		kvi_from_param(kv_info);
 	}
 	si->util = kv_info;
 

Any reason to remove the "if (!reader->source)" guard? I don't think this patch does anything to ensure that reader is present. We can probably remove this once "reader" is removed.

Hm you're right, this shouldn't be removed here. I think this a leftover from an ancient version where I dropped references to "reader".

#72 lore raw

PATCH v3 8/12 trace2: plumb config kvi

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

There is a code path starting from trace2_def_param_fl() that eventually calls current_config_scope(), and thus it needs to have "kvi" plumbed through it. Additional plumbing is also needed to get "kvi" to trace2_def_param_fl(), which gets called by two code paths:

- Through tr2_cfg_cb(), which is a config callback, so it trivially receives "kvi" via the "struct config_context ctx" parameter.

- Through tr2_list_env_vars_fl(), which is a high level function that lists environment variables for tracing. This has been secretly behaving like git_config_from_parameters() (in that it parses config from environment variables/the CLI), but does not set config source information.

Teach tr2_list_env_vars_fl() to be well-behaved by using kvi_from_param(), which is used elsewhere for CLI/environment variable-based config.

As a result, current_config_scope() has no more callers, so remove it.

---
 config.c                | 46 -----------------------------------------
 config.h                |  1 -
 trace2.c                |  4 ++--
 trace2.h                |  3 ++-
 trace2/tr2_cfg.c        |  7 +++++--
 trace2/tr2_tgt.h        |  4 +++-
 trace2/tr2_tgt_event.c  |  4 ++--
 trace2/tr2_tgt_normal.c |  4 ++--
 trace2/tr2_tgt_perf.c   |  4 ++--
 9 files changed, 18 insertions(+), 59 deletions(-)
config.c +0 -46
index cfc5c7f10b3..f519656ebcf 100644
@@ -85,16 +85,6 @@ struct config_reader {
 	 */
 	struct config_source *source;
 	struct key_value_info *config_kvi;
-	/*
-	 * The "scope" of the current config source being parsed (repo, global,
-	 * etc). Like "source", this is only set when parsing a config source.
-	 * It's not part of "source" because it transcends a single file (i.e.,
-	 * a file included from .git/config is still in "repo" scope).
-	 *
-	 * When iterating through a configset, the equivalent value is
-	 * "config_kvi.scope" (see above).
-	 */
-	enum config_scope parsing_scope;
 };
 /*
  * Where possible, prefer to accept "struct config_reader" as an arg than to use
@@ -125,19 +115,9 @@ static inline struct config_source *config_reader_pop_source(struct config_reade
 static inline void config_reader_set_kvi(struct config_reader *reader,
 					 struct key_value_info *kvi)
 {
-	if (kvi && (reader->source || reader->parsing_scope))
-		BUG("kvi should not be set while parsing a config source");
 	reader->config_kvi = kvi;
 }
 
-static inline void config_reader_set_scope(struct config_reader *reader,
-					   enum config_scope scope)
-{
-	if (scope && reader->config_kvi)
-		BUG("scope should only be set when iterating through a config source");
-	reader->parsing_scope = scope;
-}
-
 static int pack_compression_seen;
 static int zlib_compression_seen;
 
@@ -411,18 +391,12 @@ static void populate_remote_urls(struct config_include_data *inc)
 {
 	struct config_options opts;
 
-	enum config_scope store_scope = inc->config_reader->parsing_scope;
-
 	opts = *inc->opts;
 	opts.unconditional_remote_url = 1;
 
-	config_reader_set_scope(inc->config_reader, 0);
-
 	inc->remote_urls = xmalloc(sizeof(*inc->remote_urls));
 	string_list_init_dup(inc->remote_urls);
 	config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
-
-	config_reader_set_scope(inc->config_reader, store_scope);
 }
 
 static int forbid_remote_url(const char *var, const char *value UNUSED,
@@ -2256,7 +2230,6 @@ static int do_git_config_sequence(struct config_reader *reader,
 	char *xdg_config = NULL;
 	char *user_config = NULL;
 	char *repo_config;
-	enum config_scope prev_parsing_scope = reader->parsing_scope;
 
 	if (opts->commondir)
 		repo_config = mkpathdup("%s/config", opts->commondir);
@@ -2265,7 +2238,6 @@ static int do_git_config_sequence(struct config_reader *reader,
 	else
 		repo_config = NULL;
 
-	config_reader_set_scope(reader, CONFIG_SCOPE_SYSTEM);
 	if (git_config_system() && system_config &&
 	    !access_or_die(system_config, R_OK,
 			   opts->system_gently ? ACCESS_EACCES_OK : 0))
@@ -2273,7 +2245,6 @@ static int do_git_config_sequence(struct config_reader *reader,
 							 data, CONFIG_SCOPE_SYSTEM,
 							 NULL);
 
-	config_reader_set_scope(reader, CONFIG_SCOPE_GLOBAL);
 	git_global_config(&user_config, &xdg_config);
 
 	if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
@@ -2284,13 +2255,11 @@ static int do_git_config_sequence(struct config_reader *reader,
 		ret += git_config_from_file_with_options(fn, user_config, data,
 							 CONFIG_SCOPE_GLOBAL, NULL);
 
-	config_reader_set_scope(reader, CONFIG_SCOPE_LOCAL);
 	if (!opts->ignore_repo && repo_config &&
 	    !access_or_die(repo_config, R_OK, 0))
 		ret += git_config_from_file_with_options(fn, repo_config, data,
 							 CONFIG_SCOPE_LOCAL, NULL);
 
-	config_reader_set_scope(reader, CONFIG_SCOPE_WORKTREE);
 	if (!opts->ignore_worktree && repository_format_worktree_config) {
 		char *path = git_pathdup("config.worktree");
 		if (!access_or_die(path, R_OK, 0))
@@ -2300,11 +2269,9 @@ static int do_git_config_sequence(struct config_reader *reader,
 		free(path);
 	}
 
-	config_reader_set_scope(reader, CONFIG_SCOPE_COMMAND);
 	if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0)
 		die(_("unable to parse command-line config"));
 
-	config_reader_set_scope(reader, prev_parsing_scope);
 	free(system_config);
 	free(xdg_config);
 	free(user_config);
@@ -2317,7 +2284,6 @@ int config_with_options(config_fn_t fn, void *data,
 			const struct config_options *opts)
 {
 	struct config_include_data inc = CONFIG_INCLUDE_INIT;
-	enum config_scope prev_scope = the_reader.parsing_scope;
 	int ret;
 
 	if (opts->respect_includes) {
@@ -2330,9 +2296,6 @@ int config_with_options(config_fn_t fn, void *data,
 		data = &inc;
 	}
 
-	if (config_source)
-		config_reader_set_scope(&the_reader, config_source->scope);
-
 	/*
 	 * If we have a specific filename, use it. Otherwise, follow the
 	 * regular lookup sequence.
@@ -2356,7 +2319,6 @@ int config_with_options(config_fn_t fn, void *data,
 		string_list_clear(inc.remote_urls, 0);
 		FREE_AND_NULL(inc.remote_urls);
 	}
-	config_reader_set_scope(&the_reader, prev_scope);
 	return ret;
 }
 
@@ -4076,14 +4038,6 @@ static int reader_config_name(struct config_reader *reader, const char **out)
 	return 0;
 }
 
-enum config_scope current_config_scope(void)
-{
-	if (the_reader.config_kvi)
-		return the_reader.config_kvi->scope;
-	else
-		return the_reader.parsing_scope;
-}
-
 int lookup_config(const char **mapping, int nr_mapping, const char *var)
 {
 	int i;
config.h +0 -1
index 7954a8cb104..092db17d2e4 100644
@@ -386,7 +386,6 @@ void git_global_config(char **user, char **xdg);
 
 int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 
-enum config_scope current_config_scope(void);
 const char *config_origin_type_name(enum config_origin_type type);
 void kvi_from_param(struct key_value_info *out);
 
trace2.c +2 -2
index 0efc4e7b958..49c23bfd05a 100644
@@ -634,7 +634,7 @@ void trace2_thread_exit_fl(const char *file, int line)
 }
 
 void trace2_def_param_fl(const char *file, int line, const char *param,
-			 const char *value)
+			 const char *value, const struct key_value_info *kvi)
 {
 	struct tr2_tgt *tgt_j;
 	int j;
@@ -644,7 +644,7 @@ void trace2_def_param_fl(const char *file, int line, const char *param,
 
 	for_each_wanted_builtin (j, tgt_j)
 		if (tgt_j->pfn_param_fl)
-			tgt_j->pfn_param_fl(file, line, param, value);
+			tgt_j->pfn_param_fl(file, line, param, value, kvi);
 }
 
 void trace2_def_repo_fl(const char *file, int line, struct repository *repo)
trace2.h +2 -1
index 4ced30c0db3..f5c5a9e6bac 100644
@@ -325,6 +325,7 @@ void trace2_thread_exit_fl(const char *file, int line);
 
 #define trace2_thread_exit() trace2_thread_exit_fl(__FILE__, __LINE__)
 
+struct key_value_info;
 /*
  * Emits a "def_param" message containing a key/value pair.
  *
@@ -334,7 +335,7 @@ void trace2_thread_exit_fl(const char *file, int line);
  * `core.abbrev`, `status.showUntrackedFiles`, or `--no-ahead-behind`.
  */
 void trace2_def_param_fl(const char *file, int line, const char *param,
-			 const char *value);
+			 const char *value, const struct key_value_info *kvi);
 
 #define trace2_def_param(param, value) \
 	trace2_def_param_fl(__FILE__, __LINE__, (param), (value))
trace2/tr2_cfg.c +5 -2
index 83bc4fd109c..00d546a27cd 100644
@@ -109,7 +109,8 @@ static int tr2_cfg_cb(const char *key, const char *value,
 		struct strbuf *buf = *s;
 		int wm = wildmatch(buf->buf, key, WM_CASEFOLD);
 		if (wm == WM_MATCH) {
-			trace2_def_param_fl(data->file, data->line, key, value);
+			trace2_def_param_fl(data->file, data->line, key, value,
+					    ctx->kvi);
 			return 0;
 		}
 	}
@@ -127,8 +128,10 @@ void tr2_cfg_list_config_fl(const char *file, int line)
 
 void tr2_list_env_vars_fl(const char *file, int line)
 {
+	struct key_value_info kvi = KVI_INIT;
 	struct strbuf **s;
 
+	kvi_from_param(&kvi);
 	if (tr2_load_env_vars() <= 0)
 		return;
 
@@ -136,7 +139,7 @@ void tr2_list_env_vars_fl(const char *file, int line)
 		struct strbuf *buf = *s;
 		const char *val = getenv(buf->buf);
 		if (val && *val)
-			trace2_def_param_fl(file, line, buf->buf, val);
+			trace2_def_param_fl(file, line, buf->buf, val, &kvi);
 	}
 }
 
trace2/tr2_tgt.h +3 -1
index bf8745c4f05..1f626cffea0 100644
@@ -69,8 +69,10 @@ typedef void(tr2_tgt_evt_exec_result_fl_t)(const char *file, int line,
 					   uint64_t us_elapsed_absolute,
 					   int exec_id, int code);
 
+struct key_value_info;
 typedef void(tr2_tgt_evt_param_fl_t)(const char *file, int line,
-				     const char *param, const char *value);
+				     const char *param, const char *value,
+				     const struct key_value_info *kvi);
 
 typedef void(tr2_tgt_evt_repo_fl_t)(const char *file, int line,
 				    const struct repository *repo);
trace2/tr2_tgt_event.c +2 -2
index 2af53e5d4de..53091781eca 100644
@@ -477,11 +477,11 @@ static void fn_exec_result_fl(const char *file, int line,
 }
 
 static void fn_param_fl(const char *file, int line, const char *param,
-			const char *value)
+			const char *value, const struct key_value_info *kvi)
 {
 	const char *event_name = "def_param";
 	struct json_writer jw = JSON_WRITER_INIT;
-	enum config_scope scope = current_config_scope();
+	enum config_scope scope = kvi->scope;
 	const char *scope_name = config_scope_name(scope);
 
 	jw_object_begin(&jw, 0);
trace2/tr2_tgt_normal.c +2 -2
index 1ebfb464d54..d25ea131643 100644
@@ -297,10 +297,10 @@ static void fn_exec_result_fl(const char *file, int line,
 }
 
 static void fn_param_fl(const char *file, int line, const char *param,
-			const char *value)
+			const char *value, const struct key_value_info *kvi)
 {
 	struct strbuf buf_payload = STRBUF_INIT;
-	enum config_scope scope = current_config_scope();
+	enum config_scope scope = kvi->scope;
 	const char *scope_name = config_scope_name(scope);
 
 	strbuf_addf(&buf_payload, "def_param scope:%s %s=%s", scope_name, param,
trace2/tr2_tgt_perf.c +2 -2
index 328e483a05e..a6f9a8a193e 100644
@@ -439,12 +439,12 @@ static void fn_exec_result_fl(const char *file, int line,
 }
 
 static void fn_param_fl(const char *file, int line, const char *param,
-			const char *value)
+			const char *value, const struct key_value_info *kvi)
 {
 	const char *event_name = "def_param";
 	struct strbuf buf_payload = STRBUF_INIT;
 	struct strbuf scope_payload = STRBUF_INIT;
-	enum config_scope scope = current_config_scope();
+	enum config_scope scope = kvi->scope;
 	const char *scope_name = config_scope_name(scope);
 
 	strbuf_addf(&buf_payload, "%s:%s", param, value);
Signature · 1 line
gitgitgadget
#73 lore raw

Re: [PATCH v3 08/12] trace2: plumb config kvi

config.c static inline struct config_source *config_reader_pop_source(struct config_reade · line 120

Quoted 9 lines · Glen Choo via GitGitGadget
config.c +0 -2
@@ -125,19 +115,9 @@ static inline struct config_source *config_reader_pop_source(struct config_reade
 static inline void config_reader_set_kvi(struct config_reader *reader,
 					 struct key_value_info *kvi)
 {
-	if (kvi && (reader->source || reader->parsing_scope))
-		BUG("kvi should not be set while parsing a config source");
 	reader->config_kvi = kvi;
 }
 

We're removing this check now because reader->parsing_scope is removed in this patch. Makes sense (no reason to just remove the reader->parsing_scope first and then remove the whole check later).

Rest of the patch looks good too.

#74 lore raw

PATCH v3 9/12 config: pass kvi to die_bad_number()

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Plumb "struct key_value_info" through all code paths that end in die_bad_number(), which lets us remove the helper functions that read analogous values from "struct config_reader". As a result, nothing reads config_reader.config_kvi any more, so remove that too.

In config.c, this requires changing the signature of git_configset_get_value() to 'return' "kvi" in an out parameter so that git_configset_get_<type>() can pass it to git_config_<type>(). Only numeric types will use "kvi", so for non-numeric types (e.g. git_configset_get_string()), pass NULL to indicate that the out parameter isn't needed.

Outside of config.c, config callbacks now need to pass "ctx->kvi" to any of the git_config_<type>() functions that parse a config string into a number type. Included is a .cocci patch to make that refactor.

The only exceptional case is builtin/config.c, where git_config_<type>() is called outside of a config callback (namely, on user-provided input), so config source information has never been available. In this case, die_bad_number() defaults to a generic, but perfectly descriptive message. Let's provide a safe, non-NULL for "kvi" anyway, but make sure not to change the message.

---
 archive-tar.c                              |   4 +-
 builtin/commit-graph.c                     |   4 +-
 builtin/commit.c                           |  10 +-
 builtin/config.c                           |  21 +--
 builtin/fetch.c                            |   4 +-
 builtin/fsmonitor--daemon.c                |   6 +-
 builtin/grep.c                             |   2 +-
 builtin/index-pack.c                       |   4 +-
 builtin/log.c                              |   2 +-
 builtin/pack-objects.c                     |  14 +-
 builtin/receive-pack.c                     |  10 +-
 builtin/submodule--helper.c                |   4 +-
 config.c                                   | 156 ++++++++-------------
 config.h                                   |  17 ++-
 contrib/coccinelle/git_config_number.cocci |  27 ++++
 diff.c                                     |   9 +-
 fmt-merge-msg.c                            |   2 +-
 help.c                                     |   4 +-
 http.c                                     |  10 +-
 imap-send.c                                |   2 +-
 sequencer.c                                |  22 +--
 setup.c                                    |   2 +-
 submodule-config.c                         |  13 +-
 submodule-config.h                         |   3 +-
 t/helper/test-config.c                     |   6 +-
 trace2/tr2_cfg.c                           |   2 +-
 upload-pack.c                              |  12 +-
 worktree.c                                 |   2 +-
 28 files changed, 191 insertions(+), 183 deletions(-)
 create mode 100644 contrib/coccinelle/git_config_number.cocci
archive-tar.c +2 -2
index ef06e516b1f..3df8af6d1b1 100644
@@ -412,14 +412,14 @@ static int tar_filter_config(const char *var, const char *value,
 }
 
 static int git_tar_config(const char *var, const char *value,
-			  const struct config_context *ctx UNUSED, void *cb)
+			  const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "tar.umask")) {
 		if (value && !strcmp(value, "user")) {
 			tar_umask = umask(0);
 			umask(tar_umask);
 		} else {
-			tar_umask = git_config_int(var, value);
+			tar_umask = git_config_int(var, value, ctx->kvi);
 		}
 		return 0;
 	}
builtin/commit-graph.c +2 -2
index 13d35b00ca8..c7e27a8c0a2 100644
@@ -186,11 +186,11 @@ static int write_option_max_new_filters(const struct option *opt,
 }
 
 static int git_commit_graph_write_config(const char *var, const char *value,
-					 const struct config_context *ctx UNUSED,
+					 const struct config_context *ctx,
 					 void *cb UNUSED)
 {
 	if (!strcmp(var, "commitgraph.maxnewfilters"))
-		write_opts.max_new_filters = git_config_int(var, value);
+		write_opts.max_new_filters = git_config_int(var, value, ctx->kvi);
 	/*
 	 * No need to fall-back to 'git_default_config', since this was already
 	 * called in 'cmd_commit_graph()'.
builtin/commit.c +6 -4
index 3bc87e5fc97..4e5e7722238 100644
@@ -1415,7 +1415,8 @@ static int git_status_config(const char *k, const char *v,
 		return git_column_config(k, v, "status", &s->colopts);
 	if (!strcmp(k, "status.submodulesummary")) {
 		int is_bool;
-		s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
+		s->submodule_summary = git_config_bool_or_int(k, v, ctx->kvi,
+							      &is_bool);
 		if (is_bool && s->submodule_summary)
 			s->submodule_summary = -1;
 		return 0;
@@ -1475,11 +1476,11 @@ static int git_status_config(const char *k, const char *v,
 	}
 	if (!strcmp(k, "diff.renamelimit")) {
 		if (s->rename_limit == -1)
-			s->rename_limit = git_config_int(k, v);
+			s->rename_limit = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "status.renamelimit")) {
-		s->rename_limit = git_config_int(k, v);
+		s->rename_limit = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "diff.renames")) {
@@ -1625,7 +1626,8 @@ static int git_commit_config(const char *k, const char *v,
 	}
 	if (!strcmp(k, "commit.verbose")) {
 		int is_bool;
-		config_commit_verbose = git_config_bool_or_int(k, v, &is_bool);
+		config_commit_verbose = git_config_bool_or_int(k, v, ctx->kvi,
+							       &is_bool);
 		return 0;
 	}
 
builtin/config.c +12 -9
index 0c01ded5f05..c61988a7bf3 100644
@@ -261,13 +261,14 @@ static int format_config(struct strbuf *buf, const char *key_,
 
 		if (type == TYPE_INT)
 			strbuf_addf(buf, "%"PRId64,
-				    git_config_int64(key_, value_ ? value_ : ""));
+				    git_config_int64(key_, value_ ? value_ : "", kvi));
 		else if (type == TYPE_BOOL)
 			strbuf_addstr(buf, git_config_bool(key_, value_) ?
 				      "true" : "false");
 		else if (type == TYPE_BOOL_OR_INT) {
 			int is_bool, v;
-			v = git_config_bool_or_int(key_, value_, &is_bool);
+			v = git_config_bool_or_int(key_, value_, kvi,
+						   &is_bool);
 			if (is_bool)
 				strbuf_addstr(buf, v ? "true" : "false");
 			else
@@ -422,7 +423,8 @@ free_strings:
 	return ret;
 }
 
-static char *normalize_value(const char *key, const char *value)
+static char *normalize_value(const char *key, const char *value,
+			     struct key_value_info *kvi)
 {
 	if (!value)
 		return NULL;
@@ -437,12 +439,12 @@ static char *normalize_value(const char *key, const char *value)
 		 */
 		return xstrdup(value);
 	if (type == TYPE_INT)
-		return xstrfmt("%"PRId64, git_config_int64(key, value));
+		return xstrfmt("%"PRId64, git_config_int64(key, value, kvi));
 	if (type == TYPE_BOOL)
 		return xstrdup(git_config_bool(key, value) ?  "true" : "false");
 	if (type == TYPE_BOOL_OR_INT) {
 		int is_bool, v;
-		v = git_config_bool_or_int(key, value, &is_bool);
+		v = git_config_bool_or_int(key, value, kvi, &is_bool);
 		if (!is_bool)
 			return xstrfmt("%d", v);
 		else
@@ -669,6 +671,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 	char *value = NULL;
 	int flags = 0;
 	int ret = 0;
+	struct key_value_info default_kvi = KVI_INIT;
 
 	given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
 
@@ -886,7 +889,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 	else if (actions == ACTION_SET) {
 		check_write();
 		check_argc(argc, 2, 2);
-		value = normalize_value(argv[0], argv[1]);
+		value = normalize_value(argv[0], argv[1], &default_kvi);
 		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"
@@ -895,7 +898,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 	else if (actions == ACTION_SET_ALL) {
 		check_write();
 		check_argc(argc, 2, 3);
-		value = normalize_value(argv[0], argv[1]);
+		value = normalize_value(argv[0], argv[1], &default_kvi);
 		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
 							     argv[0], value, argv[2],
 							     flags);
@@ -903,7 +906,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 	else if (actions == ACTION_ADD) {
 		check_write();
 		check_argc(argc, 2, 2);
-		value = normalize_value(argv[0], argv[1]);
+		value = normalize_value(argv[0], argv[1], &default_kvi);
 		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
 							     argv[0], value,
 							     CONFIG_REGEX_NONE,
@@ -912,7 +915,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 	else if (actions == ACTION_REPLACE_ALL) {
 		check_write();
 		check_argc(argc, 2, 3);
-		value = normalize_value(argv[0], argv[1]);
+		value = normalize_value(argv[0], argv[1], &default_kvi);
 		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
 							     argv[0], value, argv[2],
 							     flags | CONFIG_FLAGS_MULTI_REPLACE);
builtin/fetch.c +2 -2
index b607605b491..033d1fe65d2 100644
@@ -138,7 +138,7 @@ static int git_fetch_config(const char *k, const char *v,
 	}
 
 	if (!strcmp(k, "submodule.fetchjobs")) {
-		submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v);
+		submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v, ctx->kvi);
 		return 0;
 	} else if (!strcmp(k, "fetch.recursesubmodules")) {
 		recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
@@ -146,7 +146,7 @@ static int git_fetch_config(const char *k, const char *v,
 	}
 
 	if (!strcmp(k, "fetch.parallel")) {
-		fetch_parallel_config = git_config_int(k, v);
+		fetch_parallel_config = git_config_int(k, v, ctx->kvi);
 		if (fetch_parallel_config < 0)
 			die(_("fetch.parallel cannot be negative"));
 		if (!fetch_parallel_config)
builtin/fsmonitor--daemon.c +3 -3
index 91a776e2f17..6d2826b07da 100644
@@ -41,7 +41,7 @@ static int fsmonitor_config(const char *var, const char *value,
 			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, FSMONITOR__IPC_THREADS)) {
-		int i = git_config_int(var, value);
+		int i = git_config_int(var, value, ctx->kvi);
 		if (i < 1)
 			return error(_("value of '%s' out of range: %d"),
 				     FSMONITOR__IPC_THREADS, i);
@@ -50,7 +50,7 @@ static int fsmonitor_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, FSMONITOR__START_TIMEOUT)) {
-		int i = git_config_int(var, value);
+		int i = git_config_int(var, value, ctx->kvi);
 		if (i < 0)
 			return error(_("value of '%s' out of range: %d"),
 				     FSMONITOR__START_TIMEOUT, i);
@@ -60,7 +60,7 @@ static int fsmonitor_config(const char *var, const char *value,
 
 	if (!strcmp(var, FSMONITOR__ANNOUNCE_STARTUP)) {
 		int is_bool;
-		int i = git_config_bool_or_int(var, value, &is_bool);
+		int i = git_config_bool_or_int(var, value, ctx->kvi, &is_bool);
 		if (i < 0)
 			return error(_("value of '%s' not bool or int: %d"),
 				     var, i);
builtin/grep.c +1 -1
index 757d52b94ec..3a464e6faab 100644
@@ -301,7 +301,7 @@ static int grep_cmd_config(const char *var, const char *value,
 		st = -1;
 
 	if (!strcmp(var, "grep.threads")) {
-		num_threads = git_config_int(var, value);
+		num_threads = git_config_int(var, value, ctx->kvi);
 		if (num_threads < 0)
 			die(_("invalid number of threads specified (%d) for %s"),
 			    num_threads, var);
builtin/index-pack.c +2 -2
index 31914aac298..857518fdf83 100644
@@ -1587,13 +1587,13 @@ static int git_index_pack_config(const char *k, const char *v,
 	struct pack_idx_option *opts = cb;
 
 	if (!strcmp(k, "pack.indexversion")) {
-		opts->version = git_config_int(k, v);
+		opts->version = git_config_int(k, v, ctx->kvi);
 		if (opts->version > 2)
 			die(_("bad pack.indexVersion=%"PRIu32), opts->version);
 		return 0;
 	}
 	if (!strcmp(k, "pack.threads")) {
-		nr_threads = git_config_int(k, v);
+		nr_threads = git_config_int(k, v, ctx->kvi);
 		if (nr_threads < 0)
 			die(_("invalid number of threads specified (%d)"),
 			    nr_threads);
builtin/log.c +1 -1
index ac5f74b6b06..675cf358f7a 100644
@@ -574,7 +574,7 @@ static int git_log_config(const char *var, const char *value,
 	if (!strcmp(var, "format.subjectprefix"))
 		return git_config_string(&fmt_patch_subject_prefix, var, value);
 	if (!strcmp(var, "format.filenamemaxlength")) {
-		fmt_patch_name_max = git_config_int(var, value);
+		fmt_patch_name_max = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(var, "format.encodeemailheaders")) {
builtin/pack-objects.c +7 -7
index ad4d8fafb0b..d6d9079fd93 100644
@@ -3139,23 +3139,23 @@ static int git_pack_config(const char *k, const char *v,
 			   const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(k, "pack.window")) {
-		window = git_config_int(k, v);
+		window = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.windowmemory")) {
-		window_memory_limit = git_config_ulong(k, v);
+		window_memory_limit = git_config_ulong(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.depth")) {
-		depth = git_config_int(k, v);
+		depth = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.deltacachesize")) {
-		max_delta_cache_size = git_config_int(k, v);
+		max_delta_cache_size = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.deltacachelimit")) {
-		cache_max_small_delta_size = git_config_int(k, v);
+		cache_max_small_delta_size = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.writebitmaphashcache")) {
@@ -3181,7 +3181,7 @@ static int git_pack_config(const char *k, const char *v,
 		return 0;
 	}
 	if (!strcmp(k, "pack.threads")) {
-		delta_search_threads = git_config_int(k, v);
+		delta_search_threads = git_config_int(k, v, ctx->kvi);
 		if (delta_search_threads < 0)
 			die(_("invalid number of threads specified (%d)"),
 			    delta_search_threads);
@@ -3192,7 +3192,7 @@ static int git_pack_config(const char *k, const char *v,
 		return 0;
 	}
 	if (!strcmp(k, "pack.indexversion")) {
-		pack_idx_opts.version = git_config_int(k, v);
+		pack_idx_opts.version = git_config_int(k, v, ctx->kvi);
 		if (pack_idx_opts.version > 2)
 			die(_("bad pack.indexVersion=%"PRIu32),
 			    pack_idx_opts.version);
builtin/receive-pack.c +5 -5
index 94d9898aff7..98f6f0038f0 100644
@@ -158,12 +158,12 @@ static int receive_pack_config(const char *var, const char *value,
 	}
 
 	if (strcmp(var, "receive.unpacklimit") == 0) {
-		receive_unpack_limit = git_config_int(var, value);
+		receive_unpack_limit = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
 	if (strcmp(var, "transfer.unpacklimit") == 0) {
-		transfer_unpack_limit = git_config_int(var, value);
+		transfer_unpack_limit = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
@@ -231,7 +231,7 @@ static int receive_pack_config(const char *var, const char *value,
 		return git_config_string(&cert_nonce_seed, var, value);
 
 	if (strcmp(var, "receive.certnonceslop") == 0) {
-		nonce_stamp_slop_limit = git_config_ulong(var, value);
+		nonce_stamp_slop_limit = git_config_ulong(var, value, ctx->kvi);
 		return 0;
 	}
 
@@ -246,12 +246,12 @@ static int receive_pack_config(const char *var, const char *value,
 	}
 
 	if (strcmp(var, "receive.keepalive") == 0) {
-		keepalive_in_sec = git_config_int(var, value);
+		keepalive_in_sec = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
 	if (strcmp(var, "receive.maxinputsize") == 0) {
-		max_input_size = git_config_int64(var, value);
+		max_input_size = git_config_int64(var, value, ctx->kvi);
 		return 0;
 	}
 
builtin/submodule--helper.c +2 -2
index 3f8f3692937..9e78e5f7a9d 100644
@@ -2189,13 +2189,13 @@ static int update_clone_task_finished(int result,
 }
 
 static int git_update_clone_config(const char *var, const char *value,
-				   const struct config_context *ctx UNUSED,
+				   const struct config_context *ctx,
 				   void *cb)
 {
 	int *max_jobs = cb;
 
 	if (!strcmp(var, "submodule.fetchjobs"))
-		*max_jobs = parse_submodule_fetchjobs(var, value);
+		*max_jobs = parse_submodule_fetchjobs(var, value, ctx->kvi);
 	return 0;
 }
 
config.c +61 -95

382 lines not on this page · raw message

config.h +11 -6
index 092db17d2e4..a8f41c59e6c 100644
@@ -249,22 +249,26 @@ int git_parse_maybe_bool(const char *);
  * Parse the string to an integer, including unit factors. Dies on error;
  * otherwise, returns the parsed result.
  */
-int git_config_int(const char *, const char *);
+int git_config_int(const char *, const char *, const struct key_value_info *);
 
-int64_t git_config_int64(const char *, const char *);
+int64_t git_config_int64(const char *, const char *,
+			 const struct key_value_info *);
 
 /**
  * Identical to `git_config_int`, but for unsigned longs.
  */
-unsigned long git_config_ulong(const char *, const char *);
+unsigned long git_config_ulong(const char *, const char *,
+			       const struct key_value_info *);
 
-ssize_t git_config_ssize_t(const char *, const char *);
+ssize_t git_config_ssize_t(const char *, const char *,
+			   const struct key_value_info *);
 
 /**
  * Same as `git_config_bool`, except that integers are returned as-is, and
  * an `is_bool` flag is unset.
  */
-int git_config_bool_or_int(const char *, const char *, int *);
+int git_config_bool_or_int(const char *, const char *,
+			   const struct key_value_info *, int *);
 
 /**
  * Parse a string into a boolean value, respecting keywords like "true" and
@@ -529,7 +533,8 @@ int git_configset_get(struct config_set *cs, const char *key);
  * touching `value`. The caller should not free or modify `value`, as it
  * is owned by the cache.
  */
-int git_configset_get_value(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_value(struct config_set *cs, const char *key,
+			    const char **dest, struct key_value_info *kvi);
 
 int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
 int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
contrib/coccinelle/git_config_number.cocci new file +27 -0
new file mode 100644
index 00000000000..7b57dceefe6
@@ -0,0 +1,27 @@
+@@
+identifier C1, C2, C3;
+@@
+(
+(
+git_config_int
+|
+git_config_int64
+|
+git_config_ulong
+|
+git_config_ssize_t
+)
+  (C1, C2
++ , ctx->kvi
+  )
+|
+(
+git_configset_get_value
+|
+git_config_bool_or_int
+)
+  (C1, C2
++ , ctx->kvi
+ , C3
+  )
+)
diff.c +5 -4
index 07be3bee137..8775323e39c 100644
@@ -379,13 +379,14 @@ int git_diff_ui_config(const char *var, const char *value,
 		return 0;
 	}
 	if (!strcmp(var, "diff.context")) {
-		diff_context_default = git_config_int(var, value);
+		diff_context_default = git_config_int(var, value, ctx->kvi);
 		if (diff_context_default < 0)
 			return -1;
 		return 0;
 	}
 	if (!strcmp(var, "diff.interhunkcontext")) {
-		diff_interhunk_context_default = git_config_int(var, value);
+		diff_interhunk_context_default = git_config_int(var, value,
+								ctx->kvi);
 		if (diff_interhunk_context_default < 0)
 			return -1;
 		return 0;
@@ -411,7 +412,7 @@ int git_diff_ui_config(const char *var, const char *value,
 		return 0;
 	}
 	if (!strcmp(var, "diff.statgraphwidth")) {
-		diff_stat_graph_width = git_config_int(var, value);
+		diff_stat_graph_width = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(var, "diff.external"))
@@ -450,7 +451,7 @@ int git_diff_basic_config(const char *var, const char *value,
 	const char *name;
 
 	if (!strcmp(var, "diff.renamelimit")) {
-		diff_rename_limit_default = git_config_int(var, value);
+		diff_rename_limit_default = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
fmt-merge-msg.c +1 -1
index 10137444321..3ae59bde2f2 100644
@@ -25,7 +25,7 @@ int fmt_merge_msg_config(const char *key, const char *value,
 {
 	if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
 		int is_bool;
-		merge_log_config = git_config_bool_or_int(key, value, &is_bool);
+		merge_log_config = git_config_bool_or_int(key, value, ctx->kvi, &is_bool);
 		if (!is_bool && merge_log_config < 0)
 			return error("%s: negative length %s", key, value);
 		if (is_bool && merge_log_config)
help.c +2 -2
index ac0ae5ac0dc..389382b1482 100644
@@ -545,7 +545,7 @@ static struct cmdnames aliases;
 #define AUTOCORRECT_IMMEDIATELY (-1)
 
 static int git_unknown_cmd_config(const char *var, const char *value,
-				  const struct config_context *ctx UNUSED,
+				  const struct config_context *ctx,
 				  void *cb UNUSED)
 {
 	const char *p;
@@ -560,7 +560,7 @@ static int git_unknown_cmd_config(const char *var, const char *value,
 		} else if (!strcmp(value, "prompt")) {
 			autocorrect = AUTOCORRECT_PROMPT;
 		} else {
-			int v = git_config_int(var, value);
+			int v = git_config_int(var, value, ctx->kvi);
 			autocorrect = (v < 0)
 				? AUTOCORRECT_IMMEDIATELY : v;
 		}
http.c +5 -5
index 762502828c9..b12b234bde1 100644
@@ -414,21 +414,21 @@ static int http_options(const char *var, const char *value,
 	}
 
 	if (!strcmp("http.minsessions", var)) {
-		min_curl_sessions = git_config_int(var, value);
+		min_curl_sessions = git_config_int(var, value, ctx->kvi);
 		if (min_curl_sessions > 1)
 			min_curl_sessions = 1;
 		return 0;
 	}
 	if (!strcmp("http.maxrequests", var)) {
-		max_requests = git_config_int(var, value);
+		max_requests = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp("http.lowspeedlimit", var)) {
-		curl_low_speed_limit = (long)git_config_int(var, value);
+		curl_low_speed_limit = (long)git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp("http.lowspeedtime", var)) {
-		curl_low_speed_time = (long)git_config_int(var, value);
+		curl_low_speed_time = (long)git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
@@ -464,7 +464,7 @@ static int http_options(const char *var, const char *value,
 	}
 
 	if (!strcmp("http.postbuffer", var)) {
-		http_post_buffer = git_config_ssize_t(var, value);
+		http_post_buffer = git_config_ssize_t(var, value, ctx->kvi);
 		if (http_post_buffer < 0)
 			warning(_("negative value for http.postBuffer; defaulting to %d"), LARGE_PACKET_MAX);
 		if (http_post_buffer < LARGE_PACKET_MAX)
imap-send.c +1 -1
index 47777e76861..3518a4ace6c 100644
@@ -1342,7 +1342,7 @@ static int git_imap_config(const char *var, const char *val,
 	else if (!strcmp("imap.authmethod", var))
 		return git_config_string(&server.auth_method, var, val);
 	else if (!strcmp("imap.port", var))
-		server.port = git_config_int(var, val);
+		server.port = git_config_int(var, val, ctx->kvi);
 	else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			git_die_config("imap.host", "Missing value for 'imap.host'");
sequencer.c +11 -11
index 34754d17596..a0aaee3056d 100644
@@ -2883,7 +2883,7 @@ static int git_config_string_dup(char **dest,
 }
 
 static int populate_opts_cb(const char *key, const char *value,
-			    const struct config_context *ctx UNUSED,
+			    const struct config_context *ctx,
 			    void *data)
 {
 	struct replay_opts *opts = data;
@@ -2892,26 +2892,26 @@ static int populate_opts_cb(const char *key, const char *value,
 	if (!value)
 		error_flag = 0;
 	else if (!strcmp(key, "options.no-commit"))
-		opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
+		opts->no_commit = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.edit"))
-		opts->edit = git_config_bool_or_int(key, value, &error_flag);
+		opts->edit = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.allow-empty"))
 		opts->allow_empty =
-			git_config_bool_or_int(key, value, &error_flag);
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.allow-empty-message"))
 		opts->allow_empty_message =
-			git_config_bool_or_int(key, value, &error_flag);
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.keep-redundant-commits"))
 		opts->keep_redundant_commits =
-			git_config_bool_or_int(key, value, &error_flag);
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.signoff"))
-		opts->signoff = git_config_bool_or_int(key, value, &error_flag);
+		opts->signoff = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.record-origin"))
-		opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
+		opts->record_origin = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.allow-ff"))
-		opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
+		opts->allow_ff = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.mainline"))
-		opts->mainline = git_config_int(key, value);
+		opts->mainline = git_config_int(key, value, ctx->kvi);
 	else if (!strcmp(key, "options.strategy"))
 		git_config_string_dup(&opts->strategy, key, value);
 	else if (!strcmp(key, "options.gpg-sign"))
@@ -2920,7 +2920,7 @@ static int populate_opts_cb(const char *key, const char *value,
 		strvec_push(&opts->xopts, value);
 	} else if (!strcmp(key, "options.allow-rerere-auto"))
 		opts->allow_rerere_auto =
-			git_config_bool_or_int(key, value, &error_flag) ?
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag) ?
 				RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;
 	else if (!strcmp(key, "options.default-msg-cleanup")) {
 		opts->explicit_cleanup = 1;
setup.c +1 -1
index e7f81151ef0..260ad92df6e 100644
@@ -597,7 +597,7 @@ static int check_repo_format(const char *var, const char *value,
 	const char *ext;
 
 	if (strcmp(var, "core.repositoryformatversion") == 0)
-		data->version = git_config_int(var, value);
+		data->version = git_config_int(var, value, ctx->kvi);
 	else if (skip_prefix(var, "extensions.", &ext)) {
 		switch (handle_extension_v0(var, value, ext, data)) {
 		case EXTENSION_ERROR:
submodule-config.c +7 -6
index e19ab593aa4..0cda0f659f2 100644
@@ -304,9 +304,10 @@ static int parse_fetch_recurse(const char *opt, const char *arg,
 	}
 }
 
-int parse_submodule_fetchjobs(const char *var, const char *value)
+int parse_submodule_fetchjobs(const char *var, const char *value,
+			      const struct key_value_info *kvi)
 {
-	int fetchjobs = git_config_int(var, value);
+	int fetchjobs = git_config_int(var, value, kvi);
 	if (fetchjobs < 0)
 		die(_("negative values not allowed for submodule.fetchJobs"));
 	if (!fetchjobs)
@@ -850,14 +851,14 @@ struct fetch_config {
 };
 
 static int gitmodules_fetch_config(const char *var, const char *value,
-				   const struct config_context *ctx UNUSED,
+				   const struct config_context *ctx,
 				   void *cb)
 {
 	struct fetch_config *config = cb;
 	if (!strcmp(var, "submodule.fetchjobs")) {
 		if (config->max_children)
 			*(config->max_children) =
-				parse_submodule_fetchjobs(var, value);
+				parse_submodule_fetchjobs(var, value, ctx->kvi);
 		return 0;
 	} else if (!strcmp(var, "fetch.recursesubmodules")) {
 		if (config->recurse_submodules)
@@ -879,12 +880,12 @@ void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules)
 }
 
 static int gitmodules_update_clone_config(const char *var, const char *value,
-					  const struct config_context *ctx UNUSED,
+					  const struct config_context *ctx,
 					  void *cb)
 {
 	int *max_jobs = cb;
 	if (!strcmp(var, "submodule.fetchjobs"))
-		*max_jobs = parse_submodule_fetchjobs(var, value);
+		*max_jobs = parse_submodule_fetchjobs(var, value, ctx->kvi);
 	return 0;
 }
 
submodule-config.h +2 -1
index c2045875bbb..2a37689cc27 100644
@@ -50,7 +50,8 @@ struct repository;
 
 void submodule_cache_free(struct submodule_cache *cache);
 
-int parse_submodule_fetchjobs(const char *var, const char *value);
+int parse_submodule_fetchjobs(const char *var, const char *value,
+			      const struct key_value_info *kvi);
 int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
 struct option;
 int option_fetch_parse_recurse_submodules(const struct option *opt,
t/helper/test-config.c +3 -3
index 3f4c3678318..ed444ca4c25 100644
@@ -63,12 +63,12 @@ static int iterate_cb(const char *var, const char *value,
 }
 
 static int parse_int_cb(const char *var, const char *value,
-			const struct config_context *ctx UNUSED, void *data)
+			const struct config_context *ctx, void *data)
 {
 	const char *key_to_match = data;
 
 	if (!strcmp(key_to_match, var)) {
-		int parsed = git_config_int(value, value);
+		int parsed = git_config_int(value, value, ctx->kvi);
 		printf("%d\n", parsed);
 	}
 	return 0;
@@ -182,7 +182,7 @@ int cmd__config(int argc, const char **argv)
 				goto exit2;
 			}
 		}
-		if (!git_configset_get_value(&cs, argv[2], &v)) {
+		if (!git_configset_get_value(&cs, argv[2], &v, NULL)) {
 			if (!v)
 				printf("(NULL)\n");
 			else
trace2/tr2_cfg.c +1 -1
index 00d546a27cd..733d9d2872a 100644
@@ -100,7 +100,7 @@ struct tr2_cfg_data {
  * See if the given config key matches any of our patterns of interest.
  */
 static int tr2_cfg_cb(const char *key, const char *value,
-		      const struct config_context *ctx UNUSED, void *d)
+		      const struct config_context *ctx, void *d)
 {
 	struct strbuf **s;
 	struct tr2_cfg_data *data = (struct tr2_cfg_data *)d;
upload-pack.c +7 -5
index 951fd1f9c25..c03415a5460 100644
@@ -1275,7 +1275,8 @@ static int find_symref(const char *refname,
 }
 
 static int parse_object_filter_config(const char *var, const char *value,
-				       struct upload_pack_data *data)
+				      const struct key_value_info *kvi,
+				      struct upload_pack_data *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 	const char *sub, *key;
@@ -1302,7 +1303,8 @@ static int parse_object_filter_config(const char *var, const char *value,
 		}
 		string_list_insert(&data->allowed_filters, buf.buf)->util =
 			(void *)(intptr_t)1;
-		data->tree_filter_max_depth = git_config_ulong(var, value);
+		data->tree_filter_max_depth = git_config_ulong(var, value,
+							       kvi);
 	}
 
 	strbuf_release(&buf);
@@ -1310,7 +1312,7 @@ static int parse_object_filter_config(const char *var, const char *value,
 }
 
 static int upload_pack_config(const char *var, const char *value,
-			      const struct config_context *ctx UNUSED,
+			      const struct config_context *ctx,
 			      void *cb_data)
 {
 	struct upload_pack_data *data = cb_data;
@@ -1331,7 +1333,7 @@ static int upload_pack_config(const char *var, const char *value,
 		else
 			data->allow_uor &= ~ALLOW_ANY_SHA1;
 	} else if (!strcmp("uploadpack.keepalive", var)) {
-		data->keepalive = git_config_int(var, value);
+		data->keepalive = git_config_int(var, value, ctx->kvi);
 		if (!data->keepalive)
 			data->keepalive = -1;
 	} else if (!strcmp("uploadpack.allowfilter", var)) {
@@ -1346,7 +1348,7 @@ static int upload_pack_config(const char *var, const char *value,
 		data->advertise_sid = git_config_bool(var, value);
 	}
 
-	if (parse_object_filter_config(var, value, data) < 0)
+	if (parse_object_filter_config(var, value, ctx->kvi, data) < 0)
 		return -1;
 
 	return parse_hide_refs_config(var, value, "uploadpack", &data->hidden_refs);
worktree.c +1 -1
index b5ee71c5ebd..1fbdbd745fb 100644
@@ -835,7 +835,7 @@ int init_worktree_config(struct repository *r)
 	 * Relocate that value to avoid breaking all worktrees with this
 	 * upgrade to worktree config.
 	 */
-	if (!git_configset_get_value(&cs, "core.worktree", &core_worktree)) {
+	if (!git_configset_get_value(&cs, "core.worktree", &core_worktree, NULL)) {
 		if ((res = move_config_setting("core.worktree", core_worktree,
 					       common_config_file,
 					       main_worktree_file)))
Signature · 1 line
gitgitgadget
#75 lore raw

PATCH v3 10/12 config.c: remove config_reader from configsets

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Remove the last usage of "struct config_reader" from configsets by copying the "kvi" arg instead of recomputing "kvi" from config_reader.source. Since we no longer need to pass both "struct config_reader" and "struct config_set" in a single "void *cb", remove "struct configset_add_data" too.

---
 config.c | 42 ++++++++++--------------------------------
 1 file changed, 10 insertions(+), 32 deletions(-)
config.c +10 -32
index a2887b856aa..958ba166cf9 100644
@@ -2303,8 +2303,7 @@ int config_with_options(config_fn_t fn, void *data,
 	return ret;
 }
 
-static void configset_iter(struct config_reader *reader, struct config_set *set,
-			   config_fn_t fn, void *data)
+static void configset_iter(struct config_set *set, config_fn_t fn, void *data)
 {
 	int i, value_index;
 	struct string_list *values;
@@ -2398,7 +2397,6 @@ static int configset_find_element(struct config_set *set, const char *key,
 }
 
 static int configset_add_value(const struct key_value_info *kvi_p,
-			       struct config_reader *reader,
 			       struct config_set *set, const char *key,
 			       const char *value)
 {
@@ -2429,11 +2427,7 @@ static int configset_add_value(const struct key_value_info *kvi_p,
 	l_item->e = e;
 	l_item->value_index = e->value_list.nr - 1;
 
-	if (reader->source->name) {
-		kvi_from_source(reader->source, kvi_p->scope, kv_info);
-	} else {
-		kvi_from_param(kv_info);
-	}
+	memcpy(kv_info, kvi_p, sizeof(struct key_value_info));
 	si->util = kv_info;
 
 	return 0;
@@ -2481,28 +2475,18 @@ void git_configset_clear(struct config_set *set)
 	set->list.items = NULL;
 }
 
-struct configset_add_data {
-	struct config_set *config_set;
-	struct config_reader *config_reader;
-};
-#define CONFIGSET_ADD_INIT { 0 }
-
 static int config_set_callback(const char *key, const char *value,
 			       const struct config_context *ctx,
 			       void *cb)
 {
-	struct configset_add_data *data = cb;
-	configset_add_value(ctx->kvi, data->config_reader, data->config_set,
-			    key, value);
+	struct config_set *set = cb;
+	configset_add_value(ctx->kvi, set, key, value);
 	return 0;
 }
 
 int git_configset_add_file(struct config_set *set, const char *filename)
 {
-	struct configset_add_data data = CONFIGSET_ADD_INIT;
-	data.config_reader = &the_reader;
-	data.config_set = set;
-	return git_config_from_file(config_set_callback, filename, &data);
+	return git_config_from_file(config_set_callback, filename, set);
 }
 
 int git_configset_get_value(struct config_set *set, const char *key,
@@ -2668,7 +2652,6 @@ int git_configset_get_pathname(struct config_set *set, const char *key, const ch
 static void repo_read_config(struct repository *repo)
 {
 	struct config_options opts = { 0 };
-	struct configset_add_data data = CONFIGSET_ADD_INIT;
 
 	opts.respect_includes = 1;
 	opts.commondir = repo->commondir;
@@ -2680,10 +2663,8 @@ static void repo_read_config(struct repository *repo)
 		git_configset_clear(repo->config);
 
 	git_configset_init(repo->config);
-	data.config_set = repo->config;
-	data.config_reader = &the_reader;
-
-	if (config_with_options(config_set_callback, &data, NULL, &opts) < 0)
+	if (config_with_options(config_set_callback, repo->config, NULL,
+				&opts) < 0)
 		/*
 		 * config_with_options() normally returns only
 		 * zero, as most errors are fatal, and
@@ -2715,7 +2696,7 @@ static void repo_config_clear(struct repository *repo)
 void repo_config(struct repository *repo, config_fn_t fn, void *data)
 {
 	git_config_check_init(repo);
-	configset_iter(&the_reader, repo->config, fn, data);
+	configset_iter(repo->config, fn, data);
 }
 
 int repo_config_get(struct repository *repo, const char *key)
@@ -2822,19 +2803,16 @@ static void read_protected_config(void)
 		.ignore_worktree = 1,
 		.system_gently = 1,
 	};
-	struct configset_add_data data = CONFIGSET_ADD_INIT;
 
 	git_configset_init(&protected_config);
-	data.config_set = &protected_config;
-	data.config_reader = &the_reader;
-	config_with_options(config_set_callback, &data, NULL, &opts);
+	config_with_options(config_set_callback, &protected_config, NULL, &opts);
 }
 
 void git_protected_config(config_fn_t fn, void *data)
 {
 	if (!protected_config.hash_initialized)
 		read_protected_config();
-	configset_iter(&the_reader, &protected_config, fn, data);
+	configset_iter(&protected_config, fn, data);
 }
 
 /* Functions used historically to read configuration from 'the_repository' */
Signature · 1 line
gitgitgadget
#76 lore raw

Re: [PATCH v3 10/12] config.c: remove config_reader from configsets

config.c static int configset_add_value(const struct key_value_info *kvi_p, · line 2433

Quoted 13 lines · Glen Choo via GitGitGadget
config.c +1 -5
@@ -2429,11 +2427,7 @@ static int configset_add_value(const struct key_value_info *kvi_p,
 	l_item->e = e;
 	l_item->value_index = e->value_list.nr - 1;
 
-	if (reader->source->name) {
-		kvi_from_source(reader->source, kvi_p->scope, kv_info);
-	} else {
-		kvi_from_param(kv_info);
-	}
+	memcpy(kv_info, kvi_p, sizeof(struct key_value_info));
 	si->util = kv_info;
 
 	return 0;

Ah, I remember seeing this memcpy from the previous round, but forgot to comment on it (I only commented on another instance, [1]).

Other than that, up to here looks good.

[1] https://lore.kernel.org/git/[email protected]/

#77 lore raw

Re: [PATCH v3 10/12] config.c: remove config_reader from configsets

Quoted 13 lines · Jonathan Tan
Quoted 12 lines · Glen Choo via GitGitGadget
config.c +1 -5
@@ -2429,11 +2427,7 @@ static int configset_add_value(const struct key_value_info *kvi_p,
 	l_item->e = e;
 	l_item->value_index = e->value_list.nr - 1;
 
-	if (reader->source->name) {
-		kvi_from_source(reader->source, kvi_p->scope, kv_info);
-	} else {
-		kvi_from_param(kv_info);
-	}
+	memcpy(kv_info, kvi_p, sizeof(struct key_value_info));
 	si->util = kv_info;
 
Quoted 4 lines

Review static int configset_add_value(const struct key_value_info *kvi_p, · line 2433

Quoted 1 line
(quoted diff) +0 -0
 	return 0;

Ah, I remember seeing this memcpy from the previous round, but forgot to comment on it (I only commented on another instance, [1]).

Yeah, I noticed this and recalled seeing a comment on structure assignment. As both are pointers,

    *kv_info = *kvi_p;

would sufficiently stand out to notify us that we are doing a not-trivial assignment here, and would avoid (slight) risks of typos by type checking.

Quoted 4 lines

Other than that, up to here looks good.

[1] https://lore.kernel.org/git/[email protected]/

#78 lore raw

PATCH v3 11/12 config: add kvi.path, use it to evaluate includes

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Include directives are evaluated using the path of the config file. To reduce the dependence on "config_reader.source", add a new "key_value_info.path" member and use that instead of "config_source.path". This allows us to remove a "struct config_reader *" field from "struct config_include_data", which will subsequently allow us to remove "struct config_reader" entirely.

---
 config.c | 40 +++++++++++++++++++---------------------
 config.h |  2 ++
 2 files changed, 21 insertions(+), 21 deletions(-)
config.c +19 -21
index 958ba166cf9..04c7db6f1be 100644
@@ -161,7 +161,6 @@ struct config_include_data {
 	void *data;
 	const struct config_options *opts;
 	struct git_config_source *config_source;
-	struct config_reader *config_reader;
 
 	/*
 	 * All remote URLs discovered when reading all config files.
@@ -180,8 +179,7 @@ static const char include_depth_advice[] = N_(
 "from\n"
 "	%s\n"
 "This might be due to circular includes.");
-static int handle_path_include(struct config_source *cs,
-			       const struct key_value_info *kvi,
+static int handle_path_include(const struct key_value_info *kvi,
 			       const char *path,
 			       struct config_include_data *inc)
 {
@@ -204,14 +202,14 @@ static int handle_path_include(struct config_source *cs,
 	if (!is_absolute_path(path)) {
 		char *slash;
 
-		if (!cs || !cs->path) {
+		if (!kvi || !kvi->path) {
 			ret = error(_("relative config includes must come from files"));
 			goto cleanup;
 		}
 
-		slash = find_last_dir_sep(cs->path);
+		slash = find_last_dir_sep(kvi->path);
 		if (slash)
-			strbuf_add(&buf, cs->path, slash - cs->path + 1);
+			strbuf_add(&buf, kvi->path, slash - kvi->path + 1);
 		strbuf_addstr(&buf, path);
 		path = buf.buf;
 	}
@@ -219,8 +217,8 @@ static int handle_path_include(struct config_source *cs,
 	if (!access_or_die(path, R_OK, 0)) {
 		if (++inc->depth > MAX_INCLUDE_DEPTH)
 			die(_(include_depth_advice), MAX_INCLUDE_DEPTH, path,
-			    !cs ? "<unknown>" :
-			    cs->name ? cs->name :
+			    !kvi ? "<unknown>" :
+			    kvi->filename ? kvi->filename :
 			    "the command line");
 		ret = git_config_from_file_with_options(git_config_include, path, inc,
 							kvi->scope, NULL);
@@ -238,7 +236,7 @@ static void add_trailing_starstar_for_dir(struct strbuf *pat)
 		strbuf_addstr(pat, "**");
 }
 
-static int prepare_include_condition_pattern(struct config_source *cs,
+static int prepare_include_condition_pattern(const struct key_value_info *kvi,
 					     struct strbuf *pat)
 {
 	struct strbuf path = STRBUF_INIT;
@@ -255,11 +253,11 @@ static int prepare_include_condition_pattern(struct config_source *cs,
 	if (pat->buf[0] == '.' && is_dir_sep(pat->buf[1])) {
 		const char *slash;
 
-		if (!cs || !cs->path)
+		if (!kvi || !kvi->path)
 			return error(_("relative config include "
 				       "conditionals must come from files"));
 
-		strbuf_realpath(&path, cs->path, 1);
+		strbuf_realpath(&path, kvi->path, 1);
 		slash = find_last_dir_sep(path.buf);
 		if (!slash)
 			BUG("how is this possible?");
@@ -274,7 +272,7 @@ static int prepare_include_condition_pattern(struct config_source *cs,
 	return prefix;
 }
 
-static int include_by_gitdir(struct config_source *cs,
+static int include_by_gitdir(const struct key_value_info *kvi,
 			     const struct config_options *opts,
 			     const char *cond, size_t cond_len, int icase)
 {
@@ -291,7 +289,7 @@ static int include_by_gitdir(struct config_source *cs,
 
 	strbuf_realpath(&text, git_dir, 1);
 	strbuf_add(&pattern, cond, cond_len);
-	prefix = prepare_include_condition_pattern(cs, &pattern);
+	prefix = prepare_include_condition_pattern(kvi, &pattern);
 
 again:
 	if (prefix < 0)
@@ -426,16 +424,16 @@ static int include_by_remote_url(struct config_include_data *inc,
 					     inc->remote_urls);
 }
 
-static int include_condition_is_true(struct config_source *cs,
+static int include_condition_is_true(const struct key_value_info *kvi,
 				     struct config_include_data *inc,
 				     const char *cond, size_t cond_len)
 {
 	const struct config_options *opts = inc->opts;
 
 	if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
-		return include_by_gitdir(cs, opts, cond, cond_len, 0);
+		return include_by_gitdir(kvi, opts, cond, cond_len, 0);
 	else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
-		return include_by_gitdir(cs, opts, cond, cond_len, 1);
+		return include_by_gitdir(kvi, opts, cond, cond_len, 1);
 	else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
 		return include_by_branch(cond, cond_len);
 	else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,
@@ -451,7 +449,6 @@ static int git_config_include(const char *var, const char *value,
 			      void *data)
 {
 	struct config_include_data *inc = data;
-	struct config_source *cs = inc->config_reader->source;
 	const char *cond, *key;
 	size_t cond_len;
 	int ret;
@@ -465,16 +462,16 @@ static int git_config_include(const char *var, const char *value,
 		return ret;
 
 	if (!strcmp(var, "include.path"))
-		ret = handle_path_include(cs, ctx->kvi, value, inc);
+		ret = handle_path_include(ctx->kvi, value, inc);
 
 	if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
-	    cond && include_condition_is_true(cs, inc, cond, cond_len) &&
+	    cond && include_condition_is_true(ctx->kvi, inc, cond, cond_len) &&
 	    !strcmp(key, "path")) {
 		config_fn_t old_fn = inc->fn;
 
 		if (inc->opts->unconditional_remote_url)
 			inc->fn = forbid_remote_url;
-		ret = handle_path_include(cs, ctx->kvi, value, inc);
+		ret = handle_path_include(ctx->kvi, value, inc);
 		inc->fn = old_fn;
 	}
 
@@ -659,6 +656,7 @@ void kvi_from_param(struct key_value_info *out)
 	out->linenr = -1;
 	out->origin_type = CONFIG_ORIGIN_CMDLINE;
 	out->scope = CONFIG_SCOPE_COMMAND;
+	out->path = NULL;
 }
 
 int git_config_parse_parameter(const char *text,
@@ -1062,6 +1060,7 @@ static void kvi_from_source(struct config_source *cs,
 	out->origin_type = cs->origin_type;
 	out->linenr = cs->linenr;
 	out->scope = scope;
+	out->path = cs->path;
 }
 
 static int git_parse_source(struct config_source *cs, config_fn_t fn,
@@ -2272,7 +2271,6 @@ int config_with_options(config_fn_t fn, void *data,
 		inc.data = data;
 		inc.opts = opts;
 		inc.config_source = config_source;
-		inc.config_reader = &the_reader;
 		fn = git_config_include;
 		data = &inc;
 	}
config.h +2 -0
index a8f41c59e6c..8841b7a1cf6 100644
@@ -117,12 +117,14 @@ struct key_value_info {
 	int linenr;
 	enum config_origin_type origin_type;
 	enum config_scope scope;
+	const char *path;
 };
 #define KVI_INIT { \
 	.filename = NULL, \
 	.linenr = -1, \
 	.origin_type = CONFIG_ORIGIN_UNKNOWN, \
 	.scope = CONFIG_SCOPE_UNKNOWN, \
+	.path = NULL, \
 }
 
 /* Captures additional information that a config callback can use. */
Signature · 1 line
gitgitgadget
#79 lore raw

PATCH v3 12/12 config: pass source to config_parser_event_fn_t

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

..so that the callback can use a "struct config_source" parameter instead of "config_reader.source". "struct config_source" is internal to config.c, so we are adding a pointer to a struct defined in config.c into a public function signature defined in config.h, but this is okay because this function has only ever been (and probably ever will be) used internally by config.c.

As a result, the_reader isn't used anywhere, so "struct config_reader" is obsolete (it was only intended to be used with the_reader). Remove them.

---
 config.c | 77 ++++++++++----------------------------------------------
 config.h |  6 +++++
 2 files changed, 19 insertions(+), 64 deletions(-)
config.c +13 -64
index 04c7db6f1be..c58e44b15c5 100644
@@ -66,40 +66,6 @@ struct config_source {
 };
 #define CONFIG_SOURCE_INIT { 0 }
 
-struct config_reader {
-	/*
-	 * These members record the "current" config source, which can be
-	 * accessed by parsing callbacks.
-	 *
-	 * The "source" variable will be non-NULL only when we are actually
-	 * parsing a real config source (file, blob, cmdline, etc).
-	 */
-	struct config_source *source;
-};
-/*
- * Where possible, prefer to accept "struct config_reader" as an arg than to use
- * "the_reader". "the_reader" should only be used if that is infeasible, e.g. in
- * a public function.
- */
-static struct config_reader the_reader;
-
-static inline void config_reader_push_source(struct config_reader *reader,
-					     struct config_source *top)
-{
-	top->prev = reader->source;
-	reader->source = top;
-}
-
-static inline struct config_source *config_reader_pop_source(struct config_reader *reader)
-{
-	struct config_source *ret;
-	if (!reader->source)
-		BUG("tried to pop config source, but we weren't reading config");
-	ret = reader->source;
-	reader->source = reader->source->prev;
-	return ret;
-}
-
 static int pack_compression_seen;
 static int zlib_compression_seen;
 
@@ -750,14 +716,9 @@ int git_config_from_parameters(config_fn_t fn, void *data)
 	struct strvec to_free = STRVEC_INIT;
 	int ret = 0;
 	char *envw = NULL;
-	struct config_source source = CONFIG_SOURCE_INIT;
 	struct key_value_info kvi = KVI_INIT;
 
-	source.origin_type = CONFIG_ORIGIN_CMDLINE;
-	config_reader_push_source(&the_reader, &source);
-
 	kvi_from_param(&kvi);
-
 	env = getenv(CONFIG_COUNT_ENVIRONMENT);
 	if (env) {
 		unsigned long count;
@@ -814,7 +775,6 @@ out:
 	strbuf_release(&envvar);
 	strvec_clear(&to_free);
 	free(envw);
-	config_reader_pop_source(&the_reader);
 	return ret;
 }
 
@@ -1043,7 +1003,7 @@ static int do_event(struct config_source *cs, enum config_event_t type,
 
 	if (data->previous_type != CONFIG_EVENT_EOF &&
 	    data->opts->event_fn(data->previous_type, data->previous_offset,
-				 offset, data->opts->event_fn_data) < 0)
+				 offset, cs, data->opts->event_fn_data) < 0)
 		return -1;
 
 	data->previous_type = type;
@@ -2005,8 +1965,7 @@ int git_default_config(const char *var, const char *value,
  * fgetc, ungetc, ftell of top need to be initialized before calling
  * this function.
  */
-static int do_config_from(struct config_reader *reader,
-			  struct config_source *top, config_fn_t fn,
+static int do_config_from(struct config_source *top, config_fn_t fn,
 			  void *data, enum config_scope scope,
 			  const struct config_options *opts)
 {
@@ -2019,21 +1978,17 @@ static int do_config_from(struct config_reader *reader,
 	top->total_len = 0;
 	strbuf_init(&top->value, 1024);
 	strbuf_init(&top->var, 1024);
-	config_reader_push_source(reader, top);
 	kvi_from_source(top, scope, &kvi);
 
 	ret = git_parse_source(top, fn, &kvi, data, opts);
 
-	/* pop config-file parsing state stack */
 	strbuf_release(&top->value);
 	strbuf_release(&top->var);
-	config_reader_pop_source(reader);
 
 	return ret;
 }
 
-static int do_config_from_file(struct config_reader *reader,
-			       config_fn_t fn,
+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, enum config_scope scope,
@@ -2052,7 +2007,7 @@ static int do_config_from_file(struct config_reader *reader,
 	top.do_ftell = config_file_ftell;
 
 	flockfile(f);
-	ret = do_config_from(reader, &top, fn, data, scope, opts);
+	ret = do_config_from(&top, fn, data, scope, opts);
 	funlockfile(f);
 	return ret;
 }
@@ -2060,8 +2015,8 @@ static int do_config_from_file(struct config_reader *reader,
 static int git_config_from_stdin(config_fn_t fn, void *data,
 				 enum config_scope scope)
 {
-	return do_config_from_file(&the_reader, fn, CONFIG_ORIGIN_STDIN, "",
-				   NULL, stdin, data, scope, NULL);
+	return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin,
+				   data, scope, NULL);
 }
 
 int git_config_from_file_with_options(config_fn_t fn, const char *filename,
@@ -2075,9 +2030,8 @@ int git_config_from_file_with_options(config_fn_t fn, const char *filename,
 		BUG("filename cannot be NULL");
 	f = fopen_or_warn(filename, "r");
 	if (f) {
-		ret = do_config_from_file(&the_reader, fn, CONFIG_ORIGIN_FILE,
-					  filename, filename, f, data, scope,
-					  opts);
+		ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename,
+					  filename, f, data, scope, opts);
 		fclose(f);
 	}
 	return ret;
@@ -2108,7 +2062,7 @@ int git_config_from_mem(config_fn_t fn,
 	top.do_ungetc = config_buf_ungetc;
 	top.do_ftell = config_buf_ftell;
 
-	return do_config_from(&the_reader, &top, fn, data, scope, opts);
+	return do_config_from(&top, fn, data, scope, opts);
 }
 
 int git_config_from_blob_oid(config_fn_t fn,
@@ -2201,8 +2155,7 @@ int git_config_system(void)
 	return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
 }
 
-static int do_git_config_sequence(struct config_reader *reader,
-				  const struct config_options *opts,
+static int do_git_config_sequence(const struct config_options *opts,
 				  config_fn_t fn, void *data)
 {
 	int ret = 0;
@@ -2291,7 +2244,7 @@ int config_with_options(config_fn_t fn, void *data,
 		ret = git_config_from_blob_ref(fn, repo, config_source->blob,
 					       data, config_source->scope);
 	} else {
-		ret = do_git_config_sequence(&the_reader, opts, fn, data);
+		ret = do_git_config_sequence(opts, fn, data);
 	}
 
 	if (inc.remote_urls) {
@@ -3000,7 +2953,6 @@ void git_die_config(const char *key, const char *err, ...)
  */
 
 struct config_store_data {
-	struct config_reader *config_reader;
 	size_t baselen;
 	char *key;
 	int do_not_match;
@@ -3046,11 +2998,10 @@ static int matches(const char *key, const char *value,
 		(value && !regexec(store->value_pattern, value, 0, NULL, 0));
 }
 
-static int store_aux_event(enum config_event_t type,
-			   size_t begin, size_t end, void *data)
+static int store_aux_event(enum config_event_t type, size_t begin, size_t end,
+			   struct config_source *cs, void *data)
 {
 	struct config_store_data *store = data;
-	struct config_source *cs = store->config_reader->source;
 
 	ALLOC_GROW(store->parsed, store->parsed_nr + 1, store->parsed_alloc);
 	store->parsed[store->parsed_nr].begin = begin;
@@ -3371,8 +3322,6 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
 	size_t contents_sz;
 	struct config_store_data store = CONFIG_STORE_INIT;
 
-	store.config_reader = &the_reader;
-
 	/* parse-key returns negative; flip the sign to feed exit(3) */
 	ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
 	if (ret)
config.h +6 -0
index 8841b7a1cf6..f663cdd8e1b 100644
@@ -73,6 +73,7 @@ enum config_event_t {
 	CONFIG_EVENT_ERROR
 };
 
+struct config_source;
 /*
  * The parser event function (if not NULL) is called with the event type and
  * the begin/end offsets of the parsed elements.
@@ -82,6 +83,7 @@ enum config_event_t {
  */
 typedef int (*config_parser_event_fn_t)(enum config_event_t type,
 					size_t begin_offset, size_t end_offset,
+					struct config_source *cs,
 					void *event_fn_data);
 
 struct config_options {
@@ -101,6 +103,10 @@ struct config_options {
 
 	const char *commondir;
 	const char *git_dir;
+	/*
+	 * event_fn and event_fn_data are for internal use only. Handles events
+	 * emitted by the config parser.
+	 */
 	config_parser_event_fn_t event_fn;
 	void *event_fn_data;
 	enum config_error_action {
Signature · 1 line
gitgitgadget
#80 lore raw

Re: [PATCH v3 12/12] config: pass source to config_parser_event_fn_t

Quoted 13 lines · Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

..so that the callback can use a "struct config_source" parameter instead of "config_reader.source". "struct config_source" is internal to config.c, so we are adding a pointer to a struct defined in config.c into a public function signature defined in config.h, but this is okay because this function has only ever been (and probably ever will be) used internally by config.c.

As a result, the_reader isn't used anywhere, so "struct config_reader" is obsolete (it was only intended to be used with the_reader). Remove them.

Quoted 5 lines
---
 config.c | 77 ++++++++++----------------------------------------------
 config.h |  6 +++++
 2 files changed, 19 insertions(+), 64 deletions(-)

Together with the removal of current_* functions in earlier steps, getting rid of the_reader interface makes the config API more easily reused in other contexts, I guess. Nicely done.

#81 lore raw
In reply to Glen Choo via GitGitGadget
Quoted 2 lines · Glen Choo via GitGitGadget

Junio: I rebased this onto a newer "master". I assume this is a noop for you - I noticed that the RFC versions weren't applied anyway (good).

It would be a noop only if I do not queue this iteration.

I've reviewed them in its current shape, but it seems to cause too many conflicts even when merged to 'next', let alone 'seen', with interactions with topics in flight:

* ds/add-i-color-configuration-fix (easy)
* ps/fetch-cleanups (easy but messy)
* vd/worktree-config-is-per-repository (moderately messy)

some of which may have graduated to 'master' in the meantime, so it might not be a bad idea to rebase on a more recent 'master' after you collect and adjust for review comments on v3.

Thanks.

#82 lore raw
Quoted 7 lines · Junio C Hamano

I've reviewed them in its current shape, but it seems to cause too many conflicts even when merged to 'next', let alone 'seen', with interactions with topics in flight:

* ds/add-i-color-configuration-fix (easy)
* ps/fetch-cleanups (easy but messy)
* vd/worktree-config-is-per-repository (moderately messy)

Ah, sorry. I ran some trial merges against these before I sent out v3, but I forgot as I sent this out. Not queueing this version sounds fine.

Quoted 3 lines

some of which may have graduated to 'master' in the meantime, so it might not be a bad idea to rebase on a more recent 'master' after you collect and adjust for review comments on v3.

Sounds good. I suppose it would also be worthwhile to base it on conflicting topics queued for 'next'.

#83 lore raw
Quoted 13 lines · Glen Choo
Quoted 7 lines · Junio C Hamano

I've reviewed them in its current shape, but it seems to cause too many conflicts even when merged to 'next', let alone 'seen', with interactions with topics in flight:

* ds/add-i-color-configuration-fix (easy)
* ps/fetch-cleanups (easy but messy)
* vd/worktree-config-is-per-repository (moderately messy)

Ah, sorry. I ran some trial merges against these before I sent out v3, but I forgot as I sent this out. Not queueing this version sounds fine.

Quoted 6 lines
Quoted 3 lines

some of which may have graduated to 'master' in the meantime, so it might not be a bad idea to rebase on a more recent 'master' after you collect and adjust for review comments on v3.

Sounds good. I suppose it would also be worthwhile to base it on conflicting topics queued for 'next'.

Glen, if you can, rebase the patches *before* updating them based on my comments so that it's easier for reviewers to see if my comments have been addressed. (My comments are minor, so they should still be relevant even after rebasing.)

Overall the patch set looks good aside from some minor comments that I've given as replies to the patches.

#84 lore raw
Quoted 21 lines · Jonathan Tan
Quoted 13 lines · Glen Choo
Quoted 7 lines · Junio C Hamano

I've reviewed them in its current shape, but it seems to cause too many conflicts even when merged to 'next', let alone 'seen', with interactions with topics in flight:

* ds/add-i-color-configuration-fix (easy)
* ps/fetch-cleanups (easy but messy)
* vd/worktree-config-is-per-repository (moderately messy)

Ah, sorry. I ran some trial merges against these before I sent out v3, but I forgot as I sent this out. Not queueing this version sounds fine.

Quoted 6 lines
Quoted 3 lines

some of which may have graduated to 'master' in the meantime, so it might not be a bad idea to rebase on a more recent 'master' after you collect and adjust for review comments on v3.

Sounds good. I suppose it would also be worthwhile to base it on conflicting topics queued for 'next'.

Quoted 7 lines

Glen, if you can, rebase the patches *before* updating them based on my comments so that it's easier for reviewers to see if my comments have been addressed. (My comments are minor, so they should still be relevant even after rebasing.)

Overall the patch set looks good aside from some minor comments that I've given as replies to the patches.

Thanks for a quick review (and thank you, Glen, for working on it).

#85 lore raw
In reply to Jonathan Tan
Quoted 7 lines · Jonathan Tan
Quoted 2 lines

Sounds good. I suppose it would also be worthwhile to base it on conflicting topics queued for 'next'.

Glen, if you can, rebase the patches *before* updating them based on my comments so that it's easier for reviewers to see if my comments have been addressed. (My comments are minor, so they should still be relevant even after rebasing.)

Good idea. Fortunately, I already have that prepared, so there's not much extra work for me.

#86 lore raw

v4 cover config: remove global state from config iteration

In reply to Glen Choo via GitGitGadget

Thanks for the careful reread of the previous round and the helpful suggestions. This version is _mostly_ just the previous one rebased onto a newer 'master' with the conflicting topics applied, per Jonathan's and Junio's suggestions, and I plan to address Jonathan's comments in the next version.

Junio: There is now a minor conflict with the bugfix I sent in [1] (the resolution is to just pick this series). It seemed small enough that I didn't think it was worth basing this series on top of that, but let me know if you feel otherwise. If both series happen to go through the same release cycle, we could also drop that one-patch.

I also tested these patches on top of 'seen', and the only conflict I found was with en/header-split-cache-h-part-3, where ll-merge.c got renamed to merge-ll.c, so we need to apply the config refactor there (should work with .cocci).

= Changes since v3

- Rebase onto newer 'master'
- Move the 'remove UNUSED from tr2_cfg_cb' hunk from 9/12 -> 8/12. It should have been there all along; v3 8/12 didn't build at all.

[1] https://lore.kernel.org/git/[email protected]/

Glen Choo (12):
config: inline git_color_default_config urlmatch.h: use config_fn_t type
config: add ctx arg to config_fn_t config.c: pass ctx in configsets
config: pass ctx with config files builtin/config.c: test misuse of format_config() config.c: pass ctx with CLI config
trace2: plumb config kvi
config: pass kvi to die_bad_number() config.c: remove config_reader from configsets
config: add kvi.path, use it to evaluate includes
config: pass source to config_parser_event_fn_t

 alias.c                                       |   3 +-
 archive-tar.c                                 |   5 +-
 archive-zip.c                                 |   1 +
 builtin/add.c                                 |   8 +-
 builtin/blame.c                               |   5 +-
 builtin/branch.c                              |   8 +-
 builtin/cat-file.c                            |   5 +-
 builtin/checkout.c                            |  12 +-
 builtin/clean.c                               |   9 +-
 builtin/clone.c                               |  11 +-
 builtin/column.c                              |   3 +-
 builtin/commit-graph.c                        |   3 +-
 builtin/commit.c                              |  20 +-
 builtin/config.c                              |  72 ++-
 builtin/difftool.c                            |   5 +-
 builtin/fetch.c                               |  13 +-
 builtin/fsmonitor--daemon.c                   |  11 +-
 builtin/grep.c                                |  12 +-
 builtin/help.c                                |   5 +-
 builtin/index-pack.c                          |   9 +-
 builtin/log.c                                 |  12 +-
 builtin/merge.c                               |   7 +-
 builtin/multi-pack-index.c                    |   1 +
 builtin/pack-objects.c                        |  19 +-
 builtin/patch-id.c                            |   5 +-
 builtin/pull.c                                |   5 +-
 builtin/push.c                                |   5 +-
 builtin/read-tree.c                           |   5 +-
 builtin/rebase.c                              |   5 +-
 builtin/receive-pack.c                        |  15 +-
 builtin/reflog.c                              |   7 +-
 builtin/remote.c                              |  15 +-
 builtin/repack.c                              |   5 +-
 builtin/reset.c                               |   5 +-
 builtin/send-pack.c                           |   5 +-
 builtin/show-branch.c                         |   8 +-
 builtin/stash.c                               |   5 +-
 builtin/submodule--helper.c                   |   3 +-
 builtin/tag.c                                 |   9 +-
 builtin/var.c                                 |   5 +-
 builtin/worktree.c                            |   5 +-
 bundle-uri.c                                  |   9 +-
 color.c                                       |   8 -
 color.h                                       |   6 +-
 compat/mingw.c                                |   3 +-
 compat/mingw.h                                |   4 +-
 config.c                                      | 552 +++++++-----------
 config.h                                      |  80 ++-
 connect.c                                     |   4 +-
 .../coccinelle/config_fn_ctx.pending.cocci    | 144 +++++
 contrib/coccinelle/git_config_number.cocci    |  27 +
 convert.c                                     |   4 +-
 credential.c                                  |   1 +
 delta-islands.c                               |   4 +-
 diff.c                                        |  19 +-
 diff.h                                        |   7 +-
 fetch-pack.c                                  |   5 +-
 fmt-merge-msg.c                               |   7 +-
 fmt-merge-msg.h                               |   3 +-
 fsck.c                                        |  12 +-
 fsck.h                                        |   4 +-
 git-compat-util.h                             |   2 +
 gpg-interface.c                               |   7 +-
 grep.c                                        |   7 +-
 grep.h                                        |   4 +-
 help.c                                        |   9 +-
 http.c                                        |  15 +-
 ident.c                                       |   4 +-
 ident.h                                       |   4 +-
 imap-send.c                                   |   7 +-
 ll-merge.c                                    |   1 +
 ls-refs.c                                     |   1 +
 mailinfo.c                                    |   5 +-
 notes-utils.c                                 |   4 +-
 notes.c                                       |   4 +-
 pager.c                                       |   5 +-
 pretty.c                                      |   1 +
 promisor-remote.c                             |   4 +-
 remote.c                                      |   8 +-
 revision.c                                    |   4 +-
 scalar.c                                      |   4 +-
 sequencer.c                                   |  29 +-
 setup.c                                       |  18 +-
 submodule-config.c                            |  31 +-
 submodule-config.h                            |   3 +-
 t/helper/test-config.c                        |  24 +-
 t/helper/test-userdiff.c                      |   4 +-
 t/t1300-config.sh                             |  27 +
 trace2.c                                      |   4 +-
 trace2.h                                      |   3 +-
 trace2/tr2_cfg.c                              |  16 +-
 trace2/tr2_sysenv.c                           |   3 +-
 trace2/tr2_tgt.h                              |   4 +-
 trace2/tr2_tgt_event.c                        |   4 +-
 trace2/tr2_tgt_normal.c                       |   4 +-
 trace2/tr2_tgt_perf.c                         |   4 +-
 trailer.c                                     |   2 +
 upload-pack.c                                 |  18 +-
 urlmatch.c                                    |   7 +-
 urlmatch.h                                    |   8 +-
 worktree.c                                    |   2 +-
 xdiff-interface.c                             |   5 +-
 xdiff-interface.h                             |   4 +-
 103 files changed, 960 insertions(+), 638 deletions(-)
 create mode 100644 contrib/coccinelle/config_fn_ctx.pending.cocci
 create mode 100644 contrib/coccinelle/git_config_number.cocci

base-commit: 6ff334181cfb6485d3ba50843038209a2a253907
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1497%2Fchooglen%2Fconfig%2Fno-global-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1497/chooglen/config/no-global-v4
Pull-Request: https://github.com/git/git/pull/1497

Range-diff vs v3:

1: 26109b65142 ! 1: 7bfffb454c5 config: inline git_color_default_config

     @@ Commit message
      
          Signed-off-by: Glen Choo <[email protected]>
      
     + ## builtin/add.c ##
     +@@ builtin/add.c: static int add_config(const char *var, const char *value, void *cb)
     + 		return 0;
     + 	}
     + 
     +-	return git_color_default_config(var, value, cb);
     ++	if (git_color_config(var, value, cb) < 0)
     ++		return -1;
     ++
     ++	return git_default_config(var, value, cb);
     + }
     + 
     + static const char embedded_advice[] = N_(
     +
       ## builtin/branch.c ##
      @@ builtin/branch.c: static int git_branch_config(const char *var, const char *value, void *cb)
       		return 0;
  2:  1aeffcb1395 =  2:  739c519ce62 urlmatch.h: use config_fn_t type
  3:  d3eb439aad2 !  3:  a9a0a50f32a config: add ctx arg to config_fn_t
     @@ builtin/add.c: static struct option builtin_add_options[] = {
       	if (!strcmp(var, "add.ignoreerrors") ||
       	    !strcmp(var, "add.ignore-errors")) {
      @@ builtin/add.c: static int add_config(const char *var, const char *value, void *cb)
     - 		return 0;
     - 	}
     + 	if (git_color_config(var, value, cb) < 0)
     + 		return -1;
       
      -	return git_default_config(var, value, cb);
      +	return git_default_config(var, value, ctx, cb);
     @@ builtin/difftool.c: static const char *const builtin_difftool_usage[] = {
      
       ## builtin/fetch.c ##
      @@ builtin/fetch.c: struct fetch_config {
     - 	enum display_format display_format;
     + 	int submodule_fetch_jobs;
       };
       
      -static int git_fetch_config(const char *k, const char *v, void *cb)
  4:  c5051ddc10d =  4:  39b2e291f86 config.c: pass ctx in configsets
  5:  595e7d2e163 !  5:  bfc6d2833c5 config: pass ctx with config files
     @@ config.c: static int do_git_config_sequence(struct config_reader *reader,
      +							 CONFIG_SCOPE_LOCAL, NULL);
       
       	config_reader_set_scope(reader, CONFIG_SCOPE_WORKTREE);
     - 	if (!opts->ignore_worktree && repository_format_worktree_config) {
     - 		char *path = git_pathdup("config.worktree");
     - 		if (!access_or_die(path, R_OK, 0))
     --			ret += git_config_from_file(fn, path, data);
     -+			ret += git_config_from_file_with_options(fn, path, data,
     + 	if (!opts->ignore_worktree && worktree_config &&
     + 	    repo && repo->repository_format_worktree_config &&
     + 	    !access_or_die(worktree_config, R_OK, 0)) {
     +-		ret += git_config_from_file(fn, worktree_config, data);
     ++			ret += git_config_from_file_with_options(fn, worktree_config, data,
      +								 CONFIG_SCOPE_WORKTREE,
      +								 NULL);
     - 		free(path);
       	}
       
     + 	config_reader_set_scope(reader, CONFIG_SCOPE_COMMAND);
      @@ config.c: int config_with_options(config_fn_t fn, void *data,
       	 * regular lookup sequence.
       	 */
     @@ config.c: int config_with_options(config_fn_t fn, void *data,
      +							data, config_source->scope,
      +							NULL);
       	} else if (config_source && config_source->blob) {
     - 		struct repository *repo = config_source->repo ?
     - 			config_source->repo : the_repository;
       		ret = git_config_from_blob_ref(fn, repo, config_source->blob,
      -						data);
      +					       data, config_source->scope);
       	} else {
     - 		ret = do_git_config_sequence(&the_reader, opts, fn, data);
     + 		ret = do_git_config_sequence(&the_reader, opts, repo, fn, data);
       	}
      @@ config.c: static int configset_add_value(struct config_reader *reader,
       	if (!reader->source)
  6:  a2a891a069f =  6:  897bdc759b5 builtin/config.c: test misuse of format_config()
  7:  1fb1708bbd9 !  7:  33e4437737d config.c: pass ctx with CLI config

@@ builtin/config.c: static int collect_config(const char *key_, const char *value_

static int get_value(const char *key_, const char *regex_, unsigned flags)
@@ builtin/config.c: static int get_value(const char *key_, const char *regex_, unsigned flags)
- &given_config_source, &config_options);
+ &config_options);

if (!values.nr && default_value) {
+ struct key_value_info kvi = KVI_INIT;
8: 66572df7beb ! 8: 9bd5f60282c trace2: plumb config kvi
@@ config.c: static void populate_remote_urls(struct config_include_data *inc)
-
inc->remote_urls = xmalloc(sizeof(*inc->remote_urls)); string_list_init_dup(inc->remote_urls);
- config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
+ config_with_options(add_remote_url, inc->remote_urls,
+ inc->config_source, inc->repo, &opts);
-
- config_reader_set_scope(inc->config_reader, store_scope);
}

static int forbid_remote_url(const char *var, const char *value UNUSED,
@@ config.c: static int do_git_config_sequence(struct config_reader *reader,
- char *xdg_config = NULL;
char *user_config = NULL;
char *repo_config;
+ char *worktree_config;
- enum config_scope prev_parsing_scope = reader->parsing_scope;

- if (opts->commondir)
- repo_config = mkpathdup("%s/config", opts->commondir);
+ /*
+ * Ensure that either:
@@ config.c: static int do_git_config_sequence(struct config_reader *reader,
- else
- repo_config = NULL;
+ worktree_config = NULL;
+ }

- config_reader_set_scope(reader, CONFIG_SCOPE_SYSTEM);
if (git_config_system() && system_config &&
@@ config.c: static int do_git_config_sequence(struct config_reader *reader,
CONFIG_SCOPE_LOCAL, NULL);

- config_reader_set_scope(reader, CONFIG_SCOPE_WORKTREE);
- if (!opts->ignore_worktree && repository_format_worktree_config) {
- char *path = git_pathdup("config.worktree");
- if (!access_or_die(path, R_OK, 0))
+ if (!opts->ignore_worktree && worktree_config &&
+ repo && repo->repository_format_worktree_config &&
+ !access_or_die(worktree_config, R_OK, 0)) {
@@ config.c: static int do_git_config_sequence(struct config_reader *reader,
- free(path);
+ NULL);
}

- config_reader_set_scope(reader, CONFIG_SCOPE_COMMAND);
@@ trace2.h: void trace2_thread_exit_fl(const char *file, int line);
trace2_def_param_fl(__FILE__, __LINE__, (param), (value))

## trace2/tr2_cfg.c ##
+@@ trace2/tr2_cfg.c: struct tr2_cfg_data {
+ * See if the given config key matches any of our patterns of interest.
+ */
+ static int tr2_cfg_cb(const char *key, const char *value,
+- const struct config_context *ctx UNUSED, void *d)
++ const struct config_context *ctx, void *d)
+ {
+ struct strbuf **s;
+ struct tr2_cfg_data *data = (struct tr2_cfg_data *)d;
@@ trace2/tr2_cfg.c: static int tr2_cfg_cb(const char *key, const char *value,
struct strbuf *buf = *s;
int wm = wildmatch(buf->buf, key, WM_CASEFOLD);
9: 123e19dda4a ! 9: 114723ee4a7 config: pass kvi to die_bad_number()
@@ builtin/fetch.c: static int git_fetch_config(const char *k, const char *v,
}

if (!strcmp(k, "submodule.fetchjobs")) {
-- submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v);
-+ submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v, ctx->kvi);
+- fetch_config->submodule_fetch_jobs = parse_submodule_fetchjobs(k, v);
++ fetch_config->submodule_fetch_jobs = parse_submodule_fetchjobs(k, v, ctx->kvi);
return 0;
} else if (!strcmp(k, "fetch.recursesubmodules")) {
- recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
+ fetch_config->recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
@@ builtin/fetch.c: static int git_fetch_config(const char *k, const char *v,
}

if (!strcmp(k, "fetch.parallel")) {
-- fetch_parallel_config = git_config_int(k, v);
-+ fetch_parallel_config = git_config_int(k, v, ctx->kvi);
- if (fetch_parallel_config < 0)
+- fetch_config->parallel = git_config_int(k, v);
++ fetch_config->parallel = git_config_int(k, v, ctx->kvi);
+ if (fetch_config->parallel < 0)
die(_("fetch.parallel cannot be negative"));
- if (!fetch_parallel_config)
+ if (!fetch_config->parallel)

## builtin/fsmonitor--daemon.c ##
@@ builtin/fsmonitor--daemon.c: static int fsmonitor_config(const char *var, const char *value,
@@ t/helper/test-config.c: int cmd__config(int argc, const char **argv)
printf("(NULL)\n");
else

- ## trace2/tr2_cfg.c ##
-@@ trace2/tr2_cfg.c: struct tr2_cfg_data {
- * See if the given config key matches any of our patterns of interest.
- */
- static int tr2_cfg_cb(const char *key, const char *value,
-- const struct config_context *ctx UNUSED, void *d)
-+ const struct config_context *ctx, void *d)
- {
- struct strbuf **s;
- struct tr2_cfg_data *data = (struct tr2_cfg_data *)d;
-
## upload-pack.c ##
@@ upload-pack.c: static int find_symref(const char *refname,
}
10: 8ec24b018e9 ! 10: 807057b6d7f config.c: remove config_reader from configsets

     @@ config.c: static void repo_read_config(struct repository *repo)
      -	data.config_set = repo->config;
      -	data.config_reader = &the_reader;
      -
     --	if (config_with_options(config_set_callback, &data, NULL, &opts) < 0)
     +-	if (config_with_options(config_set_callback, &data, NULL, repo, &opts) < 0)
      +	if (config_with_options(config_set_callback, repo->config, NULL,
     -+				&opts) < 0)
     ++				repo, &opts) < 0)
       		/*
       		 * config_with_options() normally returns only
       		 * zero, as most errors are fatal, and
     @@ config.c: static void read_protected_config(void)
       	git_configset_init(&protected_config);
      -	data.config_set = &protected_config;
      -	data.config_reader = &the_reader;
     --	config_with_options(config_set_callback, &data, NULL, &opts);
     -+	config_with_options(config_set_callback, &protected_config, NULL, &opts);
     +-	config_with_options(config_set_callback, &data, NULL, NULL, &opts);
     ++	config_with_options(config_set_callback, &protected_config, NULL,
     ++			    NULL, &opts);
       }
       
       void git_protected_config(config_fn_t fn, void *data)
 11:  8ae115cff88 ! 11:  3f0f84df972 config: add kvi.path, use it to evaluate includes
     @@ Commit message
      
       ## config.c ##
      @@ config.c: struct config_include_data {
     - 	void *data;
       	const struct config_options *opts;
       	struct git_config_source *config_source;
     + 	struct repository *repo;
      -	struct config_reader *config_reader;
       
       	/*
     @@ config.c: static void kvi_from_source(struct config_source *cs,
       
       static int git_parse_source(struct config_source *cs, config_fn_t fn,
      @@ config.c: int config_with_options(config_fn_t fn, void *data,
     - 		inc.data = data;
       		inc.opts = opts;
     + 		inc.repo = repo;
       		inc.config_source = config_source;
      -		inc.config_reader = &the_reader;
       		fn = git_config_include;
 12:  484d553cc7d ! 12:  fe2f154fe8b config: pass source to config_parser_event_fn_t
     @@ config.c: int git_config_system(void)
      -static int do_git_config_sequence(struct config_reader *reader,
      -				  const struct config_options *opts,
      +static int do_git_config_sequence(const struct config_options *opts,
     + 				  const struct repository *repo,
       				  config_fn_t fn, void *data)
       {
     - 	int ret = 0;
      @@ config.c: int config_with_options(config_fn_t fn, void *data,
       		ret = git_config_from_blob_ref(fn, repo, config_source->blob,
       					       data, config_source->scope);
       	} else {
     --		ret = do_git_config_sequence(&the_reader, opts, fn, data);
     -+		ret = do_git_config_sequence(opts, fn, data);
     +-		ret = do_git_config_sequence(&the_reader, opts, repo, fn, data);
     ++		ret = do_git_config_sequence(opts, repo, fn, data);
       	}
       
       	if (inc.remote_urls) {
Signature · 1 line
gitgitgadget
#87 lore raw

PATCH v4 1/12 config: inline git_color_default_config

From: Glen Choo <[email protected]>

git_color_default_config() is a shorthand for calling two other config callbacks. There are no other non-static functions that do this and it will complicate our refactoring of config_fn_t so inline it instead.

---
 builtin/add.c         | 5 ++++-
 builtin/branch.c      | 5 ++++-
 builtin/clean.c       | 6 ++++--
 builtin/grep.c        | 5 ++++-
 builtin/show-branch.c | 5 ++++-
 builtin/tag.c         | 6 +++++-
 color.c               | 8 --------
 color.h               | 6 +-----
 8 files changed, 26 insertions(+), 20 deletions(-)
builtin/add.c +4 -1
index 6137e7b4ad7..e01efdfc50d 100644
@@ -365,7 +365,10 @@ static int add_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_color_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
 }
 
 static const char embedded_advice[] = N_(
builtin/branch.c +4 -1
index 075e580d224..8337b9e71bb 100644
@@ -117,7 +117,10 @@ static int git_branch_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_color_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
 }
 
 static const char *branch_get_color(enum color_branch ix)
builtin/clean.c +4 -2
index 78852d28cec..57e7f7cac64 100644
@@ -130,8 +130,10 @@ static int git_clean_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	/* inspect the color.ui config variable and others */
-	return git_color_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
 }
 
 static const char *clean_get_color(enum color_clean ix)
builtin/grep.c +4 -1
index b86c754defb..76cf999d310 100644
@@ -293,7 +293,10 @@ static int wait_all(void)
 static int grep_cmd_config(const char *var, const char *value, void *cb)
 {
 	int st = grep_config(var, value, cb);
-	if (git_color_default_config(var, value, NULL) < 0)
+
+	if (git_color_config(var, value, cb) < 0)
+		st = -1;
+	else if (git_default_config(var, value, cb) < 0)
 		st = -1;
 
 	if (!strcmp(var, "grep.threads")) {
builtin/show-branch.c +4 -1
index 7ef4a642c17..a2461270d4b 100644
@@ -579,7 +579,10 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_color_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
 }
 
 static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
builtin/tag.c +5 -1
index 49b64c7a288..1acf5f7a59f 100644
@@ -209,7 +209,11 @@ static int git_tag_config(const char *var, const char *value, void *cb)
 
 	if (starts_with(var, "column."))
 		return git_column_config(var, value, "tag", &colopts);
-	return git_color_default_config(var, value, cb);
+
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, cb);
 }
 
 static void write_tag_body(int fd, const struct object_id *oid)
color.c +0 -8
index 83abb11eda0..b24b19566b9 100644
@@ -430,14 +430,6 @@ int git_color_config(const char *var, const char *value, void *cb UNUSED)
 	return 0;
 }
 
-int git_color_default_config(const char *var, const char *value, void *cb)
-{
-	if (git_color_config(var, value, cb) < 0)
-		return -1;
-
-	return git_default_config(var, value, cb);
-}
-
 void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb)
 {
 	if (*color)
color.h +1 -5
index cfc8f841b23..bb28343be21 100644
@@ -88,12 +88,8 @@ extern const int column_colors_ansi_max;
  */
 extern int color_stdout_is_tty;
 
-/*
- * Use the first one if you need only color config; the second is a convenience
- * if you are just going to change to git_default_config, too.
- */
+/* Parse color config. */
 int git_color_config(const char *var, const char *value, void *cb);
-int git_color_default_config(const char *var, const char *value, void *cb);
 
 /*
  * Parse a config option, which can be a boolean or one of
Signature · 1 line
gitgitgadget
#88 lore raw

PATCH v4 2/12 urlmatch.h: use config_fn_t type

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

These are actually used as config callbacks, so use the typedef-ed type and make future refactors easier.

---
 urlmatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
urlmatch.h +3 -2
index 9f40b00bfb8..bee374a642c 100644
@@ -2,6 +2,7 @@
 #define URL_MATCH_H
 
 #include "string-list.h"
+#include "config.h"
 
 struct url_info {
 	/* normalized url on success, must be freed, otherwise NULL */
@@ -48,8 +49,8 @@ struct urlmatch_config {
 	const char *key;
 
 	void *cb;
-	int (*collect_fn)(const char *var, const char *value, void *cb);
-	int (*cascade_fn)(const char *var, const char *value, void *cb);
+	config_fn_t collect_fn;
+	config_fn_t cascade_fn;
 	/*
 	 * Compare the two matches, the one just discovered and the existing
 	 * best match and return a negative value if the found item is to be
Signature · 1 line
gitgitgadget
#89 lore raw

PATCH v4 3/12 config: add ctx arg to config_fn_t

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Add a new "const struct config_context *ctx" arg to config_fn_t to hold additional information about the config iteration operation. config_context has a "struct key_value_info kvi" member that holds metadata about the config source being read (e.g. what kind of config source it is, the filename, etc). In this series, we're only interested in .kvi, so we could have just used "struct key_value_info" as an arg, but config_context makes it possible to add/adjust members in the future without changing the config_fn_t signature. We could also consider other ways of organizing the args (e.g. moving the config name and value into config_context or key_value_info), but in my experiments, the incremental benefit doesn't justify the added complexity (e.g. a config_fn_t will sometimes invoke another config_fn_t but with a different config value).

In subsequent commits, the .kvi member will replace the global "struct config_reader" in config.c, making config iteration a global-free operation. It requires much more work for the machinery to provide meaningful values of .kvi, so for now, merely change the signature and call sites, pass NULL as a placeholder value, and don't rely on the arg in any meaningful way.

Most of the changes are performed by contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every config_fn_t:

- Modifies the signature to accept "const struct config_context *ctx"
- Passes "ctx" to any inner config_fn_t, if needed
- Adds UNUSED attributes to "ctx", if needed

Most config_fn_t instances are easily identified by seeing if they are called by the various config functions. Most of the remaining ones are manually named in the .cocci patch. Manual cleanups are still needed, but the majority of it is trivial; it's either adjusting config_fn_t that the .cocci patch didn't catch, or adding forward declarations of "struct config_context ctx" to make the signatures make sense.

The non-trivial changes are in cases where we are invoking a config_fn_t outside of config machinery, and we now need to decide what value of "ctx" to pass. These cases are:

- trace2/tr2_cfg.c:tr2_cfg_set_fl()

This is indirectly called by git_config_set() so that the trace2 machinery can notice the new config values and update its settings using the tr2 config parsing function, i.e. tr2_cfg_cb().

- builtin/checkout.c:checkout_main()

This calls git_xmerge_config() as a shorthand for parsing a CLI arg. This might be worth refactoring away in the future, since git_xmerge_config() can call git_default_config(), which can do much more than just parsing.

Handle them by creating a KVI_INIT macro that initializes "struct key_value_info" to a reasonable default, and use that to construct the "ctx" arg.

---
 alias.c                                       |   3 +-
 archive-tar.c                                 |   3 +-
 archive-zip.c                                 |   1 +
 builtin/add.c                                 |   5 +-
 builtin/blame.c                               |   5 +-
 builtin/branch.c                              |   5 +-
 builtin/cat-file.c                            |   5 +-
 builtin/checkout.c                            |  12 +-
 builtin/clean.c                               |   5 +-
 builtin/clone.c                               |  11 +-
 builtin/column.c                              |   3 +-
 builtin/commit-graph.c                        |   1 +
 builtin/commit.c                              |  10 +-
 builtin/config.c                              |  10 +-
 builtin/difftool.c                            |   5 +-
 builtin/fetch.c                               |   9 +-
 builtin/fsmonitor--daemon.c                   |   5 +-
 builtin/grep.c                                |   7 +-
 builtin/help.c                                |   5 +-
 builtin/index-pack.c                          |   5 +-
 builtin/log.c                                 |  10 +-
 builtin/merge.c                               |   7 +-
 builtin/multi-pack-index.c                    |   1 +
 builtin/pack-objects.c                        |   5 +-
 builtin/patch-id.c                            |   5 +-
 builtin/pull.c                                |   5 +-
 builtin/push.c                                |   5 +-
 builtin/read-tree.c                           |   5 +-
 builtin/rebase.c                              |   5 +-
 builtin/receive-pack.c                        |   5 +-
 builtin/reflog.c                              |   7 +-
 builtin/remote.c                              |   7 +-
 builtin/repack.c                              |   5 +-
 builtin/reset.c                               |   5 +-
 builtin/send-pack.c                           |   5 +-
 builtin/show-branch.c                         |   5 +-
 builtin/stash.c                               |   5 +-
 builtin/submodule--helper.c                   |   1 +
 builtin/tag.c                                 |   5 +-
 builtin/var.c                                 |   5 +-
 builtin/worktree.c                            |   5 +-
 bundle-uri.c                                  |   8 +-
 compat/mingw.c                                |   3 +-
 compat/mingw.h                                |   4 +-
 config.c                                      |  38 +++--
 config.h                                      |  41 +++--
 connect.c                                     |   4 +-
 .../coccinelle/config_fn_ctx.pending.cocci    | 144 ++++++++++++++++++
 convert.c                                     |   4 +-
 credential.c                                  |   1 +
 delta-islands.c                               |   4 +-
 diff.c                                        |  10 +-
 diff.h                                        |   7 +-
 fetch-pack.c                                  |   5 +-
 fmt-merge-msg.c                               |   5 +-
 fmt-merge-msg.h                               |   3 +-
 fsck.c                                        |   9 +-
 fsck.h                                        |   4 +-
 git-compat-util.h                             |   2 +
 gpg-interface.c                               |   7 +-
 grep.c                                        |   7 +-
 grep.h                                        |   4 +-
 help.c                                        |   7 +-
 http.c                                        |   5 +-
 ident.c                                       |   4 +-
 ident.h                                       |   4 +-
 imap-send.c                                   |   5 +-
 ll-merge.c                                    |   1 +
 ls-refs.c                                     |   1 +
 mailinfo.c                                    |   5 +-
 notes-utils.c                                 |   4 +-
 notes.c                                       |   4 +-
 pager.c                                       |   5 +-
 pretty.c                                      |   1 +
 promisor-remote.c                             |   4 +-
 remote.c                                      |   3 +-
 revision.c                                    |   4 +-
 scalar.c                                      |   4 +-
 sequencer.c                                   |   9 +-
 setup.c                                       |  16 +-
 submodule-config.c                            |  17 ++-
 t/helper/test-config.c                        |  11 +-
 t/helper/test-userdiff.c                      |   4 +-
 trace2/tr2_cfg.c                              |   9 +-
 trace2/tr2_sysenv.c                           |   3 +-
 trailer.c                                     |   2 +
 upload-pack.c                                 |   8 +-
 urlmatch.c                                    |   7 +-
 urlmatch.h                                    |   3 +-
 xdiff-interface.c                             |   5 +-
 xdiff-interface.h                             |   4 +-
 91 files changed, 515 insertions(+), 181 deletions(-)
 create mode 100644 contrib/coccinelle/config_fn_ctx.pending.cocci
alias.c +2 -1
index 54a1a23d2cf..910dd252a01 100644
@@ -12,7 +12,8 @@ struct config_alias_data {
 	struct string_list *list;
 };
 
-static int config_alias_cb(const char *key, const char *value, void *d)
+static int config_alias_cb(const char *key, const char *value,
+			   const struct config_context *ctx UNUSED, void *d)
 {
 	struct config_alias_data *data = d;
 	const char *p;
archive-tar.c +2 -1
index 4cd81d8161e..ef06e516b1f 100644
@@ -411,7 +411,8 @@ static int tar_filter_config(const char *var, const char *value,
 	return 0;
 }
 
-static int git_tar_config(const char *var, const char *value, void *cb)
+static int git_tar_config(const char *var, const char *value,
+			  const struct config_context *ctx UNUSED, void *cb)
 {
 	if (!strcmp(var, "tar.umask")) {
 		if (value && !strcmp(value, "user")) {
archive-zip.c +1 -0
index d0d065a312e..b6811951955 100644
@@ -617,6 +617,7 @@ static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
 }
 
 static int archive_zip_config(const char *var, const char *value,
+			      const struct config_context *ctx UNUSED,
 			      void *data UNUSED)
 {
 	return userdiff_config(var, value);
builtin/add.c +3 -2
index e01efdfc50d..1009ae13c13 100644
@@ -357,7 +357,8 @@ static struct option builtin_add_options[] = {
 	OPT_END(),
 };
 
-static int add_config(const char *var, const char *value, void *cb)
+static int add_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "add.ignoreerrors") ||
 	    !strcmp(var, "add.ignore-errors")) {
@@ -368,7 +369,7 @@ static int add_config(const char *var, const char *value, void *cb)
 	if (git_color_config(var, value, cb) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static const char embedded_advice[] = N_(
builtin/blame.c +3 -2
index 2df6039a6e0..d0970a1ab13 100644
@@ -694,7 +694,8 @@ static const char *add_prefix(const char *prefix, const char *path)
 	return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
 }
 
-static int git_blame_config(const char *var, const char *value, void *cb)
+static int git_blame_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "blame.showroot")) {
 		show_root = git_config_bool(var, value);
@@ -767,7 +768,7 @@ static int git_blame_config(const char *var, const char *value, void *cb)
 	if (userdiff_config(var, value) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int blame_copy_callback(const struct option *option, const char *arg, int unset)
builtin/branch.c +3 -2
index 8337b9e71bb..af6d2e75fb0 100644
@@ -83,7 +83,8 @@ static unsigned int colopts;
 
 define_list_config_array(color_branch_slots);
 
-static int git_branch_config(const char *var, const char *value, void *cb)
+static int git_branch_config(const char *var, const char *value,
+			     const struct config_context *ctx, void *cb)
 {
 	const char *slot_name;
 
@@ -120,7 +121,7 @@ static int git_branch_config(const char *var, const char *value, void *cb)
 	if (git_color_config(var, value, cb) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static const char *branch_get_color(enum color_branch ix)
builtin/cat-file.c +3 -2
index 7ff56d5a781..b3c41b54c8d 100644
@@ -873,12 +873,13 @@ static int batch_objects(struct batch_options *opt)
 	return retval;
 }
 
-static int git_cat_file_config(const char *var, const char *value, void *cb)
+static int git_cat_file_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	if (userdiff_config(var, value) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int batch_option_callback(const struct option *opt,
builtin/checkout.c +9 -3
index 715eeb5048f..4e1f7dc26c2 100644
@@ -1186,7 +1186,8 @@ static int switch_branches(const struct checkout_opts *opts,
 	return ret || writeout_error;
 }
 
-static int git_checkout_config(const char *var, const char *value, void *cb)
+static int git_checkout_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	struct checkout_opts *opts = cb;
 
@@ -1202,7 +1203,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
 	if (starts_with(var, "submodule."))
 		return git_default_submodule_config(var, value, NULL);
 
-	return git_xmerge_config(var, value, NULL);
+	return git_xmerge_config(var, value, ctx, NULL);
 }
 
 static void setup_new_branch_info_and_source_tree(
@@ -1689,8 +1690,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 	}
 
 	if (opts->conflict_style) {
+		struct key_value_info kvi = KVI_INIT;
+		struct config_context ctx = {
+			.kvi = &kvi,
+		};
 		opts->merge = 1; /* implied */
-		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
+		git_xmerge_config("merge.conflictstyle", opts->conflict_style,
+				  &ctx, NULL);
 	}
 	if (opts->force) {
 		opts->discard_changes = 1;
builtin/clean.c +3 -2
index 57e7f7cac64..5eff1b802a7 100644
@@ -103,7 +103,8 @@ struct menu_stuff {
 
 define_list_config_array(color_interactive_slots);
 
-static int git_clean_config(const char *var, const char *value, void *cb)
+static int git_clean_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	const char *slot_name;
 
@@ -133,7 +134,7 @@ static int git_clean_config(const char *var, const char *value, void *cb)
 	if (git_color_config(var, value, cb) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static const char *clean_get_color(enum color_clean ix)
builtin/clone.c +7 -4
index 15f9912b4ca..c0f6e067493 100644
@@ -790,7 +790,8 @@ static int checkout(int submodule_progress, int filter_submodules)
 	return err;
 }
 
-static int git_clone_config(const char *k, const char *v, void *cb)
+static int git_clone_config(const char *k, const char *v,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(k, "clone.defaultremotename")) {
 		free(remote_name);
@@ -801,17 +802,19 @@ static int git_clone_config(const char *k, const char *v, void *cb)
 	if (!strcmp(k, "clone.filtersubmodules"))
 		config_filter_submodules = git_config_bool(k, v);
 
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
-static int write_one_config(const char *key, const char *value, void *data)
+static int write_one_config(const char *key, const char *value,
+			    const struct config_context *ctx,
+			    void *data)
 {
 	/*
 	 * give git_clone_config a chance to write config values back to the
 	 * environment, since git_config_set_multivar_gently only deals with
 	 * config-file writes
 	 */
-	int apply_failed = git_clone_config(key, value, data);
+	int apply_failed = git_clone_config(key, value, ctx, data);
 	if (apply_failed)
 		return apply_failed;
 
builtin/column.c +2 -1
index de623a16c2d..4a6148ca479 100644
@@ -13,7 +13,8 @@ static const char * const builtin_column_usage[] = {
 };
 static unsigned int colopts;
 
-static int column_config(const char *var, const char *value, void *cb)
+static int column_config(const char *var, const char *value,
+			 const struct config_context *ctx UNUSED, void *cb)
 {
 	return git_column_config(var, value, cb, &colopts);
 }
builtin/commit-graph.c +1 -0
index dd732b35348..1185c49239a 100644
@@ -186,6 +186,7 @@ static int write_option_max_new_filters(const struct option *opt,
 }
 
 static int git_commit_graph_write_config(const char *var, const char *value,
+					 const struct config_context *ctx UNUSED,
 					 void *cb UNUSED)
 {
 	if (!strcmp(var, "commitgraph.maxnewfilters"))
builtin/commit.c +6 -4
index 9ab57ea1aaa..6a2b2503328 100644
@@ -1405,7 +1405,8 @@ static int parse_status_slot(const char *slot)
 	return LOOKUP_CONFIG(color_status_slots, slot);
 }
 
-static int git_status_config(const char *k, const char *v, void *cb)
+static int git_status_config(const char *k, const char *v,
+			     const struct config_context *ctx, void *cb)
 {
 	struct wt_status *s = cb;
 	const char *slot_name;
@@ -1490,7 +1491,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
 		s->detect_rename = git_config_rename(k, v);
 		return 0;
 	}
-	return git_diff_ui_config(k, v, NULL);
+	return git_diff_ui_config(k, v, ctx, NULL);
 }
 
 int cmd_status(int argc, const char **argv, const char *prefix)
@@ -1605,7 +1606,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static int git_commit_config(const char *k, const char *v, void *cb)
+static int git_commit_config(const char *k, const char *v,
+			     const struct config_context *ctx, void *cb)
 {
 	struct wt_status *s = cb;
 
@@ -1627,7 +1629,7 @@ static int git_commit_config(const char *k, const char *v, void *cb)
 		return 0;
 	}
 
-	return git_status_config(k, v, s);
+	return git_status_config(k, v, ctx, s);
 }
 
 int cmd_commit(int argc, const char **argv, const char *prefix)
builtin/config.c +8 -2
index d40fddb042a..f4fccf99cb8 100644
@@ -217,6 +217,7 @@ static void show_config_scope(struct strbuf *buf)
 }
 
 static int show_all_config(const char *key_, const char *value_,
+			   const struct config_context *ctx UNUSED,
 			   void *cb UNUSED)
 {
 	if (show_origin || show_scope) {
@@ -301,7 +302,8 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
 	return 0;
 }
 
-static int collect_config(const char *key_, const char *value_, void *cb)
+static int collect_config(const char *key_, const char *value_,
+			  const struct config_context *ctx UNUSED, void *cb)
 {
 	struct strbuf_list *values = cb;
 
@@ -470,6 +472,7 @@ static const char *get_colorbool_slot;
 static char parsed_color[COLOR_MAXLEN];
 
 static int git_get_color_config(const char *var, const char *value,
+				const struct config_context *ctx UNUSED,
 				void *cb UNUSED)
 {
 	if (!strcmp(var, get_color_slot)) {
@@ -503,6 +506,7 @@ static int get_colorbool_found;
 static int get_diff_color_found;
 static int get_color_ui_found;
 static int git_get_colorbool_config(const char *var, const char *value,
+				    const struct config_context *ctx UNUSED,
 				    void *data UNUSED)
 {
 	if (!strcmp(var, get_colorbool_slot))
@@ -561,7 +565,9 @@ struct urlmatch_current_candidate_value {
 	struct strbuf value;
 };
 
-static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
+static int urlmatch_collect_fn(const char *var, const char *value,
+			       const struct config_context *ctx UNUSED,
+			       void *cb)
 {
 	struct string_list *values = cb;
 	struct string_list_item *item = string_list_insert(values, var);
builtin/difftool.c +3 -2
index 0049342f5c0..f289530068b 100644
@@ -40,14 +40,15 @@ static const char *const builtin_difftool_usage[] = {
 	NULL
 };
 
-static int difftool_config(const char *var, const char *value, void *cb)
+static int difftool_config(const char *var, const char *value,
+			   const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "difftool.trustexitcode")) {
 		trust_exit_code = git_config_bool(var, value);
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int print_tool_help(void)
builtin/fetch.c +6 -3
index e3871048cf6..a4aa0fbb8f5 100644
@@ -110,7 +110,8 @@ struct fetch_config {
 	int submodule_fetch_jobs;
 };
 
-static int git_fetch_config(const char *k, const char *v, void *cb)
+static int git_fetch_config(const char *k, const char *v,
+			    const struct config_context *ctx, void *cb)
 {
 	struct fetch_config *fetch_config = cb;
 
@@ -164,7 +165,7 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
 			    "fetch.output", v);
 	}
 
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
 static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
@@ -1799,7 +1800,9 @@ struct remote_group_data {
 	struct string_list *list;
 };
 
-static int get_remote_group(const char *key, const char *value, void *priv)
+static int get_remote_group(const char *key, const char *value,
+			    const struct config_context *ctx UNUSED,
+			    void *priv)
 {
 	struct remote_group_data *g = priv;
 
builtin/fsmonitor--daemon.c +3 -2
index f6dd9a784c1..91a776e2f17 100644
@@ -37,7 +37,8 @@ static int fsmonitor__start_timeout_sec = 60;
 #define FSMONITOR__ANNOUNCE_STARTUP "fsmonitor.announcestartup"
 static int fsmonitor__announce_startup = 0;
 
-static int fsmonitor_config(const char *var, const char *value, void *cb)
+static int fsmonitor_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, FSMONITOR__IPC_THREADS)) {
 		int i = git_config_int(var, value);
@@ -67,7 +68,7 @@ static int fsmonitor_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 /*
builtin/grep.c +4 -3
index 76cf999d310..757d52b94ec 100644
@@ -290,13 +290,14 @@ static int wait_all(void)
 	return hit;
 }
 
-static int grep_cmd_config(const char *var, const char *value, void *cb)
+static int grep_cmd_config(const char *var, const char *value,
+			   const struct config_context *ctx, void *cb)
 {
-	int st = grep_config(var, value, cb);
+	int st = grep_config(var, value, ctx, cb);
 
 	if (git_color_config(var, value, cb) < 0)
 		st = -1;
-	else if (git_default_config(var, value, cb) < 0)
+	else if (git_default_config(var, value, ctx, cb) < 0)
 		st = -1;
 
 	if (!strcmp(var, "grep.threads")) {
builtin/help.c +3 -2
index d3cf4af3f6e..c348f201254 100644
@@ -398,7 +398,8 @@ static int add_man_viewer_info(const char *var, const char *value)
 	return 0;
 }
 
-static int git_help_config(const char *var, const char *value, void *cb)
+static int git_help_config(const char *var, const char *value,
+			   const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "help.format")) {
 		if (!value)
@@ -421,7 +422,7 @@ static int git_help_config(const char *var, const char *value, void *cb)
 	if (starts_with(var, "man."))
 		return add_man_viewer_info(var, value);
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static struct cmdnames main_cmds, other_cmds;
builtin/index-pack.c +3 -2
index d0d8067510b..de8884ea5c2 100644
@@ -1581,7 +1581,8 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
 	strbuf_release(&pack_name);
 }
 
-static int git_index_pack_config(const char *k, const char *v, void *cb)
+static int git_index_pack_config(const char *k, const char *v,
+				 const struct config_context *ctx, void *cb)
 {
 	struct pack_idx_option *opts = cb;
 
@@ -1608,7 +1609,7 @@ static int git_index_pack_config(const char *k, const char *v, void *cb)
 		else
 			opts->flags &= ~WRITE_REV;
 	}
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
 static int cmp_uint32(const void *a_, const void *b_)
builtin/log.c +6 -4
index c85f13a5d5e..09d6a13075b 100644
@@ -564,7 +564,8 @@ static int cmd_log_walk(struct rev_info *rev)
 	return retval;
 }
 
-static int git_log_config(const char *var, const char *value, void *cb)
+static int git_log_config(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb)
 {
 	const char *slot_name;
 
@@ -613,7 +614,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_diff_ui_config(var, value, cb);
+	return git_diff_ui_config(var, value, ctx, cb);
 }
 
 int cmd_whatchanged(int argc, const char **argv, const char *prefix)
@@ -979,7 +980,8 @@ static enum cover_from_description parse_cover_from_description(const char *arg)
 		die(_("%s: invalid cover from description mode"), arg);
 }
 
-static int git_format_config(const char *var, const char *value, void *cb)
+static int git_format_config(const char *var, const char *value,
+			     const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "format.headers")) {
 		if (!value)
@@ -1108,7 +1110,7 @@ static int git_format_config(const char *var, const char *value, void *cb)
 	if (!strcmp(var, "diff.noprefix"))
 		return 0;
 
-	return git_log_config(var, value, cb);
+	return git_log_config(var, value, ctx, cb);
 }
 
 static const char *output_directory = NULL;
builtin/merge.c +4 -3
index 8da3e46abb0..cad624fb797 100644
@@ -623,7 +623,8 @@ static void parse_branch_merge_options(char *bmo)
 	free(argv);
 }
 
-static int git_merge_config(const char *k, const char *v, void *cb)
+static int git_merge_config(const char *k, const char *v,
+			    const struct config_context *ctx, void *cb)
 {
 	int status;
 	const char *str;
@@ -668,10 +669,10 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 		return 0;
 	}
 
-	status = fmt_merge_msg_config(k, v, cb);
+	status = fmt_merge_msg_config(k, v, ctx, cb);
 	if (status)
 		return status;
-	return git_diff_ui_config(k, v, cb);
+	return git_diff_ui_config(k, v, ctx, cb);
 }
 
 static int read_tree_trivial(struct object_id *common, struct object_id *head,
builtin/multi-pack-index.c +1 -0
index 1b5083f8b26..a0a7b82cc69 100644
@@ -82,6 +82,7 @@ static struct option *add_common_options(struct option *prev)
 }
 
 static int git_multi_pack_index_write_config(const char *var, const char *value,
+					     const struct config_context *ctx UNUSED,
 					     void *cb UNUSED)
 {
 	if (!strcmp(var, "pack.writebitmaphashcache")) {
builtin/pack-objects.c +3 -2
index 3af2d84f589..34aa0b483a0 100644
@@ -3135,7 +3135,8 @@ static void prepare_pack(int window, int depth)
 	free(delta_list);
 }
 
-static int git_pack_config(const char *k, const char *v, void *cb)
+static int git_pack_config(const char *k, const char *v,
+			   const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(k, "pack.window")) {
 		window = git_config_int(k, v);
@@ -3227,7 +3228,7 @@ static int git_pack_config(const char *k, const char *v, void *cb)
 		ex->uri = xstrdup(pack_end + 1);
 		oidmap_put(&configured_exclusions, ex);
 	}
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
 /* Counters for trace2 output when in --stdin-packs mode. */
builtin/patch-id.c +3 -2
index 9d5585d3a72..03eddd0fb82 100644
@@ -196,7 +196,8 @@ struct patch_id_opts {
 	int verbatim;
 };
 
-static int git_patch_id_config(const char *var, const char *value, void *cb)
+static int git_patch_id_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	struct patch_id_opts *opts = cb;
 
@@ -209,7 +210,7 @@ static int git_patch_id_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_patch_id(int argc, const char **argv, const char *prefix)
builtin/pull.c +3 -2
index 0c7bac97b75..83fca5b1d46 100644
@@ -361,7 +361,8 @@ static enum rebase_type config_get_rebase(int *rebase_unspecified)
 /**
  * Read config variables.
  */
-static int git_pull_config(const char *var, const char *value, void *cb)
+static int git_pull_config(const char *var, const char *value,
+			   const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "rebase.autostash")) {
 		config_autostash = git_config_bool(var, value);
@@ -374,7 +375,7 @@ static int git_pull_config(const char *var, const char *value, void *cb)
 		check_trust_level = 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 /**
builtin/push.c +3 -2
index dbdf609daf3..a2f68f77324 100644
@@ -510,7 +510,8 @@ static void set_push_cert_flags(int *flags, int v)
 }
 
 
-static int git_push_config(const char *k, const char *v, void *cb)
+static int git_push_config(const char *k, const char *v,
+			   const struct config_context *ctx, void *cb)
 {
 	const char *slot_name;
 	int *flags = cb;
@@ -577,7 +578,7 @@ static int git_push_config(const char *k, const char *v, void *cb)
 		return 0;
 	}
 
-	return git_default_config(k, v, NULL);
+	return git_default_config(k, v, ctx, NULL);
 }
 
 int cmd_push(int argc, const char **argv, const char *prefix)
builtin/read-tree.c +3 -2
index 440f19b1b87..8877dd6d4b5 100644
@@ -102,12 +102,13 @@ static int debug_merge(const struct cache_entry * const *stages,
 	return 0;
 }
 
-static int git_read_tree_config(const char *var, const char *value, void *cb)
+static int git_read_tree_config(const char *var, const char *value,
+				const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "submodule.recurse"))
 		return git_default_submodule_config(var, value, cb);
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
builtin/rebase.c +3 -2
index ace1d5e8d11..60930e2d8e0 100644
@@ -772,7 +772,8 @@ static void parse_rebase_merges_value(struct rebase_options *options, const char
 		die(_("Unknown rebase-merges mode: %s"), value);
 }
 
-static int rebase_config(const char *var, const char *value, void *data)
+static int rebase_config(const char *var, const char *value,
+			 const struct config_context *ctx, void *data)
 {
 	struct rebase_options *opts = data;
 
@@ -831,7 +832,7 @@ static int rebase_config(const char *var, const char *value, void *data)
 		return git_config_string(&opts->default_backend, var, value);
 	}
 
-	return git_default_config(var, value, data);
+	return git_default_config(var, value, ctx, data);
 }
 
 static int checkout_up_to_date(struct rebase_options *options)
builtin/receive-pack.c +3 -2
index 1a31a583674..94d9898aff7 100644
@@ -139,7 +139,8 @@ static enum deny_action parse_deny_action(const char *var, const char *value)
 	return DENY_IGNORE;
 }
 
-static int receive_pack_config(const char *var, const char *value, void *cb)
+static int receive_pack_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	int status = parse_hide_refs_config(var, value, "receive", &hidden_refs);
 
@@ -266,7 +267,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static void show_ref(const char *path, const struct object_id *oid)
builtin/reflog.c +4 -3
index a1fa0c855f4..84251cc9517 100644
@@ -108,7 +108,8 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
 #define EXPIRE_TOTAL   01
 #define EXPIRE_UNREACH 02
 
-static int reflog_expire_config(const char *var, const char *value, void *cb)
+static int reflog_expire_config(const char *var, const char *value,
+				const struct config_context *ctx, void *cb)
 {
 	const char *pattern, *key;
 	size_t pattern_len;
@@ -117,7 +118,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
 	struct reflog_expire_cfg *ent;
 
 	if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
-		return git_default_config(var, value, cb);
+		return git_default_config(var, value, ctx, cb);
 
 	if (!strcmp(key, "reflogexpire")) {
 		slot = EXPIRE_TOTAL;
@@ -128,7 +129,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
 		if (git_config_expiry_date(&expire, var, value))
 			return -1;
 	} else
-		return git_default_config(var, value, cb);
+		return git_default_config(var, value, ctx, cb);
 
 	if (!pattern) {
 		switch (slot) {
builtin/remote.c +5 -2
index 1e0b137d977..87de81105e2 100644
@@ -268,6 +268,7 @@ static const char *abbrev_ref(const char *name, const char *prefix)
 #define abbrev_branch(name) abbrev_ref((name), "refs/heads/")
 
 static int config_read_branches(const char *key, const char *value,
+				const struct config_context *ctx UNUSED,
 				void *data UNUSED)
 {
 	const char *orig_key = key;
@@ -645,7 +646,7 @@ struct push_default_info
 };
 
 static int config_read_push_default(const char *key, const char *value,
-	void *cb)
+	const struct config_context *ctx UNUSED, void *cb)
 {
 	struct push_default_info* info = cb;
 	if (strcmp(key, "remote.pushdefault") ||
@@ -1494,7 +1495,9 @@ static int prune(int argc, const char **argv, const char *prefix)
 	return result;
 }
 
-static int get_remote_default(const char *key, const char *value UNUSED, void *priv)
+static int get_remote_default(const char *key, const char *value UNUSED,
+			      const struct config_context *ctx UNUSED,
+			      void *priv)
 {
 	if (strcmp(key, "remotes.default") == 0) {
 		int *found = priv;
builtin/repack.c +3 -2
index 0541c3ce157..6f74570bf94 100644
@@ -59,7 +59,8 @@ struct pack_objects_args {
 	int local;
 };
 
-static int repack_config(const char *var, const char *value, void *cb)
+static int repack_config(const char *var, const char *value,
+			 const struct config_context *ctx, void *cb)
 {
 	struct pack_objects_args *cruft_po_args = cb;
 	if (!strcmp(var, "repack.usedeltabaseoffset")) {
@@ -91,7 +92,7 @@ static int repack_config(const char *var, const char *value, void *cb)
 		return git_config_string(&cruft_po_args->depth, var, value);
 	if (!strcmp(var, "repack.cruftthreads"))
 		return git_config_string(&cruft_po_args->threads, var, value);
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 /*
builtin/reset.c +3 -2
index f99f32d5802..1ae82f2e89c 100644
@@ -312,12 +312,13 @@ static int reset_refs(const char *rev, const struct object_id *oid)
 	return update_ref_status;
 }
 
-static int git_reset_config(const char *var, const char *value, void *cb)
+static int git_reset_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "submodule.recurse"))
 		return git_default_submodule_config(var, value, cb);
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_reset(int argc, const char **argv, const char *prefix)
builtin/send-pack.c +3 -2
index 4784143004d..cd6d9e41129 100644
@@ -131,7 +131,8 @@ static void print_helper_status(struct ref *ref)
 	strbuf_release(&buf);
 }
 
-static int send_pack_config(const char *k, const char *v, void *cb)
+static int send_pack_config(const char *k, const char *v,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(k, "push.gpgsign")) {
 		const char *value;
@@ -151,7 +152,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)
 			}
 		}
 	}
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
 int cmd_send_pack(int argc, const char **argv, const char *prefix)
builtin/show-branch.c +3 -2
index a2461270d4b..f2fd245b838 100644
@@ -559,7 +559,8 @@ static void append_one_rev(const char *av)
 	die("bad sha1 reference %s", av);
 }
 
-static int git_show_branch_config(const char *var, const char *value, void *cb)
+static int git_show_branch_config(const char *var, const char *value,
+				  const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "showbranch.default")) {
 		if (!value)
@@ -582,7 +583,7 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
 	if (git_color_config(var, value, cb) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
builtin/stash.c +3 -2
index a7e17ffe384..e5c4246d2d4 100644
@@ -837,7 +837,8 @@ static int show_stat = 1;
 static int show_patch;
 static int show_include_untracked;
 
-static int git_stash_config(const char *var, const char *value, void *cb)
+static int git_stash_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "stash.showstat")) {
 		show_stat = git_config_bool(var, value);
@@ -851,7 +852,7 @@ static int git_stash_config(const char *var, const char *value, void *cb)
 		show_include_untracked = git_config_bool(var, value);
 		return 0;
 	}
-	return git_diff_basic_config(var, value, cb);
+	return git_diff_basic_config(var, value, ctx, cb);
 }
 
 static void diff_include_untracked(const struct stash_info *info, struct diff_options *diff_opt)
builtin/submodule--helper.c +1 -0
index 6a16208e8a8..f8e9d85e77a 100644
@@ -2192,6 +2192,7 @@ static int update_clone_task_finished(int result,
 }
 
 static int git_update_clone_config(const char *var, const char *value,
+				   const struct config_context *ctx UNUSED,
 				   void *cb)
 {
 	int *max_jobs = cb;
builtin/tag.c +3 -2
index 1acf5f7a59f..b7dfb5e2cad 100644
@@ -188,7 +188,8 @@ static const char tag_template_nocleanup[] =
 	"Lines starting with '%c' will be kept; you may remove them"
 	" yourself if you want to.\n");
 
-static int git_tag_config(const char *var, const char *value, void *cb)
+static int git_tag_config(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "tag.gpgsign")) {
 		config_sign_tag = git_config_bool(var, value);
@@ -213,7 +214,7 @@ static int git_tag_config(const char *var, const char *value, void *cb)
 	if (git_color_config(var, value, cb) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static void write_tag_body(int fd, const struct object_id *oid)
builtin/var.c +3 -2
index 21499989807..ae011bdf409 100644
@@ -71,13 +71,14 @@ static const struct git_var *get_git_var(const char *var)
 	return NULL;
 }
 
-static int show_config(const char *var, const char *value, void *cb)
+static int show_config(const char *var, const char *value,
+		       const struct config_context *ctx, void *cb)
 {
 	if (value)
 		printf("%s=%s\n", var, value);
 	else
 		printf("%s\n", var);
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_var(int argc, const char **argv, const char *prefix UNUSED)
builtin/worktree.c +3 -2
index 5a9cf076ad2..a30d37a273f 100644
@@ -128,14 +128,15 @@ static int verbose;
 static int guess_remote;
 static timestamp_t expire;
 
-static int git_worktree_config(const char *var, const char *value, void *cb)
+static int git_worktree_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "worktree.guessremote")) {
 		guess_remote = git_config_bool(var, value);
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int delete_git_dir(const char *id)
bundle-uri.c +6 -2
index 2a2db1a1d39..0d5acc3dc51 100644
@@ -224,7 +224,9 @@ static int bundle_list_update(const char *key, const char *value,
 	return 0;
 }
 
-static int config_to_bundle_list(const char *key, const char *value, void *data)
+static int config_to_bundle_list(const char *key, const char *value,
+				 const struct config_context *ctx UNUSED,
+				 void *data)
 {
 	struct bundle_list *list = data;
 	return bundle_list_update(key, value, list);
@@ -871,7 +873,9 @@ cached:
 	return advertise_bundle_uri;
 }
 
-static int config_to_packet_line(const char *key, const char *value, void *data)
+static int config_to_packet_line(const char *key, const char *value,
+				 const struct config_context *ctx UNUSED,
+				 void *data)
 {
 	struct packet_reader *writer = data;
 
compat/mingw.c +2 -1
index d06cdc6254f..4186bc7a417 100644
@@ -244,7 +244,8 @@ static int core_restrict_inherited_handles = -1;
 static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
 static char *unset_environment_variables;
 
-int mingw_core_config(const char *var, const char *value, void *cb)
+int mingw_core_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "core.hidedotfiles")) {
 		if (value && !strcasecmp(value, "dotgitonly"))
compat/mingw.h +3 -1
index 209cf7cebad..5e34c873473 100644
@@ -11,7 +11,9 @@ typedef _sigset_t sigset_t;
 #undef _POSIX_THREAD_SAFE_FUNCTIONS
 #endif
 
-int mingw_core_config(const char *var, const char *value, void *cb);
+struct config_context;
+int mingw_core_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb);
 #define platform_core_config mingw_core_config
 
 /*
config.c +24 -14
index 2eeb6621421..850e432e301 100644
@@ -209,7 +209,8 @@ struct config_include_data {
 };
 #define CONFIG_INCLUDE_INIT { 0 }
 
-static int git_config_include(const char *var, const char *value, void *data);
+static int git_config_include(const char *var, const char *value,
+			      const struct config_context *ctx, void *data);
 
 #define MAX_INCLUDE_DEPTH 10
 static const char include_depth_advice[] = N_(
@@ -388,7 +389,8 @@ static int include_by_branch(const char *cond, size_t cond_len)
 	return ret;
 }
 
-static int add_remote_url(const char *var, const char *value, void *data)
+static int add_remote_url(const char *var, const char *value,
+			  const struct config_context *ctx UNUSED, void *data)
 {
 	struct string_list *remote_urls = data;
 	const char *remote_name;
@@ -423,6 +425,7 @@ static void populate_remote_urls(struct config_include_data *inc)
 }
 
 static int forbid_remote_url(const char *var, const char *value UNUSED,
+			     const struct config_context *ctx UNUSED,
 			     void *data UNUSED)
 {
 	const char *remote_name;
@@ -486,7 +489,9 @@ static int include_condition_is_true(struct config_source *cs,
 	return 0;
 }
 
-static int git_config_include(const char *var, const char *value, void *data)
+static int git_config_include(const char *var, const char *value,
+			      const struct config_context *ctx,
+			      void *data)
 {
 	struct config_include_data *inc = data;
 	struct config_source *cs = inc->config_reader->source;
@@ -498,7 +503,7 @@ static int git_config_include(const char *var, const char *value, void *data)
 	 * 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);
+	ret = inc->fn(var, value, NULL, inc->data);
 	if (ret < 0)
 		return ret;
 
@@ -680,7 +685,7 @@ static int config_parse_pair(const char *key, const char *value,
 	if (git_config_parse_key(key, &canonical_name, NULL))
 		return -1;
 
-	ret = (fn(canonical_name, value, data) < 0) ? -1 : 0;
+	ret = (fn(canonical_name, value, NULL, data) < 0) ? -1 : 0;
 	free(canonical_name);
 	return ret;
 }
@@ -968,7 +973,7 @@ static int get_value(struct config_source *cs, config_fn_t fn, void *data,
 	 * accurate line number in error messages.
 	 */
 	cs->linenr--;
-	ret = fn(name->buf, value, data);
+	ret = fn(name->buf, value, NULL, data);
 	if (ret >= 0)
 		cs->linenr++;
 	return ret;
@@ -1562,7 +1567,8 @@ int git_config_color(char *dest, const char *var, const char *value)
 	return 0;
 }
 
-static int git_default_core_config(const char *var, const char *value, void *cb)
+static int git_default_core_config(const char *var, const char *value,
+				   const struct config_context *ctx, void *cb)
 {
 	/* This needs a better name */
 	if (!strcmp(var, "core.filemode")) {
@@ -1842,7 +1848,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
 	}
 
 	/* Add other config variables here and to Documentation/config.txt. */
-	return platform_core_config(var, value, cb);
+	return platform_core_config(var, value, ctx, cb);
 }
 
 static int git_default_sparse_config(const char *var, const char *value)
@@ -1944,15 +1950,16 @@ static int git_default_mailmap_config(const char *var, const char *value)
 	return 0;
 }
 
-int git_default_config(const char *var, const char *value, void *cb)
+int git_default_config(const char *var, const char *value,
+		       const struct config_context *ctx, void *cb)
 {
 	if (starts_with(var, "core."))
-		return git_default_core_config(var, value, cb);
+		return git_default_core_config(var, value, ctx, cb);
 
 	if (starts_with(var, "user.") ||
 	    starts_with(var, "author.") ||
 	    starts_with(var, "committer."))
-		return git_ident_config(var, value, cb);
+		return git_ident_config(var, value, ctx, cb);
 
 	if (starts_with(var, "i18n."))
 		return git_default_i18n_config(var, value);
@@ -2318,7 +2325,7 @@ static void configset_iter(struct config_reader *reader, struct config_set *set,
 
 		config_reader_set_kvi(reader, values->items[value_index].util);
 
-		if (fn(entry->key, values->items[value_index].string, data) < 0)
+		if (fn(entry->key, values->items[value_index].string, NULL, data) < 0)
 			git_die_config_linenr(entry->key,
 					      reader->config_kvi->filename,
 					      reader->config_kvi->linenr);
@@ -2496,7 +2503,9 @@ struct configset_add_data {
 };
 #define CONFIGSET_ADD_INIT { 0 }
 
-static int config_set_callback(const char *key, const char *value, void *cb)
+static int config_set_callback(const char *key, const char *value,
+			       const struct config_context *ctx UNUSED,
+			       void *cb)
 {
 	struct configset_add_data *data = cb;
 	configset_add_value(data->config_reader, data->config_set, key, value);
@@ -3106,7 +3115,8 @@ static int store_aux_event(enum config_event_t type,
 	return 0;
 }
 
-static int store_aux(const char *key, const char *value, void *cb)
+static int store_aux(const char *key, const char *value,
+		     const struct config_context *ctx UNUSED, void *cb)
 {
 	struct config_store_data *store = cb;
 
config.h +31 -10

67 lines not on this page · raw message

connect.c +2 -2
index 3a0186280c4..cddac1d96b8 100644
@@ -964,7 +964,7 @@ static struct child_process *git_tcp_connect(int fd[2], char *host, int flags)
 static char *git_proxy_command;
 
 static int git_proxy_command_options(const char *var, const char *value,
-		void *cb)
+		const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "core.gitproxy")) {
 		const char *for_pos;
@@ -1010,7 +1010,7 @@ static int git_proxy_command_options(const char *var, const char *value,
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int git_use_proxy(const char *host)
contrib/coccinelle/config_fn_ctx.pending.cocci new file +144 -0

144 lines not on this page · raw message

convert.c +3 -1
index 9ee79fe4699..8e96cf83030 100644
@@ -1015,7 +1015,9 @@ static int apply_filter(const char *path, const char *src, size_t len,
 	return 0;
 }
 
-static int read_convert_config(const char *var, const char *value, void *cb UNUSED)
+static int read_convert_config(const char *var, const char *value,
+			       const struct config_context *ctx UNUSED,
+			       void *cb UNUSED)
 {
 	const char *key, *name;
 	size_t namelen;
credential.c +1 -0
index 8825c6f1320..d6647541634 100644
@@ -49,6 +49,7 @@ static int credential_from_potentially_partial_url(struct credential *c,
 						   const char *url);
 
 static int credential_config_callback(const char *var, const char *value,
+				      const struct config_context *ctx UNUSED,
 				      void *data)
 {
 	struct credential *c = data;
delta-islands.c +3 -1
index c824a5f6a42..5fc6ea6ff55 100644
@@ -341,7 +341,9 @@ static void free_remote_islands(kh_str_t *remote_islands)
 	kh_destroy_str(remote_islands);
 }
 
-static int island_config_callback(const char *k, const char *v, void *cb)
+static int island_config_callback(const char *k, const char *v,
+				  const struct config_context *ctx UNUSED,
+				  void *cb)
 {
 	struct island_load_data *ild = cb;
 
diff.c +6 -4
index c106f8a4ffa..0e382c8f7f0 100644
@@ -357,7 +357,8 @@ static unsigned parse_color_moved_ws(const char *arg)
 	return ret;
 }
 
-int git_diff_ui_config(const char *var, const char *value, void *cb)
+int git_diff_ui_config(const char *var, const char *value,
+		       const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
 		diff_use_color_default = git_config_colorbool(var, value);
@@ -440,10 +441,11 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
 	if (git_color_config(var, value, cb) < 0)
 		return -1;
 
-	return git_diff_basic_config(var, value, cb);
+	return git_diff_basic_config(var, value, ctx, cb);
 }
 
-int git_diff_basic_config(const char *var, const char *value, void *cb)
+int git_diff_basic_config(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb)
 {
 	const char *name;
 
@@ -495,7 +497,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
 	if (git_diff_heuristic_config(var, value, cb) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static char *quote_two(const char *one, const char *two)
diff.h +5 -2
index 6c10ce289da..33a4f4d5988 100644
@@ -531,10 +531,13 @@ void free_diffstat_info(struct diffstat_t *diffstat);
 int parse_long_opt(const char *opt, const char **argv,
 		   const char **optarg);
 
-int git_diff_basic_config(const char *var, const char *value, void *cb);
+struct config_context;
+int git_diff_basic_config(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb);
 int git_diff_heuristic_config(const char *var, const char *value, void *cb);
 void init_diff_ui_defaults(void);
-int git_diff_ui_config(const char *var, const char *value, void *cb);
+int git_diff_ui_config(const char *var, const char *value,
+		       const struct config_context *ctx, void *cb);
 void repo_diff_setup(struct repository *, struct diff_options *);
 struct option *add_diff_options(const struct option *, struct diff_options *);
 int diff_opt_parse(struct diff_options *, const char **, int, const char *);
fetch-pack.c +3 -2
index 0f71054fbae..5f3d40f3d09 100644
@@ -1860,7 +1860,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 	return ref;
 }
 
-static int fetch_pack_config_cb(const char *var, const char *value, void *cb)
+static int fetch_pack_config_cb(const char *var, const char *value,
+				const struct config_context *ctx, void *cb)
 {
 	if (strcmp(var, "fetch.fsck.skiplist") == 0) {
 		const char *path;
@@ -1882,7 +1883,7 @@ static int fetch_pack_config_cb(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static void fetch_pack_config(void)
fmt-merge-msg.c +3 -2
index 5af0d4715ba..10137444321 100644
@@ -20,7 +20,8 @@ static int use_branch_desc;
 static int suppress_dest_pattern_seen;
 static struct string_list suppress_dest_patterns = STRING_LIST_INIT_DUP;
 
-int fmt_merge_msg_config(const char *key, const char *value, void *cb)
+int fmt_merge_msg_config(const char *key, const char *value,
+			 const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
 		int is_bool;
@@ -40,7 +41,7 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)
 			string_list_append(&suppress_dest_patterns, value);
 		suppress_dest_pattern_seen = 1;
 	} else {
-		return git_default_config(key, value, cb);
+		return git_default_config(key, value, ctx, cb);
 	}
 	return 0;
 }
fmt-merge-msg.h +2 -1
index 99054042dc5..73ca3e44652 100644
@@ -13,7 +13,8 @@ struct fmt_merge_msg_opts {
 };
 
 extern int merge_log_config;
-int fmt_merge_msg_config(const char *key, const char *value, void *cb);
+int fmt_merge_msg_config(const char *key, const char *value,
+			 const struct config_context *ctx, void *cb);
 int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 		  struct fmt_merge_msg_opts *);
 
fsck.c +6 -3
index 3261ef9ec28..55b6a694853 100644
@@ -1163,7 +1163,9 @@ struct fsck_gitmodules_data {
 	int ret;
 };
 
-static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
+static int fsck_gitmodules_fn(const char *var, const char *value,
+			      const struct config_context *ctx UNUSED,
+			      void *vdata)
 {
 	struct fsck_gitmodules_data *data = vdata;
 	const char *subsection, *key;
@@ -1373,7 +1375,8 @@ int fsck_finish(struct fsck_options *options)
 	return ret;
 }
 
-int git_fsck_config(const char *var, const char *value, void *cb)
+int git_fsck_config(const char *var, const char *value,
+		    const struct config_context *ctx, void *cb)
 {
 	struct fsck_options *options = cb;
 	if (strcmp(var, "fsck.skiplist") == 0) {
@@ -1394,7 +1397,7 @@ int git_fsck_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 /*
fsck.h +3 -1
index e17730e9da9..6359ba359bd 100644
@@ -233,10 +233,12 @@ void fsck_put_object_name(struct fsck_options *options,
 const char *fsck_describe_object(struct fsck_options *options,
 				 const struct object_id *oid);
 
+struct key_value_info;
 /*
  * git_config() callback for use by fsck-y tools that want to support
  * fsck.<msg> fsck.skipList etc.
  */
-int git_fsck_config(const char *var, const char *value, void *cb);
+int git_fsck_config(const char *var, const char *value,
+		    const struct config_context *ctx, void *cb);
 
 #endif
git-compat-util.h +2 -0
index 5b2b99c17c5..14e8aacb957 100644
@@ -440,8 +440,10 @@ typedef uintmax_t timestamp_t;
 #endif
 
 #ifndef platform_core_config
+struct config_context;
 static inline int noop_core_config(const char *var UNUSED,
 				   const char *value UNUSED,
+				   const struct config_context *ctx UNUSED,
 				   void *cb UNUSED)
 {
 	return 0;
gpg-interface.c +5 -2
index 19a3471a0b5..57c862a3a22 100644
@@ -14,7 +14,8 @@
 #include "alias.h"
 #include "wrapper.h"
 
-static int git_gpg_config(const char *, const char *, void *);
+static int git_gpg_config(const char *, const char *,
+			  const struct config_context *, void *);
 
 static void gpg_interface_lazy_init(void)
 {
@@ -720,7 +721,9 @@ void set_signing_key(const char *key)
 	configured_signing_key = xstrdup(key);
 }
 
-static int git_gpg_config(const char *var, const char *value, void *cb UNUSED)
+static int git_gpg_config(const char *var, const char *value,
+			  const struct config_context *ctx UNUSED,
+			  void *cb UNUSED)
 {
 	struct gpg_format *fmt = NULL;
 	char *fmtname = NULL;
grep.c +4 -3
index f00986c451a..fc22c3e2afb 100644
@@ -56,7 +56,8 @@ define_list_config_array_extra(color_grep_slots, {"match"});
  * Read the configuration file once and store it in
  * the grep_defaults template.
  */
-int grep_config(const char *var, const char *value, void *cb)
+int grep_config(const char *var, const char *value,
+		const struct config_context *ctx, void *cb)
 {
 	struct grep_opt *opt = cb;
 	const char *slot;
@@ -91,9 +92,9 @@ int grep_config(const char *var, const char *value, void *cb)
 	if (!strcmp(var, "color.grep"))
 		opt->color = git_config_colorbool(var, value);
 	if (!strcmp(var, "color.grep.match")) {
-		if (grep_config("color.grep.matchcontext", value, cb) < 0)
+		if (grep_config("color.grep.matchcontext", value, ctx, cb) < 0)
 			return -1;
-		if (grep_config("color.grep.matchselected", value, cb) < 0)
+		if (grep_config("color.grep.matchselected", value, ctx, cb) < 0)
 			return -1;
 	} else if (skip_prefix(var, "color.grep.", &slot)) {
 		int i = LOOKUP_CONFIG(color_grep_slots, slot);
grep.h +3 -1
index c59592e3bdb..926c0875c42 100644
@@ -202,7 +202,9 @@ struct grep_opt {
 	.output = std_output, \
 }
 
-int grep_config(const char *var, const char *value, void *);
+struct config_context;
+int grep_config(const char *var, const char *value,
+		const struct config_context *ctx, void *data);
 void grep_init(struct grep_opt *, struct repository *repo);
 
 void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
help.c +5 -2
index 5d7637dce92..ac0ae5ac0dc 100644
@@ -309,7 +309,8 @@ void load_command_list(const char *prefix,
 	exclude_cmds(other_cmds, main_cmds);
 }
 
-static int get_colopts(const char *var, const char *value, void *data)
+static int get_colopts(const char *var, const char *value,
+		       const struct config_context *ctx UNUSED, void *data)
 {
 	unsigned int *colopts = data;
 
@@ -459,7 +460,8 @@ void list_developer_interfaces_help(void)
 	putchar('\n');
 }
 
-static int get_alias(const char *var, const char *value, void *data)
+static int get_alias(const char *var, const char *value,
+		     const struct config_context *ctx UNUSED, void *data)
 {
 	struct string_list *list = data;
 
@@ -543,6 +545,7 @@ static struct cmdnames aliases;
 #define AUTOCORRECT_IMMEDIATELY (-1)
 
 static int git_unknown_cmd_config(const char *var, const char *value,
+				  const struct config_context *ctx UNUSED,
 				  void *cb UNUSED)
 {
 	const char *p;
http.c +3 -2
index bb58bb3e6a3..762502828c9 100644
@@ -363,7 +363,8 @@ static void process_curl_messages(void)
 	}
 }
 
-static int http_options(const char *var, const char *value, void *cb)
+static int http_options(const char *var, const char *value,
+			const struct config_context *ctx, void *data)
 {
 	if (!strcmp("http.version", var)) {
 		return git_config_string(&curl_http_version, var, value);
@@ -534,7 +535,7 @@ static int http_options(const char *var, const char *value, void *cb)
 	}
 
 	/* Fall back on the default ones */
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, data);
 }
 
 static int curl_empty_auth_enabled(void)
ident.c +3 -1
index 8fad92d7007..08be4d0747d 100644
@@ -671,7 +671,9 @@ static int set_ident(const char *var, const char *value)
 	return 0;
 }
 
-int git_ident_config(const char *var, const char *value, void *data UNUSED)
+int git_ident_config(const char *var, const char *value,
+		     const struct config_context *ctx UNUSED,
+		     void *data UNUSED)
 {
 	if (!strcmp(var, "user.useconfigonly")) {
 		ident_use_config_only = git_config_bool(var, value);
ident.h +3 -1
index 96a64896a01..6a79febba15 100644
@@ -62,6 +62,8 @@ const char *fmt_name(enum want_ident);
 int committer_ident_sufficiently_given(void);
 int author_ident_sufficiently_given(void);
 
-int git_ident_config(const char *, const char *, void *);
+struct config_context;
+int git_ident_config(const char *, const char *, const struct config_context *,
+		     void *);
 
 #endif
imap-send.c +3 -2
index 7f5426177a1..47777e76861 100644
@@ -1323,7 +1323,8 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
 	return 1;
 }
 
-static int git_imap_config(const char *var, const char *val, void *cb)
+static int git_imap_config(const char *var, const char *val,
+			   const struct config_context *ctx, void *cb)
 {
 
 	if (!strcmp("imap.sslverify", var))
@@ -1357,7 +1358,7 @@ static int git_imap_config(const char *var, const char *val, void *cb)
 			server.host = xstrdup(val);
 		}
 	} else
-		return git_default_config(var, val, cb);
+		return git_default_config(var, val, ctx, cb);
 
 	return 0;
 }
ll-merge.c +1 -0
index 07ec16e8e5b..3936d112e00 100644
@@ -254,6 +254,7 @@ static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
 static const char *default_ll_merge;
 
 static int read_merge_config(const char *var, const char *value,
+			     const struct config_context *ctx UNUSED,
 			     void *cb UNUSED)
 {
 	struct ll_merge_driver *fn;
ls-refs.c +1 -0
index f385938b64c..a29c2364a59 100644
@@ -137,6 +137,7 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)
 }
 
 static int ls_refs_config(const char *var, const char *value,
+			  const struct config_context *ctx UNUSED,
 			  void *cb_data)
 {
 	struct ls_refs_data *data = cb_data;
mailinfo.c +3 -2
index 2aeb20e5e62..931505363cd 100644
@@ -1241,12 +1241,13 @@ int mailinfo_parse_quoted_cr_action(const char *actionstr, int *action)
 	return 0;
 }
 
-static int git_mailinfo_config(const char *var, const char *value, void *mi_)
+static int git_mailinfo_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *mi_)
 {
 	struct mailinfo *mi = mi_;
 
 	if (!starts_with(var, "mailinfo."))
-		return git_default_config(var, value, NULL);
+		return git_default_config(var, value, ctx, NULL);
 	if (!strcmp(var, "mailinfo.scissors")) {
 		mi->use_scissors = git_config_bool(var, value);
 		return 0;
notes-utils.c +3 -1
index 4a793eb347f..97c031c26ec 100644
@@ -94,7 +94,9 @@ static combine_notes_fn parse_combine_notes_fn(const char *v)
 		return NULL;
 }
 
-static int notes_rewrite_config(const char *k, const char *v, void *cb)
+static int notes_rewrite_config(const char *k, const char *v,
+				const struct config_context *ctx UNUSED,
+				void *cb)
 {
 	struct notes_rewrite_cfg *c = cb;
 	if (starts_with(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
notes.c +3 -1
index f51a2d3630e..e68645a4b89 100644
@@ -974,7 +974,9 @@ void string_list_add_refs_from_colon_sep(struct string_list *list,
 	free(globs_copy);
 }
 
-static int notes_display_config(const char *k, const char *v, void *cb)
+static int notes_display_config(const char *k, const char *v,
+				const struct config_context *ctx UNUSED,
+				void *cb)
 {
 	int *load_refs = cb;
 
pager.c +4 -1
index 63055d0873f..b8822a9381e 100644
@@ -43,6 +43,7 @@ static void wait_for_pager_signal(int signo)
 }
 
 static int core_pager_config(const char *var, const char *value,
+			     const struct config_context *ctx UNUSED,
 			     void *data UNUSED)
 {
 	if (!strcmp(var, "core.pager"))
@@ -228,7 +229,9 @@ struct pager_command_config_data {
 	char *value;
 };
 
-static int pager_command_config(const char *var, const char *value, void *vdata)
+static int pager_command_config(const char *var, const char *value,
+				const struct config_context *ctx UNUSED,
+				void *vdata)
 {
 	struct pager_command_config_data *data = vdata;
 	const char *cmd;
pretty.c +1 -0
index 0bb938021ba..87245353452 100644
@@ -56,6 +56,7 @@ static void save_user_format(struct rev_info *rev, const char *cp, int is_tforma
 }
 
 static int git_pretty_formats_config(const char *var, const char *value,
+				     const struct config_context *ctx UNUSED,
 				     void *cb UNUSED)
 {
 	struct cmt_fmt_map *commit_format = NULL;
promisor-remote.c +3 -1
index 1adcd6fb0a5..c22abb85b15 100644
@@ -100,7 +100,9 @@ static void promisor_remote_move_to_tail(struct promisor_remote_config *config,
 	config->promisors_tail = &r->next;
 }
 
-static int promisor_remote_config(const char *var, const char *value, void *data)
+static int promisor_remote_config(const char *var, const char *value,
+				  const struct config_context *ctx UNUSED,
+				  void *data)
 {
 	struct promisor_remote_config *config = data;
 	const char *name;
remote.c +2 -1
index 1bcd36e358a..241999c2842 100644
@@ -349,7 +349,8 @@ static void read_branches_file(struct remote_state *remote_state,
 	remote->fetch_tags = 1; /* always auto-follow */
 }
 
-static int handle_config(const char *key, const char *value, void *cb)
+static int handle_config(const char *key, const char *value,
+			 const struct config_context *ctx UNUSED, void *cb)
 {
 	const char *name;
 	size_t namelen;
revision.c +3 -1
index b33cc1d106a..87ed8ccd444 100644
@@ -1572,7 +1572,9 @@ struct exclude_hidden_refs_cb {
 	const char *section;
 };
 
-static int hide_refs_config(const char *var, const char *value, void *cb_data)
+static int hide_refs_config(const char *var, const char *value,
+			    const struct config_context *ctx UNUSED,
+			    void *cb_data)
 {
 	struct exclude_hidden_refs_cb *cb = cb_data;
 	cb->exclusions->hidden_refs_configured = 1;
scalar.c +3 -1
index 1326e1f6089..df7358f481c 100644
@@ -594,7 +594,9 @@ static int cmd_register(int argc, const char **argv)
 	return register_dir();
 }
 
-static int get_scalar_repos(const char *key, const char *value, void *data)
+static int get_scalar_repos(const char *key, const char *value,
+			    const struct config_context *ctx UNUSED,
+			    void *data)
 {
 	struct string_list *list = data;
 
sequencer.c +6 -3
index bceb6abcb6c..34754d17596 100644
@@ -219,7 +219,8 @@ static struct update_ref_record *init_update_ref_record(const char *ref)
 	return rec;
 }
 
-static int git_sequencer_config(const char *k, const char *v, void *cb)
+static int git_sequencer_config(const char *k, const char *v,
+				const struct config_context *ctx, void *cb)
 {
 	struct replay_opts *opts = cb;
 	int status;
@@ -274,7 +275,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)
 	if (opts->action == REPLAY_REVERT && !strcmp(k, "revert.reference"))
 		opts->commit_use_reference = git_config_bool(k, v);
 
-	return git_diff_basic_config(k, v, NULL);
+	return git_diff_basic_config(k, v, ctx, NULL);
 }
 
 void sequencer_init_config(struct replay_opts *opts)
@@ -2881,7 +2882,9 @@ static int git_config_string_dup(char **dest,
 	return 0;
 }
 
-static int populate_opts_cb(const char *key, const char *value, void *data)
+static int populate_opts_cb(const char *key, const char *value,
+			    const struct config_context *ctx UNUSED,
+			    void *data)
 {
 	struct replay_opts *opts = data;
 	int error_flag = 1;
setup.c +11 -5
index 6f6e92b96be..fadba5bab4b 100644
@@ -517,7 +517,9 @@ no_prevention_needed:
 	startup_info->original_cwd = NULL;
 }
 
-static int read_worktree_config(const char *var, const char *value, void *vdata)
+static int read_worktree_config(const char *var, const char *value,
+				const struct config_context *ctx UNUSED,
+				void *vdata)
 {
 	struct repository_format *data = vdata;
 
@@ -588,7 +590,8 @@ static enum extension_result handle_extension(const char *var,
 	return EXTENSION_UNKNOWN;
 }
 
-static int check_repo_format(const char *var, const char *value, void *vdata)
+static int check_repo_format(const char *var, const char *value,
+			     const struct config_context *ctx, void *vdata)
 {
 	struct repository_format *data = vdata;
 	const char *ext;
@@ -617,7 +620,7 @@ static int check_repo_format(const char *var, const char *value, void *vdata)
 		}
 	}
 
-	return read_worktree_config(var, value, vdata);
+	return read_worktree_config(var, value, ctx, vdata);
 }
 
 static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)
@@ -1115,7 +1118,8 @@ struct safe_directory_data {
 	int is_safe;
 };
 
-static int safe_directory_cb(const char *key, const char *value, void *d)
+static int safe_directory_cb(const char *key, const char *value,
+			     const struct config_context *ctx UNUSED, void *d)
 {
 	struct safe_directory_data *data = d;
 
@@ -1171,7 +1175,9 @@ static int ensure_valid_ownership(const char *gitfile,
 	return data.is_safe;
 }
 
-static int allowed_bare_repo_cb(const char *key, const char *value, void *d)
+static int allowed_bare_repo_cb(const char *key, const char *value,
+				const struct config_context *ctx UNUSED,
+				void *d)
 {
 	enum allowed_bare_repo *allowed_bare_repo = d;
 
submodule-config.c +12 -5
index 7eb7a0d88d2..a38d4d49731 100644
@@ -426,7 +426,8 @@ struct parse_config_parameter {
  * config store (.git/config, etc).  Callers are responsible for
  * checking for overrides in the main config store when appropriate.
  */
-static int parse_config(const char *var, const char *value, void *data)
+static int parse_config(const char *var, const char *value,
+			const struct config_context *ctx UNUSED, void *data)
 {
 	struct parse_config_parameter *me = data;
 	struct submodule *submodule;
@@ -674,7 +675,8 @@ out:
 	}
 }
 
-static int gitmodules_cb(const char *var, const char *value, void *data)
+static int gitmodules_cb(const char *var, const char *value,
+			 const struct config_context *ctx, void *data)
 {
 	struct repository *repo = data;
 	struct parse_config_parameter parameter;
@@ -684,7 +686,7 @@ static int gitmodules_cb(const char *var, const char *value, void *data)
 	parameter.gitmodules_oid = null_oid();
 	parameter.overwrite = 1;
 
-	return parse_config(var, value, &parameter);
+	return parse_config(var, value, ctx, &parameter);
 }
 
 void repo_read_gitmodules(struct repository *repo, int skip_if_read)
@@ -801,7 +803,9 @@ void submodule_free(struct repository *r)
 		submodule_cache_clear(r->submodule_cache);
 }
 
-static int config_print_callback(const char *var, const char *value, void *cb_data)
+static int config_print_callback(const char *var, const char *value,
+				 const struct config_context *ctx UNUSED,
+				 void *cb_data)
 {
 	char *wanted_key = cb_data;
 
@@ -843,7 +847,9 @@ struct fetch_config {
 	int *recurse_submodules;
 };
 
-static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
+static int gitmodules_fetch_config(const char *var, const char *value,
+				   const struct config_context *ctx UNUSED,
+				   void *cb)
 {
 	struct fetch_config *config = cb;
 	if (!strcmp(var, "submodule.fetchjobs")) {
@@ -871,6 +877,7 @@ void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules)
 }
 
 static int gitmodules_update_clone_config(const char *var, const char *value,
+					  const struct config_context *ctx UNUSED,
 					  void *cb)
 {
 	int *max_jobs = cb;
t/helper/test-config.c +8 -3
index ad78fc17683..85ad815358e 100644
@@ -42,7 +42,9 @@
  *
  */
 
-static int iterate_cb(const char *var, const char *value, void *data UNUSED)
+static int iterate_cb(const char *var, const char *value,
+		      const struct config_context *ctx UNUSED,
+		      void *data UNUSED)
 {
 	static int nr;
 
@@ -59,7 +61,8 @@ static int iterate_cb(const char *var, const char *value, void *data UNUSED)
 	return 0;
 }
 
-static int parse_int_cb(const char *var, const char *value, void *data)
+static int parse_int_cb(const char *var, const char *value,
+			const struct config_context *ctx UNUSED, void *data)
 {
 	const char *key_to_match = data;
 
@@ -70,7 +73,9 @@ static int parse_int_cb(const char *var, const char *value, void *data)
 	return 0;
 }
 
-static int early_config_cb(const char *var, const char *value, void *vdata)
+static int early_config_cb(const char *var, const char *value,
+			   const struct config_context *ctx UNUSED,
+			   void *vdata)
 {
 	const char *key = vdata;
 
t/helper/test-userdiff.c +3 -1
index 680124a6760..0ce31ce59f5 100644
@@ -12,7 +12,9 @@ static int driver_cb(struct userdiff_driver *driver,
 	return 0;
 }
 
-static int cmd__userdiff_config(const char *var, const char *value, void *cb UNUSED)
+static int cmd__userdiff_config(const char *var, const char *value,
+				const struct config_context *ctx UNUSED,
+				void *cb UNUSED)
 {
 	if (userdiff_config(var, value) < 0)
 		return -1;
trace2/tr2_cfg.c +7 -2
index 78cfc15d52d..83bc4fd109c 100644
@@ -99,7 +99,8 @@ struct tr2_cfg_data {
 /*
  * See if the given config key matches any of our patterns of interest.
  */
-static int tr2_cfg_cb(const char *key, const char *value, void *d)
+static int tr2_cfg_cb(const char *key, const char *value,
+		      const struct config_context *ctx UNUSED, void *d)
 {
 	struct strbuf **s;
 	struct tr2_cfg_data *data = (struct tr2_cfg_data *)d;
@@ -142,8 +143,12 @@ void tr2_list_env_vars_fl(const char *file, int line)
 void tr2_cfg_set_fl(const char *file, int line, const char *key,
 		    const char *value)
 {
+	struct key_value_info kvi = KVI_INIT;
+	struct config_context ctx = {
+		.kvi = &kvi,
+	};
 	struct tr2_cfg_data data = { file, line };
 
 	if (tr2_cfg_load_patterns() > 0)
-		tr2_cfg_cb(key, value, &data);
+		tr2_cfg_cb(key, value, &ctx, &data);
 }
trace2/tr2_sysenv.c +2 -1
index 069786cb927..f26ec95ab4d 100644
@@ -57,7 +57,8 @@ static struct tr2_sysenv_entry tr2_sysenv_settings[] = {
 };
 /* clang-format on */
 
-static int tr2_sysenv_cb(const char *key, const char *value, void *d)
+static int tr2_sysenv_cb(const char *key, const char *value,
+			 const struct config_context *ctx UNUSED, void *d)
 {
 	int k;
 
trailer.c +2 -0
index a2c3ed6f28c..06dc0b7f683 100644
@@ -482,6 +482,7 @@ static struct {
 };
 
 static int git_trailer_default_config(const char *conf_key, const char *value,
+				      const struct config_context *ctx UNUSED,
 				      void *cb UNUSED)
 {
 	const char *trailer_item, *variable_name;
@@ -514,6 +515,7 @@ static int git_trailer_default_config(const char *conf_key, const char *value,
 }
 
 static int git_trailer_config(const char *conf_key, const char *value,
+			      const struct config_context *ctx UNUSED,
 			      void *cb UNUSED)
 {
 	const char *trailer_item, *variable_name;
upload-pack.c +6 -2
index d3312006a32..951fd1f9c25 100644
@@ -1309,7 +1309,9 @@ static int parse_object_filter_config(const char *var, const char *value,
 	return 0;
 }
 
-static int upload_pack_config(const char *var, const char *value, void *cb_data)
+static int upload_pack_config(const char *var, const char *value,
+			      const struct config_context *ctx UNUSED,
+			      void *cb_data)
 {
 	struct upload_pack_data *data = cb_data;
 
@@ -1350,7 +1352,9 @@ static int upload_pack_config(const char *var, const char *value, void *cb_data)
 	return parse_hide_refs_config(var, value, "uploadpack", &data->hidden_refs);
 }
 
-static int upload_pack_protected_config(const char *var, const char *value, void *cb_data)
+static int upload_pack_protected_config(const char *var, const char *value,
+					const struct config_context *ctx UNUSED,
+					void *cb_data)
 {
 	struct upload_pack_data *data = cb_data;
 
urlmatch.c +4 -3
index eba0bdd77fe..1c45f23adf2 100644
@@ -551,7 +551,8 @@ static int cmp_matches(const struct urlmatch_item *a,
 	return 0;
 }
 
-int urlmatch_config_entry(const char *var, const char *value, void *cb)
+int urlmatch_config_entry(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb)
 {
 	struct string_list_item *item;
 	struct urlmatch_config *collect = cb;
@@ -565,7 +566,7 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
 
 	if (!skip_prefix(var, collect->section, &key) || *(key++) != '.') {
 		if (collect->cascade_fn)
-			return collect->cascade_fn(var, value, cb);
+			return collect->cascade_fn(var, value, ctx, cb);
 		return 0; /* not interested */
 	}
 	dot = strrchr(key, '.');
@@ -609,7 +610,7 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
 	strbuf_addstr(&synthkey, collect->section);
 	strbuf_addch(&synthkey, '.');
 	strbuf_addstr(&synthkey, key);
-	retval = collect->collect_fn(synthkey.buf, value, collect->cb);
+	retval = collect->collect_fn(synthkey.buf, value, ctx, collect->cb);
 
 	strbuf_release(&synthkey);
 	return retval;
urlmatch.h +2 -1
index bee374a642c..5ba85cea139 100644
@@ -71,7 +71,8 @@ struct urlmatch_config {
 	.vars = STRING_LIST_INIT_DUP, \
 }
 
-int urlmatch_config_entry(const char *var, const char *value, void *cb);
+int urlmatch_config_entry(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb);
 void urlmatch_config_release(struct urlmatch_config *config);
 
 #endif /* URL_MATCH_H */
xdiff-interface.c +3 -2
index 0460e03f5ed..dcbb5e09857 100644
@@ -307,7 +307,8 @@ int xdiff_compare_lines(const char *l1, long s1,
 
 int git_xmerge_style = -1;
 
-int git_xmerge_config(const char *var, const char *value, void *cb)
+int git_xmerge_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "merge.conflictstyle")) {
 		if (!value)
@@ -327,5 +328,5 @@ int git_xmerge_config(const char *var, const char *value, void *cb)
 			    value, var);
 		return 0;
 	}
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
xdiff-interface.h +3 -1
index 733c364d26c..e6f80df0462 100644
@@ -50,7 +50,9 @@ int buffer_is_binary(const char *ptr, unsigned long size);
 
 void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
 void xdiff_clear_find_func(xdemitconf_t *xecfg);
-int git_xmerge_config(const char *var, const char *value, void *cb);
+struct config_context;
+int git_xmerge_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb);
 extern int git_xmerge_style;
 
 /*
Signature · 1 line
gitgitgadget
#90 lore raw

PATCH v4 4/12 config.c: pass ctx in configsets

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Pass config_context to config callbacks in configset_iter(), trivially setting the .kvi member to the cached key_value_info. Then, in config callbacks that are only used with configsets, use the .kvi member to replace calls to current_config_*(), and delete current_config_line() because it has no remaining callers.

This leaves builtin/config.c and config.c as the only remaining users of current_config_*().

---
 builtin/remote.c       | 10 ++++++----
 config.c               | 35 ++++++++++++++++-------------------
 config.h               |  2 +-
 remote.c               |  7 ++++---
 t/helper/test-config.c | 11 ++++++-----
 5 files changed, 33 insertions(+), 32 deletions(-)
builtin/remote.c +6 -4
index 87de81105e2..d47f9ee21cf 100644
@@ -646,17 +646,19 @@ struct push_default_info
 };
 
 static int config_read_push_default(const char *key, const char *value,
-	const struct config_context *ctx UNUSED, void *cb)
+	const struct config_context *ctx, void *cb)
 {
+	const struct key_value_info *kvi = ctx->kvi;
+
 	struct push_default_info* info = cb;
 	if (strcmp(key, "remote.pushdefault") ||
 	    !value || strcmp(value, info->old_name))
 		return 0;
 
-	info->scope = current_config_scope();
+	info->scope = kvi->scope;
 	strbuf_reset(&info->origin);
-	strbuf_addstr(&info->origin, current_config_name());
-	info->linenr = current_config_line();
+	strbuf_addstr(&info->origin, config_origin_type_name(kvi->origin_type));
+	info->linenr = kvi->linenr;
 
 	return 0;
 }
config.c +16 -19
index 850e432e301..662d406ac1e 100644
@@ -2317,6 +2317,7 @@ static void configset_iter(struct config_reader *reader, struct config_set *set,
 	struct string_list *values;
 	struct config_set_element *entry;
 	struct configset_list *list = &set->list;
+	struct config_context ctx = CONFIG_CONTEXT_INIT;
 
 	for (i = 0; i < list->nr; i++) {
 		entry = list->items[i].e;
@@ -2324,12 +2325,11 @@ static void configset_iter(struct config_reader *reader, struct config_set *set,
 		values = &entry->value_list;
 
 		config_reader_set_kvi(reader, values->items[value_index].util);
-
-		if (fn(entry->key, values->items[value_index].string, NULL, data) < 0)
+		ctx.kvi = values->items[value_index].util;
+		if (fn(entry->key, values->items[value_index].string, &ctx, data) < 0)
 			git_die_config_linenr(entry->key,
-					      reader->config_kvi->filename,
-					      reader->config_kvi->linenr);
-
+					      ctx.kvi->filename,
+					      ctx.kvi->linenr);
 		config_reader_set_kvi(reader, NULL);
 	}
 }
@@ -3984,13 +3984,8 @@ static int reader_origin_type(struct config_reader *reader,
 	return 0;
 }
 
-const char *current_config_origin_type(void)
+const char *config_origin_type_name(enum config_origin_type type)
 {
-	enum config_origin_type type = CONFIG_ORIGIN_UNKNOWN;
-
-	if (reader_origin_type(&the_reader, &type))
-		BUG("current_config_origin_type called outside config callback");
-
 	switch (type) {
 	case CONFIG_ORIGIN_BLOB:
 		return "blob";
@@ -4007,6 +4002,16 @@ const char *current_config_origin_type(void)
 	}
 }
 
+const char *current_config_origin_type(void)
+{
+	enum config_origin_type type = CONFIG_ORIGIN_UNKNOWN;
+
+	if (reader_origin_type(&the_reader, &type))
+		BUG("current_config_origin_type called outside config callback");
+
+	return config_origin_type_name(type);
+}
+
 const char *config_scope_name(enum config_scope scope)
 {
 	switch (scope) {
@@ -4054,14 +4059,6 @@ enum config_scope current_config_scope(void)
 		return the_reader.parsing_scope;
 }
 
-int current_config_line(void)
-{
-	if (the_reader.config_kvi)
-		return the_reader.config_kvi->linenr;
-	else
-		return the_reader.source->linenr;
-}
-
 int lookup_config(const char **mapping, int nr_mapping, const char *var)
 {
 	int i;
config.h +1 -1
index cd30125a8a4..ddf147bb2d1 100644
@@ -387,7 +387,7 @@ int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 enum config_scope current_config_scope(void);
 const char *current_config_origin_type(void);
 const char *current_config_name(void);
-int current_config_line(void);
+const char *config_origin_type_name(enum config_origin_type type);
 
 /*
  * Match and parse a config key of the form:
remote.c +4 -3
index 241999c2842..1dab860141b 100644
@@ -350,7 +350,7 @@ static void read_branches_file(struct remote_state *remote_state,
 }
 
 static int handle_config(const char *key, const char *value,
-			 const struct config_context *ctx UNUSED, void *cb)
+			 const struct config_context *ctx, void *cb)
 {
 	const char *name;
 	size_t namelen;
@@ -358,6 +358,7 @@ static int handle_config(const char *key, const char *value,
 	struct remote *remote;
 	struct branch *branch;
 	struct remote_state *remote_state = cb;
+	const struct key_value_info *kvi = ctx->kvi;
 
 	if (parse_config_key(key, "branch", &name, &namelen, &subkey) >= 0) {
 		/* There is no subsection. */
@@ -415,8 +416,8 @@ static int handle_config(const char *key, const char *value,
 	}
 	remote = make_remote(remote_state, name, namelen);
 	remote->origin = REMOTE_CONFIG;
-	if (current_config_scope() == CONFIG_SCOPE_LOCAL ||
-	    current_config_scope() == CONFIG_SCOPE_WORKTREE)
+	if (kvi->scope == CONFIG_SCOPE_LOCAL ||
+	    kvi->scope == CONFIG_SCOPE_WORKTREE)
 		remote->configured_in_repo = 1;
 	if (!strcmp(subkey, "mirror"))
 		remote->mirror = git_config_bool(key, value);
t/helper/test-config.c +6 -5
index 85ad815358e..3f4c3678318 100644
@@ -43,9 +43,10 @@
  */
 
 static int iterate_cb(const char *var, const char *value,
-		      const struct config_context *ctx UNUSED,
+		      const struct config_context *ctx,
 		      void *data UNUSED)
 {
+	const struct key_value_info *kvi = ctx->kvi;
 	static int nr;
 
 	if (nr++)
@@ -53,10 +54,10 @@ static int iterate_cb(const char *var, const char *value,
 
 	printf("key=%s\n", var);
 	printf("value=%s\n", value ? value : "(null)");
-	printf("origin=%s\n", current_config_origin_type());
-	printf("name=%s\n", current_config_name());
-	printf("lno=%d\n", current_config_line());
-	printf("scope=%s\n", config_scope_name(current_config_scope()));
+	printf("origin=%s\n", config_origin_type_name(kvi->origin_type));
+	printf("name=%s\n", kvi->filename ? kvi->filename : "");
+	printf("lno=%d\n", kvi->linenr);
+	printf("scope=%s\n", config_scope_name(kvi->scope));
 
 	return 0;
 }
Signature · 1 line
gitgitgadget
#91 lore raw

PATCH v4 5/12 config: pass ctx with config files

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Pass config_context to config_callbacks when parsing config files. To provide the .kvi member, refactor out the configset logic that caches "struct config_source" and "enum config_scope" as a "struct key_value_info". Make the "enum config_scope" available to the config file machinery by plumbing an additional arg through git_config_from_file_with_options().

We do not exercise ctx yet because the remaining current_config_*() callers may be used with config_with_options(), which may read config from parameters, but parameters don't pass ctx yet.

---
 bundle-uri.c       |   1 +
 config.c           | 105 ++++++++++++++++++++++++++++++---------------
 config.h           |   8 ++--
 fsck.c             |   3 +-
 submodule-config.c |   5 ++-
 5 files changed, 81 insertions(+), 41 deletions(-)
bundle-uri.c +1 -0
index 0d5acc3dc51..64f32387745 100644
@@ -255,6 +255,7 @@ int bundle_uri_parse_config_format(const char *uri,
 	}
 	result = git_config_from_file_with_options(config_to_bundle_list,
 						   filename, list,
+						   CONFIG_SCOPE_UNKNOWN,
 						   &opts);
 
 	if (!result && list->mode == BUNDLE_MODE_NONE) {
config.c +70 -35

277 lines not on this page · raw message

config.h +5 -3
index ddf147bb2d1..206bf1f175a 100644
@@ -169,16 +169,18 @@ int git_default_config(const char *, const char *,
 int git_config_from_file(config_fn_t fn, const char *, void *);
 
 int git_config_from_file_with_options(config_fn_t fn, const char *,
-				      void *,
+				      void *, enum config_scope,
 				      const struct config_options *);
 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, const struct config_options *opts);
+			void *data, enum config_scope scope,
+			const struct config_options *opts);
 int git_config_from_blob_oid(config_fn_t fn, const char *name,
 			     struct repository *repo,
-			     const struct object_id *oid, void *data);
+			     const struct object_id *oid, void *data,
+			     enum config_scope scope);
 void git_config_push_parameter(const char *text);
 void git_config_push_env(const char *spec);
 int git_config_from_parameters(config_fn_t fn, void *data);
fsck.c +2 -1
index 55b6a694853..f92c216fb5c 100644
@@ -1238,7 +1238,8 @@ static int fsck_blob(const struct object_id *oid, const char *buf,
 		data.ret = 0;
 		config_opts.error_action = CONFIG_ERROR_SILENT;
 		if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB,
-					".gitmodules", buf, size, &data, &config_opts))
+					".gitmodules", buf, size, &data,
+					CONFIG_SCOPE_UNKNOWN, &config_opts))
 			data.ret |= report(options, oid, OBJ_BLOB,
 					FSCK_MSG_GITMODULES_PARSE,
 					"could not parse gitmodules blob");
submodule-config.c +3 -2
index a38d4d49731..3f25bd13674 100644
@@ -606,7 +606,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,
 	parameter.gitmodules_oid = &oid;
 	parameter.overwrite = 0;
 	git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
-			config, config_size, &parameter, NULL);
+			    config, config_size, &parameter, CONFIG_SCOPE_UNKNOWN, NULL);
 	strbuf_release(&rev);
 	free(config);
 
@@ -714,7 +714,8 @@ void gitmodules_config_oid(const struct object_id *commit_oid)
 
 	if (gitmodule_oid_from_commit(commit_oid, &oid, &rev)) {
 		git_config_from_blob_oid(gitmodules_cb, rev.buf,
-					 the_repository, &oid, the_repository);
+					 the_repository, &oid, the_repository,
+					 CONFIG_SCOPE_UNKNOWN);
 	}
 	strbuf_release(&rev);
 
Signature · 1 line
gitgitgadget
#92 lore raw

PATCH v4 6/12 builtin/config.c: test misuse of format_config()

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

current_config_*() functions aren't meant to be called outside of config callbacks because they read state that is only set when iterating through config. However, several sites in builtin/config.c are indirectly calling these functions outside of config callbacks thanks to the format_config() helper. Show the current, bad behavior via tests so that the fixes in a subsequent commit will be clearer.

The misbehaving cases are:

* "git config --get-urlmatch --show-scope" results in an "unknown" scope, where it arguably should show the config file's scope. It's clear that this wasn't intended, though: we knew that "--get-urlmatch" couldn't show config source metadata, which is why "--show-origin" was marked incompatible with "--get-urlmatch" when it was introduced [1]. It was most likely a mistake that we allowed "--show-scope" to sneak through.

* Similarly, "git config --default" doesn't set config source metadata , so "--show-scope" also results in "unknown", and "--show-origin" results in a BUG().

[1] https://lore.kernel.org/git/[email protected]/

---
 t/t1300-config.sh | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)
t/t1300-config.sh +25 -0
index 86bfbc2b364..fa6a8df2521 100755
@@ -1668,6 +1668,21 @@ test_expect_success 'urlmatch' '
 	test_cmp expect actual
 '
 
+test_expect_success 'urlmatch with --show-scope' '
+	cat >.git/config <<-\EOF &&
+	[http "https://weak.example.com"]
+		sslVerify = false
+		cookieFile = /tmp/cookie.txt
+	EOF
+
+	cat >expect <<-EOF &&
+	unknown	http.cookiefile /tmp/cookie.txt
+	unknown	http.sslverify false
+	EOF
+	git config --get-urlmatch --show-scope 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/"]
@@ -2055,6 +2070,10 @@ test_expect_success '--show-origin blob ref' '
 	test_cmp expect output
 '
 
+test_expect_success '--show-origin with --default' '
+	test_must_fail git config --show-origin --default foo some.key
+'
+
 test_expect_success '--show-scope with --list' '
 	cat >expect <<-EOF &&
 	global	user.global=true
@@ -2123,6 +2142,12 @@ test_expect_success '--show-scope with --show-origin' '
 	test_cmp expect output
 '
 
+test_expect_success '--show-scope with --default' '
+	git config --show-scope --default foo some.key >actual &&
+	echo "unknown	foo" >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success 'override global and system config' '
 	test_when_finished rm -f \"\$HOME\"/.gitconfig &&
 	cat >"$HOME"/.gitconfig <<-EOF &&
Signature · 1 line
gitgitgadget
#93 lore raw

PATCH v4 7/12 config.c: pass ctx with CLI config

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Pass config_context when parsing CLI config. To provide the .kvi member, refactor out kvi_from_param() from the logic that caches CLI config in configsets. Now that config_context and config_context.kvi is always present when config machinery calls config callbacks, plumb "kvi" so that we can remove all calls of current_config_scope() except for trace2/*.c (which will be handled in a later commit), and remove all other current_config_*() (the functions themselves and their calls). Note that this results in .kvi containing a different, more complete set of information than the mocked up "struct config_source" in git_config_from_parameters().

Plumbing "kvi" reveals a few places where we've been doing the wrong thing:

* git_config_parse_parameter() hasn't been setting config source information, so plumb "kvi" there too.

* "git config --get-urlmatch --show-scope" iterates config to collect values, but then attempts to display the scope after config iteration. Fix this by copying the "kvi" value in the collection phase so that it can be read back later. This means that we can now support "git config --get-urlmatch --show-origin" (we don't allow this combination of args because of this bug), but that is left unchanged for now.

* "git config --default" doesn't have config source metadata when displaying the default value. Fix this by treating the default value as if it came from the command line (e.g. like we do with "git -c" or "git config --file"), using kvi_from_param().

---
 builtin/config.c  | 47 +++++++++++++++++----------
 config.c          | 82 +++++++++++++++++++++++------------------------
 config.h          |  3 +-
 t/t1300-config.sh | 10 +++---
 4 files changed, 78 insertions(+), 64 deletions(-)
builtin/config.c +30 -17
index f4fccf99cb8..9b9f5527311 100644
@@ -194,38 +194,42 @@ static void check_argc(int argc, int min, int max)
 	usage_builtin_config();
 }
 
-static void show_config_origin(struct strbuf *buf)
+static void show_config_origin(const struct key_value_info *kvi,
+			       struct strbuf *buf)
 {
 	const char term = end_nul ? '\0' : '\t';
 
-	strbuf_addstr(buf, current_config_origin_type());
+	strbuf_addstr(buf, config_origin_type_name(kvi->origin_type));
 	strbuf_addch(buf, ':');
 	if (end_nul)
-		strbuf_addstr(buf, current_config_name());
+		strbuf_addstr(buf, kvi->filename ? kvi->filename : "");
 	else
-		quote_c_style(current_config_name(), buf, NULL, 0);
+		quote_c_style(kvi->filename ? kvi->filename : "", buf, NULL, 0);
 	strbuf_addch(buf, term);
 }
 
-static void show_config_scope(struct strbuf *buf)
+static void show_config_scope(const struct key_value_info *kvi,
+			      struct strbuf *buf)
 {
 	const char term = end_nul ? '\0' : '\t';
-	const char *scope = config_scope_name(current_config_scope());
+	const char *scope = config_scope_name(kvi->scope);
 
 	strbuf_addstr(buf, N_(scope));
 	strbuf_addch(buf, term);
 }
 
 static int show_all_config(const char *key_, const char *value_,
-			   const struct config_context *ctx UNUSED,
+			   const struct config_context *ctx,
 			   void *cb UNUSED)
 {
+	const struct key_value_info *kvi = ctx->kvi;
+
 	if (show_origin || show_scope) {
 		struct strbuf buf = STRBUF_INIT;
 		if (show_scope)
-			show_config_scope(&buf);
+			show_config_scope(kvi, &buf);
 		if (show_origin)
-			show_config_origin(&buf);
+			show_config_origin(kvi, &buf);
 		/* Use fwrite as "buf" can contain \0's if "end_null" is set. */
 		fwrite(buf.buf, 1, buf.len, stdout);
 		strbuf_release(&buf);
@@ -243,12 +247,13 @@ struct strbuf_list {
 	int alloc;
 };
 
-static int format_config(struct strbuf *buf, const char *key_, const char *value_)
+static int format_config(struct strbuf *buf, const char *key_,
+			 const char *value_, const struct key_value_info *kvi)
 {
 	if (show_scope)
-		show_config_scope(buf);
+		show_config_scope(kvi, buf);
 	if (show_origin)
-		show_config_origin(buf);
+		show_config_origin(kvi, buf);
 	if (show_keys)
 		strbuf_addstr(buf, key_);
 	if (!omit_values) {
@@ -303,9 +308,10 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
 }
 
 static int collect_config(const char *key_, const char *value_,
-			  const struct config_context *ctx UNUSED, void *cb)
+			  const struct config_context *ctx, void *cb)
 {
 	struct strbuf_list *values = cb;
+	const struct key_value_info *kvi = ctx->kvi;
 
 	if (!use_key_regexp && strcmp(key_, key))
 		return 0;
@@ -320,7 +326,7 @@ static int collect_config(const char *key_, const char *value_,
 	ALLOC_GROW(values->items, values->nr + 1, values->alloc);
 	strbuf_init(&values->items[values->nr], 0);
 
-	return format_config(&values->items[values->nr++], key_, value_);
+	return format_config(&values->items[values->nr++], key_, value_, kvi);
 }
 
 static int get_value(const char *key_, const char *regex_, unsigned flags)
@@ -382,11 +388,14 @@ static int get_value(const char *key_, const char *regex_, unsigned flags)
 			    &config_options);
 
 	if (!values.nr && default_value) {
+		struct key_value_info kvi = KVI_INIT;
 		struct strbuf *item;
+
+		kvi_from_param(&kvi);
 		ALLOC_GROW(values.items, values.nr + 1, values.alloc);
 		item = &values.items[values.nr++];
 		strbuf_init(item, 0);
-		if (format_config(item, key_, default_value) < 0)
+		if (format_config(item, key_, default_value, &kvi) < 0)
 			die(_("failed to format default config value: %s"),
 				default_value);
 	}
@@ -563,15 +572,17 @@ static void check_write(void)
 struct urlmatch_current_candidate_value {
 	char value_is_null;
 	struct strbuf value;
+	struct key_value_info kvi;
 };
 
 static int urlmatch_collect_fn(const char *var, const char *value,
-			       const struct config_context *ctx UNUSED,
+			       const struct config_context *ctx,
 			       void *cb)
 {
 	struct string_list *values = cb;
 	struct string_list_item *item = string_list_insert(values, var);
 	struct urlmatch_current_candidate_value *matched = item->util;
+	const struct key_value_info *kvi = ctx->kvi;
 
 	if (!matched) {
 		matched = xmalloc(sizeof(*matched));
@@ -580,6 +591,7 @@ static int urlmatch_collect_fn(const char *var, const char *value,
 	} else {
 		strbuf_reset(&matched->value);
 	}
+	matched->kvi = *kvi;
 
 	if (value) {
 		strbuf_addstr(&matched->value, value);
@@ -627,7 +639,8 @@ static int get_urlmatch(const char *var, const char *url)
 		struct strbuf buf = STRBUF_INIT;
 
 		format_config(&buf, item->string,
-			      matched->value_is_null ? NULL : matched->value.buf);
+			      matched->value_is_null ? NULL : matched->value.buf,
+			      &matched->kvi);
 		fwrite(buf.buf, 1, buf.len, stdout);
 		strbuf_release(&buf);
 
config.c +41 -41

204 lines not on this page · raw message

config.h +1 -2
index 206bf1f175a..ea92392400e 100644
@@ -387,9 +387,8 @@ void git_global_config(char **user, char **xdg);
 int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 
 enum config_scope current_config_scope(void);
-const char *current_config_origin_type(void);
-const char *current_config_name(void);
 const char *config_origin_type_name(enum config_origin_type type);
+void kvi_from_param(struct key_value_info *out);
 
 /*
  * Match and parse a config key of the form:
t/t1300-config.sh +6 -4
index fa6a8df2521..387d336c91f 100755
@@ -1676,8 +1676,8 @@ test_expect_success 'urlmatch with --show-scope' '
 	EOF
 
 	cat >expect <<-EOF &&
-	unknown	http.cookiefile /tmp/cookie.txt
-	unknown	http.sslverify false
+	local	http.cookiefile /tmp/cookie.txt
+	local	http.sslverify false
 	EOF
 	git config --get-urlmatch --show-scope HTTP https://weak.example.com >actual &&
 	test_cmp expect actual
@@ -2071,7 +2071,9 @@ test_expect_success '--show-origin blob ref' '
 '
 
 test_expect_success '--show-origin with --default' '
-	test_must_fail git config --show-origin --default foo some.key
+	git config --show-origin --default foo some.key >actual &&
+	echo "command line:	foo" >expect &&
+	test_cmp expect actual
 '
 
 test_expect_success '--show-scope with --list' '
@@ -2144,7 +2146,7 @@ test_expect_success '--show-scope with --show-origin' '
 
 test_expect_success '--show-scope with --default' '
 	git config --show-scope --default foo some.key >actual &&
-	echo "unknown	foo" >expect &&
+	echo "command	foo" >expect &&
 	test_cmp expect actual
 '
 
Signature · 1 line
gitgitgadget
#94 lore raw

PATCH v4 8/12 trace2: plumb config kvi

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

There is a code path starting from trace2_def_param_fl() that eventually calls current_config_scope(), and thus it needs to have "kvi" plumbed through it. Additional plumbing is also needed to get "kvi" to trace2_def_param_fl(), which gets called by two code paths:

- Through tr2_cfg_cb(), which is a config callback, so it trivially receives "kvi" via the "struct config_context ctx" parameter.

- Through tr2_list_env_vars_fl(), which is a high level function that lists environment variables for tracing. This has been secretly behaving like git_config_from_parameters() (in that it parses config from environment variables/the CLI), but does not set config source information.

Teach tr2_list_env_vars_fl() to be well-behaved by using kvi_from_param(), which is used elsewhere for CLI/environment variable-based config.

As a result, current_config_scope() has no more callers, so remove it.

---
 config.c                | 46 -----------------------------------------
 config.h                |  1 -
 trace2.c                |  4 ++--
 trace2.h                |  3 ++-
 trace2/tr2_cfg.c        |  9 +++++---
 trace2/tr2_tgt.h        |  4 +++-
 trace2/tr2_tgt_event.c  |  4 ++--
 trace2/tr2_tgt_normal.c |  4 ++--
 trace2/tr2_tgt_perf.c   |  4 ++--
 9 files changed, 19 insertions(+), 60 deletions(-)
config.c +0 -46
index 2e24e6ea515..8546fea370e 100644
@@ -85,16 +85,6 @@ struct config_reader {
 	 */
 	struct config_source *source;
 	struct key_value_info *config_kvi;
-	/*
-	 * The "scope" of the current config source being parsed (repo, global,
-	 * etc). Like "source", this is only set when parsing a config source.
-	 * It's not part of "source" because it transcends a single file (i.e.,
-	 * a file included from .git/config is still in "repo" scope).
-	 *
-	 * When iterating through a configset, the equivalent value is
-	 * "config_kvi.scope" (see above).
-	 */
-	enum config_scope parsing_scope;
 };
 /*
  * Where possible, prefer to accept "struct config_reader" as an arg than to use
@@ -125,19 +115,9 @@ static inline struct config_source *config_reader_pop_source(struct config_reade
 static inline void config_reader_set_kvi(struct config_reader *reader,
 					 struct key_value_info *kvi)
 {
-	if (kvi && (reader->source || reader->parsing_scope))
-		BUG("kvi should not be set while parsing a config source");
 	reader->config_kvi = kvi;
 }
 
-static inline void config_reader_set_scope(struct config_reader *reader,
-					   enum config_scope scope)
-{
-	if (scope && reader->config_kvi)
-		BUG("scope should only be set when iterating through a config source");
-	reader->parsing_scope = scope;
-}
-
 static int pack_compression_seen;
 static int zlib_compression_seen;
 
@@ -412,19 +392,13 @@ static void populate_remote_urls(struct config_include_data *inc)
 {
 	struct config_options opts;
 
-	enum config_scope store_scope = inc->config_reader->parsing_scope;
-
 	opts = *inc->opts;
 	opts.unconditional_remote_url = 1;
 
-	config_reader_set_scope(inc->config_reader, 0);
-
 	inc->remote_urls = xmalloc(sizeof(*inc->remote_urls));
 	string_list_init_dup(inc->remote_urls);
 	config_with_options(add_remote_url, inc->remote_urls,
 			    inc->config_source, inc->repo, &opts);
-
-	config_reader_set_scope(inc->config_reader, store_scope);
 }
 
 static int forbid_remote_url(const char *var, const char *value UNUSED,
@@ -2255,7 +2229,6 @@ static int do_git_config_sequence(struct config_reader *reader,
 	char *user_config = NULL;
 	char *repo_config;
 	char *worktree_config;
-	enum config_scope prev_parsing_scope = reader->parsing_scope;
 
 	/*
 	 * Ensure that either:
@@ -2273,7 +2246,6 @@ static int do_git_config_sequence(struct config_reader *reader,
 		worktree_config = NULL;
 	}
 
-	config_reader_set_scope(reader, CONFIG_SCOPE_SYSTEM);
 	if (git_config_system() && system_config &&
 	    !access_or_die(system_config, R_OK,
 			   opts->system_gently ? ACCESS_EACCES_OK : 0))
@@ -2281,7 +2253,6 @@ static int do_git_config_sequence(struct config_reader *reader,
 							 data, CONFIG_SCOPE_SYSTEM,
 							 NULL);
 
-	config_reader_set_scope(reader, CONFIG_SCOPE_GLOBAL);
 	git_global_config(&user_config, &xdg_config);
 
 	if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
@@ -2292,13 +2263,11 @@ static int do_git_config_sequence(struct config_reader *reader,
 		ret += git_config_from_file_with_options(fn, user_config, data,
 							 CONFIG_SCOPE_GLOBAL, NULL);
 
-	config_reader_set_scope(reader, CONFIG_SCOPE_LOCAL);
 	if (!opts->ignore_repo && repo_config &&
 	    !access_or_die(repo_config, R_OK, 0))
 		ret += git_config_from_file_with_options(fn, repo_config, data,
 							 CONFIG_SCOPE_LOCAL, NULL);
 
-	config_reader_set_scope(reader, CONFIG_SCOPE_WORKTREE);
 	if (!opts->ignore_worktree && worktree_config &&
 	    repo && repo->repository_format_worktree_config &&
 	    !access_or_die(worktree_config, R_OK, 0)) {
@@ -2307,11 +2276,9 @@ static int do_git_config_sequence(struct config_reader *reader,
 								 NULL);
 	}
 
-	config_reader_set_scope(reader, CONFIG_SCOPE_COMMAND);
 	if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0)
 		die(_("unable to parse command-line config"));
 
-	config_reader_set_scope(reader, prev_parsing_scope);
 	free(system_config);
 	free(xdg_config);
 	free(user_config);
@@ -2326,7 +2293,6 @@ int config_with_options(config_fn_t fn, void *data,
 			const struct config_options *opts)
 {
 	struct config_include_data inc = CONFIG_INCLUDE_INIT;
-	enum config_scope prev_scope = the_reader.parsing_scope;
 	int ret;
 
 	if (opts->respect_includes) {
@@ -2340,9 +2306,6 @@ int config_with_options(config_fn_t fn, void *data,
 		data = &inc;
 	}
 
-	if (config_source)
-		config_reader_set_scope(&the_reader, config_source->scope);
-
 	/*
 	 * If we have a specific filename, use it. Otherwise, follow the
 	 * regular lookup sequence.
@@ -2364,7 +2327,6 @@ int config_with_options(config_fn_t fn, void *data,
 		string_list_clear(inc.remote_urls, 0);
 		FREE_AND_NULL(inc.remote_urls);
 	}
-	config_reader_set_scope(&the_reader, prev_scope);
 	return ret;
 }
 
@@ -4086,14 +4048,6 @@ static int reader_config_name(struct config_reader *reader, const char **out)
 	return 0;
 }
 
-enum config_scope current_config_scope(void)
-{
-	if (the_reader.config_kvi)
-		return the_reader.config_kvi->scope;
-	else
-		return the_reader.parsing_scope;
-}
-
 int lookup_config(const char **mapping, int nr_mapping, const char *var)
 {
 	int i;
config.h +0 -1
index ea92392400e..82eeba94e71 100644
@@ -386,7 +386,6 @@ void git_global_config(char **user, char **xdg);
 
 int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 
-enum config_scope current_config_scope(void);
 const char *config_origin_type_name(enum config_origin_type type);
 void kvi_from_param(struct key_value_info *out);
 
trace2.c +2 -2
index 0efc4e7b958..49c23bfd05a 100644
@@ -634,7 +634,7 @@ void trace2_thread_exit_fl(const char *file, int line)
 }
 
 void trace2_def_param_fl(const char *file, int line, const char *param,
-			 const char *value)
+			 const char *value, const struct key_value_info *kvi)
 {
 	struct tr2_tgt *tgt_j;
 	int j;
@@ -644,7 +644,7 @@ void trace2_def_param_fl(const char *file, int line, const char *param,
 
 	for_each_wanted_builtin (j, tgt_j)
 		if (tgt_j->pfn_param_fl)
-			tgt_j->pfn_param_fl(file, line, param, value);
+			tgt_j->pfn_param_fl(file, line, param, value, kvi);
 }
 
 void trace2_def_repo_fl(const char *file, int line, struct repository *repo)
trace2.h +2 -1
index 4ced30c0db3..f5c5a9e6bac 100644
@@ -325,6 +325,7 @@ void trace2_thread_exit_fl(const char *file, int line);
 
 #define trace2_thread_exit() trace2_thread_exit_fl(__FILE__, __LINE__)
 
+struct key_value_info;
 /*
  * Emits a "def_param" message containing a key/value pair.
  *
@@ -334,7 +335,7 @@ void trace2_thread_exit_fl(const char *file, int line);
  * `core.abbrev`, `status.showUntrackedFiles`, or `--no-ahead-behind`.
  */
 void trace2_def_param_fl(const char *file, int line, const char *param,
-			 const char *value);
+			 const char *value, const struct key_value_info *kvi);
 
 #define trace2_def_param(param, value) \
 	trace2_def_param_fl(__FILE__, __LINE__, (param), (value))
trace2/tr2_cfg.c +6 -3
index 83bc4fd109c..733d9d2872a 100644
@@ -100,7 +100,7 @@ struct tr2_cfg_data {
  * See if the given config key matches any of our patterns of interest.
  */
 static int tr2_cfg_cb(const char *key, const char *value,
-		      const struct config_context *ctx UNUSED, void *d)
+		      const struct config_context *ctx, void *d)
 {
 	struct strbuf **s;
 	struct tr2_cfg_data *data = (struct tr2_cfg_data *)d;
@@ -109,7 +109,8 @@ static int tr2_cfg_cb(const char *key, const char *value,
 		struct strbuf *buf = *s;
 		int wm = wildmatch(buf->buf, key, WM_CASEFOLD);
 		if (wm == WM_MATCH) {
-			trace2_def_param_fl(data->file, data->line, key, value);
+			trace2_def_param_fl(data->file, data->line, key, value,
+					    ctx->kvi);
 			return 0;
 		}
 	}
@@ -127,8 +128,10 @@ void tr2_cfg_list_config_fl(const char *file, int line)
 
 void tr2_list_env_vars_fl(const char *file, int line)
 {
+	struct key_value_info kvi = KVI_INIT;
 	struct strbuf **s;
 
+	kvi_from_param(&kvi);
 	if (tr2_load_env_vars() <= 0)
 		return;
 
@@ -136,7 +139,7 @@ void tr2_list_env_vars_fl(const char *file, int line)
 		struct strbuf *buf = *s;
 		const char *val = getenv(buf->buf);
 		if (val && *val)
-			trace2_def_param_fl(file, line, buf->buf, val);
+			trace2_def_param_fl(file, line, buf->buf, val, &kvi);
 	}
 }
 
trace2/tr2_tgt.h +3 -1
index bf8745c4f05..1f626cffea0 100644
@@ -69,8 +69,10 @@ typedef void(tr2_tgt_evt_exec_result_fl_t)(const char *file, int line,
 					   uint64_t us_elapsed_absolute,
 					   int exec_id, int code);
 
+struct key_value_info;
 typedef void(tr2_tgt_evt_param_fl_t)(const char *file, int line,
-				     const char *param, const char *value);
+				     const char *param, const char *value,
+				     const struct key_value_info *kvi);
 
 typedef void(tr2_tgt_evt_repo_fl_t)(const char *file, int line,
 				    const struct repository *repo);
trace2/tr2_tgt_event.c +2 -2
index 2af53e5d4de..53091781eca 100644
@@ -477,11 +477,11 @@ static void fn_exec_result_fl(const char *file, int line,
 }
 
 static void fn_param_fl(const char *file, int line, const char *param,
-			const char *value)
+			const char *value, const struct key_value_info *kvi)
 {
 	const char *event_name = "def_param";
 	struct json_writer jw = JSON_WRITER_INIT;
-	enum config_scope scope = current_config_scope();
+	enum config_scope scope = kvi->scope;
 	const char *scope_name = config_scope_name(scope);
 
 	jw_object_begin(&jw, 0);
trace2/tr2_tgt_normal.c +2 -2
index 1ebfb464d54..d25ea131643 100644
@@ -297,10 +297,10 @@ static void fn_exec_result_fl(const char *file, int line,
 }
 
 static void fn_param_fl(const char *file, int line, const char *param,
-			const char *value)
+			const char *value, const struct key_value_info *kvi)
 {
 	struct strbuf buf_payload = STRBUF_INIT;
-	enum config_scope scope = current_config_scope();
+	enum config_scope scope = kvi->scope;
 	const char *scope_name = config_scope_name(scope);
 
 	strbuf_addf(&buf_payload, "def_param scope:%s %s=%s", scope_name, param,
trace2/tr2_tgt_perf.c +2 -2
index 328e483a05e..a6f9a8a193e 100644
@@ -439,12 +439,12 @@ static void fn_exec_result_fl(const char *file, int line,
 }
 
 static void fn_param_fl(const char *file, int line, const char *param,
-			const char *value)
+			const char *value, const struct key_value_info *kvi)
 {
 	const char *event_name = "def_param";
 	struct strbuf buf_payload = STRBUF_INIT;
 	struct strbuf scope_payload = STRBUF_INIT;
-	enum config_scope scope = current_config_scope();
+	enum config_scope scope = kvi->scope;
 	const char *scope_name = config_scope_name(scope);
 
 	strbuf_addf(&buf_payload, "%s:%s", param, value);
Signature · 1 line
gitgitgadget
#95 lore raw

PATCH v4 9/12 config: pass kvi to die_bad_number()

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Plumb "struct key_value_info" through all code paths that end in die_bad_number(), which lets us remove the helper functions that read analogous values from "struct config_reader". As a result, nothing reads config_reader.config_kvi any more, so remove that too.

In config.c, this requires changing the signature of git_configset_get_value() to 'return' "kvi" in an out parameter so that git_configset_get_<type>() can pass it to git_config_<type>(). Only numeric types will use "kvi", so for non-numeric types (e.g. git_configset_get_string()), pass NULL to indicate that the out parameter isn't needed.

Outside of config.c, config callbacks now need to pass "ctx->kvi" to any of the git_config_<type>() functions that parse a config string into a number type. Included is a .cocci patch to make that refactor.

The only exceptional case is builtin/config.c, where git_config_<type>() is called outside of a config callback (namely, on user-provided input), so config source information has never been available. In this case, die_bad_number() defaults to a generic, but perfectly descriptive message. Let's provide a safe, non-NULL for "kvi" anyway, but make sure not to change the message.

---
 archive-tar.c                              |   4 +-
 builtin/commit-graph.c                     |   4 +-
 builtin/commit.c                           |  10 +-
 builtin/config.c                           |  21 +--
 builtin/fetch.c                            |   4 +-
 builtin/fsmonitor--daemon.c                |   6 +-
 builtin/grep.c                             |   2 +-
 builtin/index-pack.c                       |   4 +-
 builtin/log.c                              |   2 +-
 builtin/pack-objects.c                     |  14 +-
 builtin/receive-pack.c                     |  10 +-
 builtin/submodule--helper.c                |   4 +-
 config.c                                   | 156 ++++++++-------------
 config.h                                   |  17 ++-
 contrib/coccinelle/git_config_number.cocci |  27 ++++
 diff.c                                     |   9 +-
 fmt-merge-msg.c                            |   2 +-
 help.c                                     |   4 +-
 http.c                                     |  10 +-
 imap-send.c                                |   2 +-
 sequencer.c                                |  22 +--
 setup.c                                    |   2 +-
 submodule-config.c                         |  13 +-
 submodule-config.h                         |   3 +-
 t/helper/test-config.c                     |   6 +-
 upload-pack.c                              |  12 +-
 worktree.c                                 |   2 +-
 27 files changed, 190 insertions(+), 182 deletions(-)
 create mode 100644 contrib/coccinelle/git_config_number.cocci
archive-tar.c +2 -2
index ef06e516b1f..3df8af6d1b1 100644
@@ -412,14 +412,14 @@ static int tar_filter_config(const char *var, const char *value,
 }
 
 static int git_tar_config(const char *var, const char *value,
-			  const struct config_context *ctx UNUSED, void *cb)
+			  const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "tar.umask")) {
 		if (value && !strcmp(value, "user")) {
 			tar_umask = umask(0);
 			umask(tar_umask);
 		} else {
-			tar_umask = git_config_int(var, value);
+			tar_umask = git_config_int(var, value, ctx->kvi);
 		}
 		return 0;
 	}
builtin/commit-graph.c +2 -2
index 1185c49239a..b071c5ab646 100644
@@ -186,11 +186,11 @@ static int write_option_max_new_filters(const struct option *opt,
 }
 
 static int git_commit_graph_write_config(const char *var, const char *value,
-					 const struct config_context *ctx UNUSED,
+					 const struct config_context *ctx,
 					 void *cb UNUSED)
 {
 	if (!strcmp(var, "commitgraph.maxnewfilters"))
-		write_opts.max_new_filters = git_config_int(var, value);
+		write_opts.max_new_filters = git_config_int(var, value, ctx->kvi);
 	/*
 	 * No need to fall-back to 'git_default_config', since this was already
 	 * called in 'cmd_commit_graph()'.
builtin/commit.c +6 -4
index 6a2b2503328..9fe691470a3 100644
@@ -1415,7 +1415,8 @@ static int git_status_config(const char *k, const char *v,
 		return git_column_config(k, v, "status", &s->colopts);
 	if (!strcmp(k, "status.submodulesummary")) {
 		int is_bool;
-		s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
+		s->submodule_summary = git_config_bool_or_int(k, v, ctx->kvi,
+							      &is_bool);
 		if (is_bool && s->submodule_summary)
 			s->submodule_summary = -1;
 		return 0;
@@ -1475,11 +1476,11 @@ static int git_status_config(const char *k, const char *v,
 	}
 	if (!strcmp(k, "diff.renamelimit")) {
 		if (s->rename_limit == -1)
-			s->rename_limit = git_config_int(k, v);
+			s->rename_limit = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "status.renamelimit")) {
-		s->rename_limit = git_config_int(k, v);
+		s->rename_limit = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "diff.renames")) {
@@ -1625,7 +1626,8 @@ static int git_commit_config(const char *k, const char *v,
 	}
 	if (!strcmp(k, "commit.verbose")) {
 		int is_bool;
-		config_commit_verbose = git_config_bool_or_int(k, v, &is_bool);
+		config_commit_verbose = git_config_bool_or_int(k, v, ctx->kvi,
+							       &is_bool);
 		return 0;
 	}
 
builtin/config.c +12 -9
index 9b9f5527311..680269d263c 100644
@@ -262,13 +262,14 @@ static int format_config(struct strbuf *buf, const char *key_,
 
 		if (type == TYPE_INT)
 			strbuf_addf(buf, "%"PRId64,
-				    git_config_int64(key_, value_ ? value_ : ""));
+				    git_config_int64(key_, value_ ? value_ : "", kvi));
 		else if (type == TYPE_BOOL)
 			strbuf_addstr(buf, git_config_bool(key_, value_) ?
 				      "true" : "false");
 		else if (type == TYPE_BOOL_OR_INT) {
 			int is_bool, v;
-			v = git_config_bool_or_int(key_, value_, &is_bool);
+			v = git_config_bool_or_int(key_, value_, kvi,
+						   &is_bool);
 			if (is_bool)
 				strbuf_addstr(buf, v ? "true" : "false");
 			else
@@ -424,7 +425,8 @@ free_strings:
 	return ret;
 }
 
-static char *normalize_value(const char *key, const char *value)
+static char *normalize_value(const char *key, const char *value,
+			     struct key_value_info *kvi)
 {
 	if (!value)
 		return NULL;
@@ -439,12 +441,12 @@ static char *normalize_value(const char *key, const char *value)
 		 */
 		return xstrdup(value);
 	if (type == TYPE_INT)
-		return xstrfmt("%"PRId64, git_config_int64(key, value));
+		return xstrfmt("%"PRId64, git_config_int64(key, value, kvi));
 	if (type == TYPE_BOOL)
 		return xstrdup(git_config_bool(key, value) ?  "true" : "false");
 	if (type == TYPE_BOOL_OR_INT) {
 		int is_bool, v;
-		v = git_config_bool_or_int(key, value, &is_bool);
+		v = git_config_bool_or_int(key, value, kvi, &is_bool);
 		if (!is_bool)
 			return xstrfmt("%d", v);
 		else
@@ -674,6 +676,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 	char *value = NULL;
 	int flags = 0;
 	int ret = 0;
+	struct key_value_info default_kvi = KVI_INIT;
 
 	given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
 
@@ -891,7 +894,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 	else if (actions == ACTION_SET) {
 		check_write();
 		check_argc(argc, 2, 2);
-		value = normalize_value(argv[0], argv[1]);
+		value = normalize_value(argv[0], argv[1], &default_kvi);
 		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"
@@ -900,7 +903,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 	else if (actions == ACTION_SET_ALL) {
 		check_write();
 		check_argc(argc, 2, 3);
-		value = normalize_value(argv[0], argv[1]);
+		value = normalize_value(argv[0], argv[1], &default_kvi);
 		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
 							     argv[0], value, argv[2],
 							     flags);
@@ -908,7 +911,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 	else if (actions == ACTION_ADD) {
 		check_write();
 		check_argc(argc, 2, 2);
-		value = normalize_value(argv[0], argv[1]);
+		value = normalize_value(argv[0], argv[1], &default_kvi);
 		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
 							     argv[0], value,
 							     CONFIG_REGEX_NONE,
@@ -917,7 +920,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 	else if (actions == ACTION_REPLACE_ALL) {
 		check_write();
 		check_argc(argc, 2, 3);
-		value = normalize_value(argv[0], argv[1]);
+		value = normalize_value(argv[0], argv[1], &default_kvi);
 		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
 							     argv[0], value, argv[2],
 							     flags | CONFIG_FLAGS_MULTI_REPLACE);
builtin/fetch.c +2 -2
index a4aa0fbb8f5..fe86dfeb62a 100644
@@ -137,7 +137,7 @@ static int git_fetch_config(const char *k, const char *v,
 	}
 
 	if (!strcmp(k, "submodule.fetchjobs")) {
-		fetch_config->submodule_fetch_jobs = parse_submodule_fetchjobs(k, v);
+		fetch_config->submodule_fetch_jobs = parse_submodule_fetchjobs(k, v, ctx->kvi);
 		return 0;
 	} else if (!strcmp(k, "fetch.recursesubmodules")) {
 		fetch_config->recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
@@ -145,7 +145,7 @@ static int git_fetch_config(const char *k, const char *v,
 	}
 
 	if (!strcmp(k, "fetch.parallel")) {
-		fetch_config->parallel = git_config_int(k, v);
+		fetch_config->parallel = git_config_int(k, v, ctx->kvi);
 		if (fetch_config->parallel < 0)
 			die(_("fetch.parallel cannot be negative"));
 		if (!fetch_config->parallel)
builtin/fsmonitor--daemon.c +3 -3
index 91a776e2f17..6d2826b07da 100644
@@ -41,7 +41,7 @@ static int fsmonitor_config(const char *var, const char *value,
 			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, FSMONITOR__IPC_THREADS)) {
-		int i = git_config_int(var, value);
+		int i = git_config_int(var, value, ctx->kvi);
 		if (i < 1)
 			return error(_("value of '%s' out of range: %d"),
 				     FSMONITOR__IPC_THREADS, i);
@@ -50,7 +50,7 @@ static int fsmonitor_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, FSMONITOR__START_TIMEOUT)) {
-		int i = git_config_int(var, value);
+		int i = git_config_int(var, value, ctx->kvi);
 		if (i < 0)
 			return error(_("value of '%s' out of range: %d"),
 				     FSMONITOR__START_TIMEOUT, i);
@@ -60,7 +60,7 @@ static int fsmonitor_config(const char *var, const char *value,
 
 	if (!strcmp(var, FSMONITOR__ANNOUNCE_STARTUP)) {
 		int is_bool;
-		int i = git_config_bool_or_int(var, value, &is_bool);
+		int i = git_config_bool_or_int(var, value, ctx->kvi, &is_bool);
 		if (i < 0)
 			return error(_("value of '%s' not bool or int: %d"),
 				     var, i);
builtin/grep.c +1 -1
index 757d52b94ec..3a464e6faab 100644
@@ -301,7 +301,7 @@ static int grep_cmd_config(const char *var, const char *value,
 		st = -1;
 
 	if (!strcmp(var, "grep.threads")) {
-		num_threads = git_config_int(var, value);
+		num_threads = git_config_int(var, value, ctx->kvi);
 		if (num_threads < 0)
 			die(_("invalid number of threads specified (%d) for %s"),
 			    num_threads, var);
builtin/index-pack.c +2 -2
index de8884ea5c2..e428f6d9a4a 100644
@@ -1587,13 +1587,13 @@ static int git_index_pack_config(const char *k, const char *v,
 	struct pack_idx_option *opts = cb;
 
 	if (!strcmp(k, "pack.indexversion")) {
-		opts->version = git_config_int(k, v);
+		opts->version = git_config_int(k, v, ctx->kvi);
 		if (opts->version > 2)
 			die(_("bad pack.indexVersion=%"PRIu32), opts->version);
 		return 0;
 	}
 	if (!strcmp(k, "pack.threads")) {
-		nr_threads = git_config_int(k, v);
+		nr_threads = git_config_int(k, v, ctx->kvi);
 		if (nr_threads < 0)
 			die(_("invalid number of threads specified (%d)"),
 			    nr_threads);
builtin/log.c +1 -1
index 09d6a13075b..c7337354aaf 100644
@@ -574,7 +574,7 @@ static int git_log_config(const char *var, const char *value,
 	if (!strcmp(var, "format.subjectprefix"))
 		return git_config_string(&fmt_patch_subject_prefix, var, value);
 	if (!strcmp(var, "format.filenamemaxlength")) {
-		fmt_patch_name_max = git_config_int(var, value);
+		fmt_patch_name_max = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(var, "format.encodeemailheaders")) {
builtin/pack-objects.c +7 -7
index 34aa0b483a0..38054a38b2b 100644
@@ -3139,23 +3139,23 @@ static int git_pack_config(const char *k, const char *v,
 			   const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(k, "pack.window")) {
-		window = git_config_int(k, v);
+		window = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.windowmemory")) {
-		window_memory_limit = git_config_ulong(k, v);
+		window_memory_limit = git_config_ulong(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.depth")) {
-		depth = git_config_int(k, v);
+		depth = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.deltacachesize")) {
-		max_delta_cache_size = git_config_int(k, v);
+		max_delta_cache_size = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.deltacachelimit")) {
-		cache_max_small_delta_size = git_config_int(k, v);
+		cache_max_small_delta_size = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.writebitmaphashcache")) {
@@ -3181,7 +3181,7 @@ static int git_pack_config(const char *k, const char *v,
 		return 0;
 	}
 	if (!strcmp(k, "pack.threads")) {
-		delta_search_threads = git_config_int(k, v);
+		delta_search_threads = git_config_int(k, v, ctx->kvi);
 		if (delta_search_threads < 0)
 			die(_("invalid number of threads specified (%d)"),
 			    delta_search_threads);
@@ -3192,7 +3192,7 @@ static int git_pack_config(const char *k, const char *v,
 		return 0;
 	}
 	if (!strcmp(k, "pack.indexversion")) {
-		pack_idx_opts.version = git_config_int(k, v);
+		pack_idx_opts.version = git_config_int(k, v, ctx->kvi);
 		if (pack_idx_opts.version > 2)
 			die(_("bad pack.indexVersion=%"PRIu32),
 			    pack_idx_opts.version);
builtin/receive-pack.c +5 -5
index 94d9898aff7..98f6f0038f0 100644
@@ -158,12 +158,12 @@ static int receive_pack_config(const char *var, const char *value,
 	}
 
 	if (strcmp(var, "receive.unpacklimit") == 0) {
-		receive_unpack_limit = git_config_int(var, value);
+		receive_unpack_limit = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
 	if (strcmp(var, "transfer.unpacklimit") == 0) {
-		transfer_unpack_limit = git_config_int(var, value);
+		transfer_unpack_limit = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
@@ -231,7 +231,7 @@ static int receive_pack_config(const char *var, const char *value,
 		return git_config_string(&cert_nonce_seed, var, value);
 
 	if (strcmp(var, "receive.certnonceslop") == 0) {
-		nonce_stamp_slop_limit = git_config_ulong(var, value);
+		nonce_stamp_slop_limit = git_config_ulong(var, value, ctx->kvi);
 		return 0;
 	}
 
@@ -246,12 +246,12 @@ static int receive_pack_config(const char *var, const char *value,
 	}
 
 	if (strcmp(var, "receive.keepalive") == 0) {
-		keepalive_in_sec = git_config_int(var, value);
+		keepalive_in_sec = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
 	if (strcmp(var, "receive.maxinputsize") == 0) {
-		max_input_size = git_config_int64(var, value);
+		max_input_size = git_config_int64(var, value, ctx->kvi);
 		return 0;
 	}
 
builtin/submodule--helper.c +2 -2
index f8e9d85e77a..57185cf5f3d 100644
@@ -2192,13 +2192,13 @@ static int update_clone_task_finished(int result,
 }
 
 static int git_update_clone_config(const char *var, const char *value,
-				   const struct config_context *ctx UNUSED,
+				   const struct config_context *ctx,
 				   void *cb)
 {
 	int *max_jobs = cb;
 
 	if (!strcmp(var, "submodule.fetchjobs"))
-		*max_jobs = parse_submodule_fetchjobs(var, value);
+		*max_jobs = parse_submodule_fetchjobs(var, value, ctx->kvi);
 	return 0;
 }
 
config.c +61 -95

382 lines not on this page · raw message

config.h +11 -6
index 82eeba94e71..bd366ddb5ef 100644
@@ -249,22 +249,26 @@ int git_parse_maybe_bool(const char *);
  * Parse the string to an integer, including unit factors. Dies on error;
  * otherwise, returns the parsed result.
  */
-int git_config_int(const char *, const char *);
+int git_config_int(const char *, const char *, const struct key_value_info *);
 
-int64_t git_config_int64(const char *, const char *);
+int64_t git_config_int64(const char *, const char *,
+			 const struct key_value_info *);
 
 /**
  * Identical to `git_config_int`, but for unsigned longs.
  */
-unsigned long git_config_ulong(const char *, const char *);
+unsigned long git_config_ulong(const char *, const char *,
+			       const struct key_value_info *);
 
-ssize_t git_config_ssize_t(const char *, const char *);
+ssize_t git_config_ssize_t(const char *, const char *,
+			   const struct key_value_info *);
 
 /**
  * Same as `git_config_bool`, except that integers are returned as-is, and
  * an `is_bool` flag is unset.
  */
-int git_config_bool_or_int(const char *, const char *, int *);
+int git_config_bool_or_int(const char *, const char *,
+			   const struct key_value_info *, int *);
 
 /**
  * Parse a string into a boolean value, respecting keywords like "true" and
@@ -529,7 +533,8 @@ int git_configset_get(struct config_set *cs, const char *key);
  * touching `value`. The caller should not free or modify `value`, as it
  * is owned by the cache.
  */
-int git_configset_get_value(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_value(struct config_set *cs, const char *key,
+			    const char **dest, struct key_value_info *kvi);
 
 int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
 int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
contrib/coccinelle/git_config_number.cocci new file +27 -0
new file mode 100644
index 00000000000..7b57dceefe6
@@ -0,0 +1,27 @@
+@@
+identifier C1, C2, C3;
+@@
+(
+(
+git_config_int
+|
+git_config_int64
+|
+git_config_ulong
+|
+git_config_ssize_t
+)
+  (C1, C2
++ , ctx->kvi
+  )
+|
+(
+git_configset_get_value
+|
+git_config_bool_or_int
+)
+  (C1, C2
++ , ctx->kvi
+ , C3
+  )
+)
diff.c +5 -4
index 0e382c8f7f0..460211e7d40 100644
@@ -379,13 +379,14 @@ int git_diff_ui_config(const char *var, const char *value,
 		return 0;
 	}
 	if (!strcmp(var, "diff.context")) {
-		diff_context_default = git_config_int(var, value);
+		diff_context_default = git_config_int(var, value, ctx->kvi);
 		if (diff_context_default < 0)
 			return -1;
 		return 0;
 	}
 	if (!strcmp(var, "diff.interhunkcontext")) {
-		diff_interhunk_context_default = git_config_int(var, value);
+		diff_interhunk_context_default = git_config_int(var, value,
+								ctx->kvi);
 		if (diff_interhunk_context_default < 0)
 			return -1;
 		return 0;
@@ -411,7 +412,7 @@ int git_diff_ui_config(const char *var, const char *value,
 		return 0;
 	}
 	if (!strcmp(var, "diff.statgraphwidth")) {
-		diff_stat_graph_width = git_config_int(var, value);
+		diff_stat_graph_width = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(var, "diff.external"))
@@ -450,7 +451,7 @@ int git_diff_basic_config(const char *var, const char *value,
 	const char *name;
 
 	if (!strcmp(var, "diff.renamelimit")) {
-		diff_rename_limit_default = git_config_int(var, value);
+		diff_rename_limit_default = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
fmt-merge-msg.c +1 -1
index 10137444321..3ae59bde2f2 100644
@@ -25,7 +25,7 @@ int fmt_merge_msg_config(const char *key, const char *value,
 {
 	if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
 		int is_bool;
-		merge_log_config = git_config_bool_or_int(key, value, &is_bool);
+		merge_log_config = git_config_bool_or_int(key, value, ctx->kvi, &is_bool);
 		if (!is_bool && merge_log_config < 0)
 			return error("%s: negative length %s", key, value);
 		if (is_bool && merge_log_config)
help.c +2 -2
index ac0ae5ac0dc..389382b1482 100644
@@ -545,7 +545,7 @@ static struct cmdnames aliases;
 #define AUTOCORRECT_IMMEDIATELY (-1)
 
 static int git_unknown_cmd_config(const char *var, const char *value,
-				  const struct config_context *ctx UNUSED,
+				  const struct config_context *ctx,
 				  void *cb UNUSED)
 {
 	const char *p;
@@ -560,7 +560,7 @@ static int git_unknown_cmd_config(const char *var, const char *value,
 		} else if (!strcmp(value, "prompt")) {
 			autocorrect = AUTOCORRECT_PROMPT;
 		} else {
-			int v = git_config_int(var, value);
+			int v = git_config_int(var, value, ctx->kvi);
 			autocorrect = (v < 0)
 				? AUTOCORRECT_IMMEDIATELY : v;
 		}
http.c +5 -5
index 762502828c9..b12b234bde1 100644
@@ -414,21 +414,21 @@ static int http_options(const char *var, const char *value,
 	}
 
 	if (!strcmp("http.minsessions", var)) {
-		min_curl_sessions = git_config_int(var, value);
+		min_curl_sessions = git_config_int(var, value, ctx->kvi);
 		if (min_curl_sessions > 1)
 			min_curl_sessions = 1;
 		return 0;
 	}
 	if (!strcmp("http.maxrequests", var)) {
-		max_requests = git_config_int(var, value);
+		max_requests = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp("http.lowspeedlimit", var)) {
-		curl_low_speed_limit = (long)git_config_int(var, value);
+		curl_low_speed_limit = (long)git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp("http.lowspeedtime", var)) {
-		curl_low_speed_time = (long)git_config_int(var, value);
+		curl_low_speed_time = (long)git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
@@ -464,7 +464,7 @@ static int http_options(const char *var, const char *value,
 	}
 
 	if (!strcmp("http.postbuffer", var)) {
-		http_post_buffer = git_config_ssize_t(var, value);
+		http_post_buffer = git_config_ssize_t(var, value, ctx->kvi);
 		if (http_post_buffer < 0)
 			warning(_("negative value for http.postBuffer; defaulting to %d"), LARGE_PACKET_MAX);
 		if (http_post_buffer < LARGE_PACKET_MAX)
imap-send.c +1 -1
index 47777e76861..3518a4ace6c 100644
@@ -1342,7 +1342,7 @@ static int git_imap_config(const char *var, const char *val,
 	else if (!strcmp("imap.authmethod", var))
 		return git_config_string(&server.auth_method, var, val);
 	else if (!strcmp("imap.port", var))
-		server.port = git_config_int(var, val);
+		server.port = git_config_int(var, val, ctx->kvi);
 	else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			git_die_config("imap.host", "Missing value for 'imap.host'");
sequencer.c +11 -11
index 34754d17596..a0aaee3056d 100644
@@ -2883,7 +2883,7 @@ static int git_config_string_dup(char **dest,
 }
 
 static int populate_opts_cb(const char *key, const char *value,
-			    const struct config_context *ctx UNUSED,
+			    const struct config_context *ctx,
 			    void *data)
 {
 	struct replay_opts *opts = data;
@@ -2892,26 +2892,26 @@ static int populate_opts_cb(const char *key, const char *value,
 	if (!value)
 		error_flag = 0;
 	else if (!strcmp(key, "options.no-commit"))
-		opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
+		opts->no_commit = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.edit"))
-		opts->edit = git_config_bool_or_int(key, value, &error_flag);
+		opts->edit = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.allow-empty"))
 		opts->allow_empty =
-			git_config_bool_or_int(key, value, &error_flag);
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.allow-empty-message"))
 		opts->allow_empty_message =
-			git_config_bool_or_int(key, value, &error_flag);
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.keep-redundant-commits"))
 		opts->keep_redundant_commits =
-			git_config_bool_or_int(key, value, &error_flag);
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.signoff"))
-		opts->signoff = git_config_bool_or_int(key, value, &error_flag);
+		opts->signoff = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.record-origin"))
-		opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
+		opts->record_origin = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.allow-ff"))
-		opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
+		opts->allow_ff = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.mainline"))
-		opts->mainline = git_config_int(key, value);
+		opts->mainline = git_config_int(key, value, ctx->kvi);
 	else if (!strcmp(key, "options.strategy"))
 		git_config_string_dup(&opts->strategy, key, value);
 	else if (!strcmp(key, "options.gpg-sign"))
@@ -2920,7 +2920,7 @@ static int populate_opts_cb(const char *key, const char *value,
 		strvec_push(&opts->xopts, value);
 	} else if (!strcmp(key, "options.allow-rerere-auto"))
 		opts->allow_rerere_auto =
-			git_config_bool_or_int(key, value, &error_flag) ?
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag) ?
 				RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;
 	else if (!strcmp(key, "options.default-msg-cleanup")) {
 		opts->explicit_cleanup = 1;
setup.c +1 -1
index fadba5bab4b..ca80fb464bb 100644
@@ -597,7 +597,7 @@ static int check_repo_format(const char *var, const char *value,
 	const char *ext;
 
 	if (strcmp(var, "core.repositoryformatversion") == 0)
-		data->version = git_config_int(var, value);
+		data->version = git_config_int(var, value, ctx->kvi);
 	else if (skip_prefix(var, "extensions.", &ext)) {
 		switch (handle_extension_v0(var, value, ext, data)) {
 		case EXTENSION_ERROR:
submodule-config.c +7 -6
index 3f25bd13674..54be580e2a9 100644
@@ -304,9 +304,10 @@ static int parse_fetch_recurse(const char *opt, const char *arg,
 	}
 }
 
-int parse_submodule_fetchjobs(const char *var, const char *value)
+int parse_submodule_fetchjobs(const char *var, const char *value,
+			      const struct key_value_info *kvi)
 {
-	int fetchjobs = git_config_int(var, value);
+	int fetchjobs = git_config_int(var, value, kvi);
 	if (fetchjobs < 0)
 		die(_("negative values not allowed for submodule.fetchJobs"));
 	if (!fetchjobs)
@@ -849,14 +850,14 @@ struct fetch_config {
 };
 
 static int gitmodules_fetch_config(const char *var, const char *value,
-				   const struct config_context *ctx UNUSED,
+				   const struct config_context *ctx,
 				   void *cb)
 {
 	struct fetch_config *config = cb;
 	if (!strcmp(var, "submodule.fetchjobs")) {
 		if (config->max_children)
 			*(config->max_children) =
-				parse_submodule_fetchjobs(var, value);
+				parse_submodule_fetchjobs(var, value, ctx->kvi);
 		return 0;
 	} else if (!strcmp(var, "fetch.recursesubmodules")) {
 		if (config->recurse_submodules)
@@ -878,12 +879,12 @@ void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules)
 }
 
 static int gitmodules_update_clone_config(const char *var, const char *value,
-					  const struct config_context *ctx UNUSED,
+					  const struct config_context *ctx,
 					  void *cb)
 {
 	int *max_jobs = cb;
 	if (!strcmp(var, "submodule.fetchjobs"))
-		*max_jobs = parse_submodule_fetchjobs(var, value);
+		*max_jobs = parse_submodule_fetchjobs(var, value, ctx->kvi);
 	return 0;
 }
 
submodule-config.h +2 -1
index c2045875bbb..2a37689cc27 100644
@@ -50,7 +50,8 @@ struct repository;
 
 void submodule_cache_free(struct submodule_cache *cache);
 
-int parse_submodule_fetchjobs(const char *var, const char *value);
+int parse_submodule_fetchjobs(const char *var, const char *value,
+			      const struct key_value_info *kvi);
 int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
 struct option;
 int option_fetch_parse_recurse_submodules(const struct option *opt,
t/helper/test-config.c +3 -3
index 3f4c3678318..ed444ca4c25 100644
@@ -63,12 +63,12 @@ static int iterate_cb(const char *var, const char *value,
 }
 
 static int parse_int_cb(const char *var, const char *value,
-			const struct config_context *ctx UNUSED, void *data)
+			const struct config_context *ctx, void *data)
 {
 	const char *key_to_match = data;
 
 	if (!strcmp(key_to_match, var)) {
-		int parsed = git_config_int(value, value);
+		int parsed = git_config_int(value, value, ctx->kvi);
 		printf("%d\n", parsed);
 	}
 	return 0;
@@ -182,7 +182,7 @@ int cmd__config(int argc, const char **argv)
 				goto exit2;
 			}
 		}
-		if (!git_configset_get_value(&cs, argv[2], &v)) {
+		if (!git_configset_get_value(&cs, argv[2], &v, NULL)) {
 			if (!v)
 				printf("(NULL)\n");
 			else
upload-pack.c +7 -5
index 951fd1f9c25..c03415a5460 100644
@@ -1275,7 +1275,8 @@ static int find_symref(const char *refname,
 }
 
 static int parse_object_filter_config(const char *var, const char *value,
-				       struct upload_pack_data *data)
+				      const struct key_value_info *kvi,
+				      struct upload_pack_data *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 	const char *sub, *key;
@@ -1302,7 +1303,8 @@ static int parse_object_filter_config(const char *var, const char *value,
 		}
 		string_list_insert(&data->allowed_filters, buf.buf)->util =
 			(void *)(intptr_t)1;
-		data->tree_filter_max_depth = git_config_ulong(var, value);
+		data->tree_filter_max_depth = git_config_ulong(var, value,
+							       kvi);
 	}
 
 	strbuf_release(&buf);
@@ -1310,7 +1312,7 @@ static int parse_object_filter_config(const char *var, const char *value,
 }
 
 static int upload_pack_config(const char *var, const char *value,
-			      const struct config_context *ctx UNUSED,
+			      const struct config_context *ctx,
 			      void *cb_data)
 {
 	struct upload_pack_data *data = cb_data;
@@ -1331,7 +1333,7 @@ static int upload_pack_config(const char *var, const char *value,
 		else
 			data->allow_uor &= ~ALLOW_ANY_SHA1;
 	} else if (!strcmp("uploadpack.keepalive", var)) {
-		data->keepalive = git_config_int(var, value);
+		data->keepalive = git_config_int(var, value, ctx->kvi);
 		if (!data->keepalive)
 			data->keepalive = -1;
 	} else if (!strcmp("uploadpack.allowfilter", var)) {
@@ -1346,7 +1348,7 @@ static int upload_pack_config(const char *var, const char *value,
 		data->advertise_sid = git_config_bool(var, value);
 	}
 
-	if (parse_object_filter_config(var, value, data) < 0)
+	if (parse_object_filter_config(var, value, ctx->kvi, data) < 0)
 		return -1;
 
 	return parse_hide_refs_config(var, value, "uploadpack", &data->hidden_refs);
worktree.c +1 -1
index c448fecd4b3..af277ec9010 100644
@@ -835,7 +835,7 @@ int init_worktree_config(struct repository *r)
 	 * Relocate that value to avoid breaking all worktrees with this
 	 * upgrade to worktree config.
 	 */
-	if (!git_configset_get_value(&cs, "core.worktree", &core_worktree)) {
+	if (!git_configset_get_value(&cs, "core.worktree", &core_worktree, NULL)) {
 		if ((res = move_config_setting("core.worktree", core_worktree,
 					       common_config_file,
 					       main_worktree_file)))
Signature · 1 line
gitgitgadget
#96 lore raw

PATCH v4 10/12 config.c: remove config_reader from configsets

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Remove the last usage of "struct config_reader" from configsets by copying the "kvi" arg instead of recomputing "kvi" from config_reader.source. Since we no longer need to pass both "struct config_reader" and "struct config_set" in a single "void *cb", remove "struct configset_add_data" too.

---
 config.c | 43 +++++++++++--------------------------------
 1 file changed, 11 insertions(+), 32 deletions(-)
config.c +11 -32
index a9a45e4ffe0..4782a289363 100644
@@ -2311,8 +2311,7 @@ int config_with_options(config_fn_t fn, void *data,
 	return ret;
 }
 
-static void configset_iter(struct config_reader *reader, struct config_set *set,
-			   config_fn_t fn, void *data)
+static void configset_iter(struct config_set *set, config_fn_t fn, void *data)
 {
 	int i, value_index;
 	struct string_list *values;
@@ -2406,7 +2405,6 @@ static int configset_find_element(struct config_set *set, const char *key,
 }
 
 static int configset_add_value(const struct key_value_info *kvi_p,
-			       struct config_reader *reader,
 			       struct config_set *set, const char *key,
 			       const char *value)
 {
@@ -2437,11 +2435,7 @@ static int configset_add_value(const struct key_value_info *kvi_p,
 	l_item->e = e;
 	l_item->value_index = e->value_list.nr - 1;
 
-	if (reader->source->name) {
-		kvi_from_source(reader->source, kvi_p->scope, kv_info);
-	} else {
-		kvi_from_param(kv_info);
-	}
+	memcpy(kv_info, kvi_p, sizeof(struct key_value_info));
 	si->util = kv_info;
 
 	return 0;
@@ -2489,28 +2483,18 @@ void git_configset_clear(struct config_set *set)
 	set->list.items = NULL;
 }
 
-struct configset_add_data {
-	struct config_set *config_set;
-	struct config_reader *config_reader;
-};
-#define CONFIGSET_ADD_INIT { 0 }
-
 static int config_set_callback(const char *key, const char *value,
 			       const struct config_context *ctx,
 			       void *cb)
 {
-	struct configset_add_data *data = cb;
-	configset_add_value(ctx->kvi, data->config_reader, data->config_set,
-			    key, value);
+	struct config_set *set = cb;
+	configset_add_value(ctx->kvi, set, key, value);
 	return 0;
 }
 
 int git_configset_add_file(struct config_set *set, const char *filename)
 {
-	struct configset_add_data data = CONFIGSET_ADD_INIT;
-	data.config_reader = &the_reader;
-	data.config_set = set;
-	return git_config_from_file(config_set_callback, filename, &data);
+	return git_config_from_file(config_set_callback, filename, set);
 }
 
 int git_configset_get_value(struct config_set *set, const char *key,
@@ -2676,7 +2660,6 @@ int git_configset_get_pathname(struct config_set *set, const char *key, const ch
 static void repo_read_config(struct repository *repo)
 {
 	struct config_options opts = { 0 };
-	struct configset_add_data data = CONFIGSET_ADD_INIT;
 
 	opts.respect_includes = 1;
 	opts.commondir = repo->commondir;
@@ -2688,10 +2671,8 @@ static void repo_read_config(struct repository *repo)
 		git_configset_clear(repo->config);
 
 	git_configset_init(repo->config);
-	data.config_set = repo->config;
-	data.config_reader = &the_reader;
-
-	if (config_with_options(config_set_callback, &data, NULL, repo, &opts) < 0)
+	if (config_with_options(config_set_callback, repo->config, NULL,
+				repo, &opts) < 0)
 		/*
 		 * config_with_options() normally returns only
 		 * zero, as most errors are fatal, and
@@ -2723,7 +2704,7 @@ static void repo_config_clear(struct repository *repo)
 void repo_config(struct repository *repo, config_fn_t fn, void *data)
 {
 	git_config_check_init(repo);
-	configset_iter(&the_reader, repo->config, fn, data);
+	configset_iter(repo->config, fn, data);
 }
 
 int repo_config_get(struct repository *repo, const char *key)
@@ -2830,19 +2811,17 @@ static void read_protected_config(void)
 		.ignore_worktree = 1,
 		.system_gently = 1,
 	};
-	struct configset_add_data data = CONFIGSET_ADD_INIT;
 
 	git_configset_init(&protected_config);
-	data.config_set = &protected_config;
-	data.config_reader = &the_reader;
-	config_with_options(config_set_callback, &data, NULL, NULL, &opts);
+	config_with_options(config_set_callback, &protected_config, NULL,
+			    NULL, &opts);
 }
 
 void git_protected_config(config_fn_t fn, void *data)
 {
 	if (!protected_config.hash_initialized)
 		read_protected_config();
-	configset_iter(&the_reader, &protected_config, fn, data);
+	configset_iter(&protected_config, fn, data);
 }
 
 /* Functions used historically to read configuration from 'the_repository' */
Signature · 1 line
gitgitgadget
#97 lore raw

PATCH v4 11/12 config: add kvi.path, use it to evaluate includes

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

Include directives are evaluated using the path of the config file. To reduce the dependence on "config_reader.source", add a new "key_value_info.path" member and use that instead of "config_source.path". This allows us to remove a "struct config_reader *" field from "struct config_include_data", which will subsequently allow us to remove "struct config_reader" entirely.

---
 config.c | 40 +++++++++++++++++++---------------------
 config.h |  2 ++
 2 files changed, 21 insertions(+), 21 deletions(-)
config.c +19 -21
index 4782a289363..070245065f4 100644
@@ -162,7 +162,6 @@ struct config_include_data {
 	const struct config_options *opts;
 	struct git_config_source *config_source;
 	struct repository *repo;
-	struct config_reader *config_reader;
 
 	/*
 	 * All remote URLs discovered when reading all config files.
@@ -181,8 +180,7 @@ static const char include_depth_advice[] = N_(
 "from\n"
 "	%s\n"
 "This might be due to circular includes.");
-static int handle_path_include(struct config_source *cs,
-			       const struct key_value_info *kvi,
+static int handle_path_include(const struct key_value_info *kvi,
 			       const char *path,
 			       struct config_include_data *inc)
 {
@@ -205,14 +203,14 @@ static int handle_path_include(struct config_source *cs,
 	if (!is_absolute_path(path)) {
 		char *slash;
 
-		if (!cs || !cs->path) {
+		if (!kvi || !kvi->path) {
 			ret = error(_("relative config includes must come from files"));
 			goto cleanup;
 		}
 
-		slash = find_last_dir_sep(cs->path);
+		slash = find_last_dir_sep(kvi->path);
 		if (slash)
-			strbuf_add(&buf, cs->path, slash - cs->path + 1);
+			strbuf_add(&buf, kvi->path, slash - kvi->path + 1);
 		strbuf_addstr(&buf, path);
 		path = buf.buf;
 	}
@@ -220,8 +218,8 @@ static int handle_path_include(struct config_source *cs,
 	if (!access_or_die(path, R_OK, 0)) {
 		if (++inc->depth > MAX_INCLUDE_DEPTH)
 			die(_(include_depth_advice), MAX_INCLUDE_DEPTH, path,
-			    !cs ? "<unknown>" :
-			    cs->name ? cs->name :
+			    !kvi ? "<unknown>" :
+			    kvi->filename ? kvi->filename :
 			    "the command line");
 		ret = git_config_from_file_with_options(git_config_include, path, inc,
 							kvi->scope, NULL);
@@ -239,7 +237,7 @@ static void add_trailing_starstar_for_dir(struct strbuf *pat)
 		strbuf_addstr(pat, "**");
 }
 
-static int prepare_include_condition_pattern(struct config_source *cs,
+static int prepare_include_condition_pattern(const struct key_value_info *kvi,
 					     struct strbuf *pat)
 {
 	struct strbuf path = STRBUF_INIT;
@@ -256,11 +254,11 @@ static int prepare_include_condition_pattern(struct config_source *cs,
 	if (pat->buf[0] == '.' && is_dir_sep(pat->buf[1])) {
 		const char *slash;
 
-		if (!cs || !cs->path)
+		if (!kvi || !kvi->path)
 			return error(_("relative config include "
 				       "conditionals must come from files"));
 
-		strbuf_realpath(&path, cs->path, 1);
+		strbuf_realpath(&path, kvi->path, 1);
 		slash = find_last_dir_sep(path.buf);
 		if (!slash)
 			BUG("how is this possible?");
@@ -275,7 +273,7 @@ static int prepare_include_condition_pattern(struct config_source *cs,
 	return prefix;
 }
 
-static int include_by_gitdir(struct config_source *cs,
+static int include_by_gitdir(const struct key_value_info *kvi,
 			     const struct config_options *opts,
 			     const char *cond, size_t cond_len, int icase)
 {
@@ -292,7 +290,7 @@ static int include_by_gitdir(struct config_source *cs,
 
 	strbuf_realpath(&text, git_dir, 1);
 	strbuf_add(&pattern, cond, cond_len);
-	prefix = prepare_include_condition_pattern(cs, &pattern);
+	prefix = prepare_include_condition_pattern(kvi, &pattern);
 
 again:
 	if (prefix < 0)
@@ -428,16 +426,16 @@ static int include_by_remote_url(struct config_include_data *inc,
 					     inc->remote_urls);
 }
 
-static int include_condition_is_true(struct config_source *cs,
+static int include_condition_is_true(const struct key_value_info *kvi,
 				     struct config_include_data *inc,
 				     const char *cond, size_t cond_len)
 {
 	const struct config_options *opts = inc->opts;
 
 	if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
-		return include_by_gitdir(cs, opts, cond, cond_len, 0);
+		return include_by_gitdir(kvi, opts, cond, cond_len, 0);
 	else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
-		return include_by_gitdir(cs, opts, cond, cond_len, 1);
+		return include_by_gitdir(kvi, opts, cond, cond_len, 1);
 	else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
 		return include_by_branch(cond, cond_len);
 	else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,
@@ -453,7 +451,6 @@ static int git_config_include(const char *var, const char *value,
 			      void *data)
 {
 	struct config_include_data *inc = data;
-	struct config_source *cs = inc->config_reader->source;
 	const char *cond, *key;
 	size_t cond_len;
 	int ret;
@@ -467,16 +464,16 @@ static int git_config_include(const char *var, const char *value,
 		return ret;
 
 	if (!strcmp(var, "include.path"))
-		ret = handle_path_include(cs, ctx->kvi, value, inc);
+		ret = handle_path_include(ctx->kvi, value, inc);
 
 	if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
-	    cond && include_condition_is_true(cs, inc, cond, cond_len) &&
+	    cond && include_condition_is_true(ctx->kvi, inc, cond, cond_len) &&
 	    !strcmp(key, "path")) {
 		config_fn_t old_fn = inc->fn;
 
 		if (inc->opts->unconditional_remote_url)
 			inc->fn = forbid_remote_url;
-		ret = handle_path_include(cs, ctx->kvi, value, inc);
+		ret = handle_path_include(ctx->kvi, value, inc);
 		inc->fn = old_fn;
 	}
 
@@ -661,6 +658,7 @@ void kvi_from_param(struct key_value_info *out)
 	out->linenr = -1;
 	out->origin_type = CONFIG_ORIGIN_CMDLINE;
 	out->scope = CONFIG_SCOPE_COMMAND;
+	out->path = NULL;
 }
 
 int git_config_parse_parameter(const char *text,
@@ -1064,6 +1062,7 @@ static void kvi_from_source(struct config_source *cs,
 	out->origin_type = cs->origin_type;
 	out->linenr = cs->linenr;
 	out->scope = scope;
+	out->path = cs->path;
 }
 
 static int git_parse_source(struct config_source *cs, config_fn_t fn,
@@ -2282,7 +2281,6 @@ int config_with_options(config_fn_t fn, void *data,
 		inc.opts = opts;
 		inc.repo = repo;
 		inc.config_source = config_source;
-		inc.config_reader = &the_reader;
 		fn = git_config_include;
 		data = &inc;
 	}
config.h +2 -0
index bd366ddb5ef..b5573e67a00 100644
@@ -116,12 +116,14 @@ struct key_value_info {
 	int linenr;
 	enum config_origin_type origin_type;
 	enum config_scope scope;
+	const char *path;
 };
 #define KVI_INIT { \
 	.filename = NULL, \
 	.linenr = -1, \
 	.origin_type = CONFIG_ORIGIN_UNKNOWN, \
 	.scope = CONFIG_SCOPE_UNKNOWN, \
+	.path = NULL, \
 }
 
 /* Captures additional information that a config callback can use. */
Signature · 1 line
gitgitgadget
#98 lore raw

PATCH v4 12/12 config: pass source to config_parser_event_fn_t

In reply to Glen Choo via GitGitGadget

From: Glen Choo <[email protected]>

..so that the callback can use a "struct config_source" parameter instead of "config_reader.source". "struct config_source" is internal to config.c, so we are adding a pointer to a struct defined in config.c into a public function signature defined in config.h, but this is okay because this function has only ever been (and probably ever will be) used internally by config.c.

As a result, the_reader isn't used anywhere, so "struct config_reader" is obsolete (it was only intended to be used with the_reader). Remove them.

---
 config.c | 77 ++++++++++----------------------------------------------
 config.h |  6 +++++
 2 files changed, 19 insertions(+), 64 deletions(-)
config.c +13 -64
index 070245065f4..2ddeb5e2685 100644
@@ -66,40 +66,6 @@ struct config_source {
 };
 #define CONFIG_SOURCE_INIT { 0 }
 
-struct config_reader {
-	/*
-	 * These members record the "current" config source, which can be
-	 * accessed by parsing callbacks.
-	 *
-	 * The "source" variable will be non-NULL only when we are actually
-	 * parsing a real config source (file, blob, cmdline, etc).
-	 */
-	struct config_source *source;
-};
-/*
- * Where possible, prefer to accept "struct config_reader" as an arg than to use
- * "the_reader". "the_reader" should only be used if that is infeasible, e.g. in
- * a public function.
- */
-static struct config_reader the_reader;
-
-static inline void config_reader_push_source(struct config_reader *reader,
-					     struct config_source *top)
-{
-	top->prev = reader->source;
-	reader->source = top;
-}
-
-static inline struct config_source *config_reader_pop_source(struct config_reader *reader)
-{
-	struct config_source *ret;
-	if (!reader->source)
-		BUG("tried to pop config source, but we weren't reading config");
-	ret = reader->source;
-	reader->source = reader->source->prev;
-	return ret;
-}
-
 static int pack_compression_seen;
 static int zlib_compression_seen;
 
@@ -752,14 +718,9 @@ int git_config_from_parameters(config_fn_t fn, void *data)
 	struct strvec to_free = STRVEC_INIT;
 	int ret = 0;
 	char *envw = NULL;
-	struct config_source source = CONFIG_SOURCE_INIT;
 	struct key_value_info kvi = KVI_INIT;
 
-	source.origin_type = CONFIG_ORIGIN_CMDLINE;
-	config_reader_push_source(&the_reader, &source);
-
 	kvi_from_param(&kvi);
-
 	env = getenv(CONFIG_COUNT_ENVIRONMENT);
 	if (env) {
 		unsigned long count;
@@ -816,7 +777,6 @@ out:
 	strbuf_release(&envvar);
 	strvec_clear(&to_free);
 	free(envw);
-	config_reader_pop_source(&the_reader);
 	return ret;
 }
 
@@ -1045,7 +1005,7 @@ static int do_event(struct config_source *cs, enum config_event_t type,
 
 	if (data->previous_type != CONFIG_EVENT_EOF &&
 	    data->opts->event_fn(data->previous_type, data->previous_offset,
-				 offset, data->opts->event_fn_data) < 0)
+				 offset, cs, data->opts->event_fn_data) < 0)
 		return -1;
 
 	data->previous_type = type;
@@ -2002,8 +1962,7 @@ int git_default_config(const char *var, const char *value,
  * fgetc, ungetc, ftell of top need to be initialized before calling
  * this function.
  */
-static int do_config_from(struct config_reader *reader,
-			  struct config_source *top, config_fn_t fn,
+static int do_config_from(struct config_source *top, config_fn_t fn,
 			  void *data, enum config_scope scope,
 			  const struct config_options *opts)
 {
@@ -2016,21 +1975,17 @@ static int do_config_from(struct config_reader *reader,
 	top->total_len = 0;
 	strbuf_init(&top->value, 1024);
 	strbuf_init(&top->var, 1024);
-	config_reader_push_source(reader, top);
 	kvi_from_source(top, scope, &kvi);
 
 	ret = git_parse_source(top, fn, &kvi, data, opts);
 
-	/* pop config-file parsing state stack */
 	strbuf_release(&top->value);
 	strbuf_release(&top->var);
-	config_reader_pop_source(reader);
 
 	return ret;
 }
 
-static int do_config_from_file(struct config_reader *reader,
-			       config_fn_t fn,
+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, enum config_scope scope,
@@ -2049,7 +2004,7 @@ static int do_config_from_file(struct config_reader *reader,
 	top.do_ftell = config_file_ftell;
 
 	flockfile(f);
-	ret = do_config_from(reader, &top, fn, data, scope, opts);
+	ret = do_config_from(&top, fn, data, scope, opts);
 	funlockfile(f);
 	return ret;
 }
@@ -2057,8 +2012,8 @@ static int do_config_from_file(struct config_reader *reader,
 static int git_config_from_stdin(config_fn_t fn, void *data,
 				 enum config_scope scope)
 {
-	return do_config_from_file(&the_reader, fn, CONFIG_ORIGIN_STDIN, "",
-				   NULL, stdin, data, scope, NULL);
+	return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin,
+				   data, scope, NULL);
 }
 
 int git_config_from_file_with_options(config_fn_t fn, const char *filename,
@@ -2072,9 +2027,8 @@ int git_config_from_file_with_options(config_fn_t fn, const char *filename,
 		BUG("filename cannot be NULL");
 	f = fopen_or_warn(filename, "r");
 	if (f) {
-		ret = do_config_from_file(&the_reader, fn, CONFIG_ORIGIN_FILE,
-					  filename, filename, f, data, scope,
-					  opts);
+		ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename,
+					  filename, f, data, scope, opts);
 		fclose(f);
 	}
 	return ret;
@@ -2105,7 +2059,7 @@ int git_config_from_mem(config_fn_t fn,
 	top.do_ungetc = config_buf_ungetc;
 	top.do_ftell = config_buf_ftell;
 
-	return do_config_from(&the_reader, &top, fn, data, scope, opts);
+	return do_config_from(&top, fn, data, scope, opts);
 }
 
 int git_config_from_blob_oid(config_fn_t fn,
@@ -2198,8 +2152,7 @@ int git_config_system(void)
 	return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
 }
 
-static int do_git_config_sequence(struct config_reader *reader,
-				  const struct config_options *opts,
+static int do_git_config_sequence(const struct config_options *opts,
 				  const struct repository *repo,
 				  config_fn_t fn, void *data)
 {
@@ -2299,7 +2252,7 @@ int config_with_options(config_fn_t fn, void *data,
 		ret = git_config_from_blob_ref(fn, repo, config_source->blob,
 					       data, config_source->scope);
 	} else {
-		ret = do_git_config_sequence(&the_reader, opts, repo, fn, data);
+		ret = do_git_config_sequence(opts, repo, fn, data);
 	}
 
 	if (inc.remote_urls) {
@@ -3009,7 +2962,6 @@ void git_die_config(const char *key, const char *err, ...)
  */
 
 struct config_store_data {
-	struct config_reader *config_reader;
 	size_t baselen;
 	char *key;
 	int do_not_match;
@@ -3055,11 +3007,10 @@ static int matches(const char *key, const char *value,
 		(value && !regexec(store->value_pattern, value, 0, NULL, 0));
 }
 
-static int store_aux_event(enum config_event_t type,
-			   size_t begin, size_t end, void *data)
+static int store_aux_event(enum config_event_t type, size_t begin, size_t end,
+			   struct config_source *cs, void *data)
 {
 	struct config_store_data *store = data;
-	struct config_source *cs = store->config_reader->source;
 
 	ALLOC_GROW(store->parsed, store->parsed_nr + 1, store->parsed_alloc);
 	store->parsed[store->parsed_nr].begin = begin;
@@ -3380,8 +3331,6 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
 	size_t contents_sz;
 	struct config_store_data store = CONFIG_STORE_INIT;
 
-	store.config_reader = &the_reader;
-
 	/* parse-key returns negative; flip the sign to feed exit(3) */
 	ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
 	if (ret)
config.h +6 -0
index b5573e67a00..6332d749047 100644
@@ -72,6 +72,7 @@ enum config_event_t {
 	CONFIG_EVENT_ERROR
 };
 
+struct config_source;
 /*
  * The parser event function (if not NULL) is called with the event type and
  * the begin/end offsets of the parsed elements.
@@ -81,6 +82,7 @@ enum config_event_t {
  */
 typedef int (*config_parser_event_fn_t)(enum config_event_t type,
 					size_t begin_offset, size_t end_offset,
+					struct config_source *cs,
 					void *event_fn_data);
 
 struct config_options {
@@ -100,6 +102,10 @@ struct config_options {
 
 	const char *commondir;
 	const char *git_dir;
+	/*
+	 * event_fn and event_fn_data are for internal use only. Handles events
+	 * emitted by the config parser.
+	 */
 	config_parser_event_fn_t event_fn;
 	void *event_fn_data;
 	enum config_error_action {
Signature · 1 line
gitgitgadget
#99 lore raw
In reply to Glen Choo via GitGitGadget
Quoted 3 lines · Glen Choo via GitGitGadget

I also tested these patches on top of 'seen', and the only conflict I found was with en/header-split-cache-h-part-3, where ll-merge.c got renamed to merge-ll.c, so we need to apply the config refactor there (should work with .cocci).

Both recursive and ort merge strategies should be able to deal with ll-merge.c <-> merge-ll.c, I think.

Thanks.