diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FirstParentRevWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FirstParentRevWalkTest.java new file mode 100644 index 000000000..474ff7a7c --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FirstParentRevWalkTest.java @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2019, Google LLC. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.revwalk; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.eclipse.jgit.revwalk.filter.MessageRevFilter; +import org.eclipse.jgit.revwalk.filter.RevFilter; +import org.junit.Test; + +public class FirstParentRevWalkTest extends RevWalkTestCase { + @Test + public void testStringOfPearls() throws Exception { + RevCommit a = commit(); + RevCommit b = commit(a); + RevCommit c = commit(b); + + rw.reset(); + rw.setFirstParent(true); + markStart(c); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testSideBranch() throws Exception { + RevCommit a = commit(); + RevCommit b1 = commit(a); + RevCommit b2 = commit(a); + RevCommit c1 = commit(b1); + RevCommit c2 = commit(b2); + RevCommit d = commit(c1, c2); + + rw.reset(); + rw.setFirstParent(true); + markStart(d); + assertCommit(d, rw.next()); + assertCommit(c1, rw.next()); + assertCommit(b1, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testSecondParentAncestorOfFirstParent() throws Exception { + RevCommit a = commit(); + RevCommit b = commit(a); + RevCommit c = commit(b, a); + + rw.reset(); + rw.setFirstParent(true); + markStart(c); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testFirstParentMultipleOccurrences() throws Exception { + RevCommit a = commit(); + RevCommit b = commit(a); + RevCommit c = commit(b); + RevCommit d = commit(b); + + rw.reset(); + rw.setFirstParent(true); + markStart(c); + markStart(d); + assertCommit(d, rw.next()); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testReachableAlongFirstAndLaterParents() throws Exception { + RevCommit a = commit(); + RevCommit b1 = commit(a); + RevCommit b2 = commit(a); + RevCommit b3 = commit(a); + RevCommit c = commit(b1, b2); + RevCommit d = commit(b2, b3); + + rw.reset(); + rw.setFirstParent(true); + markStart(c); + markStart(d); + assertCommit(d, rw.next()); + assertCommit(c, rw.next()); + // b3 is only reachable from c's second parent. + // b2 is reachable from c's second parent but d's first parent. + assertCommit(b2, rw.next()); + assertCommit(b1, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testStartCommitReachableOnlyFromLaterParents() + throws Exception { + RevCommit a = commit(); + RevCommit b1 = commit(a); + RevCommit b2 = commit(a); + RevCommit c = commit(b1, b2); + + rw.reset(); + rw.setFirstParent(true); + markStart(c); + markStart(b2); + assertCommit(c, rw.next()); + // b2 is only reachable from second parent, but is itself a start + // commit. + assertCommit(b2, rw.next()); + assertCommit(b1, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testRevFilter() throws Exception { + RevCommit a = commit(); + RevCommit b1 = commitBuilder().parent(a).message("commit b1").create(); + RevCommit b2 = commitBuilder().parent(a).message("commit b2").create(); + RevCommit c = commit(b1, b2); + + rw.reset(); + rw.setFirstParent(true); + rw.setRevFilter(MessageRevFilter.create("commit b")); + rw.markStart(c); + assertCommit(b1, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testTopoSort() throws Exception { + RevCommit a = commit(); + RevCommit b1 = commit(a); + RevCommit b2 = commit(a); + RevCommit c = commit(b1, b2); + + rw.reset(); + rw.sort(RevSort.TOPO); + rw.setFirstParent(true); + markStart(c); + assertCommit(c, rw.next()); + assertCommit(b1, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testCommitTimeSort() throws Exception { + RevCommit a = commit(); + RevCommit b1 = commit(a); + RevCommit b2 = commit(a); + RevCommit c = commit(b1, b2); + + rw.reset(); + rw.sort(RevSort.COMMIT_TIME_DESC); + rw.setFirstParent(true); + markStart(c); + assertCommit(c, rw.next()); + assertCommit(b1, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testReverseSort() throws Exception { + RevCommit a = commit(); + RevCommit b1 = commit(a); + RevCommit b2 = commit(a); + RevCommit c = commit(b1, b2); + + rw.reset(); + rw.sort(RevSort.REVERSE); + rw.setFirstParent(true); + markStart(c); + assertCommit(a, rw.next()); + assertCommit(b1, rw.next()); + assertCommit(c, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testBoundarySort() throws Exception { + RevCommit a = commit(); + RevCommit b = commit(a); + RevCommit c1 = commit(b); + RevCommit c2 = commit(b); + RevCommit d = commit(c1, c2); + + rw.reset(); + rw.sort(RevSort.BOUNDARY); + rw.setFirstParent(true); + markStart(d); + markUninteresting(a); + assertCommit(d, rw.next()); + assertCommit(c1, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testFirstParentOfFirstParentMarkedUninteresting() + throws Exception { + RevCommit a = commit(); + RevCommit b1 = commit(a); + RevCommit b2 = commit(a); + RevCommit c1 = commit(b1); + RevCommit c2 = commit(b2); + RevCommit d = commit(c1, c2); + + rw.reset(); + rw.setFirstParent(true); + markStart(d); + markUninteresting(b1); + assertCommit(d, rw.next()); + assertCommit(c1, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testFirstParentMarkedUninteresting() throws Exception { + RevCommit a = commit(); + RevCommit b1 = commit(a); + RevCommit b2 = commit(a); + RevCommit c = commit(b1, b2); + + rw.reset(); + rw.setFirstParent(true); + markStart(c); + markUninteresting(b1); + assertCommit(c, rw.next()); + assertNull(rw.next()); + } + + @Test + public void testDepthWalk() throws Exception { + RevCommit a = commit(); + RevCommit b1 = commit(a); + RevCommit b2 = commit(a); + RevCommit c = commit(b1, b2); + + try (DepthWalk.RevWalk dw = new DepthWalk.RevWalk(db, 1)) { + dw.setFirstParent(true); + dw.markRoot(dw.parseCommit(c)); + dw.markStart(dw.parseCommit(c)); + assertEquals(c, dw.next()); + assertEquals(b1, dw.next()); + assertNull(dw.next()); + } + } + + @Test + public void testDoNotRewriteParents() throws Exception { + RevCommit a = commit(); + RevCommit b1 = commit(a); + RevCommit b2 = commit(a); + RevCommit c = commit(b1, b2); + + rw.reset(); + rw.setFirstParent(true); + rw.setRewriteParents(false); + markStart(c); + assertCommit(c, rw.next()); + assertCommit(b1, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + @Test(expected = IllegalStateException.class) + public void testMarkStartBeforeSetFirstParent() throws Exception { + RevCommit a = commit(); + + rw.reset(); + markStart(a); + rw.setFirstParent(true); + } + + @Test(expected = IllegalStateException.class) + public void testMergeBaseWithFirstParentNotAllowed() throws Exception { + RevCommit a = commit(); + + rw.reset(); + rw.setFirstParent(true); + rw.setRevFilter(RevFilter.MERGE_BASE); + markStart(a); + assertNull(rw.next()); + } +} diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index df42dc757..b610deb1f 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -77,6 +77,7 @@ cannotDownload=Cannot download {0} cannotEnterObjectsPath=Cannot enter {0}/objects: {1} cannotEnterPathFromParent=Cannot enter {0} from {1}: {2} cannotExecute=cannot execute: {0} +cannotFindMergeBaseUsingFirstParent=Cannot find merge bases using a first-parent walk. cannotGet=Cannot get {0} cannotGetObjectsPath=Cannot get {0}/{1}: {2} cannotListObjectsPath=Cannot ls {0}/{1}: {2} @@ -134,6 +135,7 @@ commitAlreadyExists=exists {0} commitMessageNotSpecified=commit message not specified commitOnRepoWithoutHEADCurrentlyNotSupported=Commit on repo without HEAD currently not supported commitAmendOnInitialNotPossible=Amending is not possible on initial commit. +commitsHaveAlreadyBeenMarkedAsStart=Commits have already been marked as walk starts. compressingObjects=Compressing objects configSubsectionContainsNewline=config subsection name contains newline configSubsectionContainsNullByte=config subsection name contains byte 0x00 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index bdaef5a36..572f96341 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -138,6 +138,7 @@ public class JGitText extends TranslationBundle { /***/ public String cannotEnterObjectsPath; /***/ public String cannotEnterPathFromParent; /***/ public String cannotExecute; + /***/ public String cannotFindMergeBaseUsingFirstParent; /***/ public String cannotGet; /***/ public String cannotGetObjectsPath; /***/ public String cannotListObjectsPath; @@ -195,6 +196,7 @@ public class JGitText extends TranslationBundle { /***/ public String commitMessageNotSpecified; /***/ public String commitOnRepoWithoutHEADCurrentlyNotSupported; /***/ public String commitAmendOnInitialNotPossible; + /***/ public String commitsHaveAlreadyBeenMarkedAsStart; /***/ public String compressingObjects; /***/ public String configSubsectionContainsNewline; /***/ public String configSubsectionContainsNullByte; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java index 247a3bdb2..7b5e6fa31 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java @@ -49,6 +49,10 @@ abstract class AbstractRevQueue extends Generator { /** Current output flags set for this generator instance. */ int outputType; + AbstractRevQueue(boolean firstParent) { + super(firstParent); + } + /** * Add a commit to the queue. *
@@ -96,10 +100,15 @@ abstract class AbstractRevQueue extends Generator {
*/
public final void addParents(RevCommit c, RevFlag queueControl) {
final RevCommit[] pList = c.parents;
- if (pList == null)
+ if (pList == null) {
return;
- for (RevCommit p : pList)
- add(p, queueControl);
+ }
+ for (int i = 0; i < pList.length; i++) {
+ if (firstParent && i > 0) {
+ break;
+ }
+ add(pList[i], queueControl);
+ }
}
/**
@@ -138,6 +147,10 @@ abstract class AbstractRevQueue extends Generator {
}
private static class AlwaysEmptyQueue extends AbstractRevQueue {
+ private AlwaysEmptyQueue() {
+ super(false);
+ }
+
@Override
public void add(RevCommit c) {
throw new UnsupportedOperationException();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BlockRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BlockRevQueue.java
index 79307b5b7..64e9e0322 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BlockRevQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BlockRevQueue.java
@@ -53,13 +53,19 @@ abstract class BlockRevQueue extends AbstractRevQueue {
/**
* Create an empty revision queue.
+ *
+ * @param firstParent
+ * whether only first-parent links should be followed when
+ * walking
*/
- protected BlockRevQueue() {
+ protected BlockRevQueue(boolean firstParent) {
+ super(firstParent);
free = new BlockFreeList();
}
BlockRevQueue(Generator s) throws MissingObjectException,
IncorrectObjectTypeException, IOException {
+ super(s.firstParent);
free = new BlockFreeList();
outputType = s.outputType();
s.shareFreeList(this);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java
index 0fd6621e6..98c99e845 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BoundaryGenerator.java
@@ -55,6 +55,7 @@ class BoundaryGenerator extends Generator {
Generator g;
BoundaryGenerator(RevWalk w, Generator s) {
+ super(s.firstParent);
g = new InitialGenerator(w, s);
}
@@ -86,8 +87,9 @@ class BoundaryGenerator extends Generator {
private final Generator source;
InitialGenerator(RevWalk w, Generator s) {
+ super(s.firstParent);
walk = w;
- held = new FIFORevQueue();
+ held = new FIFORevQueue(firstParent);
source = s;
source.shareFreeList(held);
}
@@ -107,13 +109,19 @@ class BoundaryGenerator extends Generator {
IncorrectObjectTypeException, IOException {
RevCommit c = source.next();
if (c != null) {
- for (RevCommit p : c.parents)
- if ((p.flags & UNINTERESTING) != 0)
+ for (int i = 0; i < c.parents.length; i++) {
+ if (firstParent && i > 0) {
+ break;
+ }
+ RevCommit p = c.parents[i];
+ if ((p.flags & UNINTERESTING) != 0) {
held.add(p);
+ }
+ }
return c;
}
- final FIFORevQueue boundary = new FIFORevQueue();
+ final FIFORevQueue boundary = new FIFORevQueue(firstParent);
boundary.shareFreeList(held);
for (;;) {
c = held.next();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java
index b86e87620..d77b05553 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java
@@ -69,15 +69,18 @@ public class DateRevQueue extends AbstractRevQueue {
private int last = -1;
- /**
- * Create an empty date queue.
- */
+ /** Create an empty date queue. */
public DateRevQueue() {
- super();
+ super(false);
+ }
+
+ DateRevQueue(boolean firstParent) {
+ super(firstParent);
}
DateRevQueue(Generator s) throws MissingObjectException,
IncorrectObjectTypeException, IOException {
+ super(s.firstParent);
for (;;) {
final RevCommit c = s.next();
if (c == null)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DelayRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DelayRevQueue.java
index c397a0131..9134e08b2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DelayRevQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DelayRevQueue.java
@@ -70,6 +70,7 @@ final class DelayRevQueue extends Generator {
private int size;
DelayRevQueue(Generator g) {
+ super(g.firstParent);
pending = g;
delay = new FIFORevQueue();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
index 5199a2927..0b04d5d17 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java
@@ -95,7 +95,8 @@ class DepthGenerator extends Generator {
*/
DepthGenerator(DepthWalk w, Generator s) throws MissingObjectException,
IncorrectObjectTypeException, IOException {
- pending = new FIFORevQueue();
+ super(s.firstParent);
+ pending = new FIFORevQueue(firstParent);
walk = (RevWalk)w;
this.depth = w.getDepth();
@@ -196,7 +197,11 @@ class DepthGenerator extends Generator {
int newDepth = c.depth + 1;
- for (RevCommit p : c.parents) {
+ for (int i = 0; i < c.parents.length; i++) {
+ if (firstParent && i > 0) {
+ break;
+ }
+ RevCommit p = c.parents[i];
DepthWalk.Commit dp = (DepthWalk.Commit) p;
// If no depth has been assigned to this commit, assign
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/EndGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/EndGenerator.java
index 627e1c7a5..03916c815 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/EndGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/EndGenerator.java
@@ -47,7 +47,7 @@ class EndGenerator extends Generator {
static final EndGenerator INSTANCE = new EndGenerator();
private EndGenerator() {
- // We have nothing to initialize.
+ super(false);
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FIFORevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FIFORevQueue.java
index cdb084c15..40ee55c73 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FIFORevQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FIFORevQueue.java
@@ -56,11 +56,13 @@ public class FIFORevQueue extends BlockRevQueue {
private Block tail;
- /**
- * Create an empty FIFO queue.
- */
+ /** Create an empty FIFO queue. */
public FIFORevQueue() {
- super();
+ super(false);
+ }
+
+ FIFORevQueue(boolean firstParent) {
+ super(firstParent);
}
FIFORevQueue(Generator s) throws MissingObjectException,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FixUninterestingGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FixUninterestingGenerator.java
index 4e6d7e681..289842a90 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FixUninterestingGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FixUninterestingGenerator.java
@@ -62,6 +62,7 @@ final class FixUninterestingGenerator extends Generator {
private final Generator pending;
FixUninterestingGenerator(Generator g) {
+ super(g.firstParent);
pending = g;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/Generator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/Generator.java
index b2c92ea24..59c5cce82 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/Generator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/Generator.java
@@ -75,6 +75,12 @@ abstract class Generator {
/** Output may have {@link RevWalk#UNINTERESTING} marked on it. */
static final int HAS_UNINTERESTING = 1 << 4;
+ protected final boolean firstParent;
+
+ protected Generator(boolean firstParent) {
+ this.firstParent = firstParent;
+ }
+
/**
* Connect the supplied queue to this generator's own free list (if any).
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/LIFORevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/LIFORevQueue.java
index 846b8d92f..5628d359b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/LIFORevQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/LIFORevQueue.java
@@ -59,7 +59,7 @@ public class LIFORevQueue extends BlockRevQueue {
* Create an empty LIFO queue.
*/
public LIFORevQueue() {
- super();
+ super(false);
}
LIFORevQueue(Generator s) throws MissingObjectException,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
index 2fe95318b..4ea57cb51 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
@@ -85,8 +85,9 @@ class MergeBaseGenerator extends Generator {
private CarryStack stack;
MergeBaseGenerator(RevWalk w) {
+ super(w.isFirstParent());
walker = w;
- pending = new DateRevQueue();
+ pending = new DateRevQueue(firstParent);
}
void init(AbstractRevQueue p) throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java
index e607b7daa..52dd56d12 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java
@@ -109,6 +109,7 @@ class PendingGenerator extends Generator {
PendingGenerator(final RevWalk w, final DateRevQueue p,
final RevFilter f, final int out) {
+ super(w.isFirstParent());
walker = w;
pending = p;
filter = f;
@@ -140,7 +141,11 @@ class PendingGenerator extends Generator {
produce = filter.include(walker, c);
}
- for (RevCommit p : c.parents) {
+ for (int i = 0; i < c.parents.length; i++) {
+ RevCommit p = c.parents[i];
+ if (firstParent && i > 0) {
+ continue;
+ }
if ((p.flags & SEEN) != 0)
continue;
if ((p.flags & PARSED) == 0)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index 80fc81073..12e9f892b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -200,6 +200,8 @@ public class RevWalk implements Iterable
+ * If set, second- and higher-parent links are not traversed at all.
+ *
+ * This must be called prior to {@link #markStart(RevCommit)}.
+ *
+ * @param enable
+ * true to walk only first-parent links.
+ * @since 5.4
+ */
+ public void setFirstParent(boolean enable) {
+ assertNotStarted();
+ assertNoCommitsMarkedStart();
+ firstParent = enable;
+ queue = new DateRevQueue(firstParent);
+ pending = new StartGenerator(this);
+ }
+
/**
* Locate a reference to a blob without loading it.
*
@@ -1292,7 +1321,8 @@ public class RevWalk implements Iterable
* Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
* instances are not invalidated. RevFlag instances are not invalidated, but
- * are removed from all RevObjects.
+ * are removed from all RevObjects. The value of {@code firstParent} is
+ * retained.
*
* @param retainFlags
* application flags that should not be cleared from
@@ -1328,7 +1358,7 @@ public class RevWalk implements Iterable
+ * If {@link #markStart(RevCommit)} has already been called,
+ * {@link #reset()} can be called to satisfy this condition.
+ */
+ protected void assertNoCommitsMarkedStart() {
+ if (roots.isEmpty())
+ return;
+ throw new IllegalStateException(
+ JGitText.get().commitsHaveAlreadyBeenMarkedAsStart);
+ }
+
private boolean isNotStarted() {
return pending instanceof StartGenerator;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java
index 1c868ffe0..2e26641eb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java
@@ -77,6 +77,7 @@ class RewriteGenerator extends Generator {
private final Generator source;
RewriteGenerator(Generator s) {
+ super(s.firstParent);
source = s;
}
@@ -102,6 +103,10 @@ class RewriteGenerator extends Generator {
final int nParents = pList.length;
for (int i = 0; i < nParents; i++) {
final RevCommit oldp = pList[i];
+ if (firstParent && i > 0) {
+ c.parents = new RevCommit[] { rewrite(oldp) };
+ return c;
+ }
final RevCommit newp = rewrite(oldp);
if (oldp != newp) {
pList[i] = newp;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java
index eb129a263..b309d6fee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java
@@ -67,6 +67,7 @@ class StartGenerator extends Generator {
private final RevWalk walker;
StartGenerator(RevWalk w) {
+ super(w.isFirstParent());
walker = w;
}
@@ -89,9 +90,14 @@ class StartGenerator extends Generator {
// Computing for merge bases is a special case and does not
// use the bulk of the generator pipeline.
//
- if (tf != TreeFilter.ALL)
+ if (tf != TreeFilter.ALL) {
throw new IllegalStateException(MessageFormat.format(
JGitText.get().cannotCombineTreeFilterWithRevFilter, tf, rf));
+ }
+ if (w.isFirstParent()) {
+ throw new IllegalStateException(
+ JGitText.get().cannotFindMergeBaseUsingFirstParent);
+ }
final MergeBaseGenerator mbg = new MergeBaseGenerator(w);
walker.pending = mbg;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java
index 645034351..a2c9ef6da 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/TopoSortGenerator.java
@@ -71,15 +71,21 @@ class TopoSortGenerator extends Generator {
*/
TopoSortGenerator(Generator s) throws MissingObjectException,
IncorrectObjectTypeException, IOException {
- pending = new FIFORevQueue();
+ super(s.firstParent);
+ pending = new FIFORevQueue(firstParent);
outputType = s.outputType() | SORT_TOPO;
s.shareFreeList(pending);
for (;;) {
final RevCommit c = s.next();
- if (c == null)
+ if (c == null) {
break;
- for (RevCommit p : c.parents)
- p.inDegree++;
+ }
+ for (int i = 0; i < c.parents.length; i++) {
+ if (firstParent && i > 0) {
+ break;
+ }
+ c.parents[i].inDegree++;
+ }
pending.add(c);
}
}
@@ -113,7 +119,11 @@ class TopoSortGenerator extends Generator {
// All of our children have already produced,
// so it is OK for us to produce now as well.
//
- for (RevCommit p : c.parents) {
+ for (int i = 0; i < c.parents.length; i++) {
+ if (firstParent && i > 0) {
+ break;
+ }
+ RevCommit p = c.parents[i];
if (--p.inDegree == 0 && (p.flags & TOPO_DELAY) != 0) {
// This parent tried to come before us, but we are
// his last child. unpop the parent so it goes right