blob: 2427ee487ea76c3ad292e2838e8a466e8ecee0b4 [file]
/*
* Copyright (C) 2023, Tencent.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
* https://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package org.eclipse.jgit.revwalk;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.jgit.diff.DiffConfig;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.filter.MessageRevFilter;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
import org.eclipse.jgit.treewalk.filter.ChangedPathTreeFilter;
import org.eclipse.jgit.treewalk.filter.OrTreeFilter;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.junit.Test;
public class RevWalkCommitGraphTest extends AbstractRevWalkWithCommitGraphTest {
@Test
public void testParseHeaders() throws Exception {
RevCommit c1 = commitFile("file1", "1", "master");
RevCommit notParseInGraph = rw.lookupCommit(c1);
rw.parseHeaders(notParseInGraph);
assertNotNull(notParseInGraph.getRawBuffer());
assertEquals(Constants.COMMIT_GENERATION_UNKNOWN,
notParseInGraph.getGeneration());
enableAndWriteCommitGraph();
reinitializeRevWalk();
RevCommit parseInGraph = rw.lookupCommit(c1);
parseInGraph.parseHeaders(rw);
assertNotNull(parseInGraph.getRawBuffer());
assertEquals(1, parseInGraph.getGeneration());
assertEquals(notParseInGraph.getId(), parseInGraph.getId());
assertEquals(notParseInGraph.getTree(), parseInGraph.getTree());
assertEquals(notParseInGraph.getCommitTime(), parseInGraph.getCommitTime());
assertArrayEquals(notParseInGraph.getParents(), parseInGraph.getParents());
reinitializeRevWalk();
rw.setRetainBody(false);
RevCommit noBody = rw.lookupCommit(c1);
noBody.parseHeaders(rw);
assertNull(noBody.getRawBuffer());
assertEquals(1, noBody.getGeneration());
assertEquals(notParseInGraph.getId(), noBody.getId());
assertEquals(notParseInGraph.getTree(), noBody.getTree());
assertEquals(notParseInGraph.getCommitTime(), noBody.getCommitTime());
assertArrayEquals(notParseInGraph.getParents(), noBody.getParents());
}
@Test
public void testParseCanonical() throws Exception {
RevCommit c1 = commitFile("file1", "1", "master");
enableAndWriteCommitGraph();
RevCommit notParseInGraph = rw.lookupCommit(c1);
rw.parseHeaders(notParseInGraph);
reinitializeRevWalk();
RevCommit parseInGraph = rw.lookupCommit(c1);
parseInGraph.parseCanonical(rw, rw.getCachedBytes(c1));
assertNotNull(parseInGraph.getRawBuffer());
assertEquals(1, parseInGraph.getGeneration());
assertEquals(notParseInGraph.getId(), parseInGraph.getId());
assertEquals(notParseInGraph.getTree(), parseInGraph.getTree());
assertEquals(notParseInGraph.getCommitTime(),
parseInGraph.getCommitTime());
assertArrayEquals(notParseInGraph.getParents(),
parseInGraph.getParents());
reinitializeRevWalk();
rw.setRetainBody(false);
RevCommit noBody = rw.lookupCommit(c1);
noBody.parseCanonical(rw, rw.getCachedBytes(c1));
assertNull(noBody.getRawBuffer());
assertEquals(1, noBody.getGeneration());
assertEquals(notParseInGraph.getId(), noBody.getId());
assertEquals(notParseInGraph.getTree(), noBody.getTree());
assertEquals(notParseInGraph.getCommitTime(), noBody.getCommitTime());
assertArrayEquals(notParseInGraph.getParents(), noBody.getParents());
}
@Test
public void testInitializeShallowCommits() throws Exception {
RevCommit c1 = commit(commit());
branch(c1, "master");
enableAndWriteCommitGraph();
assertCommitCntInGraph(2);
db.getObjectDatabase().setShallowCommits(Collections.singleton(c1));
RevCommit parseInGraph = rw.lookupCommit(c1);
parseInGraph.parseHeaders(rw);
assertNotNull(parseInGraph.getRawBuffer());
assertEquals(2, parseInGraph.getGeneration());
assertEquals(0, parseInGraph.getParentCount());
}
@Test
public void testTreeFilter() throws Exception {
RevCommit c1 = commitFile("file1", "1", "master");
RevCommit c2 = commitFile("file2", "2", "master");
RevCommit c3 = commitFile("file1", "3", "master");
RevCommit c4 = commitFile("file2", "4", "master");
enableAndWriteCommitGraph();
assertCommitCntInGraph(4);
rw.markStart(rw.lookupCommit(c4));
rw.setTreeFilter(AndTreeFilter.create(PathFilter.create("file1"),
TreeFilter.ANY_DIFF));
assertEquals(c3, rw.next());
assertEquals(c1, rw.next());
assertNull(rw.next());
reinitializeRevWalk();
rw.markStart(rw.lookupCommit(c4));
rw.setTreeFilter(AndTreeFilter.create(PathFilter.create("file2"),
TreeFilter.ANY_DIFF));
assertEquals(c4, rw.next());
assertEquals(c2, rw.next());
assertNull(rw.next());
}
@Test
public void testChangedPathFilterMergeCommit_followFilter()
throws Exception {
RevCommit root1 = commit(tree(file("file1", blob("1"))));
RevCommit root2 = commit(tree(file("file1", blob("2"))));
RevCommit root3 = commit(tree(file("file1", blob("3"))));
RevCommit merge1 = commit(tree(file("file1", blob("1"))), root1, root2);
RevCommit merge2 = commit(tree(file("file1", blob("1"))), merge1,
root3);
RevCommit tip2 = commit(tree(file("file1", blob("1"))), merge2);
RevCommit tip = commit(tree(file("file2", blob("1"))), tip2);
branch(tip, "master");
enableAndWriteCommitGraph();
FollowFilter followFilter = FollowFilter.create("file2",
db.getConfig().get(DiffConfig.KEY));
rw.setTreeFilter(followFilter);
rw.setRevFilter(RevFilter.ALL);
rw.sort(RevSort.NONE);
rw.setRetainBody(false);
rw.markStart(rw.lookupCommit(db.resolve("master")));
assertCommits(
// no CG nor BF
travel(followFilter, RevFilter.ALL, RevSort.NONE, false,
"master"),
// with CG and BF
travel(rw, true));
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// tip did a rename but didn't change content
assertEquals(1, rfs.getChangedPathFilterTruePositive());
assertEquals(0, rfs.getChangedPathFilterFalsePositive());
// tip2, merge2, merge1 didn't change content relative to their base
// parent
assertEquals(3, rfs.getChangedPathFilterNegative());
}
@Test
public void testChangedPathFilterMergeCommit_usedBaseParentAsRewrite()
throws Exception {
RevCommit root1 = commit(tree(file("file1", blob("1"))));
RevCommit root2 = commit(tree(file("file1", blob("2"))));
RevCommit root3 = commit(tree(file("file1", blob("3"))));
RevCommit merge1 = commit(tree(file("file1", blob("1"))), root1, root2);
RevCommit merge2 = commit(tree(file("file1", blob("1"))), merge1,
root3);
branch(merge2, "master");
enableAndWriteCommitGraph();
ChangedPathTreeFilter changedPathTreeFilter = ChangedPathTreeFilter.create("file1");
rw.setTreeFilter(changedPathTreeFilter);
rw.setRevFilter(RevFilter.ALL);
rw.sort(RevSort.NONE);
rw.setRetainBody(false);
rw.markStart(rw.lookupCommit(db.resolve("master")));
assertCommits(
// no CG nor BF
travel(changedPathTreeFilter, RevFilter.ALL, RevSort.NONE, false,
"master"),
// with CG and BF
travel(rw, true));
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// both merge1 and merge2 used their base parent as redirect
assertEquals(2, rfs.getNumMergeCommitsUsedBaseParentAsRedirect());
assertEquals(0,
rfs.getNumMergeCommitsUsedPullRequestParentAsRedirect());
assertEquals(0, rfs.getNumMergeCommitsHadNoRedirect());
assertEquals(0,
rfs.getNumMergeCommitsHadNoDiffWithAnyInterestingParent());
}
@Test
public void testChangedPathFilterMergeCommit_usedPullRequestParentAsRewrite()
throws Exception {
RevCommit root1 = commit(tree(file("file1", blob("1"))));
RevCommit root2 = commit(tree(file("file1", blob("2"))));
RevCommit root3 = commit(tree(file("file1", blob("3"))));
RevCommit merge1 = commit(tree(file("file1", blob("2"))), root1, root2);
RevCommit merge2 = commit(tree(file("file1", blob("2"))), root3,
merge1);
branch(merge2, "master");
enableAndWriteCommitGraph();
ChangedPathTreeFilter changedPathTreeFilter = ChangedPathTreeFilter.create("file1");
rw.setTreeFilter(changedPathTreeFilter);
rw.setRevFilter(RevFilter.ALL);
rw.sort(RevSort.NONE);
rw.setRetainBody(false);
rw.markStart(rw.lookupCommit(db.resolve("master")));
assertCommits(
// no CG nor BF
travel(changedPathTreeFilter, RevFilter.ALL, RevSort.NONE, false,
"master"),
// with CG and BF
travel(rw, true));
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// both merge1 and merge2 used their 2nd parent as redirect
assertEquals(0, rfs.getNumMergeCommitsUsedBaseParentAsRedirect());
assertEquals(2,
rfs.getNumMergeCommitsUsedPullRequestParentAsRedirect());
assertEquals(0, rfs.getNumMergeCommitsHadNoRedirect());
assertEquals(0,
rfs.getNumMergeCommitsHadNoDiffWithAnyInterestingParent());
}
@Test
public void testChangedPathFilterMergeCommit_noParentRedirect()
throws Exception {
RevCommit root1 = commit(tree(file("file1", blob("1"))));
RevCommit root2 = commit(tree(file("file1", blob("2"))));
RevCommit root3 = commit(tree(file("file1", blob("3"))));
RevCommit merge1 = commit(tree(file("file1", blob("4"))), root1, root2);
RevCommit merge2 = commit(tree(file("file1", blob("5"))), root3,
merge1);
branch(merge2, "master");
enableAndWriteCommitGraph();
ChangedPathTreeFilter changedPathTreeFilter = ChangedPathTreeFilter.create("file1");
rw.setTreeFilter(changedPathTreeFilter);
rw.setRevFilter(RevFilter.ALL);
rw.sort(RevSort.NONE);
rw.setRetainBody(false);
rw.markStart(rw.lookupCommit(db.resolve("master")));
assertCommits(
// no CG nor BF
travel(changedPathTreeFilter, RevFilter.ALL, RevSort.NONE, false,
"master"),
// with CG and BF
travel(rw, true));
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// both merge1 and merge2 did not need redirect since they are different
// from all of their parents
assertEquals(0, rfs.getNumMergeCommitsUsedBaseParentAsRedirect());
assertEquals(0,
rfs.getNumMergeCommitsUsedPullRequestParentAsRedirect());
assertEquals(2, rfs.getNumMergeCommitsHadNoRedirect());
assertEquals(0,
rfs.getNumMergeCommitsHadNoDiffWithAnyInterestingParent());
}
@Test
public void testChangedPathFilterMergeCommit_noInterestingParentForRedirect()
throws Exception {
RevCommit root1 = commit(tree(file("file1", blob("1"))));
RevCommit root2 = commit(tree(file("file1", blob("2"))));
RevCommit root3 = commit(tree(file("file1", blob("2"))));
RevCommit root4 = commit(tree(file("file1", blob("3"))));
RevCommit merge1 = commit(tree(file("file1", blob("1"))), root1, root2);
RevCommit merge2 = commit(tree(file("file1", blob("2"))), root3, root4);
RevCommit merge3 = commit(tree(file("file1", blob("1"))), merge1,
merge2);
branch(merge3, "master");
ChangedPathTreeFilter changedPathTreeFilter = ChangedPathTreeFilter.create("file1");
RevWalk expectedRevWalk = new RevWalk(db);
expectedRevWalk.setTreeFilter(changedPathTreeFilter);
expectedRevWalk.setRevFilter(RevFilter.ALL);
expectedRevWalk.sort(RevSort.NONE);
expectedRevWalk.setRetainBody(false);
expectedRevWalk
.markStart(expectedRevWalk.lookupCommit(db.resolve("master")));
expectedRevWalk.markUninteresting(expectedRevWalk.lookupCommit(merge1));
expectedRevWalk.markUninteresting(expectedRevWalk.lookupCommit(root3));
enableAndWriteCommitGraph();
rw.setTreeFilter(changedPathTreeFilter);
rw.setRevFilter(RevFilter.ALL);
rw.sort(RevSort.NONE);
rw.setRetainBody(false);
rw.markStart(rw.lookupCommit(db.resolve("master")));
rw.markUninteresting(rw.lookupCommit(merge1));
rw.markUninteresting(rw.lookupCommit(root3));
assertCommits(
// no CG nor BF
travel(expectedRevWalk, false),
// with CG and BF
travel(rw, true));
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// both merge3 and merge2 had same content base parent but they were
// UNINTERESTING
assertEquals(0, rfs.getNumMergeCommitsUsedBaseParentAsRedirect());
assertEquals(0,
rfs.getNumMergeCommitsUsedPullRequestParentAsRedirect());
assertEquals(0, rfs.getNumMergeCommitsHadNoRedirect());
assertEquals(2,
rfs.getNumMergeCommitsHadNoDiffWithAnyInterestingParent());
}
@Test
public void testChangedPathFilter_allModify() throws Exception {
RevCommit c1 = commit(tree(file("file1", blob("1"))));
RevCommit c2 = commit(tree(file("file2", blob("2"))), c1);
RevCommit c3 = commit(tree(file("file1", blob("3"))), c2);
RevCommit c4 = commit(tree(file("file2", blob("4"))), c3);
branch(c4, "master");
enableAndWriteCommitGraph();
TreeRevFilter trf = new TreeRevFilter(rw,
ChangedPathTreeFilter.create("file1"));
rw.markStart(rw.lookupCommit(c4));
rw.setRevFilter(trf);
assertEquals(c4, rw.next());
assertEquals(c3, rw.next());
assertEquals(c2, rw.next());
assertEquals(c1, rw.next());
assertNull(rw.next());
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// all commits modified file1 but c1 did not have a parent
assertEquals(3, rfs.getChangedPathFilterTruePositive());
// No false positives
assertEquals(0, rfs.getChangedPathFilterFalsePositive());
// No negatives because all 4 commits had modified file1
assertEquals(0, rfs.getChangedPathFilterNegative());
}
@Test
public void testChangedPathFilter_someModify() throws Exception {
RevCommit c1 = commit(tree(file("file1", blob("1"))));
RevCommit c2 = commit(tree(file("file1", blob("1"))), c1);
RevCommit c3 = commit(tree(file("file1", blob("2"))), c2);
RevCommit c4 = commit(tree(file("file1", blob("1"))), c3);
branch(c4, "master");
enableAndWriteCommitGraph();
TreeRevFilter trf = new TreeRevFilter(rw,
ChangedPathTreeFilter.create("file1"));
rw.markStart(rw.lookupCommit(c4));
rw.setRevFilter(trf);
assertEquals(c4, rw.next());
assertEquals(c3, rw.next());
assertEquals(c1, rw.next());
assertNull(rw.next());
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// c4 and c3 modified file1. c1 did not have a parent
assertEquals(2, rfs.getChangedPathFilterTruePositive());
// No false positives
assertEquals(0, rfs.getChangedPathFilterFalsePositive());
// c2 did not modify file1
assertEquals(1, rfs.getChangedPathFilterNegative());
}
@Test
public void testChangedPathFilterWithMultiPaths() throws Exception {
RevCommit c1 = commit(tree(file("file1", blob("1"))));
RevCommit c2 = commit(tree(file("file1", blob("2"))), c1);
RevCommit c3 = commit(tree(file("file2", blob("3"))), c2);
RevCommit c4 = commit(tree(file("file3", blob("4"))), c3);
branch(c4, "master");
enableAndWriteCommitGraph();
TreeRevFilter trf = new TreeRevFilter(rw,
ChangedPathTreeFilter.create("file1", "file2"));
rw.markStart(rw.lookupCommit(c4));
rw.setRevFilter(trf);
assertEquals(c4, rw.next());
assertEquals(c3, rw.next());
assertEquals(c2, rw.next());
assertEquals(c1, rw.next());
assertNull(rw.next());
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// all commits have modified either file1 or file2, c1 did not have a
// parent
assertEquals(3, rfs.getChangedPathFilterTruePositive());
// No false positives
assertEquals(0, rfs.getChangedPathFilterFalsePositive());
// No negative
assertEquals(0, rfs.getChangedPathFilterNegative());
}
@Test
public void testChangedPathFilterWithFollowFilter() throws Exception {
RevCommit c0 = commit(tree());
RevCommit c1 = commit(tree(file("file", blob("contents"))), c0);
RevCommit c2 = commit(tree(file("file", blob("contents")),
file("unrelated", blob("unrelated change"))), c1);
RevCommit c3 = commit(tree(file("renamed-file", blob("contents")),
file("unrelated", blob("unrelated change"))), c2);
RevCommit c4 = commit(
tree(file("renamed-file", blob("contents")),
file("unrelated", blob("another unrelated change"))),
c3);
branch(c4, "master");
enableAndWriteCommitGraph();
db.getConfig().setString(ConfigConstants.CONFIG_DIFF_SECTION, null,
ConfigConstants.CONFIG_KEY_RENAMES, "true");
TreeRevFilter trf = new TreeRevFilter(rw, FollowFilter
.create("renamed-file", db.getConfig().get(DiffConfig.KEY)));
rw.markStart(rw.lookupCommit(c4));
rw.setRevFilter(trf);
assertEquals(c3, rw.next());
assertEquals(c1, rw.next());
assertNull(rw.next());
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// Path "renamed-file" is in c3's bloom filter, and another path "file"
// is in c1's bloom filter (we know of "file" because the rev walk
// detected that "renamed-file" is a renaming of "file")
assertEquals(2, rfs.getChangedPathFilterTruePositive());
// No false positives
assertEquals(0, rfs.getChangedPathFilterFalsePositive());
// 2 commits that have exactly one parent and don't match path
assertEquals(2, rfs.getChangedPathFilterNegative());
}
@Test
public void testChangedPathFilter_pathFilter_or_pathFilter_binaryOperation()
throws Exception {
RevCommit c1 = commit(tree(file("file1", blob("1"))));
RevCommit c2 = commit(
tree(file("file1", blob("1")), file("file2", blob("2"))), c1);
RevCommit c3 = commit(tree(file("file2", blob("2"))), c2);
RevCommit c4 = commit(
tree(file("file2", blob("2")), file("file3", blob("3"))), c3);
RevCommit c5 = commit(
tree(file("file2", blob("2")), file("file3", blob("3"))), c4);
branch(c5, "master");
enableAndWriteCommitGraph();
ChangedPathTreeFilter pf1 = ChangedPathTreeFilter.create("file1");
ChangedPathTreeFilter pf2 = ChangedPathTreeFilter.create("file2");
TreeFilter tf = OrTreeFilter
.create(new ChangedPathTreeFilter[] { pf1, pf2 });
TreeRevFilter trf = new TreeRevFilter(rw, tf);
rw.markStart(rw.lookupCommit(c5));
rw.setRevFilter(trf);
assertEquals(c3, rw.next());
assertEquals(c2, rw.next());
assertEquals(c1, rw.next());
assertNull(rw.next());
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// c2 and c3 has either file1 or file2, c1 is not counted as
// ChangedPathFilter only applies to commits with 1 parent
assertEquals(2, rfs.getChangedPathFilterTruePositive());
// No false positives
assertEquals(0, rfs.getChangedPathFilterFalsePositive());
// c4 and c5 did not modify file1 or file2
assertEquals(2, rfs.getChangedPathFilterNegative());
}
@Test
public void testChangedPathFilter_pathFilter_or_pathFilter_or_pathFilter_listOperation()
throws Exception {
RevCommit c1 = commit(tree(file("file1", blob("1"))));
RevCommit c2 = commit(
tree(file("file1", blob("1")), file("file2", blob("2"))), c1);
RevCommit c3 = commit(tree(file("file2", blob("2"))), c2);
RevCommit c4 = commit(tree(file("file3", blob("3"))), c3);
RevCommit c5 = commit(tree(file("file3", blob("3"))), c4);
branch(c5, "master");
enableAndWriteCommitGraph();
ChangedPathTreeFilter pf1 = ChangedPathTreeFilter.create("file1");
ChangedPathTreeFilter pf2 = ChangedPathTreeFilter.create("file2");
ChangedPathTreeFilter pf3 = ChangedPathTreeFilter.create("file3");
TreeFilter tf = OrTreeFilter
.create(new ChangedPathTreeFilter[] { pf1, pf2, pf3 });
TreeRevFilter trf = new TreeRevFilter(rw, tf);
rw.markStart(rw.lookupCommit(c5));
rw.setRevFilter(trf);
assertEquals(c4, rw.next());
assertEquals(c3, rw.next());
assertEquals(c2, rw.next());
assertEquals(c1, rw.next());
assertNull(rw.next());
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// c2 and c3 has either modified file1 or file2 or file3, c1 is not
// counted as ChangedPathFilter only applies to commits with 1 parent
assertEquals(3, rfs.getChangedPathFilterTruePositive());
// No false positives
assertEquals(0, rfs.getChangedPathFilterFalsePositive());
// c5 does not modify either file1 or file2 or file3
assertEquals(1, rfs.getChangedPathFilterNegative());
}
@Test
public void testChangedPathFilter_pathFilter_or_nonPathFilter_binaryOperation()
throws Exception {
RevCommit c1 = commit(tree(file("file1", blob("1"))));
RevCommit c2 = commit(tree(file("file2", blob("2"))), c1);
RevCommit c3 = commit(tree(file("file2", blob("3"))), c2);
RevCommit c4 = commit(tree(file("file2", blob("3"))), c3);
branch(c4, "master");
enableAndWriteCommitGraph();
ChangedPathTreeFilter pf = ChangedPathTreeFilter.create("file1");
TreeFilter npf = TreeFilter.ANY_DIFF;
TreeFilter tf = OrTreeFilter.create(new TreeFilter[] { pf, npf });
TreeRevFilter trf = new TreeRevFilter(rw, tf);
rw.markStart(rw.lookupCommit(c4));
rw.setRevFilter(trf);
assertEquals(c3, rw.next());
assertEquals(c2, rw.next());
assertEquals(c1, rw.next());
assertNull(rw.next());
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// c2 modified file1, c3 defaulted positive due to ANY_DIFF, c1 is not
// counted as ChangedPathFilter only applies to commits with 1 parent
assertEquals(2, rfs.getChangedPathFilterTruePositive());
// c4 defaulted positive due to ANY_DIFF, but didn't no diff with its
// parent c3
assertEquals(1, rfs.getChangedPathFilterFalsePositive());
// No negative due to the OrTreeFilter
assertEquals(0, rfs.getChangedPathFilterNegative());
}
@Test
public void testChangedPathFilter_nonPathFilter_or_nonPathFilter_binaryOperation()
throws Exception {
RevCommit c1 = commitFile("file1", "1", "master");
RevCommit c2 = commitFile("file2", "2", "master");
RevCommit c3 = commitFile("file3", "3", "master");
RevCommit c4 = commitFile("file4", "4", "master");
enableAndWriteCommitGraph();
TreeFilter npf1 = TreeFilter.ANY_DIFF;
TreeFilter npf2 = TreeFilter.ANY_DIFF;
TreeFilter tf = OrTreeFilter.create(new TreeFilter[] { npf1, npf2 });
TreeRevFilter trf = new TreeRevFilter(rw, tf);
rw.markStart(rw.lookupCommit(c4));
rw.setRevFilter(trf);
assertEquals(c4, rw.next());
assertEquals(c3, rw.next());
assertEquals(c2, rw.next());
assertEquals(c1, rw.next());
assertNull(rw.next());
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// No true positives since there's no pathFilter
assertEquals(0, rfs.getChangedPathFilterTruePositive());
// No false positives since there's no pathFilter
assertEquals(0, rfs.getChangedPathFilterFalsePositive());
// No negative since there's no pathFilter
assertEquals(0, rfs.getChangedPathFilterNegative());
}
@Test
public void testChangedPathFilter_pathFilter_and_pathFilter_binaryOperation()
throws Exception {
RevCommit c1 = commit(tree(file("file1", blob("1"))));
RevCommit c2 = commit(tree(file("file2", blob("2"))), c1);
branch(c2, "master");
enableAndWriteCommitGraph();
ChangedPathTreeFilter pf1 = ChangedPathTreeFilter.create("file1");
ChangedPathTreeFilter pf2 = ChangedPathTreeFilter.create("file2");
TreeFilter atf = AndTreeFilter
.create(new ChangedPathTreeFilter[] { pf1, pf2 });
TreeRevFilter trf = new TreeRevFilter(rw, atf);
rw.markStart(rw.lookupCommit(c2));
rw.setRevFilter(trf);
assertNull(rw.next());
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// c1 is not counted as ChangedPathFilter only applies to commits with 1
// parent
assertEquals(0, rfs.getChangedPathFilterTruePositive());
// c2 has modified both file 1 and file2,
// however nothing is returned from TreeWalk since a TreeHead
// cannot be two paths at once
assertEquals(1, rfs.getChangedPathFilterFalsePositive());
// No negatives
assertEquals(0, rfs.getChangedPathFilterNegative());
}
@Test
public void testChangedPathFilter_pathFilter_and_pathFilter_and_pathFilter_listOperation()
throws Exception {
RevCommit c1 = commit(tree(file("file1", blob("1"))));
RevCommit c2 = commit(tree(file("file2", blob("2"))), c1);
RevCommit c3 = commit(tree(file("file3", blob("3"))), c2);
branch(c3, "master");
enableAndWriteCommitGraph();
ChangedPathTreeFilter pf1 = ChangedPathTreeFilter.create("file1");
ChangedPathTreeFilter pf2 = ChangedPathTreeFilter.create("file2");
ChangedPathTreeFilter pf3 = ChangedPathTreeFilter.create("file3");
TreeFilter tf = AndTreeFilter
.create(new ChangedPathTreeFilter[] { pf1, pf2, pf3 });
TreeRevFilter trf = new TreeRevFilter(rw, tf);
rw.markStart(rw.lookupCommit(c3));
rw.setRevFilter(trf);
assertNull(rw.next());
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// c1 is not counted as ChangedPathFilter only applies to commits with 1
// parent
assertEquals(0, rfs.getChangedPathFilterTruePositive());
// No false positives
assertEquals(0, rfs.getChangedPathFilterFalsePositive());
// c2 and c3 can not possibly have both file1, file2, and file3 as
// treeHead at once
assertEquals(2, rfs.getChangedPathFilterNegative());
}
@Test
public void testChangedPathFilter_pathFilter_and_nonPathFilter_binaryOperation()
throws Exception {
RevCommit c1 = commit(tree(file("file1", blob("1"))));
RevCommit c2 = commit(tree(file("file1", blob("2"))), c1);
RevCommit c3 = commit(tree(file("file1", blob("2"))), c2);
branch(c3, "master");
enableAndWriteCommitGraph();
ChangedPathTreeFilter pf = ChangedPathTreeFilter.create("file1");
TreeFilter npf = TreeFilter.ANY_DIFF;
TreeFilter tf = AndTreeFilter.create(new TreeFilter[] { pf, npf });
TreeRevFilter trf = new TreeRevFilter(rw, tf);
rw.markStart(rw.lookupCommit(c3));
rw.setRevFilter(trf);
assertEquals(c2, rw.next());
assertEquals(c1, rw.next());
assertNull(rw.next());
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// c2 modified file1 and c1 is not counted as ChangedPathFilter only
// applies to commits with 1 parent
assertEquals(1, rfs.getChangedPathFilterTruePositive());
// No false positives
assertEquals(0, rfs.getChangedPathFilterFalsePositive());
// c3 did not modify file1
assertEquals(1, rfs.getChangedPathFilterNegative());
}
@Test
public void testChangedPathFilter_nonPathFilter_and_nonPathFilter_binaryOperation()
throws Exception {
RevCommit c1 = commitFile("file1", "1", "master");
commitFile("file1", "1", "master");
RevCommit c3 = commitFile("file3", "3", "master");
RevCommit c4 = commitFile("file4", "4", "master");
enableAndWriteCommitGraph();
TreeFilter npf1 = TreeFilter.ANY_DIFF;
TreeFilter npf2 = TreeFilter.ANY_DIFF;
TreeFilter tf = AndTreeFilter.create(new TreeFilter[] { npf1, npf2 });
TreeRevFilter trf = new TreeRevFilter(rw, tf);
rw.markStart(rw.lookupCommit(c4));
rw.setRevFilter(trf);
assertEquals(c4, rw.next());
assertEquals(c3, rw.next());
assertEquals(c1, rw.next());
assertNull(rw.next());
RevWalk.RevFilterStats rfs = rw.getRevFilterStats();
// No true positives since there's no path
assertEquals(0, rfs.getChangedPathFilterTruePositive());
// No false positives since there's no path
assertEquals(0, rfs.getChangedPathFilterFalsePositive());
// No negative since there's no path
assertEquals(0, rfs.getChangedPathFilterNegative());
}
@Test
public void testWalkWithCommitMessageFilter() throws Exception {
RevCommit a = commit();
RevCommit b = commitBuilder().parent(a)
.message("The quick brown fox jumps over the lazy dog!")
.create();
RevCommit c = commitBuilder().parent(b).message("commit-c").create();
branch(c, "master");
enableAndWriteCommitGraph();
assertCommitCntInGraph(3);
rw.setRevFilter(MessageRevFilter.create("quick brown fox jumps"));
rw.markStart(rw.lookupCommit(c));
assertEquals(b, rw.next());
assertNull(rw.next());
}
@Test
public void testCommitsWalk() throws Exception {
RevCommit c1 = commit();
branch(c1, "commits/1");
RevCommit c2 = commit(c1);
branch(c2, "commits/2");
RevCommit c3 = commit(c2);
branch(c3, "commits/3");
enableAndWriteCommitGraph();
assertCommitCntInGraph(3);
testRevWalkBehavior("commits/1", "commits/3");
// add more commits
RevCommit c4 = commit(c1);
RevCommit c5 = commit(c4);
RevCommit c6 = commit(c1);
RevCommit c7 = commit(c6);
RevCommit m1 = commit(c2, c4);
branch(m1, "merge/1");
RevCommit m2 = commit(c4, c6);
branch(m2, "merge/2");
RevCommit m3 = commit(c3, c5, c7);
branch(m3, "merge/3");
/*
* <pre>
* current graph structure:
*
* __M3___
* / | \
* 3 M1 5 M2 7
* |/ \|/ \|
* 2 4 6
* |___/____/
* 1
* </pre>
*/
enableAndWriteCommitGraph();
reinitializeRevWalk();
assertCommitCntInGraph(10);
testRevWalkBehavior("merge/1", "merge/2");
testRevWalkBehavior("merge/1", "merge/3");
testRevWalkBehavior("merge/2", "merge/3");
// add one more commit
RevCommit c8 = commit(m3);
branch(c8, "commits/8");
/*
* <pre>
* current graph structure:
* 8
* |
* __M3___
* / | \
* 3 M1 5 M2 7
* |/ \|/ \|
* 2 4 6
* |___/____/
* 1
* </pre>
*/
testRevWalkBehavior("commits/8", "merge/1");
testRevWalkBehavior("commits/8", "merge/2");
enableAndWriteCommitGraph();
reinitializeRevWalk();
assertCommitCntInGraph(11);
testRevWalkBehavior("commits/8", "merge/1");
testRevWalkBehavior("commits/8", "merge/2");
}
@Test
public void testMergedInto() throws Exception {
RevCommit c1 = commit();
Ref branch1 = branch(c1, "commits/1");
RevCommit c2 = commit(c1);
Ref branch2 = branch(c2, "commits/2");
RevCommit c3 = commit(c2);
Ref branch3 = branch(c3, "commits/3");
RevCommit c4 = commit(c1);
Ref branch4 = branch(c4, "commits/4");
RevCommit c5 = commit(c4);
Ref branch5 = branch(c5, "commits/5");
enableAndWriteCommitGraph();
RevCommit c6 = commit(c1);
Ref branch6 = branch(c6, "commits/6");
RevCommit c7 = commit(c2, c4);
Ref branch7 = branch(c7, "commits/7");
RevCommit c8 = commit(c5);
Ref branch8 = branch(c8, "commits/8");
RevCommit c9 = commit(c4, c6);
Ref branch9 = branch(c9, "commits/9");
/*
* <pre>
* current graph structure:
* 8
* |
* 3 7 5 9
* |/ \|/ \
* 2 4 6
* |___/____/
* 1
* </pre>
*
* [6, 7, 8, 9] are not in commit-graph.
*/
reinitializeRevWalk();
assertFalse(isObjectIdInGraph(c9));
assertRefsEquals(asList(branch9), allMergedInto(c9));
assertFalse(isObjectIdInGraph(c8));
assertRefsEquals(asList(branch8), allMergedInto(c8));
assertFalse(isObjectIdInGraph(c7));
assertRefsEquals(asList(branch7), allMergedInto(c7));
assertFalse(isObjectIdInGraph(c6));
assertRefsEquals(asList(branch6, branch9), allMergedInto(c6));
assertTrue(isObjectIdInGraph(c5));
assertRefsEquals(asList(branch5, branch8), allMergedInto(c5));
assertTrue(isObjectIdInGraph(c4));
assertRefsEquals(asList(branch4, branch5, branch7, branch8, branch9),
allMergedInto(c4));
assertTrue(isObjectIdInGraph(c3));
assertRefsEquals(asList(branch3), allMergedInto(c3));
assertTrue(isObjectIdInGraph(c2));
assertRefsEquals(asList(branch2, branch3, branch7), allMergedInto(c2));
assertTrue(isObjectIdInGraph(c1));
assertRefsEquals(asList(branch1, branch2, branch3, branch4, branch5,
branch6, branch7, branch8, branch9), allMergedInto(c1));
}
boolean isObjectIdInGraph(AnyObjectId id) {
return rw.commitGraph().findGraphPosition(id) >= 0;
}
List<Ref> allMergedInto(RevCommit needle) throws IOException {
List<Ref> refs = db.getRefDatabase().getRefs();
return rw.getMergedInto(rw.lookupCommit(needle), refs);
}
void assertRefsEquals(List<Ref> expecteds, List<Ref> actuals) {
assertEquals(expecteds.size(), actuals.size());
Collections.sort(expecteds, Comparator.comparing(Ref::getName));
Collections.sort(actuals, Comparator.comparing(Ref::getName));
for (int i = 0; i < expecteds.size(); i++) {
Ref expected = expecteds.get(i);
Ref actual = actuals.get(i);
assertEquals(expected.getName(), actual.getName());
assertEquals(expected.getObjectId(), actual.getObjectId());
}
}
void testRevWalkBehavior(String branch, String compare) throws Exception {
assertCommits(
travel(TreeFilter.ALL, RevFilter.MERGE_BASE, RevSort.NONE, true,
branch, compare),
travel(TreeFilter.ALL, RevFilter.MERGE_BASE, RevSort.NONE,
false, branch, compare));
assertCommits(
travel(TreeFilter.ALL, RevFilter.ALL, RevSort.TOPO, true,
branch),
travel(TreeFilter.ALL, RevFilter.ALL, RevSort.TOPO, false,
branch));
assertCommits(
travel(TreeFilter.ALL, RevFilter.ALL, RevSort.TOPO, true,
compare),
travel(TreeFilter.ALL, RevFilter.ALL, RevSort.TOPO, false,
compare));
assertCommits(
travel(TreeFilter.ALL, RevFilter.ALL, RevSort.COMMIT_TIME_DESC,
true, branch),
travel(TreeFilter.ALL, RevFilter.ALL, RevSort.COMMIT_TIME_DESC,
false, branch));
assertCommits(
travel(TreeFilter.ALL, RevFilter.ALL, RevSort.COMMIT_TIME_DESC,
true, compare),
travel(TreeFilter.ALL, RevFilter.ALL, RevSort.COMMIT_TIME_DESC,
false, compare));
}
}