2 * Example implementation for the Git filter protocol version 2
3 * See Documentation/gitattributes.txt, section "Filter Protocol"
5 * Usage: test-tool rot13-filter [--always-delay] --log=<path> <capabilities>
7 * Log path defines a debug log file that the script writes to. The
8 * subsequent arguments define a list of supported protocol capabilities
9 * ("clean", "smudge", etc).
11 * When --always-delay is given all pathnames with the "can-delay" flag
12 * that don't appear on the list bellow are delayed with a count of 1
15 * This implementation supports special test cases:
16 * (1) If data with the pathname "clean-write-fail.r" is processed with
17 * a "clean" operation then the write operation will die.
18 * (2) If data with the pathname "smudge-write-fail.r" is processed with
19 * a "smudge" operation then the write operation will die.
20 * (3) If data with the pathname "error.r" is processed with any
21 * operation then the filter signals that it cannot or does not want
22 * to process the file.
23 * (4) If data with the pathname "abort.r" is processed with any
24 * operation then the filter signals that it cannot or does not want
25 * to process the file and any file after that is processed with the
27 * (5) If data with a pathname that is a key in the delay hash is
28 * requested (e.g. "test-delay10.a") then the filter responds with
29 * a "delay" status and sets the "requested" field in the delay hash.
30 * The filter will signal the availability of this object after
31 * "count" (field in delay hash) "list_available_blobs" commands.
32 * (6) If data with the pathname "missing-delay.a" is processed that the
33 * filter will drop the path from the "list_available_blobs" response.
34 * (7) If data with the pathname "invalid-delay.a" is processed that the
35 * filter will add the path "unfiltered" which was not delayed before
36 * to the "list_available_blobs" response.
39 #include "test-tool.h"
41 #include "string-list.h"
43 #include "parse-options.h"
46 static int always_delay
, has_clean_cap
, has_smudge_cap
;
47 static struct strmap delay
= STRMAP_INIT
;
49 static inline const char *str_or_null(const char *str
)
51 return str
? str
: "(null)";
54 static char *rot13(char *str
)
57 for (c
= str
; *c
; c
++)
59 *c
+= tolower(*c
) < 'n' ? 13 : -13;
63 static char *get_value(char *buf
, const char *key
)
65 const char *orig_buf
= buf
;
67 !skip_prefix((const char *)buf
, key
, (const char **)&buf
) ||
68 !skip_prefix((const char *)buf
, "=", (const char **)&buf
) ||
70 die("expected key '%s', got '%s'", key
, str_or_null(orig_buf
));
75 * Read a text packet, expecting that it is in the form "key=value" for
76 * the given key. An EOF does not trigger any error and is reported
77 * back to the caller with NULL. Die if the "key" part of "key=value" does
78 * not match the given key, or the value part is empty.
80 static char *packet_key_val_read(const char *key
)
83 if (packet_read_line_gently(0, NULL
, &buf
) < 0)
85 return xstrdup(get_value(buf
, key
));
88 static inline void assert_remote_capability(struct strset
*caps
, const char *cap
)
90 if (!strset_contains(caps
, cap
))
91 die("required '%s' capability not available from remote", cap
);
94 static void read_capabilities(struct strset
*remote_caps
)
97 char *buf
= packet_read_line(0, NULL
);
100 strset_add(remote_caps
, get_value(buf
, "capability"));
103 assert_remote_capability(remote_caps
, "clean");
104 assert_remote_capability(remote_caps
, "smudge");
105 assert_remote_capability(remote_caps
, "delay");
108 static void check_and_write_capabilities(struct strset
*remote_caps
,
109 const char **caps
, int nr_caps
)
112 for (i
= 0; i
< nr_caps
; i
++) {
113 if (!strset_contains(remote_caps
, caps
[i
]))
114 die("our capability '%s' is not available from remote",
116 packet_write_fmt(1, "capability=%s\n", caps
[i
]);
122 int requested
, count
;
126 static void free_delay_entries(void)
128 struct hashmap_iter iter
;
129 struct strmap_entry
*ent
;
131 strmap_for_each_entry(&delay
, &iter
, ent
) {
132 struct delay_entry
*delay_entry
= ent
->value
;
133 free(delay_entry
->output
);
136 strmap_clear(&delay
, 0);
139 static void add_delay_entry(char *pathname
, int count
, int requested
)
141 struct delay_entry
*entry
= xcalloc(1, sizeof(*entry
));
142 entry
->count
= count
;
143 entry
->requested
= requested
;
144 if (strmap_put(&delay
, pathname
, entry
))
145 BUG("adding the same path twice to delay hash?");
148 static void reply_list_available_blobs_cmd(void)
150 struct hashmap_iter iter
;
151 struct strmap_entry
*ent
;
152 struct string_list_item
*str_item
;
153 struct string_list paths
= STRING_LIST_INIT_NODUP
;
156 if (packet_read_line(0, NULL
))
157 die("bad list_available_blobs end");
159 strmap_for_each_entry(&delay
, &iter
, ent
) {
160 struct delay_entry
*delay_entry
= ent
->value
;
161 if (!delay_entry
->requested
)
163 delay_entry
->count
--;
164 if (!strcmp(ent
->key
, "invalid-delay.a")) {
165 /* Send Git a pathname that was not delayed earlier */
166 packet_write_fmt(1, "pathname=unfiltered");
168 if (!strcmp(ent
->key
, "missing-delay.a")) {
169 /* Do not signal Git that this file is available */
170 } else if (!delay_entry
->count
) {
171 string_list_append(&paths
, ent
->key
);
172 packet_write_fmt(1, "pathname=%s", ent
->key
);
176 /* Print paths in sorted order. */
177 string_list_sort(&paths
);
178 for_each_string_list_item(str_item
, &paths
)
179 fprintf(logfile
, " %s", str_item
->string
);
180 string_list_clear(&paths
, 0);
184 fprintf(logfile
, " [OK]\n");
185 packet_write_fmt(1, "status=success");
189 static void command_loop(void)
194 struct delay_entry
*entry
;
195 struct strbuf input
= STRBUF_INIT
;
196 char *command
= packet_key_val_read("command");
199 fprintf(logfile
, "STOP\n");
202 fprintf(logfile
, "IN: %s", command
);
204 if (!strcmp(command
, "list_available_blobs")) {
205 reply_list_available_blobs_cmd();
210 pathname
= packet_key_val_read("pathname");
212 die("unexpected EOF while expecting pathname");
213 fprintf(logfile
, " %s", pathname
);
215 /* Read until flush */
216 while ((buf
= packet_read_line(0, NULL
))) {
217 if (!strcmp(buf
, "can-delay=1")) {
218 entry
= strmap_get(&delay
, pathname
);
219 if (entry
&& !entry
->requested
)
220 entry
->requested
= 1;
221 else if (!entry
&& always_delay
)
222 add_delay_entry(pathname
, 1, 1);
223 } else if (starts_with(buf
, "ref=") ||
224 starts_with(buf
, "treeish=") ||
225 starts_with(buf
, "blob=")) {
226 fprintf(logfile
, " %s", buf
);
229 * In general, filters need to be graceful about
230 * new metadata, since it's documented that we
231 * can pass any key-value pairs, but for tests,
232 * let's be a little stricter.
234 die("Unknown message '%s'", buf
);
238 read_packetized_to_strbuf(0, &input
, 0);
239 fprintf(logfile
, " %"PRIuMAX
" [OK] -- ", (uintmax_t)input
.len
);
241 entry
= strmap_get(&delay
, pathname
);
242 if (entry
&& entry
->output
) {
243 output
= entry
->output
;
244 } else if (!strcmp(pathname
, "error.r") || !strcmp(pathname
, "abort.r")) {
246 } else if (!strcmp(command
, "clean") && has_clean_cap
) {
247 output
= rot13(input
.buf
);
248 } else if (!strcmp(command
, "smudge") && has_smudge_cap
) {
249 output
= rot13(input
.buf
);
251 die("bad command '%s'", command
);
254 if (!strcmp(pathname
, "error.r")) {
255 fprintf(logfile
, "[ERROR]\n");
256 packet_write_fmt(1, "status=error");
258 } else if (!strcmp(pathname
, "abort.r")) {
259 fprintf(logfile
, "[ABORT]\n");
260 packet_write_fmt(1, "status=abort");
262 } else if (!strcmp(command
, "smudge") &&
263 (entry
= strmap_get(&delay
, pathname
)) &&
264 entry
->requested
== 1) {
265 fprintf(logfile
, "[DELAYED]\n");
266 packet_write_fmt(1, "status=delayed");
268 entry
->requested
= 2;
269 if (entry
->output
!= output
) {
271 entry
->output
= xstrdup(output
);
274 int i
, nr_packets
= 0;
277 packet_write_fmt(1, "status=success");
280 if (skip_prefix(pathname
, command
, &p
) &&
281 !strcmp(p
, "-write-fail.r")) {
282 fprintf(logfile
, "[WRITE FAIL]\n");
283 die("%s write error", command
);
286 output_len
= strlen(output
);
287 fprintf(logfile
, "OUT: %"PRIuMAX
" ", (uintmax_t)output_len
);
289 if (write_packetized_from_buf_no_flush_count(output
,
290 output_len
, 1, &nr_packets
))
291 die("failed to write buffer to stdout");
294 for (i
= 0; i
< nr_packets
; i
++)
295 fprintf(logfile
, ".");
296 fprintf(logfile
, " [OK]\n");
301 strbuf_release(&input
);
306 static void packet_initialize(void)
308 char *pkt_buf
= packet_read_line(0, NULL
);
310 if (!pkt_buf
|| strcmp(pkt_buf
, "git-filter-client"))
311 die("bad initialize: '%s'", str_or_null(pkt_buf
));
313 pkt_buf
= packet_read_line(0, NULL
);
314 if (!pkt_buf
|| strcmp(pkt_buf
, "version=2"))
315 die("bad version: '%s'", str_or_null(pkt_buf
));
317 pkt_buf
= packet_read_line(0, NULL
);
319 die("bad version end: '%s'", pkt_buf
);
321 packet_write_fmt(1, "git-filter-server");
322 packet_write_fmt(1, "version=2");
326 static const char *rot13_usage
[] = {
327 "test-tool rot13-filter [--always-delay] --log=<path> <capabilities>",
331 int cmd__rot13_filter(int argc
, const char **argv
)
334 struct strset remote_caps
= STRSET_INIT
;
335 const char *log_path
= NULL
;
337 struct option options
[] = {
338 OPT_BOOL(0, "always-delay", &always_delay
,
339 "delay all paths with the can-delay flag"),
340 OPT_STRING(0, "log", &log_path
, "path",
341 "path to the debug log file"),
344 nr_caps
= parse_options(argc
, argv
, NULL
, options
, rot13_usage
,
345 PARSE_OPT_STOP_AT_NON_OPTION
);
347 if (!log_path
|| !nr_caps
)
348 usage_with_options(rot13_usage
, options
);
350 logfile
= fopen(log_path
, "a");
352 die_errno("failed to open log file");
354 for (i
= 0; i
< nr_caps
; i
++) {
355 if (!strcmp(argv
[i
], "smudge"))
357 if (!strcmp(argv
[i
], "clean"))
361 add_delay_entry("test-delay10.a", 1, 0);
362 add_delay_entry("test-delay11.a", 1, 0);
363 add_delay_entry("test-delay20.a", 2, 0);
364 add_delay_entry("test-delay10.b", 1, 0);
365 add_delay_entry("missing-delay.a", 1, 0);
366 add_delay_entry("invalid-delay.a", 1, 0);
368 fprintf(logfile
, "START\n");
371 read_capabilities(&remote_caps
);
372 check_and_write_capabilities(&remote_caps
, argv
, nr_caps
);
373 fprintf(logfile
, "init handshake complete\n");
374 strset_clear(&remote_caps
);
379 die_errno("error closing logfile");
380 free_delay_entries();