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 testV2FetchServerStopsNegotiationForRefWithoutParents()
1111 throws Exception {
1112 RevCommit fooCommit = remote.commit().message("x").create();
1113 RevCommit barCommit = remote.commit().message("y").create();
1114 remote.update("refs/changes/01/1/1", fooCommit);
1115 remote.update("refs/changes/02/2/1", barCommit);
1116
1117 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
1118 PacketLineIn.delimiter(),
1119 "want " + fooCommit.toObjectId().getName() + "\n",
1120 "have " + barCommit.toObjectId().getName() + "\n",
1121 PacketLineIn.end());
1122 PacketLineIn pckIn = new PacketLineIn(recvStream);
1123
1124 assertThat(pckIn.readString(), is("acknowledgments"));
1125 assertThat(pckIn.readString(),
1126 is("ACK " + barCommit.toObjectId().getName()));
1127 assertThat(pckIn.readString(), is("ready"));
1128 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1129 assertThat(pckIn.readString(), is("packfile"));
1130 parsePack(recvStream);
1131 assertTrue(client.getObjectDatabase().has(fooCommit.toObjectId()));
1132 }
1133
1134 @Test
1135 public void testV2FetchServerDoesNotStopNegotiationWhenOneRefWithoutParentAndOtherWithParents()
1136 throws Exception {
1137 RevCommit fooCommit = remote.commit().message("x").create();
1138 RevCommit barParent = remote.commit().message("y").create();
1139 RevCommit barChild = remote.commit().message("y").parent(barParent)
1140 .create();
1141 RevCommit fooBarParent = remote.commit().message("z").create();
1142 RevCommit fooBarChild = remote.commit().message("y")
1143 .parent(fooBarParent)
1144 .create();
1145 remote.update("refs/changes/01/1/1", fooCommit);
1146 remote.update("refs/changes/02/2/1", barChild);
1147 remote.update("refs/changes/03/3/1", fooBarChild);
1148
1149 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
1150 PacketLineIn.delimiter(),
1151 "want " + fooCommit.toObjectId().getName() + "\n",
1152 "want " + barChild.toObjectId().getName() + "\n",
1153 "want " + fooBarChild.toObjectId().getName() + "\n",
1154 "have " + fooBarParent.toObjectId().getName() + "\n",
1155 PacketLineIn.end());
1156 PacketLineIn pckIn = new PacketLineIn(recvStream);
1157
1158 assertThat(pckIn.readString(), is("acknowledgments"));
1159 assertThat(pckIn.readString(),
1160 is("ACK " + fooBarParent.toObjectId().getName()));
1161 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
1162 }
1163
1164 @Test
1165 public void testV2FetchThinPack() throws Exception {
1166 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
1167
1168 RevBlob parentBlob = remote.blob(commonInBlob + "a");
1169 RevCommit parent = remote
1170 .commit(remote.tree(remote.file("foo", parentBlob)));
1171 RevBlob childBlob = remote.blob(commonInBlob + "b");
1172 RevCommit child = remote
1173 .commit(remote.tree(remote.file("foo", childBlob)), parent);
1174 remote.update("branch1", child);
1175
1176
1177 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
1178 PacketLineIn.delimiter(),
1179 "want " + child.toObjectId().getName() + "\n",
1180 "have " + parent.toObjectId().getName() + "\n", "thin-pack\n",
1181 "done\n", PacketLineIn.end());
1182 PacketLineIn pckIn = new PacketLineIn(recvStream);
1183
1184 assertThat(pckIn.readString(), is("packfile"));
1185
1186
1187
1188 IOException e = assertThrows(IOException.class,
1189 () -> parsePack(recvStream));
1190 assertThat(e.getMessage(),
1191 containsString("pack has unresolved deltas"));
1192 }
1193
1194 @Test
1195 public void testV2FetchNoProgress() throws Exception {
1196 RevCommit commit = remote.commit().message("x").create();
1197 remote.update("branch1", commit);
1198
1199
1200 StringWriter sw = new StringWriter();
1201 ByteArrayInputStream recvStream = uploadPackV2(
1202 "command=fetch\n",
1203 PacketLineIn.delimiter(),
1204 "want " + commit.toObjectId().getName() + "\n",
1205 "done\n",
1206 PacketLineIn.end());
1207 PacketLineIn pckIn = new PacketLineIn(recvStream);
1208 assertThat(pckIn.readString(), is("packfile"));
1209 parsePack(recvStream, new TextProgressMonitor(sw));
1210 assertFalse(sw.toString().isEmpty());
1211
1212
1213 sw = new StringWriter();
1214 recvStream = uploadPackV2(
1215 "command=fetch\n",
1216 PacketLineIn.delimiter(),
1217 "want " + commit.toObjectId().getName() + "\n",
1218 "no-progress\n",
1219 "done\n",
1220 PacketLineIn.end());
1221 pckIn = new PacketLineIn(recvStream);
1222 assertThat(pckIn.readString(), is("packfile"));
1223 parsePack(recvStream, new TextProgressMonitor(sw));
1224 assertTrue(sw.toString().isEmpty());
1225 }
1226
1227 @Test
1228 public void testV2FetchIncludeTag() throws Exception {
1229 RevCommit commit = remote.commit().message("x").create();
1230 RevTag tag = remote.tag("tag", commit);
1231 remote.update("branch1", commit);
1232 remote.update("refs/tags/tag", tag);
1233
1234
1235 ByteArrayInputStream recvStream = uploadPackV2(
1236 "command=fetch\n",
1237 PacketLineIn.delimiter(),
1238 "want " + commit.toObjectId().getName() + "\n",
1239 "done\n",
1240 PacketLineIn.end());
1241 PacketLineIn pckIn = new PacketLineIn(recvStream);
1242 assertThat(pckIn.readString(), is("packfile"));
1243 parsePack(recvStream);
1244 assertFalse(client.getObjectDatabase().has(tag.toObjectId()));
1245
1246
1247 recvStream = uploadPackV2(
1248 "command=fetch\n",
1249 PacketLineIn.delimiter(),
1250 "want " + commit.toObjectId().getName() + "\n",
1251 "include-tag\n",
1252 "done\n",
1253 PacketLineIn.end());
1254 pckIn = new PacketLineIn(recvStream);
1255 assertThat(pckIn.readString(), is("packfile"));
1256 parsePack(recvStream);
1257 assertTrue(client.getObjectDatabase().has(tag.toObjectId()));
1258 }
1259
1260 @Test
1261 public void testUploadNewBytes() throws Exception {
1262 String commonInBlob = "abcdefghijklmnopqrstuvwx";
1263
1264 RevBlob parentBlob = remote.blob(commonInBlob + "a");
1265 RevCommit parent = remote.commit(remote.tree(remote.file("foo", parentBlob)));
1266 RevBlob childBlob = remote.blob(commonInBlob + "b");
1267 RevCommit child = remote.commit(remote.tree(remote.file("foo", childBlob)), parent);
1268 remote.update("branch1", child);
1269
1270 ByteArrayInputStream recvStream = uploadPackV2(
1271 "command=fetch\n",
1272 PacketLineIn.delimiter(),
1273 "want " + child.toObjectId().getName() + "\n",
1274 "ofs-delta\n",
1275 "done\n",
1276 PacketLineIn.end());
1277 PacketLineIn pckIn = new PacketLineIn(recvStream);
1278 assertThat(pckIn.readString(), is("packfile"));
1279 ReceivedPackStatistics receivedStats = parsePack(recvStream);
1280 assertTrue(receivedStats.getNumBytesDuplicated() == 0);
1281 assertTrue(receivedStats.getNumObjectsDuplicated() == 0);
1282 }
1283
1284 @Test
1285 public void testUploadRedundantBytes() throws Exception {
1286 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
1287
1288 RevBlob parentBlob = remote.blob(commonInBlob + "a");
1289 RevCommit parent = remote.commit(remote.tree(remote.file("foo", parentBlob)));
1290 RevBlob childBlob = remote.blob(commonInBlob + "b");
1291 RevCommit child = remote.commit(remote.tree(remote.file("foo", childBlob)), parent);
1292 remote.update("branch1", child);
1293
1294 try (TestRepository<InMemoryRepository> local = new TestRepository<>(
1295 client)) {
1296 RevBlob localParentBlob = local.blob(commonInBlob + "a");
1297 RevCommit localParent = local
1298 .commit(local.tree(local.file("foo", localParentBlob)));
1299 RevBlob localChildBlob = local.blob(commonInBlob + "b");
1300 RevCommit localChild = local.commit(
1301 local.tree(local.file("foo", localChildBlob)), localParent);
1302 local.update("branch1", localChild);
1303 }
1304
1305 ByteArrayInputStream recvStream = uploadPackV2(
1306 "command=fetch\n",
1307 PacketLineIn.delimiter(),
1308 "want " + child.toObjectId().getName() + "\n",
1309 "ofs-delta\n",
1310 "done\n",
1311 PacketLineIn.end());
1312 PacketLineIn pckIn = new PacketLineIn(recvStream);
1313 assertThat(pckIn.readString(), is("packfile"));
1314 ReceivedPackStatistics receivedStats = parsePack(recvStream);
1315
1316 long sizeOfHeader = 12;
1317 long sizeOfTrailer = 20;
1318 long expectedSize = receivedStats.getNumBytesRead() - sizeOfHeader
1319 - sizeOfTrailer;
1320 assertTrue(receivedStats.getNumBytesDuplicated() == expectedSize);
1321 assertTrue(receivedStats.getNumObjectsDuplicated() == 6);
1322 }
1323
1324 @Test
1325 public void testV2FetchOfsDelta() throws Exception {
1326 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
1327
1328 RevBlob parentBlob = remote.blob(commonInBlob + "a");
1329 RevCommit parent = remote.commit(remote.tree(remote.file("foo", parentBlob)));
1330 RevBlob childBlob = remote.blob(commonInBlob + "b");
1331 RevCommit child = remote.commit(remote.tree(remote.file("foo", childBlob)), parent);
1332 remote.update("branch1", child);
1333
1334
1335 ByteArrayInputStream recvStream = uploadPackV2(
1336 "command=fetch\n",
1337 PacketLineIn.delimiter(),
1338 "want " + child.toObjectId().getName() + "\n",
1339 "done\n",
1340 PacketLineIn.end());
1341 PacketLineIn pckIn = new PacketLineIn(recvStream);
1342 assertThat(pckIn.readString(), is("packfile"));
1343 ReceivedPackStatistics receivedStats = parsePack(recvStream);
1344 assertTrue(receivedStats.getNumOfsDelta() == 0);
1345
1346
1347 recvStream = uploadPackV2(
1348 "command=fetch\n",
1349 PacketLineIn.delimiter(),
1350 "want " + child.toObjectId().getName() + "\n",
1351 "ofs-delta\n",
1352 "done\n",
1353 PacketLineIn.end());
1354 pckIn = new PacketLineIn(recvStream);
1355 assertThat(pckIn.readString(), is("packfile"));
1356 receivedStats = parsePack(recvStream);
1357 assertTrue(receivedStats.getNumOfsDelta() != 0);
1358 }
1359
1360 @Test
1361 public void testV2FetchShallow() throws Exception {
1362 RevCommit commonParent = remote.commit().message("parent").create();
1363 RevCommit fooChild = remote.commit().message("x").parent(commonParent).create();
1364 RevCommit barChild = remote.commit().message("y").parent(commonParent).create();
1365 remote.update("branch1", barChild);
1366
1367
1368
1369 ByteArrayInputStream recvStream = uploadPackV2(
1370 "command=fetch\n",
1371 PacketLineIn.delimiter(),
1372 "want " + barChild.toObjectId().getName() + "\n",
1373 "have " + fooChild.toObjectId().getName() + "\n",
1374 "done\n",
1375 PacketLineIn.end());
1376 PacketLineIn pckIn = new PacketLineIn(recvStream);
1377 assertThat(pckIn.readString(), is("packfile"));
1378 parsePack(recvStream);
1379 assertTrue(client.getObjectDatabase().has(barChild.toObjectId()));
1380 assertFalse(client.getObjectDatabase().has(commonParent.toObjectId()));
1381
1382
1383
1384 recvStream = uploadPackV2(
1385 "command=fetch\n",
1386 PacketLineIn.delimiter(),
1387 "want " + barChild.toObjectId().getName() + "\n",
1388 "have " + fooChild.toObjectId().getName() + "\n",
1389 "shallow " + fooChild.toObjectId().getName() + "\n",
1390 "done\n",
1391 PacketLineIn.end());
1392 pckIn = new PacketLineIn(recvStream);
1393 assertThat(pckIn.readString(), is("packfile"));
1394 parsePack(recvStream);
1395 assertTrue(client.getObjectDatabase().has(commonParent.toObjectId()));
1396 }
1397
1398 @Test
1399 public void testV2FetchDeepenAndDone() throws Exception {
1400 RevCommit parent = remote.commit().message("parent").create();
1401 RevCommit child = remote.commit().message("x").parent(parent).create();
1402 remote.update("branch1", child);
1403
1404
1405 ByteArrayInputStream recvStream = uploadPackV2(
1406 "command=fetch\n",
1407 PacketLineIn.delimiter(),
1408 "want " + child.toObjectId().getName() + "\n",
1409 "deepen 1\n",
1410 "done\n",
1411 PacketLineIn.end());
1412 PacketLineIn pckIn = new PacketLineIn(recvStream);
1413 assertThat(pckIn.readString(), is("shallow-info"));
1414 assertThat(pckIn.readString(), is("shallow " + child.toObjectId().getName()));
1415 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1416 assertThat(pckIn.readString(), is("packfile"));
1417 parsePack(recvStream);
1418 assertTrue(client.getObjectDatabase().has(child.toObjectId()));
1419 assertFalse(client.getObjectDatabase().has(parent.toObjectId()));
1420
1421
1422 recvStream = uploadPackV2(
1423 "command=fetch\n",
1424 PacketLineIn.delimiter(),
1425 "want " + child.toObjectId().getName() + "\n",
1426 "done\n",
1427 PacketLineIn.end());
1428 pckIn = new PacketLineIn(recvStream);
1429 assertThat(pckIn.readString(), is("packfile"));
1430 parsePack(recvStream);
1431 assertTrue(client.getObjectDatabase().has(parent.toObjectId()));
1432 }
1433
1434 @Test
1435 public void testV2FetchDeepenWithoutDone() throws Exception {
1436 RevCommit parent = remote.commit().message("parent").create();
1437 RevCommit child = remote.commit().message("x").parent(parent).create();
1438 remote.update("branch1", child);
1439
1440 ByteArrayInputStream recvStream = uploadPackV2(
1441 "command=fetch\n",
1442 PacketLineIn.delimiter(),
1443 "want " + child.toObjectId().getName() + "\n",
1444 "deepen 1\n",
1445 PacketLineIn.end());
1446 PacketLineIn pckIn = new PacketLineIn(recvStream);
1447
1448
1449
1450
1451 assertThat(pckIn.readString(), is("acknowledgments"));
1452 assertThat(pckIn.readString(), is("NAK"));
1453 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
1454 }
1455
1456 @Test
1457 public void testV2FetchShallowSince() throws Exception {
1458 PersonIdent person = new PersonIdent(remote.getRepository());
1459
1460 RevCommit beyondBoundary = remote.commit()
1461 .committer(new PersonIdent(person, 1510000000, 0)).create();
1462 RevCommit boundary = remote.commit().parent(beyondBoundary)
1463 .committer(new PersonIdent(person, 1520000000, 0)).create();
1464 RevCommit tooOld = remote.commit()
1465 .committer(new PersonIdent(person, 1500000000, 0)).create();
1466 RevCommit merge = remote.commit().parent(boundary).parent(tooOld)
1467 .committer(new PersonIdent(person, 1530000000, 0)).create();
1468
1469 remote.update("branch1", merge);
1470
1471
1472 ByteArrayInputStream recvStream = uploadPackV2(
1473 "command=fetch\n",
1474 PacketLineIn.delimiter(),
1475 "shallow " + boundary.toObjectId().getName() + "\n",
1476 "deepen-since 1510000\n",
1477 "want " + merge.toObjectId().getName() + "\n",
1478 "have " + boundary.toObjectId().getName() + "\n",
1479 "done\n",
1480 PacketLineIn.end());
1481 PacketLineIn pckIn = new PacketLineIn(recvStream);
1482 assertThat(pckIn.readString(), is("shallow-info"));
1483
1484
1485
1486 assertThat(pckIn.readString(), is("shallow " + merge.toObjectId().getName()));
1487
1488
1489
1490 assertThat(pckIn.readString(), is("unshallow " + boundary.toObjectId().getName()));
1491
1492 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1493 assertThat(pckIn.readString(), is("packfile"));
1494 parsePack(recvStream);
1495
1496
1497
1498 assertFalse(client.getObjectDatabase().has(tooOld.toObjectId()));
1499
1500
1501
1502 assertFalse(client.getObjectDatabase().has(boundary.toObjectId()));
1503
1504
1505 assertTrue(client.getObjectDatabase().has(beyondBoundary.toObjectId()));
1506 assertTrue(client.getObjectDatabase().has(merge.toObjectId()));
1507 }
1508
1509 @Test
1510 public void testV2FetchShallowSince_excludedParentWithMultipleChildren() throws Exception {
1511 PersonIdent person = new PersonIdent(remote.getRepository());
1512
1513 RevCommit base = remote.commit()
1514 .committer(new PersonIdent(person, 1500000000, 0)).create();
1515 RevCommit child1 = remote.commit().parent(base)
1516 .committer(new PersonIdent(person, 1510000000, 0)).create();
1517 RevCommit child2 = remote.commit().parent(base)
1518 .committer(new PersonIdent(person, 1520000000, 0)).create();
1519
1520 remote.update("branch1", child1);
1521 remote.update("branch2", child2);
1522
1523 ByteArrayInputStream recvStream = uploadPackV2(
1524 "command=fetch\n",
1525 PacketLineIn.delimiter(),
1526 "deepen-since 1510000\n",
1527 "want " + child1.toObjectId().getName() + "\n",
1528 "want " + child2.toObjectId().getName() + "\n",
1529 "done\n",
1530 PacketLineIn.end());
1531 PacketLineIn pckIn = new PacketLineIn(recvStream);
1532 assertThat(pckIn.readString(), is("shallow-info"));
1533
1534
1535 assertThat(
1536 Arrays.asList(pckIn.readString(), pckIn.readString()),
1537 hasItems(
1538 "shallow " + child1.toObjectId().getName(),
1539 "shallow " + child2.toObjectId().getName()));
1540
1541 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1542 assertThat(pckIn.readString(), is("packfile"));
1543 parsePack(recvStream);
1544
1545
1546 assertFalse(client.getObjectDatabase().has(base.toObjectId()));
1547 assertTrue(client.getObjectDatabase().has(child1.toObjectId()));
1548 assertTrue(client.getObjectDatabase().has(child2.toObjectId()));
1549 }
1550
1551 @Test
1552 public void testV2FetchShallowSince_noCommitsSelected() throws Exception {
1553 PersonIdent person = new PersonIdent(remote.getRepository());
1554
1555 RevCommit tooOld = remote.commit()
1556 .committer(new PersonIdent(person, 1500000000, 0)).create();
1557
1558 remote.update("branch1", tooOld);
1559
1560 UploadPackInternalServerErrorException e = assertThrows(
1561 UploadPackInternalServerErrorException.class,
1562 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
1563 "deepen-since 1510000\n",
1564 "want " + tooOld.toObjectId().getName() + "\n",
1565 "done\n", PacketLineIn.end()));
1566 assertThat(e.getCause().getMessage(),
1567 containsString("No commits selected for shallow request"));
1568 }
1569
1570 @Test
1571 public void testV2FetchDeepenNot() throws Exception {
1572 RevCommit one = remote.commit().message("one").create();
1573 RevCommit two = remote.commit().message("two").parent(one).create();
1574 RevCommit three = remote.commit().message("three").parent(two).create();
1575 RevCommit side = remote.commit().message("side").parent(one).create();
1576 RevCommit merge = remote.commit().message("merge")
1577 .parent(three).parent(side).create();
1578
1579 remote.update("branch1", merge);
1580 remote.update("side", side);
1581
1582
1583
1584 ByteArrayInputStream recvStream = uploadPackV2(
1585 "command=fetch\n",
1586 PacketLineIn.delimiter(),
1587 "shallow " + three.toObjectId().getName() + "\n",
1588 "deepen-not side\n",
1589 "want " + merge.toObjectId().getName() + "\n",
1590 "have " + three.toObjectId().getName() + "\n",
1591 "done\n",
1592 PacketLineIn.end());
1593 PacketLineIn pckIn = new PacketLineIn(recvStream);
1594 assertThat(pckIn.readString(), is("shallow-info"));
1595
1596
1597
1598 assertThat(
1599 Arrays.asList(pckIn.readString(), pckIn.readString()),
1600 hasItems(
1601 "shallow " + merge.toObjectId().getName(),
1602 "shallow " + two.toObjectId().getName()));
1603
1604
1605 assertThat(pckIn.readString(), is("unshallow " + three.toObjectId().getName()));
1606
1607 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1608 assertThat(pckIn.readString(), is("packfile"));
1609 parsePack(recvStream);
1610
1611
1612
1613 assertFalse(client.getObjectDatabase().has(side.toObjectId()));
1614 assertFalse(client.getObjectDatabase().has(one.toObjectId()));
1615
1616
1617
1618 assertFalse(client.getObjectDatabase().has(three.toObjectId()));
1619
1620
1621 assertTrue(client.getObjectDatabase().has(merge.toObjectId()));
1622 assertTrue(client.getObjectDatabase().has(two.toObjectId()));
1623 }
1624
1625 @Test
1626 public void testV2FetchDeepenNot_excludeDescendantOfWant()
1627 throws Exception {
1628 RevCommit one = remote.commit().message("one").create();
1629 RevCommit two = remote.commit().message("two").parent(one).create();
1630 RevCommit three = remote.commit().message("three").parent(two).create();
1631 RevCommit four = remote.commit().message("four").parent(three).create();
1632
1633 remote.update("two", two);
1634 remote.update("four", four);
1635
1636 UploadPackInternalServerErrorException e = assertThrows(
1637 UploadPackInternalServerErrorException.class,
1638 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
1639 "deepen-not four\n",
1640 "want " + two.toObjectId().getName() + "\n", "done\n",
1641 PacketLineIn.end()));
1642 assertThat(e.getCause().getMessage(),
1643 containsString("No commits selected for shallow request"));
1644 }
1645
1646 @Test
1647 public void testV2FetchDeepenNot_supportAnnotatedTags() throws Exception {
1648 RevCommit one = remote.commit().message("one").create();
1649 RevCommit two = remote.commit().message("two").parent(one).create();
1650 RevCommit three = remote.commit().message("three").parent(two).create();
1651 RevCommit four = remote.commit().message("four").parent(three).create();
1652 RevTag twoTag = remote.tag("twotag", two);
1653
1654 remote.update("refs/tags/twotag", twoTag);
1655 remote.update("four", four);
1656
1657 ByteArrayInputStream recvStream = uploadPackV2(
1658 "command=fetch\n",
1659 PacketLineIn.delimiter(),
1660 "deepen-not twotag\n",
1661 "want " + four.toObjectId().getName() + "\n",
1662 "done\n",
1663 PacketLineIn.end());
1664 PacketLineIn pckIn = new PacketLineIn(recvStream);
1665 assertThat(pckIn.readString(), is("shallow-info"));
1666 assertThat(pckIn.readString(), is("shallow " + three.toObjectId().getName()));
1667 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1668 assertThat(pckIn.readString(), is("packfile"));
1669 parsePack(recvStream);
1670 assertFalse(client.getObjectDatabase().has(one.toObjectId()));
1671 assertFalse(client.getObjectDatabase().has(two.toObjectId()));
1672 assertTrue(client.getObjectDatabase().has(three.toObjectId()));
1673 assertTrue(client.getObjectDatabase().has(four.toObjectId()));
1674 }
1675
1676 @Test
1677 public void testV2FetchDeepenNot_excludedParentWithMultipleChildren() throws Exception {
1678 PersonIdent person = new PersonIdent(remote.getRepository());
1679
1680 RevCommit base = remote.commit()
1681 .committer(new PersonIdent(person, 1500000000, 0)).create();
1682 RevCommit child1 = remote.commit().parent(base)
1683 .committer(new PersonIdent(person, 1510000000, 0)).create();
1684 RevCommit child2 = remote.commit().parent(base)
1685 .committer(new PersonIdent(person, 1520000000, 0)).create();
1686
1687 remote.update("base", base);
1688 remote.update("branch1", child1);
1689 remote.update("branch2", child2);
1690
1691 ByteArrayInputStream recvStream = uploadPackV2(
1692 "command=fetch\n",
1693 PacketLineIn.delimiter(),
1694 "deepen-not base\n",
1695 "want " + child1.toObjectId().getName() + "\n",
1696 "want " + child2.toObjectId().getName() + "\n",
1697 "done\n",
1698 PacketLineIn.end());
1699 PacketLineIn pckIn = new PacketLineIn(recvStream);
1700 assertThat(pckIn.readString(), is("shallow-info"));
1701
1702
1703 assertThat(
1704 Arrays.asList(pckIn.readString(), pckIn.readString()),
1705 hasItems(
1706 "shallow " + child1.toObjectId().getName(),
1707 "shallow " + child2.toObjectId().getName()));
1708
1709 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
1710 assertThat(pckIn.readString(), is("packfile"));
1711 parsePack(recvStream);
1712
1713
1714 assertFalse(client.getObjectDatabase().has(base.toObjectId()));
1715 assertTrue(client.getObjectDatabase().has(child1.toObjectId()));
1716 assertTrue(client.getObjectDatabase().has(child2.toObjectId()));
1717 }
1718
1719 @Test
1720 public void testV2FetchUnrecognizedArgument() throws Exception {
1721 UploadPackInternalServerErrorException e = assertThrows(
1722 UploadPackInternalServerErrorException.class,
1723 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
1724 "invalid-argument\n", PacketLineIn.end()));
1725 assertThat(e.getCause().getMessage(),
1726 containsString("unexpected invalid-argument"));
1727 }
1728
1729 @Test
1730 public void testV2FetchServerOptions() throws Exception {
1731 String[] lines = { "command=fetch\n", "server-option=one\n",
1732 "server-option=two\n", PacketLineIn.delimiter(),
1733 PacketLineIn.end() };
1734
1735 TestV2Hook testHook = new TestV2Hook();
1736 uploadPackSetup(TransferConfig.ProtocolVersion.V2.version(),
1737 (UploadPack up) -> {
1738 up.setProtocolV2Hook(testHook);
1739 }, lines);
1740
1741 FetchV2Request req = testHook.fetchRequest;
1742 assertNotNull(req);
1743 assertEquals(2, req.getServerOptions().size());
1744 assertThat(req.getServerOptions(), hasItems("one", "two"));
1745 }
1746
1747 @Test
1748 public void testV2FetchFilter() throws Exception {
1749 RevBlob big = remote.blob("foobar");
1750 RevBlob small = remote.blob("fooba");
1751 RevTree tree = remote.tree(remote.file("1", big),
1752 remote.file("2", small));
1753 RevCommit commit = remote.commit(tree);
1754 remote.update("master", commit);
1755
1756 server.getConfig().setBoolean("uploadpack", null, "allowfilter", true);
1757
1758 ByteArrayInputStream recvStream = uploadPackV2(
1759 "command=fetch\n",
1760 PacketLineIn.delimiter(),
1761 "want " + commit.toObjectId().getName() + "\n",
1762 "filter blob:limit=5\n",
1763 "done\n",
1764 PacketLineIn.end());
1765 PacketLineIn pckIn = new PacketLineIn(recvStream);
1766 assertThat(pckIn.readString(), is("packfile"));
1767 parsePack(recvStream);
1768
1769 assertFalse(client.getObjectDatabase().has(big.toObjectId()));
1770 assertTrue(client.getObjectDatabase().has(small.toObjectId()));
1771 }
1772
1773 abstract class TreeBuilder {
1774 abstract void addElements(DirCacheBuilder dcBuilder) throws Exception;
1775
1776 RevTree build() throws Exception {
1777 DirCache dc = DirCache.newInCore();
1778 DirCacheBuilder dcBuilder = dc.builder();
1779 addElements(dcBuilder);
1780 dcBuilder.finish();
1781 ObjectId id;
1782 try (ObjectInserter ins =
1783 remote.getRepository().newObjectInserter()) {
1784 id = dc.writeTree(ins);
1785 ins.flush();
1786 }
1787 return remote.getRevWalk().parseTree(id);
1788 }
1789 }
1790
1791 class DeepTreePreparator {
1792 RevBlob blobLowDepth = remote.blob("lo");
1793 RevBlob blobHighDepth = remote.blob("hi");
1794
1795 RevTree subtree = remote.tree(remote.file("1", blobHighDepth));
1796 RevTree rootTree = (new TreeBuilder() {
1797 @Override
1798 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1799 dcBuilder.add(remote.file("1", blobLowDepth));
1800 dcBuilder.addTree(new byte[] {'2'}, DirCacheEntry.STAGE_0,
1801 remote.getRevWalk().getObjectReader(), subtree);
1802 }
1803 }).build();
1804 RevCommit commit = remote.commit(rootTree);
1805
1806 DeepTreePreparator() throws Exception {}
1807 }
1808
1809 private void uploadV2WithTreeDepthFilter(
1810 long depth, ObjectId... wants) throws Exception {
1811 server.getConfig().setBoolean("uploadpack", null, "allowfilter", true);
1812
1813 List<String> input = new ArrayList<>();
1814 input.add("command=fetch\n");
1815 input.add(PacketLineIn.delimiter());
1816 for (ObjectId want : wants) {
1817 input.add("want " + want.getName() + "\n");
1818 }
1819 input.add("filter tree:" + depth + "\n");
1820 input.add("done\n");
1821 input.add(PacketLineIn.end());
1822 ByteArrayInputStream recvStream =
1823 uploadPackV2(
1824 (UploadPack up) -> {up.setRequestPolicy(RequestPolicy.ANY);},
1825 input.toArray(new String[0]));
1826 PacketLineIn pckIn = new PacketLineIn(recvStream);
1827 assertThat(pckIn.readString(), is("packfile"));
1828 parsePack(recvStream);
1829 }
1830
1831 @Test
1832 public void testV2FetchFilterTreeDepth0() throws Exception {
1833 DeepTreePreparator preparator = new DeepTreePreparator();
1834 remote.update("master", preparator.commit);
1835
1836 uploadV2WithTreeDepthFilter(0, preparator.commit.toObjectId());
1837
1838 assertFalse(client.getObjectDatabase()
1839 .has(preparator.rootTree.toObjectId()));
1840 assertFalse(client.getObjectDatabase()
1841 .has(preparator.subtree.toObjectId()));
1842 assertFalse(client.getObjectDatabase()
1843 .has(preparator.blobLowDepth.toObjectId()));
1844 assertFalse(client.getObjectDatabase()
1845 .has(preparator.blobHighDepth.toObjectId()));
1846 assertEquals(1, stats.getTreesTraversed());
1847 }
1848
1849 @Test
1850 public void testV2FetchFilterTreeDepth1_serverHasBitmap() throws Exception {
1851 DeepTreePreparator preparator = new DeepTreePreparator();
1852 remote.update("master", preparator.commit);
1853
1854
1855
1856 generateBitmaps(server);
1857
1858 uploadV2WithTreeDepthFilter(1, preparator.commit.toObjectId());
1859
1860 assertTrue(client.getObjectDatabase()
1861 .has(preparator.rootTree.toObjectId()));
1862 assertFalse(client.getObjectDatabase()
1863 .has(preparator.subtree.toObjectId()));
1864 assertFalse(client.getObjectDatabase()
1865 .has(preparator.blobLowDepth.toObjectId()));
1866 assertFalse(client.getObjectDatabase()
1867 .has(preparator.blobHighDepth.toObjectId()));
1868 assertEquals(1, stats.getTreesTraversed());
1869 }
1870
1871 @Test
1872 public void testV2FetchFilterTreeDepth2() throws Exception {
1873 DeepTreePreparator preparator = new DeepTreePreparator();
1874 remote.update("master", preparator.commit);
1875
1876 uploadV2WithTreeDepthFilter(2, preparator.commit.toObjectId());
1877
1878 assertTrue(client.getObjectDatabase()
1879 .has(preparator.rootTree.toObjectId()));
1880 assertTrue(client.getObjectDatabase()
1881 .has(preparator.subtree.toObjectId()));
1882 assertTrue(client.getObjectDatabase()
1883 .has(preparator.blobLowDepth.toObjectId()));
1884 assertFalse(client.getObjectDatabase()
1885 .has(preparator.blobHighDepth.toObjectId()));
1886 assertEquals(2, stats.getTreesTraversed());
1887 }
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897 class RepeatedSubtreePreparator {
1898 RevBlob foo = remote.blob("foo");
1899 RevTree subtree3 = remote.tree(remote.file("foo", foo));
1900 RevTree subtree2 = (new TreeBuilder() {
1901 @Override
1902 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1903 dcBuilder.addTree(new byte[] {'b'}, DirCacheEntry.STAGE_0,
1904 remote.getRevWalk().getObjectReader(), subtree3);
1905 }
1906 }).build();
1907 RevTree subtree1 = (new TreeBuilder() {
1908 @Override
1909 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1910 dcBuilder.addTree(new byte[] {'x'}, DirCacheEntry.STAGE_0,
1911 remote.getRevWalk().getObjectReader(), subtree2);
1912 }
1913 }).build();
1914 RevTree rootTree = (new TreeBuilder() {
1915 @Override
1916 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1917 dcBuilder.addTree(new byte[] {'a'}, DirCacheEntry.STAGE_0,
1918 remote.getRevWalk().getObjectReader(), subtree1);
1919 dcBuilder.addTree(new byte[] {'x'}, DirCacheEntry.STAGE_0,
1920 remote.getRevWalk().getObjectReader(), subtree2);
1921 }
1922 }).build();
1923 RevCommit commit = remote.commit(rootTree);
1924
1925 RepeatedSubtreePreparator() throws Exception {}
1926 }
1927
1928 @Test
1929 public void testV2FetchFilterTreeDepth_iterateOverTreeAtTwoLevels()
1930 throws Exception {
1931
1932
1933
1934 RepeatedSubtreePreparator preparator = new RepeatedSubtreePreparator();
1935 remote.update("master", preparator.commit);
1936
1937 uploadV2WithTreeDepthFilter(4, preparator.commit.toObjectId());
1938
1939 assertTrue(client.getObjectDatabase()
1940 .has(preparator.foo.toObjectId()));
1941 }
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959 class RepeatedSubtreeAtSameLevelPreparator {
1960 RevBlob foo = remote.blob("foo");
1961
1962
1963 RevTree subtree1 = remote.tree(remote.file("foo", foo));
1964
1965
1966 RevTree subtree2 = (new TreeBuilder() {
1967 @Override
1968 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1969 dcBuilder.addTree(new byte[] {'b'}, DirCacheEntry.STAGE_0,
1970 remote.getRevWalk().getObjectReader(), subtree1);
1971 }
1972 }).build();
1973
1974
1975 RevTree subtree3 = (new TreeBuilder() {
1976 @Override
1977 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1978 dcBuilder.addTree(new byte[] {'x'}, DirCacheEntry.STAGE_0,
1979 remote.getRevWalk().getObjectReader(), subtree2);
1980 }
1981 }).build();
1982
1983 RevBlob baz = remote.blob("baz");
1984
1985
1986 RevTree subtree4 = remote.tree(remote.file("baz", baz));
1987
1988
1989 RevTree subtree5 = (new TreeBuilder() {
1990 @Override
1991 void addElements(DirCacheBuilder dcBuilder) throws Exception {
1992 dcBuilder.addTree(new byte[] {'c'}, DirCacheEntry.STAGE_0,
1993 remote.getRevWalk().getObjectReader(), subtree4);
1994 }
1995 }).build();
1996
1997
1998 RevTree subtree6 = (new TreeBuilder() {
1999 @Override
2000 void addElements(DirCacheBuilder dcBuilder) throws Exception {
2001 dcBuilder.addTree(new byte[] {'u'}, DirCacheEntry.STAGE_0,
2002 remote.getRevWalk().getObjectReader(), subtree5);
2003 }
2004 }).build();
2005
2006
2007 RevTree subtree7 = (new TreeBuilder() {
2008 @Override
2009 void addElements(DirCacheBuilder dcBuilder) throws Exception {
2010 dcBuilder.addTree(new byte[] {'v'}, DirCacheEntry.STAGE_0,
2011 remote.getRevWalk().getObjectReader(), subtree5);
2012 }
2013 }).build();
2014
2015 RevTree rootTree = (new TreeBuilder() {
2016 @Override
2017 void addElements(DirCacheBuilder dcBuilder) throws Exception {
2018 dcBuilder.addTree(new byte[] {'a'}, DirCacheEntry.STAGE_0,
2019 remote.getRevWalk().getObjectReader(), subtree3);
2020 dcBuilder.addTree(new byte[] {'b'}, DirCacheEntry.STAGE_0,
2021 remote.getRevWalk().getObjectReader(), subtree6);
2022 dcBuilder.addTree(new byte[] {'y'}, DirCacheEntry.STAGE_0,
2023 remote.getRevWalk().getObjectReader(), subtree3);
2024 dcBuilder.addTree(new byte[] {'z'}, DirCacheEntry.STAGE_0,
2025 remote.getRevWalk().getObjectReader(), subtree7);
2026 }
2027 }).build();
2028 RevCommit commit = remote.commit(rootTree);
2029
2030 RepeatedSubtreeAtSameLevelPreparator() throws Exception {}
2031 }
2032
2033 @Test
2034 public void testV2FetchFilterTreeDepth_repeatTreeAtSameLevelIncludeFile()
2035 throws Exception {
2036 RepeatedSubtreeAtSameLevelPreparator preparator =
2037 new RepeatedSubtreeAtSameLevelPreparator();
2038 remote.update("master", preparator.commit);
2039
2040 uploadV2WithTreeDepthFilter(5, preparator.commit.toObjectId());
2041
2042 assertTrue(client.getObjectDatabase()
2043 .has(preparator.foo.toObjectId()));
2044 assertTrue(client.getObjectDatabase()
2045 .has(preparator.baz.toObjectId()));
2046 assertEquals(8, stats.getTreesTraversed());
2047 }
2048
2049 @Test
2050 public void testV2FetchFilterTreeDepth_repeatTreeAtSameLevelExcludeFile()
2051 throws Exception {
2052 RepeatedSubtreeAtSameLevelPreparator preparator =
2053 new RepeatedSubtreeAtSameLevelPreparator();
2054 remote.update("master", preparator.commit);
2055
2056 uploadV2WithTreeDepthFilter(4, preparator.commit.toObjectId());
2057
2058 assertFalse(client.getObjectDatabase()
2059 .has(preparator.foo.toObjectId()));
2060 assertFalse(client.getObjectDatabase()
2061 .has(preparator.baz.toObjectId()));
2062 assertEquals(8, stats.getTreesTraversed());
2063 }
2064
2065 @Test
2066 public void testWantFilteredObject() throws Exception {
2067 RepeatedSubtreePreparator preparator = new RepeatedSubtreePreparator();
2068 remote.update("master", preparator.commit);
2069
2070
2071
2072 uploadV2WithTreeDepthFilter(
2073 3,
2074 preparator.commit.toObjectId(),
2075 preparator.foo.toObjectId());
2076 assertTrue(client.getObjectDatabase()
2077 .has(preparator.foo.toObjectId()));
2078
2079 client = newRepo("client");
2080
2081
2082 uploadV2WithTreeDepthFilter(
2083 2,
2084 preparator.commit.toObjectId(),
2085 preparator.subtree3.toObjectId());
2086 assertTrue(client.getObjectDatabase()
2087 .has(preparator.foo.toObjectId()));
2088 assertTrue(client.getObjectDatabase()
2089 .has(preparator.subtree3.toObjectId()));
2090 }
2091
2092 private void checkV2FetchWhenNotAllowed(String fetchLine, String expectedMessage)
2093 throws Exception {
2094 RevCommit commit = remote.commit().message("0").create();
2095 remote.update("master", commit);
2096
2097 UploadPackInternalServerErrorException e = assertThrows(
2098 UploadPackInternalServerErrorException.class,
2099 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
2100 "want " + commit.toObjectId().getName() + "\n",
2101 fetchLine, "done\n", PacketLineIn.end()));
2102 assertThat(e.getCause().getMessage(),
2103 containsString(expectedMessage));
2104 }
2105
2106 @Test
2107 public void testV2FetchFilterWhenNotAllowed() throws Exception {
2108 checkV2FetchWhenNotAllowed(
2109 "filter blob:limit=5\n",
2110 "unexpected filter blob:limit=5");
2111 }
2112
2113 @Test
2114 public void testV2FetchWantRefIfNotAllowed() throws Exception {
2115 checkV2FetchWhenNotAllowed(
2116 "want-ref refs/heads/one\n",
2117 "unexpected want-ref refs/heads/one");
2118 }
2119
2120 @Test
2121 public void testV2FetchSidebandAllIfNotAllowed() throws Exception {
2122 checkV2FetchWhenNotAllowed(
2123 "sideband-all\n",
2124 "unexpected sideband-all");
2125 }
2126
2127 @Test
2128 public void testV2FetchWantRef() throws Exception {
2129 RevCommit one = remote.commit().message("1").create();
2130 RevCommit two = remote.commit().message("2").create();
2131 RevCommit three = remote.commit().message("3").create();
2132 remote.update("one", one);
2133 remote.update("two", two);
2134 remote.update("three", three);
2135
2136 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
2137
2138 ByteArrayInputStream recvStream = uploadPackV2(
2139 "command=fetch\n",
2140 PacketLineIn.delimiter(),
2141 "want-ref refs/heads/one\n",
2142 "want-ref refs/heads/two\n",
2143 "done\n",
2144 PacketLineIn.end());
2145 PacketLineIn pckIn = new PacketLineIn(recvStream);
2146 assertThat(pckIn.readString(), is("wanted-refs"));
2147 assertThat(
2148 Arrays.asList(pckIn.readString(), pckIn.readString()),
2149 hasItems(
2150 one.toObjectId().getName() + " refs/heads/one",
2151 two.toObjectId().getName() + " refs/heads/two"));
2152 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2153 assertThat(pckIn.readString(), is("packfile"));
2154 parsePack(recvStream);
2155
2156 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
2157 assertTrue(client.getObjectDatabase().has(two.toObjectId()));
2158 assertFalse(client.getObjectDatabase().has(three.toObjectId()));
2159 }
2160
2161 @Test
2162 public void testV2FetchBadWantRef() throws Exception {
2163 RevCommit one = remote.commit().message("1").create();
2164 remote.update("one", one);
2165
2166 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant",
2167 true);
2168
2169 UploadPackInternalServerErrorException e = assertThrows(
2170 UploadPackInternalServerErrorException.class,
2171 () -> uploadPackV2("command=fetch\n", PacketLineIn.delimiter(),
2172 "want-ref refs/heads/one\n",
2173 "want-ref refs/heads/nonExistentRef\n", "done\n",
2174 PacketLineIn.end()));
2175 assertThat(e.getCause().getMessage(),
2176 containsString("Invalid ref name: refs/heads/nonExistentRef"));
2177 }
2178
2179 @Test
2180 public void testV2FetchMixedWantRef() throws Exception {
2181 RevCommit one = remote.commit().message("1").create();
2182 RevCommit two = remote.commit().message("2").create();
2183 RevCommit three = remote.commit().message("3").create();
2184 remote.update("one", one);
2185 remote.update("two", two);
2186 remote.update("three", three);
2187
2188 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
2189
2190 ByteArrayInputStream recvStream = uploadPackV2(
2191 "command=fetch\n",
2192 PacketLineIn.delimiter(),
2193 "want-ref refs/heads/one\n",
2194 "want " + two.toObjectId().getName() + "\n",
2195 "done\n",
2196 PacketLineIn.end());
2197 PacketLineIn pckIn = new PacketLineIn(recvStream);
2198 assertThat(pckIn.readString(), is("wanted-refs"));
2199 assertThat(
2200 pckIn.readString(),
2201 is(one.toObjectId().getName() + " refs/heads/one"));
2202 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2203 assertThat(pckIn.readString(), is("packfile"));
2204 parsePack(recvStream);
2205
2206 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
2207 assertTrue(client.getObjectDatabase().has(two.toObjectId()));
2208 assertFalse(client.getObjectDatabase().has(three.toObjectId()));
2209 }
2210
2211 @Test
2212 public void testV2FetchWantRefWeAlreadyHave() throws Exception {
2213 RevCommit one = remote.commit().message("1").create();
2214 remote.update("one", one);
2215
2216 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
2217
2218 ByteArrayInputStream recvStream = uploadPackV2(
2219 "command=fetch\n",
2220 PacketLineIn.delimiter(),
2221 "want-ref refs/heads/one\n",
2222 "have " + one.toObjectId().getName(),
2223 "done\n",
2224 PacketLineIn.end());
2225 PacketLineIn pckIn = new PacketLineIn(recvStream);
2226
2227
2228
2229
2230 assertThat(pckIn.readString(), is("wanted-refs"));
2231 assertThat(
2232 pckIn.readString(),
2233 is(one.toObjectId().getName() + " refs/heads/one"));
2234 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2235
2236
2237 assertThat(pckIn.readString(), is("packfile"));
2238 parsePack(recvStream);
2239 assertFalse(client.getObjectDatabase().has(one.toObjectId()));
2240 }
2241
2242 @Test
2243 public void testV2FetchWantRefAndDeepen() throws Exception {
2244 RevCommit parent = remote.commit().message("parent").create();
2245 RevCommit child = remote.commit().message("x").parent(parent).create();
2246 remote.update("branch1", child);
2247
2248 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
2249
2250 ByteArrayInputStream recvStream = uploadPackV2(
2251 "command=fetch\n",
2252 PacketLineIn.delimiter(),
2253 "want-ref refs/heads/branch1\n",
2254 "deepen 1\n",
2255 "done\n",
2256 PacketLineIn.end());
2257 PacketLineIn pckIn = new PacketLineIn(recvStream);
2258
2259
2260 assertThat(pckIn.readString(), is("shallow-info"));
2261 assertThat(pckIn.readString(), is("shallow " + child.toObjectId().getName()));
2262 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2263 assertThat(pckIn.readString(), is("wanted-refs"));
2264 assertThat(pckIn.readString(), is(child.toObjectId().getName() + " refs/heads/branch1"));
2265 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2266 assertThat(pckIn.readString(), is("packfile"));
2267 parsePack(recvStream);
2268 assertTrue(client.getObjectDatabase().has(child.toObjectId()));
2269 assertFalse(client.getObjectDatabase().has(parent.toObjectId()));
2270 }
2271
2272 @Test
2273 public void testV2FetchMissingShallow() throws Exception {
2274 RevCommit one = remote.commit().message("1").create();
2275 RevCommit two = remote.commit().message("2").parent(one).create();
2276 RevCommit three = remote.commit().message("3").parent(two).create();
2277 remote.update("three", three);
2278
2279 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant",
2280 true);
2281
2282 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
2283 PacketLineIn.delimiter(),
2284 "want-ref refs/heads/three\n",
2285 "deepen 3",
2286 "shallow 0123012301230123012301230123012301230123",
2287 "shallow " + two.getName() + '\n',
2288 "done\n",
2289 PacketLineIn.end());
2290 PacketLineIn pckIn = new PacketLineIn(recvStream);
2291
2292 assertThat(pckIn.readString(), is("shallow-info"));
2293 assertThat(pckIn.readString(),
2294 is("shallow " + one.toObjectId().getName()));
2295 assertThat(pckIn.readString(),
2296 is("unshallow " + two.toObjectId().getName()));
2297 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2298 assertThat(pckIn.readString(), is("wanted-refs"));
2299 assertThat(pckIn.readString(),
2300 is(three.toObjectId().getName() + " refs/heads/three"));
2301 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2302 assertThat(pckIn.readString(), is("packfile"));
2303 parsePack(recvStream);
2304
2305 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
2306 assertTrue(client.getObjectDatabase().has(two.toObjectId()));
2307 assertTrue(client.getObjectDatabase().has(three.toObjectId()));
2308 }
2309
2310 @Test
2311 public void testV2FetchSidebandAllNoPackfile() throws Exception {
2312 RevCommit fooParent = remote.commit().message("x").create();
2313 RevCommit fooChild = remote.commit().message("x").parent(fooParent).create();
2314 RevCommit barParent = remote.commit().message("y").create();
2315 RevCommit barChild = remote.commit().message("y").parent(barParent).create();
2316 remote.update("branch1", fooChild);
2317 remote.update("branch2", barChild);
2318
2319 server.getConfig().setBoolean("uploadpack", null, "allowsidebandall", true);
2320
2321 ByteArrayInputStream recvStream = uploadPackV2(
2322 "command=fetch\n",
2323 PacketLineIn.delimiter(),
2324 "sideband-all\n",
2325 "want " + fooChild.toObjectId().getName() + "\n",
2326 "want " + barChild.toObjectId().getName() + "\n",
2327 "have " + fooParent.toObjectId().getName() + "\n",
2328 PacketLineIn.end());
2329 PacketLineIn pckIn = new PacketLineIn(recvStream);
2330
2331 assertThat(pckIn.readString(), is("\001acknowledgments"));
2332 assertThat(pckIn.readString(), is("\001ACK " + fooParent.getName()));
2333 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
2334 }
2335
2336 @Test
2337 public void testV2FetchSidebandAllPackfile() throws Exception {
2338 RevCommit commit = remote.commit().message("x").create();
2339 remote.update("master", commit);
2340
2341 server.getConfig().setBoolean("uploadpack", null, "allowsidebandall", true);
2342
2343 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
2344 PacketLineIn.delimiter(),
2345 "want " + commit.getName() + "\n",
2346 "sideband-all\n",
2347 "done\n",
2348 PacketLineIn.end());
2349 PacketLineIn pckIn = new PacketLineIn(recvStream);
2350
2351 String s;
2352
2353
2354
2355 for (s = pckIn.readString(); s.startsWith("\002"); s = pckIn.readString()) {
2356
2357 }
2358 assertThat(s, is("\001packfile"));
2359 parsePack(recvStream);
2360 }
2361
2362 @Test
2363 public void testV2FetchPackfileUris() throws Exception {
2364
2365 RevCommit commit = remote.commit().message("x").create();
2366 remote.update("master", commit);
2367 generateBitmaps(server);
2368
2369
2370 RevCommit commit2 = remote.commit().message("x").parent(commit).create();
2371 remote.update("master", commit2);
2372
2373 server.getConfig().setBoolean("uploadpack", null, "allowsidebandall", true);
2374
2375 ByteArrayInputStream recvStream = uploadPackV2(
2376 (UploadPack up) -> {
2377 up.setCachedPackUriProvider(new CachedPackUriProvider() {
2378 @Override
2379 public PackInfo getInfo(CachedPack pack,
2380 Collection<String> protocolsSupported)
2381 throws IOException {
2382 assertThat(protocolsSupported, hasItems("https"));
2383 if (!protocolsSupported.contains("https"))
2384 return null;
2385 return new PackInfo("myhash", "myuri", 100);
2386 }
2387
2388 });
2389 },
2390 "command=fetch\n",
2391 PacketLineIn.delimiter(),
2392 "want " + commit2.getName() + "\n",
2393 "sideband-all\n",
2394 "packfile-uris https\n",
2395 "done\n",
2396 PacketLineIn.end());
2397 PacketLineIn pckIn = new PacketLineIn(recvStream);
2398
2399 String s;
2400
2401 for (s = pckIn.readString(); s.startsWith("\002"); s = pckIn.readString()) {
2402
2403 }
2404 assertThat(s, is("\001packfile-uris"));
2405 assertThat(pckIn.readString(), is("\001myhash myuri"));
2406 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2407 assertThat(pckIn.readString(), is("\001packfile"));
2408 parsePack(recvStream);
2409
2410 assertFalse(client.getObjectDatabase().has(commit.toObjectId()));
2411 assertTrue(client.getObjectDatabase().has(commit2.toObjectId()));
2412 }
2413
2414 @Test
2415 public void testGetPeerAgentProtocolV0() throws Exception {
2416 RevCommit one = remote.commit().message("1").create();
2417 remote.update("one", one);
2418
2419 UploadPack up = new UploadPack(server);
2420 ByteArrayInputStream send = linesAsInputStream(
2421 "want " + one.getName() + " agent=JGit-test/1.2.3\n",
2422 PacketLineIn.end(),
2423 "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n");
2424
2425 ByteArrayOutputStream recv = new ByteArrayOutputStream();
2426 up.upload(send, recv, null);
2427
2428 assertEquals(up.getPeerUserAgent(), "JGit-test/1.2.3");
2429 }
2430
2431 @Test
2432 public void testGetPeerAgentProtocolV2() throws Exception {
2433 server.getConfig().setString(ConfigConstants.CONFIG_PROTOCOL_SECTION,
2434 null, ConfigConstants.CONFIG_KEY_VERSION,
2435 TransferConfig.ProtocolVersion.V2.version());
2436
2437 RevCommit one = remote.commit().message("1").create();
2438 remote.update("one", one);
2439
2440 UploadPack up = new UploadPack(server);
2441 up.setExtraParameters(Sets.of("version=2"));
2442
2443 ByteArrayInputStream send = linesAsInputStream(
2444 "command=fetch\n", "agent=JGit-test/1.2.4\n",
2445 PacketLineIn.delimiter(), "want " + one.getName() + "\n",
2446 "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n",
2447 PacketLineIn.end());
2448
2449 ByteArrayOutputStream recv = new ByteArrayOutputStream();
2450 up.upload(send, recv, null);
2451
2452 assertEquals(up.getPeerUserAgent(), "JGit-test/1.2.4");
2453 }
2454
2455 private static class RejectAllRefFilter implements RefFilter {
2456 @Override
2457 public Map<String, Ref> filter(Map<String, Ref> refs) {
2458 return new HashMap<>();
2459 }
2460 }
2461
2462 @Test
2463 public void testSingleBranchCloneTagChain() throws Exception {
2464 RevBlob blob0 = remote.blob("Initial content of first file");
2465 RevBlob blob1 = remote.blob("Second file content");
2466 RevCommit commit0 = remote
2467 .commit(remote.tree(remote.file("prvni.txt", blob0)));
2468 RevCommit commit1 = remote
2469 .commit(remote.tree(remote.file("druhy.txt", blob1)), commit0);
2470 remote.update("master", commit1);
2471
2472 RevTag heavyTag1 = remote.tag("commitTagRing", commit0);
2473 remote.getRevWalk().parseHeaders(heavyTag1);
2474 RevTag heavyTag2 = remote.tag("middleTagRing", heavyTag1);
2475 remote.lightweightTag("refTagRing", heavyTag2);
2476
2477 UploadPack uploadPack = new UploadPack(remote.getRepository());
2478
2479 ByteArrayOutputStream cli = new ByteArrayOutputStream();
2480 PacketLineOut clientWant = new PacketLineOut(cli);
2481 clientWant.writeString("want " + commit1.name()
2482 + " multi_ack_detailed include-tag thin-pack ofs-delta agent=tempo/pflaska");
2483 clientWant.end();
2484 clientWant.writeString("done\n");
2485
2486 try (ByteArrayOutputStream serverResponse = new ByteArrayOutputStream()) {
2487
2488 uploadPack.setPreUploadHook(new PreUploadHook() {
2489 @Override
2490 public void onBeginNegotiateRound(UploadPack up,
2491 Collection<? extends ObjectId> wants, int cntOffered)
2492 throws ServiceMayNotContinueException {
2493
2494 }
2495
2496 @Override
2497 public void onEndNegotiateRound(UploadPack up,
2498 Collection<? extends ObjectId> wants, int cntCommon,
2499 int cntNotFound, boolean ready)
2500 throws ServiceMayNotContinueException {
2501
2502 }
2503
2504 @Override
2505 public void onSendPack(UploadPack up,
2506 Collection<? extends ObjectId> wants,
2507 Collection<? extends ObjectId> haves)
2508 throws ServiceMayNotContinueException {
2509
2510 serverResponse.reset();
2511 }
2512 });
2513 uploadPack.upload(new ByteArrayInputStream(cli.toByteArray()),
2514 serverResponse, System.err);
2515 InputStream packReceived = new ByteArrayInputStream(
2516 serverResponse.toByteArray());
2517 PackLock lock = null;
2518 try (ObjectInserter ins = client.newObjectInserter()) {
2519 PackParser parser = ins.newPackParser(packReceived);
2520 parser.setAllowThin(true);
2521 parser.setLockMessage("receive-tag-chain");
2522 ProgressMonitor mlc = NullProgressMonitor.INSTANCE;
2523 lock = parser.parse(mlc, mlc);
2524 ins.flush();
2525 } finally {
2526 if (lock != null) {
2527 lock.unlock();
2528 }
2529 }
2530 InMemoryRepository.MemObjDatabase objDb = client
2531 .getObjectDatabase();
2532 assertTrue(objDb.has(blob0.toObjectId()));
2533 assertTrue(objDb.has(blob1.toObjectId()));
2534 assertTrue(objDb.has(commit0.toObjectId()));
2535 assertTrue(objDb.has(commit1.toObjectId()));
2536 assertTrue(objDb.has(heavyTag1.toObjectId()));
2537 assertTrue(objDb.has(heavyTag2.toObjectId()));
2538 }
2539 }
2540
2541 @Test
2542 public void testSafeToClearRefsInFetchV0() throws Exception {
2543 server =
2544 new RefCallsCountingRepository(
2545 new DfsRepositoryDescription("server"));
2546 remote = new TestRepository<>(server);
2547 RevCommit one = remote.commit().message("1").create();
2548 remote.update("one", one);
2549 testProtocol = new TestProtocol<>((Object req, Repository db) -> {
2550 UploadPack up = new UploadPack(db);
2551 return up;
2552 }, null);
2553 uri = testProtocol.register(ctx, server);
2554 try (Transport tn = testProtocol.open(uri, client, "server")) {
2555 tn.fetch(NullProgressMonitor.INSTANCE,
2556 Collections.singletonList(new RefSpec(one.name())));
2557 }
2558 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
2559 assertEquals(1, ((RefCallsCountingRepository)server).numRefCalls());
2560 }
2561
2562 @Test
2563 public void testSafeToClearRefsInFetchV2() throws Exception {
2564 server =
2565 new RefCallsCountingRepository(
2566 new DfsRepositoryDescription("server"));
2567 remote = new TestRepository<>(server);
2568 RevCommit one = remote.commit().message("1").create();
2569 RevCommit two = remote.commit().message("2").create();
2570 remote.update("one", one);
2571 remote.update("two", two);
2572 server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
2573 ByteArrayInputStream recvStream = uploadPackV2(
2574 "command=fetch\n",
2575 PacketLineIn.delimiter(),
2576 "want-ref refs/heads/one\n",
2577 "want-ref refs/heads/two\n",
2578 "done\n",
2579 PacketLineIn.end());
2580 PacketLineIn pckIn = new PacketLineIn(recvStream);
2581 assertThat(pckIn.readString(), is("wanted-refs"));
2582 assertThat(
2583 Arrays.asList(pckIn.readString(), pckIn.readString()),
2584 hasItems(
2585 one.toObjectId().getName() + " refs/heads/one",
2586 two.toObjectId().getName() + " refs/heads/two"));
2587 assertTrue(PacketLineIn.isDelimiter(pckIn.readString()));
2588 assertThat(pckIn.readString(), is("packfile"));
2589 parsePack(recvStream);
2590 assertTrue(client.getObjectDatabase().has(one.toObjectId()));
2591 assertEquals(1, ((RefCallsCountingRepository)server).numRefCalls());
2592 }
2593
2594 @Test
2595 public void testNotAdvertisedWantsV1Fetch() throws Exception {
2596 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
2597
2598 RevBlob parentBlob = remote.blob(commonInBlob + "a");
2599 RevCommit parent = remote
2600 .commit(remote.tree(remote.file("foo", parentBlob)));
2601 RevBlob childBlob = remote.blob(commonInBlob + "b");
2602 RevCommit child = remote
2603 .commit(remote.tree(remote.file("foo", childBlob)), parent);
2604 remote.update("branch1", child);
2605
2606 uploadPackV1("want " + child.toObjectId().getName() + "\n",
2607 PacketLineIn.end(),
2608 "have " + parent.toObjectId().getName() + "\n",
2609 "done\n", PacketLineIn.end());
2610
2611 assertEquals(0, stats.getNotAdvertisedWants());
2612 }
2613
2614 @Test
2615 public void testNotAdvertisedWantsV1FetchRequestPolicyReachableCommit() throws Exception {
2616 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
2617
2618 RevBlob parentBlob = remote.blob(commonInBlob + "a");
2619 RevCommit parent = remote
2620 .commit(remote.tree(remote.file("foo", parentBlob)));
2621 RevBlob childBlob = remote.blob(commonInBlob + "b");
2622 RevCommit child = remote
2623 .commit(remote.tree(remote.file("foo", childBlob)), parent);
2624
2625 remote.update("branch1", child);
2626
2627 uploadPackV1((UploadPack up) -> {up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);},
2628 "want " + parent.toObjectId().getName() + "\n",
2629 PacketLineIn.end(),
2630 "done\n", PacketLineIn.end());
2631
2632 assertEquals(1, stats.getNotAdvertisedWants());
2633 }
2634
2635 @Test
2636 public void testNotAdvertisedWantsV2FetchThinPack() throws Exception {
2637 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
2638
2639 RevBlob parentBlob = remote.blob(commonInBlob + "a");
2640 RevCommit parent = remote
2641 .commit(remote.tree(remote.file("foo", parentBlob)));
2642 RevBlob childBlob = remote.blob(commonInBlob + "b");
2643 RevCommit child = remote
2644 .commit(remote.tree(remote.file("foo", childBlob)), parent);
2645 remote.update("branch1", child);
2646
2647 ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n",
2648 PacketLineIn.delimiter(),
2649 "want " + child.toObjectId().getName() + "\n",
2650 "have " + parent.toObjectId().getName() + "\n", "thin-pack\n",
2651 "done\n", PacketLineIn.end());
2652 PacketLineIn pckIn = new PacketLineIn(recvStream);
2653
2654 assertThat(pckIn.readString(), is("packfile"));
2655
2656 assertEquals(0, stats.getNotAdvertisedWants());
2657 }
2658
2659 @Test
2660 public void testNotAdvertisedWantsV2FetchRequestPolicyReachableCommit() throws Exception {
2661 String commonInBlob = "abcdefghijklmnopqrstuvwxyz";
2662
2663 RevBlob parentBlob = remote.blob(commonInBlob + "a");
2664 RevCommit parent = remote
2665 .commit(remote.tree(remote.file("foo", parentBlob)));
2666 RevBlob childBlob = remote.blob(commonInBlob + "b");
2667 RevCommit child = remote
2668 .commit(remote.tree(remote.file("foo", childBlob)), parent);
2669
2670 remote.update("branch1", child);
2671
2672 uploadPackV2((UploadPack up) -> {up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);},
2673 "command=fetch\n",
2674 PacketLineIn.delimiter(),
2675 "want " + parent.toObjectId().getName() + "\n", "thin-pack\n",
2676 "done\n", PacketLineIn.end());
2677
2678 assertEquals(1, stats.getNotAdvertisedWants());
2679 }
2680
2681 private class RefCallsCountingRepository extends InMemoryRepository {
2682 private final InMemoryRepository.MemRefDatabase refdb;
2683 private int numRefCalls;
2684
2685 public RefCallsCountingRepository(DfsRepositoryDescription repoDesc) {
2686 super(repoDesc);
2687 refdb = new InMemoryRepository.MemRefDatabase() {
2688 @Override
2689 public List<Ref> getRefs() throws IOException {
2690 numRefCalls++;
2691 return super.getRefs();
2692 }
2693 };
2694 }
2695
2696 public int numRefCalls() {
2697 return numRefCalls;
2698 }
2699
2700 @Override
2701 public RefDatabase getRefDatabase() {
2702 return refdb;
2703 }
2704 }
2705
2706 @Test
2707 public void testObjectInfo() throws Exception {
2708 server.getConfig().setBoolean("uploadpack", null, "advertiseobjectinfo",
2709 true);
2710
2711 RevBlob blob1 = remote.blob("foobar");
2712 RevBlob blob2 = remote.blob("fooba");
2713 RevTree tree = remote.tree(remote.file("1", blob1),
2714 remote.file("2", blob2));
2715 RevCommit commit = remote.commit(tree);
2716 remote.update("master", commit);
2717
2718 TestV2Hook hook = new TestV2Hook();
2719 ByteArrayInputStream recvStream = uploadPackV2((UploadPack up) -> {
2720 up.setProtocolV2Hook(hook);
2721 }, "command=object-info\n", "size",
2722 "oid " + ObjectId.toString(blob1.getId()),
2723 "oid " + ObjectId.toString(blob2.getId()), PacketLineIn.end());
2724 PacketLineIn pckIn = new PacketLineIn(recvStream);
2725
2726 assertThat(hook.objectInfoRequest, notNullValue());
2727 assertThat(pckIn.readString(), is("size"));
2728 assertThat(pckIn.readString(),
2729 is(ObjectId.toString(blob1.getId()) + " 6"));
2730 assertThat(pckIn.readString(),
2731 is(ObjectId.toString(blob2.getId()) + " 5"));
2732 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
2733 }
2734
2735 @Test
2736 public void testObjectInfo_invalidOid() throws Exception {
2737 server.getConfig().setBoolean("uploadpack", null, "advertiseobjectinfo",
2738 true);
2739
2740 assertThrows(UploadPackInternalServerErrorException.class,
2741 () -> uploadPackV2("command=object-info\n", "size",
2742 "oid invalid",
2743 PacketLineIn.end()));
2744 }
2745 }