*/
package org.sonar.plugins.scm.git;
+import org.eclipse.jgit.api.errors.GitAPIException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.utils.command.StringStreamConsumer;
import java.io.File;
+import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
public class GitBlameCommand extends BlameCommand {
public void blame(BlameInput input, BlameOutput output) {
FileSystem fs = input.fileSystem();
LOG.debug("Working directory: " + fs.baseDir().getAbsolutePath());
+ ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);
+ List<Future<Void>> tasks = new ArrayList<Future<Void>>();
for (InputFile inputFile : input.filesToBlame()) {
- String filename = inputFile.relativePath();
- Command cl = createCommandLine(fs.baseDir(), filename);
- GitBlameConsumer consumer = new GitBlameConsumer(filename);
- StringStreamConsumer stderr = new StringStreamConsumer();
-
- int exitCode = execute(cl, consumer, stderr);
- if (exitCode != 0) {
- throw new IllegalStateException("The git blame command [" + cl.toString() + "] failed: " + stderr.getOutput());
+ tasks.add(submitTask(fs.baseDir(), output, inputFile, executorService));
+ }
+ for (Future<Void> task : tasks) {
+ try {
+ task.get();
+ } catch (ExecutionException e) {
+ // Unwrap ExecutionException
+ throw e.getCause() instanceof RuntimeException ? (RuntimeException) e.getCause() : new IllegalStateException(e.getCause());
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
}
- List<BlameLine> lines = consumer.getLines();
- if (lines.size() == inputFile.lines() - 1) {
- // SONARPLUGINS-3097 Git do not report blame on last empty line
- lines.add(lines.get(lines.size() - 1));
+ }
+ }
+
+ private Future<Void> submitTask(final File baseDir, final BlameOutput output, final InputFile inputFile, ExecutorService executorService) {
+ return executorService.submit(new Callable<Void>() {
+ @Override
+ public Void call() throws GitAPIException {
+ blame(baseDir, output, inputFile);
+ return null;
}
- output.blameResult(inputFile, lines);
+
+ });
+ }
+
+ private void blame(File baseDir, BlameOutput output, InputFile inputFile) {
+ String filename = inputFile.relativePath();
+ Command cl = createCommandLine(baseDir, filename);
+ GitBlameConsumer consumer = new GitBlameConsumer(filename);
+ StringStreamConsumer stderr = new StringStreamConsumer();
+
+ int exitCode = execute(cl, consumer, stderr);
+ if (exitCode != 0) {
+ throw new IllegalStateException("The git blame command [" + cl.toString() + "] failed: " + stderr.getOutput());
}
+ List<BlameLine> lines = consumer.getLines();
+ if (lines.size() == inputFile.lines() - 1) {
+ // SONARPLUGINS-3097 Git do not report blame on last empty line
+ lines.add(lines.get(lines.size() - 1));
+ }
+ output.blameResult(inputFile, lines);
+
}
public int execute(Command cl, StreamConsumer consumer, StreamConsumer stderr) {
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
public class JGitBlameCommand extends BlameCommand {
.build();
git = Git.wrap(repo);
File gitBaseDir = repo.getWorkTree();
+ ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);
+ List<Future<Void>> tasks = new ArrayList<Future<Void>>();
for (InputFile inputFile : input.filesToBlame()) {
- blame(output, git, gitBaseDir, inputFile);
+ tasks.add(submitTask(output, git, gitBaseDir, inputFile, executorService));
+ }
+ for (Future<Void> task : tasks) {
+ try {
+ task.get();
+ } catch (ExecutionException e) {
+ // Unwrap ExecutionException
+ throw e.getCause() instanceof RuntimeException ? (RuntimeException) e.getCause() : new IllegalStateException(e.getCause());
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
}
} catch (IOException e) {
throw new IllegalStateException("Unable to open Git repository", e);
- } catch (GitAPIException e) {
- throw new IllegalStateException("Unable to blame", e);
} finally {
if (git != null && git.getRepository() != null) {
git.getRepository().close();
}
}
+ private Future<Void> submitTask(final BlameOutput output, final Git git, final File gitBaseDir, final InputFile inputFile, ExecutorService executorService) {
+ return executorService.submit(new Callable<Void>() {
+ @Override
+ public Void call() throws GitAPIException {
+ blame(output, git, gitBaseDir, inputFile);
+ return null;
+ }
+ });
+ }
+
private void blame(BlameOutput output, Git git, File gitBaseDir, InputFile inputFile) throws GitAPIException {
String filename = pathResolver.relativePath(gitBaseDir, inputFile.file());
org.eclipse.jgit.blame.BlameResult blameResult = git.blame()