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
.fetch
.FetchConfiguredRemoteAction
;
54 import org
.eclipse
.egit
.ui
.internal
.push
.PushConfiguredRemoteAction
;
55 import org
.eclipse
.egit
.ui
.internal
.repository
.RepositoryTreeNode
.RepositoryTreeNodeType
;
56 import org
.eclipse
.jface
.action
.Action
;
57 import org
.eclipse
.jface
.action
.IAction
;
58 import org
.eclipse
.jface
.action
.IToolBarManager
;
59 import org
.eclipse
.jface
.action
.Separator
;
60 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
61 import org
.eclipse
.jface
.dialogs
.ProgressMonitorDialog
;
62 import org
.eclipse
.jface
.operation
.IRunnableWithProgress
;
63 import org
.eclipse
.jface
.viewers
.IOpenListener
;
64 import org
.eclipse
.jface
.viewers
.ISelection
;
65 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
66 import org
.eclipse
.jface
.viewers
.ISelectionProvider
;
67 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
68 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
69 import org
.eclipse
.jface
.viewers
.OpenEvent
;
70 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
71 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
72 import org
.eclipse
.jface
.viewers
.TreeViewer
;
73 import org
.eclipse
.jface
.window
.Window
;
74 import org
.eclipse
.jface
.wizard
.Wizard
;
75 import org
.eclipse
.jface
.wizard
.WizardDialog
;
76 import org
.eclipse
.jgit
.lib
.Constants
;
77 import org
.eclipse
.jgit
.lib
.IndexChangedEvent
;
78 import org
.eclipse
.jgit
.lib
.Ref
;
79 import org
.eclipse
.jgit
.lib
.RefUpdate
;
80 import org
.eclipse
.jgit
.lib
.RefsChangedEvent
;
81 import org
.eclipse
.jgit
.lib
.Repository
;
82 import org
.eclipse
.jgit
.lib
.RepositoryCache
;
83 import org
.eclipse
.jgit
.lib
.RepositoryConfig
;
84 import org
.eclipse
.jgit
.lib
.RepositoryListener
;
85 import org
.eclipse
.jgit
.transport
.RemoteConfig
;
86 import org
.eclipse
.osgi
.util
.NLS
;
87 import org
.eclipse
.swt
.SWT
;
88 import org
.eclipse
.swt
.dnd
.Clipboard
;
89 import org
.eclipse
.swt
.dnd
.TextTransfer
;
90 import org
.eclipse
.swt
.dnd
.Transfer
;
91 import org
.eclipse
.swt
.events
.MenuDetectEvent
;
92 import org
.eclipse
.swt
.events
.MenuDetectListener
;
93 import org
.eclipse
.swt
.events
.SelectionAdapter
;
94 import org
.eclipse
.swt
.events
.SelectionEvent
;
95 import org
.eclipse
.swt
.graphics
.Point
;
96 import org
.eclipse
.swt
.widgets
.Composite
;
97 import org
.eclipse
.swt
.widgets
.Display
;
98 import org
.eclipse
.swt
.widgets
.Menu
;
99 import org
.eclipse
.swt
.widgets
.MenuItem
;
100 import org
.eclipse
.swt
.widgets
.TreeItem
;
101 import org
.eclipse
.ui
.IEditorInput
;
102 import org
.eclipse
.ui
.IEditorPart
;
103 import org
.eclipse
.ui
.IFileEditorInput
;
104 import org
.eclipse
.ui
.IPageLayout
;
105 import org
.eclipse
.ui
.ISelectionListener
;
106 import org
.eclipse
.ui
.ISelectionService
;
107 import org
.eclipse
.ui
.IViewPart
;
108 import org
.eclipse
.ui
.IWorkbenchPart
;
109 import org
.eclipse
.ui
.PartInitException
;
110 import org
.eclipse
.ui
.PlatformUI
;
111 import org
.eclipse
.ui
.actions
.ActionFactory
;
112 import org
.eclipse
.ui
.editors
.text
.EditorsUI
;
113 import org
.eclipse
.ui
.ide
.FileStoreEditorInput
;
114 import org
.eclipse
.ui
.ide
.IDE
;
115 import org
.eclipse
.ui
.part
.IShowInTarget
;
116 import org
.eclipse
.ui
.part
.ShowInContext
;
117 import org
.eclipse
.ui
.part
.ViewPart
;
118 import org
.eclipse
.ui
.progress
.IWorkbenchSiteProgressService
;
119 import org
.eclipse
.ui
.views
.properties
.IPropertySheetPage
;
120 import org
.eclipse
.ui
.views
.properties
.PropertySheet
;
121 import org
.eclipse
.ui
.views
.properties
.PropertySheetPage
;
122 import org
.osgi
.service
.prefs
.BackingStoreException
;
126 * The Git Repositories view.
128 * This keeps track of a bunch of local directory names each of which represent
129 * a Git Repository. This list is stored in some Preferences object and used to
130 * build the tree in the view.
132 * Implements {@link ISelectionProvider} in order to integrate with the
135 * This periodically refreshes itself in order to react on Repository changes.
137 public class RepositoriesView
extends ViewPart
implements ISelectionProvider
,
141 public static final String VIEW_ID
= "org.eclipse.egit.ui.RepositoriesView"; //$NON-NLS-1$
143 // TODO central constants? RemoteConfig ones are private
144 static final String REMOTE
= "remote"; //$NON-NLS-1$
146 static final String URL
= "url"; //$NON-NLS-1$
148 static final String PUSHURL
= "pushurl"; //$NON-NLS-1$
150 static final String FETCH
= "fetch"; //$NON-NLS-1$
152 static final String PUSH
= "push"; //$NON-NLS-1$
154 private static final String PREFS_DIRECTORIES
= "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$
156 private static final String PREFS_SYNCED
= "GitRepositoriesView.SyncWithSelection"; //$NON-NLS-1$
158 private final List
<ISelectionChangedListener
> selectionListeners
= new ArrayList
<ISelectionChangedListener
>();
160 private final static long AUTO_REFRESH_INTERVAL_MILLISECONDS
= 10000l;
162 private ISelection currentSelection
= new StructuredSelection();
164 private Job scheduledJob
;
166 private Job autoRefreshJob
;
168 private TreeViewer tv
;
170 private IAction importAction
;
172 private IAction addAction
;
174 private IAction refreshAction
;
176 private IAction linkWithSelectionAction
;
178 private IAction copyAction
;
180 private IAction pasteAction
;
183 * TODO move to utility class
185 * @return the directories as configured for this view
187 public static List
<String
> getDirs() {
188 List
<String
> resultStrings
= new ArrayList
<String
>();
189 String dirs
= getPrefs().get(PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
190 if (dirs
!= null && dirs
.length() > 0) {
191 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
192 while (tok
.hasMoreTokens()) {
193 String dirName
= tok
.nextToken();
194 File testFile
= new File(dirName
);
195 if (testFile
.exists() && !resultStrings
.contains(dirName
)) {
196 resultStrings
.add(dirName
);
200 Collections
.sort(resultStrings
);
201 return resultStrings
;
204 private static void removeDir(File file
) {
208 dir
= file
.getCanonicalPath();
209 } catch (IOException e1
) {
210 dir
= file
.getAbsolutePath();
213 IEclipsePreferences prefs
= getPrefs();
215 TreeSet
<String
> resultStrings
= new TreeSet
<String
>();
216 String dirs
= prefs
.get(PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
217 if (dirs
!= null && dirs
.length() > 0) {
218 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
219 while (tok
.hasMoreTokens()) {
220 String dirName
= tok
.nextToken();
221 File testFile
= new File(dirName
);
222 if (testFile
.exists()) {
224 resultStrings
.add(testFile
.getCanonicalPath());
225 } catch (IOException e
) {
226 resultStrings
.add(testFile
.getAbsolutePath());
232 if (resultStrings
.remove(dir
)) {
233 StringBuilder sb
= new StringBuilder();
234 for (String gitDirString
: resultStrings
) {
235 sb
.append(gitDirString
);
236 sb
.append(File
.pathSeparatorChar
);
239 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
242 } catch (BackingStoreException e
) {
243 IStatus error
= new Status(IStatus
.ERROR
, Activator
244 .getPluginId(), e
.getMessage(), e
);
245 Activator
.getDefault().getLog().log(error
);
252 public Object
getAdapter(Class adapter
) {
253 // integrate with Properties view
254 if (adapter
== IPropertySheetPage
.class) {
255 PropertySheetPage page
= new PropertySheetPage();
257 .setPropertySourceProvider(new RepositoryPropertySourceProvider(
262 return super.getAdapter(adapter
);
266 public void createPartControl(Composite parent
) {
267 tv
= new TreeViewer(parent
, SWT
.MULTI
| SWT
.H_SCROLL
| SWT
.V_SCROLL
);
268 tv
.setContentProvider(new RepositoriesViewContentProvider());
269 // the label provider registers itself
270 new RepositoriesViewLabelProvider(tv
);
272 getSite().setSelectionProvider(this);
274 tv
.addSelectionChangedListener(new ISelectionChangedListener() {
276 public void selectionChanged(SelectionChangedEvent event
) {
278 copyAction
.setEnabled(false);
280 IStructuredSelection ssel
= (IStructuredSelection
) event
282 if (ssel
.size() == 1) {
283 RepositoryTreeNode node
= (RepositoryTreeNode
) ssel
285 // allow copy on repository, file, or folder (copying the
287 if (node
.getType() == RepositoryTreeNodeType
.REPO
288 || node
.getType() == RepositoryTreeNodeType
.WORKINGDIR
289 || node
.getType() == RepositoryTreeNodeType
.FOLDER
290 || node
.getType() == RepositoryTreeNodeType
.FILE
) {
291 copyAction
.setEnabled(true);
293 setSelection(new StructuredSelection(ssel
.getFirstElement()));
295 setSelection(new StructuredSelection());
300 tv
.addOpenListener(new IOpenListener() {
301 public void open(OpenEvent event
) {
302 IStructuredSelection selection
= (IStructuredSelection
) event
304 if (selection
.isEmpty()) {
305 // nothing selected, ignore
309 Object element
= selection
.getFirstElement();
310 ITreeContentProvider contentProvider
= (ITreeContentProvider
) tv
311 .getContentProvider();
312 if (contentProvider
.hasChildren(element
)) {
313 // this element has children, expand/collapse it
314 tv
.setExpandedState(element
, !tv
.getExpandedState(element
));
316 Object
[] selectionArray
= selection
.toArray();
317 for (Object selectedElement
: selectionArray
) {
318 RepositoryTreeNode node
= (RepositoryTreeNode
) selectedElement
;
319 // if any of the selected elements are not files, ignore
321 if (node
.getType() != RepositoryTreeNodeType
.FILE
322 && node
.getType() != RepositoryTreeNodeType
.REF
323 && node
.getType() != RepositoryTreeNodeType
.TAG
) {
328 // open the files the user has selected
329 for (Object selectedElement
: selectionArray
) {
330 RepositoryTreeNode node
= (RepositoryTreeNode
) selectedElement
;
331 if (node
.getType() == RepositoryTreeNodeType
.FILE
)
332 openFile((File
) node
.getObject());
333 else if (node
.getType() == RepositoryTreeNodeType
.REF
334 || node
.getType() == RepositoryTreeNodeType
.TAG
) {
335 Ref ref
= (Ref
) node
.getObject();
336 if (!isBare(node
.getRepository())
337 && ref
.getName().startsWith(
339 checkoutBranch(node
, ref
.getName());
348 addActionsToToolbar();
352 // schedule the auto-refresh job
353 autoRefreshJob
= new Job("Git Repositories View Auto-Refresh") { //$NON-NLS-1$
356 protected IStatus
run(IProgressMonitor monitor
) {
358 schedule(AUTO_REFRESH_INTERVAL_MILLISECONDS
);
359 return Status
.OK_STATUS
;
363 autoRefreshJob
.setSystem(true);
364 autoRefreshJob
.schedule(AUTO_REFRESH_INTERVAL_MILLISECONDS
);
366 ISelectionService srv
= (ISelectionService
) getSite().getService(
367 ISelectionService
.class);
368 srv
.addPostSelectionListener(new ISelectionListener() {
370 public void selectionChanged(IWorkbenchPart part
,
371 ISelection selection
) {
373 // if the "link with selection" toggle is off, we're done
374 if (linkWithSelectionAction
== null
375 || !linkWithSelectionAction
.isChecked())
378 // this may happen if we switch between editors
379 if (part
instanceof IEditorPart
) {
380 IEditorInput input
= ((IEditorPart
) part
).getEditorInput();
381 if (input
instanceof IFileEditorInput
)
382 reactOnSelection(new StructuredSelection(
383 ((IFileEditorInput
) input
).getFile()));
386 reactOnSelection(selection
);
393 private void reactOnSelection(ISelection selection
) {
394 if (selection
instanceof StructuredSelection
) {
395 StructuredSelection ssel
= (StructuredSelection
) selection
;
396 if (ssel
.size() != 1)
398 if (ssel
.getFirstElement() instanceof IResource
) {
399 showResource((IResource
) ssel
.getFirstElement());
401 if (ssel
.getFirstElement() instanceof IAdaptable
) {
402 IResource adapted
= (IResource
) ((IAdaptable
) ssel
403 .getFirstElement()).getAdapter(IResource
.class);
405 showResource(adapted
);
410 private void addContextMenu() {
411 tv
.getTree().addMenuDetectListener(new MenuDetectListener() {
413 public void menuDetected(MenuDetectEvent e
) {
414 Menu men
= tv
.getTree().getMenu();
418 men
= new Menu(tv
.getTree());
420 TreeItem testItem
= tv
.getTree().getItem(
421 tv
.getTree().toControl(new Point(e
.x
, e
.y
)));
422 if (testItem
== null) {
423 addMenuItemsForPanel(men
);
425 addMenuItemsForTreeSelection(men
);
428 tv
.getTree().setMenu(men
);
433 private void addMenuItemsForPanel(Menu men
) {
435 MenuItem importItem
= new MenuItem(men
, SWT
.PUSH
);
436 importItem
.setText(UIText
.RepositoriesView_ImportRepository_MenuItem
);
437 importItem
.addSelectionListener(new SelectionAdapter() {
440 public void widgetSelected(SelectionEvent e
) {
446 MenuItem addItem
= new MenuItem(men
, SWT
.PUSH
);
447 addItem
.setText(UIText
.RepositoriesView_AddRepository_MenuItem
);
448 addItem
.addSelectionListener(new SelectionAdapter() {
451 public void widgetSelected(SelectionEvent e
) {
457 MenuItem pasteItem
= new MenuItem(men
, SWT
.PUSH
);
458 pasteItem
.setText(UIText
.RepositoriesView_PasteMenu
);
459 pasteItem
.addSelectionListener(new SelectionAdapter() {
462 public void widgetSelected(SelectionEvent e
) {
468 MenuItem refreshItem
= new MenuItem(men
, SWT
.PUSH
);
469 refreshItem
.setText(refreshAction
.getText());
470 refreshItem
.addSelectionListener(new SelectionAdapter() {
473 public void widgetSelected(SelectionEvent e
) {
481 private void addMenuItemsForTreeSelection(Menu men
) {
483 final IStructuredSelection sel
= (IStructuredSelection
) tv
486 boolean repoOnly
= true;
487 for (Object selected
: sel
.toArray()) {
489 if (((RepositoryTreeNode
) selected
).getType() != RepositoryTreeNodeType
.REPO
) {
495 if (sel
.size() > 1 && repoOnly
) {
496 List nodes
= sel
.toList();
497 final Repository
[] repos
= new Repository
[nodes
.size()];
498 for (int i
= 0; i
< sel
.size(); i
++)
499 repos
[i
] = ((RepositoryTreeNode
) nodes
.get(i
)).getRepository();
501 MenuItem remove
= new MenuItem(men
, SWT
.PUSH
);
502 remove
.setText(UIText
.RepositoriesView_Remove_MenuItem
);
503 remove
.addSelectionListener(new SelectionAdapter() {
506 public void widgetSelected(SelectionEvent e
) {
507 // TODO progress monitoring/cancellation
508 removeRepository(new NullProgressMonitor(), repos
);
514 // from here on, we only deal with single selection
518 final RepositoryTreeNode node
= (RepositoryTreeNode
) sel
521 final boolean isBare
= isBare(node
.getRepository());
523 if (node
.getType() == RepositoryTreeNodeType
.REF
) {
525 final Ref ref
= (Ref
) node
.getObject();
527 // we don't check out symbolic references
528 if (!ref
.isSymbolic()) {
531 MenuItem checkout
= new MenuItem(men
, SWT
.PUSH
);
532 checkout
.setText(UIText
.RepositoriesView_CheckOut_MenuItem
);
534 checkout
.setEnabled(!isRefCheckedOut(node
.getRepository(),
537 checkout
.addSelectionListener(new SelectionAdapter() {
540 public void widgetSelected(SelectionEvent e
) {
541 checkoutBranch(node
, ref
.getLeaf().getName());
545 new MenuItem(men
, SWT
.SEPARATOR
);
548 createCreateBranchItem(men
, node
);
549 createDeleteBranchItem(men
, node
);
554 if (node
.getType() == RepositoryTreeNodeType
.TAG
) {
556 final Ref ref
= (Ref
) node
.getObject();
558 MenuItem checkout
= new MenuItem(men
, SWT
.PUSH
);
559 checkout
.setText(UIText
.RepositoriesView_CheckOut_MenuItem
);
561 checkout
.setEnabled(!isRefCheckedOut(node
.getRepository(), ref
564 checkout
.addSelectionListener(new SelectionAdapter() {
567 public void widgetSelected(SelectionEvent e
) {
568 checkoutBranch(node
, ref
.getLeaf().getName());
573 if (node
.getType() == RepositoryTreeNodeType
.BRANCHES
574 || node
.getType() == RepositoryTreeNodeType
.LOCALBRANCHES
)
575 // offering this on the "Remote Branches" node would probably be
577 createCreateBranchItem(men
, node
);
579 // for Repository: import existing projects, remove, (delete), open
581 if (node
.getType() == RepositoryTreeNodeType
.REPO
) {
583 final Repository repo
= (Repository
) node
.getObject();
585 // TODO "import existing plug-in" menu item
587 MenuItem remove
= new MenuItem(men
, SWT
.PUSH
);
588 remove
.setText(UIText
.RepositoriesView_Remove_MenuItem
);
589 remove
.addSelectionListener(new SelectionAdapter() {
592 public void widgetSelected(SelectionEvent e
) {
593 // TODO progress monitoring/cancellation
594 removeRepository(new NullProgressMonitor(), repo
);
598 // TODO delete does not work because of file locks on .pack-files
599 // Shawn Pearce has added the following thoughts:
601 // Hmm. We probably can't active detect file locks on pack files on
603 // It would be nice if we could support a delete, but only if the
605 // reasonably believed to be not-in-use right now.
607 // Within EGit you might be able to check GitProjectData and its
608 // repositoryCache to
609 // see if the repository is open by this workspace. If it is, then
610 // we know we shouldn't
613 // Some coding might look like this:
615 // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH);
616 // deleteRepo.setText("Delete");
617 // deleteRepo.addSelectionListener(new SelectionAdapter() {
620 // public void widgetSelected(SelectionEvent e) {
622 // boolean confirmed = MessageDialog.openConfirm(getSite()
623 // .getShell(), "Confirm",
624 // "This will delete the repository, continue?");
629 // IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
631 // public void run(IProgressMonitor monitor)
632 // throws CoreException {
633 // File workDir = repos.get(0).getRepository()
636 // File gitDir = repos.get(0).getRepository()
639 // IPath wdPath = new Path(workDir.getAbsolutePath());
640 // for (IProject prj : ResourcesPlugin.getWorkspace()
641 // .getRoot().getProjects()) {
642 // if (wdPath.isPrefixOf(prj.getLocation())) {
643 // prj.delete(false, false, monitor);
647 // repos.get(0).getRepository().close();
649 // boolean deleted = deleteRecursively(gitDir, monitor);
651 // MessageDialog.openError(getSite().getShell(),
653 // "Could not delete Git Repository");
656 // deleted = deleteRecursively(workDir, monitor);
659 // .openError(getSite().getShell(),
661 // "Could not delete Git Working Directory");
664 // scheduleRefresh();
667 // private boolean deleteRecursively(File fileToDelete,
668 // IProgressMonitor monitor) {
669 // if (fileToDelete.isDirectory()) {
670 // for (File file : fileToDelete.listFiles()) {
671 // if (!deleteRecursively(file, monitor)) {
676 // monitor.setTaskName(fileToDelete.getAbsolutePath());
677 // boolean deleted = fileToDelete.delete();
679 // System.err.println("Could not delete "
680 // + fileToDelete.getAbsolutePath());
687 // ResourcesPlugin.getWorkspace().run(wsr,
688 // ResourcesPlugin.getWorkspace().getRoot(),
689 // IWorkspace.AVOID_UPDATE,
690 // new NullProgressMonitor());
691 // } catch (CoreException e1) {
692 // // TODO Exception handling
693 // e1.printStackTrace();
700 new MenuItem(men
, SWT
.SEPARATOR
);
703 createImportProjectItem(men
, repo
, repo
.getWorkDir().getPath());
705 new MenuItem(men
, SWT
.SEPARATOR
);
708 MenuItem openPropsView
= new MenuItem(men
, SWT
.PUSH
);
709 openPropsView
.setText(UIText
.RepositoriesView_OpenPropertiesMenu
);
710 openPropsView
.addSelectionListener(new SelectionAdapter() {
713 public void widgetSelected(SelectionEvent e
) {
715 PlatformUI
.getWorkbench().getActiveWorkbenchWindow()
716 .getActivePage().showView(
717 IPageLayout
.ID_PROP_SHEET
);
718 } catch (PartInitException e1
) {
725 new MenuItem(men
, SWT
.SEPARATOR
);
727 createCopyPathItem(men
, repo
.getDirectory().getPath());
730 if (node
.getType() == RepositoryTreeNodeType
.REMOTES
) {
732 MenuItem remoteConfig
= new MenuItem(men
, SWT
.PUSH
);
733 remoteConfig
.setText(UIText
.RepositoriesView_NewRemoteMenu
);
734 remoteConfig
.addSelectionListener(new SelectionAdapter() {
737 public void widgetSelected(SelectionEvent e
) {
739 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
740 new NewRemoteWizard(node
.getRepository()));
741 if (dlg
.open() == Window
.OK
)
749 if (node
.getType() == RepositoryTreeNodeType
.REMOTE
) {
751 final String configName
= (String
) node
.getObject();
753 RemoteConfig rconfig
;
755 rconfig
= new RemoteConfig(node
.getRepository().getConfig(),
757 } catch (URISyntaxException e2
) {
758 // TODO Exception handling
762 boolean fetchExists
= rconfig
!= null
763 && !rconfig
.getURIs().isEmpty();
764 boolean pushExists
= rconfig
!= null
765 && !rconfig
.getPushURIs().isEmpty();
768 MenuItem configureUrlFetch
= new MenuItem(men
, SWT
.PUSH
);
770 .setText(UIText
.RepositoriesView_CreateFetch_menu
);
772 configureUrlFetch
.addSelectionListener(new SelectionAdapter() {
775 public void widgetSelected(SelectionEvent e
) {
777 WizardDialog dlg
= new WizardDialog(getSite()
778 .getShell(), new ConfigureRemoteWizard(node
779 .getRepository(), configName
, false));
780 if (dlg
.open() == Window
.OK
)
789 MenuItem configureUrlPush
= new MenuItem(men
, SWT
.PUSH
);
792 .setText(UIText
.RepositoriesView_CreatePush_menu
);
794 configureUrlPush
.addSelectionListener(new SelectionAdapter() {
797 public void widgetSelected(SelectionEvent e
) {
799 WizardDialog dlg
= new WizardDialog(getSite()
800 .getShell(), new ConfigureRemoteWizard(node
801 .getRepository(), configName
, true));
802 if (dlg
.open() == Window
.OK
)
810 if (!fetchExists
|| !pushExists
)
811 // add a separator dynamically
812 new MenuItem(men
, SWT
.SEPARATOR
);
814 MenuItem removeRemote
= new MenuItem(men
, SWT
.PUSH
);
815 removeRemote
.setText(UIText
.RepositoriesView_RemoveRemoteMenu
);
816 removeRemote
.addSelectionListener(new SelectionAdapter() {
819 public void widgetSelected(SelectionEvent e
) {
821 boolean ok
= MessageDialog
823 getSite().getShell(),
824 UIText
.RepositoriesView_ConfirmDeleteRemoteHeader
,
827 UIText
.RepositoriesView_ConfirmDeleteRemoteMessage
,
830 RepositoryConfig config
= node
.getRepository()
832 config
.unsetSection(REMOTE
, configName
);
836 } catch (IOException e1
) {
837 Activator
.handleError(
838 UIText
.RepositoriesView_ErrorHeader
, e1
,
847 new MenuItem(men
, SWT
.SEPARATOR
);
849 MenuItem openPropsView
= new MenuItem(men
, SWT
.PUSH
);
850 openPropsView
.setText(UIText
.RepositoriesView_OpenPropertiesMenu
);
851 openPropsView
.addSelectionListener(new SelectionAdapter() {
854 public void widgetSelected(SelectionEvent e
) {
856 PlatformUI
.getWorkbench().getActiveWorkbenchWindow()
857 .getActivePage().showView(
858 IPageLayout
.ID_PROP_SHEET
);
859 } catch (PartInitException e1
) {
867 if (node
.getType() == RepositoryTreeNodeType
.FETCH
) {
869 final String configName
= (String
) node
.getParent().getObject();
871 MenuItem doFetch
= new MenuItem(men
, SWT
.PUSH
);
872 doFetch
.setText(UIText
.RepositoriesView_FetchMenu
);
873 doFetch
.addSelectionListener(new SelectionAdapter() {
876 public void widgetSelected(SelectionEvent evt
) {
877 new FetchConfiguredRemoteAction(node
.getRepository(),
878 configName
).run(getSite().getShell());
883 MenuItem configureUrlFetch
= new MenuItem(men
, SWT
.PUSH
);
885 .setText(UIText
.RepositoriesView_ConfigureFetchMenu
);
887 configureUrlFetch
.addSelectionListener(new SelectionAdapter() {
890 public void widgetSelected(SelectionEvent e
) {
892 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
893 new ConfigureRemoteWizard(node
.getRepository(),
895 if (dlg
.open() == Window
.OK
)
902 MenuItem deleteFetch
= new MenuItem(men
, SWT
.PUSH
);
903 deleteFetch
.setText(UIText
.RepositoriesView_RemoveFetch_menu
);
904 deleteFetch
.addSelectionListener(new SelectionAdapter() {
907 public void widgetSelected(SelectionEvent e
) {
908 RepositoryConfig config
= node
.getRepository().getConfig();
909 config
.unset("remote", configName
, "url"); //$NON-NLS-1$ //$NON-NLS-2$
910 config
.unset("remote", configName
, "fetch"); //$NON-NLS-1$//$NON-NLS-2$
914 } catch (IOException e1
) {
915 MessageDialog
.openError(getSite().getShell(),
916 UIText
.RepositoriesView_ErrorHeader
, e1
925 if (node
.getType() == RepositoryTreeNodeType
.PUSH
) {
927 final String configName
= (String
) node
.getParent().getObject();
929 MenuItem doPush
= new MenuItem(men
, SWT
.PUSH
);
930 doPush
.setText(UIText
.RepositoriesView_DoPushMenuItem
);
931 doPush
.addSelectionListener(new SelectionAdapter() {
934 public void widgetSelected(SelectionEvent evt
) {
935 new PushConfiguredRemoteAction(node
.getRepository(),
936 configName
).run(getSite().getShell(), false);
940 MenuItem configureUrlPush
= new MenuItem(men
, SWT
.PUSH
);
941 configureUrlPush
.setText(UIText
.RepositoriesView_ConfigurePushMenu
);
943 configureUrlPush
.addSelectionListener(new SelectionAdapter() {
946 public void widgetSelected(SelectionEvent e
) {
948 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
949 new ConfigureRemoteWizard(node
.getRepository(),
951 if (dlg
.open() == Window
.OK
)
957 MenuItem deleteFetch
= new MenuItem(men
, SWT
.PUSH
);
958 deleteFetch
.setText(UIText
.RepositoriesView_RemovePush_menu
);
959 deleteFetch
.addSelectionListener(new SelectionAdapter() {
962 public void widgetSelected(SelectionEvent e
) {
963 RepositoryConfig config
= node
.getRepository().getConfig();
964 config
.unset("remote", configName
, "pushurl"); //$NON-NLS-1$ //$NON-NLS-2$
965 config
.unset("remote", configName
, "push"); //$NON-NLS-1$ //$NON-NLS-2$
969 } catch (IOException e1
) {
970 MessageDialog
.openError(getSite().getShell(),
971 UIText
.RepositoriesView_ErrorHeader
, e1
979 if (node
.getType() == RepositoryTreeNodeType
.FILE
) {
981 final File file
= (File
) node
.getObject();
983 MenuItem openInTextEditor
= new MenuItem(men
, SWT
.PUSH
);
985 .setText(UIText
.RepositoriesView_OpenInTextEditor_menu
);
986 openInTextEditor
.addSelectionListener(new SelectionAdapter() {
989 public void widgetSelected(SelectionEvent e
) {
995 new MenuItem(men
, SWT
.SEPARATOR
);
996 createCopyPathItem(men
, file
.getPath());
999 if (!isBare
&& node
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
1000 String path
= node
.getRepository().getWorkDir().getAbsolutePath();
1001 createImportProjectItem(men
, node
.getRepository(), path
);
1002 new MenuItem(men
, SWT
.SEPARATOR
);
1003 createCopyPathItem(men
, path
);
1006 if (node
.getType() == RepositoryTreeNodeType
.FOLDER
) {
1007 String path
= ((File
) node
.getObject()).getPath();
1008 createImportProjectItem(men
, node
.getRepository(), path
);
1009 new MenuItem(men
, SWT
.SEPARATOR
);
1010 createCopyPathItem(men
, path
);
1015 private boolean isBare(Repository repository
) {
1016 return repository
.getConfig().getBoolean("core", "bare", false); //$NON-NLS-1$ //$NON-NLS-2$
1019 private boolean isRefCheckedOut(Repository repository
, String refName
) {
1021 String compareString
;
1024 branchName
= repository
.getFullBranch();
1025 if (branchName
== null)
1027 if (refName
.startsWith(Constants
.R_HEADS
)) {
1028 // local branch: HEAD would be on the branch
1029 compareString
= refName
;
1030 } else if (refName
.startsWith(Constants
.R_TAGS
)) {
1031 // tag: HEAD would be on the commit id to which the tag is
1033 compareString
= repository
.mapTag(refName
).getObjId().getName();
1034 } else if (refName
.startsWith(Constants
.R_REMOTES
)) {
1035 // remote branch: HEAD would be on the commit id to which
1036 // the branch is pointing
1037 compareString
= repository
.mapCommit(refName
).getCommitId()
1040 // some other symbolic reference
1043 } catch (IOException e1
) {
1047 return compareString
.equals(branchName
);
1050 private void createCopyPathItem(Menu men
, final String path
) {
1053 copyPath
= new MenuItem(men
, SWT
.PUSH
);
1054 copyPath
.setText(UIText
.RepositoriesView_CopyPathToClipboardMenu
);
1055 copyPath
.addSelectionListener(new SelectionAdapter() {
1058 public void widgetSelected(SelectionEvent e
) {
1059 Clipboard clipboard
= new Clipboard(null);
1060 TextTransfer textTransfer
= TextTransfer
.getInstance();
1061 Transfer
[] transfers
= new Transfer
[] { textTransfer
};
1062 Object
[] data
= new Object
[] { path
};
1063 clipboard
.setContents(data
, transfers
);
1064 clipboard
.dispose();
1071 private void createCreateBranchItem(Menu men
, final RepositoryTreeNode node
) {
1073 if (node
.getType() == RepositoryTreeNodeType
.REF
)
1074 ref
= (Ref
) node
.getObject();
1078 MenuItem createLocal
= new MenuItem(men
, SWT
.PUSH
);
1079 createLocal
.setText(UIText
.RepositoriesView_NewBranchMenu
);
1081 createLocal
.addSelectionListener(new SelectionAdapter() {
1084 public void widgetSelected(SelectionEvent e
) {
1086 Wizard wiz
= new Wizard() {
1089 public void addPages() {
1090 addPage(new CreateBranchPage(node
.getRepository(), ref
));
1091 setWindowTitle(UIText
.RepositoriesView_NewBranchTitle
);
1095 public boolean performFinish() {
1098 getContainer().run(false, true,
1099 new IRunnableWithProgress() {
1101 public void run(IProgressMonitor monitor
)
1102 throws InvocationTargetException
,
1103 InterruptedException
{
1104 CreateBranchPage cp
= (CreateBranchPage
) getPages()[0];
1106 cp
.createBranch(monitor
);
1107 } catch (CoreException ce
) {
1108 throw new InvocationTargetException(
1110 } catch (IOException ioe
) {
1111 throw new InvocationTargetException(
1117 } catch (InvocationTargetException ite
) {
1120 UIText
.RepositoriesView_BranchCreationFailureMessage
,
1121 ite
.getCause(), true);
1123 } catch (InterruptedException ie
) {
1129 if (new WizardDialog(getSite().getShell(), wiz
).open() == Window
.OK
)
1136 private void createDeleteBranchItem(Menu men
, final RepositoryTreeNode node
) {
1138 final Ref ref
= (Ref
) node
.getObject();
1140 MenuItem deleteBranch
= new MenuItem(men
, SWT
.PUSH
);
1141 deleteBranch
.setText(UIText
.RepositoriesView_DeleteBranchMenu
);
1143 deleteBranch
.setEnabled(!isRefCheckedOut(node
.getRepository(), ref
1146 deleteBranch
.addSelectionListener(new SelectionAdapter() {
1149 public void widgetSelected(SelectionEvent e
) {
1153 getSite().getShell(),
1154 UIText
.RepositoriesView_ConfirmDeleteTitle
,
1157 UIText
.RepositoriesView_ConfirmBranchDeletionMessage
,
1162 new ProgressMonitorDialog(getSite().getShell()).run(false,
1163 false, new IRunnableWithProgress() {
1165 public void run(IProgressMonitor monitor
)
1166 throws InvocationTargetException
,
1167 InterruptedException
{
1170 RefUpdate op
= node
.getRepository()
1171 .updateRef(ref
.getName());
1172 op
.setRefLogMessage("branch deleted", //$NON-NLS-1$
1174 // we set the force update in order
1175 // to avoid having this rejected
1176 // due to minor issues
1177 op
.setForceUpdate(true);
1180 } catch (IOException ioe
) {
1181 throw new InvocationTargetException(ioe
);
1186 } catch (InvocationTargetException e1
) {
1189 UIText
.RepositoriesView_BranchDeletionFailureMessage
,
1190 e1
.getCause(), true);
1191 e1
.printStackTrace();
1192 } catch (InterruptedException e1
) {
1201 private void openFile(File file
) {
1202 IFileStore store
= EFS
.getLocalFileSystem().getStore(
1203 new Path(file
.getAbsolutePath()));
1205 // TODO do we need a read-only editor here?
1206 IDE
.openEditor(getSite().getPage(),
1207 new FileStoreEditorInput(store
),
1208 EditorsUI
.DEFAULT_TEXT_EDITOR_ID
);
1209 } catch (PartInitException e
) {
1210 Activator
.handleError(UIText
.RepositoriesView_Error_WindowTitle
, e
,
1215 private void checkoutBranch(final RepositoryTreeNode node
,
1216 final String refName
) {
1217 // for the sake of UI responsiveness, let's start a job
1218 Job job
= new Job(NLS
.bind(UIText
.RepositoriesView_CheckingOutMessage
,
1222 protected IStatus
run(IProgressMonitor monitor
) {
1224 Repository repo
= node
.getRepository();
1226 final BranchOperation op
= new BranchOperation(repo
, refName
);
1227 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
1229 public void run(IProgressMonitor myMonitor
)
1230 throws CoreException
{
1231 op
.execute(myMonitor
);
1236 ResourcesPlugin
.getWorkspace().run(wsr
,
1237 ResourcesPlugin
.getWorkspace().getRoot(),
1238 IWorkspace
.AVOID_UPDATE
, monitor
);
1239 Display
.getDefault().syncExec(new Runnable() {
1246 } catch (CoreException e1
) {
1247 return new Status(IStatus
.ERROR
, Activator
.getPluginId(),
1248 e1
.getMessage(), e1
);
1251 return Status
.OK_STATUS
;
1259 private void createImportProjectItem(Menu men
, final Repository repo
,
1260 final String path
) {
1262 MenuItem startWizard
;
1263 startWizard
= new MenuItem(men
, SWT
.PUSH
);
1264 startWizard
.setText(UIText
.RepositoriesView_ImportProjectsMenu
);
1265 startWizard
.addSelectionListener(new SelectionAdapter() {
1268 public void widgetSelected(SelectionEvent e
) {
1269 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
1270 new GitCreateProjectViaWizardWizard(repo
, path
));
1276 // we could start the ImportWizard here,
1277 // unfortunately, this fails within a wizard
1278 // startWizard = new MenuItem(men, SWT.PUSH);
1279 // startWizard.setText("Start the Import wizard...");
1280 // startWizard.addSelectionListener(new SelectionAdapter() {
1283 // public void widgetSelected(SelectionEvent e) {
1285 // IHandlerService handlerService = (IHandlerService) getSite()
1286 // .getWorkbenchWindow().getWorkbench().getService(
1287 // IHandlerService.class);
1290 // handlerService.executeCommand("org.eclipse.ui.file.import", //$NON-NLS-1$
1292 // } catch (ExecutionException e1) {
1293 // Activator.handleError(e1.getMessage(), e1, true);
1294 // } catch (NotDefinedException e1) {
1295 // Activator.handleError(e1.getMessage(), e1, true);
1296 // } catch (NotEnabledException e1) {
1297 // Activator.handleError(e1.getMessage(), e1, true);
1298 // } catch (NotHandledException e1) {
1299 // Activator.handleError(e1.getMessage(), e1, true);
1306 private void addActionsToToolbar() {
1308 IToolBarManager manager
= getViewSite().getActionBars()
1309 .getToolBarManager();
1311 refreshAction
= new Action(UIText
.RepositoriesView_Refresh_Button
) {
1318 refreshAction
.setImageDescriptor(UIIcons
.ELCL16_REFRESH
);
1319 manager
.add(refreshAction
);
1321 linkWithSelectionAction
= new Action(
1322 UIText
.RepositoriesView_LinkWithSelection_action
,
1323 IAction
.AS_CHECK_BOX
) {
1327 IEclipsePreferences prefs
= getPrefs();
1328 prefs
.putBoolean(PREFS_SYNCED
, isChecked());
1331 } catch (BackingStoreException e
) {
1335 ISelectionService srv
= (ISelectionService
) getSite()
1336 .getService(ISelectionService
.class);
1337 reactOnSelection(srv
.getSelection());
1344 linkWithSelectionAction
1345 .setToolTipText(UIText
.RepositoriesView_LinkWithSelection_action
);
1346 linkWithSelectionAction
.setImageDescriptor(UIIcons
.ELCL16_SYNCED
);
1347 linkWithSelectionAction
.setChecked(getPrefs().getBoolean(PREFS_SYNCED
,
1350 manager
.add(linkWithSelectionAction
);
1352 manager
.add(new Separator());
1354 IAction collapseAllAction
= new Action(
1355 UIText
.RepositoriesView_CollapseAllMenu
) {
1362 collapseAllAction
.setImageDescriptor(UIIcons
.COLLAPSEALL
);
1363 manager
.add(collapseAllAction
);
1365 manager
.add(new Separator());
1367 importAction
= new Action(UIText
.RepositoriesView_Import_Button
) {
1371 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
1372 new GitCloneWizard());
1373 if (dlg
.open() == Window
.OK
)
1377 importAction
.setToolTipText(UIText
.RepositoriesView_Clone_Tooltip
);
1378 importAction
.setImageDescriptor(UIIcons
.CLONEGIT
);
1380 manager
.add(importAction
);
1382 addAction
= new Action(UIText
.RepositoriesView_Add_Button
) {
1386 RepositorySearchDialog sd
= new RepositorySearchDialog(
1387 getSite().getShell(), getDirs());
1388 if (sd
.open() == Window
.OK
) {
1389 Set
<String
> dirs
= new HashSet
<String
>();
1390 dirs
.addAll(getDirs());
1391 if (dirs
.addAll(sd
.getDirectories()))
1398 addAction
.setToolTipText(UIText
.RepositoriesView_AddRepository_Tooltip
);
1399 addAction
.setImageDescriptor(UIIcons
.NEW_REPOSITORY
);
1401 manager
.add(addAction
);
1403 // copy and paste are global actions; we just implement them
1404 // and register them with the global action handler
1405 // we enable/disable them upon tree selection changes
1407 copyAction
= new Action("") { //$NON-NLS-1$
1411 // for REPO, WORKINGDIR, FILE, FOLDER: copy directory
1412 IStructuredSelection sel
= (IStructuredSelection
) tv
1414 if (sel
.size() == 1) {
1415 RepositoryTreeNode node
= (RepositoryTreeNode
) sel
1418 if (node
.getType() == RepositoryTreeNodeType
.REPO
) {
1419 dir
= node
.getRepository().getDirectory().getPath();
1420 } else if (node
.getType() == RepositoryTreeNodeType
.FILE
1421 || node
.getType() == RepositoryTreeNodeType
.FOLDER
) {
1422 dir
= ((File
) node
.getObject()).getPath();
1423 } else if (node
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
1424 if (!isBare(node
.getRepository()))
1425 dir
= node
.getRepository().getWorkDir().getPath();
1428 Clipboard clip
= null;
1430 clip
= new Clipboard(getSite().getShell()
1433 .setContents(new Object
[] { dir
},
1434 new Transfer
[] { TextTransfer
1438 // we must dispose ourselves
1446 copyAction
.setEnabled(false);
1448 getViewSite().getActionBars().setGlobalActionHandler(
1449 ActionFactory
.COPY
.getId(), copyAction
);
1451 pasteAction
= new Action("") { //$NON-NLS-1$
1455 // we check if the pasted content is a directory
1456 // repository location and try to add this
1457 String errorMessage
= null;
1459 Clipboard clip
= null;
1461 clip
= new Clipboard(getSite().getShell().getDisplay());
1462 String content
= (String
) clip
.getContents(TextTransfer
1464 if (content
== null) {
1465 errorMessage
= UIText
.RepositoriesView_NothingToPasteMessage
;
1469 File file
= new File(content
);
1470 if (!file
.exists() || !file
.isDirectory()) {
1471 errorMessage
= UIText
.RepositoriesView_ClipboardContentNotDirectoryMessage
;
1475 if (!RepositoryCache
.FileKey
.isGitRepository(file
)) {
1478 UIText
.RepositoriesView_ClipboardContentNoGitRepoMessage
,
1486 errorMessage
= NLS
.bind(
1487 UIText
.RepositoriesView_PasteRepoAlreadyThere
,
1491 // we must dispose ourselves
1493 if (errorMessage
!= null)
1495 MessageDialog
.openWarning(getSite().getShell(),
1496 UIText
.RepositoriesView_PasteFailureTitle
,
1503 getViewSite().getActionBars().setGlobalActionHandler(
1504 ActionFactory
.PASTE
.getId(), pasteAction
);
1509 * @return the preferences
1511 protected static IEclipsePreferences
getPrefs() {
1512 return new InstanceScope().getNode(Activator
.getPluginId());
1516 public void dispose() {
1517 // make sure to cancel the refresh job
1518 if (this.scheduledJob
!= null) {
1519 this.scheduledJob
.cancel();
1520 this.scheduledJob
= null;
1522 // and the auto refresh job, too
1523 if (this.autoRefreshJob
!= null) {
1524 this.autoRefreshJob
.cancel();
1525 this.autoRefreshJob
= null;
1530 @SuppressWarnings("unchecked")
1531 private boolean checkForRepositoryChanges() {
1533 if (tv
.getInput() == null)
1536 final Set
<Repository
> reposToRefresh
= new HashSet
<Repository
>();
1538 RepositoryListener listener
= new RepositoryListener() {
1540 public void refsChanged(RefsChangedEvent e
) {
1541 reposToRefresh
.add(e
.getRepository());
1544 public void indexChanged(IndexChangedEvent e
) {
1545 reposToRefresh
.add(e
.getRepository());
1549 List
<RepositoryTreeNode
<Repository
>> input
= (List
<RepositoryTreeNode
<Repository
>>) tv
1552 for (final RepositoryTreeNode
<Repository
> node
: input
) {
1554 Repository repository
= node
.getRepository();
1555 repository
.addRepositoryChangedListener(listener
);
1557 repository
.scanForRepoChanges();
1558 } catch (IOException e1
) {
1561 repository
.removeRepositoryChangedListener(listener
);
1565 return !reposToRefresh
.isEmpty();
1569 * Schedules a refresh
1571 private void scheduleRefresh() {
1573 Job job
= new Job("Refreshing Git Repositories view") { //$NON-NLS-1$
1575 @SuppressWarnings("unchecked")
1577 protected IStatus
run(IProgressMonitor monitor
) {
1578 // first, let's check if the list of Directories has changed
1579 final List
<String
> directories
= getDirs();
1581 boolean needsNewInput
= tv
.getInput() == null;
1582 List
<RepositoryTreeNode
<Repository
>> oldInput
= (List
) tv
1585 needsNewInput
= oldInput
.size() != directories
.size();
1587 if (!needsNewInput
) {
1588 List
<String
> oldDirectories
= new ArrayList
<String
>();
1589 for (RepositoryTreeNode
<Repository
> node
: oldInput
) {
1590 oldDirectories
.add(node
.getRepository().getDirectory()
1593 needsNewInput
= !directories
.containsAll(oldDirectories
);
1596 final boolean updateInput
= needsNewInput
;
1597 final List newInput
;
1600 newInput
= getRepositoriesFromDirs(monitor
);
1601 } catch (InterruptedException e
) {
1602 return new Status(IStatus
.ERROR
, Activator
1603 .getPluginId(), e
.getMessage(), e
);
1608 // we only check for Repository changes if we don't
1610 if (updateInput
|| checkForRepositoryChanges()) {
1611 Display
.getDefault().asyncExec(new Runnable() {
1613 // keep expansion state and selection so that we can
1616 Object
[] expanded
= tv
.getExpandedElements();
1617 IStructuredSelection sel
= (IStructuredSelection
) tv
1621 tv
.setInput(newInput
);
1624 tv
.setExpandedElements(expanded
);
1626 Object selected
= sel
.getFirstElement();
1627 if (selected
!= null)
1628 tv
.reveal(selected
);
1630 IViewPart part
= PlatformUI
.getWorkbench()
1631 .getActiveWorkbenchWindow().getActivePage()
1632 .findView(IPageLayout
.ID_PROP_SHEET
);
1634 PropertySheet sheet
= (PropertySheet
) part
;
1635 PropertySheetPage page
= (PropertySheetPage
) sheet
1642 return new Status(IStatus
.OK
, Activator
.getPluginId(), ""); //$NON-NLS-1$
1646 job
.setSystem(true);
1648 IWorkbenchSiteProgressService service
= (IWorkbenchSiteProgressService
) getSite()
1649 .getService(IWorkbenchSiteProgressService
.class);
1651 service
.schedule(job
);
1658 * Adds a directory to the list if it is not already there
1661 * @return see {@link Collection#add(Object)}
1663 public static boolean addDir(File file
) {
1667 dirString
= file
.getCanonicalPath();
1668 } catch (IOException e
) {
1669 dirString
= file
.getAbsolutePath();
1672 List
<String
> dirStrings
= getDirs();
1673 if (dirStrings
.contains(dirString
)) {
1676 Set
<String
> dirs
= new HashSet
<String
>();
1677 dirs
.addAll(dirStrings
);
1678 dirs
.add(dirString
);
1685 * Converts the directories as configured for this view into a list of
1686 * {@link Repository} objects suitable for the tree content provider
1688 * TODO move to some utility class
1691 * @return a list of nodes
1692 * @throws InterruptedException
1694 public static List
<RepositoryTreeNode
<Repository
>> getRepositoriesFromDirs(
1695 IProgressMonitor monitor
) throws InterruptedException
{
1697 List
<String
> gitDirStrings
= getDirs();
1698 List
<RepositoryTreeNode
<Repository
>> input
= new ArrayList
<RepositoryTreeNode
<Repository
>>();
1700 for (String dirString
: gitDirStrings
) {
1701 if (monitor
!= null && monitor
.isCanceled()) {
1702 throw new InterruptedException(
1703 UIText
.RepositoriesView_ActionCanceled_Message
);
1706 File dir
= new File(dirString
);
1707 if (dir
.exists() && dir
.isDirectory()) {
1708 Repository repo
= new Repository(dir
);
1709 // reset repository change events here so that check for
1710 // repository changes does not trigger an unnecessary
1712 repo
.scanForRepoChanges();
1713 RepositoryTreeNode
<Repository
> node
= new RepositoryTreeNode
<Repository
>(
1714 null, RepositoryTreeNodeType
.REPO
, repo
, repo
);
1717 } catch (IOException e
) {
1718 IStatus error
= new Status(IStatus
.ERROR
, Activator
1719 .getPluginId(), e
.getMessage(), e
);
1720 Activator
.getDefault().getLog().log(error
);
1723 Collections
.sort(input
);
1727 private static void saveDirs(Set
<String
> gitDirStrings
) {
1728 StringBuilder sb
= new StringBuilder();
1729 for (String gitDirString
: gitDirStrings
) {
1730 sb
.append(gitDirString
);
1731 sb
.append(File
.pathSeparatorChar
);
1734 IEclipsePreferences prefs
= getPrefs();
1735 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
1738 } catch (BackingStoreException e
) {
1739 IStatus error
= new Status(IStatus
.ERROR
, Activator
.getPluginId(),
1741 Activator
.getDefault().getLog().log(error
);
1746 public void setFocus() {
1747 tv
.getTree().setFocus();
1750 @SuppressWarnings("boxing")
1751 private boolean confirmProjectDeletion(List
<IProject
> projectsToDelete
) {
1753 confirmed
= MessageDialog
1755 getSite().getShell(),
1756 UIText
.RepositoriesView_ConfirmProjectDeletion_WindowTitle
,
1759 UIText
.RepositoriesView_ConfirmProjectDeletion_Question
,
1760 projectsToDelete
.size()));
1764 public void addSelectionChangedListener(ISelectionChangedListener listener
) {
1765 selectionListeners
.add(listener
);
1768 public ISelection
getSelection() {
1769 return currentSelection
;
1772 public void removeSelectionChangedListener(
1773 ISelectionChangedListener listener
) {
1774 selectionListeners
.remove(listener
);
1778 public void setSelection(ISelection selection
) {
1779 currentSelection
= selection
;
1780 for (ISelectionChangedListener listener
: selectionListeners
) {
1781 listener
.selectionChanged(new SelectionChangedEvent(
1782 RepositoriesView
.this, selection
));
1787 * Opens the tree and marks the folder to which a project is pointing
1792 @SuppressWarnings("unchecked")
1793 public void showResource(final IResource resource
) {
1794 IProject project
= resource
.getProject();
1795 RepositoryMapping mapping
= RepositoryMapping
.getMapping(project
);
1796 if (mapping
== null)
1799 if (addDir(mapping
.getRepository().getDirectory())) {
1803 boolean doSetSelection
= false;
1805 if (this.scheduledJob
!= null) {
1806 int state
= this.scheduledJob
.getState();
1807 if (state
== Job
.WAITING
|| state
== Job
.RUNNING
) {
1808 this.scheduledJob
.addJobChangeListener(new JobChangeAdapter() {
1811 public void done(IJobChangeEvent event
) {
1812 showResource(resource
);
1816 doSetSelection
= true;
1820 if (doSetSelection
) {
1821 RepositoriesViewContentProvider cp
= (RepositoriesViewContentProvider
) tv
1822 .getContentProvider();
1823 RepositoryTreeNode currentNode
= null;
1824 Object
[] repos
= cp
.getElements(tv
.getInput());
1825 for (Object repo
: repos
) {
1826 RepositoryTreeNode node
= (RepositoryTreeNode
) repo
;
1827 // TODO equals implementation of Repository?
1828 if (mapping
.getRepository().getDirectory().equals(
1829 ((Repository
) node
.getObject()).getDirectory())) {
1830 for (Object child
: cp
.getChildren(node
)) {
1831 RepositoryTreeNode childNode
= (RepositoryTreeNode
) child
;
1832 if (childNode
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
1833 currentNode
= childNode
;
1841 IPath relPath
= new Path(mapping
.getRepoRelativePath(resource
));
1843 for (String segment
: relPath
.segments()) {
1844 for (Object child
: cp
.getChildren(currentNode
)) {
1845 RepositoryTreeNode
<File
> childNode
= (RepositoryTreeNode
<File
>) child
;
1846 if (childNode
.getObject().getName().equals(segment
)) {
1847 currentNode
= childNode
;
1853 final RepositoryTreeNode selNode
= currentNode
;
1855 Display
.getDefault().asyncExec(new Runnable() {
1858 tv
.setSelection(new StructuredSelection(selNode
), true);
1866 public boolean show(ShowInContext context
) {
1867 ISelection selection
= context
.getSelection();
1868 if (selection
instanceof IStructuredSelection
) {
1869 IStructuredSelection ss
= (IStructuredSelection
) selection
;
1870 if (ss
.size() == 1) {
1871 Object element
= ss
.getFirstElement();
1872 if (element
instanceof IAdaptable
) {
1873 IResource resource
= (IResource
) ((IAdaptable
) element
)
1874 .getAdapter(IResource
.class);
1875 if (resource
!= null) {
1876 showResource(resource
);
1885 private void removeRepository(final IProgressMonitor monitor
,
1886 final Repository
... repository
) {
1887 final List
<IProject
> projectsToDelete
= new ArrayList
<IProject
>();
1890 .setTaskName(UIText
.RepositoriesView_DeleteRepoDeterminProjectsMessage
);
1892 for (Repository repo
: repository
) {
1893 File workDir
= repo
.getWorkDir();
1894 final IPath wdPath
= new Path(workDir
.getAbsolutePath());
1895 for (IProject prj
: ResourcesPlugin
.getWorkspace().getRoot()
1897 if (monitor
.isCanceled())
1899 if (wdPath
.isPrefixOf(prj
.getLocation())) {
1900 projectsToDelete
.add(prj
);
1905 if (!projectsToDelete
.isEmpty()) {
1907 confirmed
= confirmProjectDeletion(projectsToDelete
);
1913 if (monitor
.isCanceled())
1916 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
1918 public void run(IProgressMonitor actMonitor
) throws CoreException
{
1920 for (IProject prj
: projectsToDelete
) {
1921 prj
.delete(false, false, actMonitor
);
1923 for (Repository repo
: repository
)
1924 removeDir(repo
.getDirectory());
1930 ResourcesPlugin
.getWorkspace().run(wsr
,
1931 ResourcesPlugin
.getWorkspace().getRoot(),
1932 IWorkspace
.AVOID_UPDATE
, monitor
);
1933 } catch (CoreException e1
) {
1934 Activator
.logError(e1
.getMessage(), e1
);