{"thread":{"id":"41673","subject":"[PATCH v2 0/10] cleaning up check_repository_format_gently","startedAt":"2016-03-11T22:36:20Z","lastAt":"2016-03-11T23:33:03Z","messageCount":12,"participants":["Jeff King","Junio C Hamano"],"isPatch":true,"patchVersion":2,"patchTotal":10},"messages":[{"id":"280639","messageId":"20160311223620.GA7963@sigill.intra.peff.net","threadId":"41673","inReplyTo":null,"subject":"[PATCH v2 0/10] cleaning up check_repository_format_gently","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-03-11T22:36:20Z","receivedAt":"2016-03-11T22:36:20Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This is a re-roll of:\n\n  http://article.gmane.org/gmane.comp.version-control.git/288017\n\nThe changes from v1 are:\n\n  - dropped original 10/10 that got rid of GIT_REPO_VERSION defines\n    (David mentioned that he's going to be expanding their use, so the\n    argument that they are not used does not make sense)\n\n  - added new 10/10 marking messages for translation (suggested by Duy)\n\n  - added int return value to new read_repository_format(); this is\n    redundant with the error value returned in the struct, but allows a\n    more idiomatic:\n\n      if (read_repository_format(&format, file) < 0)\n         ...\n\n  - drop confusing comment from 06/10, in favor of a better explanation\n    in the commit message\n\n  - fixed newline regression from v1 when printing out unknown\n    extensions. Note that because we switch from looping over warning()\n    or die() to sticking errors into a strbuf (which the caller then\n    feeds to warning/die), the format changed a bit.  Naively, it would\n    become:\n\n      warning: unknown extension: foo\n      unknown:extension: bar\n\n    but I turned it into the more pleasant:\n\n      warning: unknown repository extensions found:\n              foo\n              bar\n\n    I think it would be better still if warning() was smart enough to\n    stick its prefix in front of all lines (like advise() does). But\n    that's outside the scope of this series. And it probably doesn't\n    matter much either way; this is not a message we'd expect anyone to\n    see routinely.\n\n  [01/10]: setup: document check_repository_format()\n  [02/10]: wrap shared_repository global in get/set accessors\n  [03/10]: lazily load core.sharedrepository\n  [04/10]: check_repository_format_gently: stop using git_config_early\n  [05/10]: config: drop git_config_early\n  [06/10]: setup: refactor repo format reading and verification\n  [07/10]: init: use setup.c's repo version verification\n  [08/10]: setup: unify repository version callbacks\n  [09/10]: setup: drop repository_format_version global\n  [10/10]: verify_repository_format: mark messages for translation\n\n-Peff\n"},{"id":"280641","messageId":"20160311223645.GA449@sigill.intra.peff.net","threadId":"41673","inReplyTo":"20160311223620.GA7963@sigill.intra.peff.net","subject":"[PATCH v2 01/10] setup: document check_repository_format()","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-03-11T22:36:45Z","receivedAt":"2016-03-11T22:36:45Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This function's interface is rather enigmatic, so let's\ndocument it further.\n\nWhile we're here, let's also drop the return value. It will\nalways either be \"0\" or the function will die (consequently,\nneither of its two callers bothered to check the return).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n cache.h | 9 ++++++++-\n setup.c | 4 ++--\n 2 files changed, 10 insertions(+), 3 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex b829410..02e38d1 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -747,7 +747,14 @@ extern int grafts_replace_parents;\n #define GIT_REPO_VERSION_READ 1\n extern int repository_format_version;\n extern int repository_format_precious_objects;\n-extern int check_repository_format(void);\n+\n+/*\n+ * Check the repository format version in the path found in get_git_dir(),\n+ * and die if it is a version we don't understand. Generally one would\n+ * set_git_dir() before calling this, and use it only for \"are we in a valid\n+ * repo?\".\n+ */\n+extern void check_repository_format(void);\n \n #define MTIME_CHANGED\t0x0001\n #define CTIME_CHANGED\t0x0002\ndiff --git a/setup.c b/setup.c\nindex de1a2a7..b2f2e69 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -982,9 +982,9 @@ int check_repository_format_version(const char *var, const char *value, void *cb\n \treturn 0;\n }\n \n-int check_repository_format(void)\n+void check_repository_format(void)\n {\n-\treturn check_repository_format_gently(get_git_dir(), NULL);\n+\tcheck_repository_format_gently(get_git_dir(), NULL);\n }\n \n /*\n-- \n2.8.0.rc2.328.g39e2a47\n"},{"id":"280640","messageId":"20160311223649.GB449@sigill.intra.peff.net","threadId":"41673","inReplyTo":"20160311223620.GA7963@sigill.intra.peff.net","subject":"[PATCH v2 02/10] wrap shared_repository global in get/set accessors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-03-11T22:36:49Z","receivedAt":"2016-03-11T22:36:49Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"It would be useful to control access to the global\nshared_repository, so that we can lazily load its config.\nThe first step to doing so is to make sure all access\ngoes through a set of functions.\n\nThis step is purely mechanical, and should result in no\nchange of behavior.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/init-db.c | 24 ++++++++++++------------\n cache.h           |  4 +++-\n environment.c     | 13 ++++++++++++-\n path.c            | 10 +++++-----\n setup.c           |  2 +-\n 5 files changed, 33 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 6223b7d..e9b2256 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -199,13 +199,13 @@ static int create_default_files(const char *template_path)\n \n \t/* reading existing config may have overwrote it */\n \tif (init_shared_repository != -1)\n-\t\tshared_repository = init_shared_repository;\n+\t\tset_shared_repository(init_shared_repository);\n \n \t/*\n \t * We would have created the above under user's umask -- under\n \t * shared-repository settings, we would need to fix them up.\n \t */\n-\tif (shared_repository) {\n+\tif (get_shared_repository()) {\n \t\tadjust_shared_perm(get_git_dir());\n \t\tadjust_shared_perm(git_path_buf(&buf, \"refs\"));\n \t\tadjust_shared_perm(git_path_buf(&buf, \"refs/heads\"));\n@@ -369,7 +369,7 @@ int init_db(const char *template_dir, unsigned int flags)\n \n \tcreate_object_directory();\n \n-\tif (shared_repository) {\n+\tif (get_shared_repository()) {\n \t\tchar buf[10];\n \t\t/* We do not spell \"group\" and such, so that\n \t\t * the configuration can be read by older version\n@@ -377,12 +377,12 @@ int init_db(const char *template_dir, unsigned int flags)\n \t\t * and compatibility values for PERM_GROUP and\n \t\t * PERM_EVERYBODY.\n \t\t */\n-\t\tif (shared_repository < 0)\n+\t\tif (get_shared_repository() < 0)\n \t\t\t/* force to the mode value */\n-\t\t\txsnprintf(buf, sizeof(buf), \"0%o\", -shared_repository);\n-\t\telse if (shared_repository == PERM_GROUP)\n+\t\t\txsnprintf(buf, sizeof(buf), \"0%o\", -get_shared_repository());\n+\t\telse if (get_shared_repository() == PERM_GROUP)\n \t\t\txsnprintf(buf, sizeof(buf), \"%d\", OLD_PERM_GROUP);\n-\t\telse if (shared_repository == PERM_EVERYBODY)\n+\t\telse if (get_shared_repository() == PERM_EVERYBODY)\n \t\t\txsnprintf(buf, sizeof(buf), \"%d\", OLD_PERM_EVERYBODY);\n \t\telse\n \t\t\tdie(\"BUG: invalid value for shared_repository\");\n@@ -398,7 +398,7 @@ int init_db(const char *template_dir, unsigned int flags)\n \t\t   \"\", and the last '%s%s' is the verbatim directory name. */\n \t\tprintf(_(\"%s%s Git repository in %s%s\\n\"),\n \t\t       reinit ? _(\"Reinitialized existing\") : _(\"Initialized empty\"),\n-\t\t       shared_repository ? _(\" shared\") : \"\",\n+\t\t       get_shared_repository() ? _(\" shared\") : \"\",\n \t\t       git_dir, len && git_dir[len-1] != '/' ? \"/\" : \"\");\n \t}\n \n@@ -493,8 +493,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t\t\t\t * and we know shared_repository should always be 0;\n \t\t\t\t * but just in case we play safe.\n \t\t\t\t */\n-\t\t\t\tsaved = shared_repository;\n-\t\t\t\tshared_repository = 0;\n+\t\t\t\tsaved = get_shared_repository();\n+\t\t\t\tset_shared_repository(0);\n \t\t\t\tswitch (safe_create_leading_directories_const(argv[0])) {\n \t\t\t\tcase SCLD_OK:\n \t\t\t\tcase SCLD_PERMS:\n@@ -506,7 +506,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tdie_errno(_(\"cannot mkdir %s\"), argv[0]);\n \t\t\t\t\tbreak;\n \t\t\t\t}\n-\t\t\t\tshared_repository = saved;\n+\t\t\t\tset_shared_repository(saved);\n \t\t\t\tif (mkdir(argv[0], 0777) < 0)\n \t\t\t\t\tdie_errno(_(\"cannot mkdir %s\"), argv[0]);\n \t\t\t\tmkdir_tried = 1;\n@@ -524,7 +524,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (init_shared_repository != -1)\n-\t\tshared_repository = init_shared_repository;\n+\t\tset_shared_repository(init_shared_repository);\n \n \t/*\n \t * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR\ndiff --git a/cache.h b/cache.h\nindex 02e38d1..fdb9583 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -651,7 +651,6 @@ extern int prefer_symlink_refs;\n extern int log_all_ref_updates;\n extern int warn_ambiguous_refs;\n extern int warn_on_object_refname_ambiguity;\n-extern int shared_repository;\n extern const char *apply_default_whitespace;\n extern const char *apply_default_ignorewhitespace;\n extern const char *git_attributes_file;\n@@ -664,6 +663,9 @@ extern size_t delta_base_cache_limit;\n extern unsigned long big_file_threshold;\n extern unsigned long pack_size_limit_cfg;\n \n+void set_shared_repository(int value);\n+int get_shared_repository(void);\n+\n /*\n  * Do replace refs need to be checked this run?  This variable is\n  * initialized to true unless --no-replace-object is used or\ndiff --git a/environment.c b/environment.c\nindex 6dec9d0..b42e238 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -29,7 +29,6 @@ int repository_format_version;\n int repository_format_precious_objects;\n const char *git_commit_encoding;\n const char *git_log_output_encoding;\n-int shared_repository = PERM_UMASK;\n const char *apply_default_whitespace;\n const char *apply_default_ignorewhitespace;\n const char *git_attributes_file;\n@@ -325,3 +324,15 @@ const char *get_commit_output_encoding(void)\n {\n \treturn git_commit_encoding ? git_commit_encoding : \"UTF-8\";\n }\n+\n+static int the_shared_repository = PERM_UMASK;\n+\n+void set_shared_repository(int value)\n+{\n+\tthe_shared_repository = value;\n+}\n+\n+int get_shared_repository(void)\n+{\n+\treturn the_shared_repository;\n+}\ndiff --git a/path.c b/path.c\nindex 8b7e168..a6f1cd6 100644\n--- a/path.c\n+++ b/path.c\n@@ -699,17 +699,17 @@ static int calc_shared_perm(int mode)\n {\n \tint tweak;\n \n-\tif (shared_repository < 0)\n-\t\ttweak = -shared_repository;\n+\tif (get_shared_repository() < 0)\n+\t\ttweak = -get_shared_repository();\n \telse\n-\t\ttweak = shared_repository;\n+\t\ttweak = get_shared_repository();\n \n \tif (!(mode & S_IWUSR))\n \t\ttweak &= ~0222;\n \tif (mode & S_IXUSR)\n \t\t/* Copy read bits to execute bits */\n \t\ttweak |= (tweak & 0444) >> 2;\n-\tif (shared_repository < 0)\n+\tif (get_shared_repository() < 0)\n \t\tmode = (mode & ~0777) | tweak;\n \telse\n \t\tmode |= tweak;\n@@ -722,7 +722,7 @@ int adjust_shared_perm(const char *path)\n {\n \tint old_mode, new_mode;\n \n-\tif (!shared_repository)\n+\tif (!get_shared_repository())\n \t\treturn 0;\n \tif (get_st_mode_bits(path, &old_mode) < 0)\n \t\treturn -1;\ndiff --git a/setup.c b/setup.c\nindex b2f2e69..ac777c5 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -377,7 +377,7 @@ static int check_repo_format(const char *var, const char *value, void *cb)\n \tif (strcmp(var, \"core.repositoryformatversion\") == 0)\n \t\trepository_format_version = git_config_int(var, value);\n \telse if (strcmp(var, \"core.sharedrepository\") == 0)\n-\t\tshared_repository = git_config_perm(var, value);\n+\t\tset_shared_repository(git_config_perm(var, value));\n \telse if (skip_prefix(var, \"extensions.\", &ext)) {\n \t\t/*\n \t\t * record any known extensions here; otherwise,\n-- \n2.8.0.rc2.328.g39e2a47\n"},{"id":"280642","messageId":"20160311223653.GC449@sigill.intra.peff.net","threadId":"41673","inReplyTo":"20160311223620.GA7963@sigill.intra.peff.net","subject":"[PATCH v2 03/10] lazily load core.sharedrepository","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-03-11T22:36:53Z","receivedAt":"2016-03-11T22:36:53Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The \"shared_repository\" config is loaded as part of\ncheck_repository_format_version, but it's not quite like the\nother values we check there. Something like\ncore.repositoryformatversion only makes sense in per-repo\nconfig, but core.sharedrepository can be set in a per-user\nconfig (e.g., to make all \"git init\" invocations shared by\ndefault).\n\nSo it would make more sense as part of git_default_config.\nCommit 457f06d (Introduce core.sharedrepository, 2005-12-22)\nsays:\n\n  [...]the config variable is set in the function which\n  checks the repository format. If this were done in\n  git_default_config instead, a lot of programs would need\n  to be modified to call git_config(git_default_config)\n  first.\n\nThis is still the case today, but we have one extra trick up\nour sleeve. Now that we have the git_configset\ninfrastructure, it's not so expensive for us to ask for a\nsingle value. So we can simply lazy-load it on demand.\n\nThis should be OK to do in general. There are some problems\nwith loading config before setup_git_directory() is called,\nbut we shouldn't be accessing the value before then (if we\nwere, then it would already be broken, as the variable would\nnot have been set by check_repository_format_version!). The\ntrickiest caller is git-init, but it handles the values\nmanually itself.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n environment.c | 9 +++++++++\n setup.c       | 2 --\n 2 files changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/environment.c b/environment.c\nindex b42e238..8b8c8e8 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -326,13 +326,22 @@ const char *get_commit_output_encoding(void)\n }\n \n static int the_shared_repository = PERM_UMASK;\n+static int need_shared_repository_from_config = 1;\n \n void set_shared_repository(int value)\n {\n \tthe_shared_repository = value;\n+\tneed_shared_repository_from_config = 0;\n }\n \n int get_shared_repository(void)\n {\n+\tif (need_shared_repository_from_config) {\n+\t\tconst char *var = \"core.sharedrepository\";\n+\t\tconst char *value;\n+\t\tif (!git_config_get_value(var, &value))\n+\t\t\tthe_shared_repository = git_config_perm(var, value);\n+\t\tneed_shared_repository_from_config = 0;\n+\t}\n \treturn the_shared_repository;\n }\ndiff --git a/setup.c b/setup.c\nindex ac777c5..a02932b 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -376,8 +376,6 @@ static int check_repo_format(const char *var, const char *value, void *cb)\n \n \tif (strcmp(var, \"core.repositoryformatversion\") == 0)\n \t\trepository_format_version = git_config_int(var, value);\n-\telse if (strcmp(var, \"core.sharedrepository\") == 0)\n-\t\tset_shared_repository(git_config_perm(var, value));\n \telse if (skip_prefix(var, \"extensions.\", &ext)) {\n \t\t/*\n \t\t * record any known extensions here; otherwise,\n-- \n2.8.0.rc2.328.g39e2a47\n"},{"id":"280643","messageId":"20160311223659.GD449@sigill.intra.peff.net","threadId":"41673","inReplyTo":"20160311223620.GA7963@sigill.intra.peff.net","subject":"[PATCH v2 04/10] check_repository_format_gently: stop using git_config_early","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-03-11T22:36:59Z","receivedAt":"2016-03-11T22:36:59Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"There's a chicken-and-egg problem with using the regular\ngit_config during the repository setup process. We get\naround it here by using a special interface that lets us\nspecify the per-repo config, and avoid calling\ngit_pathdup().\n\nBut this interface doesn't actually make sense. It will look\nin the system and per-user config, too; we definitely would\nnot want to accept a core.repositoryformatversion from\nthere.\n\nThe git_config_from_file interface is a better match, as it\nlets us look at a single file.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n setup.c | 11 +++--------\n 1 file changed, 3 insertions(+), 8 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex a02932b..a6013e6 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -409,15 +409,10 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)\n \trepo_config = sb.buf;\n \n \t/*\n-\t * git_config() can't be used here because it calls git_pathdup()\n-\t * to get $GIT_CONFIG/config. That call will make setup_git_env()\n-\t * set git_dir to \".git\".\n-\t *\n-\t * We are in gitdir setup, no git dir has been found useable yet.\n-\t * Use a gentler version of git_config() to check if this repo\n-\t * is a good one.\n+\t * Ignore return value; for historical reasons, we must treat a missing\n+\t * config file as a noop (git-init relies on this).\n \t */\n-\tgit_config_early(fn, NULL, repo_config);\n+\tgit_config_from_file(fn, repo_config, NULL);\n \tif (GIT_REPO_VERSION_READ < repository_format_version) {\n \t\tif (!nongit_ok)\n \t\t\tdie (\"Expected git repo version <= %d, found %d\",\n-- \n2.8.0.rc2.328.g39e2a47\n"},{"id":"280644","messageId":"20160311223703.GE449@sigill.intra.peff.net","threadId":"41673","inReplyTo":"20160311223620.GA7963@sigill.intra.peff.net","subject":"[PATCH v2 05/10] config: drop git_config_early","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-03-11T22:37:03Z","receivedAt":"2016-03-11T22:37:03Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"There are no more callers, and it's a rather confusing\ninterface. This could just be folded into\ngit_config_with_options(), but for the sake of readability,\nwe'll leave it as a separate (static) helper function.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/technical/api-config.txt |  7 -------\n cache.h                                |  1 -\n config.c                               | 12 ++++--------\n 3 files changed, 4 insertions(+), 16 deletions(-)\n\ndiff --git a/Documentation/technical/api-config.txt b/Documentation/technical/api-config.txt\nindex 0d8b99b..20741f3 100644\n--- a/Documentation/technical/api-config.txt\n+++ b/Documentation/technical/api-config.txt\n@@ -63,13 +63,6 @@ parse for configuration, rather than looking in the usual files. Regular\n Specify whether include directives should be followed in parsed files.\n Regular `git_config` defaults to `1`.\n \n-There is a special version of `git_config` called `git_config_early`.\n-This version takes an additional parameter to specify the repository\n-config, instead of having it looked up via `git_path`. This is useful\n-early in a Git program before the repository has been found. Unless\n-you're working with early setup code, you probably don't want to use\n-this.\n-\n Reading Specific Files\n ----------------------\n \ndiff --git a/cache.h b/cache.h\nindex fdb9583..867e73e 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1535,7 +1535,6 @@ extern void git_config(config_fn_t fn, void *);\n extern int git_config_with_options(config_fn_t fn, void *,\n \t\t\t\t   struct git_config_source *config_source,\n \t\t\t\t   int respect_includes);\n-extern int git_config_early(config_fn_t fn, void *, const char *repo_config);\n extern int git_parse_ulong(const char *, unsigned long *);\n extern int git_parse_maybe_bool(const char *);\n extern int git_config_int(const char *, const char *);\ndiff --git a/config.c b/config.c\nindex 9ba40bc..7ddb287 100644\n--- a/config.c\n+++ b/config.c\n@@ -1188,11 +1188,12 @@ int git_config_system(void)\n \treturn !git_env_bool(\"GIT_CONFIG_NOSYSTEM\", 0);\n }\n \n-int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n+static int do_git_config_sequence(config_fn_t fn, void *data)\n {\n \tint ret = 0, found = 0;\n \tchar *xdg_config = xdg_config_home(\"config\");\n \tchar *user_config = expand_user_path(\"~/.gitconfig\");\n+\tchar *repo_config = git_pathdup(\"config\");\n \n \tif (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {\n \t\tret += git_config_from_file(fn, git_etc_gitconfig(),\n@@ -1228,6 +1229,7 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \n \tfree(xdg_config);\n \tfree(user_config);\n+\tfree(repo_config);\n \treturn ret == 0 ? found : ret;\n }\n \n@@ -1235,8 +1237,6 @@ int git_config_with_options(config_fn_t fn, void *data,\n \t\t\t    struct git_config_source *config_source,\n \t\t\t    int respect_includes)\n {\n-\tchar *repo_config = NULL;\n-\tint ret;\n \tstruct config_include_data inc = CONFIG_INCLUDE_INIT;\n \n \tif (respect_includes) {\n@@ -1257,11 +1257,7 @@ int git_config_with_options(config_fn_t fn, void *data,\n \telse if (config_source && config_source->blob)\n \t\treturn git_config_from_blob_ref(fn, config_source->blob, data);\n \n-\trepo_config = git_pathdup(\"config\");\n-\tret = git_config_early(fn, data, repo_config);\n-\tif (repo_config)\n-\t\tfree(repo_config);\n-\treturn ret;\n+\treturn do_git_config_sequence(fn, data);\n }\n \n static void git_config_raw(config_fn_t fn, void *data)\n-- \n2.8.0.rc2.328.g39e2a47\n"},{"id":"280645","messageId":"20160311223706.GF449@sigill.intra.peff.net","threadId":"41673","inReplyTo":"20160311223620.GA7963@sigill.intra.peff.net","subject":"[PATCH v2 06/10] setup: refactor repo format reading and verification","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-03-11T22:37:07Z","receivedAt":"2016-03-11T22:37:07Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When we want to know if we're in a git repository of\nreasonable vintage, we can call check_repository_format_gently(),\nwhich does three things:\n\n  1. Reads the config from the .git/config file.\n\n  2. Verifies that the version info we read is sane.\n\n  3. Writes some global variables based on this.\n\nThere are a few things we could improve here.\n\nOne is that steps 1 and 3 happen together. So if the\nverification in step 2 fails, we still clobber the global\nvariables. This is especially bad if we go on to try another\nrepository directory; we may end up with a state of mixed\nconfig variables.\n\nThe second is there's no way to ask about the repository\nversion for anything besides the main repository we're in.\ngit-init wants to do this, and it's possible that we would\nwant to start doing so for submodules (e.g., to find out\nwhich ref backend they're using).\n\nWe can improve both by splitting the first two steps into\nseparate functions. Now check_repository_format_gently()\ncalls out to steps 1 and 2, and does 3 only if step 2\nsucceeds.\n\nNote that the public interface for read_repository_format()\nand what check_repository_format_gently() needs from it are\nnot quite the same, leading us to have an extra\nread_repository_format_1() helper. The extra needs from\ncheck_repository_format_gently() will go away in a future\npatch, and we can simplify this then to just the public\ninterface.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n cache.h |  24 +++++++++++++\n setup.c | 118 +++++++++++++++++++++++++++++++++++++++++++---------------------\n 2 files changed, 103 insertions(+), 39 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 867e73e..4fc42b5 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -750,6 +750,30 @@ extern int grafts_replace_parents;\n extern int repository_format_version;\n extern int repository_format_precious_objects;\n \n+struct repository_format {\n+\tint version;\n+\tint precious_objects;\n+\tint is_bare;\n+\tchar *work_tree;\n+\tstruct string_list unknown_extensions;\n+};\n+\n+/*\n+ * Read the repository format characteristics from the config file \"path\" into\n+ * \"format\" struct. Returns the numeric version. On error, -1 is returned,\n+ * format->version is set to -1, and all other fields in the struct are\n+ * undefined.\n+ */\n+int read_repository_format(struct repository_format *format, const char *path);\n+\n+/*\n+ * Verify that the repository described by repository_format is something we\n+ * can read. If it is, return 0. Otherwise, return -1, and \"err\" will describe\n+ * any errors encountered.\n+ */\n+int verify_repository_format(const struct repository_format *format,\n+\t\t\t     struct strbuf *err);\n+\n /*\n  * Check the repository format version in the path found in get_git_dir(),\n  * and die if it is a version we don't understand. Generally one would\ndiff --git a/setup.c b/setup.c\nindex a6013e6..8aa49a9 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -5,7 +5,6 @@\n static int inside_git_dir = -1;\n static int inside_work_tree = -1;\n static int work_tree_config_is_bogus;\n-static struct string_list unknown_extensions = STRING_LIST_INIT_DUP;\n \n /*\n  * The input parameter must contain an absolute path, and it must already be\n@@ -370,12 +369,13 @@ void setup_work_tree(void)\n \tinitialized = 1;\n }\n \n-static int check_repo_format(const char *var, const char *value, void *cb)\n+static int check_repo_format(const char *var, const char *value, void *vdata)\n {\n+\tstruct repository_format *data = vdata;\n \tconst char *ext;\n \n \tif (strcmp(var, \"core.repositoryformatversion\") == 0)\n-\t\trepository_format_version = git_config_int(var, value);\n+\t\tdata->version = git_config_int(var, value);\n \telse if (skip_prefix(var, \"extensions.\", &ext)) {\n \t\t/*\n \t\t * record any known extensions here; otherwise,\n@@ -385,61 +385,104 @@ static int check_repo_format(const char *var, const char *value, void *cb)\n \t\tif (!strcmp(ext, \"noop\"))\n \t\t\t;\n \t\telse if (!strcmp(ext, \"preciousobjects\"))\n-\t\t\trepository_format_precious_objects = git_config_bool(var, value);\n+\t\t\tdata->precious_objects = git_config_bool(var, value);\n \t\telse\n-\t\t\tstring_list_append(&unknown_extensions, ext);\n+\t\t\tstring_list_append(&data->unknown_extensions, ext);\n \t}\n \treturn 0;\n }\n \n+static int read_repository_format_1(struct repository_format *, config_fn_t,\n+\t\t\t\t    const char *);\n+\n static int check_repository_format_gently(const char *gitdir, int *nongit_ok)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n-\tconst char *repo_config;\n+\tstruct strbuf err = STRBUF_INIT;\n+\tstruct repository_format candidate;\n \tconfig_fn_t fn;\n-\tint ret = 0;\n-\n-\tstring_list_clear(&unknown_extensions, 0);\n \n \tif (get_common_dir(&sb, gitdir))\n \t\tfn = check_repo_format;\n \telse\n \t\tfn = check_repository_format_version;\n+\n \tstrbuf_addstr(&sb, \"/config\");\n-\trepo_config = sb.buf;\n+\tread_repository_format_1(&candidate, fn, sb.buf);\n+\tstrbuf_release(&sb);\n \n \t/*\n-\t * Ignore return value; for historical reasons, we must treat a missing\n-\t * config file as a noop (git-init relies on this).\n+\t * For historical use of check_repository_format() in git-init,\n+\t * we treat a missing config as a silent \"ok\", even when nongit_ok\n+\t * is unset.\n \t */\n-\tgit_config_from_file(fn, repo_config, NULL);\n-\tif (GIT_REPO_VERSION_READ < repository_format_version) {\n-\t\tif (!nongit_ok)\n-\t\t\tdie (\"Expected git repo version <= %d, found %d\",\n-\t\t\t     GIT_REPO_VERSION_READ, repository_format_version);\n-\t\twarning(\"Expected git repo version <= %d, found %d\",\n-\t\t\tGIT_REPO_VERSION_READ, repository_format_version);\n-\t\twarning(\"Please upgrade Git\");\n-\t\t*nongit_ok = -1;\n-\t\tret = -1;\n+\tif (candidate.version < 0)\n+\t\treturn 0;\n+\n+\tif (verify_repository_format(&candidate, &err) < 0) {\n+\t\tif (nongit_ok) {\n+\t\t\twarning(\"%s\", err.buf);\n+\t\t\tstrbuf_release(&err);\n+\t\t\t*nongit_ok = -1;\n+\t\t\treturn -1;\n+\t\t}\n+\t\tdie(\"%s\", err.buf);\n \t}\n \n-\tif (repository_format_version >= 1 && unknown_extensions.nr) {\n+\trepository_format_version = candidate.version;\n+\trepository_format_precious_objects = candidate.precious_objects;\n+\tstring_list_clear(&candidate.unknown_extensions, 0);\n+\tif (candidate.is_bare != -1) {\n+\t\tis_bare_repository_cfg = candidate.is_bare;\n+\t\tif (is_bare_repository_cfg == 1)\n+\t\t\tinside_work_tree = -1;\n+\t}\n+\tif (candidate.work_tree) {\n+\t\tfree(git_work_tree_cfg);\n+\t\tgit_work_tree_cfg = candidate.work_tree;\n+\t\tinside_work_tree = -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int read_repository_format_1(struct repository_format *format,\n+\t\t\t\t    config_fn_t fn, const char *path)\n+{\n+\tmemset(format, 0, sizeof(*format));\n+\tformat->version = -1;\n+\tformat->is_bare = -1;\n+\tstring_list_init(&format->unknown_extensions, 1);\n+\tgit_config_from_file(fn, path, format);\n+\treturn format->version;\n+}\n+\n+int read_repository_format(struct repository_format *format, const char *path)\n+{\n+\treturn read_repository_format_1(format, check_repository_format_version, path);\n+}\n+\n+int verify_repository_format(const struct repository_format *format,\n+\t\t\t     struct strbuf *err)\n+{\n+\tif (GIT_REPO_VERSION_READ < format->version) {\n+\t\tstrbuf_addf(err, \"Expected git repo version <= %d, found %d\",\n+\t\t\t    GIT_REPO_VERSION_READ, format->version);\n+\t\treturn -1;\n+\t}\n+\n+\tif (format->version >= 1 && format->unknown_extensions.nr) {\n \t\tint i;\n \n-\t\tif (!nongit_ok)\n-\t\t\tdie(\"unknown repository extension: %s\",\n-\t\t\t    unknown_extensions.items[0].string);\n+\t\tstrbuf_addstr(err, \"unknown repository extensions found:\");\n \n-\t\tfor (i = 0; i < unknown_extensions.nr; i++)\n-\t\t\twarning(\"unknown repository extension: %s\",\n-\t\t\t\tunknown_extensions.items[i].string);\n-\t\t*nongit_ok = -1;\n-\t\tret = -1;\n+\t\tfor (i = 0; i < format->unknown_extensions.nr; i++)\n+\t\t\tstrbuf_addf(err, \"\\n\\t%s\",\n+\t\t\t\t    format->unknown_extensions.items[i].string);\n+\t\treturn -1;\n \t}\n \n-\tstrbuf_release(&sb);\n-\treturn ret;\n+\treturn 0;\n }\n \n /*\n@@ -958,19 +1001,16 @@ int git_config_perm(const char *var, const char *value)\n \n int check_repository_format_version(const char *var, const char *value, void *cb)\n {\n+\tstruct repository_format *data = cb;\n \tint ret = check_repo_format(var, value, cb);\n \tif (ret)\n \t\treturn ret;\n \tif (strcmp(var, \"core.bare\") == 0) {\n-\t\tis_bare_repository_cfg = git_config_bool(var, value);\n-\t\tif (is_bare_repository_cfg == 1)\n-\t\t\tinside_work_tree = -1;\n+\t\tdata->is_bare = git_config_bool(var, value);\n \t} else if (strcmp(var, \"core.worktree\") == 0) {\n \t\tif (!value)\n \t\t\treturn config_error_nonbool(var);\n-\t\tfree(git_work_tree_cfg);\n-\t\tgit_work_tree_cfg = xstrdup(value);\n-\t\tinside_work_tree = -1;\n+\t\tdata->work_tree = xstrdup(value);\n \t}\n \treturn 0;\n }\n-- \n2.8.0.rc2.328.g39e2a47\n"},{"id":"280646","messageId":"20160311223710.GG449@sigill.intra.peff.net","threadId":"41673","inReplyTo":"20160311223620.GA7963@sigill.intra.peff.net","subject":"[PATCH v2 07/10] init: use setup.c's repo version verification","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-03-11T22:37:11Z","receivedAt":"2016-03-11T22:37:11Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We check our templates to make sure they are from a\nversion of git we understand (otherwise we would init a\nrepository we cannot ourselves run in!). But our simple\ninteger check has fallen behind the times. Let's use the\nhelpers that setup.c provides to do it right.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/init-db.c | 21 ++++++++++++---------\n 1 file changed, 12 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex e9b2256..d9934f3 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -95,6 +95,8 @@ static void copy_templates(const char *template_dir)\n \tstruct strbuf path = STRBUF_INIT;\n \tstruct strbuf template_path = STRBUF_INIT;\n \tsize_t template_len;\n+\tstruct repository_format template_format;\n+\tstruct strbuf err = STRBUF_INIT;\n \tDIR *dir;\n \tchar *to_free = NULL;\n \n@@ -121,17 +123,18 @@ static void copy_templates(const char *template_dir)\n \n \t/* Make sure that template is from the correct vintage */\n \tstrbuf_addstr(&template_path, \"config\");\n-\trepository_format_version = 0;\n-\tgit_config_from_file(check_repository_format_version,\n-\t\t\t     template_path.buf, NULL);\n+\tread_repository_format(&template_format, template_path.buf);\n \tstrbuf_setlen(&template_path, template_len);\n \n-\tif (repository_format_version &&\n-\t    repository_format_version != GIT_REPO_VERSION) {\n-\t\twarning(_(\"not copying templates of \"\n-\t\t\t\"a wrong format version %d from '%s'\"),\n-\t\t\trepository_format_version,\n-\t\t\ttemplate_dir);\n+\t/*\n+\t * No mention of version at all is OK, but anything else should be\n+\t * verified.\n+\t */\n+\tif (template_format.version >= 0 &&\n+\t    verify_repository_format(&template_format, &err) < 0) {\n+\t\twarning(_(\"not copying templates from '%s': %s\"),\n+\t\t\t  template_dir, err.buf);\n+\t\tstrbuf_release(&err);\n \t\tgoto close_free_return;\n \t}\n \n-- \n2.8.0.rc2.328.g39e2a47\n"},{"id":"280647","messageId":"20160311223714.GH449@sigill.intra.peff.net","threadId":"41673","inReplyTo":"20160311223620.GA7963@sigill.intra.peff.net","subject":"[PATCH v2 08/10] setup: unify repository version callbacks","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-03-11T22:37:14Z","receivedAt":"2016-03-11T22:37:14Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Once upon a time, check_repository_format_gently would parse\nthe config with a single callback, and that callback would\nset up a bunch of global variables. But now that we have\nseparate workdirs, we have to be more careful. Commit\n31e26eb (setup.c: support multi-checkout repo setup,\n2014-11-30) introduced a reduced callback which omits some\nvalues like core.worktree. In the \"main\" callback we call\nthe reduced one, and then add back in the missing variables.\n\nNow that we have split the config-parsing from the munging\nof the global variables, we can do it all with a single\ncallback, and keep all of the \"are we in a separate workdir\"\nlogic together.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n cache.h |  1 -\n setup.c | 65 +++++++++++++++++++++++------------------------------------------\n 2 files changed, 23 insertions(+), 43 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 4fc42b5..7704bc6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1582,7 +1582,6 @@ extern void git_config_set_multivar_in_file(const char *, const char *, const ch\n extern int git_config_rename_section(const char *, const char *);\n extern int git_config_rename_section_in_file(const char *, const char *, const char *);\n extern const char *git_etc_gitconfig(void);\n-extern int check_repository_format_version(const char *var, const char *value, void *cb);\n extern int git_env_bool(const char *, int);\n extern unsigned long git_env_ulong(const char *, unsigned long);\n extern int git_config_system(void);\ndiff --git a/setup.c b/setup.c\nindex 8aa49a9..fbe7ec1 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -388,27 +388,26 @@ static int check_repo_format(const char *var, const char *value, void *vdata)\n \t\t\tdata->precious_objects = git_config_bool(var, value);\n \t\telse\n \t\t\tstring_list_append(&data->unknown_extensions, ext);\n+\t} else if (strcmp(var, \"core.bare\") == 0) {\n+\t\tdata->is_bare = git_config_bool(var, value);\n+\t} else if (strcmp(var, \"core.worktree\") == 0) {\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\tdata->work_tree = xstrdup(value);\n \t}\n \treturn 0;\n }\n \n-static int read_repository_format_1(struct repository_format *, config_fn_t,\n-\t\t\t\t    const char *);\n-\n static int check_repository_format_gently(const char *gitdir, int *nongit_ok)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct repository_format candidate;\n-\tconfig_fn_t fn;\n-\n-\tif (get_common_dir(&sb, gitdir))\n-\t\tfn = check_repo_format;\n-\telse\n-\t\tfn = check_repository_format_version;\n+\tint has_common;\n \n+\thas_common = get_common_dir(&sb, gitdir);\n \tstrbuf_addstr(&sb, \"/config\");\n-\tread_repository_format_1(&candidate, fn, sb.buf);\n+\tread_repository_format(&candidate, sb.buf);\n \tstrbuf_release(&sb);\n \n \t/*\n@@ -432,36 +431,34 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)\n \trepository_format_version = candidate.version;\n \trepository_format_precious_objects = candidate.precious_objects;\n \tstring_list_clear(&candidate.unknown_extensions, 0);\n-\tif (candidate.is_bare != -1) {\n-\t\tis_bare_repository_cfg = candidate.is_bare;\n-\t\tif (is_bare_repository_cfg == 1)\n+\tif (!has_common) {\n+\t\tif (candidate.is_bare != -1) {\n+\t\t\tis_bare_repository_cfg = candidate.is_bare;\n+\t\t\tif (is_bare_repository_cfg == 1)\n+\t\t\t\tinside_work_tree = -1;\n+\t\t}\n+\t\tif (candidate.work_tree) {\n+\t\t\tfree(git_work_tree_cfg);\n+\t\t\tgit_work_tree_cfg = candidate.work_tree;\n \t\t\tinside_work_tree = -1;\n-\t}\n-\tif (candidate.work_tree) {\n-\t\tfree(git_work_tree_cfg);\n-\t\tgit_work_tree_cfg = candidate.work_tree;\n-\t\tinside_work_tree = -1;\n+\t\t}\n+\t} else {\n+\t\tfree(candidate.work_tree);\n \t}\n \n \treturn 0;\n }\n \n-static int read_repository_format_1(struct repository_format *format,\n-\t\t\t\t    config_fn_t fn, const char *path)\n+int read_repository_format(struct repository_format *format, const char *path)\n {\n \tmemset(format, 0, sizeof(*format));\n \tformat->version = -1;\n \tformat->is_bare = -1;\n \tstring_list_init(&format->unknown_extensions, 1);\n-\tgit_config_from_file(fn, path, format);\n+\tgit_config_from_file(check_repo_format, path, format);\n \treturn format->version;\n }\n \n-int read_repository_format(struct repository_format *format, const char *path)\n-{\n-\treturn read_repository_format_1(format, check_repository_format_version, path);\n-}\n-\n int verify_repository_format(const struct repository_format *format,\n \t\t\t     struct strbuf *err)\n {\n@@ -999,22 +996,6 @@ int git_config_perm(const char *var, const char *value)\n \treturn -(i & 0666);\n }\n \n-int check_repository_format_version(const char *var, const char *value, void *cb)\n-{\n-\tstruct repository_format *data = cb;\n-\tint ret = check_repo_format(var, value, cb);\n-\tif (ret)\n-\t\treturn ret;\n-\tif (strcmp(var, \"core.bare\") == 0) {\n-\t\tdata->is_bare = git_config_bool(var, value);\n-\t} else if (strcmp(var, \"core.worktree\") == 0) {\n-\t\tif (!value)\n-\t\t\treturn config_error_nonbool(var);\n-\t\tdata->work_tree = xstrdup(value);\n-\t}\n-\treturn 0;\n-}\n-\n void check_repository_format(void)\n {\n \tcheck_repository_format_gently(get_git_dir(), NULL);\n-- \n2.8.0.rc2.328.g39e2a47\n"},{"id":"280648","messageId":"20160311223718.GI449@sigill.intra.peff.net","threadId":"41673","inReplyTo":"20160311223620.GA7963@sigill.intra.peff.net","subject":"[PATCH v2 09/10] setup: drop repository_format_version global","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-03-11T22:37:18Z","receivedAt":"2016-03-11T22:37:18Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Nobody reads this anymore, and they're not likely to; the\ninteresting thing is whether or not we passed\ncheck_repository_format(), and possibly the individual\n\"extension\" variables.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n cache.h       | 1 -\n environment.c | 1 -\n setup.c       | 1 -\n 3 files changed, 3 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 7704bc6..bec6db5 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -747,7 +747,6 @@ extern int grafts_replace_parents;\n  */\n #define GIT_REPO_VERSION 0\n #define GIT_REPO_VERSION_READ 1\n-extern int repository_format_version;\n extern int repository_format_precious_objects;\n \n struct repository_format {\ndiff --git a/environment.c b/environment.c\nindex 8b8c8e8..d9e3861 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -25,7 +25,6 @@ int log_all_ref_updates = -1; /* unspecified */\n int warn_ambiguous_refs = 1;\n int warn_on_object_refname_ambiguity = 1;\n int ref_paranoia = -1;\n-int repository_format_version;\n int repository_format_precious_objects;\n const char *git_commit_encoding;\n const char *git_log_output_encoding;\ndiff --git a/setup.c b/setup.c\nindex fbe7ec1..1314c17 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -428,7 +428,6 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)\n \t\tdie(\"%s\", err.buf);\n \t}\n \n-\trepository_format_version = candidate.version;\n \trepository_format_precious_objects = candidate.precious_objects;\n \tstring_list_clear(&candidate.unknown_extensions, 0);\n \tif (!has_common) {\n-- \n2.8.0.rc2.328.g39e2a47\n"},{"id":"280649","messageId":"20160311223722.GJ449@sigill.intra.peff.net","threadId":"41673","inReplyTo":"20160311223620.GA7963@sigill.intra.peff.net","subject":"[PATCH v2 10/10] verify_repository_format: mark messages for translation","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-03-11T22:37:22Z","receivedAt":"2016-03-11T22:37:22Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"These messages are human-readable and should be translated.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n setup.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex 1314c17..09d3720 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -462,7 +462,7 @@ int verify_repository_format(const struct repository_format *format,\n \t\t\t     struct strbuf *err)\n {\n \tif (GIT_REPO_VERSION_READ < format->version) {\n-\t\tstrbuf_addf(err, \"Expected git repo version <= %d, found %d\",\n+\t\tstrbuf_addf(err, _(\"Expected git repo version <= %d, found %d\"),\n \t\t\t    GIT_REPO_VERSION_READ, format->version);\n \t\treturn -1;\n \t}\n@@ -470,7 +470,7 @@ int verify_repository_format(const struct repository_format *format,\n \tif (format->version >= 1 && format->unknown_extensions.nr) {\n \t\tint i;\n \n-\t\tstrbuf_addstr(err, \"unknown repository extensions found:\");\n+\t\tstrbuf_addstr(err, _(\"unknown repository extensions found:\"));\n \n \t\tfor (i = 0; i < format->unknown_extensions.nr; i++)\n \t\t\tstrbuf_addf(err, \"\\n\\t%s\",\n-- \n2.8.0.rc2.328.g39e2a47\n"},{"id":"280650","messageId":"xmqqegbga8uo.fsf@gitster.mtv.corp.google.com","threadId":"41673","inReplyTo":"20160311223703.GE449@sigill.intra.peff.net","subject":"Re: [PATCH v2 05/10] config: drop git_config_early","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-03-11T23:33:03Z","receivedAt":"2016-03-11T23:33:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> There are no more callers, and it's a rather confusing\n> interface. This could just be folded into\n> git_config_with_options(), but for the sake of readability,\n> we'll leave it as a separate (static) helper function.\n\nYay!\n\n;-)\n"}]}