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:
parent
cbccfed4b3
commit
4973f05252
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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).
|
||||||
*
|
*
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue