teach config --blob option to parse config from database
This can be used to read configuration values directly from git's database. For example it is useful for reading to be checked out .gitmodules files directly from the database. Signed-off-by: Heiko Voigt <hvoigt@hvoigt.net> Acked-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Heiko Voigt committed
Jul 12, 2013 at 00:46 UTC
1bc888193e1044db317a45b9a4c8d2b87b998f40
5 files changed
+193
-7
Documentation/git-config.txt
+7
@@ -118,6 +118,13 @@ See also <<FILES>>.
118
--file config-file::
119
Use the given config file instead of the one specified by GIT_CONFIG.
120
121
+--blob blob::
122
+ Similar to '--file' but use the given blob instead of a file. E.g.
123
+ you can use 'master:.gitmodules' to read values from the file
124
+ '.gitmodules' in the master branch. See "SPECIFYING REVISIONS"
125
+ section in linkgit:gitrevisions[7] for a more complete list of
126
+ ways to spell blob names.
127
+
128
--remove-section::
129
Remove the given section from the configuration file.
130
builtin/config.c
+27
-4
@@ -21,6 +21,7 @@ static char term = '\n';
21
22
static int use_global_config, use_system_config, use_local_config;
23
static const char *given_config_file;
24
+static const char *given_config_blob;
25
static int actions, types;
26
static const char *get_color_slot, *get_colorbool_slot;
27
static int end_null;
@@ -53,6 +54,7 @@ static struct option builtin_config_options[] = {
54
OPT_BOOLEAN(0, "system", &use_system_config, N_("use system config file")),
55
OPT_BOOLEAN(0, "local", &use_local_config, N_("use repository config file")),
56
OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")),
57
+ OPT_STRING(0, "blob", &given_config_blob, N_("blob-id"), N_("read config from given blob object")),
58
OPT_GROUP(N_("Action")),
59
OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
60
OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
@@ -218,7 +220,8 @@ static int get_value(const char *key_, const char *regex_)
220
}
221
222
git_config_with_options(collect_config, &values,
221
- given_config_file, respect_includes);
223
+ given_config_file, given_config_blob,
224
+ respect_includes);
225
226
ret = !values.nr;
227
@@ -302,7 +305,8 @@ static void get_color(const char *def_color)
305
get_color_found = 0;
306
parsed_color[0] = '\0';
307
git_config_with_options(git_get_color_config, NULL,
305
- given_config_file, respect_includes);
308
+ given_config_file, given_config_blob,
309
+ respect_includes);
310
311
if (!get_color_found && def_color)
312
color_parse(def_color, "command line", parsed_color);
@@ -330,7 +334,8 @@ static int get_colorbool(int print)
334
get_colorbool_found = -1;
335
get_diff_color_found = -1;
336
git_config_with_options(git_get_colorbool_config, NULL,
333
- given_config_file, respect_includes);
337
+ given_config_file, given_config_blob,
338
+ respect_includes);
339
340
if (get_colorbool_found < 0) {
341
if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -348,6 +353,12 @@ static int get_colorbool(int print)
353
return get_colorbool_found ? 0 : 1;
354
}
355
356
+static void check_blob_write(void)
357
+{
358
+ if (given_config_blob)
359
+ die("writing config blobs is not supported");
360
+}
361
+
362
int cmd_config(int argc, const char **argv, const char *prefix)
363
{
364
int nongit = !startup_info->have_repository;
@@ -359,7 +370,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
370
builtin_config_usage,
371
PARSE_OPT_STOP_AT_NON_OPTION);
372
362
- if (use_global_config + use_system_config + use_local_config + !!given_config_file > 1) {
373
+ if (use_global_config + use_system_config + use_local_config +
374
+ !!given_config_file + !!given_config_blob > 1) {
375
error("only one config file at a time.");
376
usage_with_options(builtin_config_usage, builtin_config_options);
377
}
@@ -438,6 +450,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
450
check_argc(argc, 0, 0);
451
if (git_config_with_options(show_all_config, NULL,
452
given_config_file,
453
+ given_config_blob,
454
respect_includes) < 0) {
455
if (given_config_file)
456
die_errno("unable to read config file '%s'",
@@ -450,6 +463,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
463
check_argc(argc, 0, 0);
464
if (!given_config_file && nongit)
465
die("not in a git directory");
466
+ if (given_config_blob)
467
+ die("editing blobs is not supported");
468
git_config(git_default_config, NULL);
469
launch_editor(given_config_file ?
470
given_config_file : git_path("config"),
@@ -457,6 +472,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
472
}
473
else if (actions == ACTION_SET) {
474
int ret;
475
+ check_blob_write();
476
check_argc(argc, 2, 2);
477
value = normalize_value(argv[0], argv[1]);
478
ret = git_config_set_in_file(given_config_file, argv[0], value);
@@ -466,18 +482,21 @@ int cmd_config(int argc, const char **argv, const char *prefix)
482
return ret;
483
}
484
else if (actions == ACTION_SET_ALL) {
485
+ check_blob_write();
486
check_argc(argc, 2, 3);
487
value = normalize_value(argv[0], argv[1]);
488
return git_config_set_multivar_in_file(given_config_file,
489
argv[0], value, argv[2], 0);
490
}
491
else if (actions == ACTION_ADD) {
492
+ check_blob_write();
493
check_argc(argc, 2, 2);
494
value = normalize_value(argv[0], argv[1]);
495
return git_config_set_multivar_in_file(given_config_file,
496
argv[0], value, "^$", 0);
497
}
498
else if (actions == ACTION_REPLACE_ALL) {
499
+ check_blob_write();
500
check_argc(argc, 2, 3);
501
value = normalize_value(argv[0], argv[1]);
502
return git_config_set_multivar_in_file(given_config_file,
@@ -500,6 +519,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
519
return get_value(argv[0], argv[1]);
520
}
521
else if (actions == ACTION_UNSET) {
522
+ check_blob_write();
523
check_argc(argc, 1, 2);
524
if (argc == 2)
525
return git_config_set_multivar_in_file(given_config_file,
@@ -509,12 +529,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)
529
argv[0], NULL);
530
}
531
else if (actions == ACTION_UNSET_ALL) {
532
+ check_blob_write();
533
check_argc(argc, 1, 2);
534
return git_config_set_multivar_in_file(given_config_file,
535
argv[0], NULL, argv[1], 1);
536
}
537
else if (actions == ACTION_RENAME_SECTION) {
538
int ret;
539
+ check_blob_write();
540
check_argc(argc, 2, 2);
541
ret = git_config_rename_section_in_file(given_config_file,
542
argv[0], argv[1]);
@@ -525,6 +547,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
547
}
548
else if (actions == ACTION_REMOVE_SECTION) {
549
int ret;
550
+ check_blob_write();
551
check_argc(argc, 1, 1);
552
ret = git_config_rename_section_in_file(given_config_file,
553
argv[0], NULL);
cache.h
+5
-1
@@ -1142,11 +1142,15 @@ extern int update_server_info(int);
1142
typedef int (*config_fn_t)(const char *, const char *, void *);
1143
extern int git_default_config(const char *, const char *, void *);
1144
extern int git_config_from_file(config_fn_t fn, const char *, void *);
1145
+extern int git_config_from_buf(config_fn_t fn, const char *name,
1146
+ const char *buf, size_t len, void *data);
1147
extern void git_config_push_parameter(const char *text);
1148
extern int git_config_from_parameters(config_fn_t fn, void *data);
1149
extern int git_config(config_fn_t fn, void *);
1150
extern int git_config_with_options(config_fn_t fn, void *,
1149
- const char *filename, int respect_includes);
1151
+ const char *filename,
1152
+ const char *blob_ref,
1153
+ int respect_includes);
1154
extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
1155
extern int git_parse_ulong(const char *, unsigned long *);
1156
extern int git_config_int(const char *, const char *);
config.c
+84
-2
@@ -14,6 +14,11 @@ struct config_source {
14
struct config_source *prev;
15
union {
16
FILE *file;
17
+ struct config_buf {
18
+ const char *buf;
19
+ size_t len;
20
+ size_t pos;
21
+ } buf;
22
} u;
23
const char *name;
24
int linenr;
@@ -45,6 +50,28 @@ static long config_file_ftell(struct config_source *conf)
50
return ftell(conf->u.file);
51
}
52
53
+
54
+static int config_buf_fgetc(struct config_source *conf)
55
+{
56
+ if (conf->u.buf.pos < conf->u.buf.len)
57
+ return conf->u.buf.buf[conf->u.buf.pos++];
58
+
59
+ return EOF;
60
+}
61
+
62
+static int config_buf_ungetc(int c, struct config_source *conf)
63
+{
64
+ if (conf->u.buf.pos > 0)
65
+ return conf->u.buf.buf[--conf->u.buf.pos];
66
+
67
+ return EOF;
68
+}
69
+
70
+static long config_buf_ftell(struct config_source *conf)
71
+{
72
+ return conf->u.buf.pos;
73
+}
74
+
75
#define MAX_INCLUDE_DEPTH 10
76
static const char include_depth_advice[] =
77
"exceeded maximum include depth (%d) while including\n"
@@ -961,6 +988,57 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
988
return ret;
989
}
990
991
+int git_config_from_buf(config_fn_t fn, const char *name, const char *buf,
992
+ size_t len, void *data)
993
+{
994
+ struct config_source top;
995
+
996
+ top.u.buf.buf = buf;
997
+ top.u.buf.len = len;
998
+ top.u.buf.pos = 0;
999
+ top.name = name;
1000
+ top.fgetc = config_buf_fgetc;
1001
+ top.ungetc = config_buf_ungetc;
1002
+ top.ftell = config_buf_ftell;
1003
+
1004
+ return do_config_from(&top, fn, data);
1005
+}
1006
+
1007
+static int git_config_from_blob_sha1(config_fn_t fn,
1008
+ const char *name,
1009
+ const unsigned char *sha1,
1010
+ void *data)
1011
+{
1012
+ enum object_type type;
1013
+ char *buf;
1014
+ unsigned long size;
1015
+ int ret;
1016
+
1017
+ buf = read_sha1_file(sha1, &type, &size);
1018
+ if (!buf)
1019
+ return error("unable to load config blob object '%s'", name);
1020
+ if (type != OBJ_BLOB) {
1021
+ free(buf);
1022
+ return error("reference '%s' does not point to a blob", name);
1023
+ }
1024
+
1025
+ ret = git_config_from_buf(fn, name, buf, size, data);
1026
+ free(buf);
1027
+
1028
+ return ret;
1029
+}
1030
+
1031
+static int git_config_from_blob_ref(config_fn_t fn,
1032
+ const char *name,
1033
+ void *data)
1034
+{
1035
+ unsigned char sha1[20];
1036
+
1037
+ if (get_sha1(name, sha1) < 0)
1038
+ return error("unable to resolve config blob '%s'", name);
1039
+ return git_config_from_blob_sha1(fn, name, sha1, data);
1040
+}
1041
+
1042
const char *git_etc_gitconfig(void)
1043
{
1044
static const char *system_wide;
@@ -1026,7 +1104,9 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
1104
}
1105
1106
int git_config_with_options(config_fn_t fn, void *data,
1029
- const char *filename, int respect_includes)
1107
+ const char *filename,
1108
+ const char *blob_ref,
1109
+ int respect_includes)
1110
{
1111
char *repo_config = NULL;
1112
int ret;
@@ -1045,6 +1125,8 @@ int git_config_with_options(config_fn_t fn, void *data,
1125
*/
1126
if (filename)
1127
return git_config_from_file(fn, filename, data);
1128
+ else if (blob_ref)
1129
+ return git_config_from_blob_ref(fn, blob_ref, data);
1130
1131
repo_config = git_pathdup("config");
1132
ret = git_config_early(fn, data, repo_config);
@@ -1055,7 +1137,7 @@ int git_config_with_options(config_fn_t fn, void *data,
1137
1138
int git_config(config_fn_t fn, void *data)
1139
{
1058
- return git_config_with_options(fn, data, NULL, 1);
1140
+ return git_config_with_options(fn, data, NULL, NULL, 1);
1141
}
1142
1143
/*
t/t1307-config-blob.sh
new
+70
@@ -0,0 +1,70 @@
1
+#!/bin/sh
2
+
3
+test_description='support for reading config from a blob'
4
+. ./test-lib.sh
5
+
6
+test_expect_success 'create config blob' '
7
+ cat >config <<-\EOF &&
8
+ [some]
9
+ value = 1
10
+ EOF
11
+ git add config &&
12
+ git commit -m foo
13
+'
14
+
15
+test_expect_success 'list config blob contents' '
16
+ echo some.value=1 >expect &&
17
+ git config --blob=HEAD:config --list >actual &&
18
+ test_cmp expect actual
19
+'
20
+
21
+test_expect_success 'fetch value from blob' '
22
+ echo true >expect &&
23
+ git config --blob=HEAD:config --bool some.value >actual &&
24
+ test_cmp expect actual
25
+'
26
+
27
+test_expect_success 'reading non-existing value from blob is an error' '
28
+ test_must_fail git config --blob=HEAD:config non.existing
29
+'
30
+
31
+test_expect_success 'reading from blob and file is an error' '
32
+ test_must_fail git config --blob=HEAD:config --system --list
33
+'
34
+
35
+test_expect_success 'reading from missing ref is an error' '
36
+ test_must_fail git config --blob=HEAD:doesnotexist --list
37
+'
38
+
39
+test_expect_success 'reading from non-blob is an error' '
40
+ test_must_fail git config --blob=HEAD --list
41
+'
42
+
43
+test_expect_success 'setting a value in a blob is an error' '
44
+ test_must_fail git config --blob=HEAD:config some.value foo
45
+'
46
+
47
+test_expect_success 'deleting a value in a blob is an error' '
48
+ test_must_fail git config --blob=HEAD:config --unset some.value
49
+'
50
+
51
+test_expect_success 'editing a blob is an error' '
52
+ test_must_fail git config --blob=HEAD:config --edit
53
+'
54
+
55
+test_expect_success 'parse errors in blobs are properly attributed' '
56
+ cat >config <<-\EOF &&
57
+ [some]
58
+ value = "
59
+ EOF
60
+ git add config &&
61
+ git commit -m broken &&
62
+
63
+ test_must_fail git config --blob=HEAD:config some.value 2>err &&
64
+
65
+ # just grep for our token as the exact error message is likely to
66
+ # change or be internationalized
67
+ grep "HEAD:config" err
68
+'
69
+
70
+test_done