Skip to content

Commit 1bc8881

Browse files
hvoigtgitster
authored andcommitted
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 <[email protected]> Acked-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4d8dd14 commit 1bc8881

File tree

5 files changed

+193
-7
lines changed

5 files changed

+193
-7
lines changed

Documentation/git-config.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ See also <<FILES>>.
118118
--file config-file::
119119
Use the given config file instead of the one specified by GIT_CONFIG.
120120

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+
121128
--remove-section::
122129
Remove the given section from the configuration file.
123130

builtin/config.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ static char term = '\n';
2121

2222
static int use_global_config, use_system_config, use_local_config;
2323
static const char *given_config_file;
24+
static const char *given_config_blob;
2425
static int actions, types;
2526
static const char *get_color_slot, *get_colorbool_slot;
2627
static int end_null;
@@ -53,6 +54,7 @@ static struct option builtin_config_options[] = {
5354
OPT_BOOLEAN(0, "system", &use_system_config, N_("use system config file")),
5455
OPT_BOOLEAN(0, "local", &use_local_config, N_("use repository config file")),
5556
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")),
5658
OPT_GROUP(N_("Action")),
5759
OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
5860
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_)
218220
}
219221

220222
git_config_with_options(collect_config, &values,
221-
given_config_file, respect_includes);
223+
given_config_file, given_config_blob,
224+
respect_includes);
222225

223226
ret = !values.nr;
224227

@@ -302,7 +305,8 @@ static void get_color(const char *def_color)
302305
get_color_found = 0;
303306
parsed_color[0] = '\0';
304307
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);
306310

307311
if (!get_color_found && def_color)
308312
color_parse(def_color, "command line", parsed_color);
@@ -330,7 +334,8 @@ static int get_colorbool(int print)
330334
get_colorbool_found = -1;
331335
get_diff_color_found = -1;
332336
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);
334339

335340
if (get_colorbool_found < 0) {
336341
if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -348,6 +353,12 @@ static int get_colorbool(int print)
348353
return get_colorbool_found ? 0 : 1;
349354
}
350355

356+
static void check_blob_write(void)
357+
{
358+
if (given_config_blob)
359+
die("writing config blobs is not supported");
360+
}
361+
351362
int cmd_config(int argc, const char **argv, const char *prefix)
352363
{
353364
int nongit = !startup_info->have_repository;
@@ -359,7 +370,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
359370
builtin_config_usage,
360371
PARSE_OPT_STOP_AT_NON_OPTION);
361372

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) {
363375
error("only one config file at a time.");
364376
usage_with_options(builtin_config_usage, builtin_config_options);
365377
}
@@ -438,6 +450,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
438450
check_argc(argc, 0, 0);
439451
if (git_config_with_options(show_all_config, NULL,
440452
given_config_file,
453+
given_config_blob,
441454
respect_includes) < 0) {
442455
if (given_config_file)
443456
die_errno("unable to read config file '%s'",
@@ -450,13 +463,16 @@ int cmd_config(int argc, const char **argv, const char *prefix)
450463
check_argc(argc, 0, 0);
451464
if (!given_config_file && nongit)
452465
die("not in a git directory");
466+
if (given_config_blob)
467+
die("editing blobs is not supported");
453468
git_config(git_default_config, NULL);
454469
launch_editor(given_config_file ?
455470
given_config_file : git_path("config"),
456471
NULL, NULL);
457472
}
458473
else if (actions == ACTION_SET) {
459474
int ret;
475+
check_blob_write();
460476
check_argc(argc, 2, 2);
461477
value = normalize_value(argv[0], argv[1]);
462478
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)
466482
return ret;
467483
}
468484
else if (actions == ACTION_SET_ALL) {
485+
check_blob_write();
469486
check_argc(argc, 2, 3);
470487
value = normalize_value(argv[0], argv[1]);
471488
return git_config_set_multivar_in_file(given_config_file,
472489
argv[0], value, argv[2], 0);
473490
}
474491
else if (actions == ACTION_ADD) {
492+
check_blob_write();
475493
check_argc(argc, 2, 2);
476494
value = normalize_value(argv[0], argv[1]);
477495
return git_config_set_multivar_in_file(given_config_file,
478496
argv[0], value, "^$", 0);
479497
}
480498
else if (actions == ACTION_REPLACE_ALL) {
499+
check_blob_write();
481500
check_argc(argc, 2, 3);
482501
value = normalize_value(argv[0], argv[1]);
483502
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)
500519
return get_value(argv[0], argv[1]);
501520
}
502521
else if (actions == ACTION_UNSET) {
522+
check_blob_write();
503523
check_argc(argc, 1, 2);
504524
if (argc == 2)
505525
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)
509529
argv[0], NULL);
510530
}
511531
else if (actions == ACTION_UNSET_ALL) {
532+
check_blob_write();
512533
check_argc(argc, 1, 2);
513534
return git_config_set_multivar_in_file(given_config_file,
514535
argv[0], NULL, argv[1], 1);
515536
}
516537
else if (actions == ACTION_RENAME_SECTION) {
517538
int ret;
539+
check_blob_write();
518540
check_argc(argc, 2, 2);
519541
ret = git_config_rename_section_in_file(given_config_file,
520542
argv[0], argv[1]);
@@ -525,6 +547,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
525547
}
526548
else if (actions == ACTION_REMOVE_SECTION) {
527549
int ret;
550+
check_blob_write();
528551
check_argc(argc, 1, 1);
529552
ret = git_config_rename_section_in_file(given_config_file,
530553
argv[0], NULL);

cache.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1142,11 +1142,15 @@ extern int update_server_info(int);
11421142
typedef int (*config_fn_t)(const char *, const char *, void *);
11431143
extern int git_default_config(const char *, const char *, void *);
11441144
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);
11451147
extern void git_config_push_parameter(const char *text);
11461148
extern int git_config_from_parameters(config_fn_t fn, void *data);
11471149
extern int git_config(config_fn_t fn, void *);
11481150
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);
11501154
extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
11511155
extern int git_parse_ulong(const char *, unsigned long *);
11521156
extern int git_config_int(const char *, const char *);

config.c

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ struct config_source {
1414
struct config_source *prev;
1515
union {
1616
FILE *file;
17+
struct config_buf {
18+
const char *buf;
19+
size_t len;
20+
size_t pos;
21+
} buf;
1722
} u;
1823
const char *name;
1924
int linenr;
@@ -45,6 +50,28 @@ static long config_file_ftell(struct config_source *conf)
4550
return ftell(conf->u.file);
4651
}
4752

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+
4875
#define MAX_INCLUDE_DEPTH 10
4976
static const char include_depth_advice[] =
5077
"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)
961988
return ret;
962989
}
963990

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+
9641042
const char *git_etc_gitconfig(void)
9651043
{
9661044
static const char *system_wide;
@@ -1026,7 +1104,9 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
10261104
}
10271105

10281106
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)
10301110
{
10311111
char *repo_config = NULL;
10321112
int ret;
@@ -1045,6 +1125,8 @@ int git_config_with_options(config_fn_t fn, void *data,
10451125
*/
10461126
if (filename)
10471127
return git_config_from_file(fn, filename, data);
1128+
else if (blob_ref)
1129+
return git_config_from_blob_ref(fn, blob_ref, data);
10481130

10491131
repo_config = git_pathdup("config");
10501132
ret = git_config_early(fn, data, repo_config);
@@ -1055,7 +1137,7 @@ int git_config_with_options(config_fn_t fn, void *data,
10551137

10561138
int git_config(config_fn_t fn, void *data)
10571139
{
1058-
return git_config_with_options(fn, data, NULL, 1);
1140+
return git_config_with_options(fn, data, NULL, NULL, 1);
10591141
}
10601142

10611143
/*

t/t1307-config-blob.sh

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

0 commit comments

Comments
 (0)