/* * Copyright (c) 2016 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.git.command; import java.io.File; import java.io.IOException; import javax.annotation.Nullable; import javax.validation.constraints.NotNull; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffFormatter; import org.eclipse.jgit.diff.RawTextComparator; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.AbstractTreeIterator; import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.eclipse.jgit.treewalk.EmptyTreeIterator; import org.eclipse.jgit.treewalk.filter.PathFilter; import org.obiba.git.GitException; import com.google.common.base.Strings; /** * Opal GIT command used to extract the diff between two commits. By default, the diff is between the given commit and * its parent. By providing a valid 'nthCommit' value, the command will extract the appropriate diff from the repo. */ public class DiffCommand extends AbstractGitCommand<Iterable<DiffEntry>> { private String path; private final String commitId; private String previousCommitId; private int nthCommit = 1; protected DiffCommand(@NotNull File repositoryPath, @Nullable File workPath, String commitId) { super(repositoryPath, workPath); this.commitId = commitId; } @Override public Iterable<DiffEntry> execute(Git git) { Repository repository = git.getRepository(); ObjectReader reader = repository.newObjectReader(); try { DiffCurrentPreviousTreeParsersFactory parsersFactory = new DiffCurrentPreviousTreeParsersFactory(repository, reader); return compareDiffTrees(repository, parsersFactory); } catch(IOException e) { throw new GitException(e); } finally { reader.release(); } } private Iterable<DiffEntry> compareDiffTrees(Repository repository, DiffCurrentPreviousTreeParsersFactory parsersFactory) throws IOException { DiffFormatter diffFormatter = new DiffFormatter(null); diffFormatter.setRepository(repository); diffFormatter.setDiffComparator(RawTextComparator.DEFAULT); diffFormatter.setDetectRenames(true); if(!Strings.isNullOrEmpty(path)) { diffFormatter.setPathFilter(PathFilter.create(path)); } return diffFormatter.scan(parsersFactory.getPreviousCommitParser(), parsersFactory.getCurrentCommitParser()); } private class DiffCurrentPreviousTreeParsersFactory { private final Repository repository; private final ObjectReader reader; private CanonicalTreeParser currentCommitParser; private AbstractTreeIterator previousCommitParser; private DiffCurrentPreviousTreeParsersFactory(Repository repository, ObjectReader reader) throws IOException { this.repository = repository; this.reader = reader; init(); } private void init() throws IOException { RevCommit currentCommit = getCommitById(commitId); if(currentCommit == null) { throw new GitException(String.format("There are no commit with id '%s'", commitId)); } currentCommitParser = new CanonicalTreeParser(); currentCommitParser.reset(reader, currentCommit.getTree()); RevCommit previousCommit = Strings.isNullOrEmpty(previousCommitId) // ? getCommitById(commitId + "~" + nthCommit) // : getCommitById(previousCommitId); if(previousCommit == null) { // currentCommit is the first commit in the tree previousCommitParser = new EmptyTreeIterator(); } else { CanonicalTreeParser parser = new CanonicalTreeParser(); parser.reset(reader, previousCommit.getTree()); previousCommitParser = parser; } } public CanonicalTreeParser getCurrentCommitParser() { return currentCommitParser; } public AbstractTreeIterator getPreviousCommitParser() { return previousCommitParser; } @Nullable private RevCommit getCommitById(String id) throws IOException { ObjectId objectId = repository.resolve(id); return objectId == null ? null : new RevWalk(repository).parseCommit(objectId); } } @SuppressWarnings("ParameterHidesMemberVariable") public static class Builder { private final DiffCommand command; public Builder(@NotNull File repositoryPath, String commitId) { this(repositoryPath, null, commitId); } public Builder(@NotNull File repositoryPath, @Nullable File workPath, String commitId) { command = new DiffCommand(repositoryPath, workPath, commitId); } public Builder path(String path) { command.path = path; return this; } public Builder previousCommitId(String previousCommitId) { command.previousCommitId = previousCommitId; return this; } public Builder nthCommit(int nthCommit) { command.nthCommit = nthCommit; return this; } public DiffCommand build() { return command; } } }