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