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:
202
convert.c
202
convert.c
@ -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;
|
||||
|
Reference in New Issue
Block a user