Add action to trigger (fast-forward) merge
[egit.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / dialogs / BranchSelectionDialog.java
blob032cc3b7070bc9ad17c6cfea17af865d0fcdf963
1 /*******************************************************************************
2 * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3 * Copyright (C) 2007, Robin Rosenberg <me@lathund.dewire.com.dewire.com>
4 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
5 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
7 * All rights reserved. This program and the accompanying materials
8 * are made available under the terms of the Eclipse Public License v1.0
9 * which accompanies this distribution, and is available at
10 * http://www.eclipse.org/legal/epl-v10.html
11 *******************************************************************************/
12 package org.eclipse.egit.ui.internal.dialogs;
14 import java.io.IOException;
15 import java.util.ArrayList;
16 import java.util.List;
18 import org.eclipse.egit.ui.Activator;
19 import org.eclipse.egit.ui.UIText;
20 import org.eclipse.egit.ui.internal.ValidationUtils;
21 import org.eclipse.egit.ui.internal.repository.RepositoriesViewContentProvider;
22 import org.eclipse.egit.ui.internal.repository.RepositoriesViewLabelProvider;
23 import org.eclipse.egit.ui.internal.repository.RepositoryTreeNode;
24 import org.eclipse.egit.ui.internal.repository.RepositoryTreeNode.RepositoryTreeNodeType;
25 import org.eclipse.jface.dialogs.Dialog;
26 import org.eclipse.jface.dialogs.IDialogConstants;
27 import org.eclipse.jface.dialogs.InputDialog;
28 import org.eclipse.jface.layout.GridDataFactory;
29 import org.eclipse.jface.layout.GridLayoutFactory;
30 import org.eclipse.jface.resource.JFaceResources;
31 import org.eclipse.jface.viewers.IOpenListener;
32 import org.eclipse.jface.viewers.ISelectionChangedListener;
33 import org.eclipse.jface.viewers.IStructuredSelection;
34 import org.eclipse.jface.viewers.OpenEvent;
35 import org.eclipse.jface.viewers.SelectionChangedEvent;
36 import org.eclipse.jface.viewers.StructuredSelection;
37 import org.eclipse.jface.viewers.TreeViewer;
38 import org.eclipse.jface.window.Window;
39 import org.eclipse.jgit.lib.Constants;
40 import org.eclipse.jgit.lib.ObjectId;
41 import org.eclipse.jgit.lib.Ref;
42 import org.eclipse.jgit.lib.RefRename;
43 import org.eclipse.jgit.lib.RefUpdate;
44 import org.eclipse.jgit.lib.Repository;
45 import org.eclipse.jgit.lib.RefUpdate.Result;
46 import org.eclipse.osgi.util.NLS;
47 import org.eclipse.swt.SWT;
48 import org.eclipse.swt.events.SelectionAdapter;
49 import org.eclipse.swt.events.SelectionEvent;
50 import org.eclipse.swt.layout.GridLayout;
51 import org.eclipse.swt.widgets.Button;
52 import org.eclipse.swt.widgets.Composite;
53 import org.eclipse.swt.widgets.Label;
54 import org.eclipse.swt.widgets.Shell;
56 /**
57 * The branch and reset selection dialog
60 public class BranchSelectionDialog extends Dialog {
62 private final Repository repo;
64 private TreeViewer branchTree;
66 /**
67 * button which finally triggers the action
69 protected Button confirmationBtn;
71 private Button renameButton;
73 private Button newButton;
75 private String selectedBranch;
77 private final RepositoryTreeNode<Repository> localBranches;
79 private final RepositoryTreeNode<Repository> remoteBranches;
81 private final RepositoryTreeNode<Repository> tags;
83 /**
84 * Construct a dialog to select a branch to reset to or check out
85 * @param parentShell
86 * @param repo
88 public BranchSelectionDialog(Shell parentShell, Repository repo) {
89 super(parentShell);
90 this.repo = repo;
91 localBranches = new RepositoryTreeNode<Repository>(null,
92 RepositoryTreeNodeType.LOCALBRANCHES, this.repo, this.repo);
93 remoteBranches = new RepositoryTreeNode<Repository>(null,
94 RepositoryTreeNodeType.REMOTEBRANCHES, this.repo, this.repo);
95 tags = new RepositoryTreeNode<Repository>(null,
96 RepositoryTreeNodeType.TAGS, this.repo, this.repo);
99 @Override
100 protected Composite createDialogArea(Composite base) {
101 Composite parent = (Composite) super.createDialogArea(base);
102 parent.setLayout(GridLayoutFactory.swtDefaults().create());
103 new Label(parent, SWT.NONE).setText(getRefsLabel());
105 branchTree = new TreeViewer(parent, SWT.SINGLE | SWT.BORDER);
106 new RepositoriesViewLabelProvider(branchTree);
107 branchTree.setContentProvider(new RepositoriesViewContentProvider());
109 GridDataFactory.fillDefaults().grab(true, true).hint(500, 300).applyTo(
110 branchTree.getTree());
111 branchTree.addSelectionChangedListener(new ISelectionChangedListener() {
113 public void selectionChanged(SelectionChangedEvent event) {
114 // enable the buttons depending on the selection
116 String refName = refNameFromDialog();
118 boolean tagSelected = refName != null
119 && refName.startsWith(Constants.R_TAGS);
121 boolean branchSelected = refName != null
122 && (refName.startsWith(Constants.R_HEADS) || refName
123 .startsWith(Constants.R_REMOTES));
125 // we don't allow reset on tags, but checkout
126 if (!canConfirmOnTag())
127 confirmationBtn.setEnabled(branchSelected);
128 else
129 confirmationBtn.setEnabled(branchSelected || tagSelected);
131 // we don't support rename on tags
132 if (renameButton != null) {
133 renameButton.setEnabled(branchSelected && !tagSelected
134 && !tagSelected);
137 // new branch can not be based on a tag
138 if (newButton != null) {
139 newButton.setEnabled(branchSelected && !tagSelected);
144 // double-click support
145 branchTree.addOpenListener(new IOpenListener() {
147 public void open(OpenEvent event) {
148 RepositoryTreeNode node = (RepositoryTreeNode) ((IStructuredSelection) branchTree
149 .getSelection()).getFirstElement();
150 if (node.getType() != RepositoryTreeNodeType.REF)
151 branchTree.setExpandedState(node, !branchTree
152 .getExpandedState(node));
153 else if (confirmationBtn.isEnabled())
154 okPressed();
159 createCustomArea(parent);
161 String rawTitle = getTitle();
163 getShell().setText(
164 NLS.bind(rawTitle, new Object[] { repo.getDirectory() }));
166 return parent;
169 @Override
170 public void create() {
171 super.create();
173 List<RepositoryTreeNode> roots = new ArrayList<RepositoryTreeNode>();
174 roots.add(localBranches);
175 roots.add(remoteBranches);
176 roots.add(tags);
178 branchTree.setInput(roots);
180 try {
181 // initially, we mark the current head if it can be determined
182 String fullBranch = repo.getFullBranch();
183 if (!markRef(fullBranch))
184 // if we can't determine a branch, we just expand local branches
185 branchTree.expandToLevel(localBranches, 1);
186 } catch (IOException e) {
187 // ignore
191 private boolean markRef(String refName) {
192 // selects the entry specified by the name
193 if (refName == null)
194 return false;
195 Ref actRef;
196 try {
197 actRef = repo.getRef(refName);
198 } catch (IOException e) {
199 // ignore
200 return false;
203 if (actRef == null)
204 return false;
206 RepositoryTreeNode<Repository> parentNode;
207 if (refName.startsWith(Constants.R_HEADS)) {
208 parentNode = localBranches;
209 // TODO fix this: if we are on a local branch or tag, we must do the
210 // indirection through the commit
211 } else if (refName.startsWith(Constants.R_REMOTES)) {
212 parentNode = remoteBranches;
213 } else if (refName.startsWith(Constants.R_TAGS)) {
214 parentNode = tags;
215 } else {
216 return false;
219 RepositoryTreeNode<Ref> actNode = new RepositoryTreeNode<Ref>(
220 parentNode, RepositoryTreeNodeType.REF, repo, actRef);
221 branchTree.setSelection(new StructuredSelection(actNode), true);
222 return true;
226 * @return the selected refName
228 public String getRefName() {
229 return this.selectedBranch;
232 @Override
233 protected void okPressed() {
234 this.selectedBranch = refNameFromDialog();
235 super.okPressed();
238 private String refNameFromDialog() {
239 IStructuredSelection sel = (IStructuredSelection) branchTree
240 .getSelection();
241 if (sel.size() != 1)
242 return null;
243 RepositoryTreeNode node = (RepositoryTreeNode) sel.getFirstElement();
244 if (node.getType() == RepositoryTreeNodeType.REF
245 || node.getType() == RepositoryTreeNodeType.TAG) {
246 return ((Ref) node.getObject()).getName();
248 return null;
251 private InputDialog getRefNameInputDialog(String prompt, final String refPrefix) {
252 InputDialog labelDialog = new InputDialog(
253 getShell(),
254 UIText.BranchSelectionDialog_QuestionNewBranchTitle,
255 prompt,
256 null, ValidationUtils.getRefNameInputValidator(repo, refPrefix));
257 labelDialog.setBlockOnOpen(true);
258 return labelDialog;
261 @Override
262 protected void createButtonsForButtonBar(Composite parent) {
263 newButton = new Button(parent, SWT.PUSH);
264 newButton.setFont(JFaceResources.getDialogFont());
265 newButton.setText(UIText.BranchSelectionDialog_NewBranch);
266 setButtonLayoutData(newButton);
267 ((GridLayout)parent.getLayout()).numColumns++;
269 renameButton = new Button(parent, SWT.PUSH);
270 renameButton.setFont(JFaceResources.getDialogFont());
271 renameButton.setText(UIText.BranchSelectionDialog_Rename);
272 setButtonLayoutData(renameButton);
273 ((GridLayout)parent.getLayout()).numColumns++;
275 renameButton.addSelectionListener(new SelectionAdapter() {
276 public void widgetSelected(SelectionEvent e) {
278 String refName = refNameFromDialog();
279 String refPrefix;
281 // the button should be disabled anyway, but we check again
282 if (refName.equals(Constants.HEAD))
283 return;
285 if (refName.startsWith(Constants.R_HEADS))
286 refPrefix = Constants.R_HEADS;
287 else if (refName.startsWith(Constants.R_REMOTES))
288 refPrefix = Constants.R_REMOTES;
289 else if (refName.startsWith(Constants.R_TAGS))
290 refPrefix = Constants.R_TAGS;
291 else {
292 // the button should be disabled anyway, but we check again
293 return;
296 String branchName = refName.substring(refPrefix.length());
298 InputDialog labelDialog = getRefNameInputDialog(NLS
299 .bind(
300 UIText.BranchSelectionDialog_QuestionNewBranchNameMessage,
301 branchName, refPrefix), refPrefix);
302 if (labelDialog.open() == Window.OK) {
303 String newRefName = refPrefix + labelDialog.getValue();
304 try {
305 RefRename renameRef = repo.renameRef(refName, newRefName);
306 if (renameRef.rename() != Result.RENAMED) {
307 reportError(
308 null,
309 UIText.BranchSelectionDialog_ErrorCouldNotRenameRef,
310 refName, newRefName, renameRef
311 .getResult());
313 branchTree.refresh();
314 markRef(newRefName);
315 } catch (Throwable e1) {
316 reportError(
318 UIText.BranchSelectionDialog_ErrorCouldNotRenameRef,
319 refName, newRefName, e1.getMessage());
324 newButton.addSelectionListener(new SelectionAdapter() {
326 public void widgetSelected(SelectionEvent e) {
327 // check what ref name the user selected, if any.
328 String refName = refNameFromDialog();
330 // the button should be disabled anyway, but we check again
331 if (refName.equals(Constants.HEAD))
332 return;
333 if (refName.startsWith(Constants.R_TAGS))
334 // the button should be disabled anyway, but we check again
335 return;
337 InputDialog labelDialog = getRefNameInputDialog(
339 .bind(
340 UIText.BranchSelectionDialog_QuestionNewBranchMessage,
341 refName, Constants.R_HEADS),
342 Constants.R_HEADS);
344 if (labelDialog.open() == Window.OK) {
345 String newRefName = Constants.R_HEADS + labelDialog.getValue();
346 RefUpdate updateRef;
347 try {
348 updateRef = repo.updateRef(newRefName);
349 Ref startRef = repo.getRef(refName);
350 ObjectId startAt = repo.resolve(refName);
351 String startBranch;
352 if (startRef != null)
353 startBranch = refName;
354 else
355 startBranch = startAt.name();
356 startBranch = repo.shortenRefName(startBranch);
357 updateRef.setNewObjectId(startAt);
358 updateRef.setRefLogMessage("branch: Created from " + startBranch, false); //$NON-NLS-1$
359 updateRef.update();
360 branchTree.refresh();
361 markRef(newRefName);
362 } catch (Throwable e1) {
363 reportError(
365 UIText.BranchSelectionDialog_ErrorCouldNotCreateNewRef,
366 newRefName);
371 confirmationBtn = createButton(parent, IDialogConstants.OK_ID,
372 UIText.BranchSelectionDialog_OkCheckout, true);
373 createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
375 // can't advance without a selection
376 confirmationBtn.setEnabled(!branchTree.getSelection().isEmpty());
380 * @return the label shown above the refs tree
382 protected String getRefsLabel() {
383 return UIText.BranchSelectionDialog_Refs;
387 * Subclasses may add UI elements
388 * @param parent
390 protected void createCustomArea(Composite parent) {
391 // do nothing
395 * Subclasses may change the title of the dialog
396 * @return the title of the dialog
398 protected String getTitle() {
399 return UIText.BranchSelectionDialog_TitleCheckout;
404 * @return if the confirmation button is enabled when a tag is selected
406 protected boolean canConfirmOnTag() {
407 return true;
410 @Override
411 protected int getShellStyle() {
412 return super.getShellStyle() | SWT.RESIZE;
415 private void reportError(Throwable e, String message, Object... args) {
416 String msg = NLS.bind(message, args);
417 Activator.handleError(msg, e, true);