1 /* 2 * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.openjdk.skara.vcs; 24 25 import org.junit.jupiter.api.Assumptions; 26 import org.openjdk.skara.test.TemporaryDirectory; 27 28 import org.junit.jupiter.api.Test; 29 import org.junit.jupiter.params.ParameterizedTest; 30 import org.junit.jupiter.params.provider.EnumSource; 31 32 import java.io.IOException; 33 import java.net.URI; 34 import java.nio.file.*; 35 import java.nio.file.attribute.*; 36 import java.util.*; 37 import java.util.stream.Collectors; 38 39 import static java.nio.file.StandardOpenOption.*; 40 import static org.junit.jupiter.api.Assertions.*; 41 import static org.junit.jupiter.api.Assumptions.assumeTrue; 42 43 public class RepositoryTests { 44 45 @ParameterizedTest 46 @EnumSource(VCS.class) 47 void testExistsOnMissingDirectory(VCS vcs) throws IOException { 48 var d = Paths.get("/", "this", "path", "does", "not", "exist"); 49 var r = Repository.get(d); 50 assertTrue(r.isEmpty()); 51 } 52 53 @ParameterizedTest 54 @EnumSource(VCS.class) 55 void testExistsOnEmptyDirectory(VCS vcs) throws IOException { 56 try (var dir = new TemporaryDirectory()) { 57 var r = Repository.get(dir.path()); 58 assertTrue(r.isEmpty()); 59 } 60 } 61 62 @ParameterizedTest 63 @EnumSource(VCS.class) 64 void testExistsOnInitializedRepository(VCS vcs) throws IOException { 65 try (var dir = new TemporaryDirectory()) { 66 var r = Repository.init(dir.path(), vcs); 67 assertTrue(r.exists()); 68 } 69 } 70 71 @ParameterizedTest 72 @EnumSource(VCS.class) 73 void testExistsOnSubdir() throws IOException { 74 try (var dir = new TemporaryDirectory()) { 75 var r = Repository.init(dir.path(), VCS.GIT); 76 assertTrue(r.exists()); 77 78 var subdir = Paths.get(dir.toString(), "test"); 79 Files.createDirectories(subdir); 80 var r2 = Repository.get(subdir); 81 assertTrue(r2.get().exists()); 82 } 83 } 84 85 @ParameterizedTest 86 @EnumSource(VCS.class) 87 void testRootOnTopLevel() throws IOException { 88 try (var dir = new TemporaryDirectory()) { 89 var r = Repository.init(dir.path(), VCS.GIT); 90 assertEquals(dir.toString(), r.root().toString()); 91 } 92 } 93 94 @ParameterizedTest 95 @EnumSource(VCS.class) 96 void testRootOnSubdirectory(VCS vcs) throws IOException { 97 try (var dir = new TemporaryDirectory()) { 98 var r = Repository.init(dir.path(), vcs); 99 assertEquals(dir.toString(), r.root().toString()); 100 101 var subdir = Paths.get(dir.toString(), "sub"); 102 Files.createDirectories(subdir); 103 104 var r2 = Repository.get(subdir); 105 assertEquals(dir.toString(), r2.get().root().toString()); 106 } 107 } 108 109 @ParameterizedTest 110 @EnumSource(VCS.class) 111 void testResolveOnEmptyRepository(VCS vcs) throws IOException { 112 try (var dir = new TemporaryDirectory()) { 113 var r = Repository.init(dir.path(), vcs); 114 assertTrue(r.resolve("HEAD").isEmpty()); 115 } 116 } 117 118 @ParameterizedTest 119 @EnumSource(VCS.class) 120 void testResolveWithHEAD(VCS vcs) throws IOException { 121 try (var dir = new TemporaryDirectory()) { 122 var r = Repository.init(dir.path(), vcs); 123 124 var readme = dir.path().resolve("README"); 125 Files.write(readme, List.of("Hello, readme!")); 126 127 r.add(readme); 128 var head = r.commit("Add README", "duke", "duke@openjdk.java.net"); 129 assertEquals(head, r.head()); 130 } 131 } 132 133 @ParameterizedTest 134 @EnumSource(VCS.class) 135 void testConfig(VCS vcs) throws IOException { 136 try (var dir = new TemporaryDirectory()) { 137 var r = Repository.init(dir.path(), vcs); 138 139 if (vcs == VCS.GIT) { 140 var config = dir.path().resolve(".git").resolve("config"); 141 Files.write(config, List.of("[user]", "name = duke"), WRITE, APPEND); 142 assertEquals(List.of("duke"), r.config("user.name")); 143 } else if (vcs == VCS.HG) { 144 var config = dir.path().resolve(".hg").resolve("hgrc"); 145 Files.write(config, List.of("[ui]", "username = duke"), WRITE, CREATE); 146 assertEquals(List.of("duke"), r.config("ui.username")); 147 } 148 149 assertEquals("duke", r.username().get()); 150 } 151 } 152 153 @ParameterizedTest 154 @EnumSource(VCS.class) 155 void testCurrentBranchOnEmptyRepository(VCS vcs) throws IOException { 156 try (var dir = new TemporaryDirectory()) { 157 var r = Repository.init(dir.path(), vcs); 158 assertEquals(r.defaultBranch(), r.currentBranch()); 159 } 160 } 161 162 @ParameterizedTest 163 @EnumSource(VCS.class) 164 void testCheckout(VCS vcs) throws IOException { 165 try (var dir = new TemporaryDirectory()) { 166 var r = Repository.init(dir.path(), vcs); 167 168 var readme = dir.path().resolve("README"); 169 Files.write(readme, List.of("Hello, readme!")); 170 r.add(readme); 171 172 var head1 = r.commit("Add README", "duke", "duke@openjdk.java.net"); 173 assertEquals(head1, r.head()); 174 175 Files.write(readme, List.of("Another line"), WRITE, APPEND); 176 r.add(readme); 177 178 var head2 = r.commit("Add one more line", "duke", "duke@openjdk.java.net"); 179 assertEquals(head2, r.head()); 180 181 r.checkout(head1, false); 182 assertEquals(head1, r.head()); 183 184 r.checkout(head2, false); 185 assertEquals(head2, r.head()); 186 } 187 } 188 189 @ParameterizedTest 190 @EnumSource(VCS.class) 191 void testLines(VCS vcs) throws IOException { 192 try (var dir = new TemporaryDirectory()) { 193 var r = Repository.init(dir.path(), vcs); 194 195 var readme = dir.path().resolve("README"); 196 Files.write(readme, List.of("Hello, readme!")); 197 r.add(readme); 198 199 var head1 = r.commit("Add README", "duke", "duke@openjdk.java.net"); 200 assertEquals(List.of("Hello, readme!"), 201 r.lines(readme, head1).orElseThrow()); 202 203 Files.write(readme, List.of("Another line"), WRITE, APPEND); 204 r.add(readme); 205 206 var head2 = r.commit("Add one more line", "duke", "duke@openjdk.java.net"); 207 assertEquals(List.of("Hello, readme!", "Another line"), 208 r.lines(readme, head2).orElseThrow()); 209 } 210 } 211 212 @ParameterizedTest 213 @EnumSource(VCS.class) 214 void testLinesInSubdir(VCS vcs) throws IOException { 215 try (var dir = new TemporaryDirectory()) { 216 Repository.init(dir.path(), vcs); 217 218 var subdir = dir.path().resolve("sub"); 219 Files.createDirectories(subdir); 220 var r = Repository.get(subdir).get(); 221 222 var readme = subdir.getParent().resolve("README"); 223 Files.write(readme, List.of("Hello, readme!")); 224 r.add(readme); 225 226 var head = r.commit("Add README", "duke", "duke@openjdk.java.net"); 227 assertEquals(List.of("Hello, readme!"), 228 r.lines(readme, head).orElseThrow()); 229 230 var example = subdir.resolve("EXAMPLE"); 231 Files.write(example, List.of("An example")); 232 r.add(example); 233 234 var head2 = r.commit("Add EXAMPLE", "duke", "duke@openjdk.java.net"); 235 assertEquals(List.of("An example"), 236 r.lines(example, head2).orElseThrow()); 237 } 238 } 239 240 @ParameterizedTest 241 @EnumSource(VCS.class) 242 void testCommitListingOnEmptyRepo(VCS vcs) throws IOException { 243 try (var dir = new TemporaryDirectory()) { 244 var r = Repository.init(dir.path(), vcs); 245 assertTrue(r.commits().asList().isEmpty()); 246 } 247 } 248 249 @ParameterizedTest 250 @EnumSource(VCS.class) 251 void testCommitListingWithSingleCommit(VCS vcs) throws IOException { 252 try (var dir = new TemporaryDirectory()) { 253 var r = Repository.init(dir.path(), vcs); 254 255 var readme = dir.path().resolve("README"); 256 Files.write(readme, List.of("Hello, readme!")); 257 258 r.add(readme); 259 260 var committerName = vcs == VCS.GIT ? "bot" : "duke"; 261 var committerEmail = vcs == VCS.GIT ? "bot@openjdk.java.net" : "duke@openjdk.java.net"; 262 var hash = r.commit("Add README", "duke", "duke@openjdk.java.net", committerName, committerEmail); 263 264 var commits = r.commits().asList(); 265 assertEquals(1, commits.size()); 266 267 var commit = commits.get(0); 268 assertEquals("duke", commit.author().name()); 269 assertEquals("duke@openjdk.java.net", commit.author().email()); 270 assertEquals(committerName, commit.committer().name()); 271 assertEquals(committerEmail, commit.committer().email()); 272 273 assertEquals(List.of("Add README"), commit.message()); 274 275 assertEquals(1, commit.numParents()); 276 assertEquals(1, commit.parents().size()); 277 278 var nullHash = "0".repeat(40); 279 var parent = commit.parents().get(0); 280 assertEquals(nullHash, parent.hex()); 281 282 assertTrue(commit.isInitialCommit()); 283 assertFalse(commit.isMerge()); 284 assertEquals(hash, commit.hash()); 285 286 var diffs = commit.parentDiffs(); 287 assertEquals(1, diffs.size()); 288 289 var diff = diffs.get(0); 290 assertEquals(nullHash, diff.from().hex()); 291 assertEquals(hash, diff.to()); 292 293 assertEquals(0, diff.removed()); 294 assertEquals(0, diff.modified()); 295 assertEquals(1, diff.added()); 296 297 var patches = diff.patches(); 298 assertEquals(1, patches.size()); 299 300 var patch = patches.get(0).asTextualPatch(); 301 assertTrue(patch.status().isAdded()); 302 303 assertTrue(patch.source().path().isEmpty()); 304 assertTrue(patch.source().type().isEmpty()); 305 306 assertEquals(Path.of("README"), patch.target().path().get()); 307 assertTrue(patch.target().type().get().isRegularNonExecutable()); 308 309 var hunks = patch.hunks(); 310 assertEquals(1, hunks.size()); 311 312 var hunk = hunks.get(0); 313 assertEquals(new Range(0, 0), hunk.source().range()); 314 assertEquals(new Range(1, 1), hunk.target().range()); 315 316 assertLinesEquals(List.of(), hunk.source().lines()); 317 assertLinesEquals(List.of("Hello, readme!"), hunk.target().lines()); 318 } 319 } 320 321 static String stripTrailingCR(String line) { 322 return line.endsWith("\r") ? line.substring(0, line.length() - 1) : line; 323 } 324 325 static void assertLinesEquals(List<String> expected, List<String> actual) { 326 assertEquals(expected, actual.stream().map(RepositoryTests::stripTrailingCR).collect(Collectors.toList())); 327 } 328 329 @ParameterizedTest 330 @EnumSource(VCS.class) 331 void testCommitListingWithMultipleCommits(VCS vcs) throws IOException { 332 try (var dir = new TemporaryDirectory()) { 333 var r = Repository.init(dir.path(), vcs); 334 335 var readme = dir.path().resolve("README"); 336 Files.write(readme, List.of("Hello, readme!")); 337 338 r.add(readme); 339 var hash1 = r.commit("Add README", "duke", "duke@openjdk.java.net"); 340 341 Files.write(readme, List.of("Another line"), WRITE, APPEND); 342 r.add(readme); 343 var hash2 = r.commit("Modify README", "duke", "duke@openjdk.java.net"); 344 345 var commits = r.commits().asList(); 346 assertEquals(2, commits.size()); 347 348 var commit = commits.get(0); 349 assertEquals("duke", commit.author().name()); 350 assertEquals("duke@openjdk.java.net", commit.author().email()); 351 352 assertEquals(List.of("Modify README"), commit.message()); 353 354 assertEquals(1, commit.numParents()); 355 assertEquals(1, commit.parents().size()); 356 357 var parent = commit.parents().get(0); 358 assertEquals(hash1, parent); 359 360 assertFalse(commit.isInitialCommit()); 361 assertFalse(commit.isMerge()); 362 assertEquals(hash2, commit.hash()); 363 364 var diffs = commit.parentDiffs(); 365 assertEquals(1, diffs.size()); 366 367 var diff = diffs.get(0); 368 assertEquals(hash1, diff.from()); 369 assertEquals(hash2, diff.to()); 370 371 assertEquals(0, diff.removed()); 372 assertEquals(0, diff.modified()); 373 assertEquals(1, diff.added()); 374 375 var patches = diff.patches(); 376 assertEquals(1, patches.size()); 377 378 var patch = patches.get(0).asTextualPatch(); 379 assertTrue(patch.status().isModified()); 380 assertEquals(Path.of("README"), patch.source().path().get()); 381 assertTrue(patch.source().type().get().isRegularNonExecutable()); 382 assertEquals(Path.of("README"), patch.target().path().get()); 383 assertTrue(patch.target().type().get().isRegularNonExecutable()); 384 385 var hunks = patch.hunks(); 386 assertEquals(1, hunks.size()); 387 388 var hunk = hunks.get(0); 389 assertEquals(new Range(2, 0), hunk.source().range()); 390 assertEquals(new Range(2, 1), hunk.target().range()); 391 392 assertLinesEquals(List.of(), hunk.source().lines()); 393 assertLinesEquals(List.of("Another line"), hunk.target().lines()); 394 } 395 } 396 397 @ParameterizedTest 398 @EnumSource(VCS.class) 399 void testSquashDeletes(VCS vcs) throws IOException { 400 try (var dir = new TemporaryDirectory()) { 401 var r = Repository.init(dir.path(), vcs); 402 403 var file1 = dir.path().resolve("file1.txt"); 404 Files.write(file1, List.of("Hello, file 1!")); 405 var file2 = dir.path().resolve("file2.txt"); 406 Files.write(file2, List.of("Hello, file 2!")); 407 var file3 = dir.path().resolve("file3.txt"); 408 Files.write(file3, List.of("Hello, file 3!")); 409 410 r.add(file1, file2, file3); 411 var hash1 = r.commit("Add files", "duke", "duke@openjdk.java.net"); 412 413 Files.delete(file2); 414 r.remove(file2); 415 var hash2 = r.commit("Remove file 2", "duke", "duke@openjdk.java.net"); 416 417 Files.delete(file3); 418 r.remove(file3); 419 var hash3 = r.commit("Remove file 3", "duke", "duke@openjdk.java.net"); 420 421 var refspec = vcs == VCS.GIT ? r.head().hex() : r.head().hex() + ":0"; 422 assertEquals(3, r.commits(refspec).asList().size()); 423 424 r.checkout(hash1, false); 425 r.squash(hash3); 426 r.commit("Squashed remove of file 2 and 3", "duke", "duke@openjdk.java.net"); 427 428 refspec = vcs == VCS.GIT ? r.head().hex() : r.head().hex() + ":0"; 429 var commits = r.commits(refspec).asList(); 430 assertEquals(2, commits.size()); 431 432 assertEquals(hash1, commits.get(1).hash()); 433 434 var head = commits.get(0); 435 assertNotEquals(hash2, head); 436 assertNotEquals(hash3, head); 437 438 assertEquals(hash1, head.parents().get(0)); 439 assertFalse(head.isInitialCommit()); 440 assertFalse(head.isMerge()); 441 442 var diffs = head.parentDiffs(); 443 assertEquals(1, diffs.size()); 444 445 var diff = diffs.get(0); 446 assertEquals(hash1, diff.from()); 447 assertEquals(head.hash(), diff.to()); 448 449 assertEquals(2, diff.removed()); 450 assertEquals(0, diff.modified()); 451 assertEquals(0, diff.added()); 452 } 453 } 454 455 @ParameterizedTest 456 @EnumSource(VCS.class) 457 void testSquash(VCS vcs) throws IOException { 458 try (var dir = new TemporaryDirectory()) { 459 var r = Repository.init(dir.path(), vcs); 460 461 var readme = dir.path().resolve("README"); 462 Files.write(readme, List.of("Hello, readme!")); 463 464 r.add(readme); 465 var hash1 = r.commit("Add README", "duke", "duke@openjdk.java.net"); 466 467 Files.write(readme, List.of("Another line"), WRITE, APPEND); 468 r.add(readme); 469 var hash2 = r.commit("Modify README", "duke", "duke@openjdk.java.net"); 470 471 Files.write(readme, List.of("A final line"), WRITE, APPEND); 472 r.add(readme); 473 var hash3 = r.commit("Modify README again", "duke", "duke@openjdk.java.net"); 474 475 var refspec = vcs == VCS.GIT ? r.head().hex() : r.head().hex() + ":0"; 476 assertEquals(3, r.commits(refspec).asList().size()); 477 478 r.checkout(hash1, false); 479 r.squash(hash3); 480 r.commit("Squashed commits 2 and 3", "duke", "duke@openjdk.java.net"); 481 482 refspec = vcs == VCS.GIT ? r.head().hex() : r.head().hex() + ":0"; 483 var commits = r.commits(refspec).asList(); 484 assertEquals(2, commits.size()); 485 486 assertEquals(hash1, commits.get(1).hash()); 487 488 var head = commits.get(0); 489 assertNotEquals(hash2, head); 490 assertNotEquals(hash3, head); 491 492 assertEquals(hash1, head.parents().get(0)); 493 assertFalse(head.isInitialCommit()); 494 assertFalse(head.isMerge()); 495 496 var diffs = head.parentDiffs(); 497 assertEquals(1, diffs.size()); 498 499 var diff = diffs.get(0); 500 assertEquals(hash1, diff.from()); 501 assertEquals(head.hash(), diff.to()); 502 503 assertEquals(0, diff.removed()); 504 assertEquals(0, diff.modified()); 505 assertEquals(2, diff.added()); 506 507 var patches = diff.patches(); 508 assertEquals(1, patches.size()); 509 510 var patch = patches.get(0).asTextualPatch(); 511 assertTrue(patch.status().isModified()); 512 assertEquals(Path.of("README"), patch.source().path().get()); 513 assertTrue(patch.source().type().get().isRegularNonExecutable()); 514 assertEquals(Path.of("README"), patch.target().path().get()); 515 assertTrue(patch.target().type().get().isRegularNonExecutable()); 516 517 var hunks = patch.hunks(); 518 assertEquals(1, hunks.size()); 519 520 var hunk = hunks.get(0); 521 assertEquals(new Range(2, 0), hunk.source().range()); 522 assertEquals(new Range(2, 2), hunk.target().range()); 523 524 assertLinesEquals(List.of(), hunk.source().lines()); 525 assertLinesEquals(List.of("Another line", "A final line"), hunk.target().lines()); 526 } 527 } 528 529 @ParameterizedTest 530 @EnumSource(VCS.class) 531 void testMergeBase(VCS vcs) throws IOException { 532 try (var dir = new TemporaryDirectory()) { 533 var r = Repository.init(dir.path(), vcs); 534 535 var readme = dir.path().resolve("README"); 536 Files.write(readme, List.of("Hello, readme!")); 537 538 r.add(readme); 539 var hash1 = r.commit("Add README", "duke", "duke@openjdk.java.net"); 540 541 Files.write(readme, List.of("Another line"), WRITE, APPEND); 542 r.add(readme); 543 var hash2 = r.commit("Modify README", "duke", "duke@openjdk.java.net"); 544 545 r.checkout(hash1, false); 546 Files.write(readme, List.of("A conflicting line"), WRITE, APPEND); 547 r.add(readme); 548 var hash3 = r.commit("Branching README modification", "duke", "duke@openjdk.java.net"); 549 550 assertEquals(hash1, r.mergeBase(hash2, hash3)); 551 } 552 } 553 554 @ParameterizedTest 555 @EnumSource(VCS.class) 556 void testRebase(VCS vcs) throws IOException { 557 try (var dir = new TemporaryDirectory()) { 558 var r = Repository.init(dir.path(), vcs); 559 560 var readme = dir.path().resolve("README"); 561 Files.write(readme, List.of("Hello, readme!")); 562 563 r.add(readme); 564 var hash1 = r.commit("Add README", "duke", "duke@openjdk.java.net"); 565 566 Files.write(readme, List.of("Another line"), WRITE, APPEND); 567 r.add(readme); 568 var hash2 = r.commit("Modify README", "duke", "duke@openjdk.java.net"); 569 570 r.checkout(hash1, false); 571 572 var contributing = dir.path().resolve("CONTRIBUTING"); 573 Files.write(contributing, List.of("Keep the patches coming")); 574 r.add(contributing); 575 var hash3 = r.commit("Add independent change", "duke", "duke@openjdk.java.net"); 576 577 var committerName = vcs == VCS.GIT ? "bot" : "duke"; 578 var committerEmail = vcs == VCS.GIT ? "bot@openjdk.java.net" : "duke@openjdk.java.net"; 579 r.rebase(hash2, committerName, committerEmail); 580 581 var refspec = vcs == VCS.GIT ? r.head().hex() : r.head().hex() + ":0"; 582 var commits = r.commits(refspec).asList(); 583 assertEquals(3, commits.size()); 584 assertEquals(hash2, commits.get(1).hash()); 585 assertEquals(hash1, commits.get(2).hash()); 586 587 assertEquals("duke", commits.get(0).author().name()); 588 assertEquals("duke@openjdk.java.net", commits.get(0).author().email()); 589 assertEquals(committerName, commits.get(0).committer().name()); 590 assertEquals(committerEmail, commits.get(0).committer().email()); 591 592 assertEquals("duke", commits.get(1).author().name()); 593 assertEquals("duke@openjdk.java.net", commits.get(1).author().email()); 594 assertEquals("duke", commits.get(1).committer().name()); 595 assertEquals("duke@openjdk.java.net", commits.get(1).committer().email()); 596 597 assertEquals("duke", commits.get(2).author().name()); 598 assertEquals("duke@openjdk.java.net", commits.get(2).author().email()); 599 assertEquals("duke", commits.get(2).committer().name()); 600 assertEquals("duke@openjdk.java.net", commits.get(2).committer().email()); 601 602 var head = commits.get(0); 603 assertEquals(hash2, head.parents().get(0)); 604 assertEquals(List.of("Add independent change"), head.message()); 605 606 var diffs = head.parentDiffs(); 607 assertEquals(1, diffs.size()); 608 var diff = diffs.get(0); 609 610 assertEquals(0, diff.removed()); 611 assertEquals(0, diff.modified()); 612 assertEquals(1, diff.added()); 613 614 var patches = diff.patches(); 615 assertEquals(1, patches.size()); 616 var patch = patches.get(0).asTextualPatch(); 617 assertEquals(Path.of("CONTRIBUTING"), patch.target().path().get()); 618 619 var hunks = patch.hunks(); 620 assertEquals(1, hunks.size()); 621 var hunk = hunks.get(0); 622 assertLinesEquals(List.of("Keep the patches coming"), hunk.target().lines()); 623 } 624 } 625 626 @ParameterizedTest 627 @EnumSource(VCS.class) 628 void testInitializedRepositoryIsEmpty(VCS vcs) throws IOException { 629 try (var dir = new TemporaryDirectory()) { 630 var r = Repository.init(dir.path(), vcs); 631 assertTrue(r.isEmpty()); 632 } 633 } 634 635 @ParameterizedTest 636 @EnumSource(VCS.class) 637 void testRepositoryWithCommitIsNonEmpty(VCS vcs) throws IOException { 638 try (var dir = new TemporaryDirectory()) { 639 var r = Repository.init(dir.path(), vcs); 640 641 var readme = dir.path().resolve("README"); 642 Files.write(readme, List.of("Hello, readme!")); 643 644 r.add(readme); 645 r.commit("Add README", "duke", "duke@openjdk.java.net"); 646 647 assertFalse(r.isEmpty()); 648 } 649 } 650 651 @ParameterizedTest 652 @EnumSource(VCS.class) 653 void testEmptyRepositoryIsHealthy(VCS vcs) throws IOException { 654 try (var dir = new TemporaryDirectory()) { 655 var r = Repository.init(dir.path(), vcs); 656 assertTrue(r.isHealthy()); 657 } 658 } 659 660 @ParameterizedTest 661 @EnumSource(VCS.class) 662 void testNonEmptyRepositoryIsHealthy(VCS vcs) throws IOException { 663 try (var dir = new TemporaryDirectory()) { 664 var r = Repository.init(dir.path(), vcs); 665 666 var readme = dir.path().resolve("README"); 667 Files.write(readme, List.of("Hello, readme!")); 668 669 r.add(readme); 670 r.commit("Add README", "duke", "duke@openjdk.java.net"); 671 672 assertTrue(r.isHealthy()); 673 } 674 } 675 676 @ParameterizedTest 677 @EnumSource(VCS.class) 678 void testNonCheckedOutRepositoryIsHealthy(VCS vcs) throws IOException { 679 try (var dir1 = new TemporaryDirectory(); 680 var dir2 = new TemporaryDirectory()) { 681 var r1 = Repository.init(dir1.path(), vcs); 682 683 var readme = dir1.path().resolve("README"); 684 Files.write(readme, List.of("Hello, readme!")); 685 686 r1.add(readme); 687 var hash = r1.commit("Add README", "duke", "duke@openjdk.java.net"); 688 r1.tag(hash, "tag", "tagging", "duke", "duke@openjdk.java.net"); 689 690 var r2 = Repository.init(dir2.path(), vcs); 691 r2.fetch(r1.root().toUri(), r1.defaultBranch().name()); 692 693 assertTrue(r2.isHealthy()); 694 } 695 } 696 697 @ParameterizedTest 698 @EnumSource(VCS.class) 699 void testBranchesOnEmptyRepository(VCS vcs) throws IOException { 700 try (var dir = new TemporaryDirectory()) { 701 var r = Repository.init(dir.path(), vcs); 702 var expected = vcs == VCS.GIT ? List.of() : List.of(new Branch("default")); 703 assertEquals(List.of(), r.branches()); 704 } 705 } 706 707 @ParameterizedTest 708 @EnumSource(VCS.class) 709 void testBranchesOnNonEmptyRepository(VCS vcs) throws IOException { 710 try (var dir = new TemporaryDirectory()) { 711 var r = Repository.init(dir.path(), vcs); 712 713 var readme = dir.path().resolve("README"); 714 Files.write(readme, List.of("Hello, readme!")); 715 716 r.add(readme); 717 r.commit("Add README", "duke", "duke@openjdk.java.net"); 718 719 assertEquals(List.of(r.defaultBranch()), r.branches()); 720 } 721 } 722 723 @ParameterizedTest 724 @EnumSource(VCS.class) 725 void testTagsOnEmptyRepository(VCS vcs) throws IOException { 726 try (var dir = new TemporaryDirectory()) { 727 var r = Repository.init(dir.path(), vcs); 728 var expected = vcs == VCS.GIT ? List.of() : List.of(new Tag("tip")); 729 assertEquals(expected, r.tags()); 730 } 731 } 732 733 @ParameterizedTest 734 @EnumSource(VCS.class) 735 void testTagsOnNonEmptyRepository(VCS vcs) throws IOException { 736 try (var dir = new TemporaryDirectory()) { 737 var r = Repository.init(dir.path(), vcs); 738 739 var readme = dir.path().resolve("README"); 740 Files.write(readme, List.of("Hello, readme!")); 741 742 r.add(readme); 743 r.commit("Add README", "duke", "duke@openjdk.java.net"); 744 745 var expected = vcs == VCS.GIT ? List.of() : List.of(new Tag("tip")); 746 assertEquals(expected, r.tags()); 747 } 748 } 749 750 @ParameterizedTest 751 @EnumSource(VCS.class) 752 void testFetchAndPush(VCS vcs) throws IOException { 753 try (var dir = new TemporaryDirectory()) { 754 var upstream = Repository.init(dir.path(), vcs); 755 756 if (vcs == VCS.GIT) { 757 Files.write(upstream.root().resolve(".git").resolve("config"), 758 List.of("[receive]", "denyCurrentBranch=ignore"), 759 WRITE, APPEND); 760 } 761 762 var readme = dir.path().resolve("README"); 763 Files.write(readme, List.of("Hello, readme!")); 764 765 upstream.add(readme); 766 upstream.commit("Add README", "duke", "duke@openjdk.java.net"); 767 768 try (var dir2 = new TemporaryDirectory()) { 769 var downstream = Repository.init(dir2.path(), vcs); 770 771 // note: forcing unix path separators for URI 772 var upstreamURI = URI.create("file:///" + dir.toString().replace('\\', '/')); 773 774 var fetchHead = downstream.fetch(upstreamURI, downstream.defaultBranch().name()); 775 downstream.checkout(fetchHead, false); 776 777 var downstreamReadme = dir2.path().resolve("README"); 778 Files.write(downstreamReadme, List.of("Downstream change"), WRITE, APPEND); 779 780 downstream.add(downstreamReadme); 781 var head = downstream.commit("Modify README", "duke", "duke@openjdk.java.net"); 782 783 downstream.push(head, upstreamURI, downstream.defaultBranch().name()); 784 } 785 786 upstream.checkout(upstream.resolve(upstream.defaultBranch().name()).get(), false); 787 788 var commits = upstream.commits().asList(); 789 assertEquals(2, commits.size()); 790 } 791 } 792 793 @ParameterizedTest 794 @EnumSource(VCS.class) 795 void testClean(VCS vcs) throws IOException { 796 try (var dir = new TemporaryDirectory()) { 797 var r = Repository.init(dir.path(), vcs); 798 r.clean(); 799 800 var readme = dir.path().resolve("README"); 801 Files.write(readme, List.of("Hello, readme!")); 802 803 r.add(readme); 804 var hash1 = r.commit("Add README", "duke", "duke@openjdk.java.net"); 805 806 r.clean(); 807 808 assertEquals(hash1, r.head()); 809 810 Files.write(readme, List.of("A random change"), WRITE, APPEND); 811 812 r.clean(); 813 814 assertEquals(List.of("Hello, readme!"), Files.readAllLines(readme)); 815 816 var untracked = dir.path().resolve("UNTRACKED"); 817 Files.write(untracked, List.of("Random text")); 818 819 r.clean(); 820 821 assertFalse(Files.exists(untracked)); 822 823 // Mercurial cannot currently deal with this situation 824 if (vcs != VCS.HG) { 825 var subRepo = Repository.init(dir.path().resolve("submodule"), vcs); 826 var subRepoFile = subRepo.root().resolve("file.txt"); 827 Files.write(subRepoFile, List.of("Looks like a file in a submodule")); 828 829 r.clean(); 830 831 assertFalse(Files.exists(subRepoFile)); 832 } 833 } 834 } 835 836 @ParameterizedTest 837 @EnumSource(VCS.class) 838 void testCleanIgnored(VCS vcs) throws IOException { 839 try (var dir = new TemporaryDirectory()) { 840 var r = Repository.init(dir.path(), vcs); 841 r.clean(); 842 843 var readme = dir.path().resolve("README"); 844 Files.write(readme, List.of("Hello, readme!")); 845 Files.write(dir.path().resolve(".gitignore"), List.of("*.txt")); 846 Files.write(dir.path().resolve(".hgignore"), List.of(".*txt")); 847 848 r.add(readme); 849 var hash1 = r.commit("Add README", "duke", "duke@openjdk.java.net"); 850 851 var ignored = dir.path().resolve("ignored.txt"); 852 Files.write(ignored, List.of("Random text")); 853 854 r.clean(); 855 856 assertFalse(Files.exists(ignored)); 857 } 858 } 859 860 @ParameterizedTest 861 @EnumSource(VCS.class) 862 void testDiffBetweenCommits(VCS vcs) throws IOException { 863 try (var dir = new TemporaryDirectory()) { 864 var r = Repository.init(dir.path(), vcs); 865 866 var readme = dir.path().resolve("README"); 867 Files.write(readme, List.of("Hello, readme!")); 868 869 r.add(readme); 870 var first = r.commit("Add README", "duke", "duke@openjdk.java.net"); 871 872 Files.write(readme, List.of("One more line"), WRITE, APPEND); 873 r.add(readme); 874 var second = r.commit("Add one more line", "duke", "duke@openjdk.java.net"); 875 876 var diff = r.diff(first, second); 877 assertEquals(first, diff.from()); 878 assertEquals(second, diff.to()); 879 880 var patches = diff.patches(); 881 assertEquals(1, patches.size()); 882 883 var patch = patches.get(0).asTextualPatch(); 884 assertEquals(Path.of("README"), patch.source().path().get()); 885 assertEquals(Path.of("README"), patch.target().path().get()); 886 assertTrue(patch.source().type().get().isRegularNonExecutable()); 887 assertTrue(patch.target().type().get().isRegularNonExecutable()); 888 assertTrue(patch.status().isModified()); 889 890 var hunks = patch.hunks(); 891 assertEquals(1, hunks.size()); 892 893 var hunk = hunks.get(0); 894 assertEquals(2, hunk.source().range().start()); 895 assertEquals(0, hunk.source().range().count()); 896 assertEquals(0, hunk.source().lines().size()); 897 898 assertEquals(2, hunk.target().range().start()); 899 assertEquals(1, hunk.target().range().count()); 900 assertLinesEquals(List.of("One more line"), hunk.target().lines()); 901 902 assertEquals(1, hunk.added()); 903 assertEquals(0, hunk.removed()); 904 assertEquals(0, hunk.modified()); 905 } 906 } 907 908 @ParameterizedTest 909 @EnumSource(VCS.class) 910 void testDiffBetweenCommitsWithMultiplePatches(VCS vcs) throws IOException { 911 try (var dir = new TemporaryDirectory()) { 912 var r = Repository.init(dir.path(), vcs); 913 914 var readme = dir.path().resolve("README"); 915 Files.write(readme, List.of("Hello, readme!")); 916 917 var building = dir.path().resolve("BUILDING"); 918 Files.write(building, List.of("make")); 919 920 r.add(readme); 921 r.add(building); 922 var first = r.commit("Add README and BUILDING", "duke", "duke@openjdk.java.net"); 923 924 Files.write(readme, List.of("Hello, Skara!"), WRITE, TRUNCATE_EXISTING); 925 Files.write(building, List.of("make images"), WRITE, TRUNCATE_EXISTING); 926 r.add(readme); 927 r.add(building); 928 var second = r.commit("Modify README and BUILDING", "duke", "duke@openjdk.java.net"); 929 930 var diff = r.diff(first, second); 931 assertEquals(first, diff.from()); 932 assertEquals(second, diff.to()); 933 934 var patches = diff.patches(); 935 assertEquals(2, patches.size()); 936 937 var patch1 = patches.get(0).asTextualPatch(); 938 assertEquals(Path.of("BUILDING"), patch1.source().path().get()); 939 assertEquals(Path.of("BUILDING"), patch1.target().path().get()); 940 assertTrue(patch1.source().type().get().isRegularNonExecutable()); 941 assertTrue(patch1.target().type().get().isRegularNonExecutable()); 942 assertTrue(patch1.status().isModified()); 943 944 var hunks1 = patch1.hunks(); 945 assertEquals(1, hunks1.size()); 946 947 var hunk1 = hunks1.get(0); 948 assertEquals(1, hunk1.source().range().start()); 949 assertEquals(1, hunk1.source().range().count()); 950 assertLinesEquals(List.of("make"), hunk1.source().lines()); 951 952 assertEquals(1, hunk1.target().range().start()); 953 assertEquals(1, hunk1.target().range().count()); 954 assertLinesEquals(List.of("make images"), hunk1.target().lines()); 955 956 var patch2 = patches.get(1).asTextualPatch(); 957 assertEquals(Path.of("README"), patch2.source().path().get()); 958 assertEquals(Path.of("README"), patch2.target().path().get()); 959 assertTrue(patch2.source().type().get().isRegularNonExecutable()); 960 assertTrue(patch2.target().type().get().isRegularNonExecutable()); 961 assertTrue(patch2.status().isModified()); 962 963 var hunks2 = patch2.hunks(); 964 assertEquals(1, hunks2.size()); 965 966 var hunk2 = hunks2.get(0); 967 assertEquals(1, hunk2.source().range().start()); 968 assertEquals(1, hunk2.source().range().count()); 969 assertLinesEquals(List.of("Hello, readme!"), hunk2.source().lines()); 970 971 assertEquals(1, hunk2.target().range().start()); 972 assertEquals(1, hunk2.target().range().count()); 973 assertLinesEquals(List.of("Hello, Skara!"), hunk2.target().lines()); 974 } 975 } 976 977 @ParameterizedTest 978 @EnumSource(VCS.class) 979 void testDiffBetweenCommitsWithMultipleHunks(VCS vcs) throws IOException { 980 try (var dir = new TemporaryDirectory()) { 981 var r = Repository.init(dir.path(), vcs); 982 983 var abc = dir.path().resolve("abc.txt"); 984 Files.write(abc, List.of("A", "B", "C")); 985 986 r.add(abc); 987 var first = r.commit("Added ABC", "duke", "duke@openjdk.java.net"); 988 989 Files.write(abc, List.of("1", "2", "B", "3"), WRITE, TRUNCATE_EXISTING); 990 r.add(abc); 991 var second = r.commit("Modify A and C", "duke", "duke@openjdk.java.net"); 992 993 var diff = r.diff(first, second); 994 assertEquals(first, diff.from()); 995 assertEquals(second, diff.to()); 996 997 var patches = diff.patches(); 998 assertEquals(1, patches.size()); 999 1000 var patch = patches.get(0).asTextualPatch(); 1001 assertEquals(Path.of("abc.txt"), patch.source().path().get()); 1002 assertEquals(Path.of("abc.txt"), patch.target().path().get()); 1003 assertTrue(patch.source().type().get().isRegularNonExecutable()); 1004 assertTrue(patch.target().type().get().isRegularNonExecutable()); 1005 assertTrue(patch.status().isModified()); 1006 1007 var hunks = patch.hunks(); 1008 assertEquals(2, hunks.size()); 1009 1010 var hunk1 = hunks.get(0); 1011 assertEquals(1, hunk1.source().range().start()); 1012 assertEquals(1, hunk1.source().range().count()); 1013 assertLinesEquals(List.of("A"), hunk1.source().lines()); 1014 1015 assertEquals(1, hunk1.target().range().start()); 1016 assertEquals(2, hunk1.target().range().count()); 1017 assertLinesEquals(List.of("1", "2"), hunk1.target().lines()); 1018 1019 assertEquals(1, hunk1.added()); 1020 assertEquals(0, hunk1.removed()); 1021 assertEquals(1, hunk1.modified()); 1022 1023 var hunk2 = hunks.get(1); 1024 assertEquals(3, hunk2.source().range().start()); 1025 assertEquals(1, hunk2.source().range().count()); 1026 assertLinesEquals(List.of("C"), hunk2.source().lines()); 1027 1028 assertEquals(4, hunk2.target().range().start()); 1029 assertEquals(1, hunk2.target().range().count()); 1030 assertLinesEquals(List.of("3"), hunk2.target().lines()); 1031 1032 assertEquals(0, hunk2.added()); 1033 assertEquals(0, hunk2.removed()); 1034 assertEquals(1, hunk2.modified()); 1035 } 1036 } 1037 1038 @ParameterizedTest 1039 @EnumSource(VCS.class) 1040 void testDiffWithRemoval(VCS vcs) throws IOException { 1041 try (var dir = new TemporaryDirectory()) { 1042 var r = Repository.init(dir.path(), vcs); 1043 1044 var readme = dir.path().resolve("README"); 1045 Files.write(readme, List.of("Hello, world!")); 1046 1047 r.add(readme); 1048 var first = r.commit("Added README", "duke", "duke@openjdk.java.net"); 1049 1050 Files.delete(readme); 1051 r.remove(readme); 1052 var second = r.commit("Removed README", "duke", "duke@openjdk.java.net"); 1053 1054 var diff = r.diff(first, second); 1055 assertEquals(first, diff.from()); 1056 assertEquals(second, diff.to()); 1057 1058 var patches = diff.patches(); 1059 assertEquals(1, patches.size()); 1060 1061 var patch = patches.get(0).asTextualPatch(); 1062 assertEquals(Path.of("README"), patch.source().path().get()); 1063 assertTrue(patch.target().path().isEmpty()); 1064 assertTrue(patch.source().type().get().isRegularNonExecutable()); 1065 assertTrue(patch.target().type().isEmpty()); 1066 assertTrue(patch.status().isDeleted()); 1067 1068 var hunks = patch.hunks(); 1069 assertEquals(1, hunks.size()); 1070 1071 var hunk = hunks.get(0); 1072 assertEquals(1, hunk.source().range().start()); 1073 assertEquals(1, hunk.source().range().count()); 1074 assertLinesEquals(List.of("Hello, world!"), hunk.source().lines()); 1075 1076 assertEquals(0, hunk.target().range().start()); 1077 assertEquals(0, hunk.target().range().count()); 1078 assertLinesEquals(List.of(), hunk.target().lines()); 1079 1080 assertEquals(0, hunk.added()); 1081 assertEquals(1, hunk.removed()); 1082 assertEquals(0, hunk.modified()); 1083 } 1084 } 1085 1086 @ParameterizedTest 1087 @EnumSource(VCS.class) 1088 void testDiffWithAddition(VCS vcs) throws IOException { 1089 try (var dir = new TemporaryDirectory()) { 1090 var r = Repository.init(dir.path(), vcs); 1091 1092 var readme = dir.path().resolve("README"); 1093 Files.write(readme, List.of("Hello, world!")); 1094 1095 r.add(readme); 1096 var first = r.commit("Added README", "duke", "duke@openjdk.java.net"); 1097 1098 var building = dir.path().resolve("BUILDING"); 1099 Files.write(building, List.of("make")); 1100 r.add(building); 1101 var second = r.commit("Added BUILDING", "duke", "duke@openjdk.java.net"); 1102 1103 var diff = r.diff(first, second); 1104 assertEquals(first, diff.from()); 1105 assertEquals(second, diff.to()); 1106 1107 var patches = diff.patches(); 1108 assertEquals(1, patches.size()); 1109 1110 var patch = patches.get(0).asTextualPatch(); 1111 assertTrue(patch.source().path().isEmpty()); 1112 assertEquals(Path.of("BUILDING"), patch.target().path().get()); 1113 assertTrue(patch.source().type().isEmpty()); 1114 assertTrue(patch.target().type().get().isRegularNonExecutable()); 1115 assertTrue(patch.status().isAdded()); 1116 1117 var hunks = patch.hunks(); 1118 assertEquals(1, hunks.size()); 1119 1120 var hunk = hunks.get(0); 1121 assertEquals(0, hunk.source().range().start()); 1122 assertEquals(0, hunk.source().range().count()); 1123 assertLinesEquals(List.of(), hunk.source().lines()); 1124 1125 assertEquals(1, hunk.target().range().start()); 1126 assertEquals(1, hunk.target().range().count()); 1127 assertLinesEquals(List.of("make"), hunk.target().lines()); 1128 1129 assertEquals(1, hunk.added()); 1130 assertEquals(0, hunk.removed()); 1131 assertEquals(0, hunk.modified()); 1132 } 1133 } 1134 1135 @ParameterizedTest 1136 @EnumSource(VCS.class) 1137 void testDiffWithWorkingDir(VCS vcs) throws IOException { 1138 try (var dir = new TemporaryDirectory()) { 1139 var r = Repository.init(dir.path(), vcs); 1140 1141 var readme = dir.path().resolve("README"); 1142 Files.write(readme, List.of("Hello, world!")); 1143 1144 r.add(readme); 1145 var first = r.commit("Added README", "duke", "duke@openjdk.java.net"); 1146 1147 Files.write(readme, List.of("One more line"), WRITE, APPEND); 1148 var diff = r.diff(first); 1149 1150 assertEquals(first, diff.from()); 1151 assertNull(diff.to()); 1152 1153 var patches = diff.patches(); 1154 assertEquals(1, patches.size()); 1155 1156 var patch = patches.get(0).asTextualPatch(); 1157 assertEquals(Path.of("README"), patch.source().path().get()); 1158 assertEquals(Path.of("README"), patch.target().path().get()); 1159 assertTrue(patch.source().type().get().isRegularNonExecutable()); 1160 assertTrue(patch.target().type().get().isRegularNonExecutable()); 1161 assertTrue(patch.status().isModified()); 1162 1163 var hunks = patch.hunks(); 1164 assertEquals(1, hunks.size()); 1165 1166 var hunk = hunks.get(0); 1167 assertEquals(2, hunk.source().range().start()); 1168 assertEquals(0, hunk.source().range().count()); 1169 assertLinesEquals(List.of(), hunk.source().lines()); 1170 1171 assertEquals(2, hunk.target().range().start()); 1172 assertEquals(1, hunk.target().range().count()); 1173 assertLinesEquals(List.of("One more line"), hunk.target().lines()); 1174 1175 assertEquals(1, hunk.added()); 1176 assertEquals(0, hunk.removed()); 1177 assertEquals(0, hunk.modified()); 1178 } 1179 } 1180 1181 @ParameterizedTest 1182 @EnumSource(VCS.class) 1183 void testCommitMetadata(VCS vcs) throws IOException { 1184 try (var dir = new TemporaryDirectory()) { 1185 var r = Repository.init(dir.path(), vcs); 1186 1187 var readme = dir.path().resolve("README"); 1188 Files.write(readme, List.of("Hello, world!")); 1189 r.add(readme); 1190 var first = r.commit("Added README", "duke", "duke@openjdk.java.net"); 1191 1192 Files.write(readme, List.of("One more line"), WRITE, APPEND); 1193 r.add(readme); 1194 var second = r.commit("Modified README", "duke", "duke@openjdk.java.net"); 1195 1196 var metadata = r.commitMetadata(); 1197 assertEquals(2, metadata.size()); 1198 1199 assertEquals(first, metadata.get(0).hash()); 1200 assertEquals(List.of("Added README"), metadata.get(0).message()); 1201 1202 assertEquals(second, metadata.get(1).hash()); 1203 assertEquals(List.of("Modified README"), metadata.get(1).message()); 1204 } 1205 } 1206 1207 @ParameterizedTest 1208 @EnumSource(VCS.class) 1209 void testTrivialMerge(VCS vcs) throws IOException { 1210 try (var dir = new TemporaryDirectory()) { 1211 var r = Repository.init(dir.path(), vcs); 1212 1213 var readme = dir.path().resolve("README"); 1214 Files.write(readme, List.of("Hello, world!")); 1215 r.add(readme); 1216 var first = r.commit("Added README", "duke", "duke@openjdk.java.net"); 1217 1218 Files.write(readme, List.of("One more line"), WRITE, APPEND); 1219 r.add(readme); 1220 var second = r.commit("Modified README", "duke", "duke@openjdk.java.net"); 1221 1222 r.checkout(first, false); 1223 1224 var contributing = dir.path().resolve("CONTRIBUTING"); 1225 Files.write(contributing, List.of("Send those patches!")); 1226 r.add(contributing); 1227 var third = r.commit("Added contributing", "duke", "duke@openjdk.java.net"); 1228 1229 r.merge(second); 1230 r.commit("Merge", "duke", "duke@openjdk.java.net"); 1231 1232 var refspec = vcs == VCS.GIT ? r.head().hex() : r.head().hex() + ":0"; 1233 var commits = r.commits(refspec).asList(); 1234 1235 assertEquals(4, commits.size()); 1236 1237 var merge = commits.get(0); 1238 assertEquals(List.of("Merge"), merge.message()); 1239 1240 var parents = new HashSet<>(merge.parents()); 1241 assertEquals(2, parents.size()); 1242 assertTrue(parents.contains(second)); 1243 assertTrue(parents.contains(third)); 1244 1245 var diffs = merge.parentDiffs(); 1246 assertEquals(2, diffs.size()); 1247 1248 var diff1 = diffs.get(0); 1249 assertEquals(merge.hash(), diff1.to()); 1250 assertEquals(0, diff1.patches().size()); 1251 assertTrue(parents.contains(diff1.from())); 1252 1253 var diff2 = diffs.get(1); 1254 assertEquals(merge.hash(), diff2.to()); 1255 assertEquals(0, diff2.patches().size()); 1256 assertTrue(parents.contains(diff2.from())); 1257 } 1258 } 1259 1260 @ParameterizedTest 1261 @EnumSource(VCS.class) 1262 void testMergeWithEdit(VCS vcs) throws IOException { 1263 try (var dir = new TemporaryDirectory()) { 1264 var r = Repository.init(dir.path(), vcs); 1265 1266 var readme = dir.path().resolve("README"); 1267 Files.write(readme, List.of("Hello, world!")); 1268 r.add(readme); 1269 var first = r.commit("Added README", "duke", "duke@openjdk.java.net"); 1270 1271 Files.write(readme, List.of("One more line"), WRITE, APPEND); 1272 r.add(readme); 1273 var second = r.commit("Modified README", "duke", "duke@openjdk.java.net"); 1274 1275 r.checkout(first, false); 1276 1277 var contributing = dir.path().resolve("CONTRIBUTING"); 1278 Files.write(contributing, List.of("Send those patches!")); 1279 r.add(contributing); 1280 var third = r.commit("Added contributing", "duke", "duke@openjdk.java.net"); 1281 1282 r.merge(second); 1283 1284 Files.write(readme, List.of("One last line"), WRITE, APPEND); 1285 r.add(readme); 1286 r.commit("Merge", "duke", "duke@openjdk.java.net"); 1287 1288 var refspec = vcs == VCS.GIT ? r.head().hex() : r.head().hex() + ":0"; 1289 var commits = r.commits(refspec).asList(); 1290 1291 assertEquals(4, commits.size()); 1292 1293 var merge = commits.get(0); 1294 assertEquals(List.of("Merge"), merge.message()); 1295 1296 var parents = new HashSet<>(merge.parents()); 1297 assertEquals(2, parents.size()); 1298 assertTrue(parents.contains(second)); 1299 assertTrue(parents.contains(third)); 1300 1301 var diffs = merge.parentDiffs(); 1302 assertEquals(2, diffs.size()); 1303 1304 var secondDiff = diffs.stream().filter(d -> d.from().equals(second)).findFirst().get(); 1305 assertEquals(merge.hash(), secondDiff.to()); 1306 assertEquals(1, secondDiff.patches().size()); 1307 var secondPatch = secondDiff.patches().get(0).asTextualPatch(); 1308 1309 assertEquals(Path.of("README"), secondPatch.source().path().get()); 1310 assertEquals(Path.of("README"), secondPatch.target().path().get()); 1311 assertTrue(secondPatch.status().isModified()); 1312 assertEquals(1, secondPatch.hunks().size()); 1313 1314 var secondHunk = secondPatch.hunks().get(0); 1315 assertLinesEquals(List.of(), secondHunk.source().lines()); 1316 assertLinesEquals(List.of("One last line"), secondHunk.target().lines()); 1317 1318 assertEquals(3, secondHunk.source().range().start()); 1319 assertEquals(0, secondHunk.source().range().count()); 1320 assertEquals(3, secondHunk.target().range().start()); 1321 assertEquals(1, secondHunk.target().range().count()); 1322 1323 var thirdDiff = diffs.stream().filter(d -> d.from().equals(third)).findFirst().get(); 1324 assertEquals(merge.hash(), thirdDiff.to()); 1325 assertEquals(1, thirdDiff.patches().size()); 1326 var thirdPatch = thirdDiff.patches().get(0).asTextualPatch(); 1327 1328 assertEquals(Path.of("README"), thirdPatch.source().path().get()); 1329 assertEquals(Path.of("README"), thirdPatch.target().path().get()); 1330 assertTrue(thirdPatch.status().isModified()); 1331 assertEquals(1, thirdPatch.hunks().size()); 1332 1333 var thirdHunk = thirdPatch.hunks().get(0); 1334 assertLinesEquals(List.of(), thirdHunk.source().lines()); 1335 assertLinesEquals(List.of("One more line", "One last line"), thirdHunk.target().lines()); 1336 1337 assertEquals(2, thirdHunk.source().range().start()); 1338 assertEquals(0, thirdHunk.source().range().count()); 1339 assertEquals(2, thirdHunk.target().range().start()); 1340 assertEquals(2, thirdHunk.target().range().count()); 1341 } 1342 } 1343 1344 @ParameterizedTest 1345 @EnumSource(VCS.class) 1346 void testDefaultBranch(VCS vcs) throws IOException { 1347 try (var dir = new TemporaryDirectory()) { 1348 var r = Repository.init(dir.path(), vcs); 1349 var expected = vcs == VCS.GIT ? "master" : "default"; 1350 assertEquals(expected, r.defaultBranch().name()); 1351 } 1352 } 1353 1354 @ParameterizedTest 1355 @EnumSource(VCS.class) 1356 void testPaths(VCS vcs) throws IOException { 1357 try (var dir = new TemporaryDirectory()) { 1358 var r = Repository.init(dir.path(), vcs); 1359 var remote = vcs == VCS.GIT ? "origin" : "default"; 1360 r.setPaths(remote, "http://pull", "http://push"); 1361 assertEquals("http://pull", r.pullPath(remote)); 1362 assertEquals("http://push", r.pushPath(remote)); 1363 } 1364 } 1365 1366 @ParameterizedTest 1367 @EnumSource(VCS.class) 1368 void testIsValidRevisionRange(VCS vcs) throws IOException { 1369 try (var dir = new TemporaryDirectory()) { 1370 var r = Repository.init(dir.path(), vcs); 1371 assertFalse(r.isValidRevisionRange("foo")); 1372 1373 var readme = dir.path().resolve("README"); 1374 Files.write(readme, List.of("Hello, world!")); 1375 r.add(readme); 1376 r.commit("Added README", "duke", "duke@openjdk.java.net"); 1377 1378 assertTrue(r.isValidRevisionRange(r.defaultBranch().toString())); 1379 } 1380 } 1381 1382 @ParameterizedTest 1383 @EnumSource(VCS.class) 1384 void testDefaultTag(VCS vcs) throws IOException { 1385 try (var dir = new TemporaryDirectory()) { 1386 var r = Repository.init(dir.path(), vcs); 1387 var expected = vcs == VCS.GIT ? Optional.empty() : Optional.of(new Tag("tip")); 1388 assertEquals(expected, r.defaultTag()); 1389 } 1390 } 1391 1392 @ParameterizedTest 1393 @EnumSource(VCS.class) 1394 void testTag(VCS vcs) throws IOException { 1395 try (var dir = new TemporaryDirectory()) { 1396 var r = Repository.init(dir.path(), vcs); 1397 1398 var readme = dir.path().resolve("README"); 1399 Files.write(readme, List.of("Hello, world!")); 1400 r.add(readme); 1401 var first = r.commit("Added README", "duke", "duke@openjdk.java.net"); 1402 1403 r.tag(first, "test", "Tagging test", "duke", "duke@openjdk.java.net"); 1404 var defaultTag = r.defaultTag().orElse(null); 1405 var nonDefaultTags = r.tags().stream() 1406 .filter(tag -> !tag.equals(defaultTag)) 1407 .map(Tag::toString) 1408 .collect(Collectors.toList()); 1409 assertEquals(List.of("test"), nonDefaultTags); 1410 } 1411 } 1412 1413 @ParameterizedTest 1414 @EnumSource(VCS.class) 1415 void testIsClean(VCS vcs) throws IOException { 1416 try (var dir = new TemporaryDirectory()) { 1417 var r = Repository.init(dir.path(), vcs); 1418 assertTrue(r.isClean()); 1419 1420 var readme = dir.path().resolve("README"); 1421 Files.write(readme, List.of("Hello, world!")); 1422 assertFalse(r.isClean()); 1423 1424 r.add(readme); 1425 assertFalse(r.isClean()); 1426 1427 r.commit("Added README", "duke", "duke@openjdk.java.net"); 1428 assertTrue(r.isClean()); 1429 1430 Files.delete(readme); 1431 assertFalse(r.isClean()); 1432 1433 Files.write(readme, List.of("Hello, world!")); 1434 assertTrue(r.isClean()); 1435 } 1436 } 1437 1438 @ParameterizedTest 1439 @EnumSource(VCS.class) 1440 void testShowOnExecutableFiles(VCS vcs) throws IOException { 1441 try (var dir = new TemporaryDirectory()) { 1442 var r = Repository.init(dir.path(), vcs); 1443 assertTrue(r.isClean()); 1444 1445 var readOnlyExecutableFile = dir.path().resolve("hello.sh"); 1446 Files.write(readOnlyExecutableFile, List.of("echo 'hello'")); 1447 if (readOnlyExecutableFile.getFileSystem().supportedFileAttributeViews().contains("posix")) { 1448 var permissions = PosixFilePermissions.fromString("r-xr-xr-x"); 1449 Files.setPosixFilePermissions(readOnlyExecutableFile, permissions); 1450 } 1451 r.add(readOnlyExecutableFile); 1452 var hash = r.commit("Added read only executable file", "duke", "duke@openjdk.java.net"); 1453 assertEquals(Optional.of(List.of("echo 'hello'")), r.lines(readOnlyExecutableFile, hash)); 1454 1455 var readWriteExecutableFile = dir.path().resolve("goodbye.sh"); 1456 Files.write(readWriteExecutableFile, List.of("echo 'goodbye'")); 1457 if (readOnlyExecutableFile.getFileSystem().supportedFileAttributeViews().contains("posix")) { 1458 var permissions = PosixFilePermissions.fromString("rwxrwxrwx"); 1459 Files.setPosixFilePermissions(readWriteExecutableFile, permissions); 1460 } 1461 r.add(readWriteExecutableFile); 1462 var hash2 = r.commit("Added read-write executable file", "duke", "duke@openjdk.java.net"); 1463 assertEquals(Optional.of(List.of("echo 'goodbye'")), r.lines(readWriteExecutableFile, hash2)); 1464 } 1465 } 1466 1467 @Test 1468 void testGetAndExistsOnNonExistingDirectory() throws IOException { 1469 var nonExistingDirectory = Path.of("this", "does", "not", "exist"); 1470 assertEquals(Optional.empty(), Repository.get(nonExistingDirectory)); 1471 assertEquals(false, Repository.exists(nonExistingDirectory)); 1472 } 1473 1474 @ParameterizedTest 1475 @EnumSource(VCS.class) 1476 void testDiffOnFilenamesWithSpace(VCS vcs) throws IOException { 1477 try (var dir = new TemporaryDirectory()) { 1478 var r = Repository.init(dir.path(), vcs); 1479 assertTrue(r.isClean()); 1480 1481 var fileWithSpaceInName = dir.path().resolve("hello world.txt"); 1482 Files.writeString(fileWithSpaceInName, "Hello world\n"); 1483 r.add(fileWithSpaceInName); 1484 var hash1 = r.commit("Added file with space in name", "duke", "duke@openjdk.java.net"); 1485 Files.writeString(fileWithSpaceInName, "Goodbye world\n"); 1486 r.add(fileWithSpaceInName); 1487 var hash2 = r.commit("Modified file with space in name", "duke", "duke@openjdk.java.net"); 1488 var diff = r.diff(hash1, hash2); 1489 var patches = diff.patches(); 1490 assertEquals(1, patches.size()); 1491 var patch = patches.get(0); 1492 assertTrue(patch.target().path().isPresent()); 1493 var path = patch.target().path().get(); 1494 assertEquals(Path.of("hello world.txt"), path); 1495 } 1496 } 1497 1498 @Test 1499 void testSingleEmptyCommit() throws IOException, InterruptedException { 1500 try (var dir = new TemporaryDirectory()) { 1501 var r = Repository.init(dir.path(), VCS.GIT); 1502 assertTrue(r.isClean()); 1503 1504 // must ust git directly to be able to pass --allow-empty 1505 var pb = new ProcessBuilder("git", "commit", "--message", "An empty commit", "--allow-empty"); 1506 pb.environment().put("GIT_AUTHOR_NAME", "duke"); 1507 pb.environment().put("GIT_AUTHOR_EMAIL", "duke@openjdk.org"); 1508 pb.environment().put("GIT_COMMITTER_NAME", "duke"); 1509 pb.environment().put("GIT_COMMITTER_EMAIL", "duke@openjdk.org"); 1510 pb.directory(dir.path().toFile()); 1511 1512 var res = pb.start().waitFor(); 1513 assertEquals(0, res); 1514 1515 var commits = r.commits().asList(); 1516 assertEquals(1, commits.size()); 1517 var commit = commits.get(0); 1518 assertEquals("duke", commit.author().name()); 1519 assertEquals("duke@openjdk.org", commit.author().email()); 1520 assertEquals("duke", commit.committer().name()); 1521 assertEquals("duke@openjdk.org", commit.committer().email()); 1522 assertEquals(List.of("An empty commit"), commit.message()); 1523 } 1524 } 1525 1526 @Test 1527 void testEmptyCommitWithParent() throws IOException, InterruptedException { 1528 try (var dir = new TemporaryDirectory()) { 1529 var r = Repository.init(dir.path(), VCS.GIT); 1530 assertTrue(r.isClean()); 1531 1532 var f = Files.createFile(dir.path().resolve("hello.txt")); 1533 Files.writeString(f, "Hello world\n"); 1534 r.add(f); 1535 r.commit("Initial commit", "duke", "duke@openjdk.org"); 1536 1537 // must ust git directly to be able to pass --allow-empty 1538 var pb = new ProcessBuilder("git", "commit", "--message", "An empty commit", "--allow-empty"); 1539 pb.environment().put("GIT_AUTHOR_NAME", "duke"); 1540 pb.environment().put("GIT_AUTHOR_EMAIL", "duke@openjdk.org"); 1541 pb.environment().put("GIT_COMMITTER_NAME", "duke"); 1542 pb.environment().put("GIT_COMMITTER_EMAIL", "duke@openjdk.org"); 1543 pb.directory(dir.path().toFile()); 1544 1545 var res = pb.start().waitFor(); 1546 assertEquals(0, res); 1547 1548 var commits = r.commits().asList(); 1549 assertEquals(2, commits.size()); 1550 var commit = commits.get(0); 1551 assertEquals("duke", commit.author().name()); 1552 assertEquals("duke@openjdk.org", commit.author().email()); 1553 assertEquals("duke", commit.committer().name()); 1554 assertEquals("duke@openjdk.org", commit.committer().email()); 1555 assertEquals(List.of("An empty commit"), commit.message()); 1556 } 1557 } 1558 1559 @ParameterizedTest 1560 @EnumSource(VCS.class) 1561 void testAmend(VCS vcs) throws IOException { 1562 try (var dir = new TemporaryDirectory()) { 1563 var r = Repository.init(dir.path(), vcs); 1564 assertTrue(r.isClean()); 1565 1566 var f = dir.path().resolve("README"); 1567 Files.writeString(f, "Hello\n"); 1568 r.add(f); 1569 r.commit("Initial commit", "duke", "duke@openjdk.org"); 1570 1571 Files.writeString(f, "Hello, world\n"); 1572 r.add(f); 1573 r.amend("Initial commit corrected", "duke", "duke@openjdk.java.net"); 1574 var commits = r.commits().asList(); 1575 assertEquals(1, commits.size()); 1576 var commit = commits.get(0); 1577 assertEquals(List.of("Initial commit corrected"), commit.message()); 1578 } 1579 } 1580 1581 @ParameterizedTest 1582 @EnumSource(VCS.class) 1583 void testRevert(VCS vcs) throws IOException { 1584 try (var dir = new TemporaryDirectory()) { 1585 var r = Repository.init(dir.path(), vcs); 1586 assertTrue(r.isClean()); 1587 1588 var f = dir.path().resolve("README"); 1589 Files.writeString(f, "Hello\n"); 1590 r.add(f); 1591 var initial = r.commit("Initial commit", "duke", "duke@openjdk.org"); 1592 1593 Files.writeString(f, "Hello, world\n"); 1594 r.revert(initial); 1595 Files.writeString(f, "Goodbye, world\n"); 1596 r.add(f); 1597 var hash = r.commit("Second commit", "duke", "duke@openjdk.org"); 1598 var commit = r.lookup(hash).orElseThrow(); 1599 var patches = commit.parentDiffs().get(0).patches(); 1600 assertEquals(1, patches.size()); 1601 var patch = patches.get(0).asTextualPatch(); 1602 assertEquals(1, patch.hunks().size()); 1603 var hunk = patch.hunks().get(0); 1604 assertEquals(List.of("Goodbye, world"), hunk.target().lines()); 1605 } 1606 } 1607 1608 @ParameterizedTest 1609 @EnumSource(VCS.class) 1610 void testFiles(VCS vcs) throws IOException { 1611 try (var dir = new TemporaryDirectory()) { 1612 var r = Repository.init(dir.path(), vcs); 1613 assertTrue(r.isClean()); 1614 1615 var f = dir.path().resolve("README"); 1616 Files.writeString(f, "Hello\n"); 1617 r.add(f); 1618 var initial = r.commit("Initial commit", "duke", "duke@openjdk.org"); 1619 1620 var entries = r.files(initial); 1621 assertEquals(1, entries.size()); 1622 var entry = entries.get(0); 1623 assertEquals(Path.of("README"), entry.path()); 1624 assertTrue(entry.type().isRegularNonExecutable()); 1625 1626 var f2 = dir.path().resolve("CONTRIBUTING"); 1627 Files.writeString(f2, "Hello\n"); 1628 r.add(f2); 1629 var second = r.commit("Second commit", "duke", "duke@openjdk.org"); 1630 1631 entries = r.files(second); 1632 assertEquals(2, entries.size()); 1633 assertTrue(entries.stream().allMatch(e -> e.type().isRegularNonExecutable())); 1634 var paths = entries.stream().map(FileEntry::path).collect(Collectors.toSet()); 1635 assertTrue(paths.contains(Path.of("README"))); 1636 assertTrue(paths.contains(Path.of("CONTRIBUTING"))); 1637 1638 entries = r.files(second, Path.of("README")); 1639 assertEquals(1, entries.size()); 1640 entry = entries.get(0); 1641 assertEquals(Path.of("README"), entry.path()); 1642 assertTrue(entry.type().isRegularNonExecutable()); 1643 } 1644 } 1645 1646 @ParameterizedTest 1647 @EnumSource(VCS.class) 1648 void testDump(VCS vcs) throws IOException { 1649 try (var dir = new TemporaryDirectory()) { 1650 var r = Repository.init(dir.path(), vcs); 1651 assertTrue(r.isClean()); 1652 1653 var f = dir.path().resolve("README"); 1654 Files.writeString(f, "Hello\n"); 1655 r.add(f); 1656 var initial = r.commit("Initial commit", "duke", "duke@openjdk.org"); 1657 1658 var readme = r.files(initial).get(0); 1659 1660 var tmp = Files.createTempFile("README", "txt"); 1661 r.dump(readme, tmp); 1662 assertEquals("Hello\n", Files.readString(tmp)); 1663 Files.delete(tmp); 1664 } 1665 } 1666 1667 @ParameterizedTest 1668 @EnumSource(VCS.class) 1669 void testStatus(VCS vcs) throws IOException { 1670 try (var dir = new TemporaryDirectory()) { 1671 var r = Repository.init(dir.path(), vcs); 1672 assertTrue(r.isClean()); 1673 1674 var f = dir.path().resolve("README"); 1675 Files.writeString(f, "Hello\n"); 1676 r.add(f); 1677 var initial = r.commit("Initial commit", "duke", "duke@openjdk.org"); 1678 1679 var f2 = dir.path().resolve("CONTRIBUTING"); 1680 Files.writeString(f2, "Goodbye\n"); 1681 r.add(f2); 1682 var second = r.commit("Second commit", "duke", "duke@openjdk.org"); 1683 1684 var entries = r.status(initial, second); 1685 assertEquals(1, entries.size()); 1686 var entry = entries.get(0); 1687 assertTrue(entry.status().isAdded()); 1688 assertTrue(entry.source().path().isEmpty()); 1689 assertTrue(entry.source().type().isEmpty()); 1690 1691 assertTrue(entry.target().path().isPresent()); 1692 assertEquals(Path.of("CONTRIBUTING"), entry.target().path().get()); 1693 assertTrue(entry.target().type().get().isRegular()); 1694 } 1695 } 1696 1697 @ParameterizedTest 1698 @EnumSource(VCS.class) 1699 void testTrackLineEndings(VCS vcs) throws IOException, InterruptedException { 1700 try (var dir = new TemporaryDirectory()) { 1701 var r = Repository.init(dir.path(), vcs); 1702 if (vcs == VCS.GIT) { // turn of git's meddling 1703 int exitCode = new ProcessBuilder() 1704 .command("git", "config", "--local", "core.autocrlf", "false") 1705 .directory(dir.path().toFile()) 1706 .start() 1707 .waitFor(); 1708 assertEquals(0, exitCode); 1709 } 1710 1711 var readme = dir.path().resolve("README"); 1712 Files.writeString(readme, "Line with Unix line ending\n"); 1713 Files.writeString(readme, "Line with Windows line ending\r\n", APPEND); 1714 1715 r.add(readme); 1716 r.commit("Add README", "duke", "duke@openjdk.java.net"); 1717 1718 var commits = r.commits().asList(); 1719 assertEquals(1, commits.size()); 1720 1721 var commit = commits.get(0); 1722 var diffs = commit.parentDiffs(); 1723 var diff = diffs.get(0); 1724 assertEquals(2, diff.added()); 1725 1726 var patches = diff.patches(); 1727 assertEquals(1, patches.size()); 1728 1729 var patch = patches.get(0).asTextualPatch(); 1730 var hunks = patch.hunks(); 1731 assertEquals(1, hunks.size()); 1732 1733 var hunk = hunks.get(0); 1734 assertEquals(new Range(0, 0), hunk.source().range()); 1735 assertEquals(new Range(1, 2), hunk.target().range()); 1736 1737 assertEquals( 1738 List.of("Line with Unix line ending", "Line with Windows line ending\r"), 1739 hunk.target().lines()); 1740 } 1741 } 1742 1743 @ParameterizedTest 1744 @EnumSource(VCS.class) 1745 void testContains(VCS vcs) throws IOException { 1746 try (var dir = new TemporaryDirectory()) { 1747 var r = Repository.init(dir.path(), vcs); 1748 assertTrue(r.isClean()); 1749 1750 var f = dir.path().resolve("README"); 1751 Files.writeString(f, "Hello\n"); 1752 r.add(f); 1753 var initial = r.commit("Initial commit", "duke", "duke@openjdk.org"); 1754 1755 assertTrue(r.contains(r.defaultBranch(), initial)); 1756 1757 Files.writeString(f, "Hello again\n"); 1758 r.add(f); 1759 var second = r.commit("Second commit", "duke", "duke@openjdk.org"); 1760 1761 assertTrue(r.contains(r.defaultBranch(), initial)); 1762 } 1763 } 1764 1765 @ParameterizedTest 1766 @EnumSource(VCS.class) 1767 void testAbortMerge(VCS vcs) throws IOException { 1768 try (var dir = new TemporaryDirectory(false)) { 1769 var r = Repository.init(dir.path(), vcs); 1770 assertTrue(r.isClean()); 1771 1772 var f = dir.path().resolve("README"); 1773 Files.writeString(f, "Hello\n"); 1774 r.add(f); 1775 var initial = r.commit("Initial commit", "duke", "duke@openjdk.org"); 1776 1777 Files.writeString(f, "Hello again\n"); 1778 r.add(f); 1779 var second = r.commit("Second commit", "duke", "duke@openjdk.org"); 1780 1781 r.checkout(initial); 1782 Files.writeString(f, "Conflicting hello\n"); 1783 r.add(f); 1784 var third = r.commit("Third commit", "duke", "duke@openjdk.org"); 1785 1786 assertThrows(IOException.class, () -> { r.merge(second); }); 1787 1788 r.abortMerge(); 1789 assertTrue(r.isClean()); 1790 } 1791 } 1792 1793 @ParameterizedTest 1794 @EnumSource(VCS.class) 1795 void testReset(VCS vcs) throws IOException { 1796 assumeTrue(vcs == VCS.GIT); // FIXME reset is not yet implemented for HG 1797 1798 try (var dir = new TemporaryDirectory()) { 1799 var repo = Repository.init(dir.path(), vcs); 1800 assertTrue(repo.isClean()); 1801 1802 var f = dir.path().resolve("README"); 1803 Files.writeString(f, "Hello\n"); 1804 repo.add(f); 1805 var initial = repo.commit("Initial commit", "duke", "duke@openjdk.org"); 1806 1807 Files.writeString(f, "Hello again\n"); 1808 repo.add(f); 1809 var second = repo.commit("Second commit", "duke", "duke@openjdk.org"); 1810 1811 assertEquals(second, repo.head()); 1812 assertEquals(2, repo.commits().asList().size()); 1813 1814 repo.reset(initial, true); 1815 1816 assertEquals(initial, repo.head()); 1817 assertEquals(1, repo.commits().asList().size()); 1818 } 1819 } 1820 }