RevWalk: Add a setFirstParent that mimics C git's --first-parent

RevWalk does not currently provide a --first-parent equivalent and the
feature has been requested.

Add a field to the RevWalk class to specify whether walks should
traverse first parents only. Modify Generator implementations to support
the feature.

Change-Id: I4a9a0d5767f82141dcf6d08659d7cb77c585fae4
Signed-off-by: Dave Borowitz <dborowitz@google.com>
Signed-off-by: Alex Spradlin <alexaspradlin@google.com>
This commit is contained in:
Dave Borowitz 2019-06-03 14:03:49 -07:00 committed by Jonathan Nieder
parent cbccfed4b3
commit 4973f05252
20 changed files with 490 additions and 32 deletions

View File

@ -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());
}
}

View File

@ -77,6 +77,7 @@ cannotDownload=Cannot download {0}
cannotEnterObjectsPath=Cannot enter {0}/objects: {1} cannotEnterObjectsPath=Cannot enter {0}/objects: {1}
cannotEnterPathFromParent=Cannot enter {0} from {1}: {2} cannotEnterPathFromParent=Cannot enter {0} from {1}: {2}
cannotExecute=cannot execute: {0} cannotExecute=cannot execute: {0}
cannotFindMergeBaseUsingFirstParent=Cannot find merge bases using a first-parent walk.
cannotGet=Cannot get {0} cannotGet=Cannot get {0}
cannotGetObjectsPath=Cannot get {0}/{1}: {2} cannotGetObjectsPath=Cannot get {0}/{1}: {2}
cannotListObjectsPath=Cannot ls {0}/{1}: {2} cannotListObjectsPath=Cannot ls {0}/{1}: {2}
@ -134,6 +135,7 @@ commitAlreadyExists=exists {0}
commitMessageNotSpecified=commit message not specified commitMessageNotSpecified=commit message not specified
commitOnRepoWithoutHEADCurrentlyNotSupported=Commit on repo without HEAD currently not supported commitOnRepoWithoutHEADCurrentlyNotSupported=Commit on repo without HEAD currently not supported
commitAmendOnInitialNotPossible=Amending is not possible on initial commit. commitAmendOnInitialNotPossible=Amending is not possible on initial commit.
commitsHaveAlreadyBeenMarkedAsStart=Commits have already been marked as walk starts.
compressingObjects=Compressing objects compressingObjects=Compressing objects
configSubsectionContainsNewline=config subsection name contains newline configSubsectionContainsNewline=config subsection name contains newline
configSubsectionContainsNullByte=config subsection name contains byte 0x00 configSubsectionContainsNullByte=config subsection name contains byte 0x00

View File

@ -138,6 +138,7 @@ public static JGitText get() {
/***/ public String cannotEnterObjectsPath; /***/ public String cannotEnterObjectsPath;
/***/ public String cannotEnterPathFromParent; /***/ public String cannotEnterPathFromParent;
/***/ public String cannotExecute; /***/ public String cannotExecute;
/***/ public String cannotFindMergeBaseUsingFirstParent;
/***/ public String cannotGet; /***/ public String cannotGet;
/***/ public String cannotGetObjectsPath; /***/ public String cannotGetObjectsPath;
/***/ public String cannotListObjectsPath; /***/ public String cannotListObjectsPath;
@ -195,6 +196,7 @@ public static JGitText get() {
/***/ public String commitMessageNotSpecified; /***/ public String commitMessageNotSpecified;
/***/ public String commitOnRepoWithoutHEADCurrentlyNotSupported; /***/ public String commitOnRepoWithoutHEADCurrentlyNotSupported;
/***/ public String commitAmendOnInitialNotPossible; /***/ public String commitAmendOnInitialNotPossible;
/***/ public String commitsHaveAlreadyBeenMarkedAsStart;
/***/ public String compressingObjects; /***/ public String compressingObjects;
/***/ public String configSubsectionContainsNewline; /***/ public String configSubsectionContainsNewline;
/***/ public String configSubsectionContainsNullByte; /***/ public String configSubsectionContainsNullByte;

View File

@ -49,6 +49,10 @@ abstract class AbstractRevQueue extends Generator {
/** Current output flags set for this generator instance. */ /** Current output flags set for this generator instance. */
int outputType; int outputType;
AbstractRevQueue(boolean firstParent) {
super(firstParent);
}
/** /**
* Add a commit to the queue. * Add a commit to the queue.
* <p> * <p>
@ -96,10 +100,15 @@ public final void add(RevCommit c, RevFlag queueControl) {
*/ */
public final void addParents(RevCommit c, RevFlag queueControl) { public final void addParents(RevCommit c, RevFlag queueControl) {
final RevCommit[] pList = c.parents; final RevCommit[] pList = c.parents;
if (pList == null) if (pList == null) {
return; 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 @@ protected static void describe(StringBuilder s, RevCommit c) {
} }
private static class AlwaysEmptyQueue extends AbstractRevQueue { private static class AlwaysEmptyQueue extends AbstractRevQueue {
private AlwaysEmptyQueue() {
super(false);
}
@Override @Override
public void add(RevCommit c) { public void add(RevCommit c) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();

View File

@ -53,13 +53,19 @@ abstract class BlockRevQueue extends AbstractRevQueue {
/** /**
* Create an empty revision queue. * 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(); free = new BlockFreeList();
} }
BlockRevQueue(Generator s) throws MissingObjectException, BlockRevQueue(Generator s) throws MissingObjectException,
IncorrectObjectTypeException, IOException { IncorrectObjectTypeException, IOException {
super(s.firstParent);
free = new BlockFreeList(); free = new BlockFreeList();
outputType = s.outputType(); outputType = s.outputType();
s.shareFreeList(this); s.shareFreeList(this);

View File

@ -55,6 +55,7 @@ class BoundaryGenerator extends Generator {
Generator g; Generator g;
BoundaryGenerator(RevWalk w, Generator s) { BoundaryGenerator(RevWalk w, Generator s) {
super(s.firstParent);
g = new InitialGenerator(w, s); g = new InitialGenerator(w, s);
} }
@ -86,8 +87,9 @@ private class InitialGenerator extends Generator {
private final Generator source; private final Generator source;
InitialGenerator(RevWalk w, Generator s) { InitialGenerator(RevWalk w, Generator s) {
super(s.firstParent);
walk = w; walk = w;
held = new FIFORevQueue(); held = new FIFORevQueue(firstParent);
source = s; source = s;
source.shareFreeList(held); source.shareFreeList(held);
} }
@ -107,13 +109,19 @@ RevCommit next() throws MissingObjectException,
IncorrectObjectTypeException, IOException { IncorrectObjectTypeException, IOException {
RevCommit c = source.next(); RevCommit c = source.next();
if (c != null) { if (c != null) {
for (RevCommit p : c.parents) for (int i = 0; i < c.parents.length; i++) {
if ((p.flags & UNINTERESTING) != 0) if (firstParent && i > 0) {
break;
}
RevCommit p = c.parents[i];
if ((p.flags & UNINTERESTING) != 0) {
held.add(p); held.add(p);
}
}
return c; return c;
} }
final FIFORevQueue boundary = new FIFORevQueue(); final FIFORevQueue boundary = new FIFORevQueue(firstParent);
boundary.shareFreeList(held); boundary.shareFreeList(held);
for (;;) { for (;;) {
c = held.next(); c = held.next();

View File

@ -69,15 +69,18 @@ public class DateRevQueue extends AbstractRevQueue {
private int last = -1; private int last = -1;
/** /** Create an empty date queue. */
* Create an empty date queue.
*/
public DateRevQueue() { public DateRevQueue() {
super(); super(false);
}
DateRevQueue(boolean firstParent) {
super(firstParent);
} }
DateRevQueue(Generator s) throws MissingObjectException, DateRevQueue(Generator s) throws MissingObjectException,
IncorrectObjectTypeException, IOException { IncorrectObjectTypeException, IOException {
super(s.firstParent);
for (;;) { for (;;) {
final RevCommit c = s.next(); final RevCommit c = s.next();
if (c == null) if (c == null)

View File

@ -70,6 +70,7 @@ final class DelayRevQueue extends Generator {
private int size; private int size;
DelayRevQueue(Generator g) { DelayRevQueue(Generator g) {
super(g.firstParent);
pending = g; pending = g;
delay = new FIFORevQueue(); delay = new FIFORevQueue();
} }

View File

@ -95,7 +95,8 @@ class DepthGenerator extends Generator {
*/ */
DepthGenerator(DepthWalk w, Generator s) throws MissingObjectException, DepthGenerator(DepthWalk w, Generator s) throws MissingObjectException,
IncorrectObjectTypeException, IOException { IncorrectObjectTypeException, IOException {
pending = new FIFORevQueue(); super(s.firstParent);
pending = new FIFORevQueue(firstParent);
walk = (RevWalk)w; walk = (RevWalk)w;
this.depth = w.getDepth(); this.depth = w.getDepth();
@ -196,7 +197,11 @@ RevCommit next() throws MissingObjectException,
int newDepth = c.depth + 1; 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; DepthWalk.Commit dp = (DepthWalk.Commit) p;
// If no depth has been assigned to this commit, assign // If no depth has been assigned to this commit, assign

View File

@ -47,7 +47,7 @@ class EndGenerator extends Generator {
static final EndGenerator INSTANCE = new EndGenerator(); static final EndGenerator INSTANCE = new EndGenerator();
private EndGenerator() { private EndGenerator() {
// We have nothing to initialize. super(false);
} }
@Override @Override

View File

@ -56,11 +56,13 @@ public class FIFORevQueue extends BlockRevQueue {
private Block tail; private Block tail;
/** /** Create an empty FIFO queue. */
* Create an empty FIFO queue.
*/
public FIFORevQueue() { public FIFORevQueue() {
super(); super(false);
}
FIFORevQueue(boolean firstParent) {
super(firstParent);
} }
FIFORevQueue(Generator s) throws MissingObjectException, FIFORevQueue(Generator s) throws MissingObjectException,

View File

@ -62,6 +62,7 @@ final class FixUninterestingGenerator extends Generator {
private final Generator pending; private final Generator pending;
FixUninterestingGenerator(Generator g) { FixUninterestingGenerator(Generator g) {
super(g.firstParent);
pending = g; pending = g;
} }

View File

@ -75,6 +75,12 @@ abstract class Generator {
/** Output may have {@link RevWalk#UNINTERESTING} marked on it. */ /** Output may have {@link RevWalk#UNINTERESTING} marked on it. */
static final int HAS_UNINTERESTING = 1 << 4; 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). * Connect the supplied queue to this generator's own free list (if any).
* *

View File

@ -59,7 +59,7 @@ public class LIFORevQueue extends BlockRevQueue {
* Create an empty LIFO queue. * Create an empty LIFO queue.
*/ */
public LIFORevQueue() { public LIFORevQueue() {
super(); super(false);
} }
LIFORevQueue(Generator s) throws MissingObjectException, LIFORevQueue(Generator s) throws MissingObjectException,

View File

@ -85,8 +85,9 @@ class MergeBaseGenerator extends Generator {
private CarryStack stack; private CarryStack stack;
MergeBaseGenerator(RevWalk w) { MergeBaseGenerator(RevWalk w) {
super(w.isFirstParent());
walker = w; walker = w;
pending = new DateRevQueue(); pending = new DateRevQueue(firstParent);
} }
void init(AbstractRevQueue p) throws IOException { void init(AbstractRevQueue p) throws IOException {

View File

@ -109,6 +109,7 @@ class PendingGenerator extends Generator {
PendingGenerator(final RevWalk w, final DateRevQueue p, PendingGenerator(final RevWalk w, final DateRevQueue p,
final RevFilter f, final int out) { final RevFilter f, final int out) {
super(w.isFirstParent());
walker = w; walker = w;
pending = p; pending = p;
filter = f; filter = f;
@ -140,7 +141,11 @@ RevCommit next() throws MissingObjectException,
produce = filter.include(walker, c); 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) if ((p.flags & SEEN) != 0)
continue; continue;
if ((p.flags & PARSED) == 0) if ((p.flags & PARSED) == 0)

View File

@ -200,6 +200,8 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
private boolean rewriteParents = true; private boolean rewriteParents = true;
private boolean firstParent;
boolean shallowCommitsInitialized; boolean shallowCommitsInitialized;
/** /**
@ -232,7 +234,7 @@ private RevWalk(ObjectReader or, boolean closeReader) {
idBuffer = new MutableObjectId(); idBuffer = new MutableObjectId();
objects = new ObjectIdOwnerMap<>(); objects = new ObjectIdOwnerMap<>();
roots = new ArrayList<>(); roots = new ArrayList<>();
queue = new DateRevQueue(); queue = new DateRevQueue(false);
pending = new StartGenerator(this); pending = new StartGenerator(this);
sorting = EnumSet.of(RevSort.NONE); sorting = EnumSet.of(RevSort.NONE);
filter = RevFilter.ALL; filter = RevFilter.ALL;
@ -660,6 +662,33 @@ public void setRetainBody(boolean retain) {
retainBody = retain; retainBody = retain;
} }
/**
* @return whether only first-parent links should be followed when walking.
* @since 5.4
*/
public boolean isFirstParent() {
return firstParent;
}
/**
* Set whether or not only first parent links should be followed.
* <p>
* If set, second- and higher-parent links are not traversed at all.
* <p>
* 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. * Locate a reference to a blob without loading it.
* <p> * <p>
@ -1292,7 +1321,8 @@ public final void resetRetain(RevFlag... retainFlags) {
* <p> * <p>
* Unlike {@link #dispose()} previously acquired RevObject (and RevCommit) * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
* instances are not invalidated. RevFlag instances are not invalidated, but * 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 * @param retainFlags
* application flags that should <b>not</b> be cleared from * application flags that should <b>not</b> be cleared from
@ -1328,7 +1358,7 @@ protected void reset(int retainFlags) {
} }
roots.clear(); roots.clear();
queue = new DateRevQueue(); queue = new DateRevQueue(firstParent);
pending = new StartGenerator(this); pending = new StartGenerator(this);
} }
@ -1346,9 +1376,10 @@ public void dispose() {
delayFreeFlags = 0; delayFreeFlags = 0;
retainOnReset = 0; retainOnReset = 0;
carryFlags = UNINTERESTING; carryFlags = UNINTERESTING;
firstParent = false;
objects.clear(); objects.clear();
roots.clear(); roots.clear();
queue = new DateRevQueue(); queue = new DateRevQueue(firstParent);
pending = new StartGenerator(this); pending = new StartGenerator(this);
shallowCommitsInitialized = false; shallowCommitsInitialized = false;
} }
@ -1420,6 +1451,19 @@ protected void assertNotStarted() {
throw new IllegalStateException(JGitText.get().outputHasAlreadyBeenStarted); throw new IllegalStateException(JGitText.get().outputHasAlreadyBeenStarted);
} }
/**
* Throws an exception if any commits have been marked as start.
* <p>
* 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() { private boolean isNotStarted() {
return pending instanceof StartGenerator; return pending instanceof StartGenerator;
} }

View File

@ -77,6 +77,7 @@ class RewriteGenerator extends Generator {
private final Generator source; private final Generator source;
RewriteGenerator(Generator s) { RewriteGenerator(Generator s) {
super(s.firstParent);
source = s; source = s;
} }
@ -102,6 +103,10 @@ RevCommit next() throws MissingObjectException,
final int nParents = pList.length; final int nParents = pList.length;
for (int i = 0; i < nParents; i++) { for (int i = 0; i < nParents; i++) {
final RevCommit oldp = pList[i]; final RevCommit oldp = pList[i];
if (firstParent && i > 0) {
c.parents = new RevCommit[] { rewrite(oldp) };
return c;
}
final RevCommit newp = rewrite(oldp); final RevCommit newp = rewrite(oldp);
if (oldp != newp) { if (oldp != newp) {
pList[i] = newp; pList[i] = newp;

View File

@ -67,6 +67,7 @@ class StartGenerator extends Generator {
private final RevWalk walker; private final RevWalk walker;
StartGenerator(RevWalk w) { StartGenerator(RevWalk w) {
super(w.isFirstParent());
walker = w; walker = w;
} }
@ -89,9 +90,14 @@ RevCommit next() throws MissingObjectException,
// Computing for merge bases is a special case and does not // Computing for merge bases is a special case and does not
// use the bulk of the generator pipeline. // use the bulk of the generator pipeline.
// //
if (tf != TreeFilter.ALL) if (tf != TreeFilter.ALL) {
throw new IllegalStateException(MessageFormat.format( throw new IllegalStateException(MessageFormat.format(
JGitText.get().cannotCombineTreeFilterWithRevFilter, tf, rf)); JGitText.get().cannotCombineTreeFilterWithRevFilter, tf, rf));
}
if (w.isFirstParent()) {
throw new IllegalStateException(
JGitText.get().cannotFindMergeBaseUsingFirstParent);
}
final MergeBaseGenerator mbg = new MergeBaseGenerator(w); final MergeBaseGenerator mbg = new MergeBaseGenerator(w);
walker.pending = mbg; walker.pending = mbg;

View File

@ -71,15 +71,21 @@ class TopoSortGenerator extends Generator {
*/ */
TopoSortGenerator(Generator s) throws MissingObjectException, TopoSortGenerator(Generator s) throws MissingObjectException,
IncorrectObjectTypeException, IOException { IncorrectObjectTypeException, IOException {
pending = new FIFORevQueue(); super(s.firstParent);
pending = new FIFORevQueue(firstParent);
outputType = s.outputType() | SORT_TOPO; outputType = s.outputType() | SORT_TOPO;
s.shareFreeList(pending); s.shareFreeList(pending);
for (;;) { for (;;) {
final RevCommit c = s.next(); final RevCommit c = s.next();
if (c == null) if (c == null) {
break; 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); pending.add(c);
} }
} }
@ -113,7 +119,11 @@ RevCommit next() throws MissingObjectException,
// All of our children have already produced, // All of our children have already produced,
// so it is OK for us to produce now as well. // 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) { if (--p.inDegree == 0 && (p.flags & TOPO_DELAY) != 0) {
// This parent tried to come before us, but we are // This parent tried to come before us, but we are
// his last child. unpop the parent so it goes right // his last child. unpop the parent so it goes right