All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.committed.speedy.format.StagedMojo Maven / Gradle / Ivy

package io.committed.speedy.format;

import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;

import com.diffplug.spotless.Formatter;
import com.diffplug.spotless.maven.SpotlessApplyMojo;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.eclipse.jgit.treewalk.filter.TreeFilter;

@Mojo(name = "staged", threadSafe = true)
public class StagedMojo extends SpotlessApplyMojo {

  private static final List CHANGE_TYPES =
      asList(ChangeType.ADD, ChangeType.COPY, ChangeType.MODIFY, ChangeType.RENAME);

  @Override
  protected void process(Iterable files, Formatter formatter) throws MojoExecutionException {
    if (!files.iterator().hasNext()) {
      return;
    }

    try (Git git = openGitRepo()) {

      Repository repository = git.getRepository();
      Path workTreePath = repository.getWorkTree().toPath();

      TreeFilter treeFilter =
          PathFilterGroup.createFromStrings(
              StreamSupport.stream(files.spliterator(), false)
                  .map(f -> workTreePath.relativize(f.toPath()))
                  .map(f -> f.toString().replace('\\', '/'))
                  .collect(Collectors.toList()));

      List stagedChangedFiles = getChangedFiles(git, true, treeFilter);
      if (stagedChangedFiles.isEmpty()) {
        getLog().debug("No files were formatted for this formatter");
        return;
      }
      List unstagedChangedFiles = getChangedFiles(git, false, treeFilter);

      Set partiallyStagedFiles =
          getPartiallyStagedFiles(stagedChangedFiles, unstagedChangedFiles);

      List fullyStagedFiles =
          stagedChangedFiles.stream()
              .distinct()
              .filter(f -> !unstagedChangedFiles.contains(f))
              .collect(Collectors.toList());

      List stagedFiles =
          stagedChangedFiles.stream()
              .map(
                  filePath ->
                      repository.getDirectory().getParentFile().toPath().resolve(filePath).toFile())
              .collect(toList());
      super.process(stagedFiles, formatter);
      getLog().info("Formatted " + stagedFiles.size() + " staged files");

      for (String f : fullyStagedFiles) {
        stage(git, f);
      }
      if (!partiallyStagedFiles.isEmpty()) {
        throwPartialUnstaged(partiallyStagedFiles);
      }
    } catch (IOException e) {
      throw new MojoExecutionException("Could not open Git repository", e);
    }
  }

  private Git openGitRepo() throws IOException {
    File cwd = Paths.get("").toFile().getAbsoluteFile();
    try {
      return Git.open(cwd);
    } catch (IOException e) {
      FileRepositoryBuilder repositoryBuilder = new FileRepositoryBuilder();
      repositoryBuilder.findGitDir(cwd);
      File gitDir = repositoryBuilder.getGitDir();
      if (gitDir != null) {
        return Git.open(gitDir);
      } else {
        throw new IOException(
            "Could not find git directory scanning upwards from " + cwd.getPath());
      }
    }
  }

  private void throwPartialUnstaged(Set partiallyStagedFiles)
      throws MojoExecutionException {
    throw new MojoExecutionException(
        format(
            "Partially staged files were formatted but not re-staged:%n%s",
            String.join("\\n", partiallyStagedFiles)));
  }

  private void stage(Git git, String f) throws MojoExecutionException {
    try {
      git.add().addFilepattern(f).call();
    } catch (GitAPIException e) {
      throw new MojoExecutionException("Failed to stage", e);
    }
  }

  private Set getPartiallyStagedFiles(
      List stagedChangedFiles, List unstagedChangedFiles) {
    return stagedChangedFiles.stream()
        .distinct()
        .filter(unstagedChangedFiles::contains)
        .collect(Collectors.toSet());
  }

  private List getChangedFiles(Git git, boolean staged, TreeFilter pathFilter)
      throws MojoExecutionException {
    try {
      // do we need to include untracked files also? e.g. ls-files --others --exclude-standard
      return git
          .diff()
          .setPathFilter(pathFilter)
          .setShowNameAndStatusOnly(true)
          .setCached(staged)
          .call()
          .stream()
          .filter(e -> CHANGE_TYPES.contains(e.getChangeType()))
          .map(DiffEntry::getNewPath)
          .collect(toList());
    } catch (GitAPIException e) {
      throw new MojoExecutionException("Failed to list changed files", e);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy