GitScmProvider extends ScmProvider { @CheckForNull public Instant forkDate(String referenceBranchName, Path projectBaseDir) { try (Repository repo = buildRepo(projectBaseDir)) { Ref targetRef = resolveTargetRef(referenceBranchName, repo); if (targetRef == null) { LOG.warn("Branch '{}' not found in git", referenceBranchName); return null; } if (isDiffAlgoInvalid(repo.getConfig())) { LOG.warn("The diff algorithm configured in git is not supported. " + "No information regarding changes in the branch will be collected, which can lead to unexpected results."); return null; } Optional<RevCommit> mergeBaseCommit = findMergeBase(repo, targetRef); if (!mergeBaseCommit.isPresent()) { LOG.warn("No fork point found between HEAD and " + targetRef.getName()); return null; } return Instant.ofEpochSecond(mergeBaseCommit.get().getCommitTime()); } catch (Exception e) { LOG.warn("Failed to find fork point with git", e); } return null; } GitScmProvider(JGitBlameCommand jgitBlameCommand, AnalysisWarnings analysisWarnings, GitIgnoreCommand gitIgnoreCommand, System2 system2); @Override GitIgnoreCommand ignoreCommand(); @Override String key(); @Override boolean supports(File baseDir); @Override BlameCommand blameCommand(); @CheckForNull @Override Set<Path> branchChangedFiles(String targetBranchName, Path rootBaseDir); @CheckForNull @Override Map<Path, Set<Integer>> branchChangedLines(String targetBranchName, Path projectBaseDir, Set<Path> changedFiles); @CheckForNull Instant forkDate(String referenceBranchName, Path projectBaseDir); @Override Path relativePathFromScmRoot(Path path); @Override @CheckForNull String revisionId(Path path);  }
@Test public void forkDate_should_not_fail_with_patience_diff_algo() throws IOException { Path gitConfig = worktree.resolve(".git").resolve("config"); Files.write(gitConfig, "[diff]\nalgorithm = patience\n".getBytes(StandardCharsets.UTF_8)); Repository repo = FileRepositoryBuilder.create(worktree.resolve(".git").toFile()); git = new Git(repo); assertThat(newScmProvider().forkDate("master", worktree)).isNull(); }
@Test public void forkDate_should_not_fail_with_invalid_basedir() throws IOException { assertThat(newScmProvider().forkDate("master", temp.newFolder().toPath())).isNull(); }
@Test public void forkDate_should_not_fail_when_no_merge_base_is_found() throws IOException, GitAPIException { createAndCommitFile("file-m1.xoo", Instant.now().minus(8, ChronoUnit.DAYS)); git.checkout().setOrphan(true).setName("b1").call(); createAndCommitFile("file-b1.xoo"); assertThat(newScmProvider().forkDate("master", worktree)).isNull(); }
@Test public void forkDate_without_target_branch() throws IOException, GitAPIException { createAndCommitFile("file-m1.xoo", Instant.now().minus(8, ChronoUnit.DAYS)); createAndCommitFile("file-m2.xoo", Instant.now().minus(7, ChronoUnit.DAYS)); Instant expectedForkDate = Instant.now().minus(6, ChronoUnit.DAYS); createAndCommitFile("file-m3.xoo", expectedForkDate); ObjectId forkPoint = git.getRepository().exactRef("HEAD").getObjectId(); appendToAndCommitFile("file-m3.xoo"); createAndCommitFile("file-m4.xoo"); git.branchCreate().setName("b1").setStartPoint(forkPoint.getName()).call(); git.checkout().setName("b1").call(); createAndCommitFile("file-b1.xoo"); appendToAndCommitFile("file-m1.xoo"); deleteAndCommitFile("file-m2.xoo"); assertThat(newScmProvider().forkDate("unknown", worktree)).isNull(); }
@Test public void forkDate_from_diverged() throws IOException, GitAPIException { createAndCommitFile("file-m1.xoo", Instant.now().minus(8, ChronoUnit.DAYS)); createAndCommitFile("file-m2.xoo", Instant.now().minus(7, ChronoUnit.DAYS)); Instant expectedForkDate = Instant.now().minus(6, ChronoUnit.DAYS); createAndCommitFile("file-m3.xoo", expectedForkDate); ObjectId forkPoint = git.getRepository().exactRef("HEAD").getObjectId(); appendToAndCommitFile("file-m3.xoo"); createAndCommitFile("file-m4.xoo"); git.branchCreate().setName("b1").setStartPoint(forkPoint.getName()).call(); git.checkout().setName("b1").call(); createAndCommitFile("file-b1.xoo"); appendToAndCommitFile("file-m1.xoo"); deleteAndCommitFile("file-m2.xoo"); assertThat(newScmProvider().forkDate("master", worktree)) .isEqualTo(expectedForkDate.truncatedTo(ChronoUnit.SECONDS)); }
@Test public void forkDate_should_not_fail_if_reference_is_the_same_branch() throws IOException, GitAPIException { createAndCommitFile("file-m1.xoo", Instant.now().minus(8, ChronoUnit.DAYS)); createAndCommitFile("file-m2.xoo", Instant.now().minus(7, ChronoUnit.DAYS)); ObjectId forkPoint = git.getRepository().exactRef("HEAD").getObjectId(); git.branchCreate().setName("b1").setStartPoint(forkPoint.getName()).call(); git.checkout().setName("b1").call(); Instant expectedForkDate = Instant.now().minus(6, ChronoUnit.DAYS); createAndCommitFile("file-m3.xoo", expectedForkDate); assertThat(newScmProvider().forkDate("b1", worktree)) .isEqualTo(expectedForkDate.truncatedTo(ChronoUnit.SECONDS)); }