[log] IDEA-125987 Consume less temporary memory when loading full log

Instead of collecting all Hash commits and then compacting them to
integers, report them one by one and compact at once.

Implement for Git, keep as is for Mercurial by now.
This commit is contained in:
Kirill Likhodedov
2014-08-04 18:08:05 +04:00
parent 95d06a5728
commit 652c69ab1e
9 changed files with 129 additions and 105 deletions
@@ -29,8 +29,8 @@ public interface VcsLogProvider {
* <p>Reads the whole history, but only hashes & parents.</p>
* <p>Also reports authors/committers of this repository to the given user registry.</p>
*/
@NotNull
List<TimedVcsCommit> readAllHashes(@NotNull VirtualFile root, @NotNull Consumer<VcsUser> userRegistry) throws VcsException;
void readAllHashes(@NotNull VirtualFile root, @NotNull Consumer<VcsUser> userRegistry,
@NotNull Consumer<TimedVcsCommit> commitConsumer) throws VcsException;
/**
* Reads those details of the given commits, which are necessary to be shown in the log table.
@@ -1,36 +1,36 @@
package com.intellij.vcs.log.data;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.TimedVcsCommit;
import com.intellij.vcs.log.graph.GraphCommit;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class VcsLogMultiRepoJoiner {
public class VcsLogMultiRepoJoiner<CommitId> {
@NotNull
public List<? extends TimedVcsCommit> join(@NotNull Collection<List<? extends TimedVcsCommit>> logsFromRepos) {
public List<? extends GraphCommit<CommitId>> join(@NotNull Collection<List<? extends GraphCommit<CommitId>>> logsFromRepos) {
if (logsFromRepos.size() == 1) {
return logsFromRepos.iterator().next();
}
int size = 0;
for (List<? extends TimedVcsCommit> repo : logsFromRepos) {
for (List<? extends GraphCommit<CommitId>> repo : logsFromRepos) {
size += repo.size();
}
List<TimedVcsCommit> result = new ArrayList<TimedVcsCommit>(size);
List<GraphCommit<CommitId>> result = new ArrayList<GraphCommit<CommitId>>(size);
Map<TimedVcsCommit, Iterator<? extends TimedVcsCommit>> nextCommits = ContainerUtil.newHashMap();
for (List<? extends TimedVcsCommit> log : logsFromRepos) {
Iterator<? extends TimedVcsCommit> iterator = log.iterator();
Map<GraphCommit<CommitId>, Iterator<? extends GraphCommit<CommitId>>> nextCommits = ContainerUtil.newHashMap();
for (List<? extends GraphCommit<CommitId>> log : logsFromRepos) {
Iterator<? extends GraphCommit<CommitId>> iterator = log.iterator();
if (iterator.hasNext()) {
nextCommits.put(iterator.next(), iterator);
}
}
while (!nextCommits.isEmpty()) {
TimedVcsCommit lastCommit = findLatestCommit(nextCommits.keySet());
Iterator<? extends TimedVcsCommit> iterator = nextCommits.get(lastCommit);
GraphCommit<CommitId> lastCommit = findLatestCommit(nextCommits.keySet());
Iterator<? extends GraphCommit<CommitId>> iterator = nextCommits.get(lastCommit);
result.add(lastCommit);
nextCommits.remove(lastCommit);
@@ -43,10 +43,10 @@ public class VcsLogMultiRepoJoiner {
}
@NotNull
private static TimedVcsCommit findLatestCommit(@NotNull Set<TimedVcsCommit> commits) {
private GraphCommit<CommitId> findLatestCommit(@NotNull Set<GraphCommit<CommitId>> commits) {
long maxTimeStamp = Long.MIN_VALUE;
TimedVcsCommit lastCommit = null;
for (TimedVcsCommit commit : commits) {
GraphCommit<CommitId> lastCommit = null;
for (GraphCommit<CommitId> commit : commits) {
if (commit.getTimestamp() >= maxTimeStamp) {
maxTimeStamp = commit.getTimestamp();
lastCommit = commit;
@@ -96,11 +96,11 @@ public class VcsLogRefresherImpl implements VcsLogRefresher {
Map<VirtualFile, Collection<VcsRef>> refs = loadRefsFromVcs(myProviders);
Set<VirtualFile> roots = myProviders.keySet();
Map<VirtualFile, VcsLogProvider.Requirements> requirements = prepareSimpleRequirements(roots, myRecentCommitCount);
Map<VirtualFile, List<? extends TimedVcsCommit>> commits = loadRecentCommitsFromVcs(myProviders, requirements,
myUserRegistry, myTopCommitsDetailsCache);
List<? extends TimedVcsCommit> compoundLog = compound(commits.values());
List<GraphCommit<Integer>> compactedLog = compactCommits(compoundLog, myHashMap);
DataPack dataPack = DataPack.build(compactedLog, new RefsModel(refs, myHashMap.asIndexGetter()),
Map<VirtualFile, List<? extends GraphCommit<Integer>>> commits = loadRecentCommitsFromVcs(myProviders, requirements,
myUserRegistry, myTopCommitsDetailsCache,
myHashMap);
List<? extends GraphCommit<Integer>> compoundLog = compound(commits.values());
DataPack dataPack = DataPack.build(compoundLog, new RefsModel(refs, myHashMap.asIndexGetter()),
myHashMap.asIndexGetter(), myHashMap.asHashGetter(), myProviders, false);
mySingleTaskController.request(RefreshRequest.RELOAD_ALL); // build/rebuild the full log in bg
return dataPack;
@@ -152,19 +152,21 @@ public class VcsLogRefresherImpl implements VcsLogRefresher {
}
@NotNull
private static Map<VirtualFile, List<? extends TimedVcsCommit>> loadRecentCommitsFromVcs(@NotNull Map<VirtualFile, VcsLogProvider> providers,
@NotNull final Map<VirtualFile, VcsLogProvider.Requirements> requirements,
@NotNull final VcsUserRegistryImpl userRegistry,
@NotNull final Map<Hash, VcsCommitMetadata> topCommitsDetailsCache)
throws VcsException {
private static Map<VirtualFile, List<? extends GraphCommit<Integer>>> loadRecentCommitsFromVcs(
@NotNull Map<VirtualFile, VcsLogProvider> providers,
@NotNull final Map<VirtualFile, VcsLogProvider.Requirements> requirements,
@NotNull final VcsUserRegistryImpl userRegistry,
@NotNull final Map<Hash, VcsCommitMetadata> topCommitsDetailsCache,
@NotNull final VcsLogHashMap hashMap) throws VcsException
{
final StopWatch sw = StopWatch.start("loading commits");
final Map<VirtualFile, List<? extends TimedVcsCommit>> commits = ContainerUtil.newHashMap();
final Map<VirtualFile, List<? extends GraphCommit<Integer>>> commits = ContainerUtil.newHashMap();
new ProviderIterator() {
@Override
public void each(@NotNull VirtualFile root, @NotNull VcsLogProvider provider) throws VcsException {
List<? extends VcsCommitMetadata> metadatas = provider.readFirstBlock(root, requirements.get(root));
storeUsersAndDetails(metadatas, userRegistry, topCommitsDetailsCache);
commits.put(root, metadatas);
commits.put(root, compactCommits(metadatas, hashMap));
sw.rootCompleted(root);
}
}.iterate(providers);
@@ -177,9 +179,9 @@ public class VcsLogRefresherImpl implements VcsLogRefresher {
* Compounds logs from different repositories into a single multi-repository log.
*/
@NotNull
private static List<? extends TimedVcsCommit> compound(@NotNull Collection<List<? extends TimedVcsCommit>> commits) {
private static List<? extends GraphCommit<Integer>> compound(@NotNull Collection<List<? extends GraphCommit<Integer>>> commits) {
StopWatch sw = StopWatch.start("multi-repo join");
List<? extends TimedVcsCommit> joined = new VcsLogMultiRepoJoiner().join(commits);
List<? extends GraphCommit<Integer>> joined = new VcsLogMultiRepoJoiner<Integer>().join(commits);
sw.report();
return joined;
}
@@ -192,8 +194,7 @@ public class VcsLogRefresherImpl implements VcsLogRefresher {
@NotNull
@Override
public GraphCommit<Integer> fun(@NotNull TimedVcsCommit commit) {
return new GraphCommitImpl<Integer>(hashMap.getCommitIndex(commit.getId()),
ContainerUtil.map(commit.getParents(), hashMap.asIndexGetter()), commit.getTimestamp());
return compactCommit(commit, hashMap);
}
});
hashMap.flush();
@@ -201,6 +202,12 @@ public class VcsLogRefresherImpl implements VcsLogRefresher {
return map;
}
@NotNull
private static GraphCommitImpl<Integer> compactCommit(@NotNull TimedVcsCommit commit, @NotNull VcsLogHashMap hashMap) {
return new GraphCommitImpl<Integer>(hashMap.getCommitIndex(commit.getId()),
ContainerUtil.map(commit.getParents(), hashMap.asIndexGetter()), commit.getTimestamp());
}
private static void storeUsersAndDetails(@NotNull List<? extends VcsCommitMetadata> metadatas, @NotNull VcsUserRegistryImpl userRegistry,
@NotNull Map<Hash, VcsCommitMetadata> topCommitsDetailsCache) {
for (VcsCommitMetadata detail : metadatas) {
@@ -218,9 +225,9 @@ public class VcsLogRefresherImpl implements VcsLogRefresher {
private final Map<VirtualFile, LogAndRefs> myLoadedInfos = ContainerUtil.newHashMap();
private class LogAndRefs {
List<? extends TimedVcsCommit> log;
List<? extends GraphCommit<Integer>> log;
Collection<VcsRef> refs;
LogAndRefs(Collection<VcsRef> refs, List<? extends TimedVcsCommit> commits) {
LogAndRefs(Collection<VcsRef> refs, List<? extends GraphCommit<Integer>> commits) {
this.refs = refs;
this.log = commits;
}
@@ -266,16 +273,16 @@ public class VcsLogRefresherImpl implements VcsLogRefresher {
try {
if (permanentGraph != null) {
loadLogAndRefs(roots, currentRefs, myRecentCommitCount);
List<? extends TimedVcsCommit> compoundLog = compound(ContainerUtil.map(myLoadedInfos.values(),
new Function<LogAndRefs, List<? extends TimedVcsCommit>>() {
List<? extends GraphCommit<Integer>> compoundLog = compound(ContainerUtil.map(myLoadedInfos.values(),
new Function<LogAndRefs, List<? extends GraphCommit<Integer>>>() {
@Override
public List<? extends TimedVcsCommit> fun(LogAndRefs refs) {
public List<? extends GraphCommit<Integer>> fun(
LogAndRefs refs) {
return refs.log;
}
}));
List<GraphCommit<Integer>> preparedLog = compactCommits(compoundLog, myHashMap);
Map<VirtualFile, Collection<VcsRef>> allNewRefs = getAllNewRefs(myLoadedInfos, currentRefs);
List<GraphCommit<Integer>> joinedFullLog = join(preparedLog, permanentGraph.getAllCommits(), currentRefs, allNewRefs);
List<GraphCommit<Integer>> joinedFullLog = join(compoundLog, permanentGraph.getAllCommits(), currentRefs, allNewRefs);
if (joinedFullLog != null) {
return DataPack.build(joinedFullLog, new RefsModel(allNewRefs, myHashMap.asIndexGetter()),
myHashMap.asIndexGetter(), myHashMap.asHashGetter(), myProviders, true);
@@ -310,8 +317,9 @@ public class VcsLogRefresherImpl implements VcsLogRefresher {
Map<VirtualFile, VcsLogProvider> providers = getProviders(roots);
Map<VirtualFile, Collection<VcsRef>> refs = loadRefsFromVcs(providers);
Map<VirtualFile, VcsLogProvider.Requirements> requirements = prepareRequirements(roots, commitCount, prevRefs, refs);
Map<VirtualFile, List<? extends TimedVcsCommit>> commits = loadRecentCommitsFromVcs(providers, requirements,
myUserRegistry, myTopCommitsDetailsCache);
Map<VirtualFile, List<? extends GraphCommit<Integer>>> commits = loadRecentCommitsFromVcs(providers, requirements,
myUserRegistry, myTopCommitsDetailsCache,
myHashMap);
for (VirtualFile root : roots) {
myLoadedInfos.put(root, new LogAndRefs(refs.get(root), commits.get(root)));
}
@@ -345,7 +353,7 @@ public class VcsLogRefresherImpl implements VcsLogRefresher {
}
@Nullable
private List<GraphCommit<Integer>> join(@NotNull List<GraphCommit<Integer>> recentCommits, @NotNull List<GraphCommit<Integer>> fullLog,
private List<GraphCommit<Integer>> join(@NotNull List<? extends GraphCommit<Integer>> recentCommits, @NotNull List<GraphCommit<Integer>> fullLog,
@NotNull Map<VirtualFile, Collection<VcsRef>> previousRefs,
@NotNull Map<VirtualFile, Collection<VcsRef>> newRefs) {
StopWatch sw = StopWatch.start("joining new commits");
@@ -374,9 +382,8 @@ public class VcsLogRefresherImpl implements VcsLogRefresher {
@NotNull
private Pair<PermanentGraph<Integer>, Map<VirtualFile, Collection<VcsRef>>> loadFullLog() throws VcsException {
StopWatch sw = StopWatch.start("full log reload");
Collection<List<? extends TimedVcsCommit>> commits = readFullLogFromVcs();
List<? extends TimedVcsCommit> compoundLog = compound(commits);
List<GraphCommit<Integer>> graphCommits = compactCommits(compoundLog, myHashMap);
Collection<List<? extends GraphCommit<Integer>>> commits = readFullLogFromVcs();
List<? extends GraphCommit<Integer>> graphCommits = compound(commits);
Map<VirtualFile, Collection<VcsRef>> refMap = loadRefsFromVcs(myProviders);
PermanentGraph<Integer> permanentGraph = DataPack.buildPermanentGraph(graphCommits, new RefsModel(refMap, myHashMap.asIndexGetter()),
myHashMap.asIndexGetter(),
@@ -386,24 +393,31 @@ public class VcsLogRefresherImpl implements VcsLogRefresher {
}
@NotNull
private Collection<List<? extends TimedVcsCommit>> readFullLogFromVcs() throws VcsException {
private Collection<List<? extends GraphCommit<Integer>>> readFullLogFromVcs() throws VcsException {
final StopWatch sw = StopWatch.start("read full log from VCS");
final Collection<List<? extends TimedVcsCommit>> commits = ContainerUtil.newArrayList();
final Collection<List<? extends GraphCommit<Integer>>> logs = ContainerUtil.newArrayList();
new ProviderIterator() {
@Override
void each(@NotNull VirtualFile root, @NotNull VcsLogProvider provider) throws VcsException {
commits.add(provider.readAllHashes(root, new Consumer<VcsUser>() {
final List<GraphCommit<Integer>> graphCommits = ContainerUtil.newArrayList();
provider.readAllHashes(root, new Consumer<VcsUser>() {
@Override
public void consume(@NotNull VcsUser user) {
myUserRegistry.addUser(user);
}
}));
}, new Consumer<TimedVcsCommit>() {
@Override
public void consume(TimedVcsCommit commit) {
graphCommits.add(compactCommit(commit, myHashMap));
}
});
logs.add(graphCommits);
sw.rootCompleted(root);
}
}.iterate(myProviders);
myUserRegistry.flush();
sw.report();
return commits;
return logs;
}
}
@@ -93,9 +93,9 @@ public class TestVcsLogProvider implements VcsLogProvider {
return ContainerUtil.map(myCommits.subList(0, requirements.getCommitCount()), myCommitToMetadataConvertor);
}
@NotNull
@Override
public List<TimedVcsCommit> readAllHashes(@NotNull VirtualFile root, @NotNull Consumer<VcsUser> userRegistry) throws VcsException {
public void readAllHashes(@NotNull VirtualFile root, @NotNull Consumer<VcsUser> userRegistry,
@NotNull Consumer<TimedVcsCommit> commitConsumer) throws VcsException {
try {
myFullLogSemaphore.acquire();
}
@@ -103,7 +103,9 @@ public class TestVcsLogProvider implements VcsLogProvider {
throw new RuntimeException(e);
}
assertRoot(root);
return myCommits;
for (TimedVcsCommit commit : myCommits) {
commitConsumer.consume(commit);
}
}
private void assertRoot(@NotNull VirtualFile root) {
@@ -520,9 +520,19 @@ public class GitHistoryUtils {
@NotNull VirtualFile root,
@NotNull final Consumer<VcsUser> userRegistry,
@NotNull List<String> parameters) throws VcsException {
List<TimedVcsCommit> collector = ContainerUtil.newArrayList();
readCommits(project, root, userRegistry, parameters, new CollectConsumer<TimedVcsCommit>(collector));
return collector;
}
public static void readCommits(@NotNull final Project project,
@NotNull VirtualFile root,
@NotNull final Consumer<VcsUser> userRegistry,
@NotNull List<String> parameters,
@NotNull final Consumer<TimedVcsCommit> commitConsumer) throws VcsException {
final VcsLogObjectsFactory factory = getObjectsFactoryWithDisposeCheck(project);
if (factory == null) {
return Collections.emptyList();
return;
}
final int COMMIT_BUFFER = 1000;
@@ -536,8 +546,6 @@ public class GitHistoryUtils {
h.addParameters(parameters);
h.endOptions();
final List<TimedVcsCommit> commits = ContainerUtil.newArrayList();
final StringBuilder record = new StringBuilder();
final AtomicInteger records = new AtomicInteger();
final Ref<VcsException> ex = new Ref<VcsException>();
@@ -560,7 +568,10 @@ public class GitHistoryUtils {
afterParseRemainder = line.substring(recordEnd + 1);
}
if (afterParseRemainder != null && records.incrementAndGet() > COMMIT_BUFFER) { // null means can't parse now
commits.addAll(parseCommit(parser, record, userRegistry, factory));
List<TimedVcsCommit> commits = parseCommit(parser, record, userRegistry, factory);
for (TimedVcsCommit commit : commits) {
commitConsumer.consume(commit);
}
record.setLength(0);
record.append(afterParseRemainder);
}
@@ -573,7 +584,10 @@ public class GitHistoryUtils {
@Override
public void processTerminated(int exitCode) {
try {
commits.addAll(parseCommit(parser, record, userRegistry, factory));
List<TimedVcsCommit> commits = parseCommit(parser, record, userRegistry, factory);
for (TimedVcsCommit commit : commits) {
commitConsumer.consume(commit);
}
}
catch (Exception e) {
ex.set(new VcsException(e));
@@ -589,7 +603,6 @@ public class GitHistoryUtils {
if (!ex.isNull()) {
throw ex.get();
}
return commits;
}
@NotNull
@@ -27,49 +27,34 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
class GitBekParentFixer {
@NotNull
private final static String MAGIC_TEXT = "Merge remote";
@NotNull
private final VcsLogFilterCollection MAGIC_FILTER = createVcsLogFilterCollection();
@NotNull private static final String MAGIC_TEXT = "Merge remote";
@NotNull private static final VcsLogFilterCollection MAGIC_FILTER = createVcsLogFilterCollection();
@NotNull
private final VirtualFile myRoot;
@NotNull
private final GitLogProvider myGitLogProvider;
@NotNull
private final List<TimedVcsCommit> myAllCommits;
@NotNull private final Set<Hash> myWrongCommits;
GitBekParentFixer(@NotNull VirtualFile root, @NotNull GitLogProvider gitLogProvider, @NotNull List<TimedVcsCommit> allCommits) {
myRoot = root;
myGitLogProvider = gitLogProvider;
myAllCommits = allCommits;
private GitBekParentFixer(@NotNull Set<Hash> wrongCommits) {
myWrongCommits = wrongCommits;
}
@NotNull
List<TimedVcsCommit> getCorrectCommits() throws VcsException {
if (!BekSorter.isBekEnabled())
return myAllCommits;
final Set<Hash> wrongCommits = getWrongCommits();
return new AbstractList<TimedVcsCommit>() {
@Override
public TimedVcsCommit get(int index) {
TimedVcsCommit commit = myAllCommits.get(index);
if (!wrongCommits.contains(commit.getId()))
return commit;
return reverseParents(commit);
}
@Override
public int size() {
return myAllCommits.size();
}
};
static GitBekParentFixer prepare(@NotNull VirtualFile root, @NotNull GitLogProvider provider) throws VcsException {
if (!BekSorter.isBekEnabled()) {
return new GitBekParentFixer(Collections.<Hash>emptySet());
}
return new GitBekParentFixer(getWrongCommits(provider, root));
}
private Set<Hash> getWrongCommits() throws VcsException {
List<TimedVcsCommit> commitsMatchingFilter = myGitLogProvider.getCommitsMatchingFilter(myRoot, MAGIC_FILTER, -1);
@NotNull
TimedVcsCommit fixCommit(@NotNull TimedVcsCommit commit) {
if (!myWrongCommits.contains(commit.getId())) {
return commit;
}
return reverseParents(commit);
}
@NotNull
private static Set<Hash> getWrongCommits(@NotNull GitLogProvider provider, @NotNull VirtualFile root) throws VcsException {
List<TimedVcsCommit> commitsMatchingFilter = provider.getCommitsMatchingFilter(root, MAGIC_FILTER, -1);
return ContainerUtil.map2Set(commitsMatchingFilter, new Function<TimedVcsCommit, Hash>() {
@Override
public Hash fun(TimedVcsCommit timedVcsCommit) {
@@ -154,18 +154,23 @@ public class GitLogProvider implements VcsLogProvider {
});
}
@NotNull
@Override
public List<TimedVcsCommit> readAllHashes(@NotNull VirtualFile root, @NotNull Consumer<VcsUser> userRegistry) throws VcsException {
public void readAllHashes(@NotNull VirtualFile root, @NotNull Consumer<VcsUser> userRegistry,
@NotNull final Consumer<TimedVcsCommit> commitConsumer) throws VcsException {
if (!isRepositoryReady(root)) {
return Collections.emptyList();
return;
}
List<String> parameters = new ArrayList<String>(GitHistoryUtils.LOG_ALL);
parameters.add("--sparse");
List<TimedVcsCommit> timedVcsCommits = GitHistoryUtils.readCommits(myProject, root, userRegistry, parameters);
return new GitBekParentFixer(root, this, timedVcsCommits).getCorrectCommits();
final GitBekParentFixer parentFixer = GitBekParentFixer.prepare(root, this);
GitHistoryUtils.readCommits(myProject, root, userRegistry, parameters, new Consumer<TimedVcsCommit>() {
@Override
public void consume(TimedVcsCommit commit) {
commitConsumer.consume(parentFixer.fixCommit(commit));
}
});
}
@NotNull
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.ArrayUtil;
import com.intellij.util.CollectConsumer;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
@@ -84,9 +85,10 @@ public class GitLogProviderTest extends GitSingleRepoTest {
prepareSomeHistory();
createTaggedBranch();
List<VcsCommitMetadata> expectedLog = log();
@SuppressWarnings("unchecked")
List<TimedVcsCommit> actualLog = myLogProvider.readAllHashes(myProjectRoot, Consumer.EMPTY_CONSUMER);
assertOrderedEquals(expectedLog, actualLog);
List<TimedVcsCommit> collector = ContainerUtil.newArrayList();
//noinspection unchecked
myLogProvider.readAllHashes(myProjectRoot, Consumer.EMPTY_CONSUMER, new CollectConsumer<TimedVcsCommit>(collector));
assertOrderedEquals(expectedLog, collector);
}
public void test_get_current_user() throws Exception {
@@ -64,10 +64,13 @@ public class HgLogProvider implements VcsLogProvider {
return HgHistoryUtil.loadMetadata(myProject, root, requirements.getCommitCount(), Collections.<String>emptyList());
}
@NotNull
@Override
public List<TimedVcsCommit> readAllHashes(@NotNull VirtualFile root, @NotNull Consumer<VcsUser> userRegistry) throws VcsException {
return HgHistoryUtil.readAllHashes(myProject, root, userRegistry, Collections.<String>emptyList());
public void readAllHashes(@NotNull VirtualFile root, @NotNull Consumer<VcsUser> userRegistry,
@NotNull Consumer<TimedVcsCommit> commitConsumer) throws VcsException {
List<TimedVcsCommit> commits = HgHistoryUtil.readAllHashes(myProject, root, userRegistry, Collections.<String>emptyList());
for (TimedVcsCommit commit : commits) {
commitConsumer.consume(commit);
}
}
@NotNull