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 */