Upgrade server-endpoint to also serve blobs in a packfile given their
hashes. Reachability checks are performed before the packfile is sent -
both an absent blob and an unreachable blob are reported to the user in
the same way ("not our blob").
Due to a bug in "rev-list" in the absence of bitmaps (discussed here
[1]), the server repositories in tests all have bitmaps.
[1] <[email protected]>
Signed-off-by: Jonathan Tan <[email protected]>
---
server-endpoint.c | 121 ++++++++++++++++++++++++++++++++++++++++++++-
t/t5573-server-endpoint.sh | 60 ++++++++++++++++++++++
2 files changed, 180 insertions(+), 1 deletion(-)
create mode 100644 t/t5573-server-endpoint.sh
diff --git a/server-endpoint.c b/server-endpoint.c
index a9c0c7c94..870b853a6 100644
--- a/server-endpoint.c
+++ b/server-endpoint.c
@@ -192,6 +192,123 @@ static int fetch_ref(int stateless_rpc)
return -1;
}
+/*
+ * Returns 1 if all blobs are reachable. If not, returns 0 and stores the hash
+ * of one of the unreachable blobs in unreachable.
+ */
+static int are_all_reachable(const struct object_array *blobs, struct
object_id *unreachable)
+{
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ static const char *argv[] = {
+ "rev-list", "--objects", "--use-bitmap-index", "--stdin",
"--not", "--all", "--not", NULL,
+ };
+ int i;
+ char buf[41] = {0};
+
+ cmd.argv = argv;
+ cmd.git_cmd = 1;
+ cmd.in = -1;
+ cmd.out = -1;
+
+ if (start_command(&cmd))
+ goto error;
+
+ for (i = 0; i < blobs->nr; i++) {
+ write_in_full(cmd.in,
sha1_to_hex(blobs->objects[i].item->oid.hash), 40);
+ write_in_full(cmd.in, "\n", 1);
+ }
+ close(cmd.in);
+ cmd.in = -1;
+
+ i = read_in_full(cmd.out, buf, 40);
+ close(cmd.out);
+ cmd.out = -1;
+
+ if (finish_command(&cmd))
+ goto error;
+
+ if (i) {
+ if (get_oid_hex(buf, unreachable))
+ goto error;
+ return 0;
+ }
+
+ return 1;
+
+error:
+ if (cmd.out >= 0)
+ close(cmd.out);
+ die("problem with running rev-list");
+}
+
+static void send_blobs(const struct object_array *blobs)
+{
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ static const char *argv[] = {
+ "pack-objects", "--stdout", NULL
+ };
+ int i;
+
+ cmd.argv = argv;
+ cmd.git_cmd = 1;
+ cmd.in = -1;
+ cmd.out = 0;
+
+ if (start_command(&cmd))
+ goto error;
+
+ for (i = 0; i < blobs->nr; i++) {
+ write_in_full(cmd.in,
sha1_to_hex(blobs->objects[i].item->oid.hash), 40);
+ write_in_full(cmd.in, "\n", 1);
+ }
+ close(cmd.in);
+ cmd.in = -1;
+
+ if (finish_command(&cmd))
+ goto error;
+
+ return;
+
+error:
+ die("problem with running pack-objects");
+}
+
+static int fetch_blob(void)
+{
+ char *line;
+
+ struct object_array wanted_blobs = OBJECT_ARRAY_INIT;
+ struct object_id unreachable;
+
+ while ((line = packet_read_line(0, NULL))) {
+ const char *arg;
+ if (skip_prefix(line, "want ", &arg)) {
+ struct object_id oid;
+ struct object *obj;
+ if (get_oid_hex(arg, &oid)) {
+ packet_write_fmt(1, "ERR invalid object ID
<%s>", arg);
+ return 0;
+ }
+ obj = parse_object(oid.hash);
+ if (!obj || obj->type != OBJ_BLOB) {
+ packet_write_fmt(1, "ERR not our blob <%s>",
arg);
+ return 0;
+ }
+ add_object_array(obj, NULL, &wanted_blobs);
+ }
+ }
+
+ if (!are_all_reachable(&wanted_blobs, &unreachable)) {
+ packet_write_fmt(1, "ERR not our blob <%s>",
oid_to_hex(&unreachable));
+ return 0;
+ }
+
+ packet_write_fmt(1, "ACK\n");
+ send_blobs(&wanted_blobs);
+
+ return 0;
+}
+
static int server_endpoint_config(const char *var, const char *value, void
*unused)
{
return parse_hide_refs_config(var, value, "uploadpack");
@@ -224,5 +341,7 @@ int cmd_main(int argc, const char **argv)
line = packet_read_line(0, NULL);
if (!strcmp(line, "fetch-refs"))
return fetch_ref(stateless_rpc);
- die("only fetch-refs is supported");
+ if (!strcmp(line, "fetch-blobs"))
+ return fetch_blob();
+ die("only fetch-refs and fetch-blobs are supported");
}
diff --git a/t/t5573-server-endpoint.sh b/t/t5573-server-endpoint.sh
new file mode 100644
index 000000000..48f052851
--- /dev/null
+++ b/t/t5573-server-endpoint.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='server-endpoint'
+
+. ./test-lib.sh
+
+test_expect_success 'fetch-blobs basic' '
+ rm -rf server client &&
+ git init server &&
+ (
+ cd server &&
+ test_commit 0 &&
+ test_commit 1 &&
+ git repack -a -d --write-bitmap-index
+ ) &&
+ BLOB0=$(git hash-object server/0.t) &&
+ BLOB1=$(git hash-object server/1.t) &&
+ printf "000ffetch-blobs0031want %s0031want %s0000" "$BLOB0" "$BLOB1" |
git server-endpoint server >out &&
+
+ test "$(head -1 out)" = "0008ACK" &&
+
+ git init client &&
+ sed 1d out | git -C client unpack-objects &&
+ git -C client cat-file -e "$BLOB0" &&
+ git -C client cat-file -e "$BLOB1"
+'
+
+test_expect_success 'fetch-blobs no such object' '
+ rm -rf server client &&
+ git init server &&
+ (
+ cd server &&
+ test_commit 0 &&
+ git repack -a -d --write-bitmap-index
+ ) &&
+ BLOB0=$(git hash-object server/0.t) &&
+ echo myblob >myblob &&
+ MYBLOB=$(git hash-object myblob) &&
+ printf "000ffetch-blobs0031want %s0031want %s0000" "$BLOB0" "$MYBLOB" |
git server-endpoint server >out &&
+
+ test_i18ngrep "$(printf "ERR not our blob.*%s" "$MYBLOB")" out
+'
+
+test_expect_success 'fetch-blobs unreachable' '
+ rm -rf server client &&
+ git init server &&
+ (
+ cd server &&
+ test_commit 0 &&
+ git repack -a -d --write-bitmap-index
+ ) &&
+ BLOB0=$(git hash-object server/0.t) &&
+ echo myblob >myblob &&
+ MYBLOB=$(git -C server hash-object -w ../myblob) &&
+ printf "000ffetch-blobs0031want %s0031want %s0000" "$BLOB0" "$MYBLOB" |
git server-endpoint server >out &&
+
+ test_i18ngrep "$(printf "ERR not our blob.*%s" "$MYBLOB")" out
+'
+
+test_done
--
2.12.2.715.g7642488e1d-goog