fetch-pack: perform a fetch using v2

When communicating with a v2 server, perform a fetch by requesting the 'fetch' command. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Brandon Williams committed Mar 15, 2018 at 10:31 UTC 685fbd32916f3e94bc89aa14e8fdce835b06f801
10 files changed +548 -48
Documentation/technical/protocol-v2.txt
+66 -1
@@ -255,12 +255,43 @@ A `fetch` request can take the following arguments:
255 to its base by position in pack rather than by an oid. That is,
256 they can read OBJ_OFS_DELTA (ake type 6) in a packfile.
257
258 + shallow <oid>
259 + A client must notify the server of all commits for which it only
260 + has shallow copies (meaning that it doesn't have the parents of
261 + a commit) by supplying a 'shallow <oid>' line for each such
262 + object so that the server is aware of the limitations of the
263 + client's history. This is so that the server is aware that the
264 + client may not have all objects reachable from such commits.
265 +
266 + deepen <depth>
267 + Requests that the fetch/clone should be shallow having a commit
268 + depth of <depth> relative to the remote side.
269 +
270 + deepen-relative
271 + Requests that the semantics of the "deepen" command be changed
272 + to indicate that the depth requested is relative to the client's
273 + current shallow boundary, instead of relative to the requested
274 + commits.
275 +
276 + deepen-since <timestamp>
277 + Requests that the shallow clone/fetch should be cut at a
278 + specific time, instead of depth. Internally it's equivalent to
279 + doing "git rev-list --max-age=<timestamp>". Cannot be used with
280 + "deepen".
281 +
282 + deepen-not <rev>
283 + Requests that the shallow clone/fetch should be cut at a
284 + specific revision specified by '<rev>', instead of a depth.
285 + Internally it's equivalent of doing "git rev-list --not <rev>".
286 + Cannot be used with "deepen", but can be used with
287 + "deepen-since".
288 +
289 The response of `fetch` is broken into a number of sections separated by
290 delimiter packets (0001), with each section beginning with its section
291 header.
292
293 output = *section
263 - section = (acknowledgments | packfile)
294 + section = (acknowledgments | shallow-info | packfile)
295 (flush-pkt | delim-pkt)
296
297 acknowledgments = PKT-LINE("acknowledgments" LF)
@@ -270,6 +301,11 @@ header.
301 nak = PKT-LINE("NAK" LF)
302 ack = PKT-LINE("ACK" SP obj-id LF)
303
304 + shallow-info = PKT-LINE("shallow-info" LF)
305 + *PKT-LINE((shallow | unshallow) LF)
306 + shallow = "shallow" SP obj-id
307 + unshallow = "unshallow" SP obj-id
308 +
309 packfile = PKT-LINE("packfile" LF)
310 *PKT-LINE(%x01-03 *%x00-ff)
311
@@ -301,6 +337,35 @@ header.
337 determined the objects it plans to send to the client and no
338 further negotiation is needed.
339
340 + shallow-info section
341 + If the client has requested a shallow fetch/clone, a shallow
342 + client requests a fetch or the server is shallow then the
343 + server's response may include a shallow-info section. The
344 + shallow-info section will be included if (due to one of the
345 + above conditions) the server needs to inform the client of any
346 + shallow boundaries or adjustments to the clients already
347 + existing shallow boundaries.
348 +
349 + * Always begins with the section header "shallow-info"
350 +
351 + * If a positive depth is requested, the server will compute the
352 + set of commits which are no deeper than the desired depth.
353 +
354 + * The server sends a "shallow obj-id" line for each commit whose
355 + parents will not be sent in the following packfile.
356 +
357 + * The server sends an "unshallow obj-id" line for each commit
358 + which the client has indicated is shallow, but is no longer
359 + shallow as a result of the fetch (due to its parents being
360 + sent in the following packfile).
361 +
362 + * The server MUST NOT send any "unshallow" lines for anything
363 + which the client has not indicated was shallow as a part of
364 + its request.
365 +
366 + * This section is only included if a packfile section is also
367 + included in the response.
368 +
369 packfile section
370 * This section is only included if the client has sent 'want'
371 lines in its request and either requested that no more
builtin/fetch-pack.c
+1 -1
@@ -212,7 +212,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
212 }
213
214 ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought,
215 - &shallow, pack_lockfile_ptr);
215 + &shallow, pack_lockfile_ptr, protocol_v0);
216 if (pack_lockfile) {
217 printf("lock %s\n", pack_lockfile);
218 fflush(stdout);
fetch-pack.c
+264 -6
@@ -303,9 +303,9 @@ static void insert_one_alternate_object(struct object *obj)
303 #define PIPESAFE_FLUSH 32
304 #define LARGE_FLUSH 16384
305
306 -static int next_flush(struct fetch_pack_args *args, int count)
306 +static int next_flush(int stateless_rpc, int count)
307 {
308 - if (args->stateless_rpc) {
308 + if (stateless_rpc) {
309 if (count < LARGE_FLUSH)
310 count <<= 1;
311 else
@@ -461,7 +461,7 @@ static int find_common(struct fetch_pack_args *args,
461 send_request(args, fd[1], &req_buf);
462 strbuf_setlen(&req_buf, state_len);
463 flushes++;
464 - flush_at = next_flush(args, count);
464 + flush_at = next_flush(args->stateless_rpc, count);
465
466 /*
467 * We keep one window "ahead" of the other side, and
@@ -1008,6 +1008,259 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
1008 return ref;
1009 }
1010
1011 +static void add_wants(const struct ref *wants, struct strbuf *req_buf)
1012 +{
1013 + for ( ; wants ; wants = wants->next) {
1014 + const struct object_id *remote = &wants->old_oid;
1015 + const char *remote_hex;
1016 + struct object *o;
1017 +
1018 + /*
1019 + * If that object is complete (i.e. it is an ancestor of a
1020 + * local ref), we tell them we have it but do not have to
1021 + * tell them about its ancestors, which they already know
1022 + * about.
1023 + *
1024 + * We use lookup_object here because we are only
1025 + * interested in the case we *know* the object is
1026 + * reachable and we have already scanned it.
1027 + */
1028 + if (((o = lookup_object(remote->hash)) != NULL) &&
1029 + (o->flags & COMPLETE)) {
1030 + continue;
1031 + }
1032 +
1033 + remote_hex = oid_to_hex(remote);
1034 + packet_buf_write(req_buf, "want %s\n", remote_hex);
1035 + }
1036 +}
1037 +
1038 +static void add_common(struct strbuf *req_buf, struct oidset *common)
1039 +{
1040 + struct oidset_iter iter;
1041 + const struct object_id *oid;
1042 + oidset_iter_init(common, &iter);
1043 +
1044 + while ((oid = oidset_iter_next(&iter))) {
1045 + packet_buf_write(req_buf, "have %s\n", oid_to_hex(oid));
1046 + }
1047 +}
1048 +
1049 +static int add_haves(struct strbuf *req_buf, int *haves_to_send, int *in_vain)
1050 +{
1051 + int ret = 0;
1052 + int haves_added = 0;
1053 + const struct object_id *oid;
1054 +
1055 + while ((oid = get_rev())) {
1056 + packet_buf_write(req_buf, "have %s\n", oid_to_hex(oid));
1057 + if (++haves_added >= *haves_to_send)
1058 + break;
1059 + }
1060 +
1061 + *in_vain += haves_added;
1062 + if (!haves_added || *in_vain >= MAX_IN_VAIN) {
1063 + /* Send Done */
1064 + packet_buf_write(req_buf, "done\n");
1065 + ret = 1;
1066 + }
1067 +
1068 + /* Increase haves to send on next round */
1069 + *haves_to_send = next_flush(1, *haves_to_send);
1070 +
1071 + return ret;
1072 +}
1073 +
1074 +static int send_fetch_request(int fd_out, const struct fetch_pack_args *args,
1075 + const struct ref *wants, struct oidset *common,
1076 + int *haves_to_send, int *in_vain)
1077 +{
1078 + int ret = 0;
1079 + struct strbuf req_buf = STRBUF_INIT;
1080 +
1081 + if (server_supports_v2("fetch", 1))
1082 + packet_buf_write(&req_buf, "command=fetch");
1083 + if (server_supports_v2("agent", 0))
1084 + packet_buf_write(&req_buf, "agent=%s", git_user_agent_sanitized());
1085 +
1086 + packet_buf_delim(&req_buf);
1087 + if (args->use_thin_pack)
1088 + packet_buf_write(&req_buf, "thin-pack");
1089 + if (args->no_progress)
1090 + packet_buf_write(&req_buf, "no-progress");
1091 + if (args->include_tag)
1092 + packet_buf_write(&req_buf, "include-tag");
1093 + if (prefer_ofs_delta)
1094 + packet_buf_write(&req_buf, "ofs-delta");
1095 +
1096 + /* add wants */
1097 + add_wants(wants, &req_buf);
1098 +
1099 + /* Add all of the common commits we've found in previous rounds */
1100 + add_common(&req_buf, common);
1101 +
1102 + /* Add initial haves */
1103 + ret = add_haves(&req_buf, haves_to_send, in_vain);
1104 +
1105 + /* Send request */
1106 + packet_buf_flush(&req_buf);
1107 + write_or_die(fd_out, req_buf.buf, req_buf.len);
1108 +
1109 + strbuf_release(&req_buf);
1110 + return ret;
1111 +}
1112 +
1113 +/*
1114 + * Processes a section header in a server's response and checks if it matches
1115 + * `section`. If the value of `peek` is 1, the header line will be peeked (and
1116 + * not consumed); if 0, the line will be consumed and the function will die if
1117 + * the section header doesn't match what was expected.
1118 + */
1119 +static int process_section_header(struct packet_reader *reader,
1120 + const char *section, int peek)
1121 +{
1122 + int ret;
1123 +
1124 + if (packet_reader_peek(reader) != PACKET_READ_NORMAL)
1125 + die("error reading packet");
1126 +
1127 + ret = !strcmp(reader->line, section);
1128 +
1129 + if (!peek) {
1130 + if (!ret)
1131 + die("expected '%s', received '%s'",
1132 + section, reader->line);
1133 + packet_reader_read(reader);
1134 + }
1135 +
1136 + return ret;
1137 +}
1138 +
1139 +static int process_acks(struct packet_reader *reader, struct oidset *common)
1140 +{
1141 + /* received */
1142 + int received_ready = 0;
1143 + int received_ack = 0;
1144 +
1145 + process_section_header(reader, "acknowledgments", 0);
1146 + while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
1147 + const char *arg;
1148 +
1149 + if (!strcmp(reader->line, "NAK"))
1150 + continue;
1151 +
1152 + if (skip_prefix(reader->line, "ACK ", &arg)) {
1153 + struct object_id oid;
1154 + if (!get_oid_hex(arg, &oid)) {
1155 + struct commit *commit;
1156 + oidset_insert(common, &oid);
1157 + commit = lookup_commit(&oid);
1158 + mark_common(commit, 0, 1);
1159 + }
1160 + continue;
1161 + }
1162 +
1163 + if (!strcmp(reader->line, "ready")) {
1164 + clear_prio_queue(&rev_list);
1165 + received_ready = 1;
1166 + continue;
1167 + }
1168 +
1169 + die("unexpected acknowledgment line: '%s'", reader->line);
1170 + }
1171 +
1172 + if (reader->status != PACKET_READ_FLUSH &&
1173 + reader->status != PACKET_READ_DELIM)
1174 + die("error processing acks: %d", reader->status);
1175 +
1176 + /* return 0 if no common, 1 if there are common, or 2 if ready */
1177 + return received_ready ? 2 : (received_ack ? 1 : 0);
1178 +}
1179 +
1180 +enum fetch_state {
1181 + FETCH_CHECK_LOCAL = 0,
1182 + FETCH_SEND_REQUEST,
1183 + FETCH_PROCESS_ACKS,
1184 + FETCH_GET_PACK,
1185 + FETCH_DONE,
1186 +};
1187 +
1188 +static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
1189 + int fd[2],
1190 + const struct ref *orig_ref,
1191 + struct ref **sought, int nr_sought,
1192 + char **pack_lockfile)
1193 +{
1194 + struct ref *ref = copy_ref_list(orig_ref);
1195 + enum fetch_state state = FETCH_CHECK_LOCAL;
1196 + struct oidset common = OIDSET_INIT;
1197 + struct packet_reader reader;
1198 + int in_vain = 0;
1199 + int haves_to_send = INITIAL_FLUSH;
1200 + packet_reader_init(&reader, fd[0], NULL, 0,
1201 + PACKET_READ_CHOMP_NEWLINE);
1202 +
1203 + while (state != FETCH_DONE) {
1204 + switch (state) {
1205 + case FETCH_CHECK_LOCAL:
1206 + sort_ref_list(&ref, ref_compare_name);
1207 + QSORT(sought, nr_sought, cmp_ref_by_name);
1208 +
1209 + /* v2 supports these by default */
1210 + allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
1211 + use_sideband = 2;
1212 +
1213 + if (marked)
1214 + for_each_ref(clear_marks, NULL);
1215 + marked = 1;
1216 +
1217 + for_each_ref(rev_list_insert_ref_oid, NULL);
1218 + for_each_cached_alternate(insert_one_alternate_object);
1219 +
1220 + /* Filter 'ref' by 'sought' and those that aren't local */
1221 + if (everything_local(args, &ref, sought, nr_sought))
1222 + state = FETCH_DONE;
1223 + else
1224 + state = FETCH_SEND_REQUEST;
1225 + break;
1226 + case FETCH_SEND_REQUEST:
1227 + if (send_fetch_request(fd[1], args, ref, &common,
1228 + &haves_to_send, &in_vain))
1229 + state = FETCH_GET_PACK;
1230 + else
1231 + state = FETCH_PROCESS_ACKS;
1232 + break;
1233 + case FETCH_PROCESS_ACKS:
1234 + /* Process ACKs/NAKs */
1235 + switch (process_acks(&reader, &common)) {
1236 + case 2:
1237 + state = FETCH_GET_PACK;
1238 + break;
1239 + case 1:
1240 + in_vain = 0;
1241 + /* fallthrough */
1242 + default:
1243 + state = FETCH_SEND_REQUEST;
1244 + break;
1245 + }
1246 + break;
1247 + case FETCH_GET_PACK:
1248 + /* get the pack */
1249 + process_section_header(&reader, "packfile", 0);
1250 + if (get_pack(args, fd, pack_lockfile))
1251 + die(_("git fetch-pack: fetch failed."));
1252 +
1253 + state = FETCH_DONE;
1254 + break;
1255 + case FETCH_DONE:
1256 + continue;
1257 + }
1258 + }
1259 +
1260 + oidset_clear(&common);
1261 + return ref;
1262 +}
1263 +
1264 static void fetch_pack_config(void)
1265 {
1266 git_config_get_int("fetch.unpacklimit", &fetch_unpack_limit);
@@ -1153,7 +1406,8 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
1406 const char *dest,
1407 struct ref **sought, int nr_sought,
1408 struct oid_array *shallow,
1156 - char **pack_lockfile)
1409 + char **pack_lockfile,
1410 + enum protocol_version version)
1411 {
1412 struct ref *ref_cpy;
1413 struct shallow_info si;
@@ -1167,8 +1421,12 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
1421 die(_("no matching remote head"));
1422 }
1423 prepare_shallow_info(&si, shallow);
1170 - ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
1171 - &si, pack_lockfile);
1424 + if (version == protocol_v2)
1425 + ref_cpy = do_fetch_pack_v2(args, fd, ref, sought, nr_sought,
1426 + pack_lockfile);
1427 + else
1428 + ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
1429 + &si, pack_lockfile);
1430 reprepare_packed_git();
1431 update_shallow(args, sought, nr_sought, &si);
1432 clear_shallow_info(&si);
fetch-pack.h
+3 -1
@@ -3,6 +3,7 @@
3
4 #include "string-list.h"
5 #include "run-command.h"
6 +#include "protocol.h"
7
8 struct oid_array;
9
@@ -43,7 +44,8 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
44 struct ref **sought,
45 int nr_sought,
46 struct oid_array *shallow,
46 - char **pack_lockfile);
47 + char **pack_lockfile,
48 + enum protocol_version version);
49
50 /*
51 * Print an appropriate error message for each sought ref that wasn't
serve.c
+1 -1
@@ -55,7 +55,7 @@ struct protocol_capability {
55 static struct protocol_capability capabilities[] = {
56 { "agent", agent_advertise, NULL },
57 { "ls-refs", always_advertise, ls_refs },
58 - { "fetch", always_advertise, upload_pack_v2 },
58 + { "fetch", upload_pack_advertise, upload_pack_v2 },
59 };
60
61 static void advertise_capabilities(void)
t/t5701-git-serve.sh
+1 -1
@@ -9,7 +9,7 @@ test_expect_success 'test capability advertisement' '
9 version 2
10 agent=git/$(git version | cut -d" " -f3)
11 ls-refs
12 - fetch
12 + fetch=shallow
13 0000
14 EOF
15
t/t5702-protocol-v2.sh
+97
@@ -45,6 +45,56 @@ test_expect_success 'ref advertisment is filtered with ls-remote using protocol
45 test_cmp actual expect
46 '
47
48 +test_expect_success 'clone with git:// using protocol v2' '
49 + test_when_finished "rm -f log" &&
50 +
51 + GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
52 + clone "$GIT_DAEMON_URL/parent" daemon_child &&
53 +
54 + git -C daemon_child log -1 --format=%s >actual &&
55 + git -C "$daemon_parent" log -1 --format=%s >expect &&
56 + test_cmp expect actual &&
57 +
58 + # Client requested to use protocol v2
59 + grep "clone> .*\\\0\\\0version=2\\\0$" log &&
60 + # Server responded using protocol v2
61 + grep "clone< version 2" log
62 +'
63 +
64 +test_expect_success 'fetch with git:// using protocol v2' '
65 + test_when_finished "rm -f log" &&
66 +
67 + test_commit -C "$daemon_parent" two &&
68 +
69 + GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_child -c protocol.version=2 \
70 + fetch &&
71 +
72 + git -C daemon_child log -1 --format=%s origin/master >actual &&
73 + git -C "$daemon_parent" log -1 --format=%s >expect &&
74 + test_cmp expect actual &&
75 +
76 + # Client requested to use protocol v2
77 + grep "fetch> .*\\\0\\\0version=2\\\0$" log &&
78 + # Server responded using protocol v2
79 + grep "fetch< version 2" log
80 +'
81 +
82 +test_expect_success 'pull with git:// using protocol v2' '
83 + test_when_finished "rm -f log" &&
84 +
85 + GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_child -c protocol.version=2 \
86 + pull &&
87 +
88 + git -C daemon_child log -1 --format=%s >actual &&
89 + git -C "$daemon_parent" log -1 --format=%s >expect &&
90 + test_cmp expect actual &&
91 +
92 + # Client requested to use protocol v2
93 + grep "fetch> .*\\\0\\\0version=2\\\0$" log &&
94 + # Server responded using protocol v2
95 + grep "fetch< version 2" log
96 +'
97 +
98 stop_git_daemon
99
100 # Test protocol v2 with 'file://' transport
@@ -80,4 +130,51 @@ test_expect_success 'ref advertisment is filtered with ls-remote using protocol
130 test_cmp actual expect
131 '
132
133 +test_expect_success 'clone with file:// using protocol v2' '
134 + test_when_finished "rm -f log" &&
135 +
136 + GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
137 + clone "file://$(pwd)/file_parent" file_child &&
138 +
139 + git -C file_child log -1 --format=%s >actual &&
140 + git -C file_parent log -1 --format=%s >expect &&
141 + test_cmp expect actual &&
142 +
143 + # Server responded using protocol v2
144 + grep "clone< version 2" log
145 +'
146 +
147 +test_expect_success 'fetch with file:// using protocol v2' '
148 + test_when_finished "rm -f log" &&
149 +
150 + test_commit -C file_parent two &&
151 +
152 + GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \
153 + fetch origin &&
154 +
155 + git -C file_child log -1 --format=%s origin/master >actual &&
156 + git -C file_parent log -1 --format=%s >expect &&
157 + test_cmp expect actual &&
158 +
159 + # Server responded using protocol v2
160 + grep "fetch< version 2" log
161 +'
162 +
163 +test_expect_success 'ref advertisment is filtered during fetch using protocol v2' '
164 + test_when_finished "rm -f log" &&
165 +
166 + test_commit -C file_parent three &&
167 +
168 + GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \
169 + fetch origin master &&
170 +
171 + git -C file_child log -1 --format=%s origin/master >actual &&
172 + git -C file_parent log -1 --format=%s >expect &&
173 + test_cmp expect actual &&
174 +
175 + ! grep "refs/tags/one" log &&
176 + ! grep "refs/tags/two" log &&
177 + ! grep "refs/tags/three" log
178 +'
179 +
180 test_done
transport.c
+5 -2
@@ -258,14 +258,17 @@ static int fetch_refs_via_pack(struct transport *transport,
258
259 switch (data->version) {
260 case protocol_v2:
261 - die("support for protocol v2 not implemented yet");
261 + refs = fetch_pack(&args, data->fd, data->conn,
262 + refs_tmp ? refs_tmp : transport->remote_refs,
263 + dest, to_fetch, nr_heads, &data->shallow,
264 + &transport->pack_lockfile, data->version);
265 break;
266 case protocol_v1:
267 case protocol_v0:
268 refs = fetch_pack(&args, data->fd, data->conn,
269 refs_tmp ? refs_tmp : transport->remote_refs,
270 dest, to_fetch, nr_heads, &data->shallow,
268 - &transport->pack_lockfile);
271 + &transport->pack_lockfile, data->version);
272 break;
273 case protocol_unknown_version:
274 BUG("unknown protocol version");
upload-pack.c
+106 -35
@@ -710,7 +710,6 @@ static void deepen(int depth, int deepen_relative,
710 }
711
712 send_unshallow(shallows);
713 - packet_flush(1);
713 }
714
715 static void deepen_by_rev_list(int ac, const char **av,
@@ -722,7 +721,53 @@ static void deepen_by_rev_list(int ac, const char **av,
721 send_shallow(result);
722 free_commit_list(result);
723 send_unshallow(shallows);
725 - packet_flush(1);
724 +}
725 +
726 +/* Returns 1 if a shallow list is sent or 0 otherwise */
727 +static int send_shallow_list(int depth, int deepen_rev_list,
728 + timestamp_t deepen_since,
729 + struct string_list *deepen_not,
730 + struct object_array *shallows)
731 +{
732 + int ret = 0;
733 +
734 + if (depth > 0 && deepen_rev_list)
735 + die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together");
736 + if (depth > 0) {
737 + deepen(depth, deepen_relative, shallows);
738 + ret = 1;
739 + } else if (deepen_rev_list) {
740 + struct argv_array av = ARGV_ARRAY_INIT;
741 + int i;
742 +
743 + argv_array_push(&av, "rev-list");
744 + if (deepen_since)
745 + argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since);
746 + if (deepen_not->nr) {
747 + argv_array_push(&av, "--not");
748 + for (i = 0; i < deepen_not->nr; i++) {
749 + struct string_list_item *s = deepen_not->items + i;
750 + argv_array_push(&av, s->string);
751 + }
752 + argv_array_push(&av, "--not");
753 + }
754 + for (i = 0; i < want_obj.nr; i++) {
755 + struct object *o = want_obj.objects[i].item;
756 + argv_array_push(&av, oid_to_hex(&o->oid));
757 + }
758 + deepen_by_rev_list(av.argc, av.argv, shallows);
759 + argv_array_clear(&av);
760 + ret = 1;
761 + } else {
762 + if (shallows->nr > 0) {
763 + int i;
764 + for (i = 0; i < shallows->nr; i++)
765 + register_shallow(&shallows->objects[i].item->oid);
766 + }
767 + }
768 +
769 + shallow_nr += shallows->nr;
770 + return ret;
771 }
772
773 static int process_shallow(const char *line, struct object_array *shallows)
@@ -884,40 +929,10 @@ static void receive_needs(void)
929
930 if (depth == 0 && !deepen_rev_list && shallows.nr == 0)
931 return;
887 - if (depth > 0 && deepen_rev_list)
888 - die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together");
889 - if (depth > 0)
890 - deepen(depth, deepen_relative, &shallows);
891 - else if (deepen_rev_list) {
892 - struct argv_array av = ARGV_ARRAY_INIT;
893 - int i;
932
895 - argv_array_push(&av, "rev-list");
896 - if (deepen_since)
897 - argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since);
898 - if (deepen_not.nr) {
899 - argv_array_push(&av, "--not");
900 - for (i = 0; i < deepen_not.nr; i++) {
901 - struct string_list_item *s = deepen_not.items + i;
902 - argv_array_push(&av, s->string);
903 - }
904 - argv_array_push(&av, "--not");
905 - }
906 - for (i = 0; i < want_obj.nr; i++) {
907 - struct object *o = want_obj.objects[i].item;
908 - argv_array_push(&av, oid_to_hex(&o->oid));
909 - }
910 - deepen_by_rev_list(av.argc, av.argv, &shallows);
911 - argv_array_clear(&av);
912 - }
913 - else
914 - if (shallows.nr > 0) {
915 - int i;
916 - for (i = 0; i < shallows.nr; i++)
917 - register_shallow(&shallows.objects[i].item->oid);
918 - }
919 -
920 - shallow_nr += shallows.nr;
933 + if (send_shallow_list(depth, deepen_rev_list, deepen_since,
934 + &deepen_not, &shallows))
935 + packet_flush(1);
936 object_array_clear(&shallows);
937 }
938
@@ -1071,6 +1086,13 @@ struct upload_pack_data {
1086 struct object_array wants;
1087 struct oid_array haves;
1088
1089 + struct object_array shallows;
1090 + struct string_list deepen_not;
1091 + int depth;
1092 + timestamp_t deepen_since;
1093 + int deepen_rev_list;
1094 + int deepen_relative;
1095 +
1096 unsigned stateless_rpc : 1;
1097
1098 unsigned use_thin_pack : 1;
@@ -1084,16 +1106,22 @@ static void upload_pack_data_init(struct upload_pack_data *data)
1106 {
1107 struct object_array wants = OBJECT_ARRAY_INIT;
1108 struct oid_array haves = OID_ARRAY_INIT;
1109 + struct object_array shallows = OBJECT_ARRAY_INIT;
1110 + struct string_list deepen_not = STRING_LIST_INIT_DUP;
1111
1112 memset(data, 0, sizeof(*data));
1113 data->wants = wants;
1114 data->haves = haves;
1115 + data->shallows = shallows;
1116 + data->deepen_not = deepen_not;
1117 }
1118
1119 static void upload_pack_data_clear(struct upload_pack_data *data)
1120 {
1121 object_array_clear(&data->wants);
1122 oid_array_clear(&data->haves);
1123 + object_array_clear(&data->shallows);
1124 + string_list_clear(&data->deepen_not, 0);
1125 }
1126
1127 static int parse_want(const char *line)
@@ -1177,6 +1205,22 @@ static void process_args(struct packet_reader *request,
1205 continue;
1206 }
1207
1208 + /* Shallow related arguments */
1209 + if (process_shallow(arg, &data->shallows))
1210 + continue;
1211 + if (process_deepen(arg, &data->depth))
1212 + continue;
1213 + if (process_deepen_since(arg, &data->deepen_since,
1214 + &data->deepen_rev_list))
1215 + continue;
1216 + if (process_deepen_not(arg, &data->deepen_not,
1217 + &data->deepen_rev_list))
1218 + continue;
1219 + if (!strcmp(arg, "deepen-relative")) {
1220 + data->deepen_relative = 1;
1221 + continue;
1222 + }
1223 +
1224 /* ignore unknown lines maybe? */
1225 die("unexpect line: '%s'", arg);
1226 }
@@ -1272,6 +1316,23 @@ static int process_haves_and_send_acks(struct upload_pack_data *data)
1316 return ret;
1317 }
1318
1319 +static void send_shallow_info(struct upload_pack_data *data)
1320 +{
1321 + /* No shallow info needs to be sent */
1322 + if (!data->depth && !data->deepen_rev_list && !data->shallows.nr &&
1323 + !is_repository_shallow())
1324 + return;
1325 +
1326 + packet_write_fmt(1, "shallow-info\n");
1327 +
1328 + if (!send_shallow_list(data->depth, data->deepen_rev_list,
1329 + data->deepen_since, &data->deepen_not,
1330 + &data->shallows) && is_repository_shallow())
1331 + deepen(INFINITE_DEPTH, data->deepen_relative, &data->shallows);
1332 +
1333 + packet_delim(1);
1334 +}
1335 +
1336 enum fetch_state {
1337 FETCH_PROCESS_ARGS = 0,
1338 FETCH_SEND_ACKS,
@@ -1319,6 +1380,8 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,
1380 state = FETCH_DONE;
1381 break;
1382 case FETCH_SEND_PACK:
1383 + send_shallow_info(&data);
1384 +
1385 packet_write_fmt(1, "packfile\n");
1386 create_pack_file();
1387 state = FETCH_DONE;
@@ -1331,3 +1394,11 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,
1394 upload_pack_data_clear(&data);
1395 return 0;
1396 }
1397 +
1398 +int upload_pack_advertise(struct repository *r,
1399 + struct strbuf *value)
1400 +{
1401 + if (value)
1402 + strbuf_addstr(value, "shallow");
1403 + return 1;
1404 +}
upload-pack.h
+4
@@ -16,4 +16,8 @@ struct packet_reader;
16 extern int upload_pack_v2(struct repository *r, struct argv_array *keys,
17 struct packet_reader *request);
18
19 +struct strbuf;
20 +extern int upload_pack_advertise(struct repository *r,
21 + struct strbuf *value);
22 +
23 #endif /* UPLOAD_PACK_H */