/******************************************************************************* * Copyright (c) 2010, 2015 SAP AG 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: * Mathias Kinzler <mathias.kinzler@sap.com> - 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 org.eclipse.core.resources.IProject; 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.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.egit.core.Activator; import org.eclipse.egit.core.EclipseGitProgressTransformer; 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.RebaseCommand.Operation; import org.eclipse.jgit.api.RebaseResult; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.merge.MergeStrategy; /** * This class implements rebase. */ public class RebaseOperation implements IEGitOperation { private final Repository repository; private final Ref ref; private final Operation operation; private RebaseResult result; private final InteractiveHandler handler; private boolean preserveMerges = false; /** * Construct a {@link RebaseOperation} object for a {@link Ref}. * <p> * Upon {@link #execute(IProgressMonitor)}, the current HEAD will be rebased * onto the provided {@link Ref} * * @param repository * the {@link Repository} * @param ref * the branch or tag */ public RebaseOperation(Repository repository, Ref ref) { this(repository, ref, Operation.BEGIN, null); } /** * Construct a {@link RebaseOperation} object for a {@link Ref}. * <p> * Upon {@link #execute(IProgressMonitor)}, the current HEAD will be rebased * interactively onto the provided {@link Ref} * * @param repository * the {@link Repository} * @param ref * the branch or tag * @param handler */ public RebaseOperation(Repository repository, Ref ref, InteractiveHandler handler) { this(repository, ref, Operation.BEGIN, handler); } /** * Used to abort, skip, or continue a stopped rebase operation that has been * started before. * * @param repository * the {@link Repository} * @param operation * one of {@link Operation#ABORT}, {@link Operation#CONTINUE}, * {@link Operation#SKIP} */ public RebaseOperation(Repository repository, Operation operation) { this(repository, null, operation, null); } /** * Used to abort, skip, or continue a stopped rebase interactive operation * that has been started before. * * @param repository * the {@link Repository} * @param operation * one of {@link Operation#ABORT}, {@link Operation#CONTINUE}, * {@link Operation#SKIP} * @param handler */ public RebaseOperation(Repository repository, Operation operation, InteractiveHandler handler) { this(repository, null, operation, handler); } private RebaseOperation(Repository repository, Ref ref, Operation operation, InteractiveHandler handler) { this.repository = repository; this.ref = ref; this.operation = operation; this.handler = handler; } @Override public void execute(IProgressMonitor m) throws CoreException { if (result != null) throw new CoreException(new Status(IStatus.ERROR, Activator .getPluginId(), CoreText.OperationAlreadyExecuted)); final IProject[] validProjects = ProjectUtil.getValidOpenProjects(repository); IWorkspaceRunnable action = new IWorkspaceRunnable() { @Override public void run(IProgressMonitor actMonitor) throws CoreException { SubMonitor progress = SubMonitor.convert(actMonitor, 2); try (Git git = new Git(repository)) { RebaseCommand cmd = git.rebase().setProgressMonitor( new EclipseGitProgressTransformer( progress.newChild(1))); MergeStrategy strategy = Activator.getDefault() .getPreferredMergeStrategy(); if (strategy != null) { cmd.setStrategy(strategy); } if (handler != null) { cmd.runInteractively(handler, true); } if (operation == Operation.BEGIN) { cmd.setPreserveMerges(preserveMerges); result = cmd.setUpstream(ref.getName()).call(); } else { result = cmd.setOperation(operation).call(); } } catch (JGitInternalException | GitAPIException e) { throw new CoreException(Activator.error(e.getMessage(), e)); } finally { if (refreshNeeded()) { ProjectUtil.refreshValidProjects(validProjects, progress.newChild(1)); } } } }; ResourcesPlugin.getWorkspace().run(action, getSchedulingRule(), IWorkspace.AVOID_UPDATE, m); } private boolean refreshNeeded() { return result == null || result.getStatus() != RebaseResult.Status.UP_TO_DATE; } @Override public ISchedulingRule getSchedulingRule() { return RuleUtil.getRule(repository); } /** * @return the result of calling {@link #execute(IProgressMonitor)}, or * <code>null</code> if this has not been executed yet */ public RebaseResult getResult() { return result; } /** * @return the {@link Repository} */ public final Repository getRepository() { return repository; } /** * @return the {@link Operation} if it has been set, otherwise null */ public final Operation getOperation() { return operation; } /** * @param preserveMerges * true to preserve merges during the rebase */ public void setPreserveMerges(boolean preserveMerges) { this.preserveMerges = preserveMerges; } }