summaryrefslogtreecommitdiff
path: root/entry.c
diff options
context:
space:
mode:
authorLibravatar Junio C Hamano <gitster@pobox.com>2017-08-11 13:27:00 -0700
committerLibravatar Junio C Hamano <gitster@pobox.com>2017-08-11 13:27:00 -0700
commit51b8aecabed1363f13c990320a50e1fb2aa3f696 (patch)
tree989a4699cd5d3e9a8a645ac67092e66ba62094d7 /entry.c
parentMerge branch 'st/lib-gpg-kill-stray-agent' (diff)
parentconvert: add "status=delayed" to filter process protocol (diff)
downloadtgif-51b8aecabed1363f13c990320a50e1fb2aa3f696.tar.xz
Merge branch 'ls/filter-process-delayed'
The filter-process interface learned to allow a process with long latency give a "delayed" response. * 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
Diffstat (limited to 'entry.c')
-rw-r--r--entry.c132
1 files changed, 127 insertions, 5 deletions
diff --git a/entry.c b/entry.c
index d6b263f78e..65458f07a4 100644
--- a/entry.c
+++ b/entry.c
@@ -137,6 +137,105 @@ static int streaming_write_entry(const struct cache_entry *ce, char *path,
return result;
}
+void enable_delayed_checkout(struct checkout *state)
+{
+ if (!state->delayed_checkout) {
+ state->delayed_checkout = xmalloc(sizeof(*state->delayed_checkout));
+ state->delayed_checkout->state = CE_CAN_DELAY;
+ string_list_init(&state->delayed_checkout->filters, 0);
+ string_list_init(&state->delayed_checkout->paths, 0);
+ }
+}
+
+static int remove_available_paths(struct string_list_item *item, void *cb_data)
+{
+ struct string_list *available_paths = cb_data;
+ struct string_list_item *available;
+
+ available = string_list_lookup(available_paths, item->string);
+ if (available)
+ available->util = (void *)item->string;
+ return !available;
+}
+
+int finish_delayed_checkout(struct checkout *state)
+{
+ int errs = 0;
+ struct string_list_item *filter, *path;
+ struct delayed_checkout *dco = state->delayed_checkout;
+
+ if (!state->delayed_checkout)
+ return errs;
+
+ dco->state = CE_RETRY;
+ while (dco->filters.nr > 0) {
+ for_each_string_list_item(filter, &dco->filters) {
+ struct string_list available_paths = STRING_LIST_INIT_NODUP;
+
+ if (!async_query_available_blobs(filter->string, &available_paths)) {
+ /* Filter reported an error */
+ errs = 1;
+ filter->string = "";
+ continue;
+ }
+ if (available_paths.nr <= 0) {
+ /*
+ * Filter responded with no entries. That means
+ * the filter is done and we can remove the
+ * filter from the list (see
+ * "string_list_remove_empty_items" call below).
+ */
+ filter->string = "";
+ continue;
+ }
+
+ /*
+ * In dco->paths we store a list of all delayed paths.
+ * The filter just send us a list of available paths.
+ * Remove them from the list.
+ */
+ filter_string_list(&dco->paths, 0,
+ &remove_available_paths, &available_paths);
+
+ for_each_string_list_item(path, &available_paths) {
+ struct cache_entry* ce;
+
+ if (!path->util) {
+ error("external filter '%s' signaled that '%s' "
+ "is now available although it has not been "
+ "delayed earlier",
+ filter->string, path->string);
+ errs |= 1;
+
+ /*
+ * Do not ask the filter for available blobs,
+ * again, as the filter is likely buggy.
+ */
+ filter->string = "";
+ continue;
+ }
+ ce = index_file_exists(state->istate, path->string,
+ strlen(path->string), 0);
+ errs |= (ce ? checkout_entry(ce, state, NULL) : 1);
+ }
+ }
+ string_list_remove_empty_items(&dco->filters, 0);
+ }
+ string_list_clear(&dco->filters, 0);
+
+ /* At this point we should not have any delayed paths anymore. */
+ errs |= dco->paths.nr;
+ for_each_string_list_item(path, &dco->paths) {
+ error("'%s' was not filtered properly", path->string);
+ }
+ string_list_clear(&dco->paths, 0);
+
+ free(dco);
+ state->delayed_checkout = NULL;
+
+ return errs;
+}
+
static int write_entry(struct cache_entry *ce,
char *path, const struct checkout *state, int to_tempfile)
{
@@ -179,11 +278,34 @@ static int write_entry(struct cache_entry *ce,
/*
* Convert from git internal format to working tree format
*/
- if (ce_mode_s_ifmt == S_IFREG &&
- convert_to_working_tree(ce->name, new, size, &buf)) {
- free(new);
- new = strbuf_detach(&buf, &newsize);
- size = newsize;
+ if (ce_mode_s_ifmt == S_IFREG) {
+ struct delayed_checkout *dco = state->delayed_checkout;
+ if (dco && dco->state != CE_NO_DELAY) {
+ /* Do not send the blob in case of a retry. */
+ if (dco->state == CE_RETRY) {
+ new = NULL;
+ size = 0;
+ }
+ ret = async_convert_to_working_tree(
+ ce->name, new, size, &buf, dco);
+ if (ret && string_list_has_string(&dco->paths, ce->name)) {
+ free(new);
+ goto finish;
+ }
+ } else
+ ret = convert_to_working_tree(
+ ce->name, new, size, &buf);
+
+ if (ret) {
+ free(new);
+ new = strbuf_detach(&buf, &newsize);
+ size = newsize;
+ }
+ /*
+ * No "else" here as errors from convert are OK at this
+ * point. If the error would have been fatal (e.g.
+ * filter is required), then we would have died already.
+ */
}
fd = open_output_fd(path, ce, to_tempfile);