object-info: support for retrieving object info

Sometimes it is useful to get information of an object without having to download it completely. Add the "object-info" capability that lets the client ask for object-related information with their full hexadecimal object names. Only sizes are returned for now. Signed-off-by: Bruno Albuquerque <bga@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Bruno Albuquerque committed Apr 20, 2021 at 16:38 UTC a2ba162cda2acc171c3e36acbbc854792b093cb7
6 files changed +183
Documentation/technical/protocol-v2.txt
+31
@@ -514,3 +514,34 @@ packet-line, and must not contain non-printable or whitespace characters. The
514 current implementation uses trace2 session IDs (see
515 link:api-trace2.html[api-trace2] for details), but this may change and users of
516 the session ID should not rely on this fact.
517 +
518 +object-info
519 +~~~~~~~~~~~
520 +
521 +`object-info` is the command to retrieve information about one or more objects.
522 +Its main purpose is to allow a client to make decisions based on this
523 +information without having to fully fetch objects. Object size is the only
524 +information that is currently supported.
525 +
526 +An `object-info` request takes the following arguments:
527 +
528 + size
529 + Requests size information to be returned for each listed object id.
530 +
531 + oid <oid>
532 + Indicates to the server an object which the client wants to obtain
533 + information for.
534 +
535 +The response of `object-info` is a list of the the requested object ids
536 +and associated requested information, each separated by a single space.
537 +
538 + output = info flush-pkt
539 +
540 + info = PKT-LINE(attrs) LF)
541 + *PKT-LINE(obj-info LF)
542 +
543 + attrs = attr | attrs SP attrs
544 +
545 + attr = "size"
546 +
547 + obj-info = obj-id SP obj-size
Makefile
+1
@@ -961,6 +961,7 @@ LIB_OBJS += progress.o
961 LIB_OBJS += promisor-remote.o
962 LIB_OBJS += prompt.o
963 LIB_OBJS += protocol.o
964 +LIB_OBJS += protocol-caps.o
965 LIB_OBJS += prune-packed.o
966 LIB_OBJS += quote.o
967 LIB_OBJS += range-diff.o
protocol-caps.c new
+113
@@ -0,0 +1,113 @@
1 +#include "git-compat-util.h"
2 +#include "protocol-caps.h"
3 +#include "gettext.h"
4 +#include "pkt-line.h"
5 +#include "strvec.h"
6 +#include "hash.h"
7 +#include "object.h"
8 +#include "object-store.h"
9 +#include "string-list.h"
10 +#include "strbuf.h"
11 +
12 +struct requested_info {
13 + unsigned size : 1;
14 +};
15 +
16 +/*
17 + * Parses oids from the given line and collects them in the given
18 + * oid_str_list. Returns 1 if parsing was successful and 0 otherwise.
19 + */
20 +static int parse_oid(const char *line, struct string_list *oid_str_list)
21 +{
22 + const char *arg;
23 +
24 + if (!skip_prefix(line, "oid ", &arg))
25 + return 0;
26 +
27 + string_list_append(oid_str_list, arg);
28 +
29 + return 1;
30 +}
31 +
32 +/*
33 + * Validates and send requested info back to the client. Any errors detected
34 + * are returned as they are detected.
35 + */
36 +static void send_info(struct repository *r, struct packet_writer *writer,
37 + struct string_list *oid_str_list,
38 + struct requested_info *info)
39 +{
40 + struct string_list_item *item;
41 + struct strbuf send_buffer = STRBUF_INIT;
42 +
43 + if (!oid_str_list->nr)
44 + return;
45 +
46 + if (info->size)
47 + packet_writer_write(writer, "size");
48 +
49 + for_each_string_list_item (item, oid_str_list) {
50 + const char *oid_str = item->string;
51 + struct object_id oid;
52 + unsigned long object_size;
53 +
54 + if (get_oid_hex(oid_str, &oid) < 0) {
55 + packet_writer_error(
56 + writer,
57 + "object-info: protocol error, expected to get oid, not '%s'",
58 + oid_str);
59 + continue;
60 + }
61 +
62 + strbuf_addstr(&send_buffer, oid_str);
63 +
64 + if (info->size) {
65 + if (oid_object_info(r, &oid, &object_size) < 0) {
66 + strbuf_addstr(&send_buffer, " ");
67 + } else {
68 + strbuf_addf(&send_buffer, " %lu", object_size);
69 + }
70 + }
71 +
72 + packet_writer_write(writer, "%s",
73 + strbuf_detach(&send_buffer, NULL));
74 + }
75 +}
76 +
77 +int cap_object_info(struct repository *r, struct strvec *keys,
78 + struct packet_reader *request)
79 +{
80 + struct requested_info info;
81 + struct packet_writer writer;
82 + struct string_list oid_str_list = STRING_LIST_INIT_DUP;
83 +
84 + packet_writer_init(&writer, 1);
85 +
86 + while (packet_reader_read(request) == PACKET_READ_NORMAL) {
87 + if (!strcmp("size", request->line)) {
88 + info.size = 1;
89 + continue;
90 + }
91 +
92 + if (parse_oid(request->line, &oid_str_list))
93 + continue;
94 +
95 + packet_writer_error(&writer,
96 + "object-info: unexpected line: '%s'",
97 + request->line);
98 + }
99 +
100 + if (request->status != PACKET_READ_FLUSH) {
101 + packet_writer_error(
102 + &writer, "object-info: expected flush after arguments");
103 + die(_("object-info: expected flush after arguments"));
104 + }
105 +
106 + send_info(r, &writer, &oid_str_list, &info);
107 +
108 + string_list_clear(&oid_str_list, 1);
109 +
110 + packet_flush(1);
111 +
112 + return 0;
113 +}
protocol-caps.h new
+10
@@ -0,0 +1,10 @@
1 +#ifndef PROTOCOL_CAPS_H
2 +#define PROTOCOL_CAPS_H
3 +
4 +struct repository;
5 +struct strvec;
6 +struct packet_reader;
7 +int cap_object_info(struct repository *r, struct strvec *keys,
8 + struct packet_reader *request);
9 +
10 +#endif /* PROTOCOL_CAPS_H */
\ No newline at end of file
serve.c
+2
@@ -5,6 +5,7 @@
5 #include "version.h"
6 #include "strvec.h"
7 #include "ls-refs.h"
8 +#include "protocol-caps.h"
9 #include "serve.h"
10 #include "upload-pack.h"
11
@@ -78,6 +79,7 @@ static struct protocol_capability capabilities[] = {
79 { "server-option", always_advertise, NULL },
80 { "object-format", object_format_advertise, NULL },
81 { "session-id", session_id_advertise, NULL },
82 + { "object-info", always_advertise, cap_object_info },
83 };
84
85 static void advertise_capabilities(void)
t/t5701-git-serve.sh
+26
@@ -19,6 +19,7 @@ test_expect_success 'test capability advertisement' '
19 fetch=shallow
20 server-option
21 object-format=$(test_oid algo)
22 + object-info
23 0000
24 EOF
25
@@ -240,4 +241,29 @@ test_expect_success 'unexpected lines are not allowed in fetch request' '
241 grep "unexpected line: .this-is-not-a-command." err
242 '
243
244 +# Test the basics of object-info
245 +#
246 +test_expect_success 'basics of object-info' '
247 + test-tool pkt-line pack >in <<-EOF &&
248 + command=object-info
249 + object-format=$(test_oid algo)
250 + 0001
251 + size
252 + oid $(git rev-parse two:two.t)
253 + oid $(git rev-parse two:two.t)
254 + 0000
255 + EOF
256 +
257 + cat >expect <<-EOF &&
258 + size
259 + $(git rev-parse two:two.t) $(wc -c <two.t | xargs)
260 + $(git rev-parse two:two.t) $(wc -c <two.t | xargs)
261 + 0000
262 + EOF
263 +
264 + test-tool serve-v2 --stateless-rpc <in >out &&
265 + test-tool pkt-line unpack <out >actual &&
266 + test_cmp expect actual
267 +'
268 +
269 test_done