1 package org.eclipse.jgit.transport;
2
3 import static org.hamcrest.MatcherAssert.assertThat;
4 import static org.hamcrest.Matchers.containsInAnyOrder;
5 import static org.hamcrest.Matchers.containsString;
6 import static org.hamcrest.Matchers.hasItems;
7 import static org.hamcrest.Matchers.is;
8 import static org.hamcrest.Matchers.notNullValue;
9 import static org.junit.Assert.assertEquals;
10 import static org.junit.Assert.assertFalse;
11 import static org.junit.Assert.assertNotNull;
12 import static org.junit.Assert.assertThrows;
13 import static org.junit.Assert.assertTrue;
14
15 import java.io.ByteArrayInputStream;
16 import java.io.ByteArrayOutputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.StringWriter;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Objects;
28 import java.util.function.Consumer;
29
30 import org.eclipse.jgit.dircache.DirCache;
31 import org.eclipse.jgit.dircache.DirCacheBuilder;
32 import org.eclipse.jgit.dircache.DirCacheEntry;
33 import org.eclipse.jgit.errors.TransportException;
34 import org.eclipse.jgit.internal.storage.dfs.DfsGarbageCollector;
35 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
36 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
37 import org.eclipse.jgit.internal.storage.file.PackLock;
38 import org.eclipse.jgit.internal.storage.pack.CachedPack;
39 import org.eclipse.jgit.internal.storage.pack.CachedPackUriProvider;
40 import org.eclipse.jgit.junit.TestRepository;
41 import org.eclipse.jgit.lib.NullProgressMonitor;
42 import org.eclipse.jgit.lib.ObjectId;
43 import org.eclipse.jgit.lib.ObjectInserter;
44 import org.eclipse.jgit.lib.PersonIdent;
45 import org.eclipse.jgit.lib.ProgressMonitor;
46 import org.eclipse.jgit.lib.Ref;
47 import org.eclipse.jgit.lib.RefDatabase;
48 import org.eclipse.jgit.lib.Repository;
49 import org.eclipse.jgit.lib.Sets;
50 import org.eclipse.jgit.lib.TextProgressMonitor;
51 import org.eclipse.jgit.revwalk.RevBlob;
52 import org.eclipse.jgit.revwalk.RevCommit;
53 import org.eclipse.jgit.revwalk.RevTag;
54 import org.eclipse.jgit.revwalk.RevTree;
55 import org.eclipse.jgit.storage.pack.PackStatistics;
56 import org.eclipse.jgit.transport.UploadPack.RequestPolicy;
57 import org.eclipse.jgit.util.io.NullOutputStream;
58 import org.junit.After;
59 import org.junit.Before;
60 import org.junit.Test;
61
62
63
64
65 public class UploadPackTest {
66 private URIish uri;
67
68 private TestProtocol<Object> testProtocol;
69
70 private final Object ctx = new Object();
71
72 private InMemoryRepository server;
73
74 private InMemoryRepository client;
75
76 private TestRepository<InMemoryRepository> remote;
77
78 private PackStatistics stats;
79
80 @Before
81 public void setUp() throws Exception {
82 server = newRepo("server");
83 client = newRepo("client");
84
85 remote = new TestRepository<>(server);
86 }
87
88 @After
89 public void tearDown() {
90 Transport.unregister(testProtocol);
91 }
92
93 private static InMemoryRepository newRepo(String name) {
94 return new InMemoryRepository(new DfsRepositoryDescription(name));
95 }
96
97 private void generateBitmaps(InMemoryRepository repo) throws Exception {
98 new DfsGarbageCollector(repo).pack(null);
99 repo.scanForRepoChanges();
100 }
101
102 @Test
103 public void testFetchParentOfShallowCommit() throws Exception {
104 RevCommit commit0 = remote.commit().message("0").create();
105 RevCommit commit1 = remote.commit().message("1").parent(commit0).create();
106 RevCommit tip = remote.commit().message("2").parent(commit1).create();
107 remote.update("master", tip);
108
109 testProtocol = new TestProtocol<>((Object req, Repository db) -> {
110 UploadPack up = new UploadPack(db);
111 up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
112
113 up.getRevWalk()
114 .assumeShallow(Collections.singleton(commit1.getId()));
115 return up;
116 }, null);
117 uri = testProtocol.register(ctx, server);
118
119 assertFalse(client.getObjectDatabase().has(commit0.toObjectId()));
120
121
122 try (Transport tn = testProtocol.open(uri, client, "server")) {
123 tn.fetch(NullProgressMonitor.INSTANCE,
124 Collections.singletonList(new RefSpec(commit0.name())));
125 assertTrue(client.getObjectDatabase().has(commit0.toObjectId()));
126 }
127 }
128
129 @Test
130 public void testFetchWithBlobNoneFilter() throws Exception {
131 InMemoryRepository server2 = newRepo("server2");
132 try (TestRepository<InMemoryRepository> remote2 = new TestRepository<>(
133 server2)) {
134 RevBlob blob1 = remote2.blob("foobar");
135 RevBlob blob2 = remote2.blob("fooba");
136 RevTree tree = remote2.tree(remote2.file("1", blob1),
137 remote2.file("2", blob2));
138 RevCommit commit = remote2.commit(tree);
139 remote2.update("master", commit);
140
141 server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
142 true);
143
144 testProtocol = new TestProtocol<>((Object req, Repository db) -> {
145 UploadPack up = new UploadPack(db);
146 return up;
147 }, null);
148 uri = testProtocol.register(ctx, server2);
149
150 try (Transport tn = testProtocol.open(uri, client, "server2")) {
151 tn.setFilterSpec(FilterSpec.withBlobLimit(0));
152 tn.fetch(NullProgressMonitor.INSTANCE,
153 Collections.singletonList(new RefSpec(commit.name())));
154 assertTrue(client.getObjectDatabase().has(tree.toObjectId()));
155 assertFalse(client.getObjectDatabase().has(blob1.toObjectId()));
156 assertFalse(client.getObjectDatabase().has(blob2.toObjectId()));
157 }
158 }
159 }
160
161 @Test
162 public void testFetchExplicitBlobWithFilter() throws Exception {
163 InMemoryRepository server2 = newRepo("server2");
164 try (TestRepository<InMemoryRepository> remote2 = new TestRepository<>(
165 server2)) {
166 RevBlob blob1 = remote2.blob("foobar");
167 RevBlob blob2 = remote2.blob("fooba");
168 RevTree tree = remote2.tree(remote2.file("1", blob1),
169 remote2.file("2", blob2));
170 RevCommit commit = remote2.commit(tree);
171 remote2.update("master", commit);
172 remote2.update("a_blob", blob1);
173
174 server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
175 true);
176
177 testProtocol = new TestProtocol<>((Object req, Repository db) -> {
178 UploadPack up = new UploadPack(db);
179 return up;
180 }, null);
181 uri = testProtocol.register(ctx, server2);
182
183 try (Transport tn = testProtocol.open(uri, client, "server2")) {
184 tn.setFilterSpec(FilterSpec.withBlobLimit(0));
185 tn.fetch(NullProgressMonitor.INSTANCE, Arrays.asList(
186 new RefSpec(commit.name()), new RefSpec(blob1.name())));
187 assertTrue(client.getObjectDatabase().has(tree.toObjectId()));
188 assertTrue(client.getObjectDatabase().has(blob1.toObjectId()));
189 assertFalse(client.getObjectDatabase().has(blob2.toObjectId()));
190 }
191 }
192 }
193
194 @Test
195 public void testFetchWithBlobLimitFilter() throws Exception {
196 InMemoryRepository server2 = newRepo("server2");
197 try (TestRepository<InMemoryRepository> remote2 = new TestRepository<>(
198 server2)) {
199 RevBlob longBlob = remote2.blob("foobar");
200 RevBlob shortBlob = remote2.blob("fooba");
201 RevTree tree = remote2.tree(remote2.file("1", longBlob),
202 remote2.file("2", shortBlob));
203 RevCommit commit = remote2.commit(tree);
204 remote2.update("master", commit);
205
206 server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
207 true);
208
209 testProtocol = new TestProtocol<>((Object req, Repository db) -> {
210 UploadPack up = new UploadPack(db);
211 return up;
212 }, null);
213 uri = testProtocol.register(ctx, server2);
214
215 try (Transport tn = testProtocol.open(uri, client, "server2")) {
216 tn.setFilterSpec(FilterSpec.withBlobLimit(5));
217 tn.fetch(NullProgressMonitor.INSTANCE,
218 Collections.singletonList(new RefSpec(commit.name())));
219 assertFalse(
220 client.getObjectDatabase().has(longBlob.toObjectId()));
221 assertTrue(
222 client.getObjectDatabase().has(shortBlob.toObjectId()));
223 }
224 }
225 }
226
227 @Test
228 public void testFetchExplicitBlobWithFilterAndBitmaps() throws Exception {
229 InMemoryRepository server2 = newRepo("server2");
230 try (TestRepository<InMemoryRepository> remote2 = new TestRepository<>(
231 server2)) {
232 RevBlob blob1 = remote2.blob("foobar");
233 RevBlob blob2 = remote2.blob("fooba");
234 RevTree tree = remote2.tree(remote2.file("1", blob1),
235 remote2.file("2", blob2));
236 RevCommit commit = remote2.commit(tree);
237 remote2.update("master", commit);
238 remote2.update("a_blob", blob1);
239
240 server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
241 true);
242
243
244 new DfsGarbageCollector(server2).pack(null);
245 server2.scanForRepoChanges();
246
247 testProtocol = new TestProtocol<>((Object req, Repository db) -> {
248 UploadPack up = new UploadPack(db);
249 return up;
250 }, null);
251 uri = testProtocol.register(ctx, server2);
252
253 try (Transport tn = testProtocol.open(uri, client, "server2")) {
254 tn.setFilterSpec(FilterSpec.withBlobLimit(0));
255 tn.fetch(NullProgressMonitor.INSTANCE, Arrays.asList(
256 new RefSpec(commit.name()), new RefSpec(blob1.name())));
257 assertTrue(client.getObjectDatabase().has(blob1.toObjectId()));
258 assertFalse(client.getObjectDatabase().has(blob2.toObjectId()));
259 }
260 }
261 }
262
263 @Test
264 public void testFetchWithBlobLimitFilterAndBitmaps() throws Exception {
265 InMemoryRepository server2 = newRepo("server2");
266 try (TestRepository<InMemoryRepository> remote2 = new TestRepository<>(
267 server2)) {
268 RevBlob longBlob = remote2.blob("foobar");
269 RevBlob shortBlob = remote2.blob("fooba");
270 RevTree tree = remote2.tree(remote2.file("1", longBlob),
271 remote2.file("2", shortBlob));
272 RevCommit commit = remote2.commit(tree);
273 remote2.update("master", commit);
274
275 server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
276 true);
277
278
279 new DfsGarbageCollector(server2).pack(null);
280 server2.scanForRepoChanges();
281
282 testProtocol = new TestProtocol<>((Object req, Repository db) -> {
283 UploadPack up = new UploadPack(db);
284 return up;
285 }, null);
286 uri = testProtocol.register(ctx, server2);
287
288 try (Transport tn = testProtocol.open(uri, client, "server2")) {
289 tn.setFilterSpec(FilterSpec.withBlobLimit(5));
290 tn.fetch(NullProgressMonitor.INSTANCE,
291 Collections.singletonList(new RefSpec(commit.name())));
292 assertFalse(
293 client.getObjectDatabase().has(longBlob.toObjectId()));
294 assertTrue(
295 client.getObjectDatabase().has(shortBlob.toObjectId()));
296 }
297 }
298 }
299
300 @Test
301 public void testFetchWithNonSupportingServer() throws Exception {
302 InMemoryRepository server2 = newRepo("server2");
303 try (TestRepository<InMemoryRepository> remote2 = new TestRepository<>(
304 server2)) {
305 RevBlob blob = remote2.blob("foo");
306 RevTree tree = remote2.tree(remote2.file("1", blob));
307 RevCommit commit = remote2.commit(tree);
308 remote2.update("master", commit);
309
310 server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
311 false);
312
313 testProtocol = new TestProtocol<>((Object req, Repository db) -> {
314 UploadPack up = new UploadPack(db);
315 return up;
316 }, null);
317 uri = testProtocol.register(ctx, server2);
318
319 try (Transport tn = testProtocol.open(uri, client, "server2")) {
320 tn.setFilterSpec(FilterSpec.withBlobLimit(0));
321
322 TransportException e = assertThrows(TransportException.class,
323 () -> tn.fetch(NullProgressMonitor.INSTANCE, Collections
324 .singletonList(new RefSpec(commit.name()))));
325 assertThat(e.getMessage(), containsString(
326 "filter requires server to advertise that capability"));
327 }
328 }
329 }
330
331
332
333
334
335 private ByteArrayInputStream uploadPackV2Setup(
336 Consumer<UploadPack> postConstructionSetup, String... inputLines)
337 throws Exception {
338
339 ByteArrayInputStream send = linesAsInputStream(inputLines);
340
341 server.getConfig().setString("protocol", null, "version", "2");
342 UploadPack up = new UploadPack(server);
343 if (postConstructionSetup != null) {
344 postConstructionSetup.accept(up);
345 }
346 up.setExtraParameters(Sets.of("version=2"));
347
348 ByteArrayOutputStream recv = new ByteArrayOutputStream();
349 up.upload(send, recv, null);
350 stats = up.getStatistics();
351
352 return new ByteArrayInputStream(recv.toByteArray());
353 }
354
355 private static ByteArrayInputStream linesAsInputStream(String... inputLines)
356 throws IOException {
357 try (ByteArrayOutputStream send = new ByteArrayOutputStream()) {
358 PacketLineOut pckOut = new PacketLineOut(send);
359 for (String line : inputLines) {
360 Objects.requireNonNull(line);
361 if (PacketLineIn.isEnd(line)) {
362 pckOut.end();
363 } else if (PacketLineIn.isDelimiter(line)) {
364 pckOut.writeDelim();
365 } else {
366 pckOut.writeString(line);
367 }
368 }
369 return new ByteArrayInputStream(send.toByteArray());
370 }
371 }
372
373
374
375
376
377
378 private ByteArrayInputStream uploadPackV2(
379 Consumer<UploadPack> postConstructionSetup,
380 String... inputLines)
381 throws Exception {
382 ByteArrayInputStream recvStream =
383 uploadPackV2Setup(postConstructionSetup, inputLines);
384 PacketLineIn pckIn = new PacketLineIn(recvStream);
385
386
387 while (!PacketLineIn.isEnd(pckIn.readString())) {
388
389 }
390 return recvStream;
391 }
392
393 private ByteArrayInputStream uploadPackV2(String... inputLines) throws Exception {
394 return uploadPackV2(null, inputLines);
395 }
396
397 private static class TestV2Hook implements ProtocolV2Hook {
398 private CapabilitiesV2Request capabilitiesRequest;
399
400 private LsRefsV2Request lsRefsRequest;
401
402 private FetchV2Request fetchRequest;
403
404 @Override
405 public void onCapabilities(CapabilitiesV2Request req) {
406 capabilitiesRequest = req;
407 }
408
409 @Override
410 public void onLsRefs(LsRefsV2Request req) {
411 lsRefsRequest = req;
412 }
413
414 @Override
415 public void onFetch(FetchV2Request req) {
416 fetchRequest = req;
417 }
418 }
419
420 @Test
421 public void testV2Capabilities() throws Exception {
422 TestV2Hook hook = new TestV2Hook();
423 ByteArrayInputStream recvStream = uploadPackV2Setup(
424 (UploadPack up) -> {up.setProtocolV2Hook(hook);},
425 PacketLineIn.end());
426 PacketLineIn pckIn = new PacketLineIn(recvStream);
427 assertThat(hook.capabilitiesRequest, notNullValue());
428 assertThat(pckIn.readString(), is("version 2"));
429 assertThat(
430 Arrays.asList(pckIn.readString(), pckIn.readString(),
431 pckIn.readString()),
432
433
434
435
436
437
438 hasItems("ls-refs", "fetch=shallow", "server-option"));
439 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
440 }
441
442 private void checkAdvertisedIfAllowed(String configSection, String configName,
443 String fetchCapability) throws Exception {
444 server.getConfig().setBoolean(configSection, null, configName, true);
445 ByteArrayInputStream recvStream =
446 uploadPackV2Setup(null, PacketLineIn.end());
447 PacketLineIn pckIn = new PacketLineIn(recvStream);
448
449 assertThat(pckIn.readString(), is("version 2"));
450
451 ArrayList<String> lines = new ArrayList<>();
452 String line;
453 while (!PacketLineIn.isEnd((line = pckIn.readString()))) {
454 if (line.startsWith("fetch=")) {
455 assertThat(
456 Arrays.asList(line.substring(6).split(" ")),
457 containsInAnyOrder(fetchCapability, "shallow"));
458 lines.add("fetch");
459 } else {
460 lines.add(line);
461 }
462 }
463 assertThat(lines, containsInAnyOrder("ls-refs", "fetch", "server-option"));
464 }
465
466 private void checkUnadvertisedIfUnallowed(String configSection,
467 String configName, String fetchCapability) throws Exception {
468 server.getConfig().setBoolean(configSection, null, configName, false);
469 ByteArrayInputStream recvStream =
470 uploadPackV2Setup(null, PacketLineIn.end());
471 PacketLineIn pckIn = new PacketLineIn(recvStream);
472
473 assertThat(pckIn.readString(), is("version 2"));
474
475 ArrayList<String> lines = new ArrayList<>();
476 String line;
477 while (!PacketLineIn.isEnd((line = pckIn.readString()))) {
478 if (line.startsWith("fetch=")) {
479 List<String> fetchItems = Arrays.asList(line.substring(6).split(" "));
480 assertThat(fetchItems, hasItems("shallow"));
481 assertFalse(fetchItems.contains(fetchCapability));
482 lines.add("fetch");
483 } else {
484 lines.add(line);
485 }
486 }
487 assertThat(lines, hasItems("ls-refs", "fetch", "server-option"));
488 }
489
490 @Test
491 public void testV2CapabilitiesAllowFilter() throws Exception {
492 checkAdvertisedIfAllowed("uploadpack", "allowfilter", "filter");
493 checkUnadvertisedIfUnallowed("uploadpack", "allowfilter", "filter");
494 }
495
496 @Test
497 public void testV2CapabilitiesRefInWant() throws Exception {
498 checkAdvertisedIfAllowed("uploadpack", "allowrefinwant", "ref-in-want");
499 }
500
501 @Test
502 public void testV2CapabilitiesRefInWantNotAdvertisedIfUnallowed() throws Exception {
503 checkUnadvertisedIfUnallowed("uploadpack", "allowrefinwant",
504 "ref-in-want");
505 }
506
507 @Test
508 public void testV2CapabilitiesAdvertiseSidebandAll() throws Exception {
509 server.getConfig().setBoolean("uploadpack", null, "allowsidebandall",
510 true);
511 checkAdvertisedIfAllowed("uploadpack", "advertisesidebandall",
512 "sideband-all");
513 checkUnadvertisedIfUnallowed("uploadpack", "advertisesidebandall",
514 "sideband-all");
515 }
516
517 @Test
518 public void testV2CapabilitiesRefInWantNotAdvertisedIfAdvertisingForbidden() throws Exception {
519 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
520 server.getConfig().setBoolean("uploadpack", null, "advertiserefinwant", false);
521 ByteArrayInputStream recvStream =
522 uploadPackV2Setup(null, PacketLineIn.end());
523 PacketLineIn pckIn = new PacketLineIn(recvStream);
524
525 assertThat(pckIn.readString(), is("version 2"));
526 assertThat(
527 Arrays.asList(pckIn.readString(), pckIn.readString(),
528 pckIn.readString()),
529 hasItems("ls-refs", "fetch=shallow", "server-option"));
530 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
531 }
532
533 @Test
534 public void testV2EmptyRequest() throws Exception {
535 ByteArrayInputStream recvStream = uploadPackV2(PacketLineIn.end());
536
537
538 assertEquals(0, recvStream.available());
539 }
540
541 @Test
542 public void testV2LsRefs() throws Exception {
543 RevCommit tip = remote.commit().message("message").create();
544 remote.update("master", tip);
545 server.updateRef("HEAD").link("refs/heads/master");
546 RevTag tag = remote.tag("tag", tip);
547 remote.update("refs/tags/tag", tag);
548
549 TestV2Hook hook = new TestV2Hook();
550 ByteArrayInputStream recvStream = uploadPackV2(
551 (UploadPack up) -> {up.setProtocolV2Hook(hook);},
552 "command=ls-refs\n", PacketLineIn.end());
553 PacketLineIn pckIn = new PacketLineIn(recvStream);
554
555 assertThat(hook.lsRefsRequest, notNullValue());
556 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD"));
557 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
558 assertThat(pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag"));
559 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
560 }
561
562 @Test
563 public void testV2LsRefsSymrefs() throws Exception {
564 RevCommit tip = remote.commit().message("message").create();
565 remote.update("master", tip);
566 server.updateRef("HEAD").link("refs/heads/master");
567 RevTag tag = remote.tag("tag", tip);
568 remote.update("refs/tags/tag", tag);
569
570 ByteArrayInputStream recvStream = uploadPackV2("command=ls-refs\n",
571 PacketLineIn.delimiter(), "symrefs", PacketLineIn.end());
572 PacketLineIn pckIn = new PacketLineIn(recvStream);
573
574 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD symref-target:refs/heads/master"));
575 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
576 assertThat(pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag"));
577 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
578 }
579
580 @Test
581 public void testV2LsRefsPeel() throws Exception {
582 RevCommit tip = remote.commit().message("message").create();
583 remote.update("master", tip);
584 server.updateRef("HEAD").link("refs/heads/master");
585 RevTag tag = remote.tag("tag", tip);
586 remote.update("refs/tags/tag", tag);
587
588 ByteArrayInputStream recvStream = uploadPackV2("command=ls-refs\n",
589 PacketLineIn.delimiter(), "peel", PacketLineIn.end());
590 PacketLineIn pckIn = new PacketLineIn(recvStream);
591
592 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD"));
593 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
594 assertThat(
595 pckIn.readString(),
596 is(tag.toObjectId().getName() + " refs/tags/tag peeled:"
597 + tip.toObjectId().getName()));
598 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
599 }
600
601 @Test
602 public void testV2LsRefsMultipleCommands() throws Exception {
603 RevCommit tip = remote.commit().message("message").create();
604 remote.update("master", tip);
605 server.updateRef("HEAD").link("refs/heads/master");
606 RevTag tag = remote.tag("tag", tip);
607 remote.update("refs/tags/tag", tag);
608
609 ByteArrayInputStream recvStream = uploadPackV2(
610 "command=ls-refs\n", PacketLineIn.delimiter(), "symrefs",
611 "peel", PacketLineIn.end(), "command=ls-refs\n",
612 PacketLineIn.delimiter(), PacketLineIn.end());
613 PacketLineIn pckIn = new PacketLineIn(recvStream);
614
615 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD symref-target:refs/heads/master"));
616 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
617 assertThat(
618 pckIn.readString(),
619 is(tag.toObjectId().getName() + " refs/tags/tag peeled:"
620 + tip.toObjectId().getName()));
621 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
622 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD"));
623 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
624 assertThat(pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag"));
625 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
626 }
627
628 @Test
629 public void testV2LsRefsRefPrefix() throws Exception {
630 RevCommit tip = remote.commit().message("message").create();
631 remote.update("master", tip);
632 remote.update("other", tip);
633 remote.update("yetAnother", tip);
634
635 ByteArrayInputStream recvStream = uploadPackV2(
636 "command=ls-refs\n",
637 PacketLineIn.delimiter(),
638 "ref-prefix refs/heads/maste",
639 "ref-prefix refs/heads/other",
640 PacketLineIn.end());
641 PacketLineIn pckIn = new PacketLineIn(recvStream);
642
643 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
644 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/other"));
645 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
646 }
647
648 @Test
649 public void testV2LsRefsRefPrefixNoSlash() throws Exception {
650 RevCommit tip = remote.commit().message("message").create();
651 remote.update("master", tip);
652 remote.update("other", tip);
653
654 ByteArrayInputStream recvStream = uploadPackV2(
655 "command=ls-refs\n",
656 PacketLineIn.delimiter(),
657 "ref-prefix refs/heads/maste",
658 "ref-prefix r",
659 PacketLineIn.end());
660 PacketLineIn pckIn = new PacketLineIn(recvStream);
661
662 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
663 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/other"));
664 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
665 }
666
667 @Test
668 public void testV2LsRefsUnrecognizedArgument() throws Exception {
669 UploadPackInternalServerErrorException e = assertThrows(
670 UploadPackInternalServerErrorException.class,
671 () -> uploadPackV2("command=ls-refs\n",
672 PacketLineIn.delimiter(), "invalid-argument\n",
673 PacketLineIn.end()));
674 assertThat(e.getCause().getMessage(),
675 containsString("unexpected invalid-argument"));
676 }
677
678 @Test
679 public void testV2LsRefsServerOptions() throws Exception {
680 String[] lines = { "command=ls-refs\n",
681 "server-option=one\n", "server-option=two\n",
682 PacketLineIn.delimiter(),
683 PacketLineIn.end() };
684
685 TestV2Hook testHook = new TestV2Hook();
686 uploadPackV2Setup((UploadPack up) -> {up.setProtocolV2Hook(testHook);}, lines);
687
688 LsRefsV2Request req = testHook.lsRefsRequest;
689 assertEquals(2, req.getServerOptions().size());
690 assertThat(req.getServerOptions(), hasItems("one", "two"));
691 }
692
693
694
695
696
697 private ReceivedPackStatistics parsePack(ByteArrayInputStream recvStream) throws Exception {
698 return parsePack(recvStream, NullProgressMonitor.INSTANCE);
699 }
700
701 private ReceivedPackStatistics parsePack(ByteArrayInputStream recvStream, ProgressMonitor pm)
702 throws Exception {
703 SideBandInputStream sb = new SideBandInputStream(
704 recvStream, pm,
705 new StringWriter(), NullOutputStream.INSTANCE);
706 PackParser pp = client.newObjectInserter().newPackParser(sb);
707 pp.parse(NullProgressMonitor.INSTANCE);
708
709
710 assertEquals(-1, recvStream.read());
711
712 return pp.getReceivedPackStatistics();
713 }
714
715 @Test
716 public void testV2FetchRequestPolicyAdvertised() throws Exception {
717 RevCommit advertized = remote.commit().message("x").create();
718 RevCommit unadvertized = remote.commit().message("y").create();
719 remote.update("branch1", advertized);
720
721
722 uploadPackV2(
723 (UploadPack up) -> {up.setRequestPolicy(RequestPolicy.ADVERTISED);},
724 "command=fetch\n",
725 PacketLineIn.delimiter(),
726 "want " + advertized.name() + "\n",
727 PacketLineIn.end());
728
729
730 UploadPackInternalServerErrorException e = assertThrows(
731 UploadPackInternalServerErrorException.class,
732 () -> uploadPackV2(
733 (UploadPack up) -> {up.setRequestPolicy(RequestPolicy.ADVERTISED);},
734 "command=fetch\n", PacketLineIn.delimiter(),
735 "want " + unadvertized.name() + "\n",
736 PacketLineIn.end()));
737 assertThat(e.getCause().getMessage(),
738 containsString("want " + unadvertized.name() + " not valid"));
739 }
740
741 @Test
742 public void testV2FetchRequestPolicyReachableCommit() throws Exception {
743 RevCommit reachable = remote.commit().message("x").create();
744 RevCommit advertized = remote.commit().message("x").parent(reachable)
745 .create();
746 RevCommit unreachable = remote.commit().message("y").create();
747 remote.update("branch1", advertized);
748
749
750 uploadPackV2(
751 (UploadPack up) -> {up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);},
752 "command=fetch\n",
753 PacketLineIn.delimiter(),
754 "want " + reachable.name() + "\n",
755 PacketLineIn.end());
756
757
758 UploadPackInternalServerErrorException e = assertThrows(
759 UploadPackInternalServerErrorException.class,
760 () -> uploadPackV2(
761 (UploadPack up) -> {up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);},
762 "command=fetch\n", PacketLineIn.delimiter(),
763 "want " + unreachable.name() + "\n",
764 PacketLineIn.end()));
765 assertThat(e.getCause().getMessage(),
766 containsString("want " + unreachable.name() + " not valid"));
767 }
768
769 @Test
770 public void testV2FetchRequestPolicyTip() throws Exception {
771 RevCommit parentOfTip = remote.commit().message("x").create();
772 RevCommit tip = remote.commit().message("y").parent(parentOfTip)
773 .create();
774 remote.update("secret", tip);
775
776
777 uploadPackV2(
778 (UploadPack up) -> {
779 up.setRequestPolicy(RequestPolicy.TIP);
780 up.setRefFilter(new RejectAllRefFilter());
781 },
782 "command=fetch\n",
783 PacketLineIn.delimiter(),
784 "want " + tip.name() + "\n",
785 PacketLineIn.end());
786
787
788 UploadPackInternalServerErrorException e = assertThrows(
789 UploadPackInternalServerErrorException.class,
790 () -> uploadPackV2(
791 (UploadPack up) -> {
792 up.setRequestPolicy(RequestPolicy.TIP);
793 up.setRefFilter(new RejectAllRefFilter());
794 },
795 "command=fetch\n", PacketLineIn.delimiter(),
796 "want " + parentOfTip.name() + "\n",
797 PacketLineIn.end()));
798 assertThat(e.getCause().getMessage(),
799 containsString("want " + parentOfTip.name() + " not valid"));
800 }
801
802 @Test
803 public void testV2FetchRequestPolicyReachableCommitTip() throws Exception {
804 RevCommit parentOfTip = remote.commit().message("x").create();
805 RevCommit tip = remote.commit().message("y").parent(parentOfTip)
806 .create();
807 RevCommit unreachable = remote.commit().message("y").create();
808 remote.update("secret", tip);
809
810
811 uploadPackV2(
812 (UploadPack up) -> {
813 up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT_TIP);
814 up.setRefFilter(new RejectAllRefFilter());
815 },
816 "command=fetch\n",
817 PacketLineIn.delimiter(), "want " + parentOfTip.name() + "\n",
818 PacketLineIn.end());
819
820
821 UploadPackInternalServerErrorException e = assertThrows(
822 UploadPackInternalServerErrorException.class,
823 () -> uploadPackV2(
824 (UploadPack up) -> {
825 up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT_TIP);
826 up.setRefFilter(new RejectAllRefFilter());
827 },
828 "command=fetch\n",
829 PacketLineIn.delimiter(),
830 "want " + unreachable.name() + "\n",
831 PacketLineIn.end()));
832 assertThat(e.getCause().getMessage(),
833 containsString("want " + unreachable.name() + " not valid"));
834 }
835
836 @Test
837 public void testV2FetchRequestPolicyAny() throws Exception {
838 RevCommit unreachable = remote.commit().message("y").create();
839
840
841 uploadPackV2(
842 (UploadPack up) -> {up.setRequestPolicy(RequestPolicy.ANY);},
843 "command=fetch\n",
844 PacketLineIn.delimiter(),
845 "want " + unreachable.name() + "\n",
846 PacketLineIn.end());
847 }
848
849 @Test
850 public void testV2FetchServerDoesNotStopNegotiation() throws Exception {
851 RevCommit fooParent = remote.commit().message("x").create();
852 RevCommit fooChild = remote.commit().message("x").parent(fooParent).create();
853 RevCommit barParent = remote.commit().message("y").create();
854 RevCommit barChild = remote.commit().message("y").parent(barParent).create();
855 remote.update("branch1", fooChild);
856 remote.update("branch2", barChild);
857
858 ByteArrayInputStream recvStream = uploadPackV2(
859 "command=fetch\n",
860 PacketLineIn.delimiter(),
861 "want " + fooChild.toObjectId().getName() + "\n",
862 "want " + barChild.toObjectId().getName() + "\n",
863 "have " + fooParent.toObjectId().getName() + "\n",
864 PacketLineIn.end());
865 PacketLineIn pckIn = new PacketLineIn(recvStream);
866
867 assertThat(pckIn.readString(), is("acknowledgments"));
868 assertThat(pckIn.readString(), is("ACK " + fooParent.toObjectId().getName()));
869 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
870 }
871
872 @Test
873 public void testV2FetchServerStopsNegotiation() throws Exception {
874 RevCommit fooParent = remote.commit().message("x").create();
875 RevCommit fooChild = remote.commit().message("x").parent(fooParent).create();
876 RevCommit barParent = remote.commit().message("y").create();
877 RevCommit barChild = remote.commit().message("y").parent(barParent).create();
878 remote.update("branch1", fooChild);
879 remote.update("branch2", barChild);
880
881 ByteArrayInputStream recvStream = uploadPackV2(
882 "command=fetch\n",
883 PacketLineIn.delimiter(),
884 "want " + fooChild.toObjectId().getName() + "\n",
885 "want " + barChild.toObjectId().getName() + "\n",
886 "have " + fooParent.toObjectId().getName() + "\n",
887 "have " + barParent.toObjectId().getName() + "\n",
888 PacketLineIn.end());
889 PacketLineIn pckIn = new PacketLineIn(recvStream);
890
891 assertThat(pckIn.readString(), is("acknowledgments"));
892 assertThat(
893 Arrays.asList(pckIn.readString(), pckIn.readString()),
894 hasItems(
895 "ACK " + fooParent.toObjectId().getName(),
896 "ACK " + barParent.toObjectId().getName()));
897 assertThat(pckIn.readString(), is("ready"));
898 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
899 assertThat(pckIn.readString(), is("packfile"));
900 parsePack(recvStream);
901 assertFalse(client.getObjectDatabase().has(fooParent.toObjectId()));
902 assertTrue(client.getObjectDatabase().has(fooChild.toObjectId()));
903 assertFalse(client.getObjectDatabase().has(barParent.toObjectId()));
904 assertTrue(client.getObjectDatabase().has(barChild.toObjectId()));
905 }
906
907 @Test
908 public void testV2FetchClientStopsNegotiation() throws Exception {
909 RevCommit fooParent = remote.commit().message("x").create();
910 RevCommit fooChild = remote.commit().message("x").parent(fooParent).create();
911 RevCommit barParent = remote.commit().message("y").create();
912 RevCommit barChild = remote.commit().message("y").parent(barParent).create();
913 remote.update("branch1", fooChild);
914 remote.update("branch2", barChild);
915
916 ByteArrayInputStream recvStream = uploadPackV2(
917 "command=fetch\n",
918 PacketLineIn.delimiter(),
919 "want " + fooChild.toObjectId().getName() + "\n",
920 "want " + barChild.toObjectId().getName() + "\n",
921 "have " + fooParent.toObjectId().getName() + "\n",
922 "done\n",
923 PacketLineIn.end());
924 PacketLineIn pckIn = new PacketLineIn(recvStream);
925
926 assertThat(pckIn.readString(), is("packfile"));
927 parsePack(recvStream);
928 assertFalse(client.getObjectDatabase().has(fooParent.toObjectId()));
929 assertTrue(client.getObjectDatabase().has(fooChild.toObjectId()));
930 assertTrue(client.getObjectDatabase().has(barParent.toObjectId()));
931 assertTrue(client.getObjectDatabase().has(barChild.toObjectId()));
932 }
933
934 @Test
935 public void testV2FetchThinPack() throws Exception {
936 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
937
938 RevBlob parentBlob = remote.blob(commonInBlob + "a");
939 RevCommit parent = remote
940 .commit(remote.tree(remote.file("foo", parentBlob)));
941 RevBlob childBlob = remote.blob(commonInBlob + "b");
942 RevCommit child = remote
943 .commit(remote.tree(remote.file("foo", childBlob)), parent);
944 remote.update("branch1", child);
945
946
947 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
948 PacketLineIn.delimiter(),
949 "want " + child.toObjectId().getName() + "\n",
950 "have " + parent.toObjectId().getName() + "\n", "thin-pack\n",
951 "done\n", PacketLineIn.end());
952 PacketLineIn pckIn = new PacketLineIn(recvStream);
953
954 assertThat(pckIn.readString(), is("packfile"));
955
956
957
958 IOException e = assertThrows(IOException.class,
959 () -> parsePack(recvStream));
960 assertThat(e.getMessage(),
961 containsString("pack has unresolved deltas"));
962 }
963
964 @Test
965 public void testV2FetchNoProgress() throws Exception {
966 RevCommit commit = remote.commit().message("x").create();
967 remote.update("branch1", commit);
968
969
970 StringWriter sw = new StringWriter();
971 ByteArrayInputStream recvStream = uploadPackV2(
972 "command=fetch\n",
973 PacketLineIn.delimiter(),
974 "want " + commit.toObjectId().getName() + "\n",
975 "done\n",
976 PacketLineIn.end());
977 PacketLineIn pckIn = new PacketLineIn(recvStream);
978 assertThat(pckIn.readString(), is("packfile"));
979 parsePack(recvStream, new TextProgressMonitor(sw));
980 assertFalse(sw.toString().isEmpty());
981
982
983 sw = new StringWriter();
984 recvStream = uploadPackV2(
985 "command=fetch\n",
986 PacketLineIn.delimiter(),
987 "want " + commit.toObjectId().getName() + "\n",
988 "no-progress\n",
989 "done\n",
990 PacketLineIn.end());
991 pckIn = new PacketLineIn(recvStream);
992 assertThat(pckIn.readString(), is("packfile"));
993 parsePack(recvStream, new TextProgressMonitor(sw));
994 assertTrue(sw.toString().isEmpty());
995 }
996
997 @Test
998 public void testV2FetchIncludeTag() throws Exception {
999 RevCommit commit = remote.commit().message("x").create();
1000 RevTag tag = remote.tag("tag", commit);
1001 remote.update("branch1", commit);
1002 remote.update("refs/tags/tag", tag);
1003
1004
1005 ByteArrayInputStream recvStream = uploadPackV2(
1006 "command=fetch\n",
1007 PacketLineIn.delimiter(),
1008 "want " + commit.toObjectId().getName() + "\n",
1009 "done\n",
1010 PacketLineIn.end());
1011 PacketLineIn pckIn = new PacketLineIn(recvStream);
1012 assertThat(pckIn.readString(), is("packfile"));
1013 parsePack(recvStream);
1014 assertFalse(client.getObjectDatabase().has(tag.toObjectId()));
1015
1016
1017 recvStream = uploadPackV2(
1018 "command=fetch\n",
1019 PacketLineIn.delimiter(),
1020 "want " + commit.toObjectId().getName() + "\n",
1021 "include-tag\n",
1022 "done\n",
1023 PacketLineIn.end());
1024 pckIn = new PacketLineIn(recvStream);
1025 assertThat(pckIn.readString(), is("packfile"));
1026 parsePack(recvStream);
1027 assertTrue(client.getObjectDatabase().has(tag.toObjectId()));
1028 }
1029
1030 @Test
1031 public void testV2FetchOfsDelta() throws Exception {
1032 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
1033
1034 RevBlob parentBlob = remote.blob(commonInBlob + "a");
1035 RevCommit parent = remote.commit(remote.tree(remote.file("foo", parentBlob)));
1036 RevBlob childBlob = remote.blob(commonInBlob + "b");
1037 RevCommit child = remote.commit(remote.tree(remote.file("foo", childBlob)), parent);
1038 remote.update("branch1", child);
1039
1040
1041 ByteArrayInputStream recvStream = uploadPackV2(
1042 "command=fetch\n",
1043 PacketLineIn.delimiter(),
1044 "want " + child.toObjectId().getName() + "\n",
1045 "done\n",
1046 PacketLineIn.end());
1047 PacketLineIn pckIn = new PacketLineIn(recvStream);
1048 assertThat(pckIn.readString(), is("packfile"));
1049 ReceivedPackStatistics receivedStats = parsePack(recvStream);
1050 assertTrue(receivedStats.getNumOfsDelta() == 0);
1051
1052
1053 recvStream = uploadPackV2(
1054 "command=fetch\n",
1055 PacketLineIn.delimiter(),
1056 "want " + child.toObjectId().getName() + "\n",
1057 "ofs-delta\n",
1058 "done\n",
1059 PacketLineIn.end());
1060 pckIn = new PacketLineIn(recvStream);
1061 assertThat(pckIn.readString(), is("packfile"));
1062 receivedStats = parsePack(recvStream);
1063 assertTrue(receivedStats.getNumOfsDelta() != 0);
1064 }
1065
1066 @Test
1067 public void testV2FetchShallow() throws Exception {
1068 RevCommit commonParent = remote.commit().message("parent").create();
1069 RevCommit fooChild = remote.commit().message("x").parent(commonParent).create();
1070 RevCommit barChild = remote.commit().message("y").parent(commonParent).create();
1071 remote.update("branch1", barChild);
1072
1073
1074
1075 ByteArrayInputStream recvStream = uploadPackV2(
1076 "command=fetch\n",
1077 PacketLineIn.delimiter(),
1078 "want " + barChild.toObjectId().getName() + "\n",
1079 "have " + fooChild.toObjectId().getName() + "\n",
1080 "done\n",
1081 PacketLineIn.end());
1082 PacketLineIn pckIn = new PacketLineIn(recvStream);
1083 assertThat(pckIn.readString(), is("packfile"));
1084 parsePack(recvStream);
1085 assertTrue(client.getObjectDatabase().has(barChild.toObjectId()));
1086 assertFalse(client.getObjectDatabase().has(commonParent.toObjectId()));
1087
1088
1089
1090 recvStream = uploadPackV2(
1091 "command=fetch\n",
1092 PacketLineIn.delimiter(),
1093 "want " + barChild.toObjectId().getName() + "\n",
1094 "have " + fooChild.toObjectId().getName() + "\n",
1095 "shallow " + fooChild.toObjectId().getName() + "\n",
1096 "done\n",
1097 PacketLineIn.end());
1098 pckIn = new PacketLineIn(recvStream);
1099 assertThat(pckIn.readString(), is("packfile"));
1100 parsePack(recvStream);
1101 assertTrue(client.getObjectDatabase().has(commonParent.toObjectId()));
1102 }
1103
1104 @Test
1105 public void testV2FetchDeepenAndDone() throws Exception {
1106 RevCommit parent = remote.commit().message("parent").create();
1107 RevCommit child = remote.commit().message("x").parent(parent).create();
1108 remote.update("branch1", child);
1109
1110
1111 ByteArrayInputStream recvStream = uploadPackV2(
1112 "command=fetch\n",
1113 PacketLineIn.delimiter(),
1114 "want " + child.toObjectId().getName() + "\n",
1115 "deepen 1\n",
1116 "done\n",
1117 PacketLineIn.end());
1118 PacketLineIn pckIn = new PacketLineIn(recvStream);
1119 assertThat(pckIn.readString(), is("shallow-info"));
1120 assertThat(pckIn.readString(), is("shallow " + child.toObjectId().getName()));
1121 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1122 assertThat(pckIn.readString(), is("packfile"));
1123 parsePack(recvStream);
1124 assertTrue(client.getObjectDatabase().has(child.toObjectId()));
1125 assertFalse(client.getObjectDatabase().has(parent.toObjectId()));
1126
1127
1128 recvStream = uploadPackV2(
1129 "command=fetch\n",
1130 PacketLineIn.delimiter(),
1131 "want " + child.toObjectId().getName() + "\n",
1132 "done\n",
1133 PacketLineIn.end());
1134 pckIn = new PacketLineIn(recvStream);
1135 assertThat(pckIn.readString(), is("packfile"));
1136 parsePack(recvStream);
1137 assertTrue(client.getObjectDatabase().has(parent.toObjectId()));
1138 }
1139
1140 @Test
1141 public void testV2FetchDeepenWithoutDone() throws Exception {
1142 RevCommit parent = remote.commit().message("parent").create();
1143 RevCommit child = remote.commit().message("x").parent(parent).create();
1144 remote.update("branch1", child);
1145
1146 ByteArrayInputStream recvStream = uploadPackV2(
1147 "command=fetch\n",
1148 PacketLineIn.delimiter(),
1149 "want " + child.toObjectId().getName() + "\n",
1150 "deepen 1\n",
1151 PacketLineIn.end());
1152 PacketLineIn pckIn = new PacketLineIn(recvStream);
1153
1154
1155
1156
1157 assertThat(pckIn.readString(), is("acknowledgments"));
1158 assertThat(pckIn.readString(), is("NAK"));
1159 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
1160 }
1161
1162 @Test
1163 public void testV2FetchShallowSince() throws Exception {
1164 PersonIdent person = new PersonIdent(remote.getRepository());
1165
1166 RevCommit beyondBoundary = remote.commit()
1167 .committer(new PersonIdent(person, 1510000000, 0)).create();
1168 RevCommit boundary = remote.commit().parent(beyondBoundary)
1169 .committer(new PersonIdent(person, 1520000000, 0)).create();
1170 RevCommit tooOld = remote.commit()
1171 .committer(new PersonIdent(person, 1500000000, 0)).create();
1172 RevCommit merge = remote.commit().parent(boundary).parent(tooOld)
1173 .committer(new PersonIdent(person, 1530000000, 0)).create();
1174
1175 remote.update("branch1", merge);
1176
1177
1178 ByteArrayInputStream recvStream = uploadPackV2(
1179 "command=fetch\n",
1180 PacketLineIn.delimiter(),
1181 "shallow " + boundary.toObjectId().getName() + "\n",
1182 "deepen-since 1510000\n",
1183 "want " + merge.toObjectId().getName() + "\n",
1184 "have " + boundary.toObjectId().getName() + "\n",
1185 "done\n",
1186 PacketLineIn.end());
1187 PacketLineIn pckIn = new PacketLineIn(recvStream);
1188 assertThat(pckIn.readString(), is("shallow-info"));
1189
1190
1191
1192 assertThat(pckIn.readString(), is("shallow " + merge.toObjectId().getName()));
1193
1194
1195
1196 assertThat(pckIn.readString(), is("unshallow " + boundary.toObjectId().getName()));
1197
1198 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1199 assertThat(pckIn.readString(), is("packfile"));
1200 parsePack(recvStream);
1201
1202
1203
1204 assertFalse(client.getObjectDatabase().has(tooOld.toObjectId()));
1205
1206
1207
1208 assertFalse(client.getObjectDatabase().has(boundary.toObjectId()));
1209
1210
1211 assertTrue(client.getObjectDatabase().has(beyondBoundary.toObjectId()));
1212 assertTrue(client.getObjectDatabase().has(merge.toObjectId()));
1213 }
1214
1215 @Test
1216 public void testV2FetchShallowSince_excludedParentWithMultipleChildren() throws Exception {
1217 PersonIdent person = new PersonIdent(remote.getRepository());
1218
1219 RevCommit base = remote.commit()
1220 .committer(new PersonIdent(person, 1500000000, 0)).create();
1221 RevCommit child1 = remote.commit().parent(base)
1222 .committer(new PersonIdent(person, 1510000000, 0)).create();
1223 RevCommit child2 = remote.commit().parent(base)
1224 .committer(new PersonIdent(person, 1520000000, 0)).create();
1225
1226 remote.update("branch1", child1);
1227 remote.update("branch2", child2);
1228
1229 ByteArrayInputStream recvStream = uploadPackV2(
1230 "command=fetch\n",
1231 PacketLineIn.delimiter(),
1232 "deepen-since 1510000\n",
1233 "want " + child1.toObjectId().getName() + "\n",
1234 "want " + child2.toObjectId().getName() + "\n",
1235 "done\n",
1236 PacketLineIn.end());
1237 PacketLineIn pckIn = new PacketLineIn(recvStream);
1238 assertThat(pckIn.readString(), is("shallow-info"));
1239
1240
1241 assertThat(
1242 Arrays.asList(pckIn.readString(), pckIn.readString()),
1243 hasItems(
1244 "shallow " + child1.toObjectId().getName(),
1245 "shallow " + child2.toObjectId().getName()));
1246
1247 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1248 assertThat(pckIn.readString(), is("packfile"));
1249 parsePack(recvStream);
1250
1251
1252 assertFalse(client.getObjectDatabase().has(base.toObjectId()));
1253 assertTrue(client.getObjectDatabase().has(child1.toObjectId()));
1254 assertTrue(client.getObjectDatabase().has(child2.toObjectId()));
1255 }
1256
1257 @Test
1258 public void testV2FetchShallowSince_noCommitsSelected() throws Exception {
1259 PersonIdent person = new PersonIdent(remote.getRepository());
1260
1261 RevCommit tooOld = remote.commit()
1262 .committer(new PersonIdent(person, 1500000000, 0)).create();
1263
1264 remote.update("branch1", tooOld);
1265
1266 UploadPackInternalServerErrorException e = assertThrows(
1267 UploadPackInternalServerErrorException.class,
1268 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
1269 "deepen-since 1510000\n",
1270 "want " + tooOld.toObjectId().getName() + "\n",
1271 "done\n", PacketLineIn.end()));
1272 assertThat(e.getCause().getMessage(),
1273 containsString("No commits selected for shallow request"));
1274 }
1275
1276 @Test
1277 public void testV2FetchDeepenNot() throws Exception {
1278 RevCommit one = remote.commit().message("one").create();
1279 RevCommit two = remote.commit().message("two").parent(one).create();
1280 RevCommit three = remote.commit().message("three").parent(two).create();
1281 RevCommit side = remote.commit().message("side").parent(one).create();
1282 RevCommit merge = remote.commit().message("merge")
1283 .parent(three).parent(side).create();
1284
1285 remote.update("branch1", merge);
1286 remote.update("side", side);
1287
1288
1289
1290 ByteArrayInputStream recvStream = uploadPackV2(
1291 "command=fetch\n",
1292 PacketLineIn.delimiter(),
1293 "shallow " + three.toObjectId().getName() + "\n",
1294 "deepen-not side\n",
1295 "want " + merge.toObjectId().getName() + "\n",
1296 "have " + three.toObjectId().getName() + "\n",
1297 "done\n",
1298 PacketLineIn.end());
1299 PacketLineIn pckIn = new PacketLineIn(recvStream);
1300 assertThat(pckIn.readString(), is("shallow-info"));
1301
1302
1303
1304 assertThat(
1305 Arrays.asList(pckIn.readString(), pckIn.readString()),
1306 hasItems(
1307 "shallow " + merge.toObjectId().getName(),
1308 "shallow " + two.toObjectId().getName()));
1309
1310
1311 assertThat(pckIn.readString(), is("unshallow " + three.toObjectId().getName()));
1312
1313 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1314 assertThat(pckIn.readString(), is("packfile"));
1315 parsePack(recvStream);
1316
1317
1318
1319 assertFalse(client.getObjectDatabase().has(side.toObjectId()));
1320 assertFalse(client.getObjectDatabase().has(one.toObjectId()));
1321
1322
1323
1324 assertFalse(client.getObjectDatabase().has(three.toObjectId()));
1325
1326
1327 assertTrue(client.getObjectDatabase().has(merge.toObjectId()));
1328 assertTrue(client.getObjectDatabase().has(two.toObjectId()));
1329 }
1330
1331 @Test
1332 public void testV2FetchDeepenNot_excludeDescendantOfWant()
1333 throws Exception {
1334 RevCommit one = remote.commit().message("one").create();
1335 RevCommit two = remote.commit().message("two").parent(one).create();
1336 RevCommit three = remote.commit().message("three").parent(two).create();
1337 RevCommit four = remote.commit().message("four").parent(three).create();
1338
1339 remote.update("two", two);
1340 remote.update("four", four);
1341
1342 UploadPackInternalServerErrorException e = assertThrows(
1343 UploadPackInternalServerErrorException.class,
1344 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
1345 "deepen-not four\n",
1346 "want " + two.toObjectId().getName() + "\n", "done\n",
1347 PacketLineIn.end()));
1348 assertThat(e.getCause().getMessage(),
1349 containsString("No commits selected for shallow request"));
1350 }
1351
1352 @Test
1353 public void testV2FetchDeepenNot_supportAnnotatedTags() throws Exception {
1354 RevCommit one = remote.commit().message("one").create();
1355 RevCommit two = remote.commit().message("two").parent(one).create();
1356 RevCommit three = remote.commit().message("three").parent(two).create();
1357 RevCommit four = remote.commit().message("four").parent(three).create();
1358 RevTag twoTag = remote.tag("twotag", two);
1359
1360 remote.update("refs/tags/twotag", twoTag);
1361 remote.update("four", four);
1362
1363 ByteArrayInputStream recvStream = uploadPackV2(
1364 "command=fetch\n",
1365 PacketLineIn.delimiter(),
1366 "deepen-not twotag\n",
1367 "want " + four.toObjectId().getName() + "\n",
1368 "done\n",
1369 PacketLineIn.end());
1370 PacketLineIn pckIn = new PacketLineIn(recvStream);
1371 assertThat(pckIn.readString(), is("shallow-info"));
1372 assertThat(pckIn.readString(), is("shallow " + three.toObjectId().getName()));
1373 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1374 assertThat(pckIn.readString(), is("packfile"));
1375 parsePack(recvStream);
1376 assertFalse(client.getObjectDatabase().has(one.toObjectId()));
1377 assertFalse(client.getObjectDatabase().has(two.toObjectId()));
1378 assertTrue(client.getObjectDatabase().has(three.toObjectId()));
1379 assertTrue(client.getObjectDatabase().has(four.toObjectId()));
1380 }
1381
1382 @Test
1383 public void testV2FetchDeepenNot_excludedParentWithMultipleChildren() throws Exception {
1384 PersonIdent person = new PersonIdent(remote.getRepository());
1385
1386 RevCommit base = remote.commit()
1387 .committer(new PersonIdent(person, 1500000000, 0)).create();
1388 RevCommit child1 = remote.commit().parent(base)
1389 .committer(new PersonIdent(person, 1510000000, 0)).create();
1390 RevCommit child2 = remote.commit().parent(base)
1391 .committer(new PersonIdent(person, 1520000000, 0)).create();
1392
1393 remote.update("base", base);
1394 remote.update("branch1", child1);
1395 remote.update("branch2", child2);
1396
1397 ByteArrayInputStream recvStream = uploadPackV2(
1398 "command=fetch\n",
1399 PacketLineIn.delimiter(),
1400 "deepen-not base\n",
1401 "want " + child1.toObjectId().getName() + "\n",
1402 "want " + child2.toObjectId().getName() + "\n",
1403 "done\n",
1404 PacketLineIn.end());
1405 PacketLineIn pckIn = new PacketLineIn(recvStream);
1406 assertThat(pckIn.readString(), is("shallow-info"));
1407
1408
1409 assertThat(
1410 Arrays.asList(pckIn.readString(), pckIn.readString()),
1411 hasItems(
1412 "shallow " + child1.toObjectId().getName(),
1413 "shallow " + child2.toObjectId().getName()));
1414
1415 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1416 assertThat(pckIn.readString(), is("packfile"));
1417 parsePack(recvStream);
1418
1419
1420 assertFalse(client.getObjectDatabase().has(base.toObjectId()));
1421 assertTrue(client.getObjectDatabase().has(child1.toObjectId()));
1422 assertTrue(client.getObjectDatabase().has(child2.toObjectId()));
1423 }
1424
1425 @Test
1426 public void testV2FetchUnrecognizedArgument() throws Exception {
1427 UploadPackInternalServerErrorException e = assertThrows(
1428 UploadPackInternalServerErrorException.class,
1429 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
1430 "invalid-argument\n", PacketLineIn.end()));
1431 assertThat(e.getCause().getMessage(),
1432 containsString("unexpected invalid-argument"));
1433 }
1434
1435 @Test
1436 public void testV2FetchServerOptions() throws Exception {
1437 String[] lines = { "command=fetch\n", "server-option=one\n",
1438 "server-option=two\n", PacketLineIn.delimiter(),
1439 PacketLineIn.end() };
1440
1441 TestV2Hook testHook = new TestV2Hook();
1442 uploadPackV2Setup((UploadPack up) -> {up.setProtocolV2Hook(testHook);}, lines);
1443
1444 FetchV2Request req = testHook.fetchRequest;
1445 assertNotNull(req);
1446 assertEquals(2, req.getServerOptions().size());
1447 assertThat(req.getServerOptions(), hasItems("one", "two"));
1448 }
1449
1450 @Test
1451 public void testV2FetchFilter() throws Exception {
1452 RevBlob big = remote.blob("foobar");
1453 RevBlob small = remote.blob("fooba");
1454 RevTree tree = remote.tree(remote.file("1", big),
1455 remote.file("2", small));
1456 RevCommit commit = remote.commit(tree);
1457 remote.update("master", commit);
1458
1459 server.getConfig().setBoolean("uploadpack", null, "allowfilter", true);
1460
1461 ByteArrayInputStream recvStream = uploadPackV2(
1462 "command=fetch\n",
1463 PacketLineIn.delimiter(),
1464 "want " + commit.toObjectId().getName() + "\n",
1465 "filter blob:limit=5\n",
1466 "done\n",
1467 PacketLineIn.end());
1468 PacketLineIn pckIn = new PacketLineIn(recvStream);
1469 assertThat(pckIn.readString(), is("packfile"));
1470 parsePack(recvStream);
1471
1472 assertFalse(client.getObjectDatabase().has(big.toObjectId()));
1473 assertTrue(client.getObjectDatabase().has(small.toObjectId()));
1474 }
1475
1476 abstract class TreeBuilder {
1477 abstract void addElements(DirCacheBuilder dcBuilder) throws Exception;
1478
1479 RevTree build() throws Exception {
1480 DirCache dc = DirCache.newInCore();
1481 DirCacheBuilder dcBuilder = dc.builder();
1482 addElements(dcBuilder);
1483 dcBuilder.finish();
1484 ObjectId id;
1485 try (ObjectInserter ins =
1486 remote.getRepository().newObjectInserter()) {
1487 id = dc.writeTree(ins);
1488 ins.flush();
1489 }
1490 return remote.getRevWalk().parseTree(id);
1491 }
1492 }
1493
1494 class DeepTreePreparator {
1495 RevBlob blobLowDepth = remote.blob("lo");
1496 RevBlob blobHighDepth = remote.blob("hi");
1497
1498 RevTree subtree = remote.tree(remote.file("1", blobHighDepth));
1499 RevTree rootTree = (new TreeBuilder() {
1500 @Override
1501 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1502 dcBuilder.add(remote.file("1", blobLowDepth));
1503 dcBuilder.addTree(new byte[] {'2'}, DirCacheEntry.STAGE_0,
1504 remote.getRevWalk().getObjectReader(), subtree);
1505 }
1506 }).build();
1507 RevCommit commit = remote.commit(rootTree);
1508
1509 DeepTreePreparator() throws Exception {}
1510 }
1511
1512 private void uploadV2WithTreeDepthFilter(
1513 long depth, ObjectId... wants) throws Exception {
1514 server.getConfig().setBoolean("uploadpack", null, "allowfilter", true);
1515
1516 List<String> input = new ArrayList<>();
1517 input.add("command=fetch\n");
1518 input.add(PacketLineIn.delimiter());
1519 for (ObjectId want : wants) {
1520 input.add("want " + want.getName() + "\n");
1521 }
1522 input.add("filter tree:" + depth + "\n");
1523 input.add("done\n");
1524 input.add(PacketLineIn.end());
1525 ByteArrayInputStream recvStream =
1526 uploadPackV2(
1527 (UploadPack up) -> {up.setRequestPolicy(RequestPolicy.ANY);},
1528 input.toArray(new String[0]));
1529 PacketLineIn pckIn = new PacketLineIn(recvStream);
1530 assertThat(pckIn.readString(), is("packfile"));
1531 parsePack(recvStream);
1532 }
1533
1534 @Test
1535 public void testV2FetchFilterTreeDepth0() throws Exception {
1536 DeepTreePreparator preparator = new DeepTreePreparator();
1537 remote.update("master", preparator.commit);
1538
1539 uploadV2WithTreeDepthFilter(0, preparator.commit.toObjectId());
1540
1541 assertFalse(client.getObjectDatabase()
1542 .has(preparator.rootTree.toObjectId()));
1543 assertFalse(client.getObjectDatabase()
1544 .has(preparator.subtree.toObjectId()));
1545 assertFalse(client.getObjectDatabase()
1546 .has(preparator.blobLowDepth.toObjectId()));
1547 assertFalse(client.getObjectDatabase()
1548 .has(preparator.blobHighDepth.toObjectId()));
1549 assertEquals(1, stats.getTreesTraversed());
1550 }
1551
1552 @Test
1553 public void testV2FetchFilterTreeDepth1_serverHasBitmap() throws Exception {
1554 DeepTreePreparator preparator = new DeepTreePreparator();
1555 remote.update("master", preparator.commit);
1556
1557
1558
1559 generateBitmaps(server);
1560
1561 uploadV2WithTreeDepthFilter(1, preparator.commit.toObjectId());
1562
1563 assertTrue(client.getObjectDatabase()
1564 .has(preparator.rootTree.toObjectId()));
1565 assertFalse(client.getObjectDatabase()
1566 .has(preparator.subtree.toObjectId()));
1567 assertFalse(client.getObjectDatabase()
1568 .has(preparator.blobLowDepth.toObjectId()));
1569 assertFalse(client.getObjectDatabase()
1570 .has(preparator.blobHighDepth.toObjectId()));
1571 assertEquals(1, stats.getTreesTraversed());
1572 }
1573
1574 @Test
1575 public void testV2FetchFilterTreeDepth2() throws Exception {
1576 DeepTreePreparator preparator = new DeepTreePreparator();
1577 remote.update("master", preparator.commit);
1578
1579 uploadV2WithTreeDepthFilter(2, preparator.commit.toObjectId());
1580
1581 assertTrue(client.getObjectDatabase()
1582 .has(preparator.rootTree.toObjectId()));
1583 assertTrue(client.getObjectDatabase()
1584 .has(preparator.subtree.toObjectId()));
1585 assertTrue(client.getObjectDatabase()
1586 .has(preparator.blobLowDepth.toObjectId()));
1587 assertFalse(client.getObjectDatabase()
1588 .has(preparator.blobHighDepth.toObjectId()));
1589 assertEquals(2, stats.getTreesTraversed());
1590 }
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600 class RepeatedSubtreePreparator {
1601 RevBlob foo = remote.blob("foo");
1602 RevTree subtree3 = remote.tree(remote.file("foo", foo));
1603 RevTree subtree2 = (new TreeBuilder() {
1604 @Override
1605 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1606 dcBuilder.addTree(new byte[] {'b'}, DirCacheEntry.STAGE_0,
1607 remote.getRevWalk().getObjectReader(), subtree3);
1608 }
1609 }).build();
1610 RevTree subtree1 = (new TreeBuilder() {
1611 @Override
1612 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1613 dcBuilder.addTree(new byte[] {'x'}, DirCacheEntry.STAGE_0,
1614 remote.getRevWalk().getObjectReader(), subtree2);
1615 }
1616 }).build();
1617 RevTree rootTree = (new TreeBuilder() {
1618 @Override
1619 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1620 dcBuilder.addTree(new byte[] {'a'}, DirCacheEntry.STAGE_0,
1621 remote.getRevWalk().getObjectReader(), subtree1);
1622 dcBuilder.addTree(new byte[] {'x'}, DirCacheEntry.STAGE_0,
1623 remote.getRevWalk().getObjectReader(), subtree2);
1624 }
1625 }).build();
1626 RevCommit commit = remote.commit(rootTree);
1627
1628 RepeatedSubtreePreparator() throws Exception {}
1629 }
1630
1631 @Test
1632 public void testV2FetchFilterTreeDepth_iterateOverTreeAtTwoLevels()
1633 throws Exception {
1634
1635
1636
1637 RepeatedSubtreePreparator preparator = new RepeatedSubtreePreparator();
1638 remote.update("master", preparator.commit);
1639
1640 uploadV2WithTreeDepthFilter(4, preparator.commit.toObjectId());
1641
1642 assertTrue(client.getObjectDatabase()
1643 .has(preparator.foo.toObjectId()));
1644 }
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662 class RepeatedSubtreeAtSameLevelPreparator {
1663 RevBlob foo = remote.blob("foo");
1664
1665
1666 RevTree subtree1 = remote.tree(remote.file("foo", foo));
1667
1668
1669 RevTree subtree2 = (new TreeBuilder() {
1670 @Override
1671 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1672 dcBuilder.addTree(new byte[] {'b'}, DirCacheEntry.STAGE_0,
1673 remote.getRevWalk().getObjectReader(), subtree1);
1674 }
1675 }).build();
1676
1677
1678 RevTree subtree3 = (new TreeBuilder() {
1679 @Override
1680 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1681 dcBuilder.addTree(new byte[] {'x'}, DirCacheEntry.STAGE_0,
1682 remote.getRevWalk().getObjectReader(), subtree2);
1683 }
1684 }).build();
1685
1686 RevBlob baz = remote.blob("baz");
1687
1688
1689 RevTree subtree4 = remote.tree(remote.file("baz", baz));
1690
1691
1692 RevTree subtree5 = (new TreeBuilder() {
1693 @Override
1694 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1695 dcBuilder.addTree(new byte[] {'c'}, DirCacheEntry.STAGE_0,
1696 remote.getRevWalk().getObjectReader(), subtree4);
1697 }
1698 }).build();
1699
1700
1701 RevTree subtree6 = (new TreeBuilder() {
1702 @Override
1703 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1704 dcBuilder.addTree(new byte[] {'u'}, DirCacheEntry.STAGE_0,
1705 remote.getRevWalk().getObjectReader(), subtree5);
1706 }
1707 }).build();
1708
1709
1710 RevTree subtree7 = (new TreeBuilder() {
1711 @Override
1712 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1713 dcBuilder.addTree(new byte[] {'v'}, DirCacheEntry.STAGE_0,
1714 remote.getRevWalk().getObjectReader(), subtree5);
1715 }
1716 }).build();
1717
1718 RevTree rootTree = (new TreeBuilder() {
1719 @Override
1720 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1721 dcBuilder.addTree(new byte[] {'a'}, DirCacheEntry.STAGE_0,
1722 remote.getRevWalk().getObjectReader(), subtree3);
1723 dcBuilder.addTree(new byte[] {'b'}, DirCacheEntry.STAGE_0,
1724 remote.getRevWalk().getObjectReader(), subtree6);
1725 dcBuilder.addTree(new byte[] {'y'}, DirCacheEntry.STAGE_0,
1726 remote.getRevWalk().getObjectReader(), subtree3);
1727 dcBuilder.addTree(new byte[] {'z'}, DirCacheEntry.STAGE_0,
1728 remote.getRevWalk().getObjectReader(), subtree7);
1729 }
1730 }).build();
1731 RevCommit commit = remote.commit(rootTree);
1732
1733 RepeatedSubtreeAtSameLevelPreparator() throws Exception {}
1734 }
1735
1736 @Test
1737 public void testV2FetchFilterTreeDepth_repeatTreeAtSameLevelIncludeFile()
1738 throws Exception {
1739 RepeatedSubtreeAtSameLevelPreparator preparator =
1740 new RepeatedSubtreeAtSameLevelPreparator();
1741 remote.update("master", preparator.commit);
1742
1743 uploadV2WithTreeDepthFilter(5, preparator.commit.toObjectId());
1744
1745 assertTrue(client.getObjectDatabase()
1746 .has(preparator.foo.toObjectId()));
1747 assertTrue(client.getObjectDatabase()
1748 .has(preparator.baz.toObjectId()));
1749 assertEquals(8, stats.getTreesTraversed());
1750 }
1751
1752 @Test
1753 public void testV2FetchFilterTreeDepth_repeatTreeAtSameLevelExcludeFile()
1754 throws Exception {
1755 RepeatedSubtreeAtSameLevelPreparator preparator =
1756 new RepeatedSubtreeAtSameLevelPreparator();
1757 remote.update("master", preparator.commit);
1758
1759 uploadV2WithTreeDepthFilter(4, preparator.commit.toObjectId());
1760
1761 assertFalse(client.getObjectDatabase()
1762 .has(preparator.foo.toObjectId()));
1763 assertFalse(client.getObjectDatabase()
1764 .has(preparator.baz.toObjectId()));
1765 assertEquals(8, stats.getTreesTraversed());
1766 }
1767
1768 @Test
1769 public void testWantFilteredObject() throws Exception {
1770 RepeatedSubtreePreparator preparator = new RepeatedSubtreePreparator();
1771 remote.update("master", preparator.commit);
1772
1773
1774
1775 uploadV2WithTreeDepthFilter(
1776 3,
1777 preparator.commit.toObjectId(),
1778 preparator.foo.toObjectId());
1779 assertTrue(client.getObjectDatabase()
1780 .has(preparator.foo.toObjectId()));
1781
1782 client = newRepo("client");
1783
1784
1785 uploadV2WithTreeDepthFilter(
1786 2,
1787 preparator.commit.toObjectId(),
1788 preparator.subtree3.toObjectId());
1789 assertTrue(client.getObjectDatabase()
1790 .has(preparator.foo.toObjectId()));
1791 assertTrue(client.getObjectDatabase()
1792 .has(preparator.subtree3.toObjectId()));
1793 }
1794
1795 private void checkV2FetchWhenNotAllowed(String fetchLine, String expectedMessage)
1796 throws Exception {
1797 RevCommit commit = remote.commit().message("0").create();
1798 remote.update("master", commit);
1799
1800 UploadPackInternalServerErrorException e = assertThrows(
1801 UploadPackInternalServerErrorException.class,
1802 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
1803 "want " + commit.toObjectId().getName() + "\n",
1804 fetchLine, "done\n", PacketLineIn.end()));
1805 assertThat(e.getCause().getMessage(),
1806 containsString(expectedMessage));
1807 }
1808
1809 @Test
1810 public void testV2FetchFilterWhenNotAllowed() throws Exception {
1811 checkV2FetchWhenNotAllowed(
1812 "filter blob:limit=5\n",
1813 "unexpected filter blob:limit=5");
1814 }
1815
1816 @Test
1817 public void testV2FetchWantRefIfNotAllowed() throws Exception {
1818 checkV2FetchWhenNotAllowed(
1819 "want-ref refs/heads/one\n",
1820 "unexpected want-ref refs/heads/one");
1821 }
1822
1823 @Test
1824 public void testV2FetchSidebandAllIfNotAllowed() throws Exception {
1825 checkV2FetchWhenNotAllowed(
1826 "sideband-all\n",
1827 "unexpected sideband-all");
1828 }
1829
1830 @Test
1831 public void testV2FetchWantRef() throws Exception {
1832 RevCommit one = remote.commit().message("1").create();
1833 RevCommit two = remote.commit().message("2").create();
1834 RevCommit three = remote.commit().message("3").create();
1835 remote.update("one", one);
1836 remote.update("two", two);
1837 remote.update("three", three);
1838
1839 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
1840
1841 ByteArrayInputStream recvStream = uploadPackV2(
1842 "command=fetch\n",
1843 PacketLineIn.delimiter(),
1844 "want-ref refs/heads/one\n",
1845 "want-ref refs/heads/two\n",
1846 "done\n",
1847 PacketLineIn.end());
1848 PacketLineIn pckIn = new PacketLineIn(recvStream);
1849 assertThat(pckIn.readString(), is("wanted-refs"));
1850 assertThat(
1851 Arrays.asList(pckIn.readString(), pckIn.readString()),
1852 hasItems(
1853 one.toObjectId().getName() + " refs/heads/one",
1854 two.toObjectId().getName() + " refs/heads/two"));
1855 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1856 assertThat(pckIn.readString(), is("packfile"));
1857 parsePack(recvStream);
1858
1859 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
1860 assertTrue(client.getObjectDatabase().has(two.toObjectId()));
1861 assertFalse(client.getObjectDatabase().has(three.toObjectId()));
1862 }
1863
1864 @Test
1865 public void testV2FetchBadWantRef() throws Exception {
1866 RevCommit one = remote.commit().message("1").create();
1867 remote.update("one", one);
1868
1869 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant",
1870 true);
1871
1872 UploadPackInternalServerErrorException e = assertThrows(
1873 UploadPackInternalServerErrorException.class,
1874 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
1875 "want-ref refs/heads/one\n",
1876 "want-ref refs/heads/nonExistentRef\n", "done\n",
1877 PacketLineIn.end()));
1878 assertThat(e.getCause().getMessage(),
1879 containsString("Invalid ref name: refs/heads/nonExistentRef"));
1880 }
1881
1882 @Test
1883 public void testV2FetchMixedWantRef() throws Exception {
1884 RevCommit one = remote.commit().message("1").create();
1885 RevCommit two = remote.commit().message("2").create();
1886 RevCommit three = remote.commit().message("3").create();
1887 remote.update("one", one);
1888 remote.update("two", two);
1889 remote.update("three", three);
1890
1891 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
1892
1893 ByteArrayInputStream recvStream = uploadPackV2(
1894 "command=fetch\n",
1895 PacketLineIn.delimiter(),
1896 "want-ref refs/heads/one\n",
1897 "want " + two.toObjectId().getName() + "\n",
1898 "done\n",
1899 PacketLineIn.end());
1900 PacketLineIn pckIn = new PacketLineIn(recvStream);
1901 assertThat(pckIn.readString(), is("wanted-refs"));
1902 assertThat(
1903 pckIn.readString(),
1904 is(one.toObjectId().getName() + " refs/heads/one"));
1905 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1906 assertThat(pckIn.readString(), is("packfile"));
1907 parsePack(recvStream);
1908
1909 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
1910 assertTrue(client.getObjectDatabase().has(two.toObjectId()));
1911 assertFalse(client.getObjectDatabase().has(three.toObjectId()));
1912 }
1913
1914 @Test
1915 public void testV2FetchWantRefWeAlreadyHave() throws Exception {
1916 RevCommit one = remote.commit().message("1").create();
1917 remote.update("one", one);
1918
1919 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
1920
1921 ByteArrayInputStream recvStream = uploadPackV2(
1922 "command=fetch\n",
1923 PacketLineIn.delimiter(),
1924 "want-ref refs/heads/one\n",
1925 "have " + one.toObjectId().getName(),
1926 "done\n",
1927 PacketLineIn.end());
1928 PacketLineIn pckIn = new PacketLineIn(recvStream);
1929
1930
1931
1932
1933 assertThat(pckIn.readString(), is("wanted-refs"));
1934 assertThat(
1935 pckIn.readString(),
1936 is(one.toObjectId().getName() + " refs/heads/one"));
1937 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1938
1939
1940 assertThat(pckIn.readString(), is("packfile"));
1941 parsePack(recvStream);
1942 assertFalse(client.getObjectDatabase().has(one.toObjectId()));
1943 }
1944
1945 @Test
1946 public void testV2FetchWantRefAndDeepen() throws Exception {
1947 RevCommit parent = remote.commit().message("parent").create();
1948 RevCommit child = remote.commit().message("x").parent(parent).create();
1949 remote.update("branch1", child);
1950
1951 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
1952
1953 ByteArrayInputStream recvStream = uploadPackV2(
1954 "command=fetch\n",
1955 PacketLineIn.delimiter(),
1956 "want-ref refs/heads/branch1\n",
1957 "deepen 1\n",
1958 "done\n",
1959 PacketLineIn.end());
1960 PacketLineIn pckIn = new PacketLineIn(recvStream);
1961
1962
1963 assertThat(pckIn.readString(), is("shallow-info"));
1964 assertThat(pckIn.readString(), is("shallow " + child.toObjectId().getName()));
1965 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1966 assertThat(pckIn.readString(), is("wanted-refs"));
1967 assertThat(pckIn.readString(), is(child.toObjectId().getName() + " refs/heads/branch1"));
1968 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1969 assertThat(pckIn.readString(), is("packfile"));
1970 parsePack(recvStream);
1971 assertTrue(client.getObjectDatabase().has(child.toObjectId()));
1972 assertFalse(client.getObjectDatabase().has(parent.toObjectId()));
1973 }
1974
1975 @Test
1976 public void testV2FetchMissingShallow() throws Exception {
1977 RevCommit one = remote.commit().message("1").create();
1978 RevCommit two = remote.commit().message("2").parent(one).create();
1979 RevCommit three = remote.commit().message("3").parent(two).create();
1980 remote.update("three", three);
1981
1982 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant",
1983 true);
1984
1985 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
1986 PacketLineIn.delimiter(),
1987 "want-ref refs/heads/three\n",
1988 "deepen 3",
1989 "shallow 0123012301230123012301230123012301230123",
1990 "shallow " + two.getName() + '\n',
1991 "done\n",
1992 PacketLineIn.end());
1993 PacketLineIn pckIn = new PacketLineIn(recvStream);
1994
1995 assertThat(pckIn.readString(), is("shallow-info"));
1996 assertThat(pckIn.readString(),
1997 is("shallow " + one.toObjectId().getName()));
1998 assertThat(pckIn.readString(),
1999 is("unshallow " + two.toObjectId().getName()));
2000 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2001 assertThat(pckIn.readString(), is("wanted-refs"));
2002 assertThat(pckIn.readString(),
2003 is(three.toObjectId().getName() + " refs/heads/three"));
2004 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2005 assertThat(pckIn.readString(), is("packfile"));
2006 parsePack(recvStream);
2007
2008 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
2009 assertTrue(client.getObjectDatabase().has(two.toObjectId()));
2010 assertTrue(client.getObjectDatabase().has(three.toObjectId()));
2011 }
2012
2013 @Test
2014 public void testV2FetchSidebandAllNoPackfile() throws Exception {
2015 RevCommit fooParent = remote.commit().message("x").create();
2016 RevCommit fooChild = remote.commit().message("x").parent(fooParent).create();
2017 RevCommit barParent = remote.commit().message("y").create();
2018 RevCommit barChild = remote.commit().message("y").parent(barParent).create();
2019 remote.update("branch1", fooChild);
2020 remote.update("branch2", barChild);
2021
2022 server.getConfig().setBoolean("uploadpack", null, "allowsidebandall", true);
2023
2024 ByteArrayInputStream recvStream = uploadPackV2(
2025 "command=fetch\n",
2026 PacketLineIn.delimiter(),
2027 "sideband-all\n",
2028 "want " + fooChild.toObjectId().getName() + "\n",
2029 "want " + barChild.toObjectId().getName() + "\n",
2030 "have " + fooParent.toObjectId().getName() + "\n",
2031 PacketLineIn.end());
2032 PacketLineIn pckIn = new PacketLineIn(recvStream);
2033
2034 assertThat(pckIn.readString(), is("\001acknowledgments"));
2035 assertThat(pckIn.readString(), is("\001ACK " + fooParent.getName()));
2036 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
2037 }
2038
2039 @Test
2040 public void testV2FetchSidebandAllPackfile() throws Exception {
2041 RevCommit commit = remote.commit().message("x").create();
2042 remote.update("master", commit);
2043
2044 server.getConfig().setBoolean("uploadpack", null, "allowsidebandall", true);
2045
2046 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
2047 PacketLineIn.delimiter(),
2048 "want " + commit.getName() + "\n",
2049 "sideband-all\n",
2050 "done\n",
2051 PacketLineIn.end());
2052 PacketLineIn pckIn = new PacketLineIn(recvStream);
2053
2054 String s;
2055
2056
2057
2058 for (s = pckIn.readString(); s.startsWith("\002"); s = pckIn.readString()) {
2059
2060 }
2061 assertThat(s, is("\001packfile"));
2062 parsePack(recvStream);
2063 }
2064
2065 @Test
2066 public void testV2FetchPackfileUris() throws Exception {
2067
2068 RevCommit commit = remote.commit().message("x").create();
2069 remote.update("master", commit);
2070 generateBitmaps(server);
2071
2072
2073 RevCommit commit2 = remote.commit().message("x").parent(commit).create();
2074 remote.update("master", commit2);
2075
2076 server.getConfig().setBoolean("uploadpack", null, "allowsidebandall", true);
2077
2078 ByteArrayInputStream recvStream = uploadPackV2(
2079 (UploadPack up) -> {
2080 up.setCachedPackUriProvider(new CachedPackUriProvider() {
2081 @Override
2082 public PackInfo getInfo(CachedPack pack,
2083 Collection<String> protocolsSupported)
2084 throws IOException {
2085 assertThat(protocolsSupported, hasItems("https"));
2086 if (!protocolsSupported.contains("https"))
2087 return null;
2088 return new PackInfo("myhash", "myuri", 100);
2089 }
2090
2091 });
2092 },
2093 "command=fetch\n",
2094 PacketLineIn.delimiter(),
2095 "want " + commit2.getName() + "\n",
2096 "sideband-all\n",
2097 "packfile-uris https\n",
2098 "done\n",
2099 PacketLineIn.end());
2100 PacketLineIn pckIn = new PacketLineIn(recvStream);
2101
2102 String s;
2103
2104 for (s = pckIn.readString(); s.startsWith("\002"); s = pckIn.readString()) {
2105
2106 }
2107 assertThat(s, is("\001packfile-uris"));
2108 assertThat(pckIn.readString(), is("\001myhash myuri"));
2109 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2110 assertThat(pckIn.readString(), is("\001packfile"));
2111 parsePack(recvStream);
2112
2113 assertFalse(client.getObjectDatabase().has(commit.toObjectId()));
2114 assertTrue(client.getObjectDatabase().has(commit2.toObjectId()));
2115 }
2116
2117 @Test
2118 public void testGetPeerAgentProtocolV0() throws Exception {
2119 RevCommit one = remote.commit().message("1").create();
2120 remote.update("one", one);
2121
2122 UploadPack up = new UploadPack(server);
2123 ByteArrayInputStream send = linesAsInputStream(
2124 "want " + one.getName() + " agent=JGit-test/1.2.3\n",
2125 PacketLineIn.end(),
2126 "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n");
2127
2128 ByteArrayOutputStream recv = new ByteArrayOutputStream();
2129 up.upload(send, recv, null);
2130
2131 assertEquals(up.getPeerUserAgent(), "JGit-test/1.2.3");
2132 }
2133
2134 @Test
2135 public void testGetPeerAgentProtocolV2() throws Exception {
2136 server.getConfig().setString("protocol", null, "version", "2");
2137
2138 RevCommit one = remote.commit().message("1").create();
2139 remote.update("one", one);
2140
2141 UploadPack up = new UploadPack(server);
2142 up.setExtraParameters(Sets.of("version=2"));
2143
2144 ByteArrayInputStream send = linesAsInputStream(
2145 "command=fetch\n", "agent=JGit-test/1.2.4\n",
2146 PacketLineIn.delimiter(), "want " + one.getName() + "\n",
2147 "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n",
2148 PacketLineIn.end());
2149
2150 ByteArrayOutputStream recv = new ByteArrayOutputStream();
2151 up.upload(send, recv, null);
2152
2153 assertEquals(up.getPeerUserAgent(), "JGit-test/1.2.4");
2154 }
2155
2156 private static class RejectAllRefFilter implements RefFilter {
2157 @Override
2158 public Map<String, Ref> filter(Map<String, Ref> refs) {
2159 return new HashMap<>();
2160 }
2161 }
2162
2163 @Test
2164 public void testSingleBranchCloneTagChain() throws Exception {
2165 RevBlob blob0 = remote.blob("Initial content of first file");
2166 RevBlob blob1 = remote.blob("Second file content");
2167 RevCommit commit0 = remote
2168 .commit(remote.tree(remote.file("prvni.txt", blob0)));
2169 RevCommit commit1 = remote
2170 .commit(remote.tree(remote.file("druhy.txt", blob1)), commit0);
2171 remote.update("master", commit1);
2172
2173 RevTag heavyTag1 = remote.tag("commitTagRing", commit0);
2174 remote.getRevWalk().parseHeaders(heavyTag1);
2175 RevTag heavyTag2 = remote.tag("middleTagRing", heavyTag1);
2176 remote.lightweightTag("refTagRing", heavyTag2);
2177
2178 UploadPack uploadPack = new UploadPack(remote.getRepository());
2179
2180 ByteArrayOutputStream cli = new ByteArrayOutputStream();
2181 PacketLineOut clientWant = new PacketLineOut(cli);
2182 clientWant.writeString("want " + commit1.name()
2183 + " multi_ack_detailed include-tag thin-pack ofs-delta agent=tempo/pflaska");
2184 clientWant.end();
2185 clientWant.writeString("done\n");
2186
2187 try (ByteArrayOutputStream serverResponse = new ByteArrayOutputStream()) {
2188
2189 uploadPack.setPreUploadHook(new PreUploadHook() {
2190 @Override
2191 public void onBeginNegotiateRound(UploadPack up,
2192 Collection<? extends ObjectId> wants, int cntOffered)
2193 throws ServiceMayNotContinueException {
2194
2195 }
2196
2197 @Override
2198 public void onEndNegotiateRound(UploadPack up,
2199 Collection<? extends ObjectId> wants, int cntCommon,
2200 int cntNotFound, boolean ready)
2201 throws ServiceMayNotContinueException {
2202
2203 }
2204
2205 @Override
2206 public void onSendPack(UploadPack up,
2207 Collection<? extends ObjectId> wants,
2208 Collection<? extends ObjectId> haves)
2209 throws ServiceMayNotContinueException {
2210
2211 serverResponse.reset();
2212 }
2213 });
2214 uploadPack.upload(new ByteArrayInputStream(cli.toByteArray()),
2215 serverResponse, System.err);
2216 InputStream packReceived = new ByteArrayInputStream(
2217 serverResponse.toByteArray());
2218 PackLock lock = null;
2219 try (ObjectInserter ins = client.newObjectInserter()) {
2220 PackParser parser = ins.newPackParser(packReceived);
2221 parser.setAllowThin(true);
2222 parser.setLockMessage("receive-tag-chain");
2223 ProgressMonitor mlc = NullProgressMonitor.INSTANCE;
2224 lock = parser.parse(mlc, mlc);
2225 ins.flush();
2226 } finally {
2227 if (lock != null) {
2228 lock.unlock();
2229 }
2230 }
2231 InMemoryRepository.MemObjDatabase objDb = client
2232 .getObjectDatabase();
2233 assertTrue(objDb.has(blob0.toObjectId()));
2234 assertTrue(objDb.has(blob1.toObjectId()));
2235 assertTrue(objDb.has(commit0.toObjectId()));
2236 assertTrue(objDb.has(commit1.toObjectId()));
2237 assertTrue(objDb.has(heavyTag1.toObjectId()));
2238 assertTrue(objDb.has(heavyTag2.toObjectId()));
2239 }
2240 }
2241
2242 @Test
2243 public void testSafeToClearRefsInFetchV0() throws Exception {
2244 server =
2245 new RefCallsCountingRepository(
2246 new DfsRepositoryDescription("server"));
2247 remote = new TestRepository<>(server);
2248 RevCommit one = remote.commit().message("1").create();
2249 remote.update("one", one);
2250 testProtocol = new TestProtocol<>((Object req, Repository db) -> {
2251 UploadPack up = new UploadPack(db);
2252 return up;
2253 }, null);
2254 uri = testProtocol.register(ctx, server);
2255 try (Transport tn = testProtocol.open(uri, client, "server")) {
2256 tn.fetch(NullProgressMonitor.INSTANCE,
2257 Collections.singletonList(new RefSpec(one.name())));
2258 }
2259 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
2260 assertEquals(1, ((RefCallsCountingRepository)server).numRefCalls());
2261 }
2262
2263 @Test
2264 public void testSafeToClearRefsInFetchV2() throws Exception {
2265 server =
2266 new RefCallsCountingRepository(
2267 new DfsRepositoryDescription("server"));
2268 remote = new TestRepository<>(server);
2269 RevCommit one = remote.commit().message("1").create();
2270 RevCommit two = remote.commit().message("2").create();
2271 remote.update("one", one);
2272 remote.update("two", two);
2273 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
2274 ByteArrayInputStream recvStream = uploadPackV2(
2275 "command=fetch\n",
2276 PacketLineIn.delimiter(),
2277 "want-ref refs/heads/one\n",
2278 "want-ref refs/heads/two\n",
2279 "done\n",
2280 PacketLineIn.end());
2281 PacketLineIn pckIn = new PacketLineIn(recvStream);
2282 assertThat(pckIn.readString(), is("wanted-refs"));
2283 assertThat(
2284 Arrays.asList(pckIn.readString(), pckIn.readString()),
2285 hasItems(
2286 one.toObjectId().getName() + " refs/heads/one",
2287 two.toObjectId().getName() + " refs/heads/two"));
2288 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2289 assertThat(pckIn.readString(), is("packfile"));
2290 parsePack(recvStream);
2291 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
2292 assertEquals(1, ((RefCallsCountingRepository)server).numRefCalls());
2293 }
2294
2295 private class RefCallsCountingRepository extends InMemoryRepository {
2296 private final InMemoryRepository.MemRefDatabase refdb;
2297 private int numRefCalls;
2298
2299 public RefCallsCountingRepository(DfsRepositoryDescription repoDesc) {
2300 super(repoDesc);
2301 refdb = new InMemoryRepository.MemRefDatabase() {
2302 @Override
2303 public List<Ref> getRefs() throws IOException {
2304 numRefCalls++;
2305 return super.getRefs();
2306 }
2307 };
2308 }
2309
2310 public int numRefCalls() {
2311 return numRefCalls;
2312 }
2313
2314 @Override
2315 public RefDatabase getRefDatabase() {
2316 return refdb;
2317 }
2318 }
2319 }