1 /*******************************************************************************
2 * Copyright (c) 2013, 2014 Robin Stocker <robin@nibor.org> and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *******************************************************************************/
8 package org
.eclipse
.egit
.ui
.internal
.push
;
10 import java
.io
.IOException
;
11 import java
.net
.URISyntaxException
;
12 import java
.util
.ArrayList
;
13 import java
.util
.Arrays
;
14 import java
.util
.Collections
;
15 import java
.util
.Comparator
;
16 import java
.util
.HashSet
;
17 import java
.util
.List
;
20 import org
.eclipse
.egit
.core
.internal
.Utils
;
21 import org
.eclipse
.egit
.core
.op
.CreateLocalBranchOperation
.UpstreamConfig
;
22 import org
.eclipse
.egit
.ui
.Activator
;
23 import org
.eclipse
.egit
.ui
.UIUtils
;
24 import org
.eclipse
.egit
.ui
.UIUtils
.IRefListProvider
;
25 import org
.eclipse
.egit
.ui
.internal
.UIIcons
;
26 import org
.eclipse
.egit
.ui
.internal
.UIText
;
27 import org
.eclipse
.egit
.ui
.internal
.components
.RefContentAssistProvider
;
28 import org
.eclipse
.egit
.ui
.internal
.components
.RemoteSelectionCombo
;
29 import org
.eclipse
.egit
.ui
.internal
.components
.RemoteSelectionCombo
.IRemoteSelectionListener
;
30 import org
.eclipse
.egit
.ui
.internal
.components
.RemoteSelectionCombo
.SelectionType
;
31 import org
.eclipse
.egit
.ui
.internal
.components
.UpstreamConfigComponent
;
32 import org
.eclipse
.egit
.ui
.internal
.components
.UpstreamConfigComponent
.UpstreamConfigSelectionListener
;
33 import org
.eclipse
.jface
.dialogs
.IMessageProvider
;
34 import org
.eclipse
.jface
.layout
.GridDataFactory
;
35 import org
.eclipse
.jface
.layout
.GridLayoutFactory
;
36 import org
.eclipse
.jface
.layout
.RowLayoutFactory
;
37 import org
.eclipse
.jface
.window
.Window
;
38 import org
.eclipse
.jface
.wizard
.WizardDialog
;
39 import org
.eclipse
.jface
.wizard
.WizardPage
;
40 import org
.eclipse
.jgit
.lib
.BranchConfig
;
41 import org
.eclipse
.jgit
.lib
.Constants
;
42 import org
.eclipse
.jgit
.lib
.ObjectId
;
43 import org
.eclipse
.jgit
.lib
.Ref
;
44 import org
.eclipse
.jgit
.lib
.Repository
;
45 import org
.eclipse
.jgit
.lib
.StoredConfig
;
46 import org
.eclipse
.jgit
.revwalk
.RevCommit
;
47 import org
.eclipse
.jgit
.revwalk
.RevWalk
;
48 import org
.eclipse
.jgit
.transport
.RemoteConfig
;
49 import org
.eclipse
.jgit
.transport
.URIish
;
50 import org
.eclipse
.swt
.SWT
;
51 import org
.eclipse
.swt
.events
.ModifyEvent
;
52 import org
.eclipse
.swt
.events
.ModifyListener
;
53 import org
.eclipse
.swt
.events
.SelectionAdapter
;
54 import org
.eclipse
.swt
.events
.SelectionEvent
;
55 import org
.eclipse
.swt
.graphics
.Image
;
56 import org
.eclipse
.swt
.graphics
.Resource
;
57 import org
.eclipse
.swt
.layout
.GridData
;
58 import org
.eclipse
.swt
.layout
.RowData
;
59 import org
.eclipse
.swt
.layout
.RowLayout
;
60 import org
.eclipse
.swt
.widgets
.Button
;
61 import org
.eclipse
.swt
.widgets
.Composite
;
62 import org
.eclipse
.swt
.widgets
.Label
;
63 import org
.eclipse
.swt
.widgets
.Link
;
64 import org
.eclipse
.swt
.widgets
.Shell
;
65 import org
.eclipse
.swt
.widgets
.Text
;
68 * Page that is part of the "Push Branch..." wizard, where the user selects the
69 * remote, the branch name and the upstream config.
71 public class PushBranchPage
extends WizardPage
{
73 private static final int MAX_SHORTCOMMIT_MESSAGE_LENGTH
= 65;
75 private final Repository repository
;
77 private final ObjectId commitToPush
;
79 private final Ref ref
;
81 private boolean showNewRemoteButton
= true;
83 private RemoteConfig remoteConfig
;
85 private List
<RemoteConfig
> remoteConfigs
;
87 private RemoteSelectionCombo remoteSelectionCombo
;
89 private Text remoteBranchNameText
;
91 private RefContentAssistProvider assist
;
93 private UpstreamConfig upstreamConfig
= UpstreamConfig
.NONE
;
95 private UpstreamConfigComponent upstreamConfigComponent
;
97 private boolean forceUpdateSelected
= false;
99 /** Only set if user selected "New Remote" */
100 private AddRemotePage addRemotePage
;
102 private Set
<Resource
> disposables
= new HashSet
<Resource
>();
108 * @param commitToPush
110 * An optional ref to give hints
112 public PushBranchPage(Repository repository
, ObjectId commitToPush
, Ref ref
) {
113 super(UIText
.PushBranchPage_PageName
);
114 setTitle(UIText
.PushBranchPage_PageTitle
);
115 setMessage(UIText
.PushBranchPage_PageMessage
);
117 this.repository
= repository
;
118 this.commitToPush
= commitToPush
;
123 * @param showNewRemoteButton
125 public void setShowNewRemoteButton(boolean showNewRemoteButton
) {
126 this.showNewRemoteButton
= showNewRemoteButton
;
130 * @return the page used to add a new remote, or null if an existing remote
133 AddRemotePage
getAddRemotePage() {
134 return addRemotePage
;
137 RemoteConfig
getRemoteConfig() {
142 * @return the chosen short name of the branch on the remote
144 String
getFullRemoteReference() {
145 if (!remoteBranchNameText
.getText().startsWith(Constants
.R_REFS
))
146 return Constants
.R_HEADS
+ remoteBranchNameText
.getText();
148 return remoteBranchNameText
.getText();
151 boolean isConfigureUpstreamSelected() {
152 return upstreamConfig
!= UpstreamConfig
.NONE
;
155 boolean isRebaseSelected() {
156 return upstreamConfig
== UpstreamConfig
.REBASE
;
159 boolean isForceUpdateSelected() {
160 return forceUpdateSelected
;
164 public void createControl(Composite parent
) {
166 this.remoteConfigs
= RemoteConfig
.getAllRemoteConfigs(repository
.getConfig());
167 Collections
.sort(remoteConfigs
, new Comparator
<RemoteConfig
>() {
170 public int compare(RemoteConfig first
, RemoteConfig second
) {
171 return String
.CASE_INSENSITIVE_ORDER
.compare(
172 first
.getName(), second
.getName());
175 } catch (URISyntaxException e
) {
176 this.remoteConfigs
= new ArrayList
<RemoteConfig
>();
180 Composite main
= new Composite(parent
, SWT
.NONE
);
181 main
.setLayout(GridLayoutFactory
.swtDefaults().create());
183 Composite inputPanel
= new Composite(main
, SWT
.NONE
);
184 GridDataFactory
.fillDefaults().grab(true, false).applyTo(inputPanel
);
185 GridLayoutFactory
.fillDefaults().numColumns(1).applyTo(inputPanel
);
187 Label sourceLabel
= new Label(inputPanel
, SWT
.NONE
);
188 sourceLabel
.setText(UIText
.PushBranchPage_Source
);
190 Composite sourceComposite
= new Composite(inputPanel
, SWT
.NONE
);
191 sourceComposite
.setLayoutData(GridDataFactory
.fillDefaults()
192 .indent(UIUtils
.getControlIndent(), 0).create());
193 RowLayout rowLayout
= RowLayoutFactory
.fillDefaults().create();
194 rowLayout
.center
= true;
195 sourceComposite
.setLayout(rowLayout
);
197 if (this.ref
!= null) {
198 Image branchIcon
= UIIcons
.BRANCH
.createImage();
199 this.disposables
.add(branchIcon
);
201 Label branchIconLabel
= new Label(sourceComposite
, SWT
.NONE
);
203 .setLayoutData(new RowData(branchIcon
.getBounds().width
,
204 branchIcon
.getBounds().height
));
205 branchIconLabel
.setImage(branchIcon
);
206 Label localBranchLabel
= new Label(sourceComposite
, SWT
.NONE
);
207 localBranchLabel
.setText(Repository
.shortenRefName(this.ref
210 Label spacer
= new Label(sourceComposite
, SWT
.NONE
);
211 spacer
.setLayoutData(new RowData(3, SWT
.DEFAULT
));
214 Image commitIcon
= UIIcons
.CHANGESET
.createImage();
215 this.disposables
.add(commitIcon
);
216 Label commitIconLabel
= new Label(sourceComposite
, SWT
.NONE
);
217 commitIconLabel
.setImage(commitIcon
);
218 commitIconLabel
.setLayoutData(new RowData(commitIcon
.getBounds().width
,
219 commitIcon
.getBounds().height
));
221 Label commit
= new Label(sourceComposite
, SWT
.NONE
);
222 StringBuilder commitBuilder
= new StringBuilder(this.commitToPush
223 .abbreviate(7).name());
224 StringBuilder commitTooltipBuilder
= new StringBuilder(
225 this.commitToPush
.getName());
226 try (RevWalk revWalk
= new RevWalk(repository
)) {
227 RevCommit revCommit
= revWalk
.parseCommit(this.commitToPush
);
228 commitBuilder
.append(" "); //$NON-NLS-1$
229 commitBuilder
.append(Utils
.shortenText(revCommit
.getShortMessage(),
230 MAX_SHORTCOMMIT_MESSAGE_LENGTH
));
231 commitTooltipBuilder
.append("\n\n"); //$NON-NLS-1$
232 commitTooltipBuilder
.append(revCommit
.getFullMessage());
233 } catch (IOException ex
) {
235 .append(UIText
.PushBranchPage_CannotAccessCommitDescription
);
236 commitTooltipBuilder
.append(ex
.getMessage());
237 Activator
.handleError(ex
.getLocalizedMessage(), ex
, false);
239 commit
.setText(commitBuilder
.toString());
240 commit
.setToolTipText(commitTooltipBuilder
.toString());
242 Label destinationLabel
= new Label(inputPanel
, SWT
.NONE
);
243 destinationLabel
.setText(UIText
.PushBranchPage_Destination
);
244 GridDataFactory
.fillDefaults().applyTo(destinationLabel
);
246 Composite remoteGroup
= new Composite(inputPanel
, SWT
.NONE
);
247 remoteGroup
.setLayoutData(GridDataFactory
.fillDefaults()
248 .indent(UIUtils
.getControlIndent(), 0).create());
249 remoteGroup
.setLayout(GridLayoutFactory
.fillDefaults().numColumns(3)
252 Label remoteLabel
= new Label(remoteGroup
, SWT
.NONE
);
253 remoteLabel
.setText(UIText
.PushBranchPage_RemoteLabel
);
255 // Use full width in case "New Remote..." button is not shown
256 int remoteSelectionSpan
= showNewRemoteButton ?
1 : 2;
258 remoteSelectionCombo
= new RemoteSelectionCombo(remoteGroup
, SWT
.NONE
,
260 GridDataFactory
.fillDefaults().grab(true, false).span(remoteSelectionSpan
, 1)
261 .applyTo(remoteSelectionCombo
);
264 .addRemoteSelectionListener(new IRemoteSelectionListener() {
266 public void remoteSelected(RemoteConfig rc
) {
273 if (showNewRemoteButton
) {
274 Button newRemoteButton
= new Button(remoteGroup
, SWT
.PUSH
);
275 newRemoteButton
.setText(UIText
.PushBranchPage_NewRemoteButton
);
276 GridDataFactory
.fillDefaults().applyTo(newRemoteButton
);
277 newRemoteButton
.addSelectionListener(new SelectionAdapter() {
279 public void widgetSelected(SelectionEvent e
) {
280 showNewRemoteDialog();
285 Label branchNameLabel
= new Label(remoteGroup
, SWT
.NONE
);
286 branchNameLabel
.setText(UIText
.PushBranchPage_RemoteBranchNameLabel
);
288 remoteBranchNameText
= new Text(remoteGroup
, SWT
.BORDER
);
289 GridDataFactory
.fillDefaults().grab(true, false).span(2, 1)
290 .applyTo(remoteBranchNameText
);
291 remoteBranchNameText
.setText(getSuggestedBranchName());
292 UIUtils
.addRefContentProposalToText(remoteBranchNameText
,
293 this.repository
, new IRefListProvider() {
296 public List
<Ref
> getRefList() {
297 if (PushBranchPage
.this.assist
!= null) {
298 return PushBranchPage
.this.assist
299 .getRefsForContentAssist(false, true);
301 return Collections
.emptyList();
305 if (this.ref
!= null) {
306 upstreamConfigComponent
= new UpstreamConfigComponent(inputPanel
,
308 upstreamConfigComponent
.getContainer().setLayoutData(
309 GridDataFactory
.fillDefaults().grab(true, false).span(3, 1)
310 .indent(SWT
.DEFAULT
, 20).create());
311 upstreamConfigComponent
312 .addUpstreamConfigSelectionListener(new UpstreamConfigSelectionListener() {
314 public void upstreamConfigSelected(
315 UpstreamConfig newUpstreamConfig
) {
316 upstreamConfig
= newUpstreamConfig
;
320 setDefaultUpstreamConfig();
323 final Button forceUpdateButton
= new Button(inputPanel
, SWT
.CHECK
);
324 forceUpdateButton
.setText(UIText
.PushBranchPage_ForceUpdateButton
);
325 forceUpdateButton
.setSelection(false);
326 forceUpdateButton
.setLayoutData(GridDataFactory
.fillDefaults()
327 .grab(true, false).span(3, 1).create());
328 forceUpdateButton
.addSelectionListener(new SelectionAdapter() {
330 public void widgetSelected(SelectionEvent e
) {
331 forceUpdateSelected
= forceUpdateButton
.getSelection();
335 Link advancedDialogLink
= new Link(main
, SWT
.NONE
);
336 advancedDialogLink
.setText(UIText
.PushBranchPage_advancedWizardLink
);
338 .setToolTipText(UIText
.PushBranchPage_advancedWizardLinkTooltip
);
339 advancedDialogLink
.setLayoutData(new GridData(SWT
.END
, SWT
.END
, false,
341 advancedDialogLink
.addSelectionListener(new SelectionAdapter() {
343 public void widgetSelected(SelectionEvent e
) {
344 Shell parentShell
= getShell().getParent().getShell();
345 PushWizard advancedWizard
= null;
347 advancedWizard
= new PushWizard(repository
);
349 new WizardDialog(parentShell
, advancedWizard
).open();
350 } catch (URISyntaxException ex
) {
351 Activator
.logError(ex
.getMessage(), ex
);
361 // Add listener now to avoid setText above to already trigger it.
362 remoteBranchNameText
.addModifyListener(new ModifyListener() {
364 public void modifyText(ModifyEvent e
) {
370 private void setRemoteConfigs() {
371 remoteSelectionCombo
.setItems(remoteConfigs
);
372 if (this.ref
!= null) {
373 String branchName
= Repository
.shortenRefName(this.ref
.getName());
374 BranchConfig branchConfig
= new BranchConfig(
375 repository
.getConfig(), branchName
);
376 String remoteName
= branchConfig
.getRemote();
377 if (remoteName
!= null) {
378 for (RemoteConfig rc
: remoteConfigs
) {
379 if (remoteName
.equals(rc
.getName()))
380 remoteSelectionCombo
.setSelectedRemote(rc
);
385 remoteConfig
= remoteSelectionCombo
.getSelectedRemote();
386 setRefAssist(remoteConfig
);
389 private void setDefaultUpstreamConfig() {
390 if (this.ref
!= null) {
391 String branchName
= Repository
.shortenRefName(ref
.getName());
392 BranchConfig branchConfig
= new BranchConfig(
393 repository
.getConfig(), branchName
);
394 boolean alreadyConfigured
= branchConfig
.getMerge() != null;
395 UpstreamConfig config
;
396 if (alreadyConfigured
) {
397 boolean rebase
= branchConfig
.isRebase();
398 config
= rebase ? UpstreamConfig
.REBASE
: UpstreamConfig
.MERGE
;
400 config
= UpstreamConfig
.getDefault(repository
, Constants
.R_REMOTES
401 + Constants
.DEFAULT_REMOTE_NAME
+ "/" + branchName
); //$NON-NLS-1$
403 this.upstreamConfig
= config
;
404 this.upstreamConfigComponent
.setUpstreamConfig(this.upstreamConfig
);
408 private void showNewRemoteDialog() {
409 AddRemoteWizard wizard
= new AddRemoteWizard(repository
);
410 WizardDialog dialog
= new WizardDialog(getShell(), wizard
);
411 int result
= dialog
.open();
412 if (result
== Window
.OK
) {
413 URIish uri
= wizard
.getUri();
414 String remoteName
= wizard
.getRemoteName();
415 addRemotePage
= wizard
.getAddRemotePage();
416 setSelectedRemote(remoteName
, uri
);
420 private void checkPage() {
422 if (remoteConfig
== null) {
423 setErrorMessage(UIText
.PushBranchPage_ChooseRemoteError
);
426 String branchName
= remoteBranchNameText
.getText();
427 if (branchName
.length() == 0) {
428 setErrorMessage(UIText
.PushBranchPage_ChooseBranchNameError
);
431 if (!Repository
.isValidRefName(Constants
.R_HEADS
+ branchName
)) {
432 setErrorMessage(UIText
.PushBranchPage_InvalidBranchNameError
);
435 if (isConfigureUpstreamSelected()
436 && hasDifferentUpstreamConfiguration()) {
438 UIText
.PushBranchPage_UpstreamConfigOverwriteWarning
,
439 IMessageProvider
.WARNING
);
441 setMessage(UIText
.PushBranchPage_PageMessage
);
443 setErrorMessage(null);
445 setPageComplete(getErrorMessage() == null);
449 void setSelectedRemote(String remoteName
, URIish urIish
) {
451 RemoteConfig config
= new RemoteConfig(repository
.getConfig(),
453 config
.addURI(urIish
);
454 remoteSelectionCombo
.setItems(Arrays
.asList(config
));
455 this.remoteConfig
= config
;
456 remoteSelectionCombo
.setEnabled(false);
457 setRefAssist(this.remoteConfig
);
459 } catch (URISyntaxException e
) {
464 private String
getSuggestedBranchName() {
466 StoredConfig config
= repository
.getConfig();
467 String branchName
= Repository
.shortenRefName(ref
.getName());
469 BranchConfig branchConfig
= new BranchConfig(config
, branchName
);
470 String merge
= branchConfig
.getMerge();
471 if (!branchConfig
.isRemoteLocal() && merge
!= null
472 && merge
.startsWith(Constants
.R_HEADS
))
473 return Repository
.shortenRefName(merge
);
477 return ""; //$NON-NLS-1$
481 private void setRefAssist(RemoteConfig config
) {
482 if (config
!= null && config
.getURIs().size() > 0) {
483 this.assist
= new RefContentAssistProvider(
484 PushBranchPage
.this.repository
, config
.getURIs().get(0),
489 private boolean hasDifferentUpstreamConfiguration() {
490 String branchName
= Repository
.shortenRefName(ref
.getName());
491 BranchConfig branchConfig
= new BranchConfig(repository
.getConfig(),
494 String remote
= branchConfig
.getRemote();
495 // No upstream config -> don't show warning
498 if (!remote
.equals(remoteConfig
.getName()))
501 String merge
= branchConfig
.getMerge();
502 if (merge
== null || !merge
.equals(getFullRemoteReference()))
505 boolean rebase
= branchConfig
.isRebase();
506 if (rebase
!= isRebaseSelected())
512 private void handleError(URISyntaxException e
) {
513 Activator
.handleError(e
.getMessage(), e
, false);
514 setErrorMessage(e
.getMessage());
518 public void dispose() {
520 for (Resource disposable
: this.disposables
)
521 disposable
.dispose();