git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v10 0/4] git config cache & special querying api utilizing the cache
@ 2014-07-18  9:18 Tanay Abhra
  2014-07-18  9:18 ` [PATCH v10 1/4] string-list: add string_list initializer helper function Tanay Abhra
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Tanay Abhra @ 2014-07-18  9:18 UTC (permalink / raw)
  To: git; +Cc: Tanay Abhra, Ramkumar Ramachandra, Matthieu Moy, Junio C Hamano

Hi,

[PATCH v10]: Minor fixes according to [12]. Re added string_list initializer function.
	Thanks to Junio and Matthieu for their suggestions.

[PATCH v9]: Applied most of the review comments mentioned in [11]. Mostly asthetic changes.
	test-config now clears the config_set before exiting. Most of the tests now use the
	check-config function. check_config_init() now handles return values correctly.
	Diff between v8 and v9 is at the bottom. Thanks to Junio and Matthieu for the review.

[PATCH V8]: Moved the contents of config-set.c to config.c for future convenience. Reverted
	test 'find value with misspelled key' to the one in v5. See [10] for the discussion.

[PATCH V7]: Style nits and a broken && chain corrected in `t/t1308-config-set.sh`. See
	[9] for the nits.

[PATCH V6]: Style nits and mistakes corrected. Diff between v6 and v5[8] is at the bottom.
		Thanks to Matthieu, Ramsay and Ram for their suggestions.

[PATCH V5]: `config_set` now uses a single hashmap. Corrected style nits raised in
			the thread[7]. Thanks to Junio and Matthieu for their suggestions.

[PATCH v4]: Introduced `config_set` construct which points to a ordered set of
	config-files cached as hashmaps. Added relevant API functions. For more
	details see the documentation. Rewrote the git_config_get* family to use
	`config_set` internally. Added tests for both config_set API and git_config_get
	family. Added type specific API functions which parses the found value and
	converts it into a specific type.
	Most of the changes implemented are the result of discussion in [6].
	Thanks to Eric, Ramsay, Junio, Matthieu & Karsten for their suggestions
	and review.

[PATCH v3]: Added flag for NULL values that were causing segfaults in some cases.
	Added test-config for usage examples.
	Minor changes and corrections. Refer to discussion thread[5] for more details.
	Thanks to Matthieu, Jeff and Junio for their valuable suggestions.

[PATCH v2]:Changed the string_list to a struct instead of pointer to a struct.
	Added string-list initilization functions.
	Minor mistakes corrected acoording to review comments[4]. Thanks to
	Eric and Matthieu for their review.

[PATCH V1]:Most of the invaluable suggestions by Eric Sunshine, Torsten Bogershausen and
	Jeff King has been implemented[1]. Complete rewrite of config_cache*() family
	using git_config() as hook as suggested by Jeff. Thanks for the review.

[RFC V2]: Improved according to the suggestions by Eric Sunshine and Torsten Bogershausen.
	Added cache invalidation when config file is changed.[2]
	I am using git_config_set_multivar_in_file() as an update hook.

This is patch is for this year's GSoC. My project is
"Git Config API improvements". The link of my proposal is appended below [3].

The aim of this patch series is to generate a cache for querying values from
the config files in a non-callback manner as the current method reads and
parses the config files every time a value is queried for.

The cache is generated from hooking the update_cache function to the current
parsing and callback mechanism in config.c. It is implemented as an hashmap
using the hashmap-api with variables and its corresponding values list as
its members. The values in the list are sorted in order of increasing priority.
The cache is initialised the first time when any of the new query functions is
called. It is invalidated by using git_config_set_multivar_in_file() as an
update hook.

We add two new functions to the config-api, git_config_get_string() and
git_config_get_string_multi() for querying in a non callback manner from
the cache.

[1] http://marc.info/?t=140172066200006&r=1&w=2
[2] http://git.661346.n2.nabble.com/RFC-PATCH-0-2-Git-config-cache-amp-special-querying-api-utilizing-the-cache-td7611691.html
[3] https://drive.google.com/file/d/0B4suZ-aHqDcnSUZJRXVTTnZUN1E/edit?usp=sharing
[4] http://thread.gmane.org/gmane.comp.version-control.git/251073/focus=251369
[5] http://thread.gmane.org/gmane.comp.version-control.git/251704/
[6] http://thread.gmane.org/gmane.comp.version-control.git/252329/
[7] http://marc.info/?t=140428115200001&r=1&w=2
[8] http://article.gmane.org/gmane.comp.version-control.git/252942/
[9] http://thread.gmane.org/gmane.comp.version-control.git/252959/
[10] http://article.gmane.org/gmane.comp.version-control.git/253113
[11] http://thread.gmane.org/gmane.comp.version-control.git/253248
[12] http://thread.gmane.org/gmane.comp.version-control.git/253562


Tanay Abhra (4):
  string-list: add string_list initialiser helper functions
  Use string-list initializaer functions to rewrite
  config set
  test-config

 .gitignore                                  |   1 +
 Documentation/technical/api-config.txt      | 137 +++++++++++++++
 Documentation/technical/api-string-list.txt |   5 +
 Makefile                                    |   1 +
 builtin/commit.c                            |   3 +-
 cache.h                                     |  30 ++++
 config.c                                    | 263 ++++++++++++++++++++++++++++
 merge-recursive.c                           |   9 +-
 string-list.c                               |   6 +
 string-list.h                               |   2 +
 submodule.c                                 |   5 +-
 t/t1308-config-set.sh                       | 200 +++++++++++++++++++++
 test-config.c                               | 142 +++++++++++++++
 transport.c                                 |   4 +-
 14 files changed, 793 insertions(+), 15 deletions(-)
 create mode 100755 t/t1308-config-set.sh
 create mode 100644 test-config.c

-- 
1.9.0.GIT

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v10 1/4] string-list: add string_list initializer helper function
  2014-07-18  9:18 [PATCH v10 0/4] git config cache & special querying api utilizing the cache Tanay Abhra
@ 2014-07-18  9:18 ` Tanay Abhra
  2014-07-18 17:15   ` Junio C Hamano
  2014-07-18  9:19 ` [PATCH v10 2/4] replace memset with string-list initializers Tanay Abhra
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 11+ messages in thread
From: Tanay Abhra @ 2014-07-18  9:18 UTC (permalink / raw)
  To: git; +Cc: Tanay Abhra, Ramkumar Ramachandra, Matthieu Moy, Junio C Hamano

The string-list API has STRING_LIST_INIT_* macros to be used
to define variables with initializers, but lacks functions
to initialise an uninitialized piece of memory to be used as
a string-list at the run-time.
Introduce `string_list_init()` function for that.

Signed-off-by: Tanay Abhra <tanayabh@gmail.com>
---
 Documentation/technical/api-string-list.txt | 5 +++++
 string-list.c                               | 6 ++++++
 string-list.h                               | 2 ++
 3 files changed, 13 insertions(+)

diff --git a/Documentation/technical/api-string-list.txt b/Documentation/technical/api-string-list.txt
index f1add51..d51a657 100644
--- a/Documentation/technical/api-string-list.txt
+++ b/Documentation/technical/api-string-list.txt
@@ -68,6 +68,11 @@ Functions
 
 * General ones (works with sorted and unsorted lists as well)
 
+`string_list_init`::
+
+	Initialize the members of the string_list, set `strdup_strings`
+	member according to the value of the second parameter.
+
 `filter_string_list`::
 
 	Apply a function to each item in a list, retaining only the
diff --git a/string-list.c b/string-list.c
index aabb25e..db38b62 100644
--- a/string-list.c
+++ b/string-list.c
@@ -1,6 +1,12 @@
 #include "cache.h"
 #include "string-list.h"
 
+void string_list_init(struct string_list *list, int strdup_strings)
+{
+	memset(list, 0, sizeof(*list));
+	list->strdup_strings = strdup_strings;
+}
+
 /* if there is no exact match, point to the index where the entry could be
  * inserted */
 static int get_entry_index(const struct string_list *list, const char *string,
diff --git a/string-list.h b/string-list.h
index dd5e294..494eb5d 100644
--- a/string-list.h
+++ b/string-list.h
@@ -18,6 +18,8 @@ struct string_list {
 #define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0, NULL }
 #define STRING_LIST_INIT_DUP   { NULL, 0, 0, 1, NULL }
 
+void string_list_init(struct string_list *list, int strdup_strings);
+
 void print_string_list(const struct string_list *p, const char *text);
 void string_list_clear(struct string_list *list, int free_util);
 
-- 
1.9.0.GIT

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v10 2/4] replace memset with string-list initializers
  2014-07-18  9:18 [PATCH v10 0/4] git config cache & special querying api utilizing the cache Tanay Abhra
  2014-07-18  9:18 ` [PATCH v10 1/4] string-list: add string_list initializer helper function Tanay Abhra
@ 2014-07-18  9:19 ` Tanay Abhra
  2014-07-18  9:19 ` [PATCH v10 3/4] add `config_set` API for caching config-like files Tanay Abhra
  2014-07-18  9:19 ` [PATCH v10 4/4] test-config: add tests for the config_set API Tanay Abhra
  3 siblings, 0 replies; 11+ messages in thread
From: Tanay Abhra @ 2014-07-18  9:19 UTC (permalink / raw)
  To: git; +Cc: Tanay Abhra, Ramkumar Ramachandra, Matthieu Moy, Junio C Hamano

Using memset and then manually setting values of the string-list
members is not future proof as the internal representation of
string-list may change any time.
Use `string_list_init()` or STRING_LIST_INIT_* macros instead of
memset.

Signed-off-by: Tanay Abhra <tanayabh@gmail.com>
---
 builtin/commit.c  | 3 +--
 merge-recursive.c | 9 +++------
 submodule.c       | 5 +----
 transport.c       | 4 +---
 4 files changed, 6 insertions(+), 15 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 72eb3be..5b196ca 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -420,8 +420,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
 			die(_("cannot do a partial commit during a cherry-pick."));
 	}
 
-	memset(&partial, 0, sizeof(partial));
-	partial.strdup_strings = 1;
+	string_list_init(&partial, 1);
 	if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
 		exit(1);
 
diff --git a/merge-recursive.c b/merge-recursive.c
index 5814d05..1d332b8 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -2059,12 +2059,9 @@ void init_merge_options(struct merge_options *o)
 	if (o->verbosity >= 5)
 		o->buffer_output = 0;
 	strbuf_init(&o->obuf, 0);
-	memset(&o->current_file_set, 0, sizeof(struct string_list));
-	o->current_file_set.strdup_strings = 1;
-	memset(&o->current_directory_set, 0, sizeof(struct string_list));
-	o->current_directory_set.strdup_strings = 1;
-	memset(&o->df_conflict_file_set, 0, sizeof(struct string_list));
-	o->df_conflict_file_set.strdup_strings = 1;
+	string_list_init(&o->current_file_set, 1);
+	string_list_init(&o->current_directory_set, 1);
+	string_list_init(&o->df_conflict_file_set, 1);
 }
 
 int parse_merge_opt(struct merge_options *o, const char *s)
diff --git a/submodule.c b/submodule.c
index 48e3b44..c3a61e7 100644
--- a/submodule.c
+++ b/submodule.c
@@ -544,10 +544,7 @@ static int push_submodule(const char *path)
 int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name)
 {
 	int i, ret = 1;
-	struct string_list needs_pushing;
-
-	memset(&needs_pushing, 0, sizeof(struct string_list));
-	needs_pushing.strdup_strings = 1;
+	struct string_list needs_pushing = STRING_LIST_INIT_DUP;
 
 	if (!find_unpushed_submodules(new_sha1, remotes_name, &needs_pushing))
 		return 1;
diff --git a/transport.c b/transport.c
index 59c9727..d32aaf2 100644
--- a/transport.c
+++ b/transport.c
@@ -1188,10 +1188,8 @@ int transport_push(struct transport *transport,
 		if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
 			      TRANSPORT_RECURSE_SUBMODULES_CHECK)) && !is_bare_repository()) {
 			struct ref *ref = remote_refs;
-			struct string_list needs_pushing;
+			struct string_list needs_pushing = STRING_LIST_INIT_DUP;
 
-			memset(&needs_pushing, 0, sizeof(struct string_list));
-			needs_pushing.strdup_strings = 1;
 			for (; ref; ref = ref->next)
 				if (!is_null_sha1(ref->new_sha1) &&
 				    find_unpushed_submodules(ref->new_sha1,
-- 
1.9.0.GIT

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v10 3/4] add `config_set` API for caching config-like files
  2014-07-18  9:18 [PATCH v10 0/4] git config cache & special querying api utilizing the cache Tanay Abhra
  2014-07-18  9:18 ` [PATCH v10 1/4] string-list: add string_list initializer helper function Tanay Abhra
  2014-07-18  9:19 ` [PATCH v10 2/4] replace memset with string-list initializers Tanay Abhra
@ 2014-07-18  9:19 ` Tanay Abhra
  2014-07-18  9:40   ` Matthieu Moy
  2014-07-18  9:19 ` [PATCH v10 4/4] test-config: add tests for the config_set API Tanay Abhra
  3 siblings, 1 reply; 11+ messages in thread
From: Tanay Abhra @ 2014-07-18  9:19 UTC (permalink / raw)
  To: git
  Cc: Tanay Abhra, Ramkumar Ramachandra, Matthieu Moy, Junio C Hamano,
	Matthieu Moy

Currently `git_config()` uses a callback mechanism and file rereads for
config values. Due to this approach, it is not uncommon for the config
files to be parsed several times during the run of a git program, with
different callbacks picking out different variables useful to themselves.

Add a `config_set`, that can be used to construct an in-memory cache for
config-like files that the caller specifies (i.e., files like `.gitmodules`,
`~/.gitconfig` etc.). Add two external functions `git_configset_get_value`
and `git_configset_get_value_multi` for querying from the config sets.
`git_configset_get_value` follows `last one wins` semantic (i.e. if there
are multiple matches for the queried key in the files of the configset the
value returned will be the last entry in `value_list`).
`git_configset_get_value_multi` returns a list of values sorted in order of
increasing priority (i.e. last match will be at the end of the list). Add
type specific query functions like `git_configset_get_bool` and similar.

Add a default `config_set`, `the_config_set` to cache all key-value pairs
read from usual config files (repo specific .git/config, user wide
~/.gitconfig, XDG config and the global /etc/gitconfig). `the_config_set`
is populated using `git_config()`.

Add two external functions `git_config_get_value` and 
`git_config_get_value_multi` for querying in a non-callback manner from
`the_config_set`. Also, add type specific query functions that are
implemented as a thin wrapper around the `config_set` API.

Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>
Signed-off-by: Tanay Abhra <tanayabh@gmail.com>
---
 Documentation/technical/api-config.txt | 137 +++++++++++++++++
 cache.h                                |  30 ++++
 config.c                               | 263 +++++++++++++++++++++++++++++++++
 3 files changed, 430 insertions(+)

diff --git a/Documentation/technical/api-config.txt b/Documentation/technical/api-config.txt
index 230b3a0..8a86e45 100644
--- a/Documentation/technical/api-config.txt
+++ b/Documentation/technical/api-config.txt
@@ -77,6 +77,81 @@ To read a specific file in git-config format, use
 `git_config_from_file`. This takes the same callback and data parameters
 as `git_config`.
 
+Querying For Specific Variables
+-------------------------------
+
+For programs wanting to query for specific variables in a non-callback
+manner, the config API provides two functions `git_config_get_value`
+and `git_config_get_value_multi`. They both read values from an internal
+cache generated previously from reading the config files.
+
+`int git_config_get_value(const char *key, const char **value)`::
+
+	Finds the highest-priority value for the configuration variable `key`,
+	stores the pointer to it in `value` and returns 0. When the
+	configuration variable `key` is not found, returns 1 without touching
+	`value`. The caller should not free or modify `value`, as it is owned
+	by the cache.
+
+`const struct string_list *git_config_get_value_multi(const char *key)`::
+
+	Finds and returns the value list, sorted in order of increasing priority
+	for the configuration variable `key`. When the configuration variable
+	`key` is not found, returns NULL. The caller should not free or modify
+	the returned pointer, as it is owned by the cache.
+
+`void git_config_clear(void)`::
+
+	Resets and invalidates the config cache.
+
+The config API also provides type specific API functions which do conversion
+as well as retrieval for the queried variable, including:
+
+`int git_config_get_int(const char *key, int *dest)`::
+
+	Finds and parses the value to an integer for the configuration variable
+	`key`. Dies on error; otherwise, stores the value of the parsed integer in
+	`dest` and returns 0. When the configuration variable `key` is not found,
+	returns 1 without touching `dest`.
+
+`int git_config_get_ulong(const char *key, unsigned long *dest)`::
+
+	Similar to `git_config_get_int` but for unsigned longs.
+
+`int git_config_get_bool(const char *key, int *dest)`::
+
+	Finds and parses the value into a boolean value, for the configuration
+	variable `key` respecting keywords like "true" and "false". Integer
+	values are converted into true/false values (when they are non-zero or
+	zero, respectively). Other values cause a die(). If parsing is successful,
+	stores the value of the parsed result in `dest` and returns 0. When the
+	configuration variable `key` is not found, returns 1 without touching
+	`dest`.
+
+`int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest)`::
+
+	Similar to `git_config_get_bool`, except that integers are copied as-is,
+	and `is_bool` flag is unset.
+
+`int git_config_get_maybe_bool(const char *key, int *dest)`::
+
+	Similar to `git_config_get_bool`, except that it returns -1 on error
+	rather than dying.
+
+`int git_config_get_string(const char *key, const char **dest)`::
+
+	Allocates and copies the retrieved string into the `dest` parameter for
+	the configuration variable `key`; if NULL string is given, prints an
+	error message and returns -1. When the configuration variable `key` is
+	not found, returns 1 without touching `dest`.
+
+`int git_config_get_pathname(const char *key, const char **dest)`::
+
+	Similar to `git_config_get_string`, but expands `~` or `~user` into
+	the user's home directory when found at the beginning of the path.
+
+See test-config.c for usage examples.
+
 Value Parsing Helpers
 ---------------------
 
@@ -134,6 +209,68 @@ int read_file_with_include(const char *file, config_fn_t fn, void *data)
 `git_config` respects includes automatically. The lower-level
 `git_config_from_file` does not.
 
+Custom Configsets
+-----------------
+
+A `config_set` can be used to construct an in-memory cache for
+config-like files that the caller specifies (i.e., files like `.gitmodules`,
+`~/.gitconfig` etc.). For example,
+
+---------------------------------------
+struct config_set gm_config;
+git_configset_init(&gm_config);
+int b;
+/* we add config files to the config_set */
+git_configset_add_file(&gm_config, ".gitmodules");
+git_configset_add_file(&gm_config, ".gitmodules_alt");
+
+if (!git_configset_get_bool(gm_config, "submodule.frotz.ignore", &b)) {
+	/* hack hack hack */
+}
+
+/* when we are done with the configset */
+git_configset_clear(&gm_config);
+----------------------------------------
+
+Configset API provides functions for the above mentioned work flow, including:
+
+`void git_configset_init(struct config_set *cs)`::
+
+	Initializes the config_set `cs`.
+
+`int git_configset_add_file(struct config_set *cs, const char *filename)`::
+
+	Parses the file and adds the variable-value pairs to the `config_set`,
+	dies if there is an error in parsing the file. Returns 0 on success, or
+	-1 if the file does not exist or is inaccessible. The user has to decide
+	if he wants to free the incomplete configset or continue using it when
+	the function returns -1.
+
+`int git_configset_get_value(struct config_set *cs, const char *key, const char **value)`::
+
+	Finds the highest-priority value for the configuration variable `key`
+	and config set `cs`, stores the pointer to it in `value` and returns 0.
+	When the configuration variable `key` is not found, returns 1 without
+	touching `value`. The caller should not free or modify `value`, as it
+	is owned by the cache.
+
+`const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)`::
+
+	Finds and returns the value list, sorted in order of increasing priority
+	for the configuration variable `key` and config set `cs`. When the
+	configuration variable `key` is not found, returns NULL. The caller
+	should not free or modify the returned pointer, as it is owned by the cache.
+
+`void git_configset_clear(struct config_set *cs)`::
+
+	Clears `config_set` structure, removes all saved variable-value pairs.
+
+In addition to above functions, the `config_set` API provides type specific
+functions in the vein of `git_config_get_int` and family but with an extra
+parameter, pointer to struct `config_set`.
+They all behave similarly to the `git_config_get*()` family described in
+"Querying For Specific Variables" above.
+
 Writing Config Files
 --------------------
 
diff --git a/cache.h b/cache.h
index 92fc9f1..e53651c 100644
--- a/cache.h
+++ b/cache.h
@@ -1348,6 +1348,36 @@ extern int parse_config_key(const char *var,
 			    const char **subsection, int *subsection_len,
 			    const char **key);
 
+struct config_set {
+	struct hashmap config_hash;
+	int hash_initialized;
+};
+
+extern void git_configset_init(struct config_set *cs);
+extern int git_configset_add_file(struct config_set *cs, const char *filename);
+extern int git_configset_get_value(struct config_set *cs, const char *key, const char **value);
+extern const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key);
+extern void git_configset_clear(struct config_set *cs);
+extern int git_configset_get_string(struct config_set *cs, const char *key, const char **dest);
+extern int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
+extern int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);
+extern int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
+extern int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest);
+extern int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest);
+extern int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest);
+
+extern int git_config_get_value(const char *key, const char **value);
+extern const struct string_list *git_config_get_value_multi(const char *key);
+extern void git_config_clear(void);
+extern void git_config_iter(config_fn_t fn, void *data);
+extern int git_config_get_string(const char *key, const char **dest);
+extern int git_config_get_int(const char *key, int *dest);
+extern int git_config_get_ulong(const char *key, unsigned long *dest);
+extern int git_config_get_bool(const char *key, int *dest);
+extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
+extern int git_config_get_maybe_bool(const char *key, int *dest);
+extern int git_config_get_pathname(const char *key, const char **dest);
+
 extern int committer_ident_sufficiently_given(void);
 extern int author_ident_sufficiently_given(void);
 
diff --git a/config.c b/config.c
index ba882a1..fe9f399 100644
--- a/config.c
+++ b/config.c
@@ -9,6 +9,8 @@
 #include "exec_cmd.h"
 #include "strbuf.h"
 #include "quote.h"
+#include "hashmap.h"
+#include "string-list.h"
 
 struct config_source {
 	struct config_source *prev;
@@ -33,10 +35,23 @@ struct config_source {
 	long (*do_ftell)(struct config_source *c);
 };
 
+struct config_set_element {
+	struct hashmap_entry ent;
+	char *key;
+	struct string_list value_list;
+};
+
 static struct config_source *cf;
 
 static int zlib_compression_seen;
 
+/*
+ * Default config_set that contains key-value pairs from the usual set of config
+ * config files (i.e repo specific .git/config, user wide ~/.gitconfig, XDG
+ * config file and the global /etc/gitconfig)
+ */
+static struct config_set the_config_set;
+
 static int config_file_fgetc(struct config_source *conf)
 {
 	return fgetc(conf->u.file);
@@ -1212,6 +1227,251 @@ int git_config(config_fn_t fn, void *data)
 	return git_config_with_options(fn, data, NULL, 1);
 }
 
+static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
+{
+	struct config_set_element k;
+	struct config_set_element *found_entry;
+	char *normalized_key;
+	int ret;
+	/*
+	 * `key` may come from the user, so normalize it before using it
+	 * for querying entries from the hashmap.
+	 */
+	ret = git_config_parse_key(key, &normalized_key, NULL);
+
+	if (ret)
+		return NULL;
+
+	hashmap_entry_init(&k, strhash(normalized_key));
+	k.key = normalized_key;
+	found_entry = hashmap_get(&cs->config_hash, &k, NULL);
+	free(normalized_key);
+	return found_entry;
+}
+
+static int configset_add_value(struct config_set *cs, const char *key, const char *value)
+{
+	struct config_set_element *e;
+	e = configset_find_element(cs, key);
+	/*
+	 * Since the keys are being fed by git_config*() callback mechanism, they
+	 * are already normalized. So simply add them without any further munging.
+	 */
+	if (!e) {
+		e = xmalloc(sizeof(*e));
+		hashmap_entry_init(e, strhash(key));
+		e->key = xstrdup(key);
+		string_list_init(&e->value_list, 1);
+		hashmap_add(&cs->config_hash, e);
+	}
+	string_list_append_nodup(&e->value_list, value ? xstrdup(value) : NULL);
+
+	return 0;
+}
+
+static int config_set_element_cmp(const struct config_set_element *e1,
+				 const struct config_set_element *e2, const void *unused)
+{
+	return strcmp(e1->key, e2->key);
+}
+
+void git_configset_init(struct config_set *cs)
+{
+	hashmap_init(&cs->config_hash, (hashmap_cmp_fn)config_set_element_cmp, 0);
+	cs->hash_initialized = 1;
+}
+
+void git_configset_clear(struct config_set *cs)
+{
+	struct config_set_element *entry;
+	struct hashmap_iter iter;
+	if (!cs->hash_initialized)
+		return;
+
+	hashmap_iter_init(&cs->config_hash, &iter);
+	while ((entry = hashmap_iter_next(&iter))) {
+		free(entry->key);
+		string_list_clear(&entry->value_list, 0);
+	}
+	hashmap_free(&cs->config_hash, 1);
+	cs->hash_initialized = 0;
+}
+
+static int config_set_callback(const char *key, const char *value, void *cb)
+{
+	struct config_set *cs = cb;
+	configset_add_value(cs, key, value);
+	return 0;
+}
+
+int git_configset_add_file(struct config_set *cs, const char *filename)
+{
+	return git_config_from_file(config_set_callback, filename, cs);
+}
+
+int git_configset_get_value(struct config_set *cs, const char *key, const char **value)
+{
+	const struct string_list *values = NULL;
+	/*
+	 * Follows "last one wins" semantic, i.e., if there are multiple matches for the
+	 * queried key in the files of the configset, the value returned will be the last
+	 * value in the value list for that key.
+	 */
+	values = git_configset_get_value_multi(cs, key);
+
+	if (!values)
+		return 1;
+	assert(values->nr > 0);
+	*value = values->items[values->nr - 1].string;
+	return 0;
+}
+
+const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)
+{
+	struct config_set_element *e = configset_find_element(cs, key);
+	return e ? &e->value_list : NULL;
+}
+
+int git_configset_get_string(struct config_set *cs, const char *key, const char **dest)
+{
+	const char *value;
+	if (!git_configset_get_value(cs, key, &value))
+		return git_config_string(dest, key, value);
+	else
+		return 1;
+}
+
+int git_configset_get_int(struct config_set *cs, const char *key, int *dest)
+{
+	const char *value;
+	if (!git_configset_get_value(cs, key, &value)) {
+		*dest = git_config_int(key, value);
+		return 0;
+	} else
+		return 1;
+}
+
+int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest)
+{
+	const char *value;
+	if (!git_configset_get_value(cs, key, &value)) {
+		*dest = git_config_ulong(key, value);
+		return 0;
+	} else
+		return 1;
+}
+
+int git_configset_get_bool(struct config_set *cs, const char *key, int *dest)
+{
+	const char *value;
+	if (!git_configset_get_value(cs, key, &value)) {
+		*dest = git_config_bool(key, value);
+		return 0;
+	} else
+		return 1;
+}
+
+int git_configset_get_bool_or_int(struct config_set *cs, const char *key,
+				int *is_bool, int *dest)
+{
+	const char *value;
+	if (!git_configset_get_value(cs, key, &value)) {
+		*dest = git_config_bool_or_int(key, value, is_bool);
+		return 0;
+	} else
+		return 1;
+}
+
+int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest)
+{
+	const char *value;
+	if (!git_configset_get_value(cs, key, &value)) {
+		*dest = git_config_maybe_bool(key, value);
+		if (*dest == -1)
+			return -1;
+		return 0;
+	} else
+		return 1;
+}
+
+int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest)
+{
+	const char *value;
+	if (!git_configset_get_value(cs, key, &value))
+		return git_config_pathname(dest, key, value);
+	else
+		return 1;
+}
+
+static void git_config_check_init(void)
+{
+	if (the_config_set.hash_initialized)
+		return;
+	git_configset_init(&the_config_set);
+	git_config(config_set_callback, &the_config_set);
+}
+
+void git_config_clear(void)
+{
+	if (!the_config_set.hash_initialized)
+		return;
+	git_configset_clear(&the_config_set);
+}
+
+int git_config_get_value(const char *key, const char **value)
+{
+	git_config_check_init();
+	return git_configset_get_value(&the_config_set, key, value);
+}
+
+const struct string_list *git_config_get_value_multi(const char *key)
+{
+	git_config_check_init();
+	return git_configset_get_value_multi(&the_config_set, key);
+}
+
+int git_config_get_string(const char *key, const char **dest)
+{
+	git_config_check_init();
+	return git_configset_get_string(&the_config_set, key, dest);
+}
+
+int git_config_get_int(const char *key, int *dest)
+{
+	git_config_check_init();
+	return git_configset_get_int(&the_config_set, key, dest);
+}
+
+int git_config_get_ulong(const char *key, unsigned long *dest)
+{
+	git_config_check_init();
+	return git_configset_get_ulong(&the_config_set, key, dest);
+}
+
+int git_config_get_bool(const char *key, int *dest)
+{
+	git_config_check_init();
+	return git_configset_get_bool(&the_config_set, key, dest);
+}
+
+int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest)
+{
+	git_config_check_init();
+	return git_configset_get_bool_or_int(&the_config_set, key, is_bool, dest);
+}
+
+int git_config_get_maybe_bool(const char *key, int *dest)
+{
+	git_config_check_init();
+	return git_configset_get_maybe_bool(&the_config_set, key, dest);
+}
+
+int git_config_get_pathname(const char *key, const char **dest)
+{
+	git_config_check_init();
+	return git_configset_get_pathname(&the_config_set, key, dest);
+}
+
 /*
  * Find all the stuff for git_config_set() below.
  */
@@ -1707,6 +1967,9 @@ int git_config_set_multivar_in_file(const char *config_filename,
 	lock = NULL;
 	ret = 0;
 
+	/* Invalidate the config cache */
+	git_config_clear();
+
 out_free:
 	if (lock)
 		rollback_lock_file(lock);
-- 
1.9.0.GIT

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v10 4/4] test-config: add tests for the config_set API
  2014-07-18  9:18 [PATCH v10 0/4] git config cache & special querying api utilizing the cache Tanay Abhra
                   ` (2 preceding siblings ...)
  2014-07-18  9:19 ` [PATCH v10 3/4] add `config_set` API for caching config-like files Tanay Abhra
@ 2014-07-18  9:19 ` Tanay Abhra
  2014-07-18 17:01   ` Junio C Hamano
  3 siblings, 1 reply; 11+ messages in thread
From: Tanay Abhra @ 2014-07-18  9:19 UTC (permalink / raw)
  To: git
  Cc: Tanay Abhra, Ramkumar Ramachandra, Matthieu Moy, Junio C Hamano,
	Matthieu Moy

Expose the `config_set` C API as a set of simple commands in order to
facilitate testing. Add tests for the `config_set` API as well as for
`git_config_get_*()` family for the usual config files.

Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>
Signed-off-by: Tanay Abhra <tanayabh@gmail.com>
---
 .gitignore            |   1 +
 Makefile              |   1 +
 t/t1308-config-set.sh | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++
 test-config.c         | 142 +++++++++++++++++++++++++++++++++++
 4 files changed, 344 insertions(+)
 create mode 100755 t/t1308-config-set.sh
 create mode 100644 test-config.c

diff --git a/.gitignore b/.gitignore
index 81e12c0..5bfb234 100644
--- a/.gitignore
+++ b/.gitignore
@@ -178,6 +178,7 @@
 /gitweb/static/gitweb.min.*
 /test-chmtime
 /test-ctype
+/test-config
 /test-date
 /test-delta
 /test-dump-cache-tree
diff --git a/Makefile b/Makefile
index 840057c..8d8da72 100644
--- a/Makefile
+++ b/Makefile
@@ -549,6 +549,7 @@ PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
 
 TEST_PROGRAMS_NEED_X += test-chmtime
 TEST_PROGRAMS_NEED_X += test-ctype
+TEST_PROGRAMS_NEED_X += test-config
 TEST_PROGRAMS_NEED_X += test-date
 TEST_PROGRAMS_NEED_X += test-delta
 TEST_PROGRAMS_NEED_X += test-dump-cache-tree
diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh
new file mode 100755
index 0000000..f0307b7
--- /dev/null
+++ b/t/t1308-config-set.sh
@@ -0,0 +1,200 @@
+#!/bin/sh
+
+test_description='Test git config-set API in different settings'
+
+. ./test-lib.sh
+
+# 'check_config get_* section.key value' verifies that the entry for
+# section.key is 'value'
+check_config () {
+	if test "$1" = expect_code
+	then
+		expect_code="$2" && shift && shift
+	else
+		expect_code=0
+	fi &&
+	op=$1 key=$2 && shift && shift &&
+	if test $# != 0
+	then
+		printf "%s\n" "$@"
+	fi >expect &&
+	test_expect_code $expect_code test-config "$op" "$key" >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success 'setup default config' '
+	cat >.git/config <<\EOF
+	[case]
+		penguin = very blue
+		Movie = BadPhysics
+		UPPERCASE = true
+		MixedCase = true
+		my =
+		foo
+		baz = sam
+	[Cores]
+		WhatEver = Second
+		baz = bar
+	[cores]
+		baz = bat
+	[CORES]
+		baz = ball
+	[my "Foo bAr"]
+		hi = mixed-case
+	[my "FOO BAR"]
+		hi = upper-case
+	[my "foo bar"]
+		hi = lower-case
+	[case]
+		baz = bat
+		baz = hask
+	[lamb]
+		chop = 65
+		head = none
+	[goat]
+		legs = 4
+		head = true
+		skin = false
+		nose = 1
+		horns
+	EOF
+'
+
+test_expect_success 'get value for a simple key' '
+	check_config get_value case.penguin "very blue"
+'
+
+test_expect_success 'get value for a key with value as an empty string' '
+	check_config get_value case.my ""
+'
+
+test_expect_success 'get value for a key with value as NULL' '
+	check_config get_value case.foo "(NULL)"
+'
+
+test_expect_success 'upper case key' '
+	check_config get_value case.UPPERCASE "true" &&
+	check_config get_value case.uppercase "true"
+'
+
+test_expect_success 'mixed case key' '
+	check_config get_value case.MixedCase "true" &&
+	check_config get_value case.MIXEDCASE "true" &&
+	check_config get_value case.mixedcase "true"
+'
+
+test_expect_success 'key and value with mixed case' '
+	check_config get_value case.Movie "BadPhysics"
+'
+
+test_expect_success 'key with case sensitive subsection' '
+	check_config get_value "my.Foo bAr.hi" "mixed-case" &&
+	check_config get_value "my.FOO BAR.hi" "upper-case" &&
+	check_config get_value "my.foo bar.hi" "lower-case"
+'
+
+test_expect_success 'key with case insensitive section header' '
+	check_config get_value cores.baz "ball" &&
+	check_config get_value Cores.baz "ball" &&
+	check_config get_value CORES.baz "ball" &&
+	check_config get_value coreS.baz "ball"
+'
+
+test_expect_success 'key with case insensitive section header & variable' '
+	check_config get_value CORES.BAZ "ball" &&
+	check_config get_value cores.baz "ball" &&
+	check_config get_value cores.BaZ "ball" &&
+	check_config get_value cOreS.bAz "ball"
+'
+
+test_expect_success 'find value with misspelled key' '
+	check_config expect_code 1 get_value "my.fOo Bar.hi" "Value not found for \"my.fOo Bar.hi\""
+'
+
+test_expect_success 'find value with the highest priority' '
+	check_config get_value case.baz "hask"
+'
+
+test_expect_success 'find integer value for a key' '
+	check_config get_int lamb.chop 65
+'
+
+test_expect_success 'find integer if value is non parse-able' '
+	check_config expect_code 128 get_int lamb.head
+'
+
+test_expect_success 'find bool value for the entered key' '
+	check_config get_bool goat.head 1 &&
+	check_config get_bool goat.skin 0 &&
+	check_config get_bool goat.nose 1 &&
+	check_config get_bool goat.horns 1 &&
+	check_config get_bool goat.legs 1
+'
+
+test_expect_success 'find multiple values' '
+	check_config get_value_multi case.baz sam bat hask
+'
+
+test_expect_success 'find value from a configset' '
+	cat >config2 <<-\EOF &&
+	[case]
+		baz = lama
+	[my]
+		new = silk
+	[case]
+		baz = ball
+	EOF
+	echo silk >expect &&
+	test-config configset_get_value my.new config2 .git/config >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'find value with highest priority from a configset' '
+	echo hask >expect &&
+	test-config configset_get_value case.baz config2 .git/config >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'find value_list for a key from a configset' '
+	cat >except <<-\EOF &&
+	sam
+	bat
+	hask
+	lama
+	ball
+	EOF
+	test-config configset_get_value case.baz config2 .git/config >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'proper error on non-existant files' '
+	echo "Error (-1) reading configuration file non-existant-file." >expect &&
+	test_expect_code 2 test-config configset_get_value foo.bar non-existant-file 2>actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'proper error on non-accessible files' '
+	chmod -r .git/config &&
+	test_when_finished "chmod +r .git/config" &&
+	echo "Error (-1) reading configuration file .git/config." >expect &&
+	test_expect_code 2 test-config configset_get_value foo.bar .git/config 2>actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'proper error on error in default config files' '
+	cp .git/config .git/config.old &&
+	test_when_finished "mv .git/config.old .git/config" &&
+	echo "[" >>.git/config &&
+	echo "fatal: bad config file line 35 in .git/config" >expect &&
+	test_expect_code 128 test-config get_value foo.bar 2>actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'proper error on error in custom config files' '
+	echo "[" >>syntax-error &&
+	echo "fatal: bad config file line 1 in syntax-error" >expect &&
+	test_expect_code 128 test-config configset_get_value foo.bar syntax-error 2>actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/test-config.c b/test-config.c
new file mode 100644
index 0000000..9dd1b22
--- /dev/null
+++ b/test-config.c
@@ -0,0 +1,142 @@
+#include "cache.h"
+#include "string-list.h"
+
+/*
+ * This program exposes the C API of the configuration mechanism
+ * as a set of simple commands in order to facilitate testing.
+ *
+ * Reads stdin and prints result of command to stdout:
+ *
+ * get_value -> prints the value with highest priority for the entered key
+ *
+ * get_value_multi -> prints all values for the entered key in increasing order
+ *		     of priority
+ *
+ * get_int -> print integer value for the entered key or die
+ *
+ * get_bool -> print bool value for the entered key or die
+ *
+ * configset_get_value -> returns value with the highest priority for the entered key
+ * 			from a config_set constructed from files entered as arguments.
+ *
+ * configset_get_value_multi -> returns value_list for the entered key sorted in
+ * 				ascending order of priority from a config_set
+ * 				constructed from files entered as arguments.
+ *
+ * Examples:
+ *
+ * To print the value with highest priority for key "foo.bAr Baz.rock":
+ * 	test-config get_value "foo.bAr Baz.rock"
+ *
+ */
+
+
+int main(int argc, char **argv)
+{
+	int i, val;
+	const char *v;
+	const struct string_list *strptr;
+	struct config_set cs;
+	git_configset_init(&cs);
+
+	if (argc < 2) {
+		fprintf(stderr, "Please, provide a command name on the command-line\n");
+		goto exit1;
+	} else if (argc == 3 && !strcmp(argv[1], "get_value")) {
+		if (!git_config_get_value(argv[2], &v)) {
+			if (!v)
+				printf("(NULL)\n");
+			else
+				printf("%s\n", v);
+			goto exit0;
+		} else {
+			printf("Value not found for \"%s\"\n", argv[2]);
+			goto exit1;
+		}
+	} else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) {
+		strptr = git_config_get_value_multi(argv[2]);
+		if (strptr) {
+			for (i = 0; i < strptr->nr; i++) {
+				v = strptr->items[i].string;
+				if (!v)
+					printf("(NULL)\n");
+				else
+					printf("%s\n", v);
+			}
+			goto exit0;
+		} else {
+			printf("Value not found for \"%s\"\n", argv[2]);
+			goto exit1;
+		}
+	} else if (argc == 3 && !strcmp(argv[1], "get_int")) {
+		if (!git_config_get_int(argv[2], &val)) {
+			printf("%d\n", val);
+			goto exit0;
+		} else {
+			printf("Value not found for \"%s\"\n", argv[2]);
+			goto exit1;
+		}
+	} else if (argc == 3 && !strcmp(argv[1], "get_bool")) {
+		if (!git_config_get_bool(argv[2], &val)) {
+			printf("%d\n", val);
+			goto exit0;
+		} else {
+			printf("Value not found for \"%s\"\n", argv[2]);
+			goto exit1;
+		}
+	} else if (!strcmp(argv[1], "configset_get_value")) {
+		for (i = 3; i < argc; i++) {
+			int err;
+			if ((err = git_configset_add_file(&cs, argv[i]))) {
+				fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
+				goto exit2;
+			}
+		}
+		if (!git_configset_get_value(&cs, argv[2], &v)) {
+			if (!v)
+				printf("(NULL)\n");
+			else
+				printf("%s\n", v);
+			goto exit0;
+		} else {
+			printf("Value not found for \"%s\"\n", argv[2]);
+			goto exit1;
+		}
+	} else if (!strcmp(argv[1], "configset_get_value_multi")) {
+		for (i = 3; i < argc; i++) {
+			int err;
+			if ((err = git_configset_add_file(&cs, argv[i]))) {
+				fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
+				goto exit2;
+			}
+		}
+		strptr = git_configset_get_value_multi(&cs, argv[2]);
+		if (strptr) {
+			for (i = 0; i < strptr->nr; i++) {
+				v = strptr->items[i].string;
+				if (!v)
+					printf("(NULL)\n");
+				else
+					printf("%s\n", v);
+			}
+			goto exit0;
+		} else {
+			printf("Value not found for \"%s\"\n", argv[2]);
+			goto exit1;
+		}
+	}
+
+	die("%s: Please check the syntax and the function name", argv[0]);
+
+exit0:
+	git_configset_clear(&cs);
+	return 0;
+
+exit1:
+	git_configset_clear(&cs);
+	return 1;
+
+exit2:
+	git_configset_clear(&cs);
+	return 2;
+}
-- 
1.9.0.GIT

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v10 3/4] add `config_set` API for caching config-like files
  2014-07-18  9:19 ` [PATCH v10 3/4] add `config_set` API for caching config-like files Tanay Abhra
@ 2014-07-18  9:40   ` Matthieu Moy
  2014-07-18  9:52     ` Tanay Abhra
  0 siblings, 1 reply; 11+ messages in thread
From: Matthieu Moy @ 2014-07-18  9:40 UTC (permalink / raw)
  To: Tanay Abhra; +Cc: git, Ramkumar Ramachandra, Matthieu Moy, Junio C Hamano

----- Original Message -----
>  Documentation/technical/api-config.txt | 137 +++++++++++++++++
>  cache.h                                |  30 ++++
>  config.c                               | 263
>  +++++++++++++++++++++++++++++++++
>  3 files changed, 430 insertions(+)

I think the added call to git_config_clear() I proposed yesterday in setup_git_directory_gently_1 should be part of this patch (with the associated comment), just like this call:

> @@ -1707,6 +1967,9 @@ int git_config_set_multivar_in_file(const char
> *config_filename,
>  	lock = NULL;
>  	ret = 0;
>  
> +	/* Invalidate the config cache */
> +	git_config_clear();
> +
>  out_free:
>  	if (lock)
>  		rollback_lock_file(lock);

I have limited access to my email and no way to apply the patches today, so I can't do a detailed review. But other than the remark above, I guess the patch series is now all right and ready to cook in pu.

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v10 3/4] add `config_set` API for caching config-like files
  2014-07-18  9:40   ` Matthieu Moy
@ 2014-07-18  9:52     ` Tanay Abhra
  2014-07-18 10:03       ` Matthieu Moy
  0 siblings, 1 reply; 11+ messages in thread
From: Tanay Abhra @ 2014-07-18  9:52 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: git, Ramkumar Ramachandra, Junio C Hamano



On 7/18/2014 3:10 PM, Matthieu Moy wrote:
> ----- Original Message -----
>>  Documentation/technical/api-config.txt | 137 +++++++++++++++++
>>  cache.h                                |  30 ++++
>>  config.c                               | 263
>>  +++++++++++++++++++++++++++++++++
>>  3 files changed, 430 insertions(+)
> 
> I think the added call to git_config_clear() I proposed yesterday in setup_git_directory_gently_1 should be part of this patch (with the associated comment), just like this call:
>

Oh, my bad, I thought you meant that I incorporate it with the git_config_raw() patch.
I wanted to ask, can we call setup_git_directory_gently() earlier in execv_dashed_external()
in git.c, which calls check_pager_config() for the first time which causes the
incomplete cache to formed.
If we can do it, we won't have to clear the cache every time setup_git_directory_gently_1()
is called.

>> @@ -1707,6 +1967,9 @@ int git_config_set_multivar_in_file(const char
>> *config_filename,
>>  	lock = NULL;
>>  	ret = 0;
>>  
>> +	/* Invalidate the config cache */
>> +	git_config_clear();
>> +
>>  out_free:
>>  	if (lock)
>>  		rollback_lock_file(lock);
> 
> I have limited access to my email and no way to apply the patches today, so I can't do a detailed review. But other than the remark above, I guess the patch series is now all right and ready to cook in pu.
> 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v10 3/4] add `config_set` API for caching config-like files
  2014-07-18  9:52     ` Tanay Abhra
@ 2014-07-18 10:03       ` Matthieu Moy
  0 siblings, 0 replies; 11+ messages in thread
From: Matthieu Moy @ 2014-07-18 10:03 UTC (permalink / raw)
  To: Tanay Abhra; +Cc: Matthieu Moy, git, Ramkumar Ramachandra, Junio C Hamano

I think my proposal is a fix, and yours is a workaround. Semantically, calling setup_git_directory changes the config, so it should invalidate the cache. Your proposal consists in not filling-in the cache before it is invalidated, which works, but isn't very future-proof: if anybody fills-in the cache before the setup in the future, it will break it.

Now, both options can go together, then moving the check_pager_config call after setup_git_directory would avoid re-filling-in the cache. Intuitively, I'd say it's even needed in case git is started from a subdirectory and the .git/config contains pager-relevant stuff, but there might be complications.

----- Original Message -----
> 
> 
> On 7/18/2014 3:10 PM, Matthieu Moy wrote:
> > ----- Original Message -----
> >>  Documentation/technical/api-config.txt | 137 +++++++++++++++++
> >>  cache.h                                |  30 ++++
> >>  config.c                               | 263
> >>  +++++++++++++++++++++++++++++++++
> >>  3 files changed, 430 insertions(+)
> > 
> > I think the added call to git_config_clear() I proposed yesterday in
> > setup_git_directory_gently_1 should be part of this patch (with the
> > associated comment), just like this call:
> >
> 
> Oh, my bad, I thought you meant that I incorporate it with the
> git_config_raw() patch.
> I wanted to ask, can we call setup_git_directory_gently() earlier in
> execv_dashed_external()
> in git.c, which calls check_pager_config() for the first time which causes
> the
> incomplete cache to formed.
> If we can do it, we won't have to clear the cache every time
> setup_git_directory_gently_1()
> is called.
> 
> >> @@ -1707,6 +1967,9 @@ int git_config_set_multivar_in_file(const char
> >> *config_filename,
> >>  	lock = NULL;
> >>  	ret = 0;
> >>  
> >> +	/* Invalidate the config cache */
> >> +	git_config_clear();
> >> +
> >>  out_free:
> >>  	if (lock)
> >>  		rollback_lock_file(lock);
> > 
> > I have limited access to my email and no way to apply the patches today, so
> > I can't do a detailed review. But other than the remark above, I guess the
> > patch series is now all right and ready to cook in pu.
> > 
> 

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v10 4/4] test-config: add tests for the config_set API
  2014-07-18  9:19 ` [PATCH v10 4/4] test-config: add tests for the config_set API Tanay Abhra
@ 2014-07-18 17:01   ` Junio C Hamano
  2014-07-18 21:52     ` Matthieu Moy
  0 siblings, 1 reply; 11+ messages in thread
From: Junio C Hamano @ 2014-07-18 17:01 UTC (permalink / raw)
  To: Tanay Abhra; +Cc: git, Ramkumar Ramachandra, Matthieu Moy, Matthieu Moy

Tanay Abhra <tanayabh@gmail.com> writes:

> +test_expect_success 'proper error on non-existant files' '
> +	echo "Error (-1) reading configuration file non-existant-file." >expect &&

s/tant/tent/ perhaps?
cf. http://en.wiktionary.org/wiki/non-existant

> +test_expect_success 'proper error on non-accessible files' '
> +	chmod -r .git/config &&
> +	test_when_finished "chmod +r .git/config" &&
> +	echo "Error (-1) reading configuration file .git/config." >expect &&
> +	test_expect_code 2 test-config configset_get_value foo.bar .git/config 2>actual &&
> +	test_cmp expect actual
> +'

Doesn't this one need some prerequisite?

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v10 1/4] string-list: add string_list initializer helper function
  2014-07-18  9:18 ` [PATCH v10 1/4] string-list: add string_list initializer helper function Tanay Abhra
@ 2014-07-18 17:15   ` Junio C Hamano
  0 siblings, 0 replies; 11+ messages in thread
From: Junio C Hamano @ 2014-07-18 17:15 UTC (permalink / raw)
  To: Tanay Abhra; +Cc: git, Ramkumar Ramachandra, Matthieu Moy

Tanay Abhra <tanayabh@gmail.com> writes:

> The string-list API has STRING_LIST_INIT_* macros to be used
> to define variables with initializers, but lacks functions
> to initialise an uninitialized piece of memory to be used as
> a string-list at the run-time.
> Introduce `string_list_init()` function for that.
>
> Signed-off-by: Tanay Abhra <tanayabh@gmail.com>
> ---

Looks sensible, modulo s/initialise/initialize/ which perhaps is
originally my fault.

Will queue; thanks.

>  Documentation/technical/api-string-list.txt | 5 +++++
>  string-list.c                               | 6 ++++++
>  string-list.h                               | 2 ++
>  3 files changed, 13 insertions(+)
>
> diff --git a/Documentation/technical/api-string-list.txt b/Documentation/technical/api-string-list.txt
> index f1add51..d51a657 100644
> --- a/Documentation/technical/api-string-list.txt
> +++ b/Documentation/technical/api-string-list.txt
> @@ -68,6 +68,11 @@ Functions
>  
>  * General ones (works with sorted and unsorted lists as well)
>  
> +`string_list_init`::
> +
> +	Initialize the members of the string_list, set `strdup_strings`
> +	member according to the value of the second parameter.
> +
>  `filter_string_list`::
>  
>  	Apply a function to each item in a list, retaining only the
> diff --git a/string-list.c b/string-list.c
> index aabb25e..db38b62 100644
> --- a/string-list.c
> +++ b/string-list.c
> @@ -1,6 +1,12 @@
>  #include "cache.h"
>  #include "string-list.h"
>  
> +void string_list_init(struct string_list *list, int strdup_strings)
> +{
> +	memset(list, 0, sizeof(*list));
> +	list->strdup_strings = strdup_strings;
> +}
> +
>  /* if there is no exact match, point to the index where the entry could be
>   * inserted */
>  static int get_entry_index(const struct string_list *list, const char *string,
> diff --git a/string-list.h b/string-list.h
> index dd5e294..494eb5d 100644
> --- a/string-list.h
> +++ b/string-list.h
> @@ -18,6 +18,8 @@ struct string_list {
>  #define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0, NULL }
>  #define STRING_LIST_INIT_DUP   { NULL, 0, 0, 1, NULL }
>  
> +void string_list_init(struct string_list *list, int strdup_strings);
> +
>  void print_string_list(const struct string_list *p, const char *text);
>  void string_list_clear(struct string_list *list, int free_util);

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v10 4/4] test-config: add tests for the config_set API
  2014-07-18 17:01   ` Junio C Hamano
@ 2014-07-18 21:52     ` Matthieu Moy
  0 siblings, 0 replies; 11+ messages in thread
From: Matthieu Moy @ 2014-07-18 21:52 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Tanay Abhra, git, Ramkumar Ramachandra

Junio C Hamano <gitster@pobox.com> writes:

> Tanay Abhra <tanayabh@gmail.com> writes:
>
>> +test_expect_success 'proper error on non-existant files' '
>> +	echo "Error (-1) reading configuration file non-existant-file." >expect &&
>
> s/tant/tent/ perhaps?
> cf. http://en.wiktionary.org/wiki/non-existant

It seems so. My bad.

>> +test_expect_success 'proper error on non-accessible files' '
>> +	chmod -r .git/config &&
>> +	test_when_finished "chmod +r .git/config" &&
>> +	echo "Error (-1) reading configuration file .git/config." >expect &&
>> +	test_expect_code 2 test-config configset_get_value foo.bar .git/config 2>actual &&
>> +	test_cmp expect actual
>> +'
>
> Doesn't this one need some prerequisite?

My bad too ;-). There should have been SANITY,POSIXPERM I guess (Tanay:
search "prereq" in t/README if you don't see what we're talking about).

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2014-07-18 21:53 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-07-18  9:18 [PATCH v10 0/4] git config cache & special querying api utilizing the cache Tanay Abhra
2014-07-18  9:18 ` [PATCH v10 1/4] string-list: add string_list initializer helper function Tanay Abhra
2014-07-18 17:15   ` Junio C Hamano
2014-07-18  9:19 ` [PATCH v10 2/4] replace memset with string-list initializers Tanay Abhra
2014-07-18  9:19 ` [PATCH v10 3/4] add `config_set` API for caching config-like files Tanay Abhra
2014-07-18  9:40   ` Matthieu Moy
2014-07-18  9:52     ` Tanay Abhra
2014-07-18 10:03       ` Matthieu Moy
2014-07-18  9:19 ` [PATCH v10 4/4] test-config: add tests for the config_set API Tanay Abhra
2014-07-18 17:01   ` Junio C Hamano
2014-07-18 21:52     ` Matthieu Moy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).