Skip to content

Commit

Permalink
When ratcheting, check dirty files from Git first
Browse files Browse the repository at this point in the history
  • Loading branch information
Erik committed Sep 24, 2020
1 parent d9a7263 commit e96e354
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ private static boolean worktreeIsCleanCheckout(TreeWalk treeWalk) {
* builds and submodules, it's quite possible that a single Gradle project will span across multiple git repositories.
* We cache the Repository for every Project in `gitRoots`, and use dynamic programming to populate it.
*/
private Repository repositoryFor(Project project) throws IOException {
protected Repository repositoryFor(Project project) throws IOException {
Repository repo = gitRoots.get(project);
if (repo == null) {
if (isGitRoot(getDir(project))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
*/
package com.diffplug.spotless.maven;

import static java.util.stream.Collectors.toList;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
Expand All @@ -30,6 +30,7 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
Expand All @@ -38,11 +39,12 @@
import org.codehaus.plexus.resource.ResourceManager;
import org.codehaus.plexus.resource.loader.FileResourceLoader;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.MatchPatterns;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.jgit.api.errors.GitAPIException;

import com.diffplug.common.collect.Iterables;
import com.diffplug.spotless.Formatter;
import com.diffplug.spotless.LineEnding;
import com.diffplug.spotless.Provisioner;
Expand All @@ -58,6 +60,8 @@
import com.diffplug.spotless.maven.sql.Sql;
import com.diffplug.spotless.maven.typescript.Typescript;

import static java.util.stream.Collectors.toList;

public abstract class AbstractSpotlessMojo extends AbstractMojo {

private static final String DEFAULT_ENCODING = "UTF-8";
Expand Down Expand Up @@ -137,38 +141,23 @@ public final void execute() throws MojoExecutionException {
}

private void execute(FormatterFactory formatterFactory) throws MojoExecutionException {
List<File> files = collectFiles(formatterFactory);
FormatterConfig config = getFormatterConfig();
Optional<String> ratchetFrom = formatterFactory.ratchetFrom(config);
Iterable<File> toFormat;
if (!ratchetFrom.isPresent()) {
toFormat = files;
} else {
toFormat = Iterables.filter(files, GitRatchetMaven.instance().isGitDirty(baseDir, ratchetFrom.get()));
}
List<File> files = collectFiles(formatterFactory, config);

try (Formatter formatter = formatterFactory.newFormatter(files, config)) {
process(toFormat, formatter);
process(files, formatter);
}
}

private List<File> collectFiles(FormatterFactory formatterFactory) throws MojoExecutionException {
Set<String> configuredIncludes = formatterFactory.includes();
Set<String> configuredExcludes = formatterFactory.excludes();

Set<String> includes = configuredIncludes.isEmpty() ? formatterFactory.defaultIncludes() : configuredIncludes;
if (includes.isEmpty()) {
throw new MojoExecutionException("You must specify some files to include, such as '<includes><include>src/**</include></includes>'");
}

Set<String> excludes = new HashSet<>(FileUtils.getDefaultExcludesAsList());
excludes.add(withTrailingSeparator(buildDir.toString()));
excludes.addAll(configuredExcludes);

String includesString = String.join(",", includes);
String excludesString = String.join(",", excludes);

private List<File> collectFiles(FormatterFactory formatterFactory, FormatterConfig config) throws MojoExecutionException {
Optional<String> ratchetFrom = formatterFactory.ratchetFrom(config);
try {
final List<File> files = FileUtils.getFiles(baseDir, includesString, excludesString);
final List<File> files;
if (ratchetFrom.isPresent()) {
files = collectFilesFromGit(formatterFactory, ratchetFrom.get());
} else {
files = collectFilesFromFormatterFactory(formatterFactory);
}
if (filePatterns == null || filePatterns.isEmpty()) {
return files;
}
Expand All @@ -189,10 +178,70 @@ private List<File> collectFiles(FormatterFactory formatterFactory) throws MojoEx
}
}

private List<File> collectFilesFromGit(FormatterFactory formatterFactory, String ratchetFrom) throws MojoExecutionException {
MatchPatterns includePatterns = MatchPatterns.from(
withNormalizedFileSeparators(getIncludes(formatterFactory)));
MatchPatterns excludePatterns = MatchPatterns.from(
withNormalizedFileSeparators(getExcludes(formatterFactory)));

Iterable<String> dirtyFiles;
try {
dirtyFiles = GitRatchetMaven
.instance().getDirtyFiles(baseDir, ratchetFrom);
} catch (IOException e) {
throw new MojoExecutionException("Unable to scan file tree rooted at " + baseDir, e);
} catch (GitAPIException e) {
throw new MojoExecutionException("Error getting diff against 'ratchetFrom' setting '" + ratchetFrom + "'", e);
}

List<File> result = new ArrayList<>();
for (String file: withNormalizedFileSeparators(dirtyFiles)) {
if (includePatterns.matches(file, true)) {
if (!excludePatterns.matches(file, true)) {
result.add(Paths.get(baseDir.getPath(), file).toFile());
}
}
}
return result;
}

private List<File> collectFilesFromFormatterFactory(FormatterFactory formatterFactory)
throws MojoExecutionException, IOException {
String includesString = String.join(",", getIncludes(formatterFactory));
String excludesString = String.join(",", getExcludes(formatterFactory));

return FileUtils.getFiles(baseDir, includesString, excludesString);
}

private Iterable<String> withNormalizedFileSeparators(Iterable<String> patterns) {
return StreamSupport.stream(patterns.spliterator(), true)
.map(pattern -> pattern.replace('/', File.separatorChar))
.map(pattern -> pattern.replace('\\', File.separatorChar))
.collect(Collectors.toSet());
}

private static String withTrailingSeparator(String path) {
return path.endsWith(File.separator) ? path : path + File.separator;
}

private Set<String> getIncludes(FormatterFactory formatterFactory) throws MojoExecutionException {
Set<String> configuredIncludes = formatterFactory.includes();
Set<String> includes = configuredIncludes.isEmpty() ? formatterFactory.defaultIncludes() : configuredIncludes;
if (includes.isEmpty()) {
throw new MojoExecutionException("You must specify some files to include, such as '<includes><include>src/**</include></includes>'");
}
return includes;
}

private Set<String> getExcludes(FormatterFactory formatterFactory) {
Set<String> configuredExcludes = formatterFactory.excludes();

Set<String> excludes = new HashSet<>(FileUtils.getDefaultExcludesAsList());
excludes.add(withTrailingSeparator(buildDir.toString()));
excludes.addAll(configuredExcludes);
return excludes;
}

private FormatterConfig getFormatterConfig() {
ArtifactResolver resolver = new ArtifactResolver(repositorySystem, repositorySystemSession, repositories, getLog());
Provisioner provisioner = MavenProvisioner.create(resolver);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,19 @@

import java.io.File;
import java.io.IOException;
import java.util.function.Predicate;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;

import com.diffplug.common.base.Errors;
import com.diffplug.spotless.extra.GitRatchet;

final class GitRatchetMaven extends GitRatchet<File> {
Expand Down Expand Up @@ -50,15 +58,28 @@ static GitRatchetMaven instance() {
return instance;
}

/** A predicate which returns only the "git dirty" files. */
Predicate<File> isGitDirty(File baseDir, String ratchetFrom) {
Iterable<String> getDirtyFiles(File baseDir, String ratchetFrom)
throws IOException, GitAPIException {
Repository repository = repositoryFor(baseDir);
ObjectId sha = rootTreeShaOf(baseDir, ratchetFrom);
return file -> {
try {
return !isClean(baseDir, sha, file);
} catch (IOException e) {
throw Errors.asRuntime(e);
}
};

ObjectReader oldReader = repository.newObjectReader();
CanonicalTreeParser oldTree = new CanonicalTreeParser();
oldTree.reset(oldReader, sha);

Git git = new Git(repository);
List<DiffEntry> diffs = git.diff()
.setShowNameAndStatusOnly(true)
.setOldTree(oldTree)
.call();

String workTreePath = repository.getWorkTree().getPath();
Path baseDirPath = Paths.get(baseDir.getPath());

return diffs.stream()
.map(DiffEntry::getNewPath)
.map(path -> Paths.get(workTreePath, path))
.map(path -> baseDirPath.relativize(path).toString())
.collect(Collectors.toList());
}
}

0 comments on commit e96e354

Please sign in to comment.