diff --git a/Documentation/config/gvfs.adoc b/Documentation/config/gvfs.adoc index b38c7ac9eb8937..dead6f00d794da 100644 --- a/Documentation/config/gvfs.adoc +++ b/Documentation/config/gvfs.adoc @@ -27,6 +27,18 @@ gvfs.fallback:: server fails to connect. This will alert users to failures with the cache server, but avoid causing throttling on the origin server. +gvfs.negativeRefCheck:: + When using the GVFS Protocol, the `core.gvfs` `GVFS_MISSING_OK` bit + allows objects behind local refs to be missing, so `git push` + normally feeds every advertised ref to `git pack-objects` as an + exclusion, even ones that are absent locally. `pack-objects` then + lazily downloads each missing exclusion while searching for delta + bases, issuing one object request per advertised ref. If set to + `true`, `git push` instead performs a non-fetching existence check + and omits any advertised object it does not have locally, matching + the behavior of Git without the `GVFS_MISSING_OK` bit. Defaults to + `false`. + gvfs.sessionKey:: If set to a string, then the value is a config key name that will be used to set a prefix in the `X-Session-Id` header sent to the server for all diff --git a/send-pack.c b/send-pack.c index 9297ad680335ce..c1c61ded76fdf8 100644 --- a/send-pack.c +++ b/send-pack.c @@ -44,9 +44,11 @@ int option_parse_push_signed(const struct option *opt, } static void feed_object(struct repository *r, - const struct object_id *oid, FILE *fh, int negative) + const struct object_id *oid, FILE *fh, int negative, + int check_missing) { - if (negative && !gvfs_config_is_set(r, GVFS_MISSING_OK) && !odb_has_object(r->objects, oid, 0)) + if (negative && check_missing && + !odb_has_object(r->objects, oid, 0)) return; if (negative) @@ -71,6 +73,8 @@ static int pack_objects(struct repository *r, struct child_process po = CHILD_PROCESS_INIT; FILE *po_in; int rc; + int negative_ref_check = 0; + int check_missing; trace2_region_enter("send_pack", "pack_objects", r); strvec_push(&po.args, "pack-objects"); @@ -103,16 +107,33 @@ static int pack_objects(struct repository *r, * parameters by writing to the pipe. */ po_in = xfdopen(po.in, "w"); + + /* + * Normally we omit a negative (exclusion) object that we do not have + * locally. The core.gvfs GVFS_MISSING_OK bit disables that check, so + * missing negatives are still fed to pack-objects. Under the GVFS + * protocol that is harmful: pack-objects treats each fed exclusion as + * an edge and lazily downloads every one that is absent while hunting + * for preferred delta bases -- one immediate object request per + * advertised ref. Setting gvfs.negativeRefCheck restores the vanilla + * behavior of omitting a negative object we do not have, using a + * non-fetching existence check (odb_has_object() with flags 0 implies + * OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT). + */ + repo_config_get_bool(r, "gvfs.negativeRefCheck", &negative_ref_check); + check_missing = negative_ref_check || + !gvfs_config_is_set(r, GVFS_MISSING_OK); + for (size_t i = 0; i < advertised->nr; i++) - feed_object(r, &advertised->oid[i], po_in, 1); + feed_object(r, &advertised->oid[i], po_in, 1, check_missing); for (size_t i = 0; i < negotiated->nr; i++) - feed_object(r, &negotiated->oid[i], po_in, 1); + feed_object(r, &negotiated->oid[i], po_in, 1, check_missing); while (refs) { if (!is_null_oid(&refs->old_oid)) - feed_object(r, &refs->old_oid, po_in, 1); + feed_object(r, &refs->old_oid, po_in, 1, check_missing); if (!is_null_oid(&refs->new_oid)) - feed_object(r, &refs->new_oid, po_in, 0); + feed_object(r, &refs->new_oid, po_in, 0, check_missing); refs = refs->next; }