Skip to content

Commit 31393f9

Browse files
committed
config-batch: create parse loop and unknown command
As we build new features in the config-batch command, we define the plaintext protocol with line-by-line output and responses. To think to the future, we make sure that the protocol has a clear way to respond to an unknown command or an unknown version of that command. Signed-off-by: Derrick Stolee <stolee@gmail.com>
1 parent 2cbdd9c commit 31393f9

File tree

3 files changed

+161
-5
lines changed

3 files changed

+161
-5
lines changed

Documentation/git-config-batch.adoc

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,28 @@ SYNOPSIS
1313

1414
DESCRIPTION
1515
-----------
16-
TODO
16+
Tools frequently need to change their behavior based on values stored in
17+
Git's configuration files. These files may have complicated conditions
18+
for including extra files, so it is difficult to produce an independent
19+
parser. To avoid executing multiple processes to discover or modify
20+
multiple configuration values, the `git config-batch` command allows a
21+
single process to handle multiple requests using a machine-parseable
22+
interface across `stdin` and `stdout`.
23+
24+
PROTOCOL
25+
--------
26+
By default, the protocol uses line feeds (`LF`) to signal the end of a
27+
command over `stdin` or a response over `stdout`.
28+
29+
The protocol will be extended in the future, and consumers should be
30+
resilient to older Git versions not understanding the latest command
31+
set. Thus, if the Git version includes the `git config-batch` builtin
32+
but doesn't understand an input command, it will return a single line
33+
response:
34+
35+
```
36+
unknown_command LF
37+
```
1738

1839
SEE ALSO
1940
--------

builtin/config-batch.c

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,135 @@
33
#include "config.h"
44
#include "environment.h"
55
#include "parse-options.h"
6+
#include "strbuf.h"
7+
#include "string-list.h"
68

79
static const char *const builtin_config_batch_usage[] = {
810
N_("git config-batch <options>"),
911
NULL
1012
};
1113

14+
#define UNKNOWN_COMMAND "unknown_command"
15+
16+
static int emit_response(const char *response, ...)
17+
{
18+
va_list params;
19+
const char *token;
20+
21+
printf("%s", response);
22+
23+
va_start(params, response);
24+
while ((token = va_arg(params, const char *)))
25+
printf(" %s", token);
26+
va_end(params);
27+
28+
printf("\n");
29+
fflush(stdout);
30+
return 0;
31+
}
32+
33+
/**
34+
* A function pointer type for defining a command. The function is
35+
* responsible for handling different versions of the command name.
36+
*
37+
* These functions should only return a negative value if they result
38+
* in such a catastrophic failure that the process should end.
39+
*
40+
* Return 0 on success.
41+
*/
42+
typedef int (*command_fn)(struct repository *repo, int version,
43+
int argc, const char **argv);
44+
45+
static int unknown_commmand(struct repository *repo UNUSED, int version UNUSED,
46+
int argc UNUSED, const char **argv UNUSED)
47+
{
48+
return emit_response(UNKNOWN_COMMAND, NULL);
49+
}
50+
51+
struct command {
52+
const char *name;
53+
command_fn fn;
54+
};
55+
56+
static struct command commands[] = {
57+
/* unknown_command must be last. */
58+
{
59+
.name = "",
60+
.fn = unknown_commmand,
61+
},
62+
};
63+
64+
#define COMMAND_COUNT ((size_t)(sizeof(commands) / sizeof(*commands)))
65+
66+
/**
67+
* Process a single line from stdin and process the command.
68+
*
69+
* Returns 0 on successful processing of command, including the
70+
* unknown_command output.
71+
*
72+
* Returns 1 on natural exit due to exist signal of empty line.
73+
*
74+
* Returns negative value on other catastrophic error.
75+
*/
76+
static int process_command(struct repository *repo)
77+
{
78+
static struct strbuf line = STRBUF_INIT;
79+
struct string_list tokens = STRING_LIST_INIT_NODUP;
80+
const char *command;
81+
int version;
82+
int argc = 0;
83+
const char **argv = NULL;
84+
int res = 0;
85+
86+
strbuf_getline(&line, stdin);
87+
88+
if (!line.len)
89+
return 1;
90+
91+
string_list_split_in_place(&tokens, line.buf, " ", -1);
92+
93+
if (tokens.nr < 2) {
94+
res = error(_("expected at least 2 tokens, got %"PRIuMAX), tokens.nr);
95+
goto cleanup;
96+
}
97+
98+
command = tokens.items[0].string;
99+
100+
if (!git_parse_int(tokens.items[1].string, &version)) {
101+
res = error(_("unable to parse '%s' to integer"),
102+
tokens.items[1].string);
103+
goto cleanup;
104+
}
105+
106+
argc = tokens.nr - 2;
107+
CALLOC_ARRAY(argv, argc + 1);
108+
109+
for (size_t i = 2; i < tokens.nr; i++)
110+
argv[i - 2] = tokens.items[i].string;
111+
112+
for (size_t i = 0; i < COMMAND_COUNT; i++) {
113+
if (!commands[i].name[0] || /* unknown command */
114+
!strcmp(command, commands[i].name)) {
115+
res = commands[i].fn(repo, version, argc, argv);
116+
goto cleanup;
117+
}
118+
}
119+
120+
BUG(_("scanned to end of command list, including 'unknown_command'"));
121+
122+
cleanup:
123+
free(argv);
124+
strbuf_reset(&line);
125+
string_list_clear(&tokens, 0);
126+
return res;
127+
}
128+
12129
int cmd_config_batch(int argc,
13130
const char **argv,
14131
const char *prefix,
15132
struct repository *repo)
16133
{
134+
int res = 0;
17135
struct option options[] = {
18136
OPT_END(),
19137
};
@@ -26,5 +144,9 @@ int cmd_config_batch(int argc,
26144

27145
repo_config(repo, git_default_config, NULL);
28146

29-
return 0;
147+
while (!(res = process_command(repo)));
148+
149+
if (res == 1)
150+
return 0;
151+
die(_("an unrecoverable error occurred during command execution"));
30152
}

t/t1312-config-batch.sh

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,22 @@ test_description='Test git config-batch'
44

55
. ./test-lib.sh
66

7-
test_expect_success 'help text' '
8-
test_must_fail git config-batch -h >out &&
9-
grep usage out
7+
test_expect_success 'no commands' '
8+
echo | git config-batch >out &&
9+
test_must_be_empty out
10+
'
11+
12+
test_expect_success 'unknown_command' '
13+
echo unknown_command >expect &&
14+
echo "bogus 1 line of tokens" >in &&
15+
git config-batch >out <in &&
16+
test_cmp expect out
17+
'
18+
19+
test_expect_success 'failed to parse version' '
20+
echo "bogus BAD_VERSION line of tokens" >in &&
21+
test_must_fail git config-batch 2>err <in &&
22+
test_grep BAD_VERSION err
1023
'
1124

1225
test_done

0 commit comments

Comments
 (0)