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