Skip to content

Commit

Permalink
SONAR-9692 - Perform issue tracking for short living branches
Browse files Browse the repository at this point in the history
  • Loading branch information
dbmeneses authored and Janos Gyerik committed Sep 12, 2017
1 parent dd47b17 commit 8f27cc5
Show file tree
Hide file tree
Showing 21 changed files with 850 additions and 143 deletions.
Expand Up @@ -49,6 +49,7 @@
import org.sonar.server.computation.task.projectanalysis.filesystem.ComputationTempFolderProvider; import org.sonar.server.computation.task.projectanalysis.filesystem.ComputationTempFolderProvider;
import org.sonar.server.computation.task.projectanalysis.issue.BaseIssuesLoader; import org.sonar.server.computation.task.projectanalysis.issue.BaseIssuesLoader;
import org.sonar.server.computation.task.projectanalysis.issue.CloseIssuesOnRemovedComponentsVisitor; import org.sonar.server.computation.task.projectanalysis.issue.CloseIssuesOnRemovedComponentsVisitor;
import org.sonar.server.computation.task.projectanalysis.issue.ComponentIssuesLoader;
import org.sonar.server.computation.task.projectanalysis.issue.ComponentIssuesRepositoryImpl; import org.sonar.server.computation.task.projectanalysis.issue.ComponentIssuesRepositoryImpl;
import org.sonar.server.computation.task.projectanalysis.issue.ComponentsWithUnprocessedIssues; import org.sonar.server.computation.task.projectanalysis.issue.ComponentsWithUnprocessedIssues;
import org.sonar.server.computation.task.projectanalysis.issue.DebtCalculator; import org.sonar.server.computation.task.projectanalysis.issue.DebtCalculator;
Expand All @@ -60,19 +61,24 @@
import org.sonar.server.computation.task.projectanalysis.issue.IssueCounter; import org.sonar.server.computation.task.projectanalysis.issue.IssueCounter;
import org.sonar.server.computation.task.projectanalysis.issue.IssueCreationDateCalculator; import org.sonar.server.computation.task.projectanalysis.issue.IssueCreationDateCalculator;
import org.sonar.server.computation.task.projectanalysis.issue.IssueLifecycle; import org.sonar.server.computation.task.projectanalysis.issue.IssueLifecycle;
import org.sonar.server.computation.task.projectanalysis.issue.IssueTrackingDelegator;
import org.sonar.server.computation.task.projectanalysis.issue.IssueVisitors; import org.sonar.server.computation.task.projectanalysis.issue.IssueVisitors;
import org.sonar.server.computation.task.projectanalysis.issue.IssuesRepositoryVisitor; import org.sonar.server.computation.task.projectanalysis.issue.IssuesRepositoryVisitor;
import org.sonar.server.computation.task.projectanalysis.issue.LoadComponentUuidsHavingOpenIssuesVisitor; import org.sonar.server.computation.task.projectanalysis.issue.LoadComponentUuidsHavingOpenIssuesVisitor;
import org.sonar.server.computation.task.projectanalysis.issue.MergeBranchIssuesLoader;
import org.sonar.server.computation.task.projectanalysis.issue.MovedIssueVisitor; import org.sonar.server.computation.task.projectanalysis.issue.MovedIssueVisitor;
import org.sonar.server.computation.task.projectanalysis.issue.NewEffortAggregator; import org.sonar.server.computation.task.projectanalysis.issue.NewEffortAggregator;
import org.sonar.server.computation.task.projectanalysis.issue.NewEffortCalculator; import org.sonar.server.computation.task.projectanalysis.issue.NewEffortCalculator;
import org.sonar.server.computation.task.projectanalysis.issue.RemoveProcessedComponentsVisitor;
import org.sonar.server.computation.task.projectanalysis.issue.RuleRepositoryImpl; import org.sonar.server.computation.task.projectanalysis.issue.RuleRepositoryImpl;
import org.sonar.server.computation.task.projectanalysis.issue.RuleTagsCopier; import org.sonar.server.computation.task.projectanalysis.issue.RuleTagsCopier;
import org.sonar.server.computation.task.projectanalysis.issue.RuleTypeCopier; import org.sonar.server.computation.task.projectanalysis.issue.RuleTypeCopier;
import org.sonar.server.computation.task.projectanalysis.issue.ScmAccountToUser; import org.sonar.server.computation.task.projectanalysis.issue.ScmAccountToUser;
import org.sonar.server.computation.task.projectanalysis.issue.ScmAccountToUserLoader; import org.sonar.server.computation.task.projectanalysis.issue.ScmAccountToUserLoader;
import org.sonar.server.computation.task.projectanalysis.issue.ShortBranchTrackerExecution;
import org.sonar.server.computation.task.projectanalysis.issue.TrackerBaseInputFactory; import org.sonar.server.computation.task.projectanalysis.issue.TrackerBaseInputFactory;
import org.sonar.server.computation.task.projectanalysis.issue.TrackerExecution; import org.sonar.server.computation.task.projectanalysis.issue.TrackerExecution;
import org.sonar.server.computation.task.projectanalysis.issue.TrackerMergeBranchInputFactory;
import org.sonar.server.computation.task.projectanalysis.issue.TrackerRawInputFactory; import org.sonar.server.computation.task.projectanalysis.issue.TrackerRawInputFactory;
import org.sonar.server.computation.task.projectanalysis.issue.UpdateConflictResolver; import org.sonar.server.computation.task.projectanalysis.issue.UpdateConflictResolver;
import org.sonar.server.computation.task.projectanalysis.issue.commonrule.BranchCoverageRule; import org.sonar.server.computation.task.projectanalysis.issue.commonrule.BranchCoverageRule;
Expand Down Expand Up @@ -212,6 +218,7 @@ private static List<Object> componentClasses() {
IssueCounter.class, IssueCounter.class,
MovedIssueVisitor.class, MovedIssueVisitor.class,
IssuesRepositoryVisitor.class, IssuesRepositoryVisitor.class,
RemoveProcessedComponentsVisitor.class,


// visitors : order is important, measure computers must be executed at the end in order to access to every measures / issues // visitors : order is important, measure computers must be executed at the end in order to access to every measures / issues
LoadComponentUuidsHavingOpenIssuesVisitor.class, LoadComponentUuidsHavingOpenIssuesVisitor.class,
Expand All @@ -227,9 +234,14 @@ private static List<Object> componentClasses() {
UpdateConflictResolver.class, UpdateConflictResolver.class,
TrackerBaseInputFactory.class, TrackerBaseInputFactory.class,
TrackerRawInputFactory.class, TrackerRawInputFactory.class,
TrackerMergeBranchInputFactory.class,
Tracker.class, Tracker.class,
TrackerExecution.class, TrackerExecution.class,
ShortBranchTrackerExecution.class,
ComponentIssuesLoader.class,
BaseIssuesLoader.class, BaseIssuesLoader.class,
MergeBranchIssuesLoader.class,
IssueTrackingDelegator.class,


// filemove // filemove
SourceSimilarityImpl.class, SourceSimilarityImpl.class,
Expand Down
Expand Up @@ -19,59 +19,22 @@
*/ */
package org.sonar.server.computation.task.projectanalysis.issue; package org.sonar.server.computation.task.projectanalysis.issue;


import java.util.ArrayList;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.DbSession; import org.sonar.db.DbSession;
import org.sonar.db.issue.IssueMapper;
import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder; import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
import org.sonar.server.computation.task.projectanalysis.qualityprofile.ActiveRulesHolder;


/** /**
* Loads all the project open issues from database, including manual issues. * Loads all the project open issues from database, including manual issues.
*
*/ */
public class BaseIssuesLoader { public class BaseIssuesLoader {


private final TreeRootHolder treeRootHolder; private final TreeRootHolder treeRootHolder;
private final DbClient dbClient; private final DbClient dbClient;
private final RuleRepository ruleRepository;
private final ActiveRulesHolder activeRulesHolder;


public BaseIssuesLoader(TreeRootHolder treeRootHolder, public BaseIssuesLoader(TreeRootHolder treeRootHolder, DbClient dbClient) {
DbClient dbClient, RuleRepository ruleRepository, ActiveRulesHolder activeRulesHolder) {
this.activeRulesHolder = activeRulesHolder;
this.treeRootHolder = treeRootHolder; this.treeRootHolder = treeRootHolder;
this.dbClient = dbClient; this.dbClient = dbClient;
this.ruleRepository = ruleRepository;
}

public List<DefaultIssue> loadForComponentUuid(String componentUuid) {
try (DbSession dbSession = dbClient.openSession(false)) {
List<DefaultIssue> result = new ArrayList<>();
dbSession.getMapper(IssueMapper.class).scrollNonClosedByComponentUuid(componentUuid, resultContext -> {
DefaultIssue issue = (resultContext.getResultObject()).toDefaultIssue();

// TODO this field should be set outside this class
if (!isActive(issue.ruleKey()) || ruleRepository.getByKey(issue.ruleKey()).getStatus() == RuleStatus.REMOVED) {
issue.setOnDisabledRule(true);
// TODO to be improved, why setOnDisabledRule(true) is not enough ?
issue.setBeingClosed(true);
}
// FIXME
issue.setSelectedAt(System.currentTimeMillis());
result.add(issue);
});
return result;
}
}

private boolean isActive(RuleKey ruleKey) {
return activeRulesHolder.get(ruleKey).isPresent();
} }


/** /**
Expand Down
Expand Up @@ -34,15 +34,15 @@
*/ */
public class CloseIssuesOnRemovedComponentsVisitor extends TypeAwareVisitorAdapter { public class CloseIssuesOnRemovedComponentsVisitor extends TypeAwareVisitorAdapter {


private final BaseIssuesLoader baseIssuesLoader; private final ComponentIssuesLoader issuesLoader;
private final ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues; private final ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues;
private final IssueCache issueCache; private final IssueCache issueCache;
private final IssueLifecycle issueLifecycle; private final IssueLifecycle issueLifecycle;


public CloseIssuesOnRemovedComponentsVisitor(BaseIssuesLoader baseIssuesLoader, ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues, IssueCache issueCache, public CloseIssuesOnRemovedComponentsVisitor(ComponentIssuesLoader issuesLoader, ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues, IssueCache issueCache,
IssueLifecycle issueLifecycle) { IssueLifecycle issueLifecycle) {
super(CrawlerDepthLimit.PROJECT, POST_ORDER); super(CrawlerDepthLimit.PROJECT, POST_ORDER);
this.baseIssuesLoader = baseIssuesLoader; this.issuesLoader = issuesLoader;
this.componentsWithUnprocessedIssues = componentsWithUnprocessedIssues; this.componentsWithUnprocessedIssues = componentsWithUnprocessedIssues;
this.issueCache = issueCache; this.issueCache = issueCache;
this.issueLifecycle = issueLifecycle; this.issueLifecycle = issueLifecycle;
Expand All @@ -57,7 +57,7 @@ private void closeIssuesForDeletedComponentUuids(Set<String> deletedComponentUui
DiskCache<DefaultIssue>.DiskAppender cacheAppender = issueCache.newAppender(); DiskCache<DefaultIssue>.DiskAppender cacheAppender = issueCache.newAppender();
try { try {
for (String deletedComponentUuid : deletedComponentUuids) { for (String deletedComponentUuid : deletedComponentUuids) {
List<DefaultIssue> issues = baseIssuesLoader.loadForComponentUuid(deletedComponentUuid); List<DefaultIssue> issues = issuesLoader.loadForComponentUuid(deletedComponentUuid);
for (DefaultIssue issue : issues) { for (DefaultIssue issue : issues) {
issue.setBeingClosed(true); issue.setBeingClosed(true);
// TODO should be renamed // TODO should be renamed
Expand Down
@@ -0,0 +1,67 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.computation.task.projectanalysis.issue;

import java.util.ArrayList;
import java.util.List;

import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.issue.IssueMapper;
import org.sonar.server.computation.task.projectanalysis.qualityprofile.ActiveRulesHolder;

public class ComponentIssuesLoader {
private final DbClient dbClient;
private final RuleRepository ruleRepository;
private final ActiveRulesHolder activeRulesHolder;

public ComponentIssuesLoader(DbClient dbClient, RuleRepository ruleRepository, ActiveRulesHolder activeRulesHolder) {
this.activeRulesHolder = activeRulesHolder;
this.dbClient = dbClient;
this.ruleRepository = ruleRepository;
}

public List<DefaultIssue> loadForComponentUuid(String componentUuid) {
try (DbSession dbSession = dbClient.openSession(false)) {
List<DefaultIssue> result = new ArrayList<>();
dbSession.getMapper(IssueMapper.class).scrollNonClosedByComponentUuid(componentUuid, resultContext -> {
DefaultIssue issue = (resultContext.getResultObject()).toDefaultIssue();

// TODO this field should be set outside this class
if (!isActive(issue.ruleKey()) || ruleRepository.getByKey(issue.ruleKey()).getStatus() == RuleStatus.REMOVED) {
issue.setOnDisabledRule(true);
// TODO to be improved, why setOnDisabledRule(true) is not enough ?
issue.setBeingClosed(true);
}
// FIXME
issue.setSelectedAt(System.currentTimeMillis());
result.add(issue);
});
return result;
}
}

private boolean isActive(RuleKey ruleKey) {
return activeRulesHolder.get(ruleKey).isPresent();
}
}
@@ -0,0 +1,55 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.computation.task.projectanalysis.issue;

import java.util.Collection;

import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.tracking.BlockHashSequence;
import org.sonar.core.issue.tracking.Input;
import org.sonar.core.issue.tracking.LineHashSequence;

public class DefaultTrackingInput implements Input<DefaultIssue> {
private final Collection<DefaultIssue> issues;
private final LineHashSequence lineHashes;
private final BlockHashSequence blockHashes;

public DefaultTrackingInput(Collection<DefaultIssue> issues, LineHashSequence lineHashes, BlockHashSequence blockHashes) {
this.issues = issues;
this.lineHashes = lineHashes;
this.blockHashes = blockHashes;
}

@Override
public LineHashSequence getLineHashSequence() {
return lineHashes;
}

@Override
public BlockHashSequence getBlockHashSequence() {
return blockHashes;
}

@Override
public Collection<DefaultIssue> getIssues() {
return issues;
}

}
Expand Up @@ -21,6 +21,7 @@


import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER; import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;


import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;


Expand All @@ -31,98 +32,76 @@
import org.sonar.server.computation.task.projectanalysis.component.Component.Status; import org.sonar.server.computation.task.projectanalysis.component.Component.Status;
import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit; import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit;
import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitorAdapter; import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitorAdapter;
import org.sonar.server.computation.task.projectanalysis.filemove.MovedFilesRepository;
import org.sonar.server.util.cache.DiskCache; import org.sonar.server.util.cache.DiskCache;


import com.google.common.base.Optional;

public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter {


private final TrackerExecution tracker;
private final IssueCache issueCache; private final IssueCache issueCache;
private final IssueLifecycle issueLifecycle; private final IssueLifecycle issueLifecycle;
private final IssueVisitors issueVisitors; private final IssueVisitors issueVisitors;
private final ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues; private final ComponentIssuesLoader issuesLoader;
private final MovedFilesRepository movedFilesRepository;
private final BaseIssuesLoader baseIssuesLoader;
private final AnalysisMetadataHolder analysisMetadataHolder; private final AnalysisMetadataHolder analysisMetadataHolder;
private final IssueTrackingDelegator issueTracking;


public IntegrateIssuesVisitor(TrackerExecution tracker, IssueCache issueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors, public IntegrateIssuesVisitor(IssueCache issueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors, ComponentIssuesLoader issuesLoader,
ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues, MovedFilesRepository movedFilesRepository, BaseIssuesLoader baseIssuesLoader, AnalysisMetadataHolder analysisMetadataHolder, IssueTrackingDelegator issueTracking) {
AnalysisMetadataHolder analysisMetadataHolder) {
super(CrawlerDepthLimit.FILE, POST_ORDER); super(CrawlerDepthLimit.FILE, POST_ORDER);
this.tracker = tracker;
this.issueCache = issueCache; this.issueCache = issueCache;
this.issueLifecycle = issueLifecycle; this.issueLifecycle = issueLifecycle;
this.issueVisitors = issueVisitors; this.issueVisitors = issueVisitors;
this.componentsWithUnprocessedIssues = componentsWithUnprocessedIssues; this.issuesLoader = issuesLoader;
this.movedFilesRepository = movedFilesRepository;
this.baseIssuesLoader = baseIssuesLoader;
this.analysisMetadataHolder = analysisMetadataHolder; this.analysisMetadataHolder = analysisMetadataHolder;
this.issueTracking = issueTracking;
} }


@Override @Override
public void visitAny(Component component) { public void visitAny(Component component) {
processIssues(component); try (DiskCache<DefaultIssue>.DiskAppender cacheAppender = issueCache.newAppender()) {

componentsWithUnprocessedIssues.remove(component.getUuid());
Optional<MovedFilesRepository.OriginalFile> originalFile = movedFilesRepository.getOriginalFile(component);
if (originalFile.isPresent()) {
componentsWithUnprocessedIssues.remove(originalFile.get().getUuid());
}
}

private void processIssues(Component component) {
DiskCache<DefaultIssue>.DiskAppender cacheAppender = issueCache.newAppender();
try {
issueVisitors.beforeComponent(component); issueVisitors.beforeComponent(component);

if (isIncremental(component)) { if (isIncremental(component)) {
fillIncrementalOpenIssues(component, cacheAppender); List<DefaultIssue> issues = issuesLoader.loadForComponentUuid(component.getUuid());
fillIncrementalOpenIssues(component, issues, cacheAppender);
} else { } else {
Tracking<DefaultIssue, DefaultIssue> tracking = tracker.track(component); Tracking<DefaultIssue, DefaultIssue> tracking = issueTracking.track(component);
fillNewOpenIssues(component, tracking, cacheAppender); fillNewOpenIssues(component, tracking.getUnmatchedRaws(), cacheAppender);
fillExistingOpenIssues(component, tracking, cacheAppender); fillExistingOpenIssues(component, tracking.getMatchedRaws(), cacheAppender);
closeUnmatchedBaseIssues(component, tracking, cacheAppender); closeUnmatchedBaseIssues(component, tracking.getUnmatchedBases(), cacheAppender);
} }
issueVisitors.afterComponent(component); issueVisitors.afterComponent(component);
} catch (Exception e) { } catch (Exception e) {
throw new IllegalStateException(String.format("Fail to process issues of component '%s'", component.getKey()), e); throw new IllegalStateException(String.format("Fail to process issues of component '%s'", component.getKey()), e);
} finally {
cacheAppender.close();
} }
} }


private boolean isIncremental(Component component) { private boolean isIncremental(Component component) {
return analysisMetadataHolder.isIncrementalAnalysis() && component.getStatus() == Status.SAME; return analysisMetadataHolder.isIncrementalAnalysis() && component.getStatus() == Status.SAME;
} }


private void fillNewOpenIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { private void fillNewOpenIssues(Component component, Iterable<DefaultIssue> issues, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
for (DefaultIssue issue : tracking.getUnmatchedRaws()) { for (DefaultIssue issue : issues) {
issueLifecycle.initNewOpenIssue(issue); issueLifecycle.initNewOpenIssue(issue);
process(component, issue, cacheAppender); process(component, issue, cacheAppender);
} }
} }


private void fillIncrementalOpenIssues(Component component, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { private void fillIncrementalOpenIssues(Component component, Collection<DefaultIssue> issues, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
List<DefaultIssue> issues = baseIssuesLoader.loadForComponentUuid(component.getUuid());

for (DefaultIssue issue : issues) { for (DefaultIssue issue : issues) {
issueLifecycle.updateExistingOpenissue(issue);
process(component, issue, cacheAppender); process(component, issue, cacheAppender);
} }
} }


private void fillExistingOpenIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { private void fillExistingOpenIssues(Component component, Map<DefaultIssue, DefaultIssue> matched, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
for (Map.Entry<DefaultIssue, DefaultIssue> entry : tracking.getMatchedRaws().entrySet()) { for (Map.Entry<DefaultIssue, DefaultIssue> entry : matched.entrySet()) {
DefaultIssue raw = entry.getKey(); DefaultIssue raw = entry.getKey();
DefaultIssue base = entry.getValue(); DefaultIssue base = entry.getValue();
issueLifecycle.mergeExistingOpenIssue(raw, base); issueLifecycle.mergeExistingOpenIssue(raw, base);
process(component, raw, cacheAppender); process(component, raw, cacheAppender);
} }
} }


private void closeUnmatchedBaseIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { private void closeUnmatchedBaseIssues(Component component, Iterable<DefaultIssue> issues, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
for (DefaultIssue issue : tracking.getUnmatchedBases()) { for (DefaultIssue issue : issues) {
// TODO should replace flag "beingClosed" by express call to transition "automaticClose" // TODO should replace flag "beingClosed" by express call to transition "automaticClose"
issue.setBeingClosed(true); issue.setBeingClosed(true);
// TODO manual issues -> was updater.setResolution(newIssue, Issue.RESOLUTION_REMOVED, changeContext);. Is it a problem ? // TODO manual issues -> was updater.setResolution(newIssue, Issue.RESOLUTION_REMOVED, changeContext);. Is it a problem ?
Expand Down

0 comments on commit 8f27cc5

Please sign in to comment.