/*******************************************************************************
* Copyright (c) 2014, 2015 Maik Schreiber and others
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Maik Schreiber - initial implementation
* Laurent Delaigue (Obeo) - use of preferred merge strategy
* Stephan Hackstedt <stephan.hackstedt@googlemail.com - Bug 477695
*******************************************************************************/
package org.eclipse.egit.core.op;
import java.text.MessageFormat;
import java.util.List;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.egit.core.Activator;
import org.eclipse.egit.core.CommitUtil;
import org.eclipse.egit.core.internal.CoreText;
import org.eclipse.egit.core.internal.job.RuleUtil;
import org.eclipse.egit.core.internal.util.ProjectUtil;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.RebaseCommand;
import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.errors.IllegalTodoFileModification;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.RebaseTodoLine;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.team.core.TeamException;
/** Squashes multiple commits into one. */
public class SquashCommitsOperation implements IEGitOperation {
private Repository repository;
private List<RevCommit> commits;
private InteractiveHandler messageHandler;
/**
* Constructs a new squash commits operation.
*
* @param repository
* the repository to work on
* @param commits
* the commits
* @param messageHandler
* handler that will be used to prompt for a commit message
*/
public SquashCommitsOperation(Repository repository,
List<RevCommit> commits, InteractiveHandler messageHandler) {
this.repository = repository;
this.commits = CommitUtil.sortCommits(commits);
this.messageHandler = messageHandler;
}
@Override
public void execute(IProgressMonitor m) throws CoreException {
IWorkspaceRunnable action = new IWorkspaceRunnable() {
@Override
public void run(IProgressMonitor pm) throws CoreException {
SubMonitor progress = SubMonitor.convert(pm, 2);
progress.subTask(MessageFormat.format(
CoreText.SquashCommitsOperation_squashing,
Integer.valueOf(commits.size())));
InteractiveHandler handler = new InteractiveHandler() {
@Override
public void prepareSteps(List<RebaseTodoLine> steps) {
RevCommit firstCommit = commits.get(0);
for (RebaseTodoLine step : steps) {
if (isRelevant(step.getCommit())) {
try {
if (step.getCommit().prefixCompare(
firstCommit) == 0)
step.setAction(RebaseTodoLine.Action.PICK);
else
step.setAction(RebaseTodoLine.Action.SQUASH);
} catch (IllegalTodoFileModification e) {
// shouldn't happen
}
}
}
}
private boolean isRelevant(AbbreviatedObjectId id) {
for (RevCommit commit : commits) {
if (id.prefixCompare(commit) == 0)
return true;
}
return false;
}
@Override
public String modifyCommitMessage(String oldMessage) {
return messageHandler.modifyCommitMessage(oldMessage);
}
};
try (Git git = new Git(repository)) {
RebaseCommand command = git.rebase()
.setUpstream(commits.get(0).getParent(0))
.runInteractively(handler)
.setOperation(RebaseCommand.Operation.BEGIN);
MergeStrategy strategy = Activator.getDefault()
.getPreferredMergeStrategy();
if (strategy != null) {
command.setStrategy(strategy);
}
command.call();
} catch (GitAPIException e) {
throw new TeamException(e.getLocalizedMessage(),
e.getCause());
}
progress.worked(1);
ProjectUtil.refreshValidProjects(
ProjectUtil.getValidOpenProjects(repository),
progress.newChild(1));
}
};
ResourcesPlugin.getWorkspace().run(action, getSchedulingRule(),
IWorkspace.AVOID_UPDATE, m);
}
@Override
public ISchedulingRule getSchedulingRule() {
return RuleUtil.getRule(repository);
}
}