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.pack.CachedPack;
38 import org.eclipse.jgit.internal.storage.pack.CachedPackUriProvider;
39 import org.eclipse.jgit.junit.TestRepository;
40 import org.eclipse.jgit.lib.ConfigConstants;
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 testFetchWithBlobZeroFilter() 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 testFetchWithTreeZeroFilter() throws Exception {
302 InMemoryRepository server2 = newRepo("server2");
303 try (TestRepository<InMemoryRepository> remote2 = new TestRepository<>(
304 server2)) {
305 RevBlob blob1 = remote2.blob("foobar");
306 RevBlob blob2 = remote2.blob("fooba");
307 RevTree tree = remote2.tree(remote2.file("1", blob1),
308 remote2.file("2", blob2));
309 RevCommit commit = remote2.commit(tree);
310 remote2.update("master", commit);
311
312 server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
313 true);
314
315 testProtocol = new TestProtocol<>((Object req, Repository db) -> {
316 UploadPack up = new UploadPack(db);
317 return up;
318 }, null);
319 uri = testProtocol.register(ctx, server2);
320
321 try (Transport tn = testProtocol.open(uri, client, "server2")) {
322 tn.setFilterSpec(FilterSpec.withTreeDepthLimit(0));
323 tn.fetch(NullProgressMonitor.INSTANCE,
324 Collections.singletonList(new RefSpec(commit.name())));
325 assertFalse(client.getObjectDatabase().has(tree.toObjectId()));
326 assertFalse(client.getObjectDatabase().has(blob1.toObjectId()));
327 assertFalse(client.getObjectDatabase().has(blob2.toObjectId()));
328 }
329 }
330 }
331
332 @Test
333 public void testFetchWithNonSupportingServer() throws Exception {
334 InMemoryRepository server2 = newRepo("server2");
335 try (TestRepository<InMemoryRepository> remote2 = new TestRepository<>(
336 server2)) {
337 RevBlob blob = remote2.blob("foo");
338 RevTree tree = remote2.tree(remote2.file("1", blob));
339 RevCommit commit = remote2.commit(tree);
340 remote2.update("master", commit);
341
342 server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
343 false);
344
345 testProtocol = new TestProtocol<>((Object req, Repository db) -> {
346 UploadPack up = new UploadPack(db);
347 return up;
348 }, null);
349 uri = testProtocol.register(ctx, server2);
350
351 try (Transport tn = testProtocol.open(uri, client, "server2")) {
352 tn.setFilterSpec(FilterSpec.withBlobLimit(0));
353
354 TransportException e = assertThrows(TransportException.class,
355 () -> tn.fetch(NullProgressMonitor.INSTANCE, Collections
356 .singletonList(new RefSpec(commit.name()))));
357 assertThat(e.getMessage(), containsString(
358 "filter requires server to advertise that capability"));
359 }
360 }
361 }
362
363
364
365
366
367 private ByteArrayInputStream uploadPackSetup(String version,
368 Consumer<UploadPack> postConstructionSetup, String... inputLines)
369 throws Exception {
370
371 ByteArrayInputStream send = linesAsInputStream(inputLines);
372
373 server.getConfig().setString(ConfigConstants.CONFIG_PROTOCOL_SECTION,
374 null, ConfigConstants.CONFIG_KEY_VERSION, version);
375 UploadPack up = new UploadPack(server);
376 if (postConstructionSetup != null) {
377 postConstructionSetup.accept(up);
378 }
379 up.setExtraParameters(Sets.of("version=".concat(version)));
380
381 ByteArrayOutputStream recv = new ByteArrayOutputStream();
382 up.upload(send, recv, null);
383 stats = up.getStatistics();
384
385 return new ByteArrayInputStream(recv.toByteArray());
386 }
387
388 private static ByteArrayInputStream linesAsInputStream(String... inputLines)
389 throws IOException {
390 try (ByteArrayOutputStream send = new ByteArrayOutputStream()) {
391 PacketLineOut pckOut = new PacketLineOut(send);
392 for (String line : inputLines) {
393 Objects.requireNonNull(line);
394 if (PacketLineIn.isEnd(line)) {
395 pckOut.end();
396 } else if (PacketLineIn.isDelimiter(line)) {
397 pckOut.writeDelim();
398 } else {
399 pckOut.writeString(line);
400 }
401 }
402 return new ByteArrayInputStream(send.toByteArray());
403 }
404 }
405
406
407
408
409
410
411 private ByteArrayInputStream uploadPackV1(
412 Consumer<UploadPack> postConstructionSetup,
413 String... inputLines)
414 throws Exception {
415 ByteArrayInputStream recvStream =
416 uploadPackSetup("1", postConstructionSetup, inputLines);
417 PacketLineIn pckIn = new PacketLineIn(recvStream);
418
419
420 while (!PacketLineIn.isEnd(pckIn.readString())) {
421
422 }
423 return recvStream;
424 }
425
426 private ByteArrayInputStream uploadPackV1(String... inputLines) throws Exception {
427 return uploadPackV1(null, inputLines);
428 }
429
430
431
432
433
434
435 private ByteArrayInputStream uploadPackV2(
436 Consumer<UploadPack> postConstructionSetup,
437 String... inputLines)
438 throws Exception {
439 ByteArrayInputStream recvStream = uploadPackSetup(
440 TransferConfig.ProtocolVersion.V2.version(),
441 postConstructionSetup, inputLines);
442 PacketLineIn pckIn = new PacketLineIn(recvStream);
443
444
445 while (!PacketLineIn.isEnd(pckIn.readString())) {
446
447 }
448 return recvStream;
449 }
450
451 private ByteArrayInputStream uploadPackV2(String... inputLines) throws Exception {
452 return uploadPackV2(null, inputLines);
453 }
454
455 private static class TestV2Hook implements ProtocolV2Hook {
456 private CapabilitiesV2Request capabilitiesRequest;
457
458 private LsRefsV2Request lsRefsRequest;
459
460 private FetchV2Request fetchRequest;
461
462 private ObjectInfoRequest objectInfoRequest;
463
464 @Override
465 public void onCapabilities(CapabilitiesV2Request req) {
466 capabilitiesRequest = req;
467 }
468
469 @Override
470 public void onLsRefs(LsRefsV2Request req) {
471 lsRefsRequest = req;
472 }
473
474 @Override
475 public void onFetch(FetchV2Request req) {
476 fetchRequest = req;
477 }
478
479 @Override
480 public void onObjectInfo(ObjectInfoRequest req) {
481 objectInfoRequest = req;
482 }
483 }
484
485 @Test
486 public void testV2Capabilities() throws Exception {
487 TestV2Hook hook = new TestV2Hook();
488 ByteArrayInputStream recvStream = uploadPackSetup(
489 TransferConfig.ProtocolVersion.V2.version(),
490 (UploadPack up) -> {
491 up.setProtocolV2Hook(hook);
492 }, PacketLineIn.end());
493 PacketLineIn pckIn = new PacketLineIn(recvStream);
494 assertThat(hook.capabilitiesRequest, notNullValue());
495 assertThat(pckIn.readString(), is("version 2"));
496 assertThat(
497 Arrays.asList(pckIn.readString(), pckIn.readString(),
498 pckIn.readString()),
499
500
501
502
503
504
505 hasItems("ls-refs", "fetch=shallow", "server-option"));
506 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
507 }
508
509 private void checkAdvertisedIfAllowed(String configSection, String configName,
510 String fetchCapability) throws Exception {
511 server.getConfig().setBoolean(configSection, null, configName, true);
512 ByteArrayInputStream recvStream = uploadPackSetup(
513 TransferConfig.ProtocolVersion.V2.version(), null,
514 PacketLineIn.end());
515 PacketLineIn pckIn = new PacketLineIn(recvStream);
516
517 assertThat(pckIn.readString(), is("version 2"));
518
519 ArrayList<String> lines = new ArrayList<>();
520 String line;
521 while (!PacketLineIn.isEnd((line = pckIn.readString()))) {
522 if (line.startsWith("fetch=")) {
523 assertThat(
524 Arrays.asList(line.substring(6).split(" ")),
525 containsInAnyOrder(fetchCapability, "shallow"));
526 lines.add("fetch");
527 } else {
528 lines.add(line);
529 }
530 }
531 assertThat(lines, containsInAnyOrder("ls-refs", "fetch", "server-option"));
532 }
533
534 private void checkUnadvertisedIfUnallowed(String configSection,
535 String configName, String fetchCapability) throws Exception {
536 server.getConfig().setBoolean(configSection, null, configName, false);
537 ByteArrayInputStream recvStream = uploadPackSetup(
538 TransferConfig.ProtocolVersion.V2.version(), null,
539 PacketLineIn.end());
540 PacketLineIn pckIn = new PacketLineIn(recvStream);
541
542 assertThat(pckIn.readString(), is("version 2"));
543
544 ArrayList<String> lines = new ArrayList<>();
545 String line;
546 while (!PacketLineIn.isEnd((line = pckIn.readString()))) {
547 if (line.startsWith("fetch=")) {
548 List<String> fetchItems = Arrays.asList(line.substring(6).split(" "));
549 assertThat(fetchItems, hasItems("shallow"));
550 assertFalse(fetchItems.contains(fetchCapability));
551 lines.add("fetch");
552 } else {
553 lines.add(line);
554 }
555 }
556 assertThat(lines, hasItems("ls-refs", "fetch", "server-option"));
557 }
558
559 @Test
560 public void testV2CapabilitiesAllowFilter() throws Exception {
561 checkAdvertisedIfAllowed("uploadpack", "allowfilter", "filter");
562 checkUnadvertisedIfUnallowed("uploadpack", "allowfilter", "filter");
563 }
564
565 @Test
566 public void testV2CapabilitiesRefInWant() throws Exception {
567 checkAdvertisedIfAllowed("uploadpack", "allowrefinwant", "ref-in-want");
568 }
569
570 @Test
571 public void testV2CapabilitiesRefInWantNotAdvertisedIfUnallowed() throws Exception {
572 checkUnadvertisedIfUnallowed("uploadpack", "allowrefinwant",
573 "ref-in-want");
574 }
575
576 @Test
577 public void testV2CapabilitiesAdvertiseSidebandAll() throws Exception {
578 server.getConfig().setBoolean("uploadpack", null, "allowsidebandall",
579 true);
580 checkAdvertisedIfAllowed("uploadpack", "advertisesidebandall",
581 "sideband-all");
582 checkUnadvertisedIfUnallowed("uploadpack", "advertisesidebandall",
583 "sideband-all");
584 }
585
586 @Test
587 public void testV2CapabilitiesRefInWantNotAdvertisedIfAdvertisingForbidden() throws Exception {
588 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
589 server.getConfig().setBoolean("uploadpack", null, "advertiserefinwant", false);
590 ByteArrayInputStream recvStream = uploadPackSetup(
591 TransferConfig.ProtocolVersion.V2.version(), null,
592 PacketLineIn.end());
593 PacketLineIn pckIn = new PacketLineIn(recvStream);
594
595 assertThat(pckIn.readString(), is("version 2"));
596 assertThat(
597 Arrays.asList(pckIn.readString(), pckIn.readString(),
598 pckIn.readString()),
599 hasItems("ls-refs", "fetch=shallow", "server-option"));
600 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
601 }
602
603 @Test
604 public void testV2EmptyRequest() throws Exception {
605 ByteArrayInputStream recvStream = uploadPackV2(PacketLineIn.end());
606
607
608 assertEquals(0, recvStream.available());
609 }
610
611 @Test
612 public void testV2LsRefs() throws Exception {
613 RevCommit tip = remote.commit().message("message").create();
614 remote.update("master", tip);
615 server.updateRef("HEAD").link("refs/heads/master");
616 RevTag tag = remote.tag("tag", tip);
617 remote.update("refs/tags/tag", tag);
618
619 TestV2Hook hook = new TestV2Hook();
620 ByteArrayInputStream recvStream = uploadPackV2(
621 (UploadPack up) -> {up.setProtocolV2Hook(hook);},
622 "command=ls-refs\n", PacketLineIn.end());
623 PacketLineIn pckIn = new PacketLineIn(recvStream);
624
625 assertThat(hook.lsRefsRequest, notNullValue());
626 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD"));
627 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
628 assertThat(pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag"));
629 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
630 }
631
632 @Test
633 public void testV2LsRefsSymrefs() throws Exception {
634 RevCommit tip = remote.commit().message("message").create();
635 remote.update("master", tip);
636 server.updateRef("HEAD").link("refs/heads/master");
637 RevTag tag = remote.tag("tag", tip);
638 remote.update("refs/tags/tag", tag);
639
640 ByteArrayInputStream recvStream = uploadPackV2("command=ls-refs\n",
641 PacketLineIn.delimiter(), "symrefs", PacketLineIn.end());
642 PacketLineIn pckIn = new PacketLineIn(recvStream);
643
644 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD symref-target:refs/heads/master"));
645 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
646 assertThat(pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag"));
647 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
648 }
649
650 @Test
651 public void testV2LsRefsPeel() throws Exception {
652 RevCommit tip = remote.commit().message("message").create();
653 remote.update("master", tip);
654 server.updateRef("HEAD").link("refs/heads/master");
655 RevTag tag = remote.tag("tag", tip);
656 remote.update("refs/tags/tag", tag);
657
658 ByteArrayInputStream recvStream = uploadPackV2("command=ls-refs\n",
659 PacketLineIn.delimiter(), "peel", PacketLineIn.end());
660 PacketLineIn pckIn = new PacketLineIn(recvStream);
661
662 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD"));
663 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
664 assertThat(
665 pckIn.readString(),
666 is(tag.toObjectId().getName() + " refs/tags/tag peeled:"
667 + tip.toObjectId().getName()));
668 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
669 }
670
671 @Test
672 public void testV2LsRefsMultipleCommands() throws Exception {
673 RevCommit tip = remote.commit().message("message").create();
674 remote.update("master", tip);
675 server.updateRef("HEAD").link("refs/heads/master");
676 RevTag tag = remote.tag("tag", tip);
677 remote.update("refs/tags/tag", tag);
678
679 ByteArrayInputStream recvStream = uploadPackV2(
680 "command=ls-refs\n", PacketLineIn.delimiter(), "symrefs",
681 "peel", PacketLineIn.end(), "command=ls-refs\n",
682 PacketLineIn.delimiter(), PacketLineIn.end());
683 PacketLineIn pckIn = new PacketLineIn(recvStream);
684
685 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD symref-target:refs/heads/master"));
686 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
687 assertThat(
688 pckIn.readString(),
689 is(tag.toObjectId().getName() + " refs/tags/tag peeled:"
690 + tip.toObjectId().getName()));
691 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
692 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " HEAD"));
693 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
694 assertThat(pckIn.readString(), is(tag.toObjectId().getName() + " refs/tags/tag"));
695 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
696 }
697
698 @Test
699 public void testV2LsRefsRefPrefix() throws Exception {
700 RevCommit tip = remote.commit().message("message").create();
701 remote.update("master", tip);
702 remote.update("other", tip);
703 remote.update("yetAnother", tip);
704
705 ByteArrayInputStream recvStream = uploadPackV2(
706 "command=ls-refs\n",
707 PacketLineIn.delimiter(),
708 "ref-prefix refs/heads/maste",
709 "ref-prefix refs/heads/other",
710 PacketLineIn.end());
711 PacketLineIn pckIn = new PacketLineIn(recvStream);
712
713 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
714 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/other"));
715 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
716 }
717
718 @Test
719 public void testV2LsRefsRefPrefixNoSlash() throws Exception {
720 RevCommit tip = remote.commit().message("message").create();
721 remote.update("master", tip);
722 remote.update("other", tip);
723
724 ByteArrayInputStream recvStream = uploadPackV2(
725 "command=ls-refs\n",
726 PacketLineIn.delimiter(),
727 "ref-prefix refs/heads/maste",
728 "ref-prefix r",
729 PacketLineIn.end());
730 PacketLineIn pckIn = new PacketLineIn(recvStream);
731
732 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/master"));
733 assertThat(pckIn.readString(), is(tip.toObjectId().getName() + " refs/heads/other"));
734 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
735 }
736
737 @Test
738 public void testV2LsRefsUnrecognizedArgument() throws Exception {
739 UploadPackInternalServerErrorException e = assertThrows(
740 UploadPackInternalServerErrorException.class,
741 () -> uploadPackV2("command=ls-refs\n",
742 PacketLineIn.delimiter(), "invalid-argument\n",
743 PacketLineIn.end()));
744 assertThat(e.getCause().getMessage(),
745 containsString("unexpected invalid-argument"));
746 }
747
748 @Test
749 public void testV2LsRefsServerOptions() throws Exception {
750 String[] lines = { "command=ls-refs\n",
751 "server-option=one\n", "server-option=two\n",
752 PacketLineIn.delimiter(),
753 PacketLineIn.end() };
754
755 TestV2Hook testHook = new TestV2Hook();
756 uploadPackSetup(TransferConfig.ProtocolVersion.V2.version(),
757 (UploadPack up) -> {
758 up.setProtocolV2Hook(testHook);
759 }, lines);
760
761 LsRefsV2Request req = testHook.lsRefsRequest;
762 assertEquals(2, req.getServerOptions().size());
763 assertThat(req.getServerOptions(), hasItems("one", "two"));
764 }
765
766
767
768
769
770 private ReceivedPackStatistics parsePack(ByteArrayInputStream recvStream) throws Exception {
771 return parsePack(recvStream, NullProgressMonitor.INSTANCE);
772 }
773
774 private ReceivedPackStatistics parsePack(ByteArrayInputStream recvStream, ProgressMonitor pm)
775 throws Exception {
776 SideBandInputStream sb = new SideBandInputStream(
777 recvStream, pm,
778 new StringWriter(), NullOutputStream.INSTANCE);
779 PackParser pp = client.newObjectInserter().newPackParser(sb);
780 pp.parse(NullProgressMonitor.INSTANCE);
781
782
783 assertEquals(-1, recvStream.read());
784
785 return pp.getReceivedPackStatistics();
786 }
787
788 @Test
789 public void testV2FetchRequestPolicyAdvertised() throws Exception {
790 RevCommit advertized = remote.commit().message("x").create();
791 RevCommit unadvertized = remote.commit().message("y").create();
792 remote.update("branch1", advertized);
793
794
795 uploadPackV2(
796 (UploadPack up) -> {up.setRequestPolicy(RequestPolicy.ADVERTISED);},
797 "command=fetch\n",
798 PacketLineIn.delimiter(),
799 "want " + advertized.name() + "\n",
800 PacketLineIn.end());
801
802
803 UploadPackInternalServerErrorException e = assertThrows(
804 UploadPackInternalServerErrorException.class,
805 () -> uploadPackV2(
806 (UploadPack up) -> {up.setRequestPolicy(RequestPolicy.ADVERTISED);},
807 "command=fetch\n", PacketLineIn.delimiter(),
808 "want " + unadvertized.name() + "\n",
809 PacketLineIn.end()));
810 assertThat(e.getCause().getMessage(),
811 containsString("want " + unadvertized.name() + " not valid"));
812 }
813
814 @Test
815 public void testV2FetchRequestPolicyReachableCommit() throws Exception {
816 RevCommit reachable = remote.commit().message("x").create();
817 RevCommit advertized = remote.commit().message("x").parent(reachable)
818 .create();
819 RevCommit unreachable = remote.commit().message("y").create();
820 remote.update("branch1", advertized);
821
822
823 uploadPackV2(
824 (UploadPack up) -> {up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);},
825 "command=fetch\n",
826 PacketLineIn.delimiter(),
827 "want " + reachable.name() + "\n",
828 PacketLineIn.end());
829
830
831 UploadPackInternalServerErrorException e = assertThrows(
832 UploadPackInternalServerErrorException.class,
833 () -> uploadPackV2(
834 (UploadPack up) -> {up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);},
835 "command=fetch\n", PacketLineIn.delimiter(),
836 "want " + unreachable.name() + "\n",
837 PacketLineIn.end()));
838 assertThat(e.getCause().getMessage(),
839 containsString("want " + unreachable.name() + " not valid"));
840 }
841
842 @Test
843 public void testV2FetchRequestPolicyTip() throws Exception {
844 RevCommit parentOfTip = remote.commit().message("x").create();
845 RevCommit tip = remote.commit().message("y").parent(parentOfTip)
846 .create();
847 remote.update("secret", tip);
848
849
850 uploadPackV2(
851 (UploadPack up) -> {
852 up.setRequestPolicy(RequestPolicy.TIP);
853 up.setRefFilter(new RejectAllRefFilter());
854 },
855 "command=fetch\n",
856 PacketLineIn.delimiter(),
857 "want " + tip.name() + "\n",
858 PacketLineIn.end());
859
860
861 UploadPackInternalServerErrorException e = assertThrows(
862 UploadPackInternalServerErrorException.class,
863 () -> uploadPackV2(
864 (UploadPack up) -> {
865 up.setRequestPolicy(RequestPolicy.TIP);
866 up.setRefFilter(new RejectAllRefFilter());
867 },
868 "command=fetch\n", PacketLineIn.delimiter(),
869 "want " + parentOfTip.name() + "\n",
870 PacketLineIn.end()));
871 assertThat(e.getCause().getMessage(),
872 containsString("want " + parentOfTip.name() + " not valid"));
873 }
874
875 @Test
876 public void testV2FetchRequestPolicyReachableCommitTip() throws Exception {
877 RevCommit parentOfTip = remote.commit().message("x").create();
878 RevCommit tip = remote.commit().message("y").parent(parentOfTip)
879 .create();
880 RevCommit unreachable = remote.commit().message("y").create();
881 remote.update("secret", tip);
882
883
884 uploadPackV2(
885 (UploadPack up) -> {
886 up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT_TIP);
887 up.setRefFilter(new RejectAllRefFilter());
888 },
889 "command=fetch\n",
890 PacketLineIn.delimiter(), "want " + parentOfTip.name() + "\n",
891 PacketLineIn.end());
892
893
894 UploadPackInternalServerErrorException e = assertThrows(
895 UploadPackInternalServerErrorException.class,
896 () -> uploadPackV2(
897 (UploadPack up) -> {
898 up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT_TIP);
899 up.setRefFilter(new RejectAllRefFilter());
900 },
901 "command=fetch\n",
902 PacketLineIn.delimiter(),
903 "want " + unreachable.name() + "\n",
904 PacketLineIn.end()));
905 assertThat(e.getCause().getMessage(),
906 containsString("want " + unreachable.name() + " not valid"));
907 }
908
909 @Test
910 public void testV2FetchRequestPolicyAny() throws Exception {
911 RevCommit unreachable = remote.commit().message("y").create();
912
913
914 uploadPackV2(
915 (UploadPack up) -> {up.setRequestPolicy(RequestPolicy.ANY);},
916 "command=fetch\n",
917 PacketLineIn.delimiter(),
918 "want " + unreachable.name() + "\n",
919 PacketLineIn.end());
920 }
921
922 @Test
923 public void testV2FetchServerDoesNotStopNegotiation() throws Exception {
924 RevCommit fooParent = remote.commit().message("x").create();
925 RevCommit fooChild = remote.commit().message("x").parent(fooParent).create();
926 RevCommit barParent = remote.commit().message("y").create();
927 RevCommit barChild = remote.commit().message("y").parent(barParent).create();
928 remote.update("branch1", fooChild);
929 remote.update("branch2", barChild);
930
931 ByteArrayInputStream recvStream = uploadPackV2(
932 "command=fetch\n",
933 PacketLineIn.delimiter(),
934 "want " + fooChild.toObjectId().getName() + "\n",
935 "want " + barChild.toObjectId().getName() + "\n",
936 "have " + fooParent.toObjectId().getName() + "\n",
937 PacketLineIn.end());
938 PacketLineIn pckIn = new PacketLineIn(recvStream);
939
940 assertThat(pckIn.readString(), is("acknowledgments"));
941 assertThat(pckIn.readString(), is("ACK " + fooParent.toObjectId().getName()));
942 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
943 }
944
945 @Test
946 public void testV2FetchServerStopsNegotiation() throws Exception {
947 RevCommit fooParent = remote.commit().message("x").create();
948 RevCommit fooChild = remote.commit().message("x").parent(fooParent).create();
949 RevCommit barParent = remote.commit().message("y").create();
950 RevCommit barChild = remote.commit().message("y").parent(barParent).create();
951 remote.update("branch1", fooChild);
952 remote.update("branch2", barChild);
953
954 ByteArrayInputStream recvStream = uploadPackV2(
955 "command=fetch\n",
956 PacketLineIn.delimiter(),
957 "want " + fooChild.toObjectId().getName() + "\n",
958 "want " + barChild.toObjectId().getName() + "\n",
959 "have " + fooParent.toObjectId().getName() + "\n",
960 "have " + barParent.toObjectId().getName() + "\n",
961 PacketLineIn.end());
962 PacketLineIn pckIn = new PacketLineIn(recvStream);
963
964 assertThat(pckIn.readString(), is("acknowledgments"));
965 assertThat(
966 Arrays.asList(pckIn.readString(), pckIn.readString()),
967 hasItems(
968 "ACK " + fooParent.toObjectId().getName(),
969 "ACK " + barParent.toObjectId().getName()));
970 assertThat(pckIn.readString(), is("ready"));
971 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
972 assertThat(pckIn.readString(), is("packfile"));
973 parsePack(recvStream);
974 assertFalse(client.getObjectDatabase().has(fooParent.toObjectId()));
975 assertTrue(client.getObjectDatabase().has(fooChild.toObjectId()));
976 assertFalse(client.getObjectDatabase().has(barParent.toObjectId()));
977 assertTrue(client.getObjectDatabase().has(barChild.toObjectId()));
978 }
979
980 @Test
981 public void testV2FetchClientStopsNegotiation() throws Exception {
982 RevCommit fooParent = remote.commit().message("x").create();
983 RevCommit fooChild = remote.commit().message("x").parent(fooParent).create();
984 RevCommit barParent = remote.commit().message("y").create();
985 RevCommit barChild = remote.commit().message("y").parent(barParent).create();
986 remote.update("branch1", fooChild);
987 remote.update("branch2", barChild);
988
989 ByteArrayInputStream recvStream = uploadPackV2(
990 "command=fetch\n",
991 PacketLineIn.delimiter(),
992 "want " + fooChild.toObjectId().getName() + "\n",
993 "want " + barChild.toObjectId().getName() + "\n",
994 "have " + fooParent.toObjectId().getName() + "\n",
995 "done\n",
996 PacketLineIn.end());
997 PacketLineIn pckIn = new PacketLineIn(recvStream);
998
999 assertThat(pckIn.readString(), is("packfile"));
1000 parsePack(recvStream);
1001 assertFalse(client.getObjectDatabase().has(fooParent.toObjectId()));
1002 assertTrue(client.getObjectDatabase().has(fooChild.toObjectId()));
1003 assertTrue(client.getObjectDatabase().has(barParent.toObjectId()));
1004 assertTrue(client.getObjectDatabase().has(barChild.toObjectId()));
1005 }
1006
1007 @Test
1008 public void testV2FetchWithoutWaitForDoneReceivesPackfile()
1009 throws Exception {
1010 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
1011
1012 RevBlob parentBlob = remote.blob(commonInBlob + "a");
1013 RevCommit parent = remote
1014 .commit(remote.tree(remote.file("foo", parentBlob)));
1015 remote.update("branch1", parent);
1016
1017 RevCommit localParent = null;
1018 RevCommit localChild = null;
1019 try (TestRepository<InMemoryRepository> local = new TestRepository<>(
1020 client)) {
1021 RevBlob localParentBlob = local.blob(commonInBlob + "a");
1022 localParent = local
1023 .commit(local.tree(local.file("foo", localParentBlob)));
1024 RevBlob localChildBlob = local.blob(commonInBlob + "b");
1025 localChild = local.commit(
1026 local.tree(local.file("foo", localChildBlob)), localParent);
1027 local.update("branch1", localChild);
1028 }
1029
1030 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
1031 PacketLineIn.delimiter(),
1032 "have " + localParent.toObjectId().getName() + "\n",
1033 "have " + localChild.toObjectId().getName() + "\n",
1034 PacketLineIn.end());
1035 PacketLineIn pckIn = new PacketLineIn(recvStream);
1036 assertThat(pckIn.readString(), is("acknowledgments"));
1037 assertThat(Arrays.asList(pckIn.readString()),
1038 hasItems("ACK " + parent.toObjectId().getName()));
1039 assertThat(pckIn.readString(), is("ready"));
1040 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1041 assertThat(pckIn.readString(), is("packfile"));
1042 parsePack(recvStream);
1043 }
1044
1045 @Test
1046 public void testV2FetchWithWaitForDoneOnlyDoesNegotiation()
1047 throws Exception {
1048 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
1049
1050 RevBlob parentBlob = remote.blob(commonInBlob + "a");
1051 RevCommit parent = remote
1052 .commit(remote.tree(remote.file("foo", parentBlob)));
1053 remote.update("branch1", parent);
1054
1055 RevCommit localParent = null;
1056 RevCommit localChild = null;
1057 try (TestRepository<InMemoryRepository> local = new TestRepository<>(
1058 client)) {
1059 RevBlob localParentBlob = local.blob(commonInBlob + "a");
1060 localParent = local
1061 .commit(local.tree(local.file("foo", localParentBlob)));
1062 RevBlob localChildBlob = local.blob(commonInBlob + "b");
1063 localChild = local.commit(
1064 local.tree(local.file("foo", localChildBlob)), localParent);
1065 local.update("branch1", localChild);
1066 }
1067
1068 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
1069 PacketLineIn.delimiter(), "wait-for-done\n",
1070 "have " + localParent.toObjectId().getName() + "\n",
1071 "have " + localChild.toObjectId().getName() + "\n",
1072 PacketLineIn.end());
1073 PacketLineIn pckIn = new PacketLineIn(recvStream);
1074 assertThat(pckIn.readString(), is("acknowledgments"));
1075 assertThat(Arrays.asList(pckIn.readString()),
1076 hasItems("ACK " + parent.toObjectId().getName()));
1077 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
1078 }
1079
1080 @Test
1081 public void testV2FetchWithWaitForDoneOnlyDoesNegotiationAndNothingToAck()
1082 throws Exception {
1083 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
1084
1085 RevCommit localParent = null;
1086 RevCommit localChild = null;
1087 try (TestRepository<InMemoryRepository> local = new TestRepository<>(
1088 client)) {
1089 RevBlob localParentBlob = local.blob(commonInBlob + "a");
1090 localParent = local
1091 .commit(local.tree(local.file("foo", localParentBlob)));
1092 RevBlob localChildBlob = local.blob(commonInBlob + "b");
1093 localChild = local.commit(
1094 local.tree(local.file("foo", localChildBlob)), localParent);
1095 local.update("branch1", localChild);
1096 }
1097
1098 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
1099 PacketLineIn.delimiter(), "wait-for-done\n",
1100 "have " + localParent.toObjectId().getName() + "\n",
1101 "have " + localChild.toObjectId().getName() + "\n",
1102 PacketLineIn.end());
1103 PacketLineIn pckIn = new PacketLineIn(recvStream);
1104 assertThat(pckIn.readString(), is("acknowledgments"));
1105 assertThat(pckIn.readString(), is("NAK"));
1106 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
1107 }
1108
1109 @Test
1110 public void testV2FetchThinPack() throws Exception {
1111 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
1112
1113 RevBlob parentBlob = remote.blob(commonInBlob + "a");
1114 RevCommit parent = remote
1115 .commit(remote.tree(remote.file("foo", parentBlob)));
1116 RevBlob childBlob = remote.blob(commonInBlob + "b");
1117 RevCommit child = remote
1118 .commit(remote.tree(remote.file("foo", childBlob)), parent);
1119 remote.update("branch1", child);
1120
1121
1122 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
1123 PacketLineIn.delimiter(),
1124 "want " + child.toObjectId().getName() + "\n",
1125 "have " + parent.toObjectId().getName() + "\n", "thin-pack\n",
1126 "done\n", PacketLineIn.end());
1127 PacketLineIn pckIn = new PacketLineIn(recvStream);
1128
1129 assertThat(pckIn.readString(), is("packfile"));
1130
1131
1132
1133 IOException e = assertThrows(IOException.class,
1134 () -> parsePack(recvStream));
1135 assertThat(e.getMessage(),
1136 containsString("pack has unresolved deltas"));
1137 }
1138
1139 @Test
1140 public void testV2FetchNoProgress() throws Exception {
1141 RevCommit commit = remote.commit().message("x").create();
1142 remote.update("branch1", commit);
1143
1144
1145 StringWriter sw = new StringWriter();
1146 ByteArrayInputStream recvStream = uploadPackV2(
1147 "command=fetch\n",
1148 PacketLineIn.delimiter(),
1149 "want " + commit.toObjectId().getName() + "\n",
1150 "done\n",
1151 PacketLineIn.end());
1152 PacketLineIn pckIn = new PacketLineIn(recvStream);
1153 assertThat(pckIn.readString(), is("packfile"));
1154 parsePack(recvStream, new TextProgressMonitor(sw));
1155 assertFalse(sw.toString().isEmpty());
1156
1157
1158 sw = new StringWriter();
1159 recvStream = uploadPackV2(
1160 "command=fetch\n",
1161 PacketLineIn.delimiter(),
1162 "want " + commit.toObjectId().getName() + "\n",
1163 "no-progress\n",
1164 "done\n",
1165 PacketLineIn.end());
1166 pckIn = new PacketLineIn(recvStream);
1167 assertThat(pckIn.readString(), is("packfile"));
1168 parsePack(recvStream, new TextProgressMonitor(sw));
1169 assertTrue(sw.toString().isEmpty());
1170 }
1171
1172 @Test
1173 public void testV2FetchIncludeTag() throws Exception {
1174 RevCommit commit = remote.commit().message("x").create();
1175 RevTag tag = remote.tag("tag", commit);
1176 remote.update("branch1", commit);
1177 remote.update("refs/tags/tag", tag);
1178
1179
1180 ByteArrayInputStream recvStream = uploadPackV2(
1181 "command=fetch\n",
1182 PacketLineIn.delimiter(),
1183 "want " + commit.toObjectId().getName() + "\n",
1184 "done\n",
1185 PacketLineIn.end());
1186 PacketLineIn pckIn = new PacketLineIn(recvStream);
1187 assertThat(pckIn.readString(), is("packfile"));
1188 parsePack(recvStream);
1189 assertFalse(client.getObjectDatabase().has(tag.toObjectId()));
1190
1191
1192 recvStream = uploadPackV2(
1193 "command=fetch\n",
1194 PacketLineIn.delimiter(),
1195 "want " + commit.toObjectId().getName() + "\n",
1196 "include-tag\n",
1197 "done\n",
1198 PacketLineIn.end());
1199 pckIn = new PacketLineIn(recvStream);
1200 assertThat(pckIn.readString(), is("packfile"));
1201 parsePack(recvStream);
1202 assertTrue(client.getObjectDatabase().has(tag.toObjectId()));
1203 }
1204
1205 @Test
1206 public void testUploadNewBytes() throws Exception {
1207 String commonInBlob = "abcdefghijklmnopqrstuvwx";
1208
1209 RevBlob parentBlob = remote.blob(commonInBlob + "a");
1210 RevCommit parent = remote.commit(remote.tree(remote.file("foo", parentBlob)));
1211 RevBlob childBlob = remote.blob(commonInBlob + "b");
1212 RevCommit child = remote.commit(remote.tree(remote.file("foo", childBlob)), parent);
1213 remote.update("branch1", child);
1214
1215 ByteArrayInputStream recvStream = uploadPackV2(
1216 "command=fetch\n",
1217 PacketLineIn.delimiter(),
1218 "want " + child.toObjectId().getName() + "\n",
1219 "ofs-delta\n",
1220 "done\n",
1221 PacketLineIn.end());
1222 PacketLineIn pckIn = new PacketLineIn(recvStream);
1223 assertThat(pckIn.readString(), is("packfile"));
1224 ReceivedPackStatistics receivedStats = parsePack(recvStream);
1225 assertTrue(receivedStats.getNumBytesDuplicated() == 0);
1226 assertTrue(receivedStats.getNumObjectsDuplicated() == 0);
1227 }
1228
1229 @Test
1230 public void testUploadRedundantBytes() throws Exception {
1231 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
1232
1233 RevBlob parentBlob = remote.blob(commonInBlob + "a");
1234 RevCommit parent = remote.commit(remote.tree(remote.file("foo", parentBlob)));
1235 RevBlob childBlob = remote.blob(commonInBlob + "b");
1236 RevCommit child = remote.commit(remote.tree(remote.file("foo", childBlob)), parent);
1237 remote.update("branch1", child);
1238
1239 try (TestRepository<InMemoryRepository> local = new TestRepository<>(
1240 client)) {
1241 RevBlob localParentBlob = local.blob(commonInBlob + "a");
1242 RevCommit localParent = local
1243 .commit(local.tree(local.file("foo", localParentBlob)));
1244 RevBlob localChildBlob = local.blob(commonInBlob + "b");
1245 RevCommit localChild = local.commit(
1246 local.tree(local.file("foo", localChildBlob)), localParent);
1247 local.update("branch1", localChild);
1248 }
1249
1250 ByteArrayInputStream recvStream = uploadPackV2(
1251 "command=fetch\n",
1252 PacketLineIn.delimiter(),
1253 "want " + child.toObjectId().getName() + "\n",
1254 "ofs-delta\n",
1255 "done\n",
1256 PacketLineIn.end());
1257 PacketLineIn pckIn = new PacketLineIn(recvStream);
1258 assertThat(pckIn.readString(), is("packfile"));
1259 ReceivedPackStatistics receivedStats = parsePack(recvStream);
1260
1261 long sizeOfHeader = 12;
1262 long sizeOfTrailer = 20;
1263 long expectedSize = receivedStats.getNumBytesRead() - sizeOfHeader
1264 - sizeOfTrailer;
1265 assertTrue(receivedStats.getNumBytesDuplicated() == expectedSize);
1266 assertTrue(receivedStats.getNumObjectsDuplicated() == 6);
1267 }
1268
1269 @Test
1270 public void testV2FetchOfsDelta() throws Exception {
1271 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
1272
1273 RevBlob parentBlob = remote.blob(commonInBlob + "a");
1274 RevCommit parent = remote.commit(remote.tree(remote.file("foo", parentBlob)));
1275 RevBlob childBlob = remote.blob(commonInBlob + "b");
1276 RevCommit child = remote.commit(remote.tree(remote.file("foo", childBlob)), parent);
1277 remote.update("branch1", child);
1278
1279
1280 ByteArrayInputStream recvStream = uploadPackV2(
1281 "command=fetch\n",
1282 PacketLineIn.delimiter(),
1283 "want " + child.toObjectId().getName() + "\n",
1284 "done\n",
1285 PacketLineIn.end());
1286 PacketLineIn pckIn = new PacketLineIn(recvStream);
1287 assertThat(pckIn.readString(), is("packfile"));
1288 ReceivedPackStatistics receivedStats = parsePack(recvStream);
1289 assertTrue(receivedStats.getNumOfsDelta() == 0);
1290
1291
1292 recvStream = uploadPackV2(
1293 "command=fetch\n",
1294 PacketLineIn.delimiter(),
1295 "want " + child.toObjectId().getName() + "\n",
1296 "ofs-delta\n",
1297 "done\n",
1298 PacketLineIn.end());
1299 pckIn = new PacketLineIn(recvStream);
1300 assertThat(pckIn.readString(), is("packfile"));
1301 receivedStats = parsePack(recvStream);
1302 assertTrue(receivedStats.getNumOfsDelta() != 0);
1303 }
1304
1305 @Test
1306 public void testV2FetchShallow() throws Exception {
1307 RevCommit commonParent = remote.commit().message("parent").create();
1308 RevCommit fooChild = remote.commit().message("x").parent(commonParent).create();
1309 RevCommit barChild = remote.commit().message("y").parent(commonParent).create();
1310 remote.update("branch1", barChild);
1311
1312
1313
1314 ByteArrayInputStream recvStream = uploadPackV2(
1315 "command=fetch\n",
1316 PacketLineIn.delimiter(),
1317 "want " + barChild.toObjectId().getName() + "\n",
1318 "have " + fooChild.toObjectId().getName() + "\n",
1319 "done\n",
1320 PacketLineIn.end());
1321 PacketLineIn pckIn = new PacketLineIn(recvStream);
1322 assertThat(pckIn.readString(), is("packfile"));
1323 parsePack(recvStream);
1324 assertTrue(client.getObjectDatabase().has(barChild.toObjectId()));
1325 assertFalse(client.getObjectDatabase().has(commonParent.toObjectId()));
1326
1327
1328
1329 recvStream = uploadPackV2(
1330 "command=fetch\n",
1331 PacketLineIn.delimiter(),
1332 "want " + barChild.toObjectId().getName() + "\n",
1333 "have " + fooChild.toObjectId().getName() + "\n",
1334 "shallow " + fooChild.toObjectId().getName() + "\n",
1335 "done\n",
1336 PacketLineIn.end());
1337 pckIn = new PacketLineIn(recvStream);
1338 assertThat(pckIn.readString(), is("packfile"));
1339 parsePack(recvStream);
1340 assertTrue(client.getObjectDatabase().has(commonParent.toObjectId()));
1341 }
1342
1343 @Test
1344 public void testV2FetchDeepenAndDone() throws Exception {
1345 RevCommit parent = remote.commit().message("parent").create();
1346 RevCommit child = remote.commit().message("x").parent(parent).create();
1347 remote.update("branch1", child);
1348
1349
1350 ByteArrayInputStream recvStream = uploadPackV2(
1351 "command=fetch\n",
1352 PacketLineIn.delimiter(),
1353 "want " + child.toObjectId().getName() + "\n",
1354 "deepen 1\n",
1355 "done\n",
1356 PacketLineIn.end());
1357 PacketLineIn pckIn = new PacketLineIn(recvStream);
1358 assertThat(pckIn.readString(), is("shallow-info"));
1359 assertThat(pckIn.readString(), is("shallow " + child.toObjectId().getName()));
1360 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1361 assertThat(pckIn.readString(), is("packfile"));
1362 parsePack(recvStream);
1363 assertTrue(client.getObjectDatabase().has(child.toObjectId()));
1364 assertFalse(client.getObjectDatabase().has(parent.toObjectId()));
1365
1366
1367 recvStream = uploadPackV2(
1368 "command=fetch\n",
1369 PacketLineIn.delimiter(),
1370 "want " + child.toObjectId().getName() + "\n",
1371 "done\n",
1372 PacketLineIn.end());
1373 pckIn = new PacketLineIn(recvStream);
1374 assertThat(pckIn.readString(), is("packfile"));
1375 parsePack(recvStream);
1376 assertTrue(client.getObjectDatabase().has(parent.toObjectId()));
1377 }
1378
1379 @Test
1380 public void testV2FetchDeepenWithoutDone() throws Exception {
1381 RevCommit parent = remote.commit().message("parent").create();
1382 RevCommit child = remote.commit().message("x").parent(parent).create();
1383 remote.update("branch1", child);
1384
1385 ByteArrayInputStream recvStream = uploadPackV2(
1386 "command=fetch\n",
1387 PacketLineIn.delimiter(),
1388 "want " + child.toObjectId().getName() + "\n",
1389 "deepen 1\n",
1390 PacketLineIn.end());
1391 PacketLineIn pckIn = new PacketLineIn(recvStream);
1392
1393
1394
1395
1396 assertThat(pckIn.readString(), is("acknowledgments"));
1397 assertThat(pckIn.readString(), is("NAK"));
1398 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
1399 }
1400
1401 @Test
1402 public void testV2FetchShallowSince() throws Exception {
1403 PersonIdent person = new PersonIdent(remote.getRepository());
1404
1405 RevCommit beyondBoundary = remote.commit()
1406 .committer(new PersonIdent(person, 1510000000, 0)).create();
1407 RevCommit boundary = remote.commit().parent(beyondBoundary)
1408 .committer(new PersonIdent(person, 1520000000, 0)).create();
1409 RevCommit tooOld = remote.commit()
1410 .committer(new PersonIdent(person, 1500000000, 0)).create();
1411 RevCommit merge = remote.commit().parent(boundary).parent(tooOld)
1412 .committer(new PersonIdent(person, 1530000000, 0)).create();
1413
1414 remote.update("branch1", merge);
1415
1416
1417 ByteArrayInputStream recvStream = uploadPackV2(
1418 "command=fetch\n",
1419 PacketLineIn.delimiter(),
1420 "shallow " + boundary.toObjectId().getName() + "\n",
1421 "deepen-since 1510000\n",
1422 "want " + merge.toObjectId().getName() + "\n",
1423 "have " + boundary.toObjectId().getName() + "\n",
1424 "done\n",
1425 PacketLineIn.end());
1426 PacketLineIn pckIn = new PacketLineIn(recvStream);
1427 assertThat(pckIn.readString(), is("shallow-info"));
1428
1429
1430
1431 assertThat(pckIn.readString(), is("shallow " + merge.toObjectId().getName()));
1432
1433
1434
1435 assertThat(pckIn.readString(), is("unshallow " + boundary.toObjectId().getName()));
1436
1437 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1438 assertThat(pckIn.readString(), is("packfile"));
1439 parsePack(recvStream);
1440
1441
1442
1443 assertFalse(client.getObjectDatabase().has(tooOld.toObjectId()));
1444
1445
1446
1447 assertFalse(client.getObjectDatabase().has(boundary.toObjectId()));
1448
1449
1450 assertTrue(client.getObjectDatabase().has(beyondBoundary.toObjectId()));
1451 assertTrue(client.getObjectDatabase().has(merge.toObjectId()));
1452 }
1453
1454 @Test
1455 public void testV2FetchShallowSince_excludedParentWithMultipleChildren() throws Exception {
1456 PersonIdent person = new PersonIdent(remote.getRepository());
1457
1458 RevCommit base = remote.commit()
1459 .committer(new PersonIdent(person, 1500000000, 0)).create();
1460 RevCommit child1 = remote.commit().parent(base)
1461 .committer(new PersonIdent(person, 1510000000, 0)).create();
1462 RevCommit child2 = remote.commit().parent(base)
1463 .committer(new PersonIdent(person, 1520000000, 0)).create();
1464
1465 remote.update("branch1", child1);
1466 remote.update("branch2", child2);
1467
1468 ByteArrayInputStream recvStream = uploadPackV2(
1469 "command=fetch\n",
1470 PacketLineIn.delimiter(),
1471 "deepen-since 1510000\n",
1472 "want " + child1.toObjectId().getName() + "\n",
1473 "want " + child2.toObjectId().getName() + "\n",
1474 "done\n",
1475 PacketLineIn.end());
1476 PacketLineIn pckIn = new PacketLineIn(recvStream);
1477 assertThat(pckIn.readString(), is("shallow-info"));
1478
1479
1480 assertThat(
1481 Arrays.asList(pckIn.readString(), pckIn.readString()),
1482 hasItems(
1483 "shallow " + child1.toObjectId().getName(),
1484 "shallow " + child2.toObjectId().getName()));
1485
1486 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1487 assertThat(pckIn.readString(), is("packfile"));
1488 parsePack(recvStream);
1489
1490
1491 assertFalse(client.getObjectDatabase().has(base.toObjectId()));
1492 assertTrue(client.getObjectDatabase().has(child1.toObjectId()));
1493 assertTrue(client.getObjectDatabase().has(child2.toObjectId()));
1494 }
1495
1496 @Test
1497 public void testV2FetchShallowSince_noCommitsSelected() throws Exception {
1498 PersonIdent person = new PersonIdent(remote.getRepository());
1499
1500 RevCommit tooOld = remote.commit()
1501 .committer(new PersonIdent(person, 1500000000, 0)).create();
1502
1503 remote.update("branch1", tooOld);
1504
1505 UploadPackInternalServerErrorException e = assertThrows(
1506 UploadPackInternalServerErrorException.class,
1507 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
1508 "deepen-since 1510000\n",
1509 "want " + tooOld.toObjectId().getName() + "\n",
1510 "done\n", PacketLineIn.end()));
1511 assertThat(e.getCause().getMessage(),
1512 containsString("No commits selected for shallow request"));
1513 }
1514
1515 @Test
1516 public void testV2FetchDeepenNot() throws Exception {
1517 RevCommit one = remote.commit().message("one").create();
1518 RevCommit two = remote.commit().message("two").parent(one).create();
1519 RevCommit three = remote.commit().message("three").parent(two).create();
1520 RevCommit side = remote.commit().message("side").parent(one).create();
1521 RevCommit merge = remote.commit().message("merge")
1522 .parent(three).parent(side).create();
1523
1524 remote.update("branch1", merge);
1525 remote.update("side", side);
1526
1527
1528
1529 ByteArrayInputStream recvStream = uploadPackV2(
1530 "command=fetch\n",
1531 PacketLineIn.delimiter(),
1532 "shallow " + three.toObjectId().getName() + "\n",
1533 "deepen-not side\n",
1534 "want " + merge.toObjectId().getName() + "\n",
1535 "have " + three.toObjectId().getName() + "\n",
1536 "done\n",
1537 PacketLineIn.end());
1538 PacketLineIn pckIn = new PacketLineIn(recvStream);
1539 assertThat(pckIn.readString(), is("shallow-info"));
1540
1541
1542
1543 assertThat(
1544 Arrays.asList(pckIn.readString(), pckIn.readString()),
1545 hasItems(
1546 "shallow " + merge.toObjectId().getName(),
1547 "shallow " + two.toObjectId().getName()));
1548
1549
1550 assertThat(pckIn.readString(), is("unshallow " + three.toObjectId().getName()));
1551
1552 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1553 assertThat(pckIn.readString(), is("packfile"));
1554 parsePack(recvStream);
1555
1556
1557
1558 assertFalse(client.getObjectDatabase().has(side.toObjectId()));
1559 assertFalse(client.getObjectDatabase().has(one.toObjectId()));
1560
1561
1562
1563 assertFalse(client.getObjectDatabase().has(three.toObjectId()));
1564
1565
1566 assertTrue(client.getObjectDatabase().has(merge.toObjectId()));
1567 assertTrue(client.getObjectDatabase().has(two.toObjectId()));
1568 }
1569
1570 @Test
1571 public void testV2FetchDeepenNot_excludeDescendantOfWant()
1572 throws Exception {
1573 RevCommit one = remote.commit().message("one").create();
1574 RevCommit two = remote.commit().message("two").parent(one).create();
1575 RevCommit three = remote.commit().message("three").parent(two).create();
1576 RevCommit four = remote.commit().message("four").parent(three).create();
1577
1578 remote.update("two", two);
1579 remote.update("four", four);
1580
1581 UploadPackInternalServerErrorException e = assertThrows(
1582 UploadPackInternalServerErrorException.class,
1583 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
1584 "deepen-not four\n",
1585 "want " + two.toObjectId().getName() + "\n", "done\n",
1586 PacketLineIn.end()));
1587 assertThat(e.getCause().getMessage(),
1588 containsString("No commits selected for shallow request"));
1589 }
1590
1591 @Test
1592 public void testV2FetchDeepenNot_supportAnnotatedTags() throws Exception {
1593 RevCommit one = remote.commit().message("one").create();
1594 RevCommit two = remote.commit().message("two").parent(one).create();
1595 RevCommit three = remote.commit().message("three").parent(two).create();
1596 RevCommit four = remote.commit().message("four").parent(three).create();
1597 RevTag twoTag = remote.tag("twotag", two);
1598
1599 remote.update("refs/tags/twotag", twoTag);
1600 remote.update("four", four);
1601
1602 ByteArrayInputStream recvStream = uploadPackV2(
1603 "command=fetch\n",
1604 PacketLineIn.delimiter(),
1605 "deepen-not twotag\n",
1606 "want " + four.toObjectId().getName() + "\n",
1607 "done\n",
1608 PacketLineIn.end());
1609 PacketLineIn pckIn = new PacketLineIn(recvStream);
1610 assertThat(pckIn.readString(), is("shallow-info"));
1611 assertThat(pckIn.readString(), is("shallow " + three.toObjectId().getName()));
1612 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1613 assertThat(pckIn.readString(), is("packfile"));
1614 parsePack(recvStream);
1615 assertFalse(client.getObjectDatabase().has(one.toObjectId()));
1616 assertFalse(client.getObjectDatabase().has(two.toObjectId()));
1617 assertTrue(client.getObjectDatabase().has(three.toObjectId()));
1618 assertTrue(client.getObjectDatabase().has(four.toObjectId()));
1619 }
1620
1621 @Test
1622 public void testV2FetchDeepenNot_excludedParentWithMultipleChildren() throws Exception {
1623 PersonIdent person = new PersonIdent(remote.getRepository());
1624
1625 RevCommit base = remote.commit()
1626 .committer(new PersonIdent(person, 1500000000, 0)).create();
1627 RevCommit child1 = remote.commit().parent(base)
1628 .committer(new PersonIdent(person, 1510000000, 0)).create();
1629 RevCommit child2 = remote.commit().parent(base)
1630 .committer(new PersonIdent(person, 1520000000, 0)).create();
1631
1632 remote.update("base", base);
1633 remote.update("branch1", child1);
1634 remote.update("branch2", child2);
1635
1636 ByteArrayInputStream recvStream = uploadPackV2(
1637 "command=fetch\n",
1638 PacketLineIn.delimiter(),
1639 "deepen-not base\n",
1640 "want " + child1.toObjectId().getName() + "\n",
1641 "want " + child2.toObjectId().getName() + "\n",
1642 "done\n",
1643 PacketLineIn.end());
1644 PacketLineIn pckIn = new PacketLineIn(recvStream);
1645 assertThat(pckIn.readString(), is("shallow-info"));
1646
1647
1648 assertThat(
1649 Arrays.asList(pckIn.readString(), pckIn.readString()),
1650 hasItems(
1651 "shallow " + child1.toObjectId().getName(),
1652 "shallow " + child2.toObjectId().getName()));
1653
1654 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1655 assertThat(pckIn.readString(), is("packfile"));
1656 parsePack(recvStream);
1657
1658
1659 assertFalse(client.getObjectDatabase().has(base.toObjectId()));
1660 assertTrue(client.getObjectDatabase().has(child1.toObjectId()));
1661 assertTrue(client.getObjectDatabase().has(child2.toObjectId()));
1662 }
1663
1664 @Test
1665 public void testV2FetchUnrecognizedArgument() throws Exception {
1666 UploadPackInternalServerErrorException e = assertThrows(
1667 UploadPackInternalServerErrorException.class,
1668 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
1669 "invalid-argument\n", PacketLineIn.end()));
1670 assertThat(e.getCause().getMessage(),
1671 containsString("unexpected invalid-argument"));
1672 }
1673
1674 @Test
1675 public void testV2FetchServerOptions() throws Exception {
1676 String[] lines = { "command=fetch\n", "server-option=one\n",
1677 "server-option=two\n", PacketLineIn.delimiter(),
1678 PacketLineIn.end() };
1679
1680 TestV2Hook testHook = new TestV2Hook();
1681 uploadPackSetup(TransferConfig.ProtocolVersion.V2.version(),
1682 (UploadPack up) -> {
1683 up.setProtocolV2Hook(testHook);
1684 }, lines);
1685
1686 FetchV2Request req = testHook.fetchRequest;
1687 assertNotNull(req);
1688 assertEquals(2, req.getServerOptions().size());
1689 assertThat(req.getServerOptions(), hasItems("one", "two"));
1690 }
1691
1692 @Test
1693 public void testV2FetchFilter() throws Exception {
1694 RevBlob big = remote.blob("foobar");
1695 RevBlob small = remote.blob("fooba");
1696 RevTree tree = remote.tree(remote.file("1", big),
1697 remote.file("2", small));
1698 RevCommit commit = remote.commit(tree);
1699 remote.update("master", commit);
1700
1701 server.getConfig().setBoolean("uploadpack", null, "allowfilter", true);
1702
1703 ByteArrayInputStream recvStream = uploadPackV2(
1704 "command=fetch\n",
1705 PacketLineIn.delimiter(),
1706 "want " + commit.toObjectId().getName() + "\n",
1707 "filter blob:limit=5\n",
1708 "done\n",
1709 PacketLineIn.end());
1710 PacketLineIn pckIn = new PacketLineIn(recvStream);
1711 assertThat(pckIn.readString(), is("packfile"));
1712 parsePack(recvStream);
1713
1714 assertFalse(client.getObjectDatabase().has(big.toObjectId()));
1715 assertTrue(client.getObjectDatabase().has(small.toObjectId()));
1716 }
1717
1718 abstract class TreeBuilder {
1719 abstract void addElements(DirCacheBuilder dcBuilder) throws Exception;
1720
1721 RevTree build() throws Exception {
1722 DirCache dc = DirCache.newInCore();
1723 DirCacheBuilder dcBuilder = dc.builder();
1724 addElements(dcBuilder);
1725 dcBuilder.finish();
1726 ObjectId id;
1727 try (ObjectInserter ins =
1728 remote.getRepository().newObjectInserter()) {
1729 id = dc.writeTree(ins);
1730 ins.flush();
1731 }
1732 return remote.getRevWalk().parseTree(id);
1733 }
1734 }
1735
1736 class DeepTreePreparator {
1737 RevBlob blobLowDepth = remote.blob("lo");
1738 RevBlob blobHighDepth = remote.blob("hi");
1739
1740 RevTree subtree = remote.tree(remote.file("1", blobHighDepth));
1741 RevTree rootTree = (new TreeBuilder() {
1742 @Override
1743 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1744 dcBuilder.add(remote.file("1", blobLowDepth));
1745 dcBuilder.addTree(new byte[] {'2'}, DirCacheEntry.STAGE_0,
1746 remote.getRevWalk().getObjectReader(), subtree);
1747 }
1748 }).build();
1749 RevCommit commit = remote.commit(rootTree);
1750
1751 DeepTreePreparator() throws Exception {}
1752 }
1753
1754 private void uploadV2WithTreeDepthFilter(
1755 long depth, ObjectId... wants) throws Exception {
1756 server.getConfig().setBoolean("uploadpack", null, "allowfilter", true);
1757
1758 List<String> input = new ArrayList<>();
1759 input.add("command=fetch\n");
1760 input.add(PacketLineIn.delimiter());
1761 for (ObjectId want : wants) {
1762 input.add("want " + want.getName() + "\n");
1763 }
1764 input.add("filter tree:" + depth + "\n");
1765 input.add("done\n");
1766 input.add(PacketLineIn.end());
1767 ByteArrayInputStream recvStream =
1768 uploadPackV2(
1769 (UploadPack up) -> {up.setRequestPolicy(RequestPolicy.ANY);},
1770 input.toArray(new String[0]));
1771 PacketLineIn pckIn = new PacketLineIn(recvStream);
1772 assertThat(pckIn.readString(), is("packfile"));
1773 parsePack(recvStream);
1774 }
1775
1776 @Test
1777 public void testV2FetchFilterTreeDepth0() throws Exception {
1778 DeepTreePreparator preparator = new DeepTreePreparator();
1779 remote.update("master", preparator.commit);
1780
1781 uploadV2WithTreeDepthFilter(0, preparator.commit.toObjectId());
1782
1783 assertFalse(client.getObjectDatabase()
1784 .has(preparator.rootTree.toObjectId()));
1785 assertFalse(client.getObjectDatabase()
1786 .has(preparator.subtree.toObjectId()));
1787 assertFalse(client.getObjectDatabase()
1788 .has(preparator.blobLowDepth.toObjectId()));
1789 assertFalse(client.getObjectDatabase()
1790 .has(preparator.blobHighDepth.toObjectId()));
1791 assertEquals(1, stats.getTreesTraversed());
1792 }
1793
1794 @Test
1795 public void testV2FetchFilterTreeDepth1_serverHasBitmap() throws Exception {
1796 DeepTreePreparator preparator = new DeepTreePreparator();
1797 remote.update("master", preparator.commit);
1798
1799
1800
1801 generateBitmaps(server);
1802
1803 uploadV2WithTreeDepthFilter(1, preparator.commit.toObjectId());
1804
1805 assertTrue(client.getObjectDatabase()
1806 .has(preparator.rootTree.toObjectId()));
1807 assertFalse(client.getObjectDatabase()
1808 .has(preparator.subtree.toObjectId()));
1809 assertFalse(client.getObjectDatabase()
1810 .has(preparator.blobLowDepth.toObjectId()));
1811 assertFalse(client.getObjectDatabase()
1812 .has(preparator.blobHighDepth.toObjectId()));
1813 assertEquals(1, stats.getTreesTraversed());
1814 }
1815
1816 @Test
1817 public void testV2FetchFilterTreeDepth2() throws Exception {
1818 DeepTreePreparator preparator = new DeepTreePreparator();
1819 remote.update("master", preparator.commit);
1820
1821 uploadV2WithTreeDepthFilter(2, preparator.commit.toObjectId());
1822
1823 assertTrue(client.getObjectDatabase()
1824 .has(preparator.rootTree.toObjectId()));
1825 assertTrue(client.getObjectDatabase()
1826 .has(preparator.subtree.toObjectId()));
1827 assertTrue(client.getObjectDatabase()
1828 .has(preparator.blobLowDepth.toObjectId()));
1829 assertFalse(client.getObjectDatabase()
1830 .has(preparator.blobHighDepth.toObjectId()));
1831 assertEquals(2, stats.getTreesTraversed());
1832 }
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842 class RepeatedSubtreePreparator {
1843 RevBlob foo = remote.blob("foo");
1844 RevTree subtree3 = remote.tree(remote.file("foo", foo));
1845 RevTree subtree2 = (new TreeBuilder() {
1846 @Override
1847 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1848 dcBuilder.addTree(new byte[] {'b'}, DirCacheEntry.STAGE_0,
1849 remote.getRevWalk().getObjectReader(), subtree3);
1850 }
1851 }).build();
1852 RevTree subtree1 = (new TreeBuilder() {
1853 @Override
1854 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1855 dcBuilder.addTree(new byte[] {'x'}, DirCacheEntry.STAGE_0,
1856 remote.getRevWalk().getObjectReader(), subtree2);
1857 }
1858 }).build();
1859 RevTree rootTree = (new TreeBuilder() {
1860 @Override
1861 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1862 dcBuilder.addTree(new byte[] {'a'}, DirCacheEntry.STAGE_0,
1863 remote.getRevWalk().getObjectReader(), subtree1);
1864 dcBuilder.addTree(new byte[] {'x'}, DirCacheEntry.STAGE_0,
1865 remote.getRevWalk().getObjectReader(), subtree2);
1866 }
1867 }).build();
1868 RevCommit commit = remote.commit(rootTree);
1869
1870 RepeatedSubtreePreparator() throws Exception {}
1871 }
1872
1873 @Test
1874 public void testV2FetchFilterTreeDepth_iterateOverTreeAtTwoLevels()
1875 throws Exception {
1876
1877
1878
1879 RepeatedSubtreePreparator preparator = new RepeatedSubtreePreparator();
1880 remote.update("master", preparator.commit);
1881
1882 uploadV2WithTreeDepthFilter(4, preparator.commit.toObjectId());
1883
1884 assertTrue(client.getObjectDatabase()
1885 .has(preparator.foo.toObjectId()));
1886 }
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904 class RepeatedSubtreeAtSameLevelPreparator {
1905 RevBlob foo = remote.blob("foo");
1906
1907
1908 RevTree subtree1 = remote.tree(remote.file("foo", foo));
1909
1910
1911 RevTree subtree2 = (new TreeBuilder() {
1912 @Override
1913 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1914 dcBuilder.addTree(new byte[] {'b'}, DirCacheEntry.STAGE_0,
1915 remote.getRevWalk().getObjectReader(), subtree1);
1916 }
1917 }).build();
1918
1919
1920 RevTree subtree3 = (new TreeBuilder() {
1921 @Override
1922 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1923 dcBuilder.addTree(new byte[] {'x'}, DirCacheEntry.STAGE_0,
1924 remote.getRevWalk().getObjectReader(), subtree2);
1925 }
1926 }).build();
1927
1928 RevBlob baz = remote.blob("baz");
1929
1930
1931 RevTree subtree4 = remote.tree(remote.file("baz", baz));
1932
1933
1934 RevTree subtree5 = (new TreeBuilder() {
1935 @Override
1936 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1937 dcBuilder.addTree(new byte[] {'c'}, DirCacheEntry.STAGE_0,
1938 remote.getRevWalk().getObjectReader(), subtree4);
1939 }
1940 }).build();
1941
1942
1943 RevTree subtree6 = (new TreeBuilder() {
1944 @Override
1945 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1946 dcBuilder.addTree(new byte[] {'u'}, DirCacheEntry.STAGE_0,
1947 remote.getRevWalk().getObjectReader(), subtree5);
1948 }
1949 }).build();
1950
1951
1952 RevTree subtree7 = (new TreeBuilder() {
1953 @Override
1954 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1955 dcBuilder.addTree(new byte[] {'v'}, DirCacheEntry.STAGE_0,
1956 remote.getRevWalk().getObjectReader(), subtree5);
1957 }
1958 }).build();
1959
1960 RevTree rootTree = (new TreeBuilder() {
1961 @Override
1962 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1963 dcBuilder.addTree(new byte[] {'a'}, DirCacheEntry.STAGE_0,
1964 remote.getRevWalk().getObjectReader(), subtree3);
1965 dcBuilder.addTree(new byte[] {'b'}, DirCacheEntry.STAGE_0,
1966 remote.getRevWalk().getObjectReader(), subtree6);
1967 dcBuilder.addTree(new byte[] {'y'}, DirCacheEntry.STAGE_0,
1968 remote.getRevWalk().getObjectReader(), subtree3);
1969 dcBuilder.addTree(new byte[] {'z'}, DirCacheEntry.STAGE_0,
1970 remote.getRevWalk().getObjectReader(), subtree7);
1971 }
1972 }).build();
1973 RevCommit commit = remote.commit(rootTree);
1974
1975 RepeatedSubtreeAtSameLevelPreparator() throws Exception {}
1976 }
1977
1978 @Test
1979 public void testV2FetchFilterTreeDepth_repeatTreeAtSameLevelIncludeFile()
1980 throws Exception {
1981 RepeatedSubtreeAtSameLevelPreparator preparator =
1982 new RepeatedSubtreeAtSameLevelPreparator();
1983 remote.update("master", preparator.commit);
1984
1985 uploadV2WithTreeDepthFilter(5, preparator.commit.toObjectId());
1986
1987 assertTrue(client.getObjectDatabase()
1988 .has(preparator.foo.toObjectId()));
1989 assertTrue(client.getObjectDatabase()
1990 .has(preparator.baz.toObjectId()));
1991 assertEquals(8, stats.getTreesTraversed());
1992 }
1993
1994 @Test
1995 public void testV2FetchFilterTreeDepth_repeatTreeAtSameLevelExcludeFile()
1996 throws Exception {
1997 RepeatedSubtreeAtSameLevelPreparator preparator =
1998 new RepeatedSubtreeAtSameLevelPreparator();
1999 remote.update("master", preparator.commit);
2000
2001 uploadV2WithTreeDepthFilter(4, preparator.commit.toObjectId());
2002
2003 assertFalse(client.getObjectDatabase()
2004 .has(preparator.foo.toObjectId()));
2005 assertFalse(client.getObjectDatabase()
2006 .has(preparator.baz.toObjectId()));
2007 assertEquals(8, stats.getTreesTraversed());
2008 }
2009
2010 @Test
2011 public void testWantFilteredObject() throws Exception {
2012 RepeatedSubtreePreparator preparator = new RepeatedSubtreePreparator();
2013 remote.update("master", preparator.commit);
2014
2015
2016
2017 uploadV2WithTreeDepthFilter(
2018 3,
2019 preparator.commit.toObjectId(),
2020 preparator.foo.toObjectId());
2021 assertTrue(client.getObjectDatabase()
2022 .has(preparator.foo.toObjectId()));
2023
2024 client = newRepo("client");
2025
2026
2027 uploadV2WithTreeDepthFilter(
2028 2,
2029 preparator.commit.toObjectId(),
2030 preparator.subtree3.toObjectId());
2031 assertTrue(client.getObjectDatabase()
2032 .has(preparator.foo.toObjectId()));
2033 assertTrue(client.getObjectDatabase()
2034 .has(preparator.subtree3.toObjectId()));
2035 }
2036
2037 private void checkV2FetchWhenNotAllowed(String fetchLine, String expectedMessage)
2038 throws Exception {
2039 RevCommit commit = remote.commit().message("0").create();
2040 remote.update("master", commit);
2041
2042 UploadPackInternalServerErrorException e = assertThrows(
2043 UploadPackInternalServerErrorException.class,
2044 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
2045 "want " + commit.toObjectId().getName() + "\n",
2046 fetchLine, "done\n", PacketLineIn.end()));
2047 assertThat(e.getCause().getMessage(),
2048 containsString(expectedMessage));
2049 }
2050
2051 @Test
2052 public void testV2FetchFilterWhenNotAllowed() throws Exception {
2053 checkV2FetchWhenNotAllowed(
2054 "filter blob:limit=5\n",
2055 "unexpected filter blob:limit=5");
2056 }
2057
2058 @Test
2059 public void testV2FetchWantRefIfNotAllowed() throws Exception {
2060 checkV2FetchWhenNotAllowed(
2061 "want-ref refs/heads/one\n",
2062 "unexpected want-ref refs/heads/one");
2063 }
2064
2065 @Test
2066 public void testV2FetchSidebandAllIfNotAllowed() throws Exception {
2067 checkV2FetchWhenNotAllowed(
2068 "sideband-all\n",
2069 "unexpected sideband-all");
2070 }
2071
2072 @Test
2073 public void testV2FetchWantRef() throws Exception {
2074 RevCommit one = remote.commit().message("1").create();
2075 RevCommit two = remote.commit().message("2").create();
2076 RevCommit three = remote.commit().message("3").create();
2077 remote.update("one", one);
2078 remote.update("two", two);
2079 remote.update("three", three);
2080
2081 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
2082
2083 ByteArrayInputStream recvStream = uploadPackV2(
2084 "command=fetch\n",
2085 PacketLineIn.delimiter(),
2086 "want-ref refs/heads/one\n",
2087 "want-ref refs/heads/two\n",
2088 "done\n",
2089 PacketLineIn.end());
2090 PacketLineIn pckIn = new PacketLineIn(recvStream);
2091 assertThat(pckIn.readString(), is("wanted-refs"));
2092 assertThat(
2093 Arrays.asList(pckIn.readString(), pckIn.readString()),
2094 hasItems(
2095 one.toObjectId().getName() + " refs/heads/one",
2096 two.toObjectId().getName() + " refs/heads/two"));
2097 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2098 assertThat(pckIn.readString(), is("packfile"));
2099 parsePack(recvStream);
2100
2101 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
2102 assertTrue(client.getObjectDatabase().has(two.toObjectId()));
2103 assertFalse(client.getObjectDatabase().has(three.toObjectId()));
2104 }
2105
2106 @Test
2107 public void testV2FetchBadWantRef() throws Exception {
2108 RevCommit one = remote.commit().message("1").create();
2109 remote.update("one", one);
2110
2111 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant",
2112 true);
2113
2114 UploadPackInternalServerErrorException e = assertThrows(
2115 UploadPackInternalServerErrorException.class,
2116 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
2117 "want-ref refs/heads/one\n",
2118 "want-ref refs/heads/nonExistentRef\n", "done\n",
2119 PacketLineIn.end()));
2120 assertThat(e.getCause().getMessage(),
2121 containsString("Invalid ref name: refs/heads/nonExistentRef"));
2122 }
2123
2124 @Test
2125 public void testV2FetchMixedWantRef() throws Exception {
2126 RevCommit one = remote.commit().message("1").create();
2127 RevCommit two = remote.commit().message("2").create();
2128 RevCommit three = remote.commit().message("3").create();
2129 remote.update("one", one);
2130 remote.update("two", two);
2131 remote.update("three", three);
2132
2133 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
2134
2135 ByteArrayInputStream recvStream = uploadPackV2(
2136 "command=fetch\n",
2137 PacketLineIn.delimiter(),
2138 "want-ref refs/heads/one\n",
2139 "want " + two.toObjectId().getName() + "\n",
2140 "done\n",
2141 PacketLineIn.end());
2142 PacketLineIn pckIn = new PacketLineIn(recvStream);
2143 assertThat(pckIn.readString(), is("wanted-refs"));
2144 assertThat(
2145 pckIn.readString(),
2146 is(one.toObjectId().getName() + " refs/heads/one"));
2147 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2148 assertThat(pckIn.readString(), is("packfile"));
2149 parsePack(recvStream);
2150
2151 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
2152 assertTrue(client.getObjectDatabase().has(two.toObjectId()));
2153 assertFalse(client.getObjectDatabase().has(three.toObjectId()));
2154 }
2155
2156 @Test
2157 public void testV2FetchWantRefWeAlreadyHave() throws Exception {
2158 RevCommit one = remote.commit().message("1").create();
2159 remote.update("one", one);
2160
2161 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
2162
2163 ByteArrayInputStream recvStream = uploadPackV2(
2164 "command=fetch\n",
2165 PacketLineIn.delimiter(),
2166 "want-ref refs/heads/one\n",
2167 "have " + one.toObjectId().getName(),
2168 "done\n",
2169 PacketLineIn.end());
2170 PacketLineIn pckIn = new PacketLineIn(recvStream);
2171
2172
2173
2174
2175 assertThat(pckIn.readString(), is("wanted-refs"));
2176 assertThat(
2177 pckIn.readString(),
2178 is(one.toObjectId().getName() + " refs/heads/one"));
2179 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2180
2181
2182 assertThat(pckIn.readString(), is("packfile"));
2183 parsePack(recvStream);
2184 assertFalse(client.getObjectDatabase().has(one.toObjectId()));
2185 }
2186
2187 @Test
2188 public void testV2FetchWantRefAndDeepen() throws Exception {
2189 RevCommit parent = remote.commit().message("parent").create();
2190 RevCommit child = remote.commit().message("x").parent(parent).create();
2191 remote.update("branch1", child);
2192
2193 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
2194
2195 ByteArrayInputStream recvStream = uploadPackV2(
2196 "command=fetch\n",
2197 PacketLineIn.delimiter(),
2198 "want-ref refs/heads/branch1\n",
2199 "deepen 1\n",
2200 "done\n",
2201 PacketLineIn.end());
2202 PacketLineIn pckIn = new PacketLineIn(recvStream);
2203
2204
2205 assertThat(pckIn.readString(), is("shallow-info"));
2206 assertThat(pckIn.readString(), is("shallow " + child.toObjectId().getName()));
2207 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2208 assertThat(pckIn.readString(), is("wanted-refs"));
2209 assertThat(pckIn.readString(), is(child.toObjectId().getName() + " refs/heads/branch1"));
2210 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2211 assertThat(pckIn.readString(), is("packfile"));
2212 parsePack(recvStream);
2213 assertTrue(client.getObjectDatabase().has(child.toObjectId()));
2214 assertFalse(client.getObjectDatabase().has(parent.toObjectId()));
2215 }
2216
2217 @Test
2218 public void testV2FetchMissingShallow() throws Exception {
2219 RevCommit one = remote.commit().message("1").create();
2220 RevCommit two = remote.commit().message("2").parent(one).create();
2221 RevCommit three = remote.commit().message("3").parent(two).create();
2222 remote.update("three", three);
2223
2224 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant",
2225 true);
2226
2227 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
2228 PacketLineIn.delimiter(),
2229 "want-ref refs/heads/three\n",
2230 "deepen 3",
2231 "shallow 0123012301230123012301230123012301230123",
2232 "shallow " + two.getName() + '\n',
2233 "done\n",
2234 PacketLineIn.end());
2235 PacketLineIn pckIn = new PacketLineIn(recvStream);
2236
2237 assertThat(pckIn.readString(), is("shallow-info"));
2238 assertThat(pckIn.readString(),
2239 is("shallow " + one.toObjectId().getName()));
2240 assertThat(pckIn.readString(),
2241 is("unshallow " + two.toObjectId().getName()));
2242 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2243 assertThat(pckIn.readString(), is("wanted-refs"));
2244 assertThat(pckIn.readString(),
2245 is(three.toObjectId().getName() + " refs/heads/three"));
2246 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2247 assertThat(pckIn.readString(), is("packfile"));
2248 parsePack(recvStream);
2249
2250 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
2251 assertTrue(client.getObjectDatabase().has(two.toObjectId()));
2252 assertTrue(client.getObjectDatabase().has(three.toObjectId()));
2253 }
2254
2255 @Test
2256 public void testV2FetchSidebandAllNoPackfile() throws Exception {
2257 RevCommit fooParent = remote.commit().message("x").create();
2258 RevCommit fooChild = remote.commit().message("x").parent(fooParent).create();
2259 RevCommit barParent = remote.commit().message("y").create();
2260 RevCommit barChild = remote.commit().message("y").parent(barParent).create();
2261 remote.update("branch1", fooChild);
2262 remote.update("branch2", barChild);
2263
2264 server.getConfig().setBoolean("uploadpack", null, "allowsidebandall", true);
2265
2266 ByteArrayInputStream recvStream = uploadPackV2(
2267 "command=fetch\n",
2268 PacketLineIn.delimiter(),
2269 "sideband-all\n",
2270 "want " + fooChild.toObjectId().getName() + "\n",
2271 "want " + barChild.toObjectId().getName() + "\n",
2272 "have " + fooParent.toObjectId().getName() + "\n",
2273 PacketLineIn.end());
2274 PacketLineIn pckIn = new PacketLineIn(recvStream);
2275
2276 assertThat(pckIn.readString(), is("\001acknowledgments"));
2277 assertThat(pckIn.readString(), is("\001ACK " + fooParent.getName()));
2278 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
2279 }
2280
2281 @Test
2282 public void testV2FetchSidebandAllPackfile() throws Exception {
2283 RevCommit commit = remote.commit().message("x").create();
2284 remote.update("master", commit);
2285
2286 server.getConfig().setBoolean("uploadpack", null, "allowsidebandall", true);
2287
2288 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
2289 PacketLineIn.delimiter(),
2290 "want " + commit.getName() + "\n",
2291 "sideband-all\n",
2292 "done\n",
2293 PacketLineIn.end());
2294 PacketLineIn pckIn = new PacketLineIn(recvStream);
2295
2296 String s;
2297
2298
2299
2300 for (s = pckIn.readString(); s.startsWith("\002"); s = pckIn.readString()) {
2301
2302 }
2303 assertThat(s, is("\001packfile"));
2304 parsePack(recvStream);
2305 }
2306
2307 @Test
2308 public void testV2FetchPackfileUris() throws Exception {
2309
2310 RevCommit commit = remote.commit().message("x").create();
2311 remote.update("master", commit);
2312 generateBitmaps(server);
2313
2314
2315 RevCommit commit2 = remote.commit().message("x").parent(commit).create();
2316 remote.update("master", commit2);
2317
2318 server.getConfig().setBoolean("uploadpack", null, "allowsidebandall", true);
2319
2320 ByteArrayInputStream recvStream = uploadPackV2(
2321 (UploadPack up) -> {
2322 up.setCachedPackUriProvider(new CachedPackUriProvider() {
2323 @Override
2324 public PackInfo getInfo(CachedPack pack,
2325 Collection<String> protocolsSupported)
2326 throws IOException {
2327 assertThat(protocolsSupported, hasItems("https"));
2328 if (!protocolsSupported.contains("https"))
2329 return null;
2330 return new PackInfo("myhash", "myuri", 100);
2331 }
2332
2333 });
2334 },
2335 "command=fetch\n",
2336 PacketLineIn.delimiter(),
2337 "want " + commit2.getName() + "\n",
2338 "sideband-all\n",
2339 "packfile-uris https\n",
2340 "done\n",
2341 PacketLineIn.end());
2342 PacketLineIn pckIn = new PacketLineIn(recvStream);
2343
2344 String s;
2345
2346 for (s = pckIn.readString(); s.startsWith("\002"); s = pckIn.readString()) {
2347
2348 }
2349 assertThat(s, is("\001packfile-uris"));
2350 assertThat(pckIn.readString(), is("\001myhash myuri"));
2351 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2352 assertThat(pckIn.readString(), is("\001packfile"));
2353 parsePack(recvStream);
2354
2355 assertFalse(client.getObjectDatabase().has(commit.toObjectId()));
2356 assertTrue(client.getObjectDatabase().has(commit2.toObjectId()));
2357 }
2358
2359 @Test
2360 public void testGetPeerAgentProtocolV0() throws Exception {
2361 RevCommit one = remote.commit().message("1").create();
2362 remote.update("one", one);
2363
2364 UploadPack up = new UploadPack(server);
2365 ByteArrayInputStream send = linesAsInputStream(
2366 "want " + one.getName() + " agent=JGit-test/1.2.3\n",
2367 PacketLineIn.end(),
2368 "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n");
2369
2370 ByteArrayOutputStream recv = new ByteArrayOutputStream();
2371 up.upload(send, recv, null);
2372
2373 assertEquals(up.getPeerUserAgent(), "JGit-test/1.2.3");
2374 }
2375
2376 @Test
2377 public void testGetPeerAgentProtocolV2() throws Exception {
2378 server.getConfig().setString(ConfigConstants.CONFIG_PROTOCOL_SECTION,
2379 null, ConfigConstants.CONFIG_KEY_VERSION,
2380 TransferConfig.ProtocolVersion.V2.version());
2381
2382 RevCommit one = remote.commit().message("1").create();
2383 remote.update("one", one);
2384
2385 UploadPack up = new UploadPack(server);
2386 up.setExtraParameters(Sets.of("version=2"));
2387
2388 ByteArrayInputStream send = linesAsInputStream(
2389 "command=fetch\n", "agent=JGit-test/1.2.4\n",
2390 PacketLineIn.delimiter(), "want " + one.getName() + "\n",
2391 "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n",
2392 PacketLineIn.end());
2393
2394 ByteArrayOutputStream recv = new ByteArrayOutputStream();
2395 up.upload(send, recv, null);
2396
2397 assertEquals(up.getPeerUserAgent(), "JGit-test/1.2.4");
2398 }
2399
2400 private static class RejectAllRefFilter implements RefFilter {
2401 @Override
2402 public Map<String, Ref> filter(Map<String, Ref> refs) {
2403 return new HashMap<>();
2404 }
2405 }
2406
2407 @Test
2408 public void testSingleBranchCloneTagChain() throws Exception {
2409 RevBlob blob0 = remote.blob("Initial content of first file");
2410 RevBlob blob1 = remote.blob("Second file content");
2411 RevCommit commit0 = remote
2412 .commit(remote.tree(remote.file("prvni.txt", blob0)));
2413 RevCommit commit1 = remote
2414 .commit(remote.tree(remote.file("druhy.txt", blob1)), commit0);
2415 remote.update("master", commit1);
2416
2417 RevTag heavyTag1 = remote.tag("commitTagRing", commit0);
2418 remote.getRevWalk().parseHeaders(heavyTag1);
2419 RevTag heavyTag2 = remote.tag("middleTagRing", heavyTag1);
2420 remote.lightweightTag("refTagRing", heavyTag2);
2421
2422 UploadPack uploadPack = new UploadPack(remote.getRepository());
2423
2424 ByteArrayOutputStream cli = new ByteArrayOutputStream();
2425 PacketLineOut clientWant = new PacketLineOut(cli);
2426 clientWant.writeString("want " + commit1.name()
2427 + " multi_ack_detailed include-tag thin-pack ofs-delta agent=tempo/pflaska");
2428 clientWant.end();
2429 clientWant.writeString("done\n");
2430
2431 try (ByteArrayOutputStream serverResponse = new ByteArrayOutputStream()) {
2432
2433 uploadPack.setPreUploadHook(new PreUploadHook() {
2434 @Override
2435 public void onBeginNegotiateRound(UploadPack up,
2436 Collection<? extends ObjectId> wants, int cntOffered)
2437 throws ServiceMayNotContinueException {
2438
2439 }
2440
2441 @Override
2442 public void onEndNegotiateRound(UploadPack up,
2443 Collection<? extends ObjectId> wants, int cntCommon,
2444 int cntNotFound, boolean ready)
2445 throws ServiceMayNotContinueException {
2446
2447 }
2448
2449 @Override
2450 public void onSendPack(UploadPack up,
2451 Collection<? extends ObjectId> wants,
2452 Collection<? extends ObjectId> haves)
2453 throws ServiceMayNotContinueException {
2454
2455 serverResponse.reset();
2456 }
2457 });
2458 uploadPack.upload(new ByteArrayInputStream(cli.toByteArray()),
2459 serverResponse, System.err);
2460 InputStream packReceived = new ByteArrayInputStream(
2461 serverResponse.toByteArray());
2462 PackLock lock = null;
2463 try (ObjectInserter ins = client.newObjectInserter()) {
2464 PackParser parser = ins.newPackParser(packReceived);
2465 parser.setAllowThin(true);
2466 parser.setLockMessage("receive-tag-chain");
2467 ProgressMonitor mlc = NullProgressMonitor.INSTANCE;
2468 lock = parser.parse(mlc, mlc);
2469 ins.flush();
2470 } finally {
2471 if (lock != null) {
2472 lock.unlock();
2473 }
2474 }
2475 InMemoryRepository.MemObjDatabase objDb = client
2476 .getObjectDatabase();
2477 assertTrue(objDb.has(blob0.toObjectId()));
2478 assertTrue(objDb.has(blob1.toObjectId()));
2479 assertTrue(objDb.has(commit0.toObjectId()));
2480 assertTrue(objDb.has(commit1.toObjectId()));
2481 assertTrue(objDb.has(heavyTag1.toObjectId()));
2482 assertTrue(objDb.has(heavyTag2.toObjectId()));
2483 }
2484 }
2485
2486 @Test
2487 public void testSafeToClearRefsInFetchV0() throws Exception {
2488 server =
2489 new RefCallsCountingRepository(
2490 new DfsRepositoryDescription("server"));
2491 remote = new TestRepository<>(server);
2492 RevCommit one = remote.commit().message("1").create();
2493 remote.update("one", one);
2494 testProtocol = new TestProtocol<>((Object req, Repository db) -> {
2495 UploadPack up = new UploadPack(db);
2496 return up;
2497 }, null);
2498 uri = testProtocol.register(ctx, server);
2499 try (Transport tn = testProtocol.open(uri, client, "server")) {
2500 tn.fetch(NullProgressMonitor.INSTANCE,
2501 Collections.singletonList(new RefSpec(one.name())));
2502 }
2503 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
2504 assertEquals(1, ((RefCallsCountingRepository)server).numRefCalls());
2505 }
2506
2507 @Test
2508 public void testSafeToClearRefsInFetchV2() throws Exception {
2509 server =
2510 new RefCallsCountingRepository(
2511 new DfsRepositoryDescription("server"));
2512 remote = new TestRepository<>(server);
2513 RevCommit one = remote.commit().message("1").create();
2514 RevCommit two = remote.commit().message("2").create();
2515 remote.update("one", one);
2516 remote.update("two", two);
2517 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
2518 ByteArrayInputStream recvStream = uploadPackV2(
2519 "command=fetch\n",
2520 PacketLineIn.delimiter(),
2521 "want-ref refs/heads/one\n",
2522 "want-ref refs/heads/two\n",
2523 "done\n",
2524 PacketLineIn.end());
2525 PacketLineIn pckIn = new PacketLineIn(recvStream);
2526 assertThat(pckIn.readString(), is("wanted-refs"));
2527 assertThat(
2528 Arrays.asList(pckIn.readString(), pckIn.readString()),
2529 hasItems(
2530 one.toObjectId().getName() + " refs/heads/one",
2531 two.toObjectId().getName() + " refs/heads/two"));
2532 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2533 assertThat(pckIn.readString(), is("packfile"));
2534 parsePack(recvStream);
2535 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
2536 assertEquals(1, ((RefCallsCountingRepository)server).numRefCalls());
2537 }
2538
2539 @Test
2540 public void testNotAdvertisedWantsV1Fetch() throws Exception {
2541 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
2542
2543 RevBlob parentBlob = remote.blob(commonInBlob + "a");
2544 RevCommit parent = remote
2545 .commit(remote.tree(remote.file("foo", parentBlob)));
2546 RevBlob childBlob = remote.blob(commonInBlob + "b");
2547 RevCommit child = remote
2548 .commit(remote.tree(remote.file("foo", childBlob)), parent);
2549 remote.update("branch1", child);
2550
2551 uploadPackV1("want " + child.toObjectId().getName() + "\n",
2552 PacketLineIn.end(),
2553 "have " + parent.toObjectId().getName() + "\n",
2554 "done\n", PacketLineIn.end());
2555
2556 assertEquals(0, stats.getNotAdvertisedWants());
2557 }
2558
2559 @Test
2560 public void testNotAdvertisedWantsV1FetchRequestPolicyReachableCommit() throws Exception {
2561 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
2562
2563 RevBlob parentBlob = remote.blob(commonInBlob + "a");
2564 RevCommit parent = remote
2565 .commit(remote.tree(remote.file("foo", parentBlob)));
2566 RevBlob childBlob = remote.blob(commonInBlob + "b");
2567 RevCommit child = remote
2568 .commit(remote.tree(remote.file("foo", childBlob)), parent);
2569
2570 remote.update("branch1", child);
2571
2572 uploadPackV1((UploadPack up) -> {up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);},
2573 "want " + parent.toObjectId().getName() + "\n",
2574 PacketLineIn.end(),
2575 "done\n", PacketLineIn.end());
2576
2577 assertEquals(1, stats.getNotAdvertisedWants());
2578 }
2579
2580 @Test
2581 public void testNotAdvertisedWantsV2FetchThinPack() throws Exception {
2582 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
2583
2584 RevBlob parentBlob = remote.blob(commonInBlob + "a");
2585 RevCommit parent = remote
2586 .commit(remote.tree(remote.file("foo", parentBlob)));
2587 RevBlob childBlob = remote.blob(commonInBlob + "b");
2588 RevCommit child = remote
2589 .commit(remote.tree(remote.file("foo", childBlob)), parent);
2590 remote.update("branch1", child);
2591
2592 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
2593 PacketLineIn.delimiter(),
2594 "want " + child.toObjectId().getName() + "\n",
2595 "have " + parent.toObjectId().getName() + "\n", "thin-pack\n",
2596 "done\n", PacketLineIn.end());
2597 PacketLineIn pckIn = new PacketLineIn(recvStream);
2598
2599 assertThat(pckIn.readString(), is("packfile"));
2600
2601 assertEquals(0, stats.getNotAdvertisedWants());
2602 }
2603
2604 @Test
2605 public void testNotAdvertisedWantsV2FetchRequestPolicyReachableCommit() throws Exception {
2606 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
2607
2608 RevBlob parentBlob = remote.blob(commonInBlob + "a");
2609 RevCommit parent = remote
2610 .commit(remote.tree(remote.file("foo", parentBlob)));
2611 RevBlob childBlob = remote.blob(commonInBlob + "b");
2612 RevCommit child = remote
2613 .commit(remote.tree(remote.file("foo", childBlob)), parent);
2614
2615 remote.update("branch1", child);
2616
2617 uploadPackV2((UploadPack up) -> {up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);},
2618 "command=fetch\n",
2619 PacketLineIn.delimiter(),
2620 "want " + parent.toObjectId().getName() + "\n", "thin-pack\n",
2621 "done\n", PacketLineIn.end());
2622
2623 assertEquals(1, stats.getNotAdvertisedWants());
2624 }
2625
2626 private class RefCallsCountingRepository extends InMemoryRepository {
2627 private final InMemoryRepository.MemRefDatabase refdb;
2628 private int numRefCalls;
2629
2630 public RefCallsCountingRepository(DfsRepositoryDescription repoDesc) {
2631 super(repoDesc);
2632 refdb = new InMemoryRepository.MemRefDatabase() {
2633 @Override
2634 public List<Ref> getRefs() throws IOException {
2635 numRefCalls++;
2636 return super.getRefs();
2637 }
2638 };
2639 }
2640
2641 public int numRefCalls() {
2642 return numRefCalls;
2643 }
2644
2645 @Override
2646 public RefDatabase getRefDatabase() {
2647 return refdb;
2648 }
2649 }
2650
2651 @Test
2652 public void testObjectInfo() throws Exception {
2653 server.getConfig().setBoolean("uploadpack", null, "advertiseobjectinfo",
2654 true);
2655
2656 RevBlob blob1 = remote.blob("foobar");
2657 RevBlob blob2 = remote.blob("fooba");
2658 RevTree tree = remote.tree(remote.file("1", blob1),
2659 remote.file("2", blob2));
2660 RevCommit commit = remote.commit(tree);
2661 remote.update("master", commit);
2662
2663 TestV2Hook hook = new TestV2Hook();
2664 ByteArrayInputStream recvStream = uploadPackV2((UploadPack up) -> {
2665 up.setProtocolV2Hook(hook);
2666 }, "command=object-info\n", "size",
2667 "oid " + ObjectId.toString(blob1.getId()),
2668 "oid " + ObjectId.toString(blob2.getId()), PacketLineIn.end());
2669 PacketLineIn pckIn = new PacketLineIn(recvStream);
2670
2671 assertThat(hook.objectInfoRequest, notNullValue());
2672 assertThat(pckIn.readString(), is("size"));
2673 assertThat(pckIn.readString(),
2674 is(ObjectId.toString(blob1.getId()) + " 6"));
2675 assertThat(pckIn.readString(),
2676 is(ObjectId.toString(blob2.getId()) + " 5"));
2677 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
2678 }
2679
2680 @Test
2681 public void testObjectInfo_invalidOid() throws Exception {
2682 server.getConfig().setBoolean("uploadpack", null, "advertiseobjectinfo",
2683 true);
2684
2685 assertThrows(UploadPackInternalServerErrorException.class,
2686 () -> uploadPackV2("command=object-info\n", "size",
2687 "oid invalid",
2688 PacketLineIn.end()));
2689 }
2690 }