Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.google.gerrit.server.patch.DiffOperationsImpl Maven / Gradle / Ivy
package com.google.gerrit.server.patch;
import static com.google.gerrit.entities.Patch.COMMIT_MSG;
import static com.google.gerrit.entities.Patch.MERGE_LIST;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Patch;
import com.google.gerrit.entities.Patch.ChangeType;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.patch.diff.ModifiedFilesCache;
import com.google.gerrit.server.patch.diff.ModifiedFilesCacheImpl;
import com.google.gerrit.server.patch.diff.ModifiedFilesCacheKey;
import com.google.gerrit.server.patch.filediff.FileDiffCache;
import com.google.gerrit.server.patch.filediff.FileDiffCacheImpl;
import com.google.gerrit.server.patch.filediff.FileDiffCacheKey;
import com.google.gerrit.server.patch.filediff.FileDiffOutput;
import com.google.gerrit.server.patch.gitdiff.GitModifiedFilesCacheImpl;
import com.google.gerrit.server.patch.gitdiff.ModifiedFile;
import com.google.gerrit.server.patch.gitfilediff.GitFileDiffCacheImpl;
import com.google.gerrit.server.patch.gitfilediff.GitFileDiffCacheImpl.DiffAlgorithm;
import com.google.inject.Inject;
import com.google.inject.Module;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
public class DiffOperationsImpl implements DiffOperations {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final int RENAME_SCORE = 60 ;
private static final DiffAlgorithm DEFAULT_DIFF_ALGORITHM = DiffAlgorithm.HISTOGRAM;
private static final Whitespace DEFAULT_WHITESPACE = Whitespace.IGNORE_NONE;
private final ModifiedFilesCache modifiedFilesCache;
private final FileDiffCache fileDiffCache;
private final BaseCommitUtil baseCommitUtil;
private final long timeoutMillis;
private final ExecutorService diffExecutor;
public static Module module () {
return new CacheModule() {
@Override
protected void configure () {
bind(DiffOperations.class).to(DiffOperationsImpl.class);
install(GitModifiedFilesCacheImpl.module ());
install(ModifiedFilesCacheImpl.module ());
install(GitFileDiffCacheImpl.module ());
install(FileDiffCacheImpl.module ());
}
};
}
@Inject
public DiffOperationsImpl (
ModifiedFilesCache modifiedFilesCache,
FileDiffCache fileDiffCache,
BaseCommitUtil baseCommit,
@DiffExecutor ExecutorService executor,
@GerritServerConfig Config cfg) {
this .modifiedFilesCache = modifiedFilesCache;
this .fileDiffCache = fileDiffCache;
this .baseCommitUtil = baseCommit;
this .diffExecutor = executor;
this .timeoutMillis =
ConfigUtil.getTimeUnit(
cfg,
"cache" ,
"diff" ,
"timeout" ,
TimeUnit.MILLISECONDS.convert(5 , TimeUnit.SECONDS),
TimeUnit.MILLISECONDS);
}
@Override
public Map listModifiedFilesAgainstParent (
Project.NameKey project, ObjectId newCommit, @Nullable Integer parent)
throws DiffNotAvailableException {
try {
DiffParameters diffParams = computeDiffParameters(project, newCommit, parent);
return listModifiedFilesWithTimeout(diffParams);
} catch (IOException e) {
throw new DiffNotAvailableException(
"Failed to evaluate the parent/base commit for commit " + newCommit, e);
}
}
@Override
public Map listModifiedFiles (
Project.NameKey project, ObjectId oldCommit, ObjectId newCommit)
throws DiffNotAvailableException {
DiffParameters params =
DiffParameters.builder()
.project(project)
.newCommit(newCommit)
.baseCommit(oldCommit)
.comparisonType(ComparisonType.againstOtherPatchSet())
.build();
return listModifiedFilesWithTimeout(params);
}
@Override
public FileDiffOutput getModifiedFileAgainstParent (
Project.NameKey project,
ObjectId newCommit,
@Nullable Integer parent,
String fileName,
@Nullable DiffPreferencesInfo.Whitespace whitespace)
throws DiffNotAvailableException {
try {
DiffParameters diffParams = computeDiffParameters(project, newCommit, parent);
FileDiffCacheKey key =
createFileDiffCacheKey(project, diffParams.baseCommit(), newCommit, fileName, whitespace);
return getModifiedFileWithTimeout(key, diffParams);
} catch (IOException e) {
throw new DiffNotAvailableException(
"Failed to evaluate the parent/base commit for commit " + newCommit, e);
}
}
@Override
public FileDiffOutput getModifiedFile (
Project.NameKey project,
ObjectId oldCommit,
ObjectId newCommit,
String fileName,
@Nullable DiffPreferencesInfo.Whitespace whitespace)
throws DiffNotAvailableException {
DiffParameters params =
DiffParameters.builder()
.project(project)
.baseCommit(oldCommit)
.newCommit(newCommit)
.comparisonType(ComparisonType.againstOtherPatchSet())
.build();
FileDiffCacheKey key =
createFileDiffCacheKey(project, oldCommit, newCommit, fileName, whitespace);
return getModifiedFileWithTimeout(key, params);
}
private Map listModifiedFilesWithTimeout (DiffParameters params)
throws DiffNotAvailableException {
Future task =
diffExecutor.submit(
() -> {
ImmutableMap modifiedFiles = getModifiedFiles(params);
return DiffResult.create(null , modifiedFiles);
});
DiffResult diffResult = execDiffWithTimeout(task, params);
return diffResult.modifiedFiles();
}
private FileDiffOutput getModifiedFileWithTimeout (FileDiffCacheKey key, DiffParameters params)
throws DiffNotAvailableException {
Future task =
diffExecutor.submit(
() -> {
Map diffList = getModifiedFilesForKeys(ImmutableList.of(key));
FileDiffOutput fileDiffOutput =
diffList.containsKey(key.newFilePath())
? diffList.get(key.newFilePath())
: FileDiffOutput.empty(key.newFilePath(), key.oldCommit(), key.newCommit());
return DiffResult.create(fileDiffOutput, null );
});
DiffResult result = execDiffWithTimeout(task, params);
return result.fileDiff();
}
private DiffResult execDiffWithTimeout (Future task, DiffParameters params)
throws DiffNotAvailableException {
try {
return task.get(timeoutMillis, TimeUnit.MILLISECONDS);
} catch (InterruptedException | TimeoutException e) {
throw new DiffNotAvailableException(
String.format(
"Timeout reached while computing diff for project %s, old commit %s, new commit %s" ,
params.project(), params.baseCommit().name(), params.newCommit().name()),
e);
} catch (ExecutionException e) {
throw new DiffNotAvailableException(e);
}
}
private ImmutableMap getModifiedFiles (DiffParameters diffParams)
throws DiffNotAvailableException {
try {
Project.NameKey project = diffParams.project();
ObjectId newCommit = diffParams.newCommit();
ObjectId oldCommit = diffParams.baseCommit();
ComparisonType cmp = diffParams.comparisonType();
ImmutableList modifiedFiles =
modifiedFilesCache.get(createModifiedFilesKey(project, oldCommit, newCommit));
List fileCacheKeys = new ArrayList<>();
fileCacheKeys.add(
createFileDiffCacheKey(
project, oldCommit, newCommit, COMMIT_MSG, null ));
if (cmp.isAgainstAutoMerge() || isMergeAgainstParent(cmp, project, newCommit)) {
fileCacheKeys.add(
createFileDiffCacheKey(
project, oldCommit, newCommit, MERGE_LIST, null ));
}
if (diffParams.skipFiles() == null ) {
modifiedFiles.stream()
.map(
entity ->
createFileDiffCacheKey(
project,
oldCommit,
newCommit,
entity.newPath().isPresent()
? entity.newPath().get()
: entity.oldPath().get(),
null ))
.forEach(fileCacheKeys::add);
}
return getModifiedFilesForKeys(fileCacheKeys);
} catch (IOException e) {
throw new DiffNotAvailableException(e);
}
}
private ImmutableMap getModifiedFilesForKeys (List keys)
throws DiffNotAvailableException {
ImmutableMap.Builder files = ImmutableMap.builder();
ImmutableMap fileDiffs = fileDiffCache.getAll(keys);
for (FileDiffOutput fileDiffOutput : fileDiffs.values()) {
if (fileDiffOutput.isEmpty() || allDueToRebase(fileDiffOutput)) {
continue ;
}
if (fileDiffOutput.changeType() == ChangeType.DELETED) {
files.put(fileDiffOutput.oldPath().get(), fileDiffOutput);
} else {
files.put(fileDiffOutput.newPath().get(), fileDiffOutput);
}
}
return files.build();
}
private static boolean allDueToRebase (FileDiffOutput fileDiffOutput) {
return fileDiffOutput.allEditsDueToRebase()
&& (!(fileDiffOutput.changeType() == ChangeType.RENAMED
|| fileDiffOutput.changeType() == ChangeType.COPIED));
}
private boolean isMergeAgainstParent (ComparisonType cmp, Project.NameKey project, ObjectId commit)
throws IOException {
return (cmp.isAgainstParent() && baseCommitUtil.getNumParents(project, commit) > 1 );
}
private static ModifiedFilesCacheKey createModifiedFilesKey (
Project.NameKey project, ObjectId aCommit, ObjectId bCommit) {
return ModifiedFilesCacheKey.builder()
.project(project)
.aCommit(aCommit)
.bCommit(bCommit)
.renameScore(RENAME_SCORE)
.build();
}
private static FileDiffCacheKey createFileDiffCacheKey (
Project.NameKey project,
ObjectId aCommit,
ObjectId bCommit,
String newPath,
@Nullable Whitespace whitespace) {
whitespace = whitespace == null ? DEFAULT_WHITESPACE : whitespace;
return FileDiffCacheKey.builder()
.project(project)
.oldCommit(aCommit)
.newCommit(bCommit)
.newFilePath(newPath)
.renameScore(RENAME_SCORE)
.diffAlgorithm(DEFAULT_DIFF_ALGORITHM)
.whitespace(whitespace)
.build();
}
@AutoValue
abstract static class DiffResult {
static DiffResult create (
@Nullable FileDiffOutput fileDiff,
@Nullable ImmutableMap modifiedFiles) {
return new AutoValue_DiffOperationsImpl_DiffResult(fileDiff, modifiedFiles);
}
@Nullable
abstract FileDiffOutput fileDiff () ;
@Nullable
abstract ImmutableMap modifiedFiles () ;
}
@AutoValue
abstract static class DiffParameters {
abstract Project.NameKey project () ;
abstract ObjectId newCommit () ;
abstract ObjectId baseCommit () ;
abstract ComparisonType comparisonType () ;
@Nullable
abstract Integer parent () ;
@Nullable
abstract Boolean skipFiles () ;
static Builder builder () {
return new AutoValue_DiffOperationsImpl_DiffParameters.Builder();
}
@AutoValue .Builder
abstract static class Builder {
abstract Builder project (Project.NameKey project) ;
abstract Builder newCommit (ObjectId newCommit) ;
abstract Builder baseCommit (ObjectId baseCommit) ;
abstract Builder parent (@Nullable Integer parent) ;
abstract Builder skipFiles (@Nullable Boolean skipFiles) ;
abstract Builder comparisonType (ComparisonType comparisonType) ;
public abstract DiffParameters build () ;
}
}
private DiffParameters computeDiffParameters (
Project.NameKey project, ObjectId newCommit, Integer parent) throws IOException {
DiffParameters.Builder result =
DiffParameters.builder().project(project).newCommit(newCommit).parent(parent);
if (parent != null ) {
result.baseCommit(baseCommitUtil.getBaseCommit(project, newCommit, parent));
result.comparisonType(ComparisonType.againstParent(parent));
return result.build();
}
int numParents = baseCommitUtil.getNumParents(project, newCommit);
if (numParents == 1 ) {
result.baseCommit(baseCommitUtil.getBaseCommit(project, newCommit, parent));
result.comparisonType(ComparisonType.againstParent(1 ));
return result.build();
}
if (numParents > 2 ) {
logger.atFine().log(
"Diff against auto-merge for merge commits "
+ "with more than two parents is not supported. Commit "
+ newCommit
+ " has "
+ numParents
+ " parents. Falling back to the diff against the first parent." );
result.baseCommit(baseCommitUtil.getBaseCommit(project, newCommit, 1 ).getId());
result.comparisonType(ComparisonType.againstParent(1 ));
result.skipFiles(true );
} else {
result.baseCommit(baseCommitUtil.getBaseCommit(project, newCommit, null ));
result.comparisonType(ComparisonType.againstAutoMerge());
}
return result.build();
}
}