Skip to content

Commit 63c3105

Browse files
calvin-wan-googlegitster
authored andcommitted
transport: add client support for object-info
Sometimes, it is beneficial to retrieve information about an object without downloading it entirely. The server-side logic for this functionality was implemented in commit "a2ba162cda (object-info: support for retrieving object info, 2021-04-20)." And the wire format is documented at https://git-scm.com/docs/protocol-v2#_object_info. This commit introduces client functions to interact with the server. Currently, the client supports requesting a list of object IDs with the 'size' feature from a v2 server. If the server does not advertise this feature (i.e., transfer.advertiseobjectinfo is set to false), the client will return an error and exit. Notice that the entire request is written into req_buf before being sent to the remote. This approach follows the pattern used in the `send_fetch_request()` logic within fetch-pack.c. Streaming the request is not addressed in this patch. Helped-by: Jonathan Tan <[email protected]> Helped-by: Christian Couder <[email protected]> Signed-off-by: Calvin Wan <[email protected]> Signed-off-by: Eric Ju <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ab4cc9d commit 63c3105

File tree

8 files changed

+160
-3
lines changed

8 files changed

+160
-3
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,7 @@ LIB_OBJS += ewah/ewah_rlw.o
10201020
LIB_OBJS += exec-cmd.o
10211021
LIB_OBJS += fetch-negotiator.o
10221022
LIB_OBJS += fetch-pack.o
1023+
LIB_OBJS += fetch-object-info.o
10231024
LIB_OBJS += fmt-merge-msg.o
10241025
LIB_OBJS += fsck.o
10251026
LIB_OBJS += fsmonitor.o

fetch-object-info.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#include "git-compat-util.h"
2+
#include "gettext.h"
3+
#include "hex.h"
4+
#include "pkt-line.h"
5+
#include "connect.h"
6+
#include "oid-array.h"
7+
#include "object-store-ll.h"
8+
#include "fetch-object-info.h"
9+
#include "string-list.h"
10+
11+
/* Sends git-cat-file object-info command and its arguments into the request buffer. */
12+
static void send_object_info_request(const int fd_out, struct object_info_args *args)
13+
{
14+
struct strbuf req_buf = STRBUF_INIT;
15+
16+
write_command_and_capabilities(&req_buf, "object-info", args->server_options);
17+
18+
if (unsorted_string_list_has_string(args->object_info_options, "size"))
19+
packet_buf_write(&req_buf, "size");
20+
21+
if (args->oids)
22+
for (size_t i = 0; i < args->oids->nr; i++)
23+
packet_buf_write(&req_buf, "oid %s", oid_to_hex(&args->oids->oid[i]));
24+
25+
packet_buf_flush(&req_buf);
26+
if (write_in_full(fd_out, req_buf.buf, req_buf.len) < 0)
27+
die_errno(_("unable to write request to remote"));
28+
29+
strbuf_release(&req_buf);
30+
}
31+
32+
int fetch_object_info(const enum protocol_version version, struct object_info_args *args,
33+
struct packet_reader *reader, struct object_info *object_info_data,
34+
const int stateless_rpc, const int fd_out)
35+
{
36+
int size_index = -1;
37+
38+
switch (version) {
39+
case protocol_v2:
40+
if (!server_supports_v2("object-info"))
41+
die(_("object-info capability is not enabled on the server"));
42+
send_object_info_request(fd_out, args);
43+
break;
44+
case protocol_v1:
45+
case protocol_v0:
46+
die(_("unsupported protocol version. expected v2"));
47+
case protocol_unknown_version:
48+
BUG("unknown protocol version");
49+
}
50+
51+
for (size_t i = 0; i < args->object_info_options->nr; i++) {
52+
if (packet_reader_read(reader) != PACKET_READ_NORMAL) {
53+
check_stateless_delimiter(stateless_rpc, reader, "stateless delimiter expected");
54+
return -1;
55+
}
56+
if (!string_list_has_string(args->object_info_options, reader->line))
57+
return -1;
58+
if (!strcmp(reader->line, "size")) {
59+
size_index = i;
60+
for (size_t j = 0; j < args->oids->nr; j++)
61+
object_info_data[j].sizep = xcalloc(1, sizeof(*object_info_data[j].sizep));
62+
}
63+
}
64+
65+
for (size_t i = 0; packet_reader_read(reader) == PACKET_READ_NORMAL && i < args->oids->nr; i++){
66+
struct string_list object_info_values = STRING_LIST_INIT_DUP;
67+
68+
string_list_split(&object_info_values, reader->line, ' ', -1);
69+
if (0 <= size_index) {
70+
if (!strcmp(object_info_values.items[1 + size_index].string, ""))
71+
die("object-info: not our ref %s",
72+
object_info_values.items[0].string);
73+
74+
if (strtoul_ul(object_info_values.items[1 + size_index].string, 10, object_info_data[i].sizep))
75+
die("object-info: ref %s has invalid size %s",
76+
object_info_values.items[0].string,
77+
object_info_values.items[1 + size_index].string);
78+
}
79+
80+
string_list_clear(&object_info_values, 0);
81+
}
82+
check_stateless_delimiter(stateless_rpc, reader, "stateless delimiter expected");
83+
84+
return 0;
85+
}

fetch-object-info.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef FETCH_OBJECT_INFO_H
2+
#define FETCH_OBJECT_INFO_H
3+
4+
#include "pkt-line.h"
5+
#include "protocol.h"
6+
#include "object-store-ll.h"
7+
8+
struct object_info_args {
9+
struct string_list *object_info_options;
10+
const struct string_list *server_options;
11+
struct oid_array *oids;
12+
};
13+
14+
/*
15+
* Sends git-cat-file object-info command into the request buf and read the
16+
* results from packets.
17+
*/
18+
int fetch_object_info(enum protocol_version version, struct object_info_args *args,
19+
struct packet_reader *reader, struct object_info *object_info_data,
20+
int stateless_rpc, int fd_out);
21+
22+
#endif /* FETCH_OBJECT_INFO_H */

fetch-pack.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,6 +1654,9 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
16541654
if (args->depth > 0 || args->deepen_since || args->deepen_not)
16551655
args->deepen = 1;
16561656

1657+
if (args->object_info)
1658+
state = FETCH_SEND_REQUEST;
1659+
16571660
while (state != FETCH_DONE) {
16581661
switch (state) {
16591662
case FETCH_CHECK_LOCAL:

fetch-pack.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ struct fetch_pack_args {
1616
const struct string_list *deepen_not;
1717
struct list_objects_filter_options filter_options;
1818
const struct string_list *server_options;
19+
struct object_info *object_info_data;
1920

2021
/*
2122
* If not NULL, during packfile negotiation, fetch-pack will send "have"
@@ -42,6 +43,7 @@ struct fetch_pack_args {
4243
unsigned reject_shallow_remote:1;
4344
unsigned deepen:1;
4445
unsigned refetch:1;
46+
unsigned object_info:1;
4547

4648
/*
4749
* Indicate that the remote of this request is a promisor remote. The

transport-helper.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -710,8 +710,8 @@ static int fetch_refs(struct transport *transport,
710710

711711
/*
712712
* If we reach here, then the server, the client, and/or the transport
713-
* helper does not support protocol v2. --negotiate-only requires
714-
* protocol v2.
713+
* helper does not support protocol v2. --negotiate-only and cat-file
714+
* remote-object-info require protocol v2.
715715
*/
716716
if (data->transport_options.acked_commits) {
717717
warning(_("--negotiate-only requires protocol v2"));
@@ -727,6 +727,13 @@ static int fetch_refs(struct transport *transport,
727727
free_refs(dummy);
728728
}
729729

730+
/* fail the command explicitly to avoid further commands input. */
731+
if (transport->smart_options->object_info)
732+
die(_("remote-object-info requires protocol v2"));
733+
734+
if (!data->get_refs_list_called)
735+
get_refs_list_using_list(transport, 0);
736+
730737
count = 0;
731738
for (i = 0; i < nr_heads; i++)
732739
if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))

transport.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "hook.h"
1010
#include "pkt-line.h"
1111
#include "fetch-pack.h"
12+
#include "fetch-object-info.h"
1213
#include "remote.h"
1314
#include "connect.h"
1415
#include "send-pack.h"
@@ -464,8 +465,33 @@ static int fetch_refs_via_pack(struct transport *transport,
464465
args.server_options = transport->server_options;
465466
args.negotiation_tips = data->options.negotiation_tips;
466467
args.reject_shallow_remote = transport->smart_options->reject_shallow;
468+
args.object_info = transport->smart_options->object_info;
469+
470+
if (transport->smart_options && transport->smart_options->object_info
471+
&& transport->smart_options->object_info_oids->nr > 0) {
472+
struct packet_reader reader;
473+
struct object_info_args obj_info_args = { 0 };
474+
475+
obj_info_args.server_options = transport->server_options;
476+
obj_info_args.oids = transport->smart_options->object_info_oids;
477+
obj_info_args.object_info_options = transport->smart_options->object_info_options;
478+
string_list_sort(obj_info_args.object_info_options);
479+
480+
connect_setup(transport, 0);
481+
packet_reader_init(&reader, data->fd[0], NULL, 0,
482+
PACKET_READ_CHOMP_NEWLINE |
483+
PACKET_READ_GENTLE_ON_EOF |
484+
PACKET_READ_DIE_ON_ERR_PACKET);
485+
486+
data->version = discover_version(&reader);
487+
transport->hash_algo = reader.hash_algo;
488+
489+
ret = fetch_object_info(data->version, &obj_info_args, &reader,
490+
data->options.object_info_data, transport->stateless_rpc,
491+
data->fd[1]);
492+
goto cleanup;
467493

468-
if (!data->finished_handshake) {
494+
} else if (!data->finished_handshake) {
469495
int i;
470496
int must_list_refs = 0;
471497
for (i = 0; i < nr_heads; i++) {

transport.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "remote.h"
66
#include "list-objects-filter-options.h"
77
#include "string-list.h"
8+
#include "object-store.h"
89

910
struct git_transport_options {
1011
unsigned thin : 1;
@@ -30,6 +31,12 @@ struct git_transport_options {
3031
*/
3132
unsigned connectivity_checked:1;
3233

34+
/*
35+
* Transport will attempt to retrieve only object-info.
36+
* If object-info is not supported, the operation will error and exit.
37+
*/
38+
unsigned object_info : 1;
39+
3340
int depth;
3441
const char *deepen_since;
3542
const struct string_list *deepen_not;
@@ -53,6 +60,10 @@ struct git_transport_options {
5360
* common commits to this oidset instead of fetching any packfiles.
5461
*/
5562
struct oidset *acked_commits;
63+
64+
struct oid_array *object_info_oids;
65+
struct object_info *object_info_data;
66+
struct string_list *object_info_options;
5667
};
5768

5869
enum transport_family {

0 commit comments

Comments
 (0)