/* * 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 java.util.ArrayList; import java.util.Collection; import javax.annotation.Nullable; import javax.validation.constraints.NotNull; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.LogCommand; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.revwalk.RevCommit; import org.obiba.git.CommitInfo; import org.obiba.git.GitException; import org.obiba.git.GitUtils; import com.google.common.base.Strings; public class LogsCommand extends AbstractGitCommand<Iterable<CommitInfo>> { private String path; private boolean excludeDeletedCommits = false; private LogsCommand(@NotNull File repositoryPath, @Nullable File workPath) { super(repositoryPath, workPath); } @Override public Iterable<CommitInfo> execute(Git git) { try { LogCommand logCommand = git.log(); if(!Strings.isNullOrEmpty(path)) logCommand.addPath(path); Collection<CommitInfo> commits = new ArrayList<>(); // for performance, get the id before looping thru all commits preventing resolving the id each time String headCommitId = GitUtils.getHeadCommitId(git.getRepository()); // TODO find an efficient way of finding the current commit of a given path // One possible solution is implementing: 'git log --ancestry-path <COMMIT_HAEH>^..HEAD' // For now, the list is in order of 'current .. oldest' boolean isCurrent = true; for(RevCommit commit : logCommand.call()) { if(excludeDeletedCommits && hasDeletedCommit(git, commit)) continue; String commitId = commit.getName(); boolean isHeadCommit = headCommitId.equals(commitId); PersonIdent personIdent = commit.getAuthorIdent(); commits.add(new CommitInfo.Builder().authorName(personIdent.getName()) // .authorEmail(personIdent.getEmailAddress()) // .date(personIdent.getWhen()) // .comment(commit.getFullMessage()) // .commitId(commit.getName()) // .current(isCurrent) // .head(isHeadCommit).build()); isCurrent = false; } return commits; } catch(IOException | GitAPIException e) { throw new GitException(e); } } /** * A file commit has only one diff entry. Having many diff entries imply that the commit path corresponds to the * whole repository or a folder in the commit tree. In this case, we do not exclude the commit if there are modified * or added changes as well. * * @param git * @param commit * @return */ private boolean hasDeletedCommit(Git git, RevCommit commit) { DiffCommand diffCommand = new DiffCommand.Builder(getRepositoryPath(), commit.getName()).path(path).build(); for(DiffEntry diff : diffCommand.execute(git)) { if(DiffEntry.ChangeType.DELETE != diff.getChangeType()) { return false; } } return true; } @SuppressWarnings("ParameterHidesMemberVariable") public static class Builder { private final LogsCommand command; public Builder(@NotNull File repositoryPath) { this(repositoryPath, null); } public Builder(@NotNull File repositoryPath, @Nullable File workPath) { command = new LogsCommand(repositoryPath, workPath); } public Builder path(String path) { command.path = path; return this; } public Builder excludeDeletedCommits(boolean excludeDeletedCommits) { command.excludeDeletedCommits = excludeDeletedCommits; return this; } public LogsCommand build() { return command; } } }