Use try-with-resource to avoid leaks with RevWalk and TreeWalk
[egit/eclipse.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / push / PushBranchPage.java
blob8cce15cf8b158dcb5a1568472bc9c149e8b67c4e
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;
18 import java.util.Set;
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;
67 /**
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>();
105 * Create the page.
107 * @param repository
108 * @param commitToPush
109 * @param ref
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;
119 this.ref = ref;
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
131 * was chosen
133 AddRemotePage getAddRemotePage() {
134 return addRemotePage;
137 RemoteConfig getRemoteConfig() {
138 return remoteConfig;
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();
147 else
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;
163 @Override
164 public void createControl(Composite parent) {
165 try {
166 this.remoteConfigs = RemoteConfig.getAllRemoteConfigs(repository.getConfig());
167 Collections.sort(remoteConfigs, new Comparator<RemoteConfig>() {
169 @Override
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>();
177 handleError(e);
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);
202 branchIconLabel
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
208 .getName()));
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) {
234 commitBuilder
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)
250 .create());
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,
259 SelectionType.PUSH);
260 GridDataFactory.fillDefaults().grab(true, false).span(remoteSelectionSpan, 1)
261 .applyTo(remoteSelectionCombo);
262 setRemoteConfigs();
263 remoteSelectionCombo
264 .addRemoteSelectionListener(new IRemoteSelectionListener() {
265 @Override
266 public void remoteSelected(RemoteConfig rc) {
267 remoteConfig = rc;
268 setRefAssist(rc);
269 checkPage();
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() {
278 @Override
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() {
295 @Override
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,
307 SWT.NONE);
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() {
313 @Override
314 public void upstreamConfigSelected(
315 UpstreamConfig newUpstreamConfig) {
316 upstreamConfig = newUpstreamConfig;
317 checkPage();
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() {
329 @Override
330 public void widgetSelected(SelectionEvent e) {
331 forceUpdateSelected = forceUpdateButton.getSelection();
335 Link advancedDialogLink = new Link(main, SWT.NONE);
336 advancedDialogLink.setText(UIText.PushBranchPage_advancedWizardLink);
337 advancedDialogLink
338 .setToolTipText(UIText.PushBranchPage_advancedWizardLinkTooltip);
339 advancedDialogLink.setLayoutData(new GridData(SWT.END, SWT.END, false,
340 true));
341 advancedDialogLink.addSelectionListener(new SelectionAdapter() {
342 @Override
343 public void widgetSelected(SelectionEvent e) {
344 Shell parentShell = getShell().getParent().getShell();
345 PushWizard advancedWizard = null;
346 try {
347 advancedWizard = new PushWizard(repository);
348 getShell().close();
349 new WizardDialog(parentShell, advancedWizard).open();
350 } catch (URISyntaxException ex) {
351 Activator.logError(ex.getMessage(), ex);
357 setControl(main);
359 checkPage();
361 // Add listener now to avoid setText above to already trigger it.
362 remoteBranchNameText.addModifyListener(new ModifyListener() {
363 @Override
364 public void modifyText(ModifyEvent e) {
365 checkPage();
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;
399 } else {
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() {
421 try {
422 if (remoteConfig == null) {
423 setErrorMessage(UIText.PushBranchPage_ChooseRemoteError);
424 return;
426 String branchName = remoteBranchNameText.getText();
427 if (branchName.length() == 0) {
428 setErrorMessage(UIText.PushBranchPage_ChooseBranchNameError);
429 return;
431 if (!Repository.isValidRefName(Constants.R_HEADS + branchName)) {
432 setErrorMessage(UIText.PushBranchPage_InvalidBranchNameError);
433 return;
435 if (isConfigureUpstreamSelected()
436 && hasDifferentUpstreamConfiguration()) {
437 setMessage(
438 UIText.PushBranchPage_UpstreamConfigOverwriteWarning,
439 IMessageProvider.WARNING);
440 } else {
441 setMessage(UIText.PushBranchPage_PageMessage);
443 setErrorMessage(null);
444 } finally {
445 setPageComplete(getErrorMessage() == null);
449 void setSelectedRemote(String remoteName, URIish urIish) {
450 try {
451 RemoteConfig config = new RemoteConfig(repository.getConfig(),
452 remoteName);
453 config.addURI(urIish);
454 remoteSelectionCombo.setItems(Arrays.asList(config));
455 this.remoteConfig = config;
456 remoteSelectionCombo.setEnabled(false);
457 setRefAssist(this.remoteConfig);
458 checkPage();
459 } catch (URISyntaxException e) {
460 handleError(e);
464 private String getSuggestedBranchName() {
465 if (ref != null) {
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);
475 return branchName;
476 } else {
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),
485 getShell());
489 private boolean hasDifferentUpstreamConfiguration() {
490 String branchName = Repository.shortenRefName(ref.getName());
491 BranchConfig branchConfig = new BranchConfig(repository.getConfig(),
492 branchName);
494 String remote = branchConfig.getRemote();
495 // No upstream config -> don't show warning
496 if (remote == null)
497 return false;
498 if (!remote.equals(remoteConfig.getName()))
499 return true;
501 String merge = branchConfig.getMerge();
502 if (merge == null || !merge.equals(getFullRemoteReference()))
503 return true;
505 boolean rebase = branchConfig.isRebase();
506 if (rebase != isRebaseSelected())
507 return true;
509 return false;
512 private void handleError(URISyntaxException e) {
513 Activator.handleError(e.getMessage(), e, false);
514 setErrorMessage(e.getMessage());
517 @Override
518 public void dispose() {
519 super.dispose();
520 for (Resource disposable : this.disposables)
521 disposable.dispose();