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
.fetch
.FetchWizard
;
55 import org
.eclipse
.egit
.ui
.internal
.push
.PushConfiguredRemoteAction
;
56 import org
.eclipse
.egit
.ui
.internal
.push
.PushWizard
;
57 import org
.eclipse
.egit
.ui
.internal
.repository
.tree
.RepositoryNode
;
58 import org
.eclipse
.egit
.ui
.internal
.repository
.tree
.RepositoryTreeNode
;
59 import org
.eclipse
.egit
.ui
.internal
.repository
.tree
.RepositoryTreeNodeType
;
60 import org
.eclipse
.jface
.action
.Action
;
61 import org
.eclipse
.jface
.action
.IAction
;
62 import org
.eclipse
.jface
.action
.IToolBarManager
;
63 import org
.eclipse
.jface
.action
.Separator
;
64 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
65 import org
.eclipse
.jface
.dialogs
.ProgressMonitorDialog
;
66 import org
.eclipse
.jface
.operation
.IRunnableWithProgress
;
67 import org
.eclipse
.jface
.viewers
.IOpenListener
;
68 import org
.eclipse
.jface
.viewers
.ISelection
;
69 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
70 import org
.eclipse
.jface
.viewers
.ISelectionProvider
;
71 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
72 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
73 import org
.eclipse
.jface
.viewers
.OpenEvent
;
74 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
75 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
76 import org
.eclipse
.jface
.viewers
.TreeViewer
;
77 import org
.eclipse
.jface
.window
.Window
;
78 import org
.eclipse
.jface
.wizard
.Wizard
;
79 import org
.eclipse
.jface
.wizard
.WizardDialog
;
80 import org
.eclipse
.jgit
.lib
.Constants
;
81 import org
.eclipse
.jgit
.lib
.IndexChangedEvent
;
82 import org
.eclipse
.jgit
.lib
.Ref
;
83 import org
.eclipse
.jgit
.lib
.RefUpdate
;
84 import org
.eclipse
.jgit
.lib
.RefsChangedEvent
;
85 import org
.eclipse
.jgit
.lib
.Repository
;
86 import org
.eclipse
.jgit
.lib
.RepositoryCache
;
87 import org
.eclipse
.jgit
.lib
.RepositoryConfig
;
88 import org
.eclipse
.jgit
.lib
.RepositoryListener
;
89 import org
.eclipse
.jgit
.transport
.RemoteConfig
;
90 import org
.eclipse
.jgit
.util
.FS
;
91 import org
.eclipse
.osgi
.util
.NLS
;
92 import org
.eclipse
.swt
.SWT
;
93 import org
.eclipse
.swt
.dnd
.Clipboard
;
94 import org
.eclipse
.swt
.dnd
.TextTransfer
;
95 import org
.eclipse
.swt
.dnd
.Transfer
;
96 import org
.eclipse
.swt
.events
.MenuDetectEvent
;
97 import org
.eclipse
.swt
.events
.MenuDetectListener
;
98 import org
.eclipse
.swt
.events
.SelectionAdapter
;
99 import org
.eclipse
.swt
.events
.SelectionEvent
;
100 import org
.eclipse
.swt
.graphics
.Point
;
101 import org
.eclipse
.swt
.widgets
.Composite
;
102 import org
.eclipse
.swt
.widgets
.Display
;
103 import org
.eclipse
.swt
.widgets
.Menu
;
104 import org
.eclipse
.swt
.widgets
.MenuItem
;
105 import org
.eclipse
.swt
.widgets
.TreeItem
;
106 import org
.eclipse
.ui
.IEditorInput
;
107 import org
.eclipse
.ui
.IEditorPart
;
108 import org
.eclipse
.ui
.IFileEditorInput
;
109 import org
.eclipse
.ui
.IPageLayout
;
110 import org
.eclipse
.ui
.ISelectionListener
;
111 import org
.eclipse
.ui
.ISelectionService
;
112 import org
.eclipse
.ui
.IViewPart
;
113 import org
.eclipse
.ui
.IWorkbenchPart
;
114 import org
.eclipse
.ui
.PartInitException
;
115 import org
.eclipse
.ui
.PlatformUI
;
116 import org
.eclipse
.ui
.actions
.ActionFactory
;
117 import org
.eclipse
.ui
.editors
.text
.EditorsUI
;
118 import org
.eclipse
.ui
.ide
.FileStoreEditorInput
;
119 import org
.eclipse
.ui
.ide
.IDE
;
120 import org
.eclipse
.ui
.part
.IShowInTarget
;
121 import org
.eclipse
.ui
.part
.ShowInContext
;
122 import org
.eclipse
.ui
.part
.ViewPart
;
123 import org
.eclipse
.ui
.progress
.IWorkbenchSiteProgressService
;
124 import org
.eclipse
.ui
.views
.properties
.IPropertySheetPage
;
125 import org
.eclipse
.ui
.views
.properties
.PropertySheet
;
126 import org
.eclipse
.ui
.views
.properties
.PropertySheetPage
;
127 import org
.osgi
.service
.prefs
.BackingStoreException
;
131 * The Git Repositories view.
133 * This keeps track of a bunch of local directory names each of which represent
134 * a Git Repository. This list is stored in some Preferences object and used to
135 * build the tree in the view.
137 * Implements {@link ISelectionProvider} in order to integrate with the
140 * This periodically refreshes itself in order to react on Repository changes.
142 public class RepositoriesView
extends ViewPart
implements ISelectionProvider
,
146 public static final String VIEW_ID
= "org.eclipse.egit.ui.RepositoriesView"; //$NON-NLS-1$
148 // TODO central constants? RemoteConfig ones are private
149 static final String REMOTE
= "remote"; //$NON-NLS-1$
151 static final String URL
= "url"; //$NON-NLS-1$
153 static final String PUSHURL
= "pushurl"; //$NON-NLS-1$
155 static final String FETCH
= "fetch"; //$NON-NLS-1$
157 static final String PUSH
= "push"; //$NON-NLS-1$
159 private static final String PREFS_DIRECTORIES
= "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$
161 private static final String PREFS_SYNCED
= "GitRepositoriesView.SyncWithSelection"; //$NON-NLS-1$
163 private final Set
<Repository
> repositories
= new HashSet
<Repository
>();
165 private final List
<ISelectionChangedListener
> selectionListeners
= new ArrayList
<ISelectionChangedListener
>();
167 private RepositoryListener repositoryListener
;
169 private ISelection currentSelection
= new StructuredSelection();
171 private Job scheduledJob
;
173 private TreeViewer tv
;
175 private IAction importAction
;
177 private IAction addAction
;
179 private IAction refreshAction
;
181 private IAction linkWithSelectionAction
;
183 private IAction copyAction
;
185 private IAction pasteAction
;
188 * TODO move to utility class
190 * @return the directories as configured for this view
192 public static List
<String
> getDirs() {
193 List
<String
> resultStrings
= new ArrayList
<String
>();
194 String dirs
= getPrefs().get(PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
195 if (dirs
!= null && dirs
.length() > 0) {
196 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
197 while (tok
.hasMoreTokens()) {
198 String dirName
= tok
.nextToken();
199 File testFile
= new File(dirName
);
200 if (testFile
.exists() && !resultStrings
.contains(dirName
)) {
201 resultStrings
.add(dirName
);
205 Collections
.sort(resultStrings
);
206 return resultStrings
;
209 private static void removeDir(File file
) {
213 dir
= file
.getCanonicalPath();
214 } catch (IOException e1
) {
215 dir
= file
.getAbsolutePath();
218 IEclipsePreferences prefs
= getPrefs();
220 TreeSet
<String
> resultStrings
= new TreeSet
<String
>();
221 String dirs
= prefs
.get(PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
222 if (dirs
!= null && dirs
.length() > 0) {
223 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
224 while (tok
.hasMoreTokens()) {
225 String dirName
= tok
.nextToken();
226 File testFile
= new File(dirName
);
227 if (testFile
.exists()) {
229 resultStrings
.add(testFile
.getCanonicalPath());
230 } catch (IOException e
) {
231 resultStrings
.add(testFile
.getAbsolutePath());
237 if (resultStrings
.remove(dir
)) {
238 StringBuilder sb
= new StringBuilder();
239 for (String gitDirString
: resultStrings
) {
240 sb
.append(gitDirString
);
241 sb
.append(File
.pathSeparatorChar
);
244 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
247 } catch (BackingStoreException e
) {
248 IStatus error
= new Status(IStatus
.ERROR
, Activator
249 .getPluginId(), e
.getMessage(), e
);
250 Activator
.getDefault().getLog().log(error
);
257 public Object
getAdapter(Class adapter
) {
258 // integrate with Properties view
259 if (adapter
== IPropertySheetPage
.class) {
260 PropertySheetPage page
= new PropertySheetPage();
262 .setPropertySourceProvider(new RepositoryPropertySourceProvider(
267 return super.getAdapter(adapter
);
271 public void createPartControl(Composite parent
) {
272 tv
= new TreeViewer(parent
, SWT
.MULTI
| SWT
.H_SCROLL
| SWT
.V_SCROLL
);
273 tv
.setContentProvider(new RepositoriesViewContentProvider());
274 // the label provider registers itself
275 new RepositoriesViewLabelProvider(tv
);
277 getSite().setSelectionProvider(this);
279 tv
.addSelectionChangedListener(new ISelectionChangedListener() {
281 public void selectionChanged(SelectionChangedEvent event
) {
283 copyAction
.setEnabled(false);
285 IStructuredSelection ssel
= (IStructuredSelection
) event
287 if (ssel
.size() == 1) {
288 RepositoryTreeNode node
= (RepositoryTreeNode
) ssel
290 // allow copy on repository, file, or folder (copying the
292 if (node
.getType() == RepositoryTreeNodeType
.REPO
293 || node
.getType() == RepositoryTreeNodeType
.WORKINGDIR
294 || node
.getType() == RepositoryTreeNodeType
.FOLDER
295 || node
.getType() == RepositoryTreeNodeType
.FILE
) {
296 copyAction
.setEnabled(true);
298 setSelection(new StructuredSelection(ssel
.getFirstElement()));
300 setSelection(new StructuredSelection());
305 tv
.addOpenListener(new IOpenListener() {
306 public void open(OpenEvent event
) {
307 IStructuredSelection selection
= (IStructuredSelection
) event
309 if (selection
.isEmpty()) {
310 // nothing selected, ignore
314 Object element
= selection
.getFirstElement();
315 ITreeContentProvider contentProvider
= (ITreeContentProvider
) tv
316 .getContentProvider();
317 if (contentProvider
.hasChildren(element
)) {
318 // this element has children, expand/collapse it
319 tv
.setExpandedState(element
, !tv
.getExpandedState(element
));
321 Object
[] selectionArray
= selection
.toArray();
322 for (Object selectedElement
: selectionArray
) {
323 RepositoryTreeNode node
= (RepositoryTreeNode
) selectedElement
;
324 // if any of the selected elements are not files, ignore
326 if (node
.getType() != RepositoryTreeNodeType
.FILE
327 && node
.getType() != RepositoryTreeNodeType
.REF
328 && node
.getType() != RepositoryTreeNodeType
.TAG
) {
333 // open the files the user has selected
334 for (Object selectedElement
: selectionArray
) {
335 RepositoryTreeNode node
= (RepositoryTreeNode
) selectedElement
;
336 if (node
.getType() == RepositoryTreeNodeType
.FILE
)
337 openFile((File
) node
.getObject());
338 else if (node
.getType() == RepositoryTreeNodeType
.REF
339 || node
.getType() == RepositoryTreeNodeType
.TAG
) {
340 Ref ref
= (Ref
) node
.getObject();
341 if (!isBare(node
.getRepository())
342 && ref
.getName().startsWith(
344 checkoutBranch(node
, ref
.getName());
351 createRepositoryChangedListener();
355 addActionsToToolbar();
359 ISelectionService srv
= (ISelectionService
) getSite().getService(
360 ISelectionService
.class);
361 srv
.addPostSelectionListener(new ISelectionListener() {
363 public void selectionChanged(IWorkbenchPart part
,
364 ISelection selection
) {
366 // if the "link with selection" toggle is off, we're done
367 if (linkWithSelectionAction
== null
368 || !linkWithSelectionAction
.isChecked())
371 // this may happen if we switch between editors
372 if (part
instanceof IEditorPart
) {
373 IEditorInput input
= ((IEditorPart
) part
).getEditorInput();
374 if (input
instanceof IFileEditorInput
)
375 reactOnSelection(new StructuredSelection(
376 ((IFileEditorInput
) input
).getFile()));
379 reactOnSelection(selection
);
386 private void reactOnSelection(ISelection selection
) {
387 if (selection
instanceof StructuredSelection
) {
388 StructuredSelection ssel
= (StructuredSelection
) selection
;
389 if (ssel
.size() != 1)
391 if (ssel
.getFirstElement() instanceof IResource
) {
392 showResource((IResource
) ssel
.getFirstElement());
394 if (ssel
.getFirstElement() instanceof IAdaptable
) {
395 IResource adapted
= (IResource
) ((IAdaptable
) ssel
396 .getFirstElement()).getAdapter(IResource
.class);
398 showResource(adapted
);
403 private void addContextMenu() {
404 tv
.getTree().addMenuDetectListener(new MenuDetectListener() {
406 public void menuDetected(MenuDetectEvent e
) {
407 Menu men
= tv
.getTree().getMenu();
411 men
= new Menu(tv
.getTree());
413 TreeItem testItem
= tv
.getTree().getItem(
414 tv
.getTree().toControl(new Point(e
.x
, e
.y
)));
415 if (testItem
== null) {
416 addMenuItemsForPanel(men
);
418 addMenuItemsForTreeSelection(men
);
421 tv
.getTree().setMenu(men
);
426 private void addMenuItemsForPanel(Menu men
) {
428 MenuItem importItem
= new MenuItem(men
, SWT
.PUSH
);
429 importItem
.setText(UIText
.RepositoriesView_ImportRepository_MenuItem
);
430 importItem
.addSelectionListener(new SelectionAdapter() {
433 public void widgetSelected(SelectionEvent e
) {
439 MenuItem addItem
= new MenuItem(men
, SWT
.PUSH
);
440 addItem
.setText(UIText
.RepositoriesView_AddRepository_MenuItem
);
441 addItem
.addSelectionListener(new SelectionAdapter() {
444 public void widgetSelected(SelectionEvent e
) {
450 MenuItem pasteItem
= new MenuItem(men
, SWT
.PUSH
);
451 pasteItem
.setText(UIText
.RepositoriesView_PasteMenu
);
452 pasteItem
.addSelectionListener(new SelectionAdapter() {
455 public void widgetSelected(SelectionEvent e
) {
461 MenuItem refreshItem
= new MenuItem(men
, SWT
.PUSH
);
462 refreshItem
.setText(refreshAction
.getText());
463 refreshItem
.addSelectionListener(new SelectionAdapter() {
466 public void widgetSelected(SelectionEvent e
) {
474 private void addMenuItemsForTreeSelection(Menu men
) {
476 final IStructuredSelection sel
= (IStructuredSelection
) tv
479 boolean repoOnly
= true;
480 for (Object selected
: sel
.toArray()) {
482 if (((RepositoryTreeNode
) selected
).getType() != RepositoryTreeNodeType
.REPO
) {
488 if (sel
.size() > 1 && repoOnly
) {
489 List nodes
= sel
.toList();
490 final Repository
[] repos
= new Repository
[nodes
.size()];
491 for (int i
= 0; i
< sel
.size(); i
++)
492 repos
[i
] = ((RepositoryTreeNode
) nodes
.get(i
)).getRepository();
494 MenuItem remove
= new MenuItem(men
, SWT
.PUSH
);
495 remove
.setText(UIText
.RepositoriesView_Remove_MenuItem
);
496 remove
.addSelectionListener(new SelectionAdapter() {
499 public void widgetSelected(SelectionEvent e
) {
500 // TODO progress monitoring/cancellation
501 removeRepository(new NullProgressMonitor(), repos
);
507 // from here on, we only deal with single selection
511 final RepositoryTreeNode node
= (RepositoryTreeNode
) sel
514 final boolean isBare
= isBare(node
.getRepository());
516 if (node
.getType() == RepositoryTreeNodeType
.REF
) {
518 final Ref ref
= (Ref
) node
.getObject();
520 // we don't check out symbolic references
521 if (!ref
.isSymbolic()) {
524 MenuItem checkout
= new MenuItem(men
, SWT
.PUSH
);
525 checkout
.setText(UIText
.RepositoriesView_CheckOut_MenuItem
);
527 checkout
.setEnabled(!isRefCheckedOut(node
.getRepository(),
530 checkout
.addSelectionListener(new SelectionAdapter() {
533 public void widgetSelected(SelectionEvent e
) {
534 checkoutBranch(node
, ref
.getLeaf().getName());
538 new MenuItem(men
, SWT
.SEPARATOR
);
541 createCreateBranchItem(men
, node
);
542 createDeleteBranchItem(men
, node
);
547 if (node
.getType() == RepositoryTreeNodeType
.TAG
) {
549 final Ref ref
= (Ref
) node
.getObject();
551 MenuItem checkout
= new MenuItem(men
, SWT
.PUSH
);
552 checkout
.setText(UIText
.RepositoriesView_CheckOut_MenuItem
);
554 checkout
.setEnabled(!isRefCheckedOut(node
.getRepository(), ref
557 checkout
.addSelectionListener(new SelectionAdapter() {
560 public void widgetSelected(SelectionEvent e
) {
561 checkoutBranch(node
, ref
.getLeaf().getName());
566 if (node
.getType() == RepositoryTreeNodeType
.BRANCHES
567 || node
.getType() == RepositoryTreeNodeType
.LOCALBRANCHES
)
568 // offering this on the "Remote Branches" node would probably be
570 createCreateBranchItem(men
, node
);
572 // for Repository: import existing projects, remove, (delete), open
574 if (node
.getType() == RepositoryTreeNodeType
.REPO
) {
576 final Repository repo
= (Repository
) node
.getObject();
578 // TODO "import existing plug-in" menu item
580 MenuItem remove
= new MenuItem(men
, SWT
.PUSH
);
581 remove
.setText(UIText
.RepositoriesView_Remove_MenuItem
);
582 remove
.addSelectionListener(new SelectionAdapter() {
585 public void widgetSelected(SelectionEvent e
) {
586 // TODO progress monitoring/cancellation
587 removeRepository(new NullProgressMonitor(), repo
);
591 new MenuItem(men
, SWT
.SEPARATOR
);
593 MenuItem fetchItem
= new MenuItem(men
, SWT
.PUSH
);
594 fetchItem
.setText(UIText
.RepositoriesView_FetchMenu
);
595 fetchItem
.setImage(UIIcons
.FETCH
.createImage());
596 fetchItem
.addSelectionListener(new SelectionAdapter() {
599 public void widgetSelected(SelectionEvent e
) {
601 new WizardDialog(getSite().getShell(), new FetchWizard(
603 } catch (URISyntaxException e1
) {
604 Activator
.handleError(e1
.getMessage(), e1
, true);
610 MenuItem pushItem
= new MenuItem(men
, SWT
.PUSH
);
611 pushItem
.setText(UIText
.RepositoriesView_PushMenu
);
612 pushItem
.setImage(UIIcons
.PUSH
.createImage());
613 pushItem
.addSelectionListener(new SelectionAdapter() {
616 public void widgetSelected(SelectionEvent e
) {
618 new WizardDialog(getSite().getShell(), new PushWizard(
620 } catch (URISyntaxException e1
) {
621 Activator
.handleError(e1
.getMessage(), e1
, true);
627 // TODO delete does not work because of file locks on .pack-files
628 // Shawn Pearce has added the following thoughts:
630 // Hmm. We probably can't active detect file locks on pack files on
632 // It would be nice if we could support a delete, but only if the
634 // reasonably believed to be not-in-use right now.
636 // Within EGit you might be able to check GitProjectData and its
637 // repositoryCache to
638 // see if the repository is open by this workspace. If it is, then
639 // we know we shouldn't
642 // Some coding might look like this:
644 // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH);
645 // deleteRepo.setText("Delete");
646 // deleteRepo.addSelectionListener(new SelectionAdapter() {
649 // public void widgetSelected(SelectionEvent e) {
651 // boolean confirmed = MessageDialog.openConfirm(getSite()
652 // .getShell(), "Confirm",
653 // "This will delete the repository, continue?");
658 // IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
660 // public void run(IProgressMonitor monitor)
661 // throws CoreException {
662 // File workDir = repos.get(0).getRepository()
665 // File gitDir = repos.get(0).getRepository()
668 // IPath wdPath = new Path(workDir.getAbsolutePath());
669 // for (IProject prj : ResourcesPlugin.getWorkspace()
670 // .getRoot().getProjects()) {
671 // if (wdPath.isPrefixOf(prj.getLocation())) {
672 // prj.delete(false, false, monitor);
676 // repos.get(0).getRepository().close();
678 // boolean deleted = deleteRecursively(gitDir, monitor);
680 // MessageDialog.openError(getSite().getShell(),
682 // "Could not delete Git Repository");
685 // deleted = deleteRecursively(workDir, monitor);
688 // .openError(getSite().getShell(),
690 // "Could not delete Git Working Directory");
693 // scheduleRefresh();
696 // private boolean deleteRecursively(File fileToDelete,
697 // IProgressMonitor monitor) {
698 // if (fileToDelete.isDirectory()) {
699 // for (File file : fileToDelete.listFiles()) {
700 // if (!deleteRecursively(file, monitor)) {
705 // monitor.setTaskName(fileToDelete.getAbsolutePath());
706 // boolean deleted = fileToDelete.delete();
708 // System.err.println("Could not delete "
709 // + fileToDelete.getAbsolutePath());
716 // ResourcesPlugin.getWorkspace().run(wsr,
717 // ResourcesPlugin.getWorkspace().getRoot(),
718 // IWorkspace.AVOID_UPDATE,
719 // new NullProgressMonitor());
720 // } catch (CoreException e1) {
721 // // TODO Exception handling
722 // e1.printStackTrace();
729 new MenuItem(men
, SWT
.SEPARATOR
);
732 createImportProjectItem(men
, repo
, repo
.getWorkDir().getPath());
734 new MenuItem(men
, SWT
.SEPARATOR
);
737 MenuItem openPropsView
= new MenuItem(men
, SWT
.PUSH
);
738 openPropsView
.setText(UIText
.RepositoriesView_OpenPropertiesMenu
);
739 openPropsView
.addSelectionListener(new SelectionAdapter() {
742 public void widgetSelected(SelectionEvent e
) {
744 PlatformUI
.getWorkbench().getActiveWorkbenchWindow()
745 .getActivePage().showView(
746 IPageLayout
.ID_PROP_SHEET
);
747 } catch (PartInitException e1
) {
754 new MenuItem(men
, SWT
.SEPARATOR
);
756 createCopyPathItem(men
, repo
.getDirectory().getPath());
759 if (node
.getType() == RepositoryTreeNodeType
.REMOTES
) {
761 MenuItem remoteConfig
= new MenuItem(men
, SWT
.PUSH
);
762 remoteConfig
.setText(UIText
.RepositoriesView_NewRemoteMenu
);
763 remoteConfig
.addSelectionListener(new SelectionAdapter() {
766 public void widgetSelected(SelectionEvent e
) {
768 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
769 new NewRemoteWizard(node
.getRepository()));
770 if (dlg
.open() == Window
.OK
)
778 if (node
.getType() == RepositoryTreeNodeType
.REMOTE
) {
780 final String configName
= (String
) node
.getObject();
782 RemoteConfig rconfig
;
784 rconfig
= new RemoteConfig(node
.getRepository().getConfig(),
786 } catch (URISyntaxException e2
) {
787 // TODO Exception handling
791 boolean fetchExists
= rconfig
!= null
792 && !rconfig
.getURIs().isEmpty();
793 boolean pushExists
= rconfig
!= null
794 && !rconfig
.getPushURIs().isEmpty();
797 MenuItem configureUrlFetch
= new MenuItem(men
, SWT
.PUSH
);
799 .setText(UIText
.RepositoriesView_CreateFetch_menu
);
801 configureUrlFetch
.addSelectionListener(new SelectionAdapter() {
804 public void widgetSelected(SelectionEvent e
) {
806 WizardDialog dlg
= new WizardDialog(getSite()
807 .getShell(), new ConfigureRemoteWizard(node
808 .getRepository(), configName
, false));
809 if (dlg
.open() == Window
.OK
)
818 MenuItem configureUrlPush
= new MenuItem(men
, SWT
.PUSH
);
821 .setText(UIText
.RepositoriesView_CreatePush_menu
);
823 configureUrlPush
.addSelectionListener(new SelectionAdapter() {
826 public void widgetSelected(SelectionEvent e
) {
828 WizardDialog dlg
= new WizardDialog(getSite()
829 .getShell(), new ConfigureRemoteWizard(node
830 .getRepository(), configName
, true));
831 if (dlg
.open() == Window
.OK
)
839 if (!fetchExists
|| !pushExists
)
840 // add a separator dynamically
841 new MenuItem(men
, SWT
.SEPARATOR
);
843 MenuItem removeRemote
= new MenuItem(men
, SWT
.PUSH
);
844 removeRemote
.setText(UIText
.RepositoriesView_RemoveRemoteMenu
);
845 removeRemote
.addSelectionListener(new SelectionAdapter() {
848 public void widgetSelected(SelectionEvent e
) {
850 boolean ok
= MessageDialog
852 getSite().getShell(),
853 UIText
.RepositoriesView_ConfirmDeleteRemoteHeader
,
856 UIText
.RepositoriesView_ConfirmDeleteRemoteMessage
,
859 RepositoryConfig config
= node
.getRepository()
861 config
.unsetSection(REMOTE
, configName
);
865 } catch (IOException e1
) {
866 Activator
.handleError(
867 UIText
.RepositoriesView_ErrorHeader
, e1
,
876 new MenuItem(men
, SWT
.SEPARATOR
);
878 MenuItem openPropsView
= new MenuItem(men
, SWT
.PUSH
);
879 openPropsView
.setText(UIText
.RepositoriesView_OpenPropertiesMenu
);
880 openPropsView
.addSelectionListener(new SelectionAdapter() {
883 public void widgetSelected(SelectionEvent e
) {
885 PlatformUI
.getWorkbench().getActiveWorkbenchWindow()
886 .getActivePage().showView(
887 IPageLayout
.ID_PROP_SHEET
);
888 } catch (PartInitException e1
) {
896 if (node
.getType() == RepositoryTreeNodeType
.FETCH
) {
898 final String configName
= (String
) node
.getParent().getObject();
900 MenuItem doFetch
= new MenuItem(men
, SWT
.PUSH
);
901 doFetch
.setText(UIText
.RepositoriesView_DoFetchMenu
);
902 doFetch
.addSelectionListener(new SelectionAdapter() {
905 public void widgetSelected(SelectionEvent evt
) {
906 new FetchConfiguredRemoteAction(node
.getRepository(),
907 configName
).run(getSite().getShell());
912 MenuItem configureUrlFetch
= new MenuItem(men
, SWT
.PUSH
);
914 .setText(UIText
.RepositoriesView_ConfigureFetchMenu
);
916 configureUrlFetch
.addSelectionListener(new SelectionAdapter() {
919 public void widgetSelected(SelectionEvent e
) {
921 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
922 new ConfigureRemoteWizard(node
.getRepository(),
924 if (dlg
.open() == Window
.OK
)
931 MenuItem deleteFetch
= new MenuItem(men
, SWT
.PUSH
);
932 deleteFetch
.setText(UIText
.RepositoriesView_RemoveFetch_menu
);
933 deleteFetch
.addSelectionListener(new SelectionAdapter() {
936 public void widgetSelected(SelectionEvent e
) {
937 RepositoryConfig config
= node
.getRepository().getConfig();
938 config
.unset("remote", configName
, "url"); //$NON-NLS-1$ //$NON-NLS-2$
939 config
.unset("remote", configName
, "fetch"); //$NON-NLS-1$//$NON-NLS-2$
943 } catch (IOException e1
) {
944 MessageDialog
.openError(getSite().getShell(),
945 UIText
.RepositoriesView_ErrorHeader
, e1
954 if (node
.getType() == RepositoryTreeNodeType
.PUSH
) {
956 final String configName
= (String
) node
.getParent().getObject();
958 MenuItem doPush
= new MenuItem(men
, SWT
.PUSH
);
959 doPush
.setText(UIText
.RepositoriesView_DoPushMenuItem
);
960 doPush
.addSelectionListener(new SelectionAdapter() {
963 public void widgetSelected(SelectionEvent evt
) {
964 new PushConfiguredRemoteAction(node
.getRepository(),
965 configName
).run(getSite().getShell(), false);
969 MenuItem configureUrlPush
= new MenuItem(men
, SWT
.PUSH
);
970 configureUrlPush
.setText(UIText
.RepositoriesView_ConfigurePushMenu
);
972 configureUrlPush
.addSelectionListener(new SelectionAdapter() {
975 public void widgetSelected(SelectionEvent e
) {
977 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
978 new ConfigureRemoteWizard(node
.getRepository(),
980 if (dlg
.open() == Window
.OK
)
986 MenuItem deleteFetch
= new MenuItem(men
, SWT
.PUSH
);
987 deleteFetch
.setText(UIText
.RepositoriesView_RemovePush_menu
);
988 deleteFetch
.addSelectionListener(new SelectionAdapter() {
991 public void widgetSelected(SelectionEvent e
) {
992 RepositoryConfig config
= node
.getRepository().getConfig();
993 config
.unset("remote", configName
, "pushurl"); //$NON-NLS-1$ //$NON-NLS-2$
994 config
.unset("remote", configName
, "push"); //$NON-NLS-1$ //$NON-NLS-2$
998 } catch (IOException e1
) {
999 MessageDialog
.openError(getSite().getShell(),
1000 UIText
.RepositoriesView_ErrorHeader
, e1
1008 if (node
.getType() == RepositoryTreeNodeType
.FILE
) {
1010 final File file
= (File
) node
.getObject();
1012 MenuItem openInTextEditor
= new MenuItem(men
, SWT
.PUSH
);
1014 .setText(UIText
.RepositoriesView_OpenInTextEditor_menu
);
1015 openInTextEditor
.addSelectionListener(new SelectionAdapter() {
1018 public void widgetSelected(SelectionEvent e
) {
1024 new MenuItem(men
, SWT
.SEPARATOR
);
1025 createCopyPathItem(men
, file
.getPath());
1028 if (!isBare
&& node
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
1029 String path
= node
.getRepository().getWorkDir().getAbsolutePath();
1030 createImportProjectItem(men
, node
.getRepository(), path
);
1031 new MenuItem(men
, SWT
.SEPARATOR
);
1032 createCopyPathItem(men
, path
);
1035 if (node
.getType() == RepositoryTreeNodeType
.FOLDER
) {
1036 String path
= ((File
) node
.getObject()).getPath();
1037 createImportProjectItem(men
, node
.getRepository(), path
);
1038 new MenuItem(men
, SWT
.SEPARATOR
);
1039 createCopyPathItem(men
, path
);
1044 private boolean isBare(Repository repository
) {
1045 return repository
.getConfig().getBoolean("core", "bare", false); //$NON-NLS-1$ //$NON-NLS-2$
1048 private boolean isRefCheckedOut(Repository repository
, String refName
) {
1050 String compareString
;
1053 branchName
= repository
.getFullBranch();
1054 if (branchName
== null)
1056 if (refName
.startsWith(Constants
.R_HEADS
)) {
1057 // local branch: HEAD would be on the branch
1058 compareString
= refName
;
1059 } else if (refName
.startsWith(Constants
.R_TAGS
)) {
1060 // tag: HEAD would be on the commit id to which the tag is
1062 compareString
= repository
.mapTag(refName
).getObjId().getName();
1063 } else if (refName
.startsWith(Constants
.R_REMOTES
)) {
1064 // remote branch: HEAD would be on the commit id to which
1065 // the branch is pointing
1066 compareString
= repository
.mapCommit(refName
).getCommitId()
1069 // some other symbolic reference
1072 } catch (IOException e1
) {
1076 return compareString
.equals(branchName
);
1079 private void createCopyPathItem(Menu men
, final String path
) {
1082 copyPath
= new MenuItem(men
, SWT
.PUSH
);
1083 copyPath
.setText(UIText
.RepositoriesView_CopyPathToClipboardMenu
);
1084 copyPath
.addSelectionListener(new SelectionAdapter() {
1087 public void widgetSelected(SelectionEvent e
) {
1088 Clipboard clipboard
= new Clipboard(null);
1089 TextTransfer textTransfer
= TextTransfer
.getInstance();
1090 Transfer
[] transfers
= new Transfer
[] { textTransfer
};
1091 Object
[] data
= new Object
[] { path
};
1092 clipboard
.setContents(data
, transfers
);
1093 clipboard
.dispose();
1100 private void createCreateBranchItem(Menu men
, final RepositoryTreeNode node
) {
1102 if (node
.getType() == RepositoryTreeNodeType
.REF
)
1103 ref
= (Ref
) node
.getObject();
1107 MenuItem createLocal
= new MenuItem(men
, SWT
.PUSH
);
1108 createLocal
.setText(UIText
.RepositoriesView_NewBranchMenu
);
1110 createLocal
.addSelectionListener(new SelectionAdapter() {
1113 public void widgetSelected(SelectionEvent e
) {
1115 Wizard wiz
= new Wizard() {
1118 public void addPages() {
1119 addPage(new CreateBranchPage(node
.getRepository(), ref
));
1120 setWindowTitle(UIText
.RepositoriesView_NewBranchTitle
);
1124 public boolean performFinish() {
1127 getContainer().run(false, true,
1128 new IRunnableWithProgress() {
1130 public void run(IProgressMonitor monitor
)
1131 throws InvocationTargetException
,
1132 InterruptedException
{
1133 CreateBranchPage cp
= (CreateBranchPage
) getPages()[0];
1135 cp
.createBranch(monitor
);
1136 } catch (CoreException ce
) {
1137 throw new InvocationTargetException(
1139 } catch (IOException ioe
) {
1140 throw new InvocationTargetException(
1146 } catch (InvocationTargetException ite
) {
1149 UIText
.RepositoriesView_BranchCreationFailureMessage
,
1150 ite
.getCause(), true);
1152 } catch (InterruptedException ie
) {
1158 if (new WizardDialog(getSite().getShell(), wiz
).open() == Window
.OK
)
1165 private void createDeleteBranchItem(Menu men
, final RepositoryTreeNode node
) {
1167 final Ref ref
= (Ref
) node
.getObject();
1169 MenuItem deleteBranch
= new MenuItem(men
, SWT
.PUSH
);
1170 deleteBranch
.setText(UIText
.RepositoriesView_DeleteBranchMenu
);
1172 deleteBranch
.setEnabled(!isRefCheckedOut(node
.getRepository(), ref
1175 deleteBranch
.addSelectionListener(new SelectionAdapter() {
1178 public void widgetSelected(SelectionEvent e
) {
1182 getSite().getShell(),
1183 UIText
.RepositoriesView_ConfirmDeleteTitle
,
1186 UIText
.RepositoriesView_ConfirmBranchDeletionMessage
,
1191 new ProgressMonitorDialog(getSite().getShell()).run(false,
1192 false, new IRunnableWithProgress() {
1194 public void run(IProgressMonitor monitor
)
1195 throws InvocationTargetException
,
1196 InterruptedException
{
1199 RefUpdate op
= node
.getRepository()
1200 .updateRef(ref
.getName());
1201 op
.setRefLogMessage("branch deleted", //$NON-NLS-1$
1203 // we set the force update in order
1204 // to avoid having this rejected
1205 // due to minor issues
1206 op
.setForceUpdate(true);
1209 } catch (IOException ioe
) {
1210 throw new InvocationTargetException(ioe
);
1215 } catch (InvocationTargetException e1
) {
1218 UIText
.RepositoriesView_BranchDeletionFailureMessage
,
1219 e1
.getCause(), true);
1220 e1
.printStackTrace();
1221 } catch (InterruptedException e1
) {
1230 private void openFile(File file
) {
1231 IFileStore store
= EFS
.getLocalFileSystem().getStore(
1232 new Path(file
.getAbsolutePath()));
1234 // TODO do we need a read-only editor here?
1235 IDE
.openEditor(getSite().getPage(),
1236 new FileStoreEditorInput(store
),
1237 EditorsUI
.DEFAULT_TEXT_EDITOR_ID
);
1238 } catch (PartInitException e
) {
1239 Activator
.handleError(UIText
.RepositoriesView_Error_WindowTitle
, e
,
1244 private void checkoutBranch(final RepositoryTreeNode node
,
1245 final String refName
) {
1246 // for the sake of UI responsiveness, let's start a job
1247 Job job
= new Job(NLS
.bind(UIText
.RepositoriesView_CheckingOutMessage
,
1251 protected IStatus
run(IProgressMonitor monitor
) {
1253 Repository repo
= node
.getRepository();
1255 final BranchOperation op
= new BranchOperation(repo
, refName
);
1256 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
1258 public void run(IProgressMonitor myMonitor
)
1259 throws CoreException
{
1260 op
.execute(myMonitor
);
1265 ResourcesPlugin
.getWorkspace().run(wsr
,
1266 ResourcesPlugin
.getWorkspace().getRoot(),
1267 IWorkspace
.AVOID_UPDATE
, monitor
);
1268 Display
.getDefault().syncExec(new Runnable() {
1275 } catch (CoreException e1
) {
1276 return new Status(IStatus
.ERROR
, Activator
.getPluginId(),
1277 e1
.getMessage(), e1
);
1280 return Status
.OK_STATUS
;
1288 private void createImportProjectItem(Menu men
, final Repository repo
,
1289 final String path
) {
1291 MenuItem startWizard
;
1292 startWizard
= new MenuItem(men
, SWT
.PUSH
);
1293 startWizard
.setText(UIText
.RepositoriesView_ImportProjectsMenu
);
1294 startWizard
.addSelectionListener(new SelectionAdapter() {
1297 public void widgetSelected(SelectionEvent e
) {
1298 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
1299 new GitCreateProjectViaWizardWizard(repo
, path
));
1305 // we could start the ImportWizard here,
1306 // unfortunately, this fails within a wizard
1307 // startWizard = new MenuItem(men, SWT.PUSH);
1308 // startWizard.setText("Start the Import wizard...");
1309 // startWizard.addSelectionListener(new SelectionAdapter() {
1312 // public void widgetSelected(SelectionEvent e) {
1314 // IHandlerService handlerService = (IHandlerService) getSite()
1315 // .getWorkbenchWindow().getWorkbench().getService(
1316 // IHandlerService.class);
1319 // handlerService.executeCommand("org.eclipse.ui.file.import", //$NON-NLS-1$
1321 // } catch (ExecutionException e1) {
1322 // Activator.handleError(e1.getMessage(), e1, true);
1323 // } catch (NotDefinedException e1) {
1324 // Activator.handleError(e1.getMessage(), e1, true);
1325 // } catch (NotEnabledException e1) {
1326 // Activator.handleError(e1.getMessage(), e1, true);
1327 // } catch (NotHandledException e1) {
1328 // Activator.handleError(e1.getMessage(), e1, true);
1335 private void addActionsToToolbar() {
1337 IToolBarManager manager
= getViewSite().getActionBars()
1338 .getToolBarManager();
1340 refreshAction
= new Action(UIText
.RepositoriesView_Refresh_Button
) {
1347 refreshAction
.setImageDescriptor(UIIcons
.ELCL16_REFRESH
);
1348 manager
.add(refreshAction
);
1350 linkWithSelectionAction
= new Action(
1351 UIText
.RepositoriesView_LinkWithSelection_action
,
1352 IAction
.AS_CHECK_BOX
) {
1356 IEclipsePreferences prefs
= getPrefs();
1357 prefs
.putBoolean(PREFS_SYNCED
, isChecked());
1360 } catch (BackingStoreException e
) {
1364 ISelectionService srv
= (ISelectionService
) getSite()
1365 .getService(ISelectionService
.class);
1366 reactOnSelection(srv
.getSelection());
1373 linkWithSelectionAction
1374 .setToolTipText(UIText
.RepositoriesView_LinkWithSelection_action
);
1375 linkWithSelectionAction
.setImageDescriptor(UIIcons
.ELCL16_SYNCED
);
1376 linkWithSelectionAction
.setChecked(getPrefs().getBoolean(PREFS_SYNCED
,
1379 manager
.add(linkWithSelectionAction
);
1381 manager
.add(new Separator());
1383 IAction collapseAllAction
= new Action(
1384 UIText
.RepositoriesView_CollapseAllMenu
) {
1391 collapseAllAction
.setImageDescriptor(UIIcons
.COLLAPSEALL
);
1392 manager
.add(collapseAllAction
);
1394 manager
.add(new Separator());
1396 importAction
= new Action(UIText
.RepositoriesView_Import_Button
) {
1400 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
1401 new GitCloneWizard());
1402 if (dlg
.open() == Window
.OK
)
1406 importAction
.setToolTipText(UIText
.RepositoriesView_Clone_Tooltip
);
1407 importAction
.setImageDescriptor(UIIcons
.CLONEGIT
);
1409 manager
.add(importAction
);
1411 addAction
= new Action(UIText
.RepositoriesView_Add_Button
) {
1415 RepositorySearchDialog sd
= new RepositorySearchDialog(
1416 getSite().getShell(), getDirs());
1417 if (sd
.open() == Window
.OK
) {
1418 Set
<String
> dirs
= new HashSet
<String
>();
1419 dirs
.addAll(getDirs());
1420 if (dirs
.addAll(sd
.getDirectories()))
1427 addAction
.setToolTipText(UIText
.RepositoriesView_AddRepository_Tooltip
);
1428 addAction
.setImageDescriptor(UIIcons
.NEW_REPOSITORY
);
1430 manager
.add(addAction
);
1432 // copy and paste are global actions; we just implement them
1433 // and register them with the global action handler
1434 // we enable/disable them upon tree selection changes
1436 copyAction
= new Action("") { //$NON-NLS-1$
1440 // for REPO, WORKINGDIR, FILE, FOLDER: copy directory
1441 IStructuredSelection sel
= (IStructuredSelection
) tv
1443 if (sel
.size() == 1) {
1444 RepositoryTreeNode node
= (RepositoryTreeNode
) sel
1447 if (node
.getType() == RepositoryTreeNodeType
.REPO
) {
1448 dir
= node
.getRepository().getDirectory().getPath();
1449 } else if (node
.getType() == RepositoryTreeNodeType
.FILE
1450 || node
.getType() == RepositoryTreeNodeType
.FOLDER
) {
1451 dir
= ((File
) node
.getObject()).getPath();
1452 } else if (node
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
1453 if (!isBare(node
.getRepository()))
1454 dir
= node
.getRepository().getWorkDir().getPath();
1457 Clipboard clip
= null;
1459 clip
= new Clipboard(getSite().getShell()
1462 .setContents(new Object
[] { dir
},
1463 new Transfer
[] { TextTransfer
1467 // we must dispose ourselves
1475 copyAction
.setEnabled(false);
1477 getViewSite().getActionBars().setGlobalActionHandler(
1478 ActionFactory
.COPY
.getId(), copyAction
);
1480 pasteAction
= new Action("") { //$NON-NLS-1$
1484 // we check if the pasted content is a directory
1485 // repository location and try to add this
1486 String errorMessage
= null;
1488 Clipboard clip
= null;
1490 clip
= new Clipboard(getSite().getShell().getDisplay());
1491 String content
= (String
) clip
.getContents(TextTransfer
1493 if (content
== null) {
1494 errorMessage
= UIText
.RepositoriesView_NothingToPasteMessage
;
1498 File file
= new File(content
);
1499 if (!file
.exists() || !file
.isDirectory()) {
1500 errorMessage
= UIText
.RepositoriesView_ClipboardContentNotDirectoryMessage
;
1504 if (!RepositoryCache
.FileKey
.isGitRepository(file
, FS
.DETECTED
)) {
1507 UIText
.RepositoriesView_ClipboardContentNoGitRepoMessage
,
1515 errorMessage
= NLS
.bind(
1516 UIText
.RepositoriesView_PasteRepoAlreadyThere
,
1520 // we must dispose ourselves
1522 if (errorMessage
!= null)
1523 MessageDialog
.openWarning(getSite().getShell(),
1524 UIText
.RepositoriesView_PasteFailureTitle
,
1531 getViewSite().getActionBars().setGlobalActionHandler(
1532 ActionFactory
.PASTE
.getId(), pasteAction
);
1537 * @return the preferences
1539 protected static IEclipsePreferences
getPrefs() {
1540 return new InstanceScope().getNode(Activator
.getPluginId());
1544 public void dispose() {
1545 // make sure to cancel the refresh job
1546 if (this.scheduledJob
!= null) {
1547 this.scheduledJob
.cancel();
1548 this.scheduledJob
= null;
1550 // remove RepositoryChangedListener
1551 unregisterRepositoryListener();
1552 repositories
.clear();
1557 * Schedules a refresh
1559 private void scheduleRefresh() {
1561 if (scheduledJob
!= null && scheduledJob
.getState() == Job
.RUNNING
)
1564 Job job
= new Job("Refreshing Git Repositories view") { //$NON-NLS-1$
1566 @SuppressWarnings("unchecked")
1568 protected IStatus
run(IProgressMonitor monitor
) {
1569 // first, let's check if the list of Directories has changed
1570 final List
<String
> directories
= getDirs();
1572 boolean needsNewInput
= tv
.getInput() == null;
1573 List
<RepositoryTreeNode
<Repository
>> oldInput
= (List
) tv
1576 needsNewInput
= oldInput
.size() != directories
.size();
1578 if (!needsNewInput
) {
1579 List
<String
> oldDirectories
= new ArrayList
<String
>();
1580 for (RepositoryTreeNode
<Repository
> node
: oldInput
) {
1581 oldDirectories
.add(node
.getRepository().getDirectory()
1584 needsNewInput
= !directories
.containsAll(oldDirectories
);
1587 final boolean updateInput
= needsNewInput
;
1588 final List
<RepositoryTreeNode
<Repository
>> newInput
;
1590 unregisterRepositoryListener();
1592 newInput
= getRepositoriesFromDirs(monitor
);
1593 } catch (InterruptedException e
) {
1594 return new Status(IStatus
.ERROR
, Activator
1595 .getPluginId(), e
.getMessage(), e
);
1597 repositories
.clear();
1598 for (RepositoryTreeNode
<Repository
> node
: newInput
) {
1599 Repository repo
= node
.getRepository();
1600 repositories
.add(repo
);
1601 // add listener if not already added
1602 repo
.removeRepositoryChangedListener(repositoryListener
);
1603 repo
.addRepositoryChangedListener(repositoryListener
);
1608 Display
.getDefault().asyncExec(new Runnable() {
1610 // keep expansion state and selection so that we can
1613 Object
[] expanded
= tv
.getExpandedElements();
1614 IStructuredSelection sel
= (IStructuredSelection
) tv
1618 tv
.setInput(newInput
);
1621 tv
.setExpandedElements(expanded
);
1623 Object selected
= sel
.getFirstElement();
1624 if (selected
!= null)
1625 tv
.reveal(selected
);
1627 IViewPart part
= PlatformUI
.getWorkbench()
1628 .getActiveWorkbenchWindow().getActivePage()
1629 .findView(IPageLayout
.ID_PROP_SHEET
);
1631 PropertySheet sheet
= (PropertySheet
) part
;
1632 PropertySheetPage page
= (PropertySheetPage
) sheet
1638 return new Status(IStatus
.OK
, Activator
.getPluginId(), ""); //$NON-NLS-1$
1642 job
.setSystem(true);
1644 IWorkbenchSiteProgressService service
= (IWorkbenchSiteProgressService
) getSite()
1645 .getService(IWorkbenchSiteProgressService
.class);
1647 service
.schedule(job
);
1653 private void createRepositoryChangedListener() {
1654 repositoryListener
= new RepositoryListener() {
1655 public void refsChanged(RefsChangedEvent e
) {
1659 public void indexChanged(IndexChangedEvent e
) {
1665 private void unregisterRepositoryListener() {
1666 for (Repository repo
:repositories
)
1667 repo
.removeRepositoryChangedListener(repositoryListener
);
1672 * Adds a directory to the list if it is not already there
1675 * @return see {@link Collection#add(Object)}
1677 public static boolean addDir(File file
) {
1681 dirString
= file
.getCanonicalPath();
1682 } catch (IOException e
) {
1683 dirString
= file
.getAbsolutePath();
1686 List
<String
> dirStrings
= getDirs();
1687 if (dirStrings
.contains(dirString
)) {
1690 Set
<String
> dirs
= new HashSet
<String
>();
1691 dirs
.addAll(dirStrings
);
1692 dirs
.add(dirString
);
1699 * Converts the directories as configured for this view into a list of
1700 * {@link Repository} objects suitable for the tree content provider
1702 * TODO move to some utility class
1705 * @return a list of nodes
1706 * @throws InterruptedException
1708 public static List
<RepositoryTreeNode
<Repository
>> getRepositoriesFromDirs(
1709 IProgressMonitor monitor
) throws InterruptedException
{
1711 List
<String
> gitDirStrings
= getDirs();
1712 List
<RepositoryTreeNode
<Repository
>> input
= new ArrayList
<RepositoryTreeNode
<Repository
>>();
1714 for (String dirString
: gitDirStrings
) {
1715 if (monitor
!= null && monitor
.isCanceled()) {
1716 throw new InterruptedException(
1717 UIText
.RepositoriesView_ActionCanceled_Message
);
1720 File dir
= new File(dirString
);
1721 if (dir
.exists() && dir
.isDirectory()) {
1722 Repository repo
= org
.eclipse
.egit
.core
.Activator
1723 .getDefault().getRepositoryCache()
1724 .lookupRepository(dir
);
1725 RepositoryNode node
= new RepositoryNode(null, repo
);
1728 } catch (IOException e
) {
1729 IStatus error
= new Status(IStatus
.ERROR
, Activator
1730 .getPluginId(), e
.getMessage(), e
);
1731 Activator
.getDefault().getLog().log(error
);
1734 Collections
.sort(input
);
1738 private static void saveDirs(Set
<String
> gitDirStrings
) {
1739 StringBuilder sb
= new StringBuilder();
1740 for (String gitDirString
: gitDirStrings
) {
1741 sb
.append(gitDirString
);
1742 sb
.append(File
.pathSeparatorChar
);
1745 IEclipsePreferences prefs
= getPrefs();
1746 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
1749 } catch (BackingStoreException e
) {
1750 IStatus error
= new Status(IStatus
.ERROR
, Activator
.getPluginId(),
1752 Activator
.getDefault().getLog().log(error
);
1757 public void setFocus() {
1758 tv
.getTree().setFocus();
1761 @SuppressWarnings("boxing")
1762 private boolean confirmProjectDeletion(List
<IProject
> projectsToDelete
) {
1764 confirmed
= MessageDialog
1766 getSite().getShell(),
1767 UIText
.RepositoriesView_ConfirmProjectDeletion_WindowTitle
,
1770 UIText
.RepositoriesView_ConfirmProjectDeletion_Question
,
1771 projectsToDelete
.size()));
1775 public void addSelectionChangedListener(ISelectionChangedListener listener
) {
1776 selectionListeners
.add(listener
);
1779 public ISelection
getSelection() {
1780 return currentSelection
;
1783 public void removeSelectionChangedListener(
1784 ISelectionChangedListener listener
) {
1785 selectionListeners
.remove(listener
);
1789 public void setSelection(ISelection selection
) {
1790 currentSelection
= selection
;
1791 for (ISelectionChangedListener listener
: selectionListeners
) {
1792 listener
.selectionChanged(new SelectionChangedEvent(
1793 RepositoriesView
.this, selection
));
1798 * Opens the tree and marks the folder to which a project is pointing
1803 @SuppressWarnings("unchecked")
1804 public void showResource(final IResource resource
) {
1805 IProject project
= resource
.getProject();
1806 RepositoryMapping mapping
= RepositoryMapping
.getMapping(project
);
1807 if (mapping
== null)
1810 if (addDir(mapping
.getRepository().getDirectory())) {
1814 boolean doSetSelection
= false;
1816 if (this.scheduledJob
!= null) {
1817 int state
= this.scheduledJob
.getState();
1818 if (state
== Job
.WAITING
|| state
== Job
.RUNNING
) {
1819 this.scheduledJob
.addJobChangeListener(new JobChangeAdapter() {
1822 public void done(IJobChangeEvent event
) {
1823 showResource(resource
);
1827 doSetSelection
= true;
1831 if (doSetSelection
) {
1832 RepositoriesViewContentProvider cp
= (RepositoriesViewContentProvider
) tv
1833 .getContentProvider();
1834 RepositoryTreeNode currentNode
= null;
1835 Object
[] repos
= cp
.getElements(tv
.getInput());
1836 for (Object repo
: repos
) {
1837 RepositoryTreeNode node
= (RepositoryTreeNode
) repo
;
1838 // TODO equals implementation of Repository?
1839 if (mapping
.getRepository().getDirectory().equals(
1840 ((Repository
) node
.getObject()).getDirectory())) {
1841 for (Object child
: cp
.getChildren(node
)) {
1842 RepositoryTreeNode childNode
= (RepositoryTreeNode
) child
;
1843 if (childNode
.getType() == RepositoryTreeNodeType
.WORKINGDIR
) {
1844 currentNode
= childNode
;
1852 IPath relPath
= new Path(mapping
.getRepoRelativePath(resource
));
1854 for (String segment
: relPath
.segments()) {
1855 for (Object child
: cp
.getChildren(currentNode
)) {
1856 RepositoryTreeNode
<File
> childNode
= (RepositoryTreeNode
<File
>) child
;
1857 if (childNode
.getObject().getName().equals(segment
)) {
1858 currentNode
= childNode
;
1864 final RepositoryTreeNode selNode
= currentNode
;
1866 Display
.getDefault().asyncExec(new Runnable() {
1869 tv
.setSelection(new StructuredSelection(selNode
), true);
1877 public boolean show(ShowInContext context
) {
1878 ISelection selection
= context
.getSelection();
1879 if (selection
instanceof IStructuredSelection
) {
1880 IStructuredSelection ss
= (IStructuredSelection
) selection
;
1881 if (ss
.size() == 1) {
1882 Object element
= ss
.getFirstElement();
1883 if (element
instanceof IAdaptable
) {
1884 IResource resource
= (IResource
) ((IAdaptable
) element
)
1885 .getAdapter(IResource
.class);
1886 if (resource
!= null) {
1887 showResource(resource
);
1896 private void removeRepository(final IProgressMonitor monitor
,
1897 final Repository
... repository
) {
1898 final List
<IProject
> projectsToDelete
= new ArrayList
<IProject
>();
1901 .setTaskName(UIText
.RepositoriesView_DeleteRepoDeterminProjectsMessage
);
1903 for (Repository repo
: repository
) {
1904 File workDir
= repo
.getWorkDir();
1905 final IPath wdPath
= new Path(workDir
.getAbsolutePath());
1906 for (IProject prj
: ResourcesPlugin
.getWorkspace().getRoot()
1908 if (monitor
.isCanceled())
1910 if (wdPath
.isPrefixOf(prj
.getLocation())) {
1911 projectsToDelete
.add(prj
);
1914 repo
.removeRepositoryChangedListener(repositoryListener
);
1917 if (!projectsToDelete
.isEmpty()) {
1919 confirmed
= confirmProjectDeletion(projectsToDelete
);
1925 if (monitor
.isCanceled())
1928 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
1930 public void run(IProgressMonitor actMonitor
) throws CoreException
{
1932 for (IProject prj
: projectsToDelete
) {
1933 prj
.delete(false, false, actMonitor
);
1935 for (Repository repo
: repository
)
1936 removeDir(repo
.getDirectory());
1942 ResourcesPlugin
.getWorkspace().run(wsr
,
1943 ResourcesPlugin
.getWorkspace().getRoot(),
1944 IWorkspace
.AVOID_UPDATE
, monitor
);
1945 } catch (CoreException e1
) {
1946 Activator
.logError(e1
.getMessage(), e1
);