com.genuitec.eclipse.gerrit.tools.internal.fbranches.commands.MergeStableIntoCurrentBranchCommand.java Source code

Java tutorial

Introduction

Here is the source code for com.genuitec.eclipse.gerrit.tools.internal.fbranches.commands.MergeStableIntoCurrentBranchCommand.java

Source

/**
 *  Copyright (c) 2015 Genuitec LLC.
 *  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:
 *  Piotr Tomiak <piotr@genuitec.com> - initial API and implementation
 */
package com.genuitec.eclipse.gerrit.tools.internal.fbranches.commands;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.List;

import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.egit.core.internal.CoreText;
import org.eclipse.egit.core.op.MergeOperation;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.credentials.EGitCredentialsProvider;
import org.eclipse.egit.ui.internal.fetch.FetchOperationUI;
import org.eclipse.egit.ui.internal.merge.MergeResultDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.team.core.TeamException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.HandlerUtil;

import com.genuitec.eclipse.gerrit.tools.utils.RepositoryUtils;

@SuppressWarnings("restriction")
public class MergeStableIntoCurrentBranchCommand extends FeatureBranchCommand {

    @Override
    protected void execute(ExecutionEvent event, List<Repository> repositories, String branchRef)
            throws ExecutionException {

        //perform branch operation
        Shell shell = HandlerUtil.getActiveShell(event);
        for (Repository repository : repositories) {
            try {
                if (!canMerge(repository)) {
                    continue;
                }
                ProgressMonitorDialog progressDialog = new ProgressMonitorDialog(shell);
                final MergeStableIntoCurrentBranchOperation op = new MergeStableIntoCurrentBranchOperation(event,
                        branchRef, repository);
                progressDialog.run(true, true, op);
            } catch (InterruptedException e) {
                //ignore
            } catch (Exception e) {
                RepositoryUtils.handleException(e);
            }
        }

    }

    private boolean canMerge(final Repository repository) {
        String message = null;
        Exception ex = null;
        try {
            Ref head = repository.getRef(Constants.HEAD);
            if (head == null || !head.isSymbolic())
                message = UIText.MergeAction_HeadIsNoBranch;
            else if (!repository.getRepositoryState().equals(RepositoryState.SAFE))
                message = NLS.bind(UIText.MergeAction_WrongRepositoryState, repository.getRepositoryState());
            else if (!head.getLeaf().getName().startsWith("refs/heads/features")) { //$NON-NLS-1$
                message = "Current branch is not a feature branch.";
            }
        } catch (IOException e) {
            message = e.getMessage();
            ex = e;
        }

        if (message != null)
            org.eclipse.egit.ui.Activator.handleError(message, ex, true);
        return (message == null);
    }

    private static class MergeStableIntoCurrentBranchOperation implements IRunnableWithProgress {

        private Repository repository;
        private String branchRef;

        public MergeStableIntoCurrentBranchOperation(ExecutionEvent event, String branchRef,
                Repository repository) {
            this.repository = repository;
            this.branchRef = branchRef;
        }

        public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
            try {
                SubMonitor mon = SubMonitor.convert(monitor, 100);
                mon.setTaskName("Fetch from upstream...");
                fetchOrigin(mon.newChild(50));

                mon.setTaskName(MessageFormat.format("Merging with {0}...", branchRef));
                performMerge(mon.newChild(50));
            } catch (Exception e) {
                RepositoryUtils.handleException(e);
            }
        }

        private void performMerge(SubMonitor monitor) {
            final String branchName = branchRef.substring("refs/heads/".length()); //$NON-NLS-1$
            final String refName = "refs/remotes/origin/" + branchName; //$NON-NLS-1$

            String featureBranchName;
            String userName = "anonymous";
            try {
                Ref head = repository.getRef(Constants.HEAD);
                featureBranchName = head.getLeaf().getName();
                IPath path = new Path(featureBranchName);
                if (path.segmentCount() > 4 && "features".equals(path.segment(2))) { //$NON-NLS-1$
                    featureBranchName = path.removeFirstSegments(4).toString();
                    userName = path.segment(3);
                }
            } catch (IOException e1) {
                throw new RuntimeException(e1);
            }
            final String commitMessage = MessageFormat.format(
                    "Merge \"{0}\" into feature branch \"{1}\" of user {2}.", branchName, featureBranchName,
                    userName);

            final MergeOperation op = new MergeOperation(repository, refName);
            op.setFastForwardMode(FastForwardMode.FF);

            String jobname = NLS.bind(UIText.MergeAction_JobNameMerge, refName);
            Job job = new WorkspaceJob(jobname) {

                @Override
                public IStatus runInWorkspace(IProgressMonitor monitor) {
                    try {
                        op.execute(monitor);

                        //Add Gerrit change-id to the commit
                        try {
                            new Git(repository).commit().setAmend(true).setMessage(commitMessage)
                                    .setInsertChangeId(true).call();
                        } catch (Exception e) {
                            throw new TeamException(CoreText.MergeOperation_InternalError, e);
                        }

                    } catch (final CoreException e) {
                        return e.getStatus();
                    }
                    return Status.OK_STATUS;
                }
            };
            job.setUser(true);
            job.setRule(op.getSchedulingRule());
            job.addJobChangeListener(new JobChangeAdapter() {
                @Override
                public void done(IJobChangeEvent jobEvent) {
                    IStatus result = jobEvent.getJob().getResult();
                    if (result.getSeverity() == IStatus.CANCEL)
                        Display.getDefault().asyncExec(new Runnable() {
                            public void run() {
                                // don't use getShell(event) here since
                                // the active shell has changed since the
                                // execution has been triggered.
                                Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
                                MessageDialog.openInformation(shell, UIText.MergeAction_MergeCanceledTitle,
                                        UIText.MergeAction_MergeCanceledMessage);
                            }
                        });
                    else if (!result.isOK())
                        org.eclipse.egit.ui.Activator.handleError(result.getMessage(), result.getException(), true);
                    else
                        Display.getDefault().asyncExec(new Runnable() {
                            public void run() {
                                Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
                                MergeResultDialog.getDialog(shell, repository, op.getResult()).open();
                            }
                        });
                }
            });
            job.schedule();
        }

        private void fetchOrigin(IProgressMonitor monitor) throws Exception {
            FetchOperationUI fetchOp = new FetchOperationUI(repository,
                    new RemoteConfig(repository.getConfig(), "origin"), 3000, false); //$NON-NLS-1$
            fetchOp.setCredentialsProvider(new EGitCredentialsProvider());
            fetchOp.execute(monitor);
        }

    }
}