Browse Source

Introduce getMergedInto(RevCommit commit, Collection<Ref> refs)

In cases where we need to determine if a given commit is merged
into many refs, using isMergedInto(base, tip) for each ref would
cause multiple unwanted walks.

getMergedInto() marks the unreachable commits as uninteresting
which would then avoid walking that same path again.

Using the same api, also introduce isMergedIntoAny() and
isMergedIntoAll()

Change-Id: I65de9873dce67af9c415d1d236bf52d31b67e8fe
Signed-off-by: Adithya Chakilam <quic_achakila@quicinc.com>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
tags/v5.12.0.202105051250-m2
Adithya Chakilam 3 years ago
parent
commit
0bd2f4bf77

+ 12
- 0
org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java View File

@@ -25,6 +25,7 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.file.Path;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@@ -39,6 +40,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -385,6 +387,16 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
updateRef.update();
}

/**
* Get all Refs
*
* @return list of refs
* @throws IOException
*/
public List<Ref> getRefs() throws IOException {
return db.getRefDatabase().getRefs();
}

/**
* Checkout a branch
*

+ 81
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkMergedIntoTest.java View File

@@ -11,6 +11,9 @@ package org.eclipse.jgit.revwalk;

import static org.junit.Assert.assertTrue;

import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.jgit.lib.Ref;
import org.junit.Test;

public class RevWalkMergedIntoTest extends RevWalkTestCase {
@@ -44,4 +47,82 @@ public class RevWalkMergedIntoTest extends RevWalkTestCase {
final RevCommit t = commit(n, o);
assertTrue(rw.isMergedInto(b, t));
}

@Test
public void testGetMergedInto() throws Exception {
/*
* i
* / \
* A o
* / \ \
* o1 o2 E
* / \ / \
* B C D
*/
String b = "refs/heads/b";
String c = "refs/heads/c";
String d = "refs/heads/d";
String e = "refs/heads/e";
final RevCommit i = commit();
final RevCommit a = commit(i);
final RevCommit o1 = commit(a);
final RevCommit o2 = commit(a);
createBranch(commit(o1), b);
createBranch(commit(o1, o2), c);
createBranch(commit(o2), d);
createBranch(commit(commit(i)), e);

List<String> modifiedResult = rw.getMergedInto(a, getRefs())
.stream().map(Ref::getName).collect(Collectors.toList());

assertTrue(modifiedResult.size() == 3);
assertTrue(modifiedResult.contains(b));
assertTrue(modifiedResult.contains(c));
assertTrue(modifiedResult.contains(d));
}

@Test
public void testIsMergedIntoAny() throws Exception {
/*
* i
* / \
* A o
* / \
* o C
* /
* B
*/
String b = "refs/heads/b";
String c = "refs/heads/c";
final RevCommit i = commit();
final RevCommit a = commit(i);
createBranch(commit(commit(a)), b);
createBranch(commit(commit(i)), c);

assertTrue( rw.isMergedIntoAny(a, getRefs()));
}

@Test
public void testIsMergedIntoAll() throws Exception {
/*
*
* A
* / \
* o1 o2
* / \ / \
* B C D
*/

String b = "refs/heads/b";
String c = "refs/heads/c";
String d = "refs/heads/c";
final RevCommit a = commit();
final RevCommit o1 = commit(a);
final RevCommit o2 = commit(a);
createBranch(commit(o1), b);
createBranch(commit(o1, o2), c);
createBranch(commit(o2), d);

assertTrue(rw.isMergedIntoAll(a, getRefs()));
}
}

+ 114
- 0
org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java View File

@@ -36,6 +36,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdOwnerMap;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
@@ -181,6 +182,12 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {

boolean shallowCommitsInitialized;

private enum GetMergedIntoStrategy {
RETURN_ON_FIRST_FOUND,
RETURN_ON_FIRST_NOT_FOUND,
EVALUATE_ALL
}

/**
* Create a new revision walker for a given repository.
*
@@ -424,6 +431,113 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
}
}

/**
* Determine the Refs into which a commit is merged.
* <p>
* A commit is merged into a ref if we can find a path of commits that leads
* from that specific ref and ends at <code>commit</code>.
* <p>
*
* @param commit
* commit the caller thinks is reachable from <code>refs</code>.
* @param refs
* refs to start iteration from, and which is most likely a
* descendant (child) of <code>commit</code>.
* @return list of refs that are reachable from <code>commit</code>.
* @throws java.io.IOException
* a pack file or loose object could not be read.
* @since 5.12
*/
public List<Ref> getMergedInto(RevCommit commit, Collection<Ref> refs)
throws IOException{
return getMergedInto(commit, refs, GetMergedIntoStrategy.EVALUATE_ALL);
}

/**
* Determine if a <code>commit</code> is merged into any of the given
* <code>refs</code>.
*
* @param commit
* commit the caller thinks is reachable from <code>refs</code>.
* @param refs
* refs to start iteration from, and which is most likely a
* descendant (child) of <code>commit</code>.
* @return true if commit is merged into any of the refs; false otherwise.
* @throws java.io.IOException
* a pack file or loose object could not be read.
* @since 5.12
*/
public boolean isMergedIntoAny(RevCommit commit, Collection<Ref> refs)
throws IOException {
return getMergedInto(commit, refs,
GetMergedIntoStrategy.RETURN_ON_FIRST_FOUND).size() > 0;
}

/**
* Determine if a <code>commit</code> is merged into all of the given
* <code>refs</code>.
*
* @param commit
* commit the caller thinks is reachable from <code>refs</code>.
* @param refs
* refs to start iteration from, and which is most likely a
* descendant (child) of <code>commit</code>.
* @return true if commit is merged into all of the refs; false otherwise.
* @throws java.io.IOException
* a pack file or loose object could not be read.
* @since 5.12
*/
public boolean isMergedIntoAll(RevCommit commit, Collection<Ref> refs)
throws IOException {
return getMergedInto(commit, refs,
GetMergedIntoStrategy.RETURN_ON_FIRST_NOT_FOUND).size()
== refs.size();
}

private List<Ref> getMergedInto(RevCommit needle, Collection<Ref> haystacks,
Enum returnStrategy) throws IOException {
List<Ref> result = new ArrayList<>();
RevFilter oldRF = filter;
TreeFilter oldTF = treeFilter;
try {
finishDelayedFreeFlags();
filter = RevFilter.ALL;
treeFilter = TreeFilter.ALL;
for (Ref r: haystacks) {
RevObject o = parseAny(r.getObjectId());
if (!(o instanceof RevCommit)) {
continue;
}
RevCommit c = (RevCommit) o;
resetRetain(RevFlag.UNINTERESTING);
markStart(c);
boolean commitFound = false;
RevCommit next;
while ((next = next()) != null) {
if (References.isSameObject(next, needle)) {
result.add(r);
if (returnStrategy == GetMergedIntoStrategy.RETURN_ON_FIRST_FOUND) {
return result;
}
commitFound = true;
break;
}
}
if(!commitFound){
markUninteresting(c);
if (returnStrategy == GetMergedIntoStrategy.RETURN_ON_FIRST_NOT_FOUND) {
return result;
}
}
}
} finally {
reset(~freeFlags & APP_FLAGS);
filter = oldRF;
treeFilter = oldTF;
}
return result;
}

/**
* Pop the next most recent commit.
*

Loading…
Cancel
Save