/*******************************************************************************
* Copyright (c) 2016 Red Hat Inc. 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
*******************************************************************************/
package org.eclipse.egit.ui.internal.pull;
import java.io.IOException;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.egit.core.op.CreateLocalBranchOperation;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.components.BranchRebaseModeCombo;
import org.eclipse.egit.ui.internal.components.RefContentAssistProvider;
import org.eclipse.egit.ui.internal.components.RemoteSelectionCombo;
import org.eclipse.egit.ui.internal.components.RemoteSelectionCombo.IRemoteSelectionListener;
import org.eclipse.egit.ui.internal.components.RemoteSelectionCombo.SelectionType;
import org.eclipse.egit.ui.internal.push.AddRemoteWizard;
import org.eclipse.egit.ui.internal.push.PushBranchPage;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.jgit.api.PullCommand;
import org.eclipse.jgit.lib.BranchConfig;
import org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
/**
* This Wizard Page allows to configure a Push operation (remote, reference,
* rebase/merge)
*
* It is heavily inspired/copy-pasted from the {@link PushBranchPage} and a lot
* of code could be factorized.
*/
public class PullWizardPage extends WizardPage {
private RemoteSelectionCombo remoteSelectionCombo;
private List<RemoteConfig> remoteConfigs;
private RemoteConfig remoteConfig;
private RefContentAssistProvider assist;
private Repository repository;
private String fullBranch;
private Button rememberConfigForBranch;
private BranchRebaseModeCombo upstreamConfigComponent;
private BranchRebaseMode upstreamConfig;
private Ref head;
private Text remoteBranchNameText;
private ControlDecoration missingBranchDecorator;
private boolean configureUpstream;
/**
* Create the page.
*
* @param repository
*/
public PullWizardPage(Repository repository) {
super(UIText.PullWizardPage_PageName);
setTitle(UIText.PullWizardPage_PageTitle);
setMessage(UIText.PullWizardPage_PageMessage);
setImageDescriptor(UIIcons.WIZBAN_PULL);
this.repository = repository;
try {
this.head = repository.findRef(Constants.HEAD);
this.fullBranch = repository.getFullBranch();
} catch (IOException ex) {
Activator.logError(ex.getMessage(), ex);
}
}
@Override
public void createControl(Composite parent) {
try {
this.remoteConfigs = RemoteConfig
.getAllRemoteConfigs(repository.getConfig());
Collections.sort(remoteConfigs, new Comparator<RemoteConfig>() {
@Override
public int compare(RemoteConfig first, RemoteConfig second) {
return String.CASE_INSENSITIVE_ORDER
.compare(first.getName(), second.getName());
}
});
setDefaultUpstreamConfig();
} catch (URISyntaxException e) {
this.remoteConfigs = new ArrayList<>();
handleError(e);
}
Composite res = new Composite(parent, SWT.NONE);
res.setLayout(new GridLayout(3, false));
Label remoteLabel = new Label(res, SWT.NONE);
remoteLabel.setText(UIText.PushBranchPage_RemoteLabel);
this.remoteSelectionCombo = new RemoteSelectionCombo(
res, SWT.NONE, SelectionType.PUSH);
GridDataFactory.fillDefaults().grab(true, false)
.applyTo(remoteSelectionCombo);
setRemoteConfigs();
remoteSelectionCombo
.addRemoteSelectionListener(new IRemoteSelectionListener() {
@Override
public void remoteSelected(RemoteConfig rc) {
remoteConfig = rc;
setRefAssist(rc);
checkPage();
}
});
Button newRemoteButton = new Button(res, SWT.PUSH);
newRemoteButton.setText(UIText.PushBranchPage_NewRemoteButton);
GridDataFactory.fillDefaults().applyTo(newRemoteButton);
newRemoteButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
showNewRemoteDialog();
}
});
Label branchNameLabel = new Label(res, SWT.NONE);
branchNameLabel.setText(UIText.PullWizardPage_referenceLabel);
branchNameLabel.setToolTipText(UIText.PullWizardPage_referenceTooltip);
remoteBranchNameText = new Text(res, SWT.BORDER);
GridDataFactory.fillDefaults().grab(true, false).span(2, 1)
.applyTo(remoteBranchNameText);
UIUtils.addRefContentProposalToText(remoteBranchNameText,
this.repository, () -> {
if (PullWizardPage.this.assist != null) {
return PullWizardPage.this.assist
.getRefsForContentAssist(false, true);
}
return Collections.emptyList();
});
remoteBranchNameText.setText(getSuggestedBranchName());
remoteBranchNameText.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
checkPage();
}
});
this.upstreamConfigComponent = new BranchRebaseModeCombo(res);
GridDataFactory.fillDefaults().grab(true, false).span(2, 1)
.align(SWT.BEGINNING, SWT.CENTER)
.applyTo(upstreamConfigComponent.getViewer().getCombo());
this.upstreamConfigComponent.getViewer().addSelectionChangedListener(
(event) -> upstreamConfig = upstreamConfigComponent
.getRebaseMode());
if (upstreamConfig != null) {
upstreamConfigComponent.setRebaseMode(upstreamConfig);
}
if (this.fullBranch != null
&& this.fullBranch.startsWith(Constants.R_HEADS)) {
this.rememberConfigForBranch = new Button(res, SWT.CHECK);
GridData checkboxLayoutData = new GridData(SWT.BEGINNING,
SWT.CENTER, false, false, 3, 1);
checkboxLayoutData.verticalIndent = 20;
this.rememberConfigForBranch.setText(
UIText.UpstreamConfigComponent_ConfigureUpstreamCheck);
this.rememberConfigForBranch.setToolTipText(
UIText.UpstreamConfigComponent_ConfigureUpstreamToolTip);
this.rememberConfigForBranch.setLayoutData(checkboxLayoutData);
this.rememberConfigForBranch.setSelection(this.configureUpstream);
this.rememberConfigForBranch
.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
configureUpstream = rememberConfigForBranch
.getSelection();
checkPage();
}
});
}
setPageComplete(isPageComplete());
setControl(res);
}
private void setRemoteConfigs() {
remoteSelectionCombo.setItems(remoteConfigs);
if (this.head != null) {
String branchName = Repository.shortenRefName(this.head.getName());
BranchConfig branchConfig = new BranchConfig(repository.getConfig(),
branchName);
String remoteName = branchConfig.getRemote();
if (remoteName != null) {
for (RemoteConfig rc : remoteConfigs) {
if (remoteName.equals(rc.getName()))
remoteSelectionCombo.setSelectedRemote(rc);
}
}
}
remoteConfig = remoteSelectionCombo.getSelectedRemote();
setRefAssist(remoteConfig);
}
private void showNewRemoteDialog() {
AddRemoteWizard wizard = new AddRemoteWizard(repository);
WizardDialog dialog = new WizardDialog(getShell(), wizard);
int result = dialog.open();
if (result == Window.OK) {
URIish uri = wizard.getUri();
String remoteName = wizard.getRemoteName();
try {
StoredConfig repoConfig = repository.getConfig();
RemoteConfig newRemoteConfig = new RemoteConfig(repoConfig,
remoteName);
newRemoteConfig.addURI(uri);
RefSpec defaultFetchSpec = new RefSpec().setForceUpdate(true)
.setSourceDestination(Constants.R_HEADS + "*", //$NON-NLS-1$
Constants.R_REMOTES + remoteName + "/*"); //$NON-NLS-1$
newRemoteConfig.addFetchRefSpec(defaultFetchSpec);
newRemoteConfig.update(repoConfig);
repoConfig.save();
List<RemoteConfig> allRemoteConfigs = RemoteConfig
.getAllRemoteConfigs(repository.getConfig());
remoteSelectionCombo.setItems(allRemoteConfigs);
// find the new remote in the list, as the initial
// newRemoteConfig object
// isn't what's stored and returned by getAllRemoteConfigs
for (RemoteConfig current : allRemoteConfigs) {
if (newRemoteConfig.getName().equals(current.getName())) {
setSelectedRemote(current);
}
}
} catch (URISyntaxException ex) {
Activator.logError(ex.getMessage(), ex);
} catch (IOException ex) {
Activator.logError(ex.getMessage(), ex);
}
}
}
private void setRefAssist(RemoteConfig config) {
if (config != null && config.getURIs().size() > 0) {
this.assist = new RefContentAssistProvider(
PullWizardPage.this.repository, config.getURIs().get(0),
getContainer());
}
}
void setSelectedRemote(RemoteConfig config) {
remoteSelectionCombo.setSelectedRemote(config);
this.remoteConfig = config;
setRefAssist(this.remoteConfig);
checkPage();
}
@Override
public boolean isPageComplete() {
return remoteConfig != null && remoteBranchNameText.getText() != null
&& !remoteBranchNameText.getText().isEmpty();
}
private void checkPage() {
try {
if (remoteConfig == null) {
setErrorMessage(UIText.PushBranchPage_ChooseRemoteError);
return;
}
String branchName = remoteBranchNameText.getText();
String branchNameMessage = null;
if (branchName.length() == 0) {
branchNameMessage = MessageFormat.format(
UIText.PullWizardPage_ChooseReference,
remoteConfig.getName());
}
if (branchNameMessage != null) {
setErrorMessage(branchNameMessage);
if (this.missingBranchDecorator == null) {
this.missingBranchDecorator = new ControlDecoration(this.remoteBranchNameText, SWT.TOP | SWT.LEFT);
this.missingBranchDecorator
.setImage(FieldDecorationRegistry.getDefault()
.getFieldDecoration(
FieldDecorationRegistry.DEC_ERROR)
.getImage());
}
this.missingBranchDecorator
.setDescriptionText(branchNameMessage);
this.missingBranchDecorator.show();
return;
} else if (this.missingBranchDecorator != null) {
this.missingBranchDecorator.hide();
}
if (overrideUpstreamConfiguration()
&& hasDifferentUpstreamConfiguration()) {
setMessage(UIText.PushBranchPage_UpstreamConfigOverwriteWarning,
IMessageProvider.WARNING);
} else {
setMessage(UIText.PullWizardPage_PageMessage);
}
setErrorMessage(null);
} finally {
setPageComplete(getErrorMessage() == null);
}
}
private void handleError(URISyntaxException e) {
Activator.handleError(e.getMessage(), e, false);
setErrorMessage(e.getMessage());
}
private String getSuggestedBranchName() {
if (fullBranch != null) {
String branchName = Repository.shortenRefName(fullBranch);
StoredConfig config = repository.getConfig();
BranchConfig branchConfig = new BranchConfig(config, branchName);
String merge = branchConfig.getMerge();
if (!branchConfig.isRemoteLocal() && merge != null
&& merge.startsWith(Constants.R_HEADS)) {
return Repository.shortenRefName(merge);
}
}
return ""; //$NON-NLS-1$
}
boolean overrideUpstreamConfiguration() {
return this.configureUpstream;
}
BranchRebaseMode getUpstreamConfig() {
return this.upstreamConfig;
}
private boolean hasDifferentUpstreamConfiguration() {
String branchName = Repository.shortenRefName(this.fullBranch);
BranchConfig branchConfig = new BranchConfig(repository.getConfig(),
branchName);
String remote = branchConfig.getRemote();
// No upstream config -> don't show warning
if (remote == null) {
return false;
}
if (!remote.equals(remoteConfig.getName())) {
return true;
}
String merge = branchConfig.getMerge();
if (merge == null || !merge.equals(getFullRemoteReference())) {
return true;
}
if (branchConfig.getRebaseMode() != getUpstreamConfig()) {
return true;
}
return false;
}
private void setDefaultUpstreamConfig() {
String branchName = Repository.shortenRefName(this.fullBranch);
BranchConfig branchConfig = new BranchConfig(repository.getConfig(),
branchName);
boolean alreadyConfigured = branchConfig.getMerge() != null;
BranchRebaseMode config;
if (alreadyConfigured) {
config = PullCommand.getRebaseMode(branchName,
repository.getConfig());
} else {
config = CreateLocalBranchOperation
.getDefaultUpstreamConfig(repository, Constants.R_REMOTES
+ Constants.DEFAULT_REMOTE_NAME + "/" + branchName); //$NON-NLS-1$
}
this.upstreamConfig = config;
}
/**
* @return the chosen short name of the branch on the remote
*/
String getFullRemoteReference() {
if (!remoteBranchNameText.getText().startsWith(Constants.R_REFS))
return Constants.R_HEADS + remoteBranchNameText.getText();
else
return remoteBranchNameText.getText();
}
RemoteConfig getRemoteConfig() {
return this.remoteConfig;
}
@Override
public void dispose() {
super.dispose();
if (this.missingBranchDecorator != null) {
this.missingBranchDecorator.dispose();
}
}
}