Merge branch 'ls/filter-process-delayed' into jt/subprocess-handshake

* ls/filter-process-delayed:
  convert: add "status=delayed" to filter process protocol
  convert: refactor capabilities negotiation
  convert: move multiple file filter error handling to separate function
  convert: put the flags field before the flag itself for consistent style
  t0021: write "OUT <size>" only on success
  t0021: make debug log file name configurable
  t0021: keep filter log files on comparison
This commit is contained in:
Junio C Hamano
2017-07-26 12:56:19 -07:00
9 changed files with 676 additions and 169 deletions

202
convert.c
View File

@ -501,6 +501,7 @@ static int apply_single_file_filter(const char *path, const char *src, size_t le
#define CAP_CLEAN (1u<<0)
#define CAP_SMUDGE (1u<<1)
#define CAP_DELAY (1u<<2)
struct cmd2process {
struct subprocess_entry subprocess; /* must be the first member! */
@ -512,7 +513,7 @@ static struct hashmap subprocess_map;
static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)
{
int err;
int err, i;
struct cmd2process *entry = (struct cmd2process *)subprocess;
struct string_list cap_list = STRING_LIST_INIT_NODUP;
char *cap_buf;
@ -520,6 +521,15 @@ static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)
struct child_process *process = &subprocess->process;
const char *cmd = subprocess->cmd;
static const struct {
const char *name;
unsigned int cap;
} known_caps[] = {
{ "clean", CAP_CLEAN },
{ "smudge", CAP_SMUDGE },
{ "delay", CAP_DELAY },
};
sigchain_push(SIGPIPE, SIG_IGN);
err = packet_writel(process->in, "git-filter-client", "version=2", NULL);
@ -538,7 +548,15 @@ static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)
if (err)
goto done;
err = packet_writel(process->in, "capability=clean", "capability=smudge", NULL);
for (i = 0; i < ARRAY_SIZE(known_caps); ++i) {
err = packet_write_fmt_gently(
process->in, "capability=%s\n", known_caps[i].name);
if (err)
goto done;
}
err = packet_flush_gently(process->in);
if (err)
goto done;
for (;;) {
cap_buf = packet_read_line(process->out, NULL);
@ -550,16 +568,15 @@ static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)
continue;
cap_name = cap_list.items[1].string;
if (!strcmp(cap_name, "clean")) {
entry->supported_capabilities |= CAP_CLEAN;
} else if (!strcmp(cap_name, "smudge")) {
entry->supported_capabilities |= CAP_SMUDGE;
} else {
warning(
"external filter '%s' requested unsupported filter capability '%s'",
cmd, cap_name
);
}
i = ARRAY_SIZE(known_caps) - 1;
while (i >= 0 && strcmp(cap_name, known_caps[i].name))
i--;
if (i >= 0)
entry->supported_capabilities |= known_caps[i].cap;
else
warning("external filter '%s' requested unsupported filter capability '%s'",
cmd, cap_name);
string_list_clear(&cap_list, 0);
}
@ -570,11 +587,36 @@ done:
return err;
}
static void handle_filter_error(const struct strbuf *filter_status,
struct cmd2process *entry,
const unsigned int wanted_capability) {
if (!strcmp(filter_status->buf, "error"))
; /* The filter signaled a problem with the file. */
else if (!strcmp(filter_status->buf, "abort") && wanted_capability) {
/*
* The filter signaled a permanent problem. Don't try to filter
* files with the same command for the lifetime of the current
* Git process.
*/
entry->supported_capabilities &= ~wanted_capability;
} else {
/*
* Something went wrong with the protocol filter.
* Force shutdown and restart if another blob requires filtering.
*/
error("external filter '%s' failed", entry->subprocess.cmd);
subprocess_stop(&subprocess_map, &entry->subprocess);
free(entry);
}
}
static int apply_multi_file_filter(const char *path, const char *src, size_t len,
int fd, struct strbuf *dst, const char *cmd,
const unsigned int wanted_capability)
const unsigned int wanted_capability,
struct delayed_checkout *dco)
{
int err;
int can_delay = 0;
struct cmd2process *entry;
struct child_process *process;
struct strbuf nbuf = STRBUF_INIT;
@ -603,12 +645,12 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
}
process = &entry->subprocess.process;
if (!(wanted_capability & entry->supported_capabilities))
if (!(entry->supported_capabilities & wanted_capability))
return 0;
if (CAP_CLEAN & wanted_capability)
if (wanted_capability & CAP_CLEAN)
filter_type = "clean";
else if (CAP_SMUDGE & wanted_capability)
else if (wanted_capability & CAP_SMUDGE)
filter_type = "smudge";
else
die("unexpected filter type");
@ -630,6 +672,14 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
if (err)
goto done;
if ((entry->supported_capabilities & CAP_DELAY) &&
dco && dco->state == CE_CAN_DELAY) {
can_delay = 1;
err = packet_write_fmt_gently(process->in, "can-delay=1\n");
if (err)
goto done;
}
err = packet_flush_gently(process->in);
if (err)
goto done;
@ -645,14 +695,73 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
if (err)
goto done;
err = strcmp(filter_status.buf, "success");
if (can_delay && !strcmp(filter_status.buf, "delayed")) {
string_list_insert(&dco->filters, cmd);
string_list_insert(&dco->paths, path);
} else {
/* The filter got the blob and wants to send us a response. */
err = strcmp(filter_status.buf, "success");
if (err)
goto done;
err = read_packetized_to_strbuf(process->out, &nbuf) < 0;
if (err)
goto done;
err = subprocess_read_status(process->out, &filter_status);
if (err)
goto done;
err = strcmp(filter_status.buf, "success");
}
done:
sigchain_pop(SIGPIPE);
if (err)
handle_filter_error(&filter_status, entry, wanted_capability);
else
strbuf_swap(dst, &nbuf);
strbuf_release(&nbuf);
return !err;
}
int async_query_available_blobs(const char *cmd, struct string_list *available_paths)
{
int err;
char *line;
struct cmd2process *entry;
struct child_process *process;
struct strbuf filter_status = STRBUF_INIT;
assert(subprocess_map_initialized);
entry = (struct cmd2process *)subprocess_find_entry(&subprocess_map, cmd);
if (!entry) {
error("external filter '%s' is not available anymore although "
"not all paths have been filtered", cmd);
return 0;
}
process = &entry->subprocess.process;
sigchain_push(SIGPIPE, SIG_IGN);
err = packet_write_fmt_gently(
process->in, "command=list_available_blobs\n");
if (err)
goto done;
err = read_packetized_to_strbuf(process->out, &nbuf) < 0;
err = packet_flush_gently(process->in);
if (err)
goto done;
while ((line = packet_read_line(process->out, NULL))) {
const char *path;
if (skip_prefix(line, "pathname=", &path))
string_list_insert(available_paths, xstrdup(path));
else
; /* ignore unknown keys */
}
err = subprocess_read_status(process->out, &filter_status);
if (err)
goto done;
@ -662,29 +771,8 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
done:
sigchain_pop(SIGPIPE);
if (err) {
if (!strcmp(filter_status.buf, "error")) {
/* The filter signaled a problem with the file. */
} else if (!strcmp(filter_status.buf, "abort")) {
/*
* The filter signaled a permanent problem. Don't try to filter
* files with the same command for the lifetime of the current
* Git process.
*/
entry->supported_capabilities &= ~wanted_capability;
} else {
/*
* Something went wrong with the protocol filter.
* Force shutdown and restart if another blob requires filtering.
*/
error("external filter '%s' failed", cmd);
subprocess_stop(&subprocess_map, &entry->subprocess);
free(entry);
}
} else {
strbuf_swap(dst, &nbuf);
}
strbuf_release(&nbuf);
if (err)
handle_filter_error(&filter_status, entry, 0);
return !err;
}
@ -699,7 +787,8 @@ static struct convert_driver {
static int apply_filter(const char *path, const char *src, size_t len,
int fd, struct strbuf *dst, struct convert_driver *drv,
const unsigned int wanted_capability)
const unsigned int wanted_capability,
struct delayed_checkout *dco)
{
const char *cmd = NULL;
@ -709,15 +798,16 @@ static int apply_filter(const char *path, const char *src, size_t len,
if (!dst)
return 1;
if ((CAP_CLEAN & wanted_capability) && !drv->process && drv->clean)
if ((wanted_capability & CAP_CLEAN) && !drv->process && drv->clean)
cmd = drv->clean;
else if ((CAP_SMUDGE & wanted_capability) && !drv->process && drv->smudge)
else if ((wanted_capability & CAP_SMUDGE) && !drv->process && drv->smudge)
cmd = drv->smudge;
if (cmd && *cmd)
return apply_single_file_filter(path, src, len, fd, dst, cmd);
else if (drv->process && *drv->process)
return apply_multi_file_filter(path, src, len, fd, dst, drv->process, wanted_capability);
return apply_multi_file_filter(path, src, len, fd, dst,
drv->process, wanted_capability, dco);
return 0;
}
@ -1058,7 +1148,7 @@ int would_convert_to_git_filter_fd(const char *path)
if (!ca.drv->required)
return 0;
return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN);
return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN, NULL);
}
const char *get_convert_attr_ascii(const char *path)
@ -1096,7 +1186,7 @@ int convert_to_git(const struct index_state *istate,
convert_attrs(&ca, path);
ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN);
ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN, NULL);
if (!ret && ca.drv && ca.drv->required)
die("%s: clean filter '%s' failed", path, ca.drv->name);
@ -1122,7 +1212,7 @@ void convert_to_git_filter_fd(const struct index_state *istate,
assert(ca.drv);
assert(ca.drv->clean || ca.drv->process);
if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN))
if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN, NULL))
die("%s: clean filter '%s' failed", path, ca.drv->name);
crlf_to_git(istate, path, dst->buf, dst->len, dst, ca.crlf_action, checksafe);
@ -1131,7 +1221,7 @@ void convert_to_git_filter_fd(const struct index_state *istate,
static int convert_to_working_tree_internal(const char *path, const char *src,
size_t len, struct strbuf *dst,
int normalizing)
int normalizing, struct delayed_checkout *dco)
{
int ret = 0, ret_filter = 0;
struct conv_attrs ca;
@ -1156,22 +1246,30 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
}
}
ret_filter = apply_filter(path, src, len, -1, dst, ca.drv, CAP_SMUDGE);
ret_filter = apply_filter(
path, src, len, -1, dst, ca.drv, CAP_SMUDGE, dco);
if (!ret_filter && ca.drv && ca.drv->required)
die("%s: smudge filter %s failed", path, ca.drv->name);
return ret | ret_filter;
}
int async_convert_to_working_tree(const char *path, const char *src,
size_t len, struct strbuf *dst,
void *dco)
{
return convert_to_working_tree_internal(path, src, len, dst, 0, dco);
}
int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
{
return convert_to_working_tree_internal(path, src, len, dst, 0);
return convert_to_working_tree_internal(path, src, len, dst, 0, NULL);
}
int renormalize_buffer(const struct index_state *istate, const char *path,
const char *src, size_t len, struct strbuf *dst)
{
int ret = convert_to_working_tree_internal(path, src, len, dst, 1);
int ret = convert_to_working_tree_internal(path, src, len, dst, 1, NULL);
if (ret) {
src = dst->buf;
len = dst->len;