1 /*******************************************************************************
2 * Copyright (c) 2010 SAP AG.
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 *******************************************************************************/
11 package org
.eclipse
.egit
.ui
.internal
.repository
;
14 import java
.io
.IOException
;
15 import java
.lang
.reflect
.InvocationTargetException
;
16 import java
.net
.URISyntaxException
;
17 import java
.util
.ArrayList
;
18 import java
.util
.Collection
;
19 import java
.util
.Collections
;
20 import java
.util
.HashSet
;
21 import java
.util
.List
;
23 import java
.util
.StringTokenizer
;
24 import java
.util
.TreeSet
;
26 import org
.eclipse
.core
.filesystem
.EFS
;
27 import org
.eclipse
.core
.filesystem
.IFileStore
;
28 import org
.eclipse
.core
.resources
.IProject
;
29 import org
.eclipse
.core
.resources
.IResource
;
30 import org
.eclipse
.core
.resources
.IWorkspace
;
31 import org
.eclipse
.core
.resources
.IWorkspaceRunnable
;
32 import org
.eclipse
.core
.resources
.ResourcesPlugin
;
33 import org
.eclipse
.core
.runtime
.CoreException
;
34 import org
.eclipse
.core
.runtime
.IAdaptable
;
35 import org
.eclipse
.core
.runtime
.IPath
;
36 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
37 import org
.eclipse
.core
.runtime
.IStatus
;
38 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
39 import org
.eclipse
.core
.runtime
.Path
;
40 import org
.eclipse
.core
.runtime
.Status
;
41 import org
.eclipse
.core
.runtime
.jobs
.IJobChangeEvent
;
42 import org
.eclipse
.core
.runtime
.jobs
.Job
;
43 import org
.eclipse
.core
.runtime
.jobs
.JobChangeAdapter
;
44 import org
.eclipse
.core
.runtime
.preferences
.IEclipsePreferences
;
45 import org
.eclipse
.core
.runtime
.preferences
.InstanceScope
;
46 import org
.eclipse
.egit
.core
.op
.BranchOperation
;
47 import org
.eclipse
.egit
.core
.project
.RepositoryMapping
;
48 import org
.eclipse
.egit
.ui
.Activator
;
49 import org
.eclipse
.egit
.ui
.UIIcons
;
50 import org
.eclipse
.egit
.ui
.UIText
;
51 import org
.eclipse
.egit
.ui
.internal
.clone
.GitCloneWizard
;
52 import org
.eclipse
.egit
.ui
.internal
.clone
.GitCreateProjectViaWizardWizard
;
53 import org
.eclipse
.egit
.ui
.internal
.repository
.RepositoryTreeNode
.RepositoryTreeNodeType
;
54 import org
.eclipse
.jface
.action
.Action
;
55 import org
.eclipse
.jface
.action
.IAction
;
56 import org
.eclipse
.jface
.action
.IToolBarManager
;
57 import org
.eclipse
.jface
.action
.Separator
;
58 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
59 import org
.eclipse
.jface
.dialogs
.ProgressMonitorDialog
;
60 import org
.eclipse
.jface
.operation
.IRunnableWithProgress
;
61 import org
.eclipse
.jface
.viewers
.IOpenListener
;
62 import org
.eclipse
.jface
.viewers
.ISelection
;
63 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
64 import org
.eclipse
.jface
.viewers
.ISelectionProvider
;
65 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
66 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
67 import org
.eclipse
.jface
.viewers
.OpenEvent
;
68 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
69 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
70 import org
.eclipse
.jface
.viewers
.TreeViewer
;
71 import org
.eclipse
.jface
.window
.Window
;
72 import org
.eclipse
.jface
.wizard
.Wizard
;
73 import org
.eclipse
.jface
.wizard
.WizardDialog
;
74 import org
.eclipse
.jgit
.lib
.Constants
;
75 import org
.eclipse
.jgit
.lib
.Ref
;
76 import org
.eclipse
.jgit
.lib
.RefUpdate
;
77 import org
.eclipse
.jgit
.lib
.Repository
;
78 import org
.eclipse
.jgit
.lib
.RepositoryCache
;
79 import org
.eclipse
.jgit
.lib
.RepositoryConfig
;
80 import org
.eclipse
.jgit
.transport
.RemoteConfig
;
81 import org
.eclipse
.osgi
.util
.NLS
;
82 import org
.eclipse
.swt
.SWT
;
83 import org
.eclipse
.swt
.dnd
.Clipboard
;
84 import org
.eclipse
.swt
.dnd
.TextTransfer
;
85 import org
.eclipse
.swt
.dnd
.Transfer
;
86 import org
.eclipse
.swt
.events
.MenuDetectEvent
;
87 import org
.eclipse
.swt
.events
.MenuDetectListener
;
88 import org
.eclipse
.swt
.events
.SelectionAdapter
;
89 import org
.eclipse
.swt
.events
.SelectionEvent
;
90 import org
.eclipse
.swt
.graphics
.Point
;
91 import org
.eclipse
.swt
.widgets
.Composite
;
92 import org
.eclipse
.swt
.widgets
.Display
;
93 import org
.eclipse
.swt
.widgets
.Menu
;
94 import org
.eclipse
.swt
.widgets
.MenuItem
;
95 import org
.eclipse
.swt
.widgets
.TreeItem
;
96 import org
.eclipse
.ui
.IEditorInput
;
97 import org
.eclipse
.ui
.IEditorPart
;
98 import org
.eclipse
.ui
.IFileEditorInput
;
99 import org
.eclipse
.ui
.IPageLayout
;
100 import org
.eclipse
.ui
.ISelectionListener
;
101 import org
.eclipse
.ui
.ISelectionService
;
102 import org
.eclipse
.ui
.IViewPart
;
103 import org
.eclipse
.ui
.IWorkbenchPart
;
104 import org
.eclipse
.ui
.PartInitException
;
105 import org
.eclipse
.ui
.PlatformUI
;
106 import org
.eclipse
.ui
.actions
.ActionFactory
;
107 import org
.eclipse
.ui
.editors
.text
.EditorsUI
;
108 import org
.eclipse
.ui
.ide
.FileStoreEditorInput
;
109 import org
.eclipse
.ui
.ide
.IDE
;
110 import org
.eclipse
.ui
.part
.IShowInTarget
;
111 import org
.eclipse
.ui
.part
.ShowInContext
;
112 import org
.eclipse
.ui
.part
.ViewPart
;
113 import org
.eclipse
.ui
.progress
.IWorkbenchSiteProgressService
;
114 import org
.eclipse
.ui
.views
.properties
.IPropertySheetPage
;
115 import org
.eclipse
.ui
.views
.properties
.PropertySheet
;
116 import org
.eclipse
.ui
.views
.properties
.PropertySheetPage
;
117 import org
.osgi
.service
.prefs
.BackingStoreException
;
121 * The Git Repositories view.
123 * This keeps track of a bunch of local directory names each of which represent
124 * a Git Repository. This list is stored in some Preferences object and used to
125 * build the tree in the view.
127 * Implements {@link ISelectionProvider} in order to integrate with the
131 * <li>Clarification whether to show projects, perhaps configurable switch</li>
134 public class RepositoriesView
extends ViewPart
implements ISelectionProvider
,
138 public static final String VIEW_ID
= "org.eclipse.egit.ui.RepositoriesView"; //$NON-NLS-1$
140 // TODO central constants? RemoteConfig ones are private
141 static final String REMOTE
= "remote"; //$NON-NLS-1$
143 static final String URL
= "url"; //$NON-NLS-1$
145 static final String PUSHURL
= "pushurl"; //$NON-NLS-1$
147 static final String FETCH
= "fetch"; //$NON-NLS-1$
149 static final String PUSH
= "push"; //$NON-NLS-1$
151 private static final String PREFS_DIRECTORIES
= "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$
153 private static final String PREFS_SYNCED
= "GitRepositoriesView.SyncWithSelection"; //$NON-NLS-1$
155 private final List
<ISelectionChangedListener
> selectionListeners
= new ArrayList
<ISelectionChangedListener
>();
157 private ISelection currentSelection
= new StructuredSelection();
159 private Job scheduledJob
;
161 private TreeViewer tv
;
163 private IAction importAction
;
165 private IAction addAction
;
167 private IAction refreshAction
;
169 private IAction linkWithSelectionAction
;
171 private IAction copyAction
;
173 private IAction pasteAction
;
176 * TODO move to utility class
178 * @return the directories as configured for this view
180 public static List
<String
> getDirs() {
181 List
<String
> resultStrings
= new ArrayList
<String
>();
182 String dirs
= getPrefs().get(PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
183 if (dirs
!= null && dirs
.length() > 0) {
184 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
185 while (tok
.hasMoreTokens()) {
186 String dirName
= tok
.nextToken();
187 File testFile
= new File(dirName
);
188 if (testFile
.exists()) {
189 resultStrings
.add(dirName
);
193 Collections
.sort(resultStrings
);
194 return resultStrings
;
197 private static void removeDir(File file
) {
201 dir
= file
.getCanonicalPath();
202 } catch (IOException e1
) {
203 dir
= file
.getAbsolutePath();
206 IEclipsePreferences prefs
= getPrefs();
208 TreeSet
<String
> resultStrings
= new TreeSet
<String
>();
209 String dirs
= prefs
.get(PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
210 if (dirs
!= null && dirs
.length() > 0) {
211 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
212 while (tok
.hasMoreTokens()) {
213 String dirName
= tok
.nextToken();
214 File testFile
= new File(dirName
);
215 if (testFile
.exists()) {
217 resultStrings
.add(testFile
.getCanonicalPath());
218 } catch (IOException e
) {
219 resultStrings
.add(testFile
.getAbsolutePath());
225 if (resultStrings
.remove(dir
)) {
226 StringBuilder sb
= new StringBuilder();
227 for (String gitDirString
: resultStrings
) {
228 sb
.append(gitDirString
);
229 sb
.append(File
.pathSeparatorChar
);
232 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
235 } catch (BackingStoreException e
) {
236 IStatus error
= new Status(IStatus
.ERROR
, Activator
237 .getPluginId(), e
.getMessage(), e
);
238 Activator
.getDefault().getLog().log(error
);
245 public Object
getAdapter(Class adapter
) {
246 // integrate with Properties view
247 if (adapter
== IPropertySheetPage
.class) {
248 PropertySheetPage page
= new PropertySheetPage();
250 .setPropertySourceProvider(new RepositoryPropertySourceProvider(
255 return super.getAdapter(adapter
);
259 public void createPartControl(Composite parent
) {
260 tv
= new TreeViewer(parent
, SWT
.MULTI
| SWT
.H_SCROLL
| SWT
.V_SCROLL
);
261 tv
.setContentProvider(new RepositoriesViewContentProvider());
262 // the label provider registers itself
263 new RepositoriesViewLabelProvider(tv
);
265 getSite().setSelectionProvider(this);
267 tv
.addSelectionChangedListener(new ISelectionChangedListener() {
269 public void selectionChanged(SelectionChangedEvent event
) {
271 copyAction
.setEnabled(false);
273 IStructuredSelection ssel
= (IStructuredSelection
) event
275 if (ssel
.size() == 1) {
276 RepositoryTreeNode node
= (RepositoryTreeNode
) ssel
278 // allow copy on repository, file, or folder (copying the
280 if (node
.getType() == RepositoryTreeNodeType
.REPO
281 || node
.getType() == RepositoryTreeNodeType
.WORKINGDIR
282 || node
.getType() == RepositoryTreeNodeType
.FOLDER
283 || node
.getType() == RepositoryTreeNodeType
.FILE
) {
284 copyAction
.setEnabled(true);
286 setSelection(new StructuredSelection(ssel
.getFirstElement()));
288 setSelection(new StructuredSelection());
293 tv
.addOpenListener(new IOpenListener() {
294 public void open(OpenEvent event
) {
295 IStructuredSelection selection
= (IStructuredSelection
) event
297 if (selection
.isEmpty()) {
298 // nothing selected, ignore
302 Object element
= selection
.getFirstElement();
303 ITreeContentProvider contentProvider
= (ITreeContentProvider
) tv
304 .getContentProvider();
305 if (contentProvider
.hasChildren(element
)) {
306 // this element has children, expand/collapse it
307 tv
.setExpandedState(element
, !tv
.getExpandedState(element
));
309 Object
[] selectionArray
= selection
.toArray();
310 for (Object selectedElement
: selectionArray
) {
311 RepositoryTreeNode node
= (RepositoryTreeNode
) selectedElement
;
312 // if any of the selected elements are not files, ignore
314 if (node
.getType() != RepositoryTreeNodeType
.FILE
315 && node
.getType() != RepositoryTreeNodeType
.REF
316 && node
.getType() != RepositoryTreeNodeType
.TAG
) {
321 // open the files the user has selected
322 for (Object selectedElement
: selectionArray
) {
323 RepositoryTreeNode node
= (RepositoryTreeNode
) selectedElement
;
324 if (node
.getType() == RepositoryTreeNodeType
.FILE
)
325 openFile((File
) node
.getObject());
326 else if (node
.getType() == RepositoryTreeNodeType
.REF
327 || node
.getType() == RepositoryTreeNodeType
.TAG
) {
328 Ref ref
= (Ref
) node
.getObject();
329 if (!isBare(node
.getRepository())
330 && ref
.getName().startsWith(
332 checkoutBranch(node
, ref
.getName());
341 addActionsToToolbar();
345 ISelectionService srv
= (ISelectionService
) getSite().getService(
346 ISelectionService
.class);
347 srv
.addPostSelectionListener(new ISelectionListener() {
349 public void selectionChanged(IWorkbenchPart part
,
350 ISelection selection
) {
352 // if the "link with selection" toggle is off, we're done
353 if (linkWithSelectionAction
== null
354 || !linkWithSelectionAction
.isChecked())
357 // this may happen if we switch between editors
358 if (part
instanceof IEditorPart
) {
359 IEditorInput input
= ((IEditorPart
) part
).getEditorInput();
360 if (input
instanceof IFileEditorInput
)
361 reactOnSelection(new StructuredSelection(
362 ((IFileEditorInput
) input
).getFile()));
365 reactOnSelection(selection
);
372 private void reactOnSelection(ISelection selection
) {
373 if (selection
instanceof StructuredSelection
) {
374 StructuredSelection ssel
= (StructuredSelection
) selection
;
375 if (ssel
.size() != 1)
377 if (ssel
.getFirstElement() instanceof IResource
) {
378 showResource((IResource
) ssel
.getFirstElement());
380 if (ssel
.getFirstElement() instanceof IAdaptable
) {
381 IResource adapted
= (IResource
) ((IAdaptable
) ssel
382 .getFirstElement()).getAdapter(IResource
.class);
384 showResource(adapted
);
389 private void addContextMenu() {
390 tv
.getTree().addMenuDetectListener(new MenuDetectListener() {
392 public void menuDetected(MenuDetectEvent e
) {
393 Menu men
= tv
.getTree().getMenu();
397 men
= new Menu(tv
.getTree());
399 TreeItem testItem
= tv
.getTree().getItem(
400 tv
.getTree().toControl(new Point(e
.x
, e
.y
)));
401 if (testItem
== null) {
402 addMenuItemsForPanel(men
);
404 addMenuItemsForTreeSelection(men
);
407 tv
.getTree().setMenu(men
);
412 private void addMenuItemsForPanel(Menu men
) {
414 MenuItem importItem
= new MenuItem(men
, SWT
.PUSH
);
415 importItem
.setText(UIText
.RepositoriesView_ImportRepository_MenuItem
);
416 importItem
.addSelectionListener(new SelectionAdapter() {
419 public void widgetSelected(SelectionEvent e
) {
425 MenuItem addItem
= new MenuItem(men
, SWT
.PUSH
);
426 addItem
.setText(UIText
.RepositoriesView_AddRepository_MenuItem
);
427 addItem
.addSelectionListener(new SelectionAdapter() {
430 public void widgetSelected(SelectionEvent e
) {
436 MenuItem pasteItem
= new MenuItem(men
, SWT
.PUSH
);
437 pasteItem
.setText(UIText
.RepositoriesView_PasteMenu
);
438 pasteItem
.addSelectionListener(new SelectionAdapter() {
441 public void widgetSelected(SelectionEvent e
) {
447 MenuItem refreshItem
= new MenuItem(men
, SWT
.PUSH
);
448 refreshItem
.setText(refreshAction
.getText());
449 refreshItem
.addSelectionListener(new SelectionAdapter() {
452 public void widgetSelected(SelectionEvent e
) {
460 private void addMenuItemsForTreeSelection(Menu men
) {
462 final IStructuredSelection sel
= (IStructuredSelection
) tv
465 boolean repoOnly
= true;
466 for (Object selected
: sel
.toArray()) {
468 if (((RepositoryTreeNode
) selected
).getType() != RepositoryTreeNodeType
.REPO
) {
474 if (sel
.size() > 1 && repoOnly
) {
475 List nodes
= sel
.toList();
476 final Repository
[] repos
= new Repository
[nodes
.size()];
477 for (int i
= 0; i
< sel
.size(); i
++)
478 repos
[i
] = ((RepositoryTreeNode
) nodes
.get(i
)).getRepository();
480 MenuItem remove
= new MenuItem(men
, SWT
.PUSH
);
481 remove
.setText(UIText
.RepositoriesView_Remove_MenuItem
);
482 remove
.addSelectionListener(new SelectionAdapter() {
485 public void widgetSelected(SelectionEvent e
) {
486 // TODO progress monitoring/cancellation
487 removeRepository(new NullProgressMonitor(), repos
);
493 // from here on, we only deal with single selection
497 final RepositoryTreeNode node
= (RepositoryTreeNode
) sel
500 final boolean isBare
= isBare(node
.getRepository());
502 if (node
.getType() == RepositoryTreeNodeType
.REF
) {
504 final Ref ref
= (Ref
) node
.getObject();
506 // we don't check out symbolic references
507 if (!ref
.isSymbolic()) {
510 MenuItem checkout
= new MenuItem(men
, SWT
.PUSH
);
511 checkout
.setText(UIText
.RepositoriesView_CheckOut_MenuItem
);
513 checkout
.setEnabled(!isRefCheckedOut(node
.getRepository(),
516 checkout
.addSelectionListener(new SelectionAdapter() {
519 public void widgetSelected(SelectionEvent e
) {
520 checkoutBranch(node
, ref
.getLeaf().getName());
524 new MenuItem(men
, SWT
.SEPARATOR
);
527 createCreateBranchItem(men
, node
);
528 createDeleteBranchItem(men
, node
);
533 if (node
.getType() == RepositoryTreeNodeType
.TAG
) {
535 final Ref ref
= (Ref
) node
.getObject();
537 MenuItem checkout
= new MenuItem(men
, SWT
.PUSH
);
538 checkout
.setText(UIText
.RepositoriesView_CheckOut_MenuItem
);
540 checkout
.setEnabled(!isRefCheckedOut(node
.getRepository(), ref
543 checkout
.addSelectionListener(new SelectionAdapter() {
546 public void widgetSelected(SelectionEvent e
) {
547 checkoutBranch(node
, ref
.getLeaf().getName());
553 if (node
.getType() == RepositoryTreeNodeType
.LOCALBRANCHES
554 || node
.getType() == RepositoryTreeNodeType
.REMOTEBRANCHES
)
555 createCreateBranchItem(men
, node
);
557 // for Repository: import existing projects, remove, (delete), open
559 if (node
.getType() == RepositoryTreeNodeType
.REPO
) {
561 final Repository repo
= (Repository
) node
.getObject();
563 // TODO "import existing plug-in" menu item
565 MenuItem remove
= new MenuItem(men
, SWT
.PUSH
);
566 remove
.setText(UIText
.RepositoriesView_Remove_MenuItem
);
567 remove
.addSelectionListener(new SelectionAdapter() {
570 public void widgetSelected(SelectionEvent e
) {
571 // TODO progress monitoring/cancellation
572 removeRepository(new NullProgressMonitor(), repo
);
576 // TODO delete does not work because of file locks on .pack-files
577 // Shawn Pearce has added the following thoughts:
579 // Hmm. We probably can't active detect file locks on pack files on
581 // It would be nice if we could support a delete, but only if the
583 // reasonably believed to be not-in-use right now.
585 // Within EGit you might be able to check GitProjectData and its
586 // repositoryCache to
587 // see if the repository is open by this workspace. If it is, then
588 // we know we shouldn't
591 // Some coding might look like this:
593 // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH);
594 // deleteRepo.setText("Delete");
595 // deleteRepo.addSelectionListener(new SelectionAdapter() {
598 // public void widgetSelected(SelectionEvent e) {
600 // boolean confirmed = MessageDialog.openConfirm(getSite()
601 // .getShell(), "Confirm",
602 // "This will delete the repository, continue?");
607 // IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
609 // public void run(IProgressMonitor monitor)
610 // throws CoreException {
611 // File workDir = repos.get(0).getRepository()
614 // File gitDir = repos.get(0).getRepository()
617 // IPath wdPath = new Path(workDir.getAbsolutePath());
618 // for (IProject prj : ResourcesPlugin.getWorkspace()
619 // .getRoot().getProjects()) {
620 // if (wdPath.isPrefixOf(prj.getLocation())) {
621 // prj.delete(false, false, monitor);
625 // repos.get(0).getRepository().close();
627 // boolean deleted = deleteRecursively(gitDir, monitor);
629 // MessageDialog.openError(getSite().getShell(),
631 // "Could not delete Git Repository");
634 // deleted = deleteRecursively(workDir, monitor);
637 // .openError(getSite().getShell(),
639 // "Could not delete Git Working Directory");
642 // scheduleRefresh();
645 // private boolean deleteRecursively(File fileToDelete,
646 // IProgressMonitor monitor) {
647 // if (fileToDelete.isDirectory()) {
648 // for (File file : fileToDelete.listFiles()) {
649 // if (!deleteRecursively(file, monitor)) {
654 // monitor.setTaskName(fileToDelete.getAbsolutePath());
655 // boolean deleted = fileToDelete.delete();
657 // System.err.println("Could not delete "
658 // + fileToDelete.getAbsolutePath());
665 // ResourcesPlugin.getWorkspace().run(wsr,
666 // ResourcesPlugin.getWorkspace().getRoot(),
667 // IWorkspace.AVOID_UPDATE,
668 // new NullProgressMonitor());
669 // } catch (CoreException e1) {
670 // // TODO Exception handling
671 // e1.printStackTrace();
678 new MenuItem(men
, SWT
.SEPARATOR
);
681 createImportProjectItem(men
, repo
, repo
.getWorkDir().getPath());
683 new MenuItem(men
, SWT
.SEPARATOR
);
686 MenuItem openPropsView
= new MenuItem(men
, SWT
.PUSH
);
687 openPropsView
.setText(UIText
.RepositoriesView_OpenPropertiesMenu
);
688 openPropsView
.addSelectionListener(new SelectionAdapter() {
691 public void widgetSelected(SelectionEvent e
) {
693 PlatformUI
.getWorkbench().getActiveWorkbenchWindow()
694 .getActivePage().showView(
695 IPageLayout
.ID_PROP_SHEET
);
696 } catch (PartInitException e1
) {
703 new MenuItem(men
, SWT
.SEPARATOR
);
705 createCopyPathItem(men
, repo
.getDirectory().getPath());
708 if (node
.getType() == RepositoryTreeNodeType
.REMOTES
) {
710 MenuItem remoteConfig
= new MenuItem(men
, SWT
.PUSH
);
711 remoteConfig
.setText(UIText
.RepositoriesView_NewRemoteMenu
);
712 remoteConfig
.addSelectionListener(new SelectionAdapter() {
715 public void widgetSelected(SelectionEvent e
) {
717 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
718 new NewRemoteWizard(node
.getRepository()));
719 if (dlg
.open() == Window
.OK
)
727 if (node
.getType() == RepositoryTreeNodeType
.REMOTE
) {
729 final String configName
= (String
) node
.getObject();
731 RemoteConfig rconfig
;
733 rconfig
= new RemoteConfig(node
.getRepository().getConfig(),
735 } catch (URISyntaxException e2
) {
736 // TODO Exception handling
740 boolean fetchExists
= rconfig
!= null
741 && !rconfig
.getURIs().isEmpty();
742 boolean pushExists
= rconfig
!= null
743 && !rconfig
.getPushURIs().isEmpty();
746 MenuItem configureUrlFetch
= new MenuItem(men
, SWT
.PUSH
);
748 .setText(UIText
.RepositoriesView_CreateFetch_menu
);
750 configureUrlFetch
.addSelectionListener(new SelectionAdapter() {
753 public void widgetSelected(SelectionEvent e
) {
755 WizardDialog dlg
= new WizardDialog(getSite()
756 .getShell(), new ConfigureRemoteWizard(node
757 .getRepository(), configName
, false));
758 if (dlg
.open() == Window
.OK
)
767 MenuItem configureUrlPush
= new MenuItem(men
, SWT
.PUSH
);
770 .setText(UIText
.RepositoriesView_CreatePush_menu
);
772 configureUrlPush
.addSelectionListener(new SelectionAdapter() {
775 public void widgetSelected(SelectionEvent e
) {
777 WizardDialog dlg
= new WizardDialog(getSite()
778 .getShell(), new ConfigureRemoteWizard(node
779 .getRepository(), configName
, true));
780 if (dlg
.open() == Window
.OK
)
788 if (!fetchExists
|| !pushExists
)
789 // add a separator dynamically
790 new MenuItem(men
, SWT
.SEPARATOR
);
792 MenuItem removeRemote
= new MenuItem(men
, SWT
.PUSH
);
793 removeRemote
.setText(UIText
.RepositoriesView_RemoveRemoteMenu
);
794 removeRemote
.addSelectionListener(new SelectionAdapter() {
797 public void widgetSelected(SelectionEvent e
) {
799 boolean ok
= MessageDialog
801 getSite().getShell(),
802 UIText
.RepositoriesView_ConfirmDeleteRemoteHeader
,
805 UIText
.RepositoriesView_ConfirmDeleteRemoteMessage
,
808 RepositoryConfig config
= node
.getRepository()
810 config
.unsetSection(REMOTE
, configName
);
814 } catch (IOException e1
) {
815 Activator
.handleError(
816 UIText
.RepositoriesView_ErrorHeader
, e1
,
825 new MenuItem(men
, SWT
.SEPARATOR
);
827 MenuItem openPropsView
= new MenuItem(men
, SWT
.PUSH
);
828 openPropsView
.setText(UIText
.RepositoriesView_OpenPropertiesMenu
);
829 openPropsView
.addSelectionListener(new SelectionAdapter() {
832 public void widgetSelected(SelectionEvent e
) {
834 PlatformUI
.getWorkbench().getActiveWorkbenchWindow()
835 .getActivePage().showView(
836 IPageLayout
.ID_PROP_SHEET
);
837 } catch (PartInitException e1
) {
845 if (node
.getType() == RepositoryTreeNodeType
.FETCH
) {
847 final String configName
= (String
) node
.getParent().getObject();
849 MenuItem configureUrlFetch
= new MenuItem(men
, SWT
.PUSH
);
851 .setText(UIText
.RepositoriesView_ConfigureFetchMenu
);
853 configureUrlFetch
.addSelectionListener(new SelectionAdapter() {
856 public void widgetSelected(SelectionEvent e
) {
858 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
859 new ConfigureRemoteWizard(node
.getRepository(),
861 if (dlg
.open() == Window
.OK
)
868 MenuItem deleteFetch
= new MenuItem(men
, SWT
.PUSH
);
869 deleteFetch
.setText(UIText
.RepositoriesView_RemoveFetch_menu
);
870 deleteFetch
.addSelectionListener(new SelectionAdapter() {
873 public void widgetSelected(SelectionEvent e
) {
874 RepositoryConfig config
= node
.getRepository().getConfig();
875 config
.unset("remote", configName
, "url"); //$NON-NLS-1$ //$NON-NLS-2$
876 config
.unset("remote", configName
, "fetch"); //$NON-NLS-1$//$NON-NLS-2$
880 } catch (IOException e1
) {
881 MessageDialog
.openError(getSite().getShell(),
882 UIText
.RepositoriesView_ErrorHeader
, e1
891 if (node
.getType() == RepositoryTreeNodeType
.PUSH
) {
893 final String configName
= (String
) node
.getParent().getObject();
895 MenuItem configureUrlPush
= new MenuItem(men
, SWT
.PUSH
);
897 configureUrlPush
.setText(UIText
.RepositoriesView_ConfigurePushMenu
);
899 configureUrlPush
.addSelectionListener(new SelectionAdapter() {
902 public void widgetSelected(SelectionEvent e
) {
904 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
905 new ConfigureRemoteWizard(node
.getRepository(),
907 if (dlg
.open() == Window
.OK
)
913 MenuItem deleteFetch
= new MenuItem(men
, SWT
.PUSH
);
914 deleteFetch
.setText(UIText
.RepositoriesView_RemovePush_menu
);
915 deleteFetch
.addSelectionListener(new SelectionAdapter() {
918 public void widgetSelected(SelectionEvent e
) {
919 RepositoryConfig config
= node
.getRepository().getConfig();
920 config
.unset("remote", configName
, "pushurl"); //$NON-NLS-1$ //$NON-NLS-2$
921 config
.unset("remote", configName
, "push"); //$NON-NLS-1$ //$NON-NLS-2$
925 } catch (IOException e1
) {
926 MessageDialog
.openError(getSite().getShell(),
927 UIText
.RepositoriesView_ErrorHeader
, e1
935 if (node
.getType() == RepositoryTreeNodeType
.FILE
) {
937 final File file
= (File
) node
.getObject();
939 MenuItem openInTextEditor
= new MenuItem(men
, SWT
.PUSH
);
941 .setText(UIText
.RepositoriesView_OpenInTextEditor_menu
);
942 openInTextEditor
.addSelectionListener(new SelectionAdapter() {
945 public void widgetSelected(SelectionEvent e
) {
951 new MenuItem(men
, SWT
.SEPARATOR
);
952 createCopyPathItem(men
, file
.getPath());
955 if (!isBare
&& node
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
956 String path
= node
.getRepository().getWorkDir().getAbsolutePath();
957 createImportProjectItem(men
, node
.getRepository(), path
);
958 new MenuItem(men
, SWT
.SEPARATOR
);
959 createCopyPathItem(men
, path
);
962 if (node
.getType() == RepositoryTreeNodeType
.FOLDER
) {
963 String path
= ((File
) node
.getObject()).getPath();
964 createImportProjectItem(men
, node
.getRepository(), path
);
965 new MenuItem(men
, SWT
.SEPARATOR
);
966 createCopyPathItem(men
, path
);
971 private boolean isBare(Repository repository
) {
972 return repository
.getConfig().getBoolean("core", "bare", false); //$NON-NLS-1$ //$NON-NLS-2$
975 private boolean isRefCheckedOut(Repository repository
, String refName
) {
977 String compareString
;
980 branchName
= repository
.getFullBranch();
981 if (branchName
== null)
983 if (refName
.startsWith(Constants
.R_HEADS
)) {
984 // local branch: HEAD would be on the branch
985 compareString
= refName
;
986 } else if (refName
.startsWith(Constants
.R_TAGS
)) {
987 // tag: HEAD would be on the commit id to which the tag is
989 compareString
= repository
.mapTag(refName
).getObjId().getName();
990 } else if (refName
.startsWith(Constants
.R_REMOTES
)) {
991 // remote branch: HEAD would be on the commit id to which
992 // the branch is pointing
993 compareString
= repository
.mapCommit(refName
).getCommitId()
996 // some other symbolic reference
999 } catch (IOException e1
) {
1003 return compareString
.equals(branchName
);
1006 private void createCopyPathItem(Menu men
, final String path
) {
1009 copyPath
= new MenuItem(men
, SWT
.PUSH
);
1010 copyPath
.setText(UIText
.RepositoriesView_CopyPathToClipboardMenu
);
1011 copyPath
.addSelectionListener(new SelectionAdapter() {
1014 public void widgetSelected(SelectionEvent e
) {
1015 Clipboard clipboard
= new Clipboard(null);
1016 TextTransfer textTransfer
= TextTransfer
.getInstance();
1017 Transfer
[] transfers
= new Transfer
[] { textTransfer
};
1018 Object
[] data
= new Object
[] { path
};
1019 clipboard
.setContents(data
, transfers
);
1020 clipboard
.dispose();
1027 private void createCreateBranchItem(Menu men
, final RepositoryTreeNode node
) {
1029 final boolean remoteMode
;
1031 if (node
.getType() == RepositoryTreeNodeType
.REF
) {
1032 remoteMode
= node
.getParent().getType() == RepositoryTreeNodeType
.REMOTEBRANCHES
;
1033 ref
= (Ref
) node
.getObject();
1034 } else if (node
.getType() == RepositoryTreeNodeType
.LOCALBRANCHES
) {
1037 } else if (node
.getType() == RepositoryTreeNodeType
.REMOTEBRANCHES
) {
1043 MenuItem createLocal
= new MenuItem(men
, SWT
.PUSH
);
1045 createLocal
.setText(UIText
.RepositoriesView_NewRemoteBranchMenu
);
1047 createLocal
.setText(UIText
.RepositoriesView_NewLocalBranchMenu
);
1049 createLocal
.addSelectionListener(new SelectionAdapter() {
1052 public void widgetSelected(SelectionEvent e
) {
1054 Wizard wiz
= new Wizard() {
1057 public void addPages() {
1058 addPage(new CreateBranchPage(node
.getRepository(), ref
,
1060 setWindowTitle(UIText
.RepositoriesView_NewBranchTitle
);
1064 public boolean performFinish() {
1067 getContainer().run(false, true,
1068 new IRunnableWithProgress() {
1070 public void run(IProgressMonitor monitor
)
1071 throws InvocationTargetException
,
1072 InterruptedException
{
1073 CreateBranchPage cp
= (CreateBranchPage
) getPages()[0];
1075 cp
.createBranch(monitor
);
1076 } catch (CoreException ce
) {
1077 throw new InvocationTargetException(
1079 } catch (IOException ioe
) {
1080 throw new InvocationTargetException(
1086 } catch (InvocationTargetException ite
) {
1089 UIText
.RepositoriesView_BranchCreationFailureMessage
,
1090 ite
.getCause(), true);
1092 } catch (InterruptedException ie
) {
1098 if (new WizardDialog(getSite().getShell(), wiz
).open() == Window
.OK
)
1106 private void createDeleteBranchItem(Menu men
, final RepositoryTreeNode node
) {
1108 final Ref ref
= (Ref
) node
.getObject();
1110 MenuItem deleteBranch
= new MenuItem(men
, SWT
.PUSH
);
1111 deleteBranch
.setText(UIText
.RepositoriesView_DeleteBranchMenu
);
1113 deleteBranch
.setEnabled(!isRefCheckedOut(node
.getRepository(), ref
1116 deleteBranch
.addSelectionListener(new SelectionAdapter() {
1119 public void widgetSelected(SelectionEvent e
) {
1123 getSite().getShell(),
1124 UIText
.RepositoriesView_ConfirmDeleteTitle
,
1127 UIText
.RepositoriesView_ConfirmBranchDeletionMessage
,
1132 new ProgressMonitorDialog(getSite().getShell()).run(false,
1133 false, new IRunnableWithProgress() {
1135 public void run(IProgressMonitor monitor
)
1136 throws InvocationTargetException
,
1137 InterruptedException
{
1140 RefUpdate op
= node
.getRepository()
1141 .updateRef(ref
.getName());
1142 op
.setRefLogMessage("branch deleted", //$NON-NLS-1$
1144 // we set the force update in order
1145 // to avoid having this rejected
1146 // due to minor issues
1147 op
.setForceUpdate(true);
1150 } catch (IOException ioe
) {
1151 throw new InvocationTargetException(ioe
);
1156 } catch (InvocationTargetException e1
) {
1159 UIText
.RepositoriesView_BranchDeletionFailureMessage
,
1160 e1
.getCause(), true);
1161 e1
.printStackTrace();
1162 } catch (InterruptedException e1
) {
1171 private void openFile(File file
) {
1172 IFileStore store
= EFS
.getLocalFileSystem().getStore(
1173 new Path(file
.getAbsolutePath()));
1175 // TODO do we need a read-only editor here?
1176 IDE
.openEditor(getSite().getPage(),
1177 new FileStoreEditorInput(store
),
1178 EditorsUI
.DEFAULT_TEXT_EDITOR_ID
);
1179 } catch (PartInitException e
) {
1180 Activator
.handleError(UIText
.RepositoriesView_Error_WindowTitle
, e
,
1185 private void checkoutBranch(final RepositoryTreeNode node
,
1186 final String refName
) {
1187 // for the sake of UI responsiveness, let's start a job
1188 Job job
= new Job(NLS
.bind(UIText
.RepositoriesView_CheckingOutMessage
,
1192 protected IStatus
run(IProgressMonitor monitor
) {
1194 Repository repo
= node
.getRepository();
1196 final BranchOperation op
= new BranchOperation(repo
, refName
);
1197 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
1199 public void run(IProgressMonitor myMonitor
)
1200 throws CoreException
{
1201 op
.execute(myMonitor
);
1206 ResourcesPlugin
.getWorkspace().run(wsr
,
1207 ResourcesPlugin
.getWorkspace().getRoot(),
1208 IWorkspace
.AVOID_UPDATE
, monitor
);
1210 } catch (CoreException e1
) {
1211 return new Status(IStatus
.ERROR
, Activator
.getPluginId(),
1212 e1
.getMessage(), e1
);
1215 return Status
.OK_STATUS
;
1223 private void createImportProjectItem(Menu men
, final Repository repo
,
1224 final String path
) {
1226 MenuItem startWizard
;
1227 startWizard
= new MenuItem(men
, SWT
.PUSH
);
1228 startWizard
.setText(UIText
.RepositoriesView_ImportProjectsMenu
);
1229 startWizard
.addSelectionListener(new SelectionAdapter() {
1232 public void widgetSelected(SelectionEvent e
) {
1233 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
1234 new GitCreateProjectViaWizardWizard(repo
, path
));
1235 if (dlg
.open() == Window
.OK
)
1242 // we could start the ImportWizard here,
1243 // unfortunately, this fails within a wizard
1244 // startWizard = new MenuItem(men, SWT.PUSH);
1245 // startWizard.setText("Start the Import wizard...");
1246 // startWizard.addSelectionListener(new SelectionAdapter() {
1249 // public void widgetSelected(SelectionEvent e) {
1251 // IHandlerService handlerService = (IHandlerService) getSite()
1252 // .getWorkbenchWindow().getWorkbench().getService(
1253 // IHandlerService.class);
1256 // handlerService.executeCommand("org.eclipse.ui.file.import", //$NON-NLS-1$
1258 // } catch (ExecutionException e1) {
1259 // Activator.handleError(e1.getMessage(), e1, true);
1260 // } catch (NotDefinedException e1) {
1261 // Activator.handleError(e1.getMessage(), e1, true);
1262 // } catch (NotEnabledException e1) {
1263 // Activator.handleError(e1.getMessage(), e1, true);
1264 // } catch (NotHandledException e1) {
1265 // Activator.handleError(e1.getMessage(), e1, true);
1272 private void addActionsToToolbar() {
1274 IToolBarManager manager
= getViewSite().getActionBars()
1275 .getToolBarManager();
1277 refreshAction
= new Action(UIText
.RepositoriesView_Refresh_Button
) {
1284 refreshAction
.setImageDescriptor(UIIcons
.ELCL16_REFRESH
);
1285 manager
.add(refreshAction
);
1287 linkWithSelectionAction
= new Action(
1288 UIText
.RepositoriesView_LinkWithSelection_action
,
1289 IAction
.AS_CHECK_BOX
) {
1293 IEclipsePreferences prefs
= getPrefs();
1294 prefs
.putBoolean(PREFS_SYNCED
, isChecked());
1297 } catch (BackingStoreException e
) {
1301 ISelectionService srv
= (ISelectionService
) getSite()
1302 .getService(ISelectionService
.class);
1303 reactOnSelection(srv
.getSelection());
1310 linkWithSelectionAction
1311 .setToolTipText(UIText
.RepositoriesView_LinkWithSelection_action
);
1312 linkWithSelectionAction
.setImageDescriptor(UIIcons
.ELCL16_SYNCED
);
1313 linkWithSelectionAction
.setChecked(getPrefs().getBoolean(PREFS_SYNCED
,
1316 manager
.add(linkWithSelectionAction
);
1318 manager
.add(new Separator());
1320 IAction collapseAllAction
= new Action(
1321 UIText
.RepositoriesView_CollapseAllMenu
) {
1328 collapseAllAction
.setImageDescriptor(UIIcons
.COLLAPSEALL
);
1329 manager
.add(collapseAllAction
);
1331 manager
.add(new Separator());
1333 importAction
= new Action(UIText
.RepositoriesView_Import_Button
) {
1337 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
1338 new GitCloneWizard());
1339 if (dlg
.open() == Window
.OK
)
1343 importAction
.setToolTipText(UIText
.RepositoriesView_Clone_Tooltip
);
1344 importAction
.setImageDescriptor(UIIcons
.CLONEGIT
);
1346 manager
.add(importAction
);
1348 addAction
= new Action(UIText
.RepositoriesView_Add_Button
) {
1352 RepositorySearchDialog sd
= new RepositorySearchDialog(
1353 getSite().getShell(), getDirs());
1354 if (sd
.open() == Window
.OK
) {
1355 Set
<String
> dirs
= new HashSet
<String
>();
1356 dirs
.addAll(getDirs());
1357 if (dirs
.addAll(sd
.getDirectories()))
1364 addAction
.setToolTipText(UIText
.RepositoriesView_AddRepository_Tooltip
);
1365 addAction
.setImageDescriptor(UIIcons
.NEW_REPOSITORY
);
1367 manager
.add(addAction
);
1369 // copy and paste are global actions; we just implement them
1370 // and register them with the global action handler
1371 // we enable/disable them upon tree selection changes
1373 copyAction
= new Action("") { //$NON-NLS-1$
1377 // for REPO, WORKINGDIR, FILE, FOLDER: copy directory
1378 IStructuredSelection sel
= (IStructuredSelection
) tv
1380 if (sel
.size() == 1) {
1381 RepositoryTreeNode node
= (RepositoryTreeNode
) sel
1384 if (node
.getType() == RepositoryTreeNodeType
.REPO
) {
1385 dir
= node
.getRepository().getDirectory().getPath();
1386 } else if (node
.getType() == RepositoryTreeNodeType
.FILE
1387 || node
.getType() == RepositoryTreeNodeType
.FOLDER
) {
1388 dir
= ((File
) node
.getObject()).getPath();
1389 } else if (node
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
1390 if (!isBare(node
.getRepository()))
1391 dir
= node
.getRepository().getWorkDir().getPath();
1394 Clipboard clip
= null;
1396 clip
= new Clipboard(getSite().getShell()
1399 .setContents(new Object
[] { dir
},
1400 new Transfer
[] { TextTransfer
1404 // we must dispose ourselves
1412 copyAction
.setEnabled(false);
1414 getViewSite().getActionBars().setGlobalActionHandler(
1415 ActionFactory
.COPY
.getId(), copyAction
);
1417 pasteAction
= new Action("") { //$NON-NLS-1$
1421 // we check if the pasted content is a directory
1422 // repository location and try to add this
1423 String errorMessage
= null;
1425 Clipboard clip
= null;
1427 clip
= new Clipboard(getSite().getShell().getDisplay());
1428 String content
= (String
) clip
.getContents(TextTransfer
1430 if (content
== null) {
1431 errorMessage
= UIText
.RepositoriesView_NothingToPasteMessage
;
1435 File file
= new File(content
);
1436 if (!file
.exists() || !file
.isDirectory()) {
1437 errorMessage
= UIText
.RepositoriesView_ClipboardContentNotDirectoryMessage
;
1441 if (!RepositoryCache
.FileKey
.isGitRepository(file
)) {
1444 UIText
.RepositoriesView_ClipboardContentNoGitRepoMessage
,
1452 errorMessage
= NLS
.bind(
1453 UIText
.RepositoriesView_PasteRepoAlreadyThere
,
1457 // we must dispose ourselves
1459 if (errorMessage
!= null)
1461 MessageDialog
.openWarning(getSite().getShell(),
1462 UIText
.RepositoriesView_PasteFailureTitle
,
1469 getViewSite().getActionBars().setGlobalActionHandler(
1470 ActionFactory
.PASTE
.getId(), pasteAction
);
1475 * @return the preferences
1477 protected static IEclipsePreferences
getPrefs() {
1478 return new InstanceScope().getNode(Activator
.getPluginId());
1482 public void dispose() {
1483 // make sure to cancel the refresh job
1484 if (this.scheduledJob
!= null) {
1485 this.scheduledJob
.cancel();
1486 this.scheduledJob
= null;
1492 * Schedules a refreh
1494 public void scheduleRefresh() {
1496 Job job
= new Job("Refreshing Git Repositories view") { //$NON-NLS-1$
1499 protected IStatus
run(IProgressMonitor monitor
) {
1501 final List
<RepositoryTreeNode
<Repository
>> input
;
1503 input
= getRepositoriesFromDirs(monitor
);
1504 } catch (InterruptedException e
) {
1505 return new Status(IStatus
.ERROR
, Activator
.getPluginId(), e
1509 boolean needsNewInput
= tv
.getInput() == null;
1510 List oldInput
= (List
) tv
.getInput();
1512 needsNewInput
= oldInput
.size() != input
.size();
1514 if (!needsNewInput
) {
1515 for (int i
= 0; i
< input
.size(); i
++) {
1516 needsNewInput
= !input
.get(i
).equals(oldInput
.get(i
));
1522 final boolean updateInput
= needsNewInput
;
1524 Display
.getDefault().syncExec(new Runnable() {
1527 // keep expansion state and selection so that we can
1530 Object
[] expanded
= tv
.getExpandedElements();
1531 IStructuredSelection sel
= (IStructuredSelection
) tv
1537 tv
.setExpandedElements(expanded
);
1539 Object selected
= sel
.getFirstElement();
1540 if (selected
!= null)
1541 tv
.reveal(selected
);
1543 IViewPart part
= PlatformUI
.getWorkbench()
1544 .getActiveWorkbenchWindow().getActivePage()
1545 .findView(IPageLayout
.ID_PROP_SHEET
);
1547 PropertySheet sheet
= (PropertySheet
) part
;
1548 PropertySheetPage page
= (PropertySheetPage
) sheet
1555 return new Status(IStatus
.OK
, Activator
.getPluginId(), ""); //$NON-NLS-1$
1560 job
.setSystem(true);
1562 IWorkbenchSiteProgressService service
= (IWorkbenchSiteProgressService
) getSite()
1563 .getService(IWorkbenchSiteProgressService
.class);
1565 service
.schedule(job
);
1572 * Adds a directory to the list if it is not already there
1575 * @return see {@link Collection#add(Object)}
1577 public static boolean addDir(File file
) {
1581 dirString
= file
.getCanonicalPath();
1582 } catch (IOException e
) {
1583 dirString
= file
.getAbsolutePath();
1586 List
<String
> dirStrings
= getDirs();
1587 if (dirStrings
.contains(dirString
)) {
1590 Set
<String
> dirs
= new HashSet
<String
>();
1591 dirs
.addAll(dirStrings
);
1592 dirs
.add(dirString
);
1599 * Converts the directories as configured for this view into a list of
1600 * {@link Repository} objects suitable for the tree content provider
1602 * TODO move to some utility class
1605 * @return a list of nodes
1606 * @throws InterruptedException
1608 public static List
<RepositoryTreeNode
<Repository
>> getRepositoriesFromDirs(
1609 IProgressMonitor monitor
) throws InterruptedException
{
1611 List
<String
> gitDirStrings
= getDirs();
1612 List
<RepositoryTreeNode
<Repository
>> input
= new ArrayList
<RepositoryTreeNode
<Repository
>>();
1614 for (String dirString
: gitDirStrings
) {
1615 if (monitor
!= null && monitor
.isCanceled()) {
1616 throw new InterruptedException(
1617 UIText
.RepositoriesView_ActionCanceled_Message
);
1620 File dir
= new File(dirString
);
1621 if (dir
.exists() && dir
.isDirectory()) {
1622 Repository repo
= new Repository(dir
);
1623 RepositoryTreeNode
<Repository
> node
= new RepositoryTreeNode
<Repository
>(
1624 null, RepositoryTreeNodeType
.REPO
, repo
, repo
);
1627 } catch (IOException e
) {
1628 IStatus error
= new Status(IStatus
.ERROR
, Activator
1629 .getPluginId(), e
.getMessage(), e
);
1630 Activator
.getDefault().getLog().log(error
);
1633 Collections
.sort(input
);
1637 private static void saveDirs(Set
<String
> gitDirStrings
) {
1638 StringBuilder sb
= new StringBuilder();
1639 for (String gitDirString
: gitDirStrings
) {
1640 sb
.append(gitDirString
);
1641 sb
.append(File
.pathSeparatorChar
);
1644 IEclipsePreferences prefs
= getPrefs();
1645 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
1648 } catch (BackingStoreException e
) {
1649 IStatus error
= new Status(IStatus
.ERROR
, Activator
.getPluginId(),
1651 Activator
.getDefault().getLog().log(error
);
1656 public void setFocus() {
1657 tv
.getTree().setFocus();
1660 @SuppressWarnings("boxing")
1661 private boolean confirmProjectDeletion(List
<IProject
> projectsToDelete
) {
1663 confirmed
= MessageDialog
1665 getSite().getShell(),
1666 UIText
.RepositoriesView_ConfirmProjectDeletion_WindowTitle
,
1669 UIText
.RepositoriesView_ConfirmProjectDeletion_Question
,
1670 projectsToDelete
.size()));
1674 public void addSelectionChangedListener(ISelectionChangedListener listener
) {
1675 selectionListeners
.add(listener
);
1678 public ISelection
getSelection() {
1679 return currentSelection
;
1682 public void removeSelectionChangedListener(
1683 ISelectionChangedListener listener
) {
1684 selectionListeners
.remove(listener
);
1688 public void setSelection(ISelection selection
) {
1689 currentSelection
= selection
;
1690 for (ISelectionChangedListener listener
: selectionListeners
) {
1691 listener
.selectionChanged(new SelectionChangedEvent(
1692 RepositoriesView
.this, selection
));
1697 * Opens the tree and marks the folder to which a project is pointing
1702 @SuppressWarnings("unchecked")
1703 public void showResource(final IResource resource
) {
1704 IProject project
= resource
.getProject();
1705 RepositoryMapping mapping
= RepositoryMapping
.getMapping(project
);
1706 if (mapping
== null)
1709 if (addDir(mapping
.getRepository().getDirectory())) {
1713 boolean doSetSelection
= false;
1715 if (this.scheduledJob
!= null) {
1716 int state
= this.scheduledJob
.getState();
1717 if (state
== Job
.WAITING
|| state
== Job
.RUNNING
) {
1718 this.scheduledJob
.addJobChangeListener(new JobChangeAdapter() {
1721 public void done(IJobChangeEvent event
) {
1722 showResource(resource
);
1726 doSetSelection
= true;
1730 if (doSetSelection
) {
1731 RepositoriesViewContentProvider cp
= (RepositoriesViewContentProvider
) tv
1732 .getContentProvider();
1733 RepositoryTreeNode currentNode
= null;
1734 Object
[] repos
= cp
.getElements(tv
.getInput());
1735 for (Object repo
: repos
) {
1736 RepositoryTreeNode node
= (RepositoryTreeNode
) repo
;
1737 // TODO equals implementation of Repository?
1738 if (mapping
.getRepository().getDirectory().equals(
1739 ((Repository
) node
.getObject()).getDirectory())) {
1740 for (Object child
: cp
.getChildren(node
)) {
1741 RepositoryTreeNode childNode
= (RepositoryTreeNode
) child
;
1742 if (childNode
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
1743 currentNode
= childNode
;
1751 IPath relPath
= new Path(mapping
.getRepoRelativePath(resource
));
1753 for (String segment
: relPath
.segments()) {
1754 for (Object child
: cp
.getChildren(currentNode
)) {
1755 RepositoryTreeNode
<File
> childNode
= (RepositoryTreeNode
<File
>) child
;
1756 if (childNode
.getObject().getName().equals(segment
)) {
1757 currentNode
= childNode
;
1763 final RepositoryTreeNode selNode
= currentNode
;
1765 Display
.getDefault().asyncExec(new Runnable() {
1768 tv
.setSelection(new StructuredSelection(selNode
), true);
1776 public boolean show(ShowInContext context
) {
1777 ISelection selection
= context
.getSelection();
1778 if (selection
instanceof IStructuredSelection
) {
1779 IStructuredSelection ss
= (IStructuredSelection
) selection
;
1780 if (ss
.size() == 1) {
1781 Object element
= ss
.getFirstElement();
1782 if (element
instanceof IAdaptable
) {
1783 IResource resource
= (IResource
) ((IAdaptable
) element
)
1784 .getAdapter(IResource
.class);
1785 if (resource
!= null) {
1786 showResource(resource
);
1795 private void removeRepository(final IProgressMonitor monitor
,
1796 final Repository
... repository
) {
1797 final List
<IProject
> projectsToDelete
= new ArrayList
<IProject
>();
1800 .setTaskName(UIText
.RepositoriesView_DeleteRepoDeterminProjectsMessage
);
1802 for (Repository repo
: repository
) {
1803 File workDir
= repo
.getWorkDir();
1804 final IPath wdPath
= new Path(workDir
.getAbsolutePath());
1805 for (IProject prj
: ResourcesPlugin
.getWorkspace().getRoot()
1807 if (monitor
.isCanceled())
1809 if (wdPath
.isPrefixOf(prj
.getLocation())) {
1810 projectsToDelete
.add(prj
);
1815 if (!projectsToDelete
.isEmpty()) {
1817 confirmed
= confirmProjectDeletion(projectsToDelete
);
1823 if (monitor
.isCanceled())
1826 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
1828 public void run(IProgressMonitor actMonitor
) throws CoreException
{
1830 for (IProject prj
: projectsToDelete
) {
1831 prj
.delete(false, false, actMonitor
);
1833 for (Repository repo
: repository
)
1834 removeDir(repo
.getDirectory());
1840 ResourcesPlugin
.getWorkspace().run(wsr
,
1841 ResourcesPlugin
.getWorkspace().getRoot(),
1842 IWorkspace
.AVOID_UPDATE
, monitor
);
1843 } catch (CoreException e1
) {
1844 Activator
.logError(e1
.getMessage(), e1
);