1 /*******************************************************************************
2 * Copyright (c) 2010, 2014 SAP AG 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
9 * Mathias Kinzler (SAP AG) - initial implementation
10 * Dariusz Luksza <dariusz@luksza.org>
11 * Steffen Pingel (Tasktop Technologies) - fixes for bug 352253
12 *******************************************************************************/
13 package org
.eclipse
.egit
.ui
.internal
.repository
;
15 import java
.io
.IOException
;
16 import java
.util
.concurrent
.atomic
.AtomicReference
;
18 import org
.eclipse
.core
.runtime
.CoreException
;
19 import org
.eclipse
.core
.runtime
.IConfigurationElement
;
20 import org
.eclipse
.core
.runtime
.IExtensionRegistry
;
21 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
22 import org
.eclipse
.core
.runtime
.Platform
;
23 import org
.eclipse
.core
.runtime
.SafeRunner
;
24 import org
.eclipse
.egit
.core
.Activator
;
25 import org
.eclipse
.egit
.core
.op
.CreateLocalBranchOperation
;
26 import org
.eclipse
.egit
.core
.op
.CreateLocalBranchOperation
.UpstreamConfig
;
27 import org
.eclipse
.egit
.ui
.IBranchNameProvider
;
28 import org
.eclipse
.egit
.ui
.UIUtils
;
29 import org
.eclipse
.egit
.ui
.internal
.UIIcons
;
30 import org
.eclipse
.egit
.ui
.internal
.UIText
;
31 import org
.eclipse
.egit
.ui
.internal
.ValidationUtils
;
32 import org
.eclipse
.egit
.ui
.internal
.branch
.BranchOperationUI
;
33 import org
.eclipse
.egit
.ui
.internal
.components
.UpstreamConfigComponent
;
34 import org
.eclipse
.egit
.ui
.internal
.components
.UpstreamConfigComponent
.UpstreamConfigSelectionListener
;
35 import org
.eclipse
.egit
.ui
.internal
.dialogs
.AbstractBranchSelectionDialog
;
36 import org
.eclipse
.jface
.dialogs
.Dialog
;
37 import org
.eclipse
.jface
.dialogs
.IInputValidator
;
38 import org
.eclipse
.jface
.dialogs
.IMessageProvider
;
39 import org
.eclipse
.jface
.layout
.GridDataFactory
;
40 import org
.eclipse
.jface
.resource
.JFaceResources
;
41 import org
.eclipse
.jface
.resource
.LocalResourceManager
;
42 import org
.eclipse
.jface
.util
.SafeRunnable
;
43 import org
.eclipse
.jface
.window
.Window
;
44 import org
.eclipse
.jface
.wizard
.WizardPage
;
45 import org
.eclipse
.jgit
.lib
.Constants
;
46 import org
.eclipse
.jgit
.lib
.Ref
;
47 import org
.eclipse
.jgit
.lib
.Repository
;
48 import org
.eclipse
.jgit
.revwalk
.RevCommit
;
49 import org
.eclipse
.swt
.SWT
;
50 import org
.eclipse
.swt
.custom
.StyledText
;
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
.events
.TraverseEvent
;
56 import org
.eclipse
.swt
.events
.TraverseListener
;
57 import org
.eclipse
.swt
.graphics
.Point
;
58 import org
.eclipse
.swt
.layout
.GridData
;
59 import org
.eclipse
.swt
.layout
.GridLayout
;
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
.Shell
;
64 import org
.eclipse
.swt
.widgets
.Text
;
67 * Allows to create a new local branch based on another branch or commit.
69 * The source can be selected using a branch selection dialog.
71 * The user can select a strategy for configuring "Pull". The default as read
72 * from the repository's autosetupmerge and autosetuprebase configuration is
73 * suggested initially.
75 class CreateBranchPage
extends WizardPage
{
77 private static final String BRANCH_NAME_PROVIDER_ID
= "org.eclipse.egit.ui.branchNameProvider"; //$NON-NLS-1$
80 * Get proposed target branch name for given source branch name
85 public String
getProposedTargetName(String sourceName
) {
86 if (sourceName
== null)
89 if (sourceName
.startsWith(Constants
.R_REMOTES
))
90 return myRepository
.shortenRemoteBranchName(sourceName
);
92 if (sourceName
.startsWith(Constants
.R_TAGS
))
93 return sourceName
.substring(Constants
.R_TAGS
.length()) + "-branch"; //$NON-NLS-1$
95 return ""; //$NON-NLS-1$
98 private final Repository myRepository
;
100 private final IInputValidator myValidator
;
102 private final String myBaseRef
;
104 private final RevCommit myBaseCommit
;
106 private Text nameText
;
109 * Whether the contents of {@code nameText} is a suggestion or was entered by the user.
111 private boolean nameIsSuggestion
;
113 private Button checkout
;
115 private UpstreamConfig upstreamConfig
;
117 private UpstreamConfigComponent upstreamConfigComponent
;
119 private Label sourceIcon
;
121 private StyledText sourceNameLabel
;
123 private String sourceRefName
= ""; //$NON-NLS-1$
125 private final LocalResourceManager resourceManager
= new LocalResourceManager(
126 JFaceResources
.getResources());
129 * Constructs this page.
131 * If a base branch is provided, the drop down will be selected accordingly
136 * the branch or tag to base the new branch on, may be null
138 public CreateBranchPage(Repository repo
, Ref baseRef
) {
139 super(CreateBranchPage
.class.getName());
140 this.myRepository
= repo
;
142 this.myBaseRef
= baseRef
.getName();
144 this.myBaseRef
= null;
145 this.myBaseCommit
= null;
146 this.myValidator
= ValidationUtils
.getRefNameInputValidator(
147 myRepository
, Constants
.R_HEADS
, false);
149 this.upstreamConfig
= UpstreamConfig
.getDefault(repo
, baseRef
.getName());
151 this.upstreamConfig
= UpstreamConfig
.NONE
;
152 setTitle(UIText
.CreateBranchPage_Title
);
153 setMessage(UIText
.CreateBranchPage_ChooseBranchAndNameMessage
);
157 * Constructs this page.
159 * If a base branch is provided, the drop down will be selected accordingly
164 * the commit to base the new branch on, may be null
166 public CreateBranchPage(Repository repo
, RevCommit baseCommit
) {
167 super(CreateBranchPage
.class.getName());
168 this.myRepository
= repo
;
169 this.myBaseRef
= null;
170 this.myBaseCommit
= baseCommit
;
171 this.myValidator
= ValidationUtils
.getRefNameInputValidator(
172 myRepository
, Constants
.R_HEADS
, false);
173 this.upstreamConfig
= UpstreamConfig
.NONE
;
174 setTitle(UIText
.CreateBranchPage_Title
);
175 setMessage(UIText
.CreateBranchPage_ChooseNameMessage
);
179 public void createControl(Composite parent
) {
180 Composite main
= new Composite(parent
, SWT
.NONE
);
181 main
.setLayout(new GridLayout(4, false));
183 Label sourceLabel
= new Label(main
, SWT
.NONE
);
184 sourceLabel
.setText(UIText
.CreateBranchPage_SourceLabel
);
185 sourceLabel
.setToolTipText(UIText
.CreateBranchPage_SourceTooltip
);
187 sourceIcon
= new Label(main
, SWT
.NONE
);
188 sourceIcon
.setImage(UIIcons
.getImage(resourceManager
, UIIcons
.BRANCH
));
189 sourceIcon
.setLayoutData(GridDataFactory
.fillDefaults()
190 .align(SWT
.END
, SWT
.CENTER
).create());
192 sourceNameLabel
= new StyledText(main
, SWT
.NONE
);
193 sourceNameLabel
.setBackground(main
.getBackground());
194 sourceNameLabel
.setEditable(false);
195 sourceNameLabel
.setLayoutData(GridDataFactory
.fillDefaults()
196 .align(SWT
.FILL
, SWT
.CENTER
)
197 .grab(true, false).create());
199 Button selectButton
= new Button(main
, SWT
.NONE
);
200 selectButton
.setText(UIText
.CreateBranchPage_SourceSelectButton
);
201 selectButton
.addSelectionListener(new SelectionAdapter() {
203 public void widgetSelected(SelectionEvent e
) {
207 UIUtils
.setButtonLayoutData(selectButton
);
209 Label nameLabel
= new Label(main
, SWT
.NONE
);
210 nameLabel
.setText(UIText
.CreateBranchPage_BranchNameLabel
);
211 nameLabel
.setLayoutData(GridDataFactory
.fillDefaults().span(1, 1)
212 .align(SWT
.BEGINNING
, SWT
.CENTER
).create());
213 nameLabel
.setToolTipText(UIText
.CreateBranchPage_BranchNameToolTip
);
215 nameText
= new Text(main
, SWT
.BORDER
);
216 // give focus to the nameText if label is activated using the mnemonic
217 nameLabel
.addTraverseListener(new TraverseListener() {
219 public void keyTraversed(TraverseEvent e
) {
224 nameText
.addModifyListener(new ModifyListener() {
226 public void modifyText(ModifyEvent e
) {
227 nameIsSuggestion
= false;
230 // enable testing with SWTBot
231 nameText
.setData("org.eclipse.swtbot.widget.key", "BranchName"); //$NON-NLS-1$ //$NON-NLS-2$
232 GridDataFactory
.fillDefaults().grab(true, false).span(3, 1)
235 upstreamConfigComponent
= new UpstreamConfigComponent(
237 GridDataFactory
.fillDefaults().grab(true, false).span(4, 1)
238 .applyTo(upstreamConfigComponent
.getContainer());
240 upstreamConfigComponent
241 .addUpstreamConfigSelectionListener(new UpstreamConfigSelectionListener() {
243 public void upstreamConfigSelected(
244 UpstreamConfig newUpstreamConfig
) {
245 upstreamConfig
= newUpstreamConfig
;
250 boolean isBare
= myRepository
.isBare();
251 checkout
= new Button(main
, SWT
.CHECK
);
252 checkout
.setText(UIText
.CreateBranchPage_CheckoutButton
);
253 // most of the time, we probably will check this out
254 // unless we have a bare repository which doesn't allow
256 checkout
.setSelection(!isBare
);
257 checkout
.setEnabled(!isBare
);
258 checkout
.setVisible(!isBare
);
259 GridDataFactory
.fillDefaults().grab(true, false).span(3, 1).applyTo(
261 checkout
.addSelectionListener(new SelectionAdapter() {
263 public void widgetSelected(SelectionEvent e
) {
268 Dialog
.applyDialogFont(main
);
271 if (this.myBaseCommit
!= null)
272 setSourceCommit(this.myBaseCommit
);
273 else if (myBaseRef
!= null)
274 setSourceRef(myBaseRef
);
277 // add the listener just now to avoid unneeded checkPage()
278 nameText
.addModifyListener(new ModifyListener() {
280 public void modifyText(ModifyEvent e
) {
287 public void dispose() {
288 resourceManager
.dispose();
291 private void setSourceRef(String refName
) {
292 String shortName
= Repository
.shortenRefName(refName
);
293 sourceNameLabel
.setText(shortName
);
294 if (refName
.startsWith(Constants
.R_HEADS
)
295 || refName
.startsWith(Constants
.R_REMOTES
))
296 sourceIcon
.setImage(UIIcons
.getImage(resourceManager
,
298 else if (refName
.startsWith(Constants
.R_TAGS
))
299 sourceIcon
.setImage(UIIcons
.getImage(resourceManager
, UIIcons
.TAG
));
301 sourceIcon
.setImage(UIIcons
.getImage(resourceManager
,
304 sourceRefName
= refName
;
306 suggestBranchName(refName
);
307 upstreamConfig
= UpstreamConfig
.getDefault(myRepository
, refName
);
311 private void setSourceCommit(RevCommit commit
) {
312 sourceNameLabel
.setText(commit
.abbreviate(7).name());
313 sourceIcon
.setImage(UIIcons
314 .getImage(resourceManager
, UIIcons
.CHANGESET
));
316 sourceRefName
= commit
.name();
318 upstreamConfig
= UpstreamConfig
.NONE
;
322 private void selectSource() {
323 SourceSelectionDialog dialog
= new SourceSelectionDialog(getShell(),
324 myRepository
, sourceRefName
);
325 int result
= dialog
.open();
326 if (result
== Window
.OK
) {
327 String refName
= dialog
.getRefName();
328 setSourceRef(refName
);
333 private void checkPage() {
335 upstreamConfigComponent
.setUpstreamConfig(upstreamConfig
);
337 boolean showUpstreamConfig
= sourceRefName
338 .startsWith(Constants
.R_HEADS
)
339 || sourceRefName
.startsWith(Constants
.R_REMOTES
);
340 Composite container
= upstreamConfigComponent
.getContainer();
341 GridData gd
= (GridData
) container
.getLayoutData();
342 if (gd
.exclude
== showUpstreamConfig
) {
343 gd
.exclude
= !showUpstreamConfig
;
344 container
.setVisible(showUpstreamConfig
);
345 container
.getParent().layout(true);
346 ensurePreferredHeight(getShell());
349 boolean basedOnLocalBranch
= sourceRefName
350 .startsWith(Constants
.R_HEADS
);
351 if (basedOnLocalBranch
&& upstreamConfig
!= UpstreamConfig
.NONE
)
352 setMessage(UIText
.CreateBranchPage_LocalBranchWarningMessage
,
353 IMessageProvider
.INFORMATION
);
355 if (sourceRefName
.length() == 0) {
356 setErrorMessage(UIText
.CreateBranchPage_MissingSourceMessage
);
359 String message
= this.myValidator
.isValid(nameText
.getText());
360 if (message
!= null) {
361 setErrorMessage(message
);
365 setErrorMessage(null);
367 setPageComplete(getErrorMessage() == null
368 && nameText
.getText().length() > 0);
372 public String
getBranchName() {
373 return nameText
.getText();
376 public boolean checkoutNewBranch() {
377 return checkout
.getSelection();
382 * @param checkoutNewBranch
384 * @throws CoreException
385 * @throws IOException
387 public void createBranch(String newRefName
, boolean checkoutNewBranch
,
388 IProgressMonitor monitor
)
389 throws CoreException
,
391 monitor
.beginTask(UIText
.CreateBranchPage_CreatingBranchMessage
,
392 IProgressMonitor
.UNKNOWN
);
394 final CreateLocalBranchOperation cbop
;
396 if (myBaseCommit
!= null
397 && this.sourceRefName
.equals(myBaseCommit
.name()))
398 cbop
= new CreateLocalBranchOperation(myRepository
, newRefName
,
401 cbop
= new CreateLocalBranchOperation(myRepository
, newRefName
,
402 myRepository
.findRef(this.sourceRefName
),
405 cbop
.execute(monitor
);
407 if (checkoutNewBranch
) {
408 if (monitor
.isCanceled())
410 monitor
.beginTask(UIText
.CreateBranchPage_CheckingOutMessage
,
411 IProgressMonitor
.UNKNOWN
);
412 BranchOperationUI
.checkout(myRepository
, Constants
.R_HEADS
+ newRefName
)
417 private void suggestBranchName(String ref
) {
418 if (nameText
.getText().length() == 0 || nameIsSuggestion
) {
419 String branchNameSuggestion
= getBranchNameSuggestionFromProvider();
420 if (branchNameSuggestion
== null)
421 branchNameSuggestion
= getProposedTargetName(ref
);
423 if (branchNameSuggestion
!= null) {
424 nameText
.setText(branchNameSuggestion
);
425 nameText
.selectAll();
426 nameIsSuggestion
= true;
431 private IBranchNameProvider
getBranchNameProvider() {
432 IExtensionRegistry registry
= Platform
.getExtensionRegistry();
433 IConfigurationElement
[] config
= registry
434 .getConfigurationElementsFor(BRANCH_NAME_PROVIDER_ID
);
435 if (config
.length
> 0) {
438 provider
= config
[0].createExecutableExtension("class"); //$NON-NLS-1$
439 if (provider
instanceof IBranchNameProvider
)
440 return (IBranchNameProvider
) provider
;
441 } catch (Throwable e
) {
443 UIText
.CreateBranchPage_CreateBranchNameProviderFailed
,
450 private String
getBranchNameSuggestionFromProvider() {
451 final AtomicReference
<String
> ref
= new AtomicReference
<>();
452 final IBranchNameProvider branchNameProvider
= getBranchNameProvider();
453 if (branchNameProvider
!= null)
454 SafeRunner
.run(new SafeRunnable() {
456 public void run() throws Exception
{
457 ref
.set(branchNameProvider
.getBranchNameSuggestion());
463 private static void ensurePreferredHeight(Shell shell
) {
464 int preferredHeight
= shell
.computeSize(SWT
.DEFAULT
, SWT
.DEFAULT
).y
;
465 Point size
= shell
.getSize();
466 if (size
.y
< preferredHeight
)
467 shell
.setSize(size
.x
, preferredHeight
);
470 private static class SourceSelectionDialog
extends
471 AbstractBranchSelectionDialog
{
473 public SourceSelectionDialog(Shell parentShell
, Repository repository
,
475 super(parentShell
, repository
, refToMark
, SHOW_LOCAL_BRANCHES
476 | SHOW_REMOTE_BRANCHES
| SHOW_TAGS
| SHOW_REFERENCES
477 | SELECT_CURRENT_REF
| EXPAND_LOCAL_BRANCHES_NODE
478 | EXPAND_REMOTE_BRANCHES_NODE
);
482 protected void refNameSelected(String refName
) {
483 setOkButtonEnabled(refName
!= null);
487 protected String
getTitle() {
488 return UIText
.CreateBranchPage_SourceSelectionDialogTitle
;
492 protected String
getMessageText() {
493 return UIText
.CreateBranchPage_SourceSelectionDialogMessage
;