| /******************************************************************************* |
| * Copyright (c) 2010, 2016 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 (SAP AG) - initial implementation |
| * Marc Khouzam (Ericsson) - Add an option not to checkout the new branch |
| * Thomas Wolf <thomas.wolf@paranor.ch> - Bug 493935, 495777 |
| *******************************************************************************/ |
| package org.eclipse.egit.ui.internal.fetch; |
| |
| import java.io.IOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.net.URISyntaxException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.SortedSet; |
| import java.util.TreeSet; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import java.util.regex.PatternSyntaxException; |
| |
| import org.eclipse.core.resources.WorkspaceJob; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.egit.core.internal.gerrit.GerritUtil; |
| import org.eclipse.egit.core.op.CreateLocalBranchOperation; |
| import org.eclipse.egit.core.op.ListRemoteOperation; |
| import org.eclipse.egit.core.op.TagOperation; |
| import org.eclipse.egit.ui.Activator; |
| import org.eclipse.egit.ui.JobFamilies; |
| import org.eclipse.egit.ui.UIPreferences; |
| import org.eclipse.egit.ui.UIUtils; |
| import org.eclipse.egit.ui.internal.UIText; |
| import org.eclipse.egit.ui.internal.ValidationUtils; |
| import org.eclipse.egit.ui.internal.branch.BranchOperationUI; |
| import org.eclipse.egit.ui.internal.dialogs.AbstractBranchSelectionDialog; |
| import org.eclipse.egit.ui.internal.dialogs.BranchEditDialog; |
| import org.eclipse.egit.ui.internal.gerrit.GerritDialogSettings; |
| import org.eclipse.jface.bindings.keys.KeyStroke; |
| import org.eclipse.jface.dialogs.Dialog; |
| import org.eclipse.jface.dialogs.IDialogSettings; |
| import org.eclipse.jface.dialogs.IInputValidator; |
| import org.eclipse.jface.dialogs.IPageChangeProvider; |
| import org.eclipse.jface.dialogs.IPageChangedListener; |
| import org.eclipse.jface.dialogs.PageChangedEvent; |
| import org.eclipse.jface.fieldassist.ContentProposalAdapter; |
| import org.eclipse.jface.fieldassist.IContentProposal; |
| import org.eclipse.jface.fieldassist.IContentProposalProvider; |
| import org.eclipse.jface.fieldassist.TextContentAdapter; |
| import org.eclipse.jface.layout.GridDataFactory; |
| import org.eclipse.jface.operation.IRunnableWithProgress; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.jface.wizard.IWizardContainer; |
| import org.eclipse.jface.wizard.WizardPage; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.PersonIdent; |
| import org.eclipse.jgit.lib.Ref; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.lib.TagBuilder; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.revwalk.RevWalk; |
| import org.eclipse.jgit.transport.FetchResult; |
| import org.eclipse.jgit.transport.RefSpec; |
| import org.eclipse.jgit.transport.RemoteConfig; |
| import org.eclipse.jgit.transport.URIish; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.dnd.Clipboard; |
| import org.eclipse.swt.dnd.TextTransfer; |
| 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.events.VerifyEvent; |
| import org.eclipse.swt.events.VerifyListener; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Combo; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Group; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.ui.IWorkbenchCommandConstants; |
| import org.eclipse.ui.PlatformUI; |
| |
| /** |
| * Fetch a change from Gerrit |
| */ |
| public class FetchGerritChangePage extends WizardPage { |
| |
| private static final String RUN_IN_BACKGROUND = "runInBackground"; //$NON-NLS-1$ |
| |
| private final Repository repository; |
| |
| private final IDialogSettings settings; |
| |
| private final String lastUriKey; |
| |
| private Combo uriCombo; |
| |
| private List<Change> changeRefs; |
| |
| private Text refText; |
| |
| private Button createBranch; |
| |
| private Button createTag; |
| |
| private Button checkout; |
| |
| private Button dontCheckout; |
| |
| private Label tagTextlabel; |
| |
| private Text tagText; |
| |
| private Label branchTextlabel; |
| |
| private Text branchText; |
| |
| private String refName; |
| |
| private Composite warningAdditionalRefNotActive; |
| |
| private Button activateAdditionalRefs; |
| |
| private Button runInBackgroud; |
| |
| private IInputValidator branchValidator; |
| private IInputValidator tagValidator; |
| |
| private Button branchEditButton; |
| |
| private Button branchCheckoutButton; |
| |
| /** |
| * @param repository |
| * @param refName initial value for the ref field |
| */ |
| public FetchGerritChangePage(Repository repository, String refName) { |
| super(FetchGerritChangePage.class.getName()); |
| this.repository = repository; |
| this.refName = refName; |
| setTitle(NLS |
| .bind(UIText.FetchGerritChangePage_PageTitle, |
| Activator.getDefault().getRepositoryUtil() |
| .getRepositoryName(repository))); |
| setMessage(UIText.FetchGerritChangePage_PageMessage); |
| settings = getDialogSettings(); |
| lastUriKey = repository + GerritDialogSettings.LAST_URI_SUFFIX; |
| |
| branchValidator = ValidationUtils.getRefNameInputValidator(repository, |
| Constants.R_HEADS, true); |
| tagValidator = ValidationUtils.getRefNameInputValidator(repository, |
| Constants.R_TAGS, true); |
| } |
| |
| @Override |
| protected IDialogSettings getDialogSettings() { |
| return GerritDialogSettings |
| .getSection(GerritDialogSettings.FETCH_FROM_GERRIT_SECTION); |
| } |
| |
| @Override |
| public void createControl(Composite parent) { |
| Clipboard clipboard = new Clipboard(parent.getDisplay()); |
| String clipText = (String) clipboard.getContents(TextTransfer |
| .getInstance()); |
| clipboard.dispose(); |
| String defaultUri = null; |
| String defaultCommand = null; |
| String defaultChange = null; |
| String candidateChange = null; |
| if (clipText != null) { |
| String pattern = "git fetch (\\w+:\\S+) (refs/changes/\\d+/\\d+/\\d+) && git (\\w+) FETCH_HEAD"; //$NON-NLS-1$ |
| Matcher matcher = Pattern.compile(pattern).matcher(clipText); |
| if (matcher.matches()) { |
| defaultUri = matcher.group(1); |
| defaultChange = matcher.group(2); |
| defaultCommand = matcher.group(3); |
| } else { |
| candidateChange = determineChangeFromString(clipText.trim()); |
| } |
| } |
| Composite main = new Composite(parent, SWT.NONE); |
| main.setLayout(new GridLayout(2, false)); |
| GridDataFactory.fillDefaults().grab(true, true).applyTo(main); |
| new Label(main, SWT.NONE) |
| .setText(UIText.FetchGerritChangePage_UriLabel); |
| uriCombo = new Combo(main, SWT.DROP_DOWN); |
| GridDataFactory.fillDefaults().grab(true, false).applyTo(uriCombo); |
| uriCombo.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| changeRefs = null; |
| } |
| }); |
| new Label(main, SWT.NONE) |
| .setText(UIText.FetchGerritChangePage_ChangeLabel); |
| refText = new Text(main, SWT.BORDER); |
| GridDataFactory.fillDefaults().grab(true, false).applyTo(refText); |
| final ExplicitContentProposalAdapter contentProposer = addRefContentProposalToText( |
| refText); |
| refText.addVerifyListener(new VerifyListener() { |
| @Override |
| public void verifyText(VerifyEvent event) { |
| event.text = event.text.trim(); |
| } |
| }); |
| |
| final Group checkoutGroup = new Group(main, SWT.SHADOW_ETCHED_IN); |
| checkoutGroup.setLayout(new GridLayout(3, false)); |
| GridDataFactory.fillDefaults().span(3, 1).grab(true, false) |
| .applyTo(checkoutGroup); |
| checkoutGroup.setText(UIText.FetchGerritChangePage_AfterFetchGroup); |
| |
| // radio: create local branch |
| createBranch = new Button(checkoutGroup, SWT.RADIO); |
| GridDataFactory.fillDefaults().span(1, 1).applyTo(createBranch); |
| createBranch.setText(UIText.FetchGerritChangePage_LocalBranchRadio); |
| createBranch.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| checkPage(); |
| } |
| }); |
| |
| branchCheckoutButton = new Button(checkoutGroup, SWT.CHECK); |
| GridDataFactory.fillDefaults().span(2, 1).align(SWT.END, SWT.CENTER) |
| .applyTo(branchCheckoutButton); |
| branchCheckoutButton.setFont(JFaceResources.getDialogFont()); |
| branchCheckoutButton |
| .setText(UIText.FetchGerritChangePage_LocalBranchCheckout); |
| branchCheckoutButton.setSelection(true); |
| |
| branchTextlabel = new Label(checkoutGroup, SWT.NONE); |
| GridDataFactory.defaultsFor(branchTextlabel).exclude(false) |
| .applyTo(branchTextlabel); |
| branchTextlabel.setText(UIText.FetchGerritChangePage_BranchNameText); |
| branchText = new Text(checkoutGroup, SWT.SINGLE | SWT.BORDER); |
| GridDataFactory.fillDefaults().grab(true, false).applyTo(branchText); |
| branchText.addModifyListener(new ModifyListener() { |
| @Override |
| public void modifyText(ModifyEvent e) { |
| checkPage(); |
| } |
| }); |
| |
| branchEditButton = new Button(checkoutGroup, SWT.PUSH); |
| branchEditButton.setFont(JFaceResources.getDialogFont()); |
| branchEditButton.setText(UIText.FetchGerritChangePage_BranchEditButton); |
| branchEditButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent selectionEvent) { |
| String txt = branchText.getText(); |
| String refToMark = "".equals(txt) ? null : Constants.R_HEADS + txt; //$NON-NLS-1$ |
| AbstractBranchSelectionDialog dlg = new BranchEditDialog( |
| checkoutGroup.getShell(), repository, refToMark); |
| if (dlg.open() == Window.OK) { |
| branchText.setText(Repository.shortenRefName(dlg |
| .getRefName())); |
| } else { |
| // force calling branchText's modify listeners |
| branchText.setText(branchText.getText()); |
| } |
| } |
| }); |
| GridDataFactory.defaultsFor(branchEditButton).exclude(false) |
| .applyTo(branchEditButton); |
| |
| // radio: create tag |
| createTag = new Button(checkoutGroup, SWT.RADIO); |
| GridDataFactory.fillDefaults().span(3, 1).applyTo(createTag); |
| createTag.setText(UIText.FetchGerritChangePage_TagRadio); |
| createTag.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| checkPage(); |
| } |
| }); |
| |
| tagTextlabel = new Label(checkoutGroup, SWT.NONE); |
| GridDataFactory.defaultsFor(tagTextlabel).exclude(true) |
| .applyTo(tagTextlabel); |
| tagTextlabel.setText(UIText.FetchGerritChangePage_TagNameText); |
| tagText = new Text(checkoutGroup, SWT.SINGLE | SWT.BORDER); |
| GridDataFactory.fillDefaults().exclude(true).grab(true, false) |
| .applyTo(tagText); |
| tagText.addModifyListener(new ModifyListener() { |
| @Override |
| public void modifyText(ModifyEvent e) { |
| checkPage(); |
| } |
| }); |
| |
| // radio: checkout FETCH_HEAD |
| checkout = new Button(checkoutGroup, SWT.RADIO); |
| GridDataFactory.fillDefaults().span(3, 1).applyTo(checkout); |
| checkout.setText(UIText.FetchGerritChangePage_CheckoutRadio); |
| checkout.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| checkPage(); |
| } |
| }); |
| |
| // radio: don't checkout |
| dontCheckout = new Button(checkoutGroup, SWT.RADIO); |
| GridDataFactory.fillDefaults().span(3, 1).applyTo(checkout); |
| dontCheckout.setText(UIText.FetchGerritChangePage_UpdateRadio); |
| dontCheckout.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| checkPage(); |
| } |
| }); |
| |
| if ("checkout".equals(defaultCommand)) //$NON-NLS-1$ |
| checkout.setSelection(true); |
| else |
| createBranch.setSelection(true); |
| |
| warningAdditionalRefNotActive = new Composite(main, SWT.NONE); |
| GridDataFactory.fillDefaults().span(2, 1).grab(true, false) |
| .exclude(true).applyTo(warningAdditionalRefNotActive); |
| warningAdditionalRefNotActive.setLayout(new GridLayout(2, false)); |
| warningAdditionalRefNotActive.setVisible(false); |
| |
| activateAdditionalRefs = new Button(warningAdditionalRefNotActive, |
| SWT.CHECK); |
| activateAdditionalRefs |
| .setText(UIText.FetchGerritChangePage_ActivateAdditionalRefsButton); |
| activateAdditionalRefs |
| .setToolTipText(UIText.FetchGerritChangePage_ActivateAdditionalRefsTooltip); |
| |
| refText.addModifyListener(new ModifyListener() { |
| @Override |
| public void modifyText(ModifyEvent e) { |
| Change change = Change.fromRef(refText.getText()); |
| if (change != null) { |
| branchText.setText(NLS |
| .bind(UIText.FetchGerritChangePage_SuggestedRefNamePattern, |
| change.getChangeNumber(), |
| change.getPatchSetNumber())); |
| tagText.setText(branchText.getText()); |
| } else { |
| branchText.setText(""); //$NON-NLS-1$ |
| tagText.setText(""); //$NON-NLS-1$ |
| } |
| checkPage(); |
| } |
| }); |
| if (defaultChange != null) { |
| refText.setText(defaultChange); |
| } else if (candidateChange != null) { |
| refText.setText(candidateChange); |
| } |
| runInBackgroud = new Button(main, SWT.CHECK); |
| GridDataFactory.fillDefaults().span(2, 1).align(SWT.BEGINNING, SWT.END) |
| .grab(true, true) |
| .applyTo(runInBackgroud); |
| runInBackgroud.setText(UIText.FetchGerritChangePage_RunInBackground); |
| |
| // get all available Gerrit URIs from the repository |
| SortedSet<String> uris = new TreeSet<>(); |
| try { |
| for (RemoteConfig rc : RemoteConfig.getAllRemoteConfigs(repository |
| .getConfig())) { |
| if (GerritUtil.isGerritFetch(rc)) { |
| if (rc.getURIs().size() > 0) { |
| uris.add(rc.getURIs().get(0).toPrivateString()); |
| } |
| for (URIish u : rc.getPushURIs()) { |
| uris.add(u.toPrivateString()); |
| } |
| } |
| |
| } |
| } catch (URISyntaxException e) { |
| Activator.handleError(e.getMessage(), e, false); |
| setErrorMessage(e.getMessage()); |
| } |
| for (String aUri : uris) { |
| uriCombo.add(aUri); |
| } |
| if (defaultUri != null) { |
| uriCombo.setText(defaultUri); |
| } else { |
| selectLastUsedUri(); |
| } |
| restoreRunInBackgroundSelection(); |
| refText.setFocus(); |
| Dialog.applyDialogFont(main); |
| setControl(main); |
| if (candidateChange != null) { |
| // Launch content assist when the page is displayed |
| final IWizardContainer container = getContainer(); |
| if (container instanceof IPageChangeProvider) { |
| ((IPageChangeProvider) container) |
| .addPageChangedListener(new IPageChangedListener() { |
| @Override |
| public void pageChanged(PageChangedEvent event) { |
| if (event |
| .getSelectedPage() == FetchGerritChangePage.this) { |
| // Only the first time: remove myself |
| event.getPageChangeProvider() |
| .removePageChangedListener(this); |
| getControl().getDisplay() |
| .asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| Control control = getControl(); |
| if (control != null |
| && !control.isDisposed()) { |
| contentProposer |
| .openProposalPopup(); |
| } |
| } |
| }); |
| } |
| } |
| }); |
| } |
| } |
| checkPage(); |
| } |
| |
| /** |
| * Tries to determine a Gerrit change number from an input string. |
| * |
| * @param input |
| * string to derive a change number from |
| * @return the change number as a string, or {@code null} if none could be |
| * determined. |
| */ |
| protected static String determineChangeFromString(String input) { |
| if (input == null) { |
| return null; |
| } |
| Pattern pattern = Pattern.compile( |
| "(?:https?://\\S+?/|/)?([1-9][0-9]*)(?:/([1-9][0-9]*)(?:/([1-9][0-9]*)(?:..\\d+)?)?)?(?:/\\S*)?"); //$NON-NLS-1$ |
| Matcher matcher = pattern.matcher(input); |
| if (matcher.matches()) { |
| String first = matcher.group(1); |
| String second = matcher.group(2); |
| String third = matcher.group(3); |
| if (second != null && !second.isEmpty()) { |
| if (third != null && !third.isEmpty()) { |
| return second; |
| } else if (input.startsWith("http")) { //$NON-NLS-1$ |
| // A URL ending with two digits: take the first. |
| return first; |
| } else { |
| // Take the numerically larger. Might be a fragment like |
| // /10/65510 as in refs/changes/10/65510/6, or /65510/6 as |
| // in https://git.eclipse.org/r/#/c/65510/6. This is a |
| // heuristic, it might go wrong on a Gerrit where there are |
| // not many changes (yet), and one of them has many patch |
| // sets. |
| try { |
| if (Integer.parseInt(first) > Integer |
| .parseInt(second)) { |
| return first; |
| } else { |
| return second; |
| } |
| } catch (NumberFormatException e) { |
| // Numerical overflow? |
| return null; |
| } |
| } |
| } else { |
| return first; |
| } |
| } |
| return null; |
| } |
| |
| private void storeLastUsedUri(String uri) { |
| settings.put(lastUriKey, uri.trim()); |
| } |
| |
| private void selectLastUsedUri() { |
| String lastUri = settings.get(lastUriKey); |
| if (lastUri != null) { |
| int i = uriCombo.indexOf(lastUri); |
| if (i != -1) { |
| uriCombo.select(i); |
| return; |
| } |
| } |
| uriCombo.select(0); |
| } |
| |
| private void storeRunInBackgroundSelection() { |
| settings.put(RUN_IN_BACKGROUND, runInBackgroud.getSelection()); |
| } |
| |
| private void restoreRunInBackgroundSelection() { |
| runInBackgroud.setSelection(settings.getBoolean(RUN_IN_BACKGROUND)); |
| } |
| |
| @Override |
| public void setVisible(boolean visible) { |
| super.setVisible(visible); |
| if (visible && refName != null) |
| refText.setText(refName); |
| } |
| |
| private void checkPage() { |
| boolean createBranchSelected = createBranch.getSelection(); |
| branchText.setEnabled(createBranchSelected); |
| branchText.setVisible(createBranchSelected); |
| branchTextlabel.setVisible(createBranchSelected); |
| branchEditButton.setVisible(createBranchSelected); |
| branchCheckoutButton.setVisible(createBranchSelected); |
| GridData gd = (GridData) branchText.getLayoutData(); |
| gd.exclude = !createBranchSelected; |
| gd = (GridData) branchTextlabel.getLayoutData(); |
| gd.exclude = !createBranchSelected; |
| gd = (GridData) branchEditButton.getLayoutData(); |
| gd.exclude = !createBranchSelected; |
| gd = (GridData) branchCheckoutButton.getLayoutData(); |
| gd.exclude = !createBranchSelected; |
| |
| boolean createTagSelected = createTag.getSelection(); |
| tagText.setEnabled(createTagSelected); |
| tagText.setVisible(createTagSelected); |
| tagTextlabel.setVisible(createTagSelected); |
| gd = (GridData) tagText.getLayoutData(); |
| gd.exclude = !createTagSelected; |
| gd = (GridData) tagTextlabel.getLayoutData(); |
| gd.exclude = !createTagSelected; |
| branchText.getParent().layout(true); |
| |
| boolean showActivateAdditionalRefs = false; |
| showActivateAdditionalRefs = (checkout.getSelection() || dontCheckout |
| .getSelection()) |
| && !Activator |
| .getDefault() |
| .getPreferenceStore() |
| .getBoolean( |
| UIPreferences.RESOURCEHISTORY_SHOW_ADDITIONAL_REFS); |
| |
| gd = (GridData) warningAdditionalRefNotActive.getLayoutData(); |
| gd.exclude = !showActivateAdditionalRefs; |
| warningAdditionalRefNotActive.setVisible(showActivateAdditionalRefs); |
| warningAdditionalRefNotActive.getParent().layout(true); |
| |
| setErrorMessage(null); |
| try { |
| if (refText.getText().length() > 0) { |
| Change change = Change.fromRef(refText.getText()); |
| if (change == null) { |
| setErrorMessage(UIText.FetchGerritChangePage_MissingChangeMessage); |
| return; |
| } |
| } else { |
| setErrorMessage(UIText.FetchGerritChangePage_MissingChangeMessage); |
| return; |
| } |
| |
| if (createBranchSelected) |
| setErrorMessage(branchValidator.isValid(branchText.getText())); |
| else if (createTagSelected) |
| setErrorMessage(tagValidator.isValid(tagText.getText())); |
| } finally { |
| setPageComplete(getErrorMessage() == null); |
| } |
| } |
| |
| private List<Change> getRefsForContentAssist() |
| throws InvocationTargetException, InterruptedException { |
| if (changeRefs == null) { |
| final String uriText = uriCombo.getText(); |
| getContainer().run(true, true, |
| new IRunnableWithProgress() { |
| @Override |
| public void run(IProgressMonitor monitor) |
| throws InvocationTargetException, |
| InterruptedException { |
| ListRemoteOperation listOp; |
| try { |
| listOp = new ListRemoteOperation( |
| repository, |
| new URIish(uriText), |
| Activator |
| .getDefault() |
| .getPreferenceStore() |
| .getInt(UIPreferences.REMOTE_CONNECTION_TIMEOUT)); |
| } catch (URISyntaxException e) { |
| throw new InvocationTargetException(e); |
| } |
| |
| listOp.run(monitor); |
| changeRefs = new ArrayList<>(); |
| for (Ref ref : listOp.getRemoteRefs()) { |
| Change change = Change.fromRef(ref.getName()); |
| if (change != null) |
| changeRefs.add(change); |
| } |
| Collections.sort(changeRefs, |
| new Comparator<Change>() { |
| @Override |
| public int compare(Change o1, Change o2) { |
| // change number descending |
| int changeDiff = o2.changeNumber |
| .compareTo(o1.changeNumber); |
| if (changeDiff == 0) |
| // patch set number descending |
| changeDiff = o2 |
| .getPatchSetNumber() |
| .compareTo( |
| o1.getPatchSetNumber()); |
| return changeDiff; |
| } |
| }); |
| } |
| }); |
| } |
| return changeRefs; |
| } |
| |
| boolean doFetch() { |
| final RefSpec spec = new RefSpec().setSource(refText.getText()) |
| .setDestination(Constants.FETCH_HEAD); |
| final String uri = uriCombo.getText(); |
| final boolean doCheckout = checkout.getSelection(); |
| final boolean doCreateTag = createTag.getSelection(); |
| final boolean doCreateBranch = createBranch.getSelection(); |
| final boolean doCheckoutNewBranch = branchCheckoutButton.getSelection(); |
| final boolean doActivateAdditionalRefs = (checkout.getSelection() || dontCheckout |
| .getSelection()) && activateAdditionalRefs.getSelection(); |
| final String textForTag = tagText.getText(); |
| final String textForBranch = branchText.getText(); |
| |
| storeRunInBackgroundSelection(); |
| |
| if (runInBackgroud.getSelection()) { |
| Job job = new WorkspaceJob( |
| UIText.FetchGerritChangePage_GetChangeTaskName) { |
| |
| @Override |
| public IStatus runInWorkspace(IProgressMonitor monitor) { |
| try { |
| internalDoFetch(spec, uri, doCheckout, doCreateTag, |
| doCreateBranch, doCheckoutNewBranch, |
| doActivateAdditionalRefs, textForTag, |
| textForBranch, monitor); |
| } catch (CoreException ce) { |
| return ce.getStatus(); |
| } catch (Exception e) { |
| return Activator.createErrorStatus(e.getLocalizedMessage(), e); |
| } |
| return org.eclipse.core.runtime.Status.OK_STATUS; |
| } |
| |
| @Override |
| public boolean belongsTo(Object family) { |
| if (JobFamilies.FETCH.equals(family)) |
| return true; |
| return super.belongsTo(family); |
| } |
| }; |
| job.setUser(true); |
| job.schedule(); |
| return true; |
| } else { |
| try { |
| getWizard().getContainer().run(true, true, |
| new IRunnableWithProgress() { |
| @Override |
| public void run(IProgressMonitor monitor) |
| throws InvocationTargetException, |
| InterruptedException { |
| try { |
| internalDoFetch(spec, uri, doCheckout, |
| doCreateTag, doCreateBranch, |
| doCheckoutNewBranch, |
| doActivateAdditionalRefs, textForTag, |
| textForBranch, monitor); |
| } catch (RuntimeException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new InvocationTargetException(e); |
| } finally { |
| monitor.done(); |
| } |
| } |
| }); |
| } catch (InvocationTargetException e) { |
| Activator.handleError(e.getCause().getMessage(), e.getCause(), |
| true); |
| return false; |
| } catch (InterruptedException e) { |
| // just return |
| } |
| return true; |
| } |
| } |
| |
| private void internalDoFetch(RefSpec spec, String uri, boolean doCheckout, |
| boolean doCreateTag, boolean doCreateBranch, |
| boolean doCheckoutNewBranch, boolean doActivateAdditionalRefs, |
| String textForTag, String textForBranch, IProgressMonitor monitor) |
| throws IOException, CoreException, URISyntaxException { |
| |
| int totalWork = 1; |
| if (doCheckout) |
| totalWork++; |
| if (doCreateTag || doCreateBranch) |
| totalWork++; |
| monitor.beginTask( |
| UIText.FetchGerritChangePage_GetChangeTaskName, |
| totalWork); |
| |
| try { |
| RevCommit commit = fetchChange(uri, spec, monitor); |
| |
| if (doCreateTag) |
| createTag(spec, textForTag, commit, monitor); |
| |
| if (doCreateBranch) |
| createBranch(textForBranch, doCheckoutNewBranch, commit, |
| monitor); |
| |
| if (doCheckout || doCreateTag) |
| checkout(commit.name(), monitor); |
| |
| if (doActivateAdditionalRefs) |
| activateAdditionalRefs(); |
| |
| storeLastUsedUri(uri); |
| |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| private RevCommit fetchChange(String uri, RefSpec spec, |
| IProgressMonitor monitor) throws CoreException, URISyntaxException, |
| IOException { |
| int timeout = Activator.getDefault().getPreferenceStore() |
| .getInt(UIPreferences.REMOTE_CONNECTION_TIMEOUT); |
| |
| List<RefSpec> specs = new ArrayList<>(1); |
| specs.add(spec); |
| |
| String taskName = NLS |
| .bind(UIText.FetchGerritChangePage_FetchingTaskName, |
| spec.getSource()); |
| monitor.setTaskName(taskName); |
| FetchResult fetchRes = new FetchOperationUI(repository, |
| new URIish(uri), specs, timeout, false).execute(monitor); |
| |
| monitor.worked(1); |
| try (RevWalk rw = new RevWalk(repository)) { |
| return rw.parseCommit( |
| fetchRes.getAdvertisedRef(spec.getSource()).getObjectId()); |
| } |
| } |
| |
| private void createTag(final RefSpec spec, final String textForTag, |
| RevCommit commit, IProgressMonitor monitor) throws CoreException { |
| monitor.setTaskName(UIText.FetchGerritChangePage_CreatingTagTaskName); |
| final TagBuilder tag = new TagBuilder(); |
| PersonIdent personIdent = new PersonIdent(repository); |
| |
| tag.setTag(textForTag); |
| tag.setTagger(personIdent); |
| tag.setMessage(NLS.bind( |
| UIText.FetchGerritChangePage_GeneratedTagMessage, |
| spec.getSource())); |
| tag.setObjectId(commit); |
| new TagOperation(repository, tag, false).execute(monitor); |
| monitor.worked(1); |
| } |
| |
| private void createBranch(final String textForBranch, boolean doCheckout, |
| RevCommit commit, IProgressMonitor monitor) throws CoreException { |
| SubMonitor progress = SubMonitor.convert(monitor, |
| UIText.FetchGerritChangePage_CreatingBranchTaskName, |
| doCheckout ? 10 : 2); |
| CreateLocalBranchOperation bop = new CreateLocalBranchOperation( |
| repository, textForBranch, commit); |
| bop.execute(progress.newChild(2)); |
| if (doCheckout) { |
| checkout(textForBranch, progress.newChild(8)); |
| } |
| } |
| |
| private void checkout(String targetName, IProgressMonitor monitor) |
| throws CoreException { |
| monitor.setTaskName(UIText.FetchGerritChangePage_CheckingOutTaskName); |
| BranchOperationUI.checkout(repository, targetName).run(monitor); |
| monitor.worked(1); |
| } |
| |
| private void activateAdditionalRefs() { |
| // do this in the UI thread as it results in a |
| // refresh() on the history page |
| PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| Activator |
| .getDefault() |
| .getPreferenceStore() |
| .setValue( |
| UIPreferences.RESOURCEHISTORY_SHOW_ADDITIONAL_REFS, |
| true); |
| } |
| }); |
| } |
| |
| private ExplicitContentProposalAdapter addRefContentProposalToText( |
| final Text textField) { |
| KeyStroke stroke = UIUtils |
| .getKeystrokeOfBestActiveBindingFor(IWorkbenchCommandConstants.EDIT_CONTENT_ASSIST); |
| if (stroke != null) |
| UIUtils.addBulbDecorator(textField, NLS.bind( |
| UIText.FetchGerritChangePage_ContentAssistTooltip, |
| stroke.format())); |
| |
| IContentProposalProvider cp = new IContentProposalProvider() { |
| @Override |
| public IContentProposal[] getProposals(String contents, int position) { |
| List<IContentProposal> resultList = new ArrayList<>(); |
| |
| // make the simplest possible pattern check: allow "*" |
| // for multiple characters |
| String patternString = contents; |
| // ignore spaces in the beginning |
| while (patternString.length() > 0 |
| && patternString.charAt(0) == ' ') |
| patternString = patternString.substring(1); |
| |
| // we quote the string as it may contain spaces |
| // and other stuff colliding with the Pattern |
| patternString = Pattern.quote(patternString); |
| |
| patternString = patternString.replaceAll("\\x2A", ".*"); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| // make sure we add a (logical) * at the end |
| if (!patternString.endsWith(".*")) //$NON-NLS-1$ |
| patternString = patternString + ".*"; //$NON-NLS-1$ |
| |
| // let's compile a case-insensitive pattern (assumes ASCII only) |
| Pattern pattern; |
| try { |
| pattern = Pattern.compile(patternString, |
| Pattern.CASE_INSENSITIVE); |
| } catch (PatternSyntaxException e) { |
| pattern = null; |
| } |
| |
| List<Change> proposals; |
| try { |
| proposals = getRefsForContentAssist(); |
| } catch (InvocationTargetException e) { |
| Activator.handleError(e.getMessage(), e, true); |
| return null; |
| } catch (InterruptedException e) { |
| return null; |
| } |
| |
| if (proposals != null) |
| for (final Change ref : proposals) { |
| if (pattern != null |
| && !pattern.matcher( |
| ref.getChangeNumber().toString()) |
| .matches()) |
| continue; |
| IContentProposal propsal = new ChangeContentProposal( |
| ref); |
| resultList.add(propsal); |
| } |
| |
| return resultList.toArray(new IContentProposal[resultList |
| .size()]); |
| } |
| }; |
| |
| ExplicitContentProposalAdapter adapter = new ExplicitContentProposalAdapter( |
| textField, cp, stroke); |
| // set the acceptance style to always replace the complete content |
| adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); |
| return adapter; |
| } |
| |
| private static class ExplicitContentProposalAdapter |
| extends ContentProposalAdapter { |
| |
| public ExplicitContentProposalAdapter(Control control, |
| IContentProposalProvider proposalProvider, |
| KeyStroke keyStroke) { |
| super(control, new TextContentAdapter(), proposalProvider, |
| keyStroke, null); |
| } |
| |
| @Override |
| public void openProposalPopup() { |
| // Make this method accessible |
| super.openProposalPopup(); |
| } |
| } |
| |
| private final static class Change { |
| private final String refName; |
| |
| private final Integer changeNumber; |
| |
| private final Integer patchSetNumber; |
| |
| static Change fromRef(String refName) { |
| try { |
| if (!refName.startsWith("refs/changes/")) //$NON-NLS-1$ |
| return null; |
| String[] tokens = refName.substring(13).split("/"); //$NON-NLS-1$ |
| if (tokens.length != 3) |
| return null; |
| Integer changeNumber = Integer.valueOf(tokens[1]); |
| Integer patchSetNumber = Integer.valueOf(tokens[2]); |
| return new Change(refName, changeNumber, patchSetNumber); |
| } catch (NumberFormatException e) { |
| // if we can't parse this, just return null |
| return null; |
| } catch (IndexOutOfBoundsException e) { |
| // if we can't parse this, just return null |
| return null; |
| } |
| } |
| |
| private Change(String refName, Integer changeNumber, |
| Integer patchSetNumber) { |
| this.refName = refName; |
| this.changeNumber = changeNumber; |
| this.patchSetNumber = patchSetNumber; |
| } |
| |
| public String getRefName() { |
| return refName; |
| } |
| |
| public Integer getChangeNumber() { |
| return changeNumber; |
| } |
| |
| public Integer getPatchSetNumber() { |
| return patchSetNumber; |
| } |
| |
| /* (non-Javadoc) |
| * @see java.lang.Object#toString() |
| */ |
| @Override |
| public String toString() { |
| return refName; |
| } |
| } |
| |
| private final static class ChangeContentProposal implements |
| IContentProposal { |
| private final Change myChange; |
| |
| ChangeContentProposal(Change change) { |
| myChange = change; |
| } |
| |
| @Override |
| public String getContent() { |
| return myChange.getRefName(); |
| } |
| |
| @Override |
| public int getCursorPosition() { |
| return 0; |
| } |
| |
| @Override |
| public String getDescription() { |
| return NLS.bind( |
| UIText.FetchGerritChangePage_ContentAssistDescription, |
| myChange.getPatchSetNumber(), myChange.getChangeNumber()); |
| } |
| |
| @Override |
| public String getLabel() { |
| return NLS |
| .bind("{0} - {1}", myChange.getChangeNumber(), myChange.getPatchSetNumber()); //$NON-NLS-1$ |
| } |
| |
| /* (non-Javadoc) |
| * @see java.lang.Object#toString() |
| */ |
| @Override |
| public String toString() { |
| return getContent(); |
| } |
| } |
| } |