Skip to content

Commit

Permalink
upload-pack: add object filtering for partial clone
Browse files Browse the repository at this point in the history
Teach upload-pack to negotiate object filtering over the protocol and
to send filter parameters to pack-objects.  This is intended for partial
clone and fetch.

The idea to make upload-pack configurable using uploadpack.allowFilter
comes from Jonathan Tan's work in [1].

[1] https://public-inbox.org/git/f211093280b422c32cc1b7034130072f35c5ed51.1506714999.git.jonathantanmy@google.com/

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
jeffhostetler authored and gitster committed Dec 8, 2017
1 parent 0c16cd4 commit 10ac85c
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 1 deletion.
4 changes: 4 additions & 0 deletions Documentation/config.txt
Expand Up @@ -3268,6 +3268,10 @@ uploadpack.packObjectsHook::
was run. I.e., `upload-pack` will feed input intended for
`pack-objects` to the hook, and expects a completed packfile on
stdout.

uploadpack.allowFilter::
If this option is set, `upload-pack` will advertise partial
clone and partial fetch object filtering.
+
Note that this configuration variable is ignored if it is seen in the
repository-level config (this is a safety measure against fetching from
Expand Down
8 changes: 8 additions & 0 deletions Documentation/technical/pack-protocol.txt
Expand Up @@ -212,6 +212,7 @@ out of what the server said it could do with the first 'want' line.
upload-request = want-list
*shallow-line
*1depth-request
[filter-request]
flush-pkt

want-list = first-want
Expand All @@ -227,6 +228,8 @@ out of what the server said it could do with the first 'want' line.
additional-want = PKT-LINE("want" SP obj-id)

depth = 1*DIGIT

filter-request = PKT-LINE("filter" SP filter-spec)
----

Clients MUST send all the obj-ids it wants from the reference
Expand All @@ -249,6 +252,11 @@ complete those commits. Commits whose parents are not received as a
result are defined as shallow and marked as such in the server. This
information is sent back to the client in the next step.

The client can optionally request that pack-objects omit various
objects from the packfile using one of several filtering techniques.
These are intended for use with partial clone and partial fetch
operations. See `rev-list` for possible "filter-spec" values.

Once all the 'want's and 'shallow's (and optional 'deepen') are
transferred, clients MUST send a flush-pkt, to tell the server side
that it is done sending the list.
Expand Down
8 changes: 8 additions & 0 deletions Documentation/technical/protocol-capabilities.txt
Expand Up @@ -309,3 +309,11 @@ to accept a signed push certificate, and asks the <nonce> to be
included in the push certificate. A send-pack client MUST NOT
send a push-cert packet unless the receive-pack server advertises
this capability.

filter
------

If the upload-pack server advertises the 'filter' capability,
fetch-pack may send "filter" commands to request a partial clone
or partial fetch and request that the server omit various objects
from the packfile.
26 changes: 25 additions & 1 deletion upload-pack.c
Expand Up @@ -10,6 +10,8 @@
#include "diff.h"
#include "revision.h"
#include "list-objects.h"
#include "list-objects-filter.h"
#include "list-objects-filter-options.h"
#include "run-command.h"
#include "connect.h"
#include "sigchain.h"
Expand All @@ -18,6 +20,7 @@
#include "parse-options.h"
#include "argv-array.h"
#include "prio-queue.h"
#include "quote.h"

static const char * const upload_pack_usage[] = {
N_("git upload-pack [<options>] <dir>"),
Expand Down Expand Up @@ -64,6 +67,10 @@ static int advertise_refs;
static int stateless_rpc;
static const char *pack_objects_hook;

static int filter_capability_requested;
static int filter_advertise;
static struct list_objects_filter_options filter_options;

static void reset_timeout(void)
{
alarm(timeout);
Expand Down Expand Up @@ -131,6 +138,12 @@ static void create_pack_file(void)
argv_array_push(&pack_objects.args, "--delta-base-offset");
if (use_include_tag)
argv_array_push(&pack_objects.args, "--include-tag");
if (filter_options.filter_spec) {
struct strbuf buf = STRBUF_INIT;
sq_quote_buf(&buf, filter_options.filter_spec);
argv_array_pushf(&pack_objects.args, "--filter=%s", buf.buf);
strbuf_release(&buf);
}

pack_objects.in = -1;
pack_objects.out = -1;
Expand Down Expand Up @@ -794,6 +807,12 @@ static void receive_needs(void)
deepen_rev_list = 1;
continue;
}
if (skip_prefix(line, "filter ", &arg)) {
if (!filter_capability_requested)
die("git upload-pack: filtering capability not negotiated");
parse_list_objects_filter(&filter_options, arg);
continue;
}
if (!skip_prefix(line, "want ", &arg) ||
get_oid_hex(arg, &oid_buf))
die("git upload-pack: protocol error, "
Expand Down Expand Up @@ -821,6 +840,8 @@ static void receive_needs(void)
no_progress = 1;
if (parse_feature_request(features, "include-tag"))
use_include_tag = 1;
if (parse_feature_request(features, "filter"))
filter_capability_requested = 1;

o = parse_object(&oid_buf);
if (!o) {
Expand Down Expand Up @@ -940,7 +961,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
struct strbuf symref_info = STRBUF_INIT;

format_symref_info(&symref_info, cb_data);
packet_write_fmt(1, "%s %s%c%s%s%s%s%s agent=%s\n",
packet_write_fmt(1, "%s %s%c%s%s%s%s%s%s agent=%s\n",
oid_to_hex(oid), refname_nons,
0, capabilities,
(allow_unadvertised_object_request & ALLOW_TIP_SHA1) ?
Expand All @@ -949,6 +970,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
" allow-reachable-sha1-in-want" : "",
stateless_rpc ? " no-done" : "",
symref_info.buf,
filter_advertise ? " filter" : "",
git_user_agent_sanitized());
strbuf_release(&symref_info);
} else {
Expand Down Expand Up @@ -1027,6 +1049,8 @@ static int upload_pack_config(const char *var, const char *value, void *unused)
} else if (current_config_scope() != CONFIG_SCOPE_REPO) {
if (!strcmp("uploadpack.packobjectshook", var))
return git_config_string(&pack_objects_hook, var, value);
} else if (!strcmp("uploadpack.allowfilter", var)) {
filter_advertise = git_config_bool(var, value);
}
return parse_hide_refs_config(var, value, "uploadpack");
}
Expand Down

0 comments on commit 10ac85c

Please sign in to comment.