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