Repository navigation
Description
Version
7.8.0.202609011348-r
Operating System
Windows/Linux (irrelevant)
Bug description
Since 7.8.0, a RevFilter that halts a walk by throwing StopWalkException — including jgit's own
MaxCountRevFilter — propagates that exception out of RevWalk.iterator() when the walk is sorted
with RevSort.TOPO. It worked in 7.7.1 and still works without RevSort.TOPO.
Introduced by ab51fe2 "RevWalk: Use an incremental topological sorting algorithm". Still
present on master.
Reproduction
JoostK/jgit-topo-sort-regression
mvn test # 7.8.0.202609011348-r -> 1 failure
mvn test -Djgit.version=7.7.1.202607240634-r # 7.7.1 -> all pass
The essential part:
// 5-commit linear history, HEAD == E
try (RevWalk revWalk = new RevWalk(repository)) {
revWalk.markStart(revWalk.parseCommit(headId));
revWalk.sort(RevSort.TOPO);
revWalk.setRevFilter(MaxCountRevFilter.create(2));
for (RevCommit commit : revWalk) { // throws StopWalkException on 7.8.0
...
}
}
Expected: two commits. Actual on 7.8.0: org.eclipse.jgit.errors.StopWalkException.
Cause
StopWalkException is the documented way for a filter to stop a walk (RevFilter#include: "the
walk is halted and no more results are provided"), and PendingGenerator.next() is the only place
in jgit that catches it:
} catch (StopWalkException swe) {
pending.clear();
return null;
}
Before 7.8.0, RevSort.TOPO wrapped a PendingGenerator (TopoSortGenerator), so the exception
was absorbed there. Since ab51fe2, StartGenerator.next() builds a TopoSortPendingGenerator
instead, which applies the RevFilter itself:
TopoSortPendingGenerator.<init>
-> TopoInDegreePhase.initialize -> calculateInDegrees -> rewriteIfNeeded
-> TopoExplorePhase.explore -> TopoExplorePhase.passThroughFilter
-> filter.include(walker, c)
TopoExplorePhase.passThroughFilter calls filter.include(walker, c) with no handling, and the
identifier StopWalkException appears in none of TopoExplorePhase, TopoInDegreePhase or
TopoSortPendingGenerator — on 7.8.0 or on master. The exception therefore travels up through
StartGenerator.next() and out of RevWalk.next() / nextForIterator() / iterator().
The filter is consulted from two places in the new code (TopoExplorePhase.explore marks
topoPassedFilterFlag, TopoSortPendingGenerator.next reads it via hasPassedFilter), so a fix
presumably needs to stop the exploration and drain whatever is already queued, rather than just
catching at the outermost frame.
Affected versions
7.8.0.202609011348-r— affected7.7.1.202607240634-r— not affected
Actual behavior
mvn test
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.example.FilterOrderTest
SLF4J(W): No SLF4J providers were found.
SLF4J(W): Defaulting to no-operation (NOP) logger implementation
SLF4J(W): See https://www.slf4j.org/codes.html#noProviders for further details.
=== TOPO
filter asked about : [H, F, E, D, C, B, A]
walk emitted : [H, F, E, D, C, B, A]
orders agree : true
=== COMMIT_TIME_DESC (default)
filter asked about : [H, F, E, D, C, B, A]
walk emitted : [H, F, E, D, C, B, A]
orders agree : true
=== REVERSE
filter asked about : [H, F, E, D, C, B, A]
walk emitted : [A, B, C, D, E, F, H]
orders agree : false
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.755 s -- in org.example.FilterOrderTest
[INFO] Running org.example.TopoSortStopWalkTest
[ERROR] Tests run: 4, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 2.545 s <<< FAILURE! -- in org.example.TopoSortStopWalkTest
[ERROR] org.example.TopoSortStopWalkTest.maxCountUnderTopoSort -- Time elapsed: 0.476 s <<< ERROR!
org.eclipse.jgit.errors.StopWalkException
at org.eclipse.jgit.errors.StopWalkException.<clinit>(StopWalkException.java:23)
at org.eclipse.jgit.treewalk.filter.PathFilterGroup$Group.include(PathFilterGroup.java:226)
at org.eclipse.jgit.treewalk.filter.TreeFilter.matchFilter(TreeFilter.java:198)
at org.eclipse.jgit.treewalk.TreeWalk.next(TreeWalk.java:919)
at org.eclipse.jgit.dircache.DirCache.updateSmudgedEntries(DirCache.java:1024)
at org.eclipse.jgit.dircache.DirCache.writeTo(DirCache.java:683)
at org.eclipse.jgit.dircache.DirCache.write(DirCache.java:632)
at org.eclipse.jgit.dircache.BaseDirCacheEditor.commit(BaseDirCacheEditor.java:247)
at org.eclipse.jgit.dircache.DirCacheBuilder.commit(DirCacheBuilder.java:1)
at org.eclipse.jgit.merge.ResolveMerger$WorkTreeUpdater.writeWorkTreeChanges(ResolveMerger.java:373)
at org.eclipse.jgit.merge.ResolveMerger.mergeTrees(ResolveMerger.java:1974)
at org.eclipse.jgit.merge.ResolveMerger.mergeImpl(ResolveMerger.java:951)
at org.eclipse.jgit.merge.Merger.merge(Merger.java:233)
at org.eclipse.jgit.merge.Merger.merge(Merger.java:186)
at org.eclipse.jgit.merge.ThreeWayMerger.merge(ThreeWayMerger.java:95)
at org.eclipse.jgit.api.MergeCommand.call(MergeCommand.java:347)
at org.example.TestHistory.mergeFrom(TestHistory.java:70)
at org.example.FilterOrderTest.reportFilterOrderVersusEmissionOrder(FilterOrderTest.java:36)
at java.base/java.lang.reflect.Method.invoke(Method.java:565)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
[INFO]
[INFO] Results:
[INFO]
[ERROR] Errors:
[ERROR] TopoSortStopWalkTest.maxCountUnderTopoSort » StopWalk
[INFO]
[ERROR] Tests run: 5, Failures: 0, Errors: 1, Skipped: 0
Expected behavior
mvn test -Djgit.version=7.7.1.202607240634-r
[INFO] --- surefire:3.5.2:test (default-test) @ jgit-topo-stopwalk-repro ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.example.FilterOrderTest
SLF4J(W): No SLF4J providers were found.
SLF4J(W): Defaulting to no-operation (NOP) logger implementation
SLF4J(W): See https://www.slf4j.org/codes.html#noProviders for further details.
=== TOPO
filter asked about : [H, F, E, D, C, B, A]
walk emitted : [H, F, E, D, C, B, A]
orders agree : true
=== COMMIT_TIME_DESC (default)
filter asked about : [H, F, E, D, C, B, A]
walk emitted : [H, F, E, D, C, B, A]
orders agree : true
=== REVERSE
filter asked about : [H, F, E, D, C, B, A]
walk emitted : [A, B, C, D, E, F, H]
orders agree : false
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.458 s -- in org.example.FilterOrderTest
[INFO] Running org.example.TopoSortStopWalkTest
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.365 s -- in org.example.TopoSortStopWalkTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
Other information
A note on the stack trace
Anyone triaging this from a user-supplied stack trace should know that
StopWalkException preallocates a singleton and does not override fillInStackTrace():
public static final StopWalkException INSTANCE = new StopWalkException();
The trace is captured once, at class initialisation, and every later throw reuses it. Since jgit
throws StopWalkException as ordinary control flow elsewhere (e.g.
PathFilterGroup$Single.include during AddCommand), the reported frames often point somewhere
unrelated to the failing walk. Might be worth overriding fillInStackTrace() to return this and
documenting that the trace is meaningless, so it stops being misleading.
This issue/repro was authored by Claude Opus 5, reproducing a regression that surfaced in the update to 7.8.0 in a private app.
Activity
nishchay2306 commentedon Oct 14, 2026
I tested TopoSortPendingGenerator directly and could reproduce the exception. But current master is using older pipeline after 0e3222f, so the public TOPO example passes now
I made a small fix locally in TopoExplorePhase to catch StopWalkException. It remembers exploration is stopped, and keeps already accepted commits for output. I tested limit of 2 and 0, both with and without commit graph.
Would it be useful if I send this as a separate Gerrit change? or should this wait until incremental implementation is enabled again.
nishchay2306 commentedon Oct 29, 2026
The reporter's unchanged reproduction fails with StopWalkException on 7.8.0.202609011348-r and passes all five tests on current master (ec7ed58a8). Would you like me to upload a backport of 0e3222f47 to stable-7.8?
That revert restores PendingGenerator → TopoSortGenerator, where StopWalkException is already handled. Its commit message describes a reachability-performance regression, so a backport may also benefit other 7.8 users; I have not measured that regression on 7.8. It would give up the incremental TOPO optimization introduced by ab51fe2bd, which avoids processing the entire history first when a commit-graph is available.
Source and ancestry checks show that stable-7.8 at 458c33e02 still selects the incremental sorter; stable-7.9, stable-7.9.0, stable-8.0 and the 7.9 M1 tag contain the revert. I have not run the reproduction against those branch heads.
msohn commentedon Oct 29, 2026
yes, please cherry-pick the fix back to the stable-7.8 branch and push it for review
nishchay2306 commentedon Oct 29, 2026
Thanks. I tried to upload the cherry-pick, but Gerrit rejects it because it keeps Florian Schwabe as the author and my account lacks the Forge Author permission (Gerrit's Cherry Pick action on change 1248949 gives the same error). Would you prefer to cherry-pick it yourself, or should I upload it with myself as author and a note crediting Florian and 0e3222f in the message?
nishchay2306 commentedon Nov 4, 2026
Hi @msohn, following up on the Forge Author restriction. Would you prefer to cherry-pick the backport yourself or may I upload it with myself as author, keeping the
cherry picked fromline and explicitly crediting Florian and0e3222f47? The backport is already validated and the patch would remain unchanged.msohn commentedon Nov 4, 2026
I cherry-picked the fix amongst other to stable-7.8, see https://eclipse.gerrithub.io/c/eclipse-jgit/jgit/+/1257979