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
.util
.ArrayList
;
16 import java
.util
.Collection
;
17 import java
.util
.Collections
;
18 import java
.util
.Comparator
;
19 import java
.util
.HashSet
;
20 import java
.util
.List
;
22 import java
.util
.StringTokenizer
;
23 import java
.util
.TreeSet
;
25 import org
.eclipse
.core
.resources
.IProject
;
26 import org
.eclipse
.core
.resources
.IProjectDescription
;
27 import org
.eclipse
.core
.resources
.IWorkspace
;
28 import org
.eclipse
.core
.resources
.IWorkspaceRunnable
;
29 import org
.eclipse
.core
.resources
.ResourcesPlugin
;
30 import org
.eclipse
.core
.runtime
.CoreException
;
31 import org
.eclipse
.core
.runtime
.IPath
;
32 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
33 import org
.eclipse
.core
.runtime
.IStatus
;
34 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
35 import org
.eclipse
.core
.runtime
.Path
;
36 import org
.eclipse
.core
.runtime
.Status
;
37 import org
.eclipse
.core
.runtime
.SubProgressMonitor
;
38 import org
.eclipse
.core
.runtime
.jobs
.Job
;
39 import org
.eclipse
.core
.runtime
.preferences
.IEclipsePreferences
;
40 import org
.eclipse
.core
.runtime
.preferences
.InstanceScope
;
41 import org
.eclipse
.egit
.core
.op
.BranchOperation
;
42 import org
.eclipse
.egit
.core
.op
.ConnectProviderOperation
;
43 import org
.eclipse
.egit
.ui
.Activator
;
44 import org
.eclipse
.egit
.ui
.internal
.clone
.GitCloneWizard
;
45 import org
.eclipse
.egit
.ui
.internal
.clone
.GitProjectsImportPage
;
46 import org
.eclipse
.jface
.action
.Action
;
47 import org
.eclipse
.jface
.action
.IAction
;
48 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
49 import org
.eclipse
.jface
.layout
.GridDataFactory
;
50 import org
.eclipse
.jface
.resource
.CompositeImageDescriptor
;
51 import org
.eclipse
.jface
.resource
.ImageDescriptor
;
52 import org
.eclipse
.jface
.text
.DefaultInformationControl
;
53 import org
.eclipse
.jface
.text
.TextPresentation
;
54 import org
.eclipse
.jface
.text
.DefaultInformationControl
.IInformationPresenter
;
55 import org
.eclipse
.jface
.viewers
.BaseLabelProvider
;
56 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
57 import org
.eclipse
.jface
.viewers
.ITableLabelProvider
;
58 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
59 import org
.eclipse
.jface
.viewers
.TreeViewer
;
60 import org
.eclipse
.jface
.viewers
.Viewer
;
61 import org
.eclipse
.jface
.viewers
.ViewerCell
;
62 import org
.eclipse
.jface
.window
.Window
;
63 import org
.eclipse
.jface
.wizard
.Wizard
;
64 import org
.eclipse
.jface
.wizard
.WizardDialog
;
65 import org
.eclipse
.jgit
.lib
.Constants
;
66 import org
.eclipse
.jgit
.lib
.Ref
;
67 import org
.eclipse
.jgit
.lib
.Repository
;
68 import org
.eclipse
.jgit
.lib
.RepositoryCache
;
69 import org
.eclipse
.osgi
.util
.NLS
;
70 import org
.eclipse
.swt
.SWT
;
71 import org
.eclipse
.swt
.events
.MenuDetectEvent
;
72 import org
.eclipse
.swt
.events
.MenuDetectListener
;
73 import org
.eclipse
.swt
.events
.MouseEvent
;
74 import org
.eclipse
.swt
.events
.MouseMoveListener
;
75 import org
.eclipse
.swt
.events
.MouseTrackAdapter
;
76 import org
.eclipse
.swt
.events
.SelectionAdapter
;
77 import org
.eclipse
.swt
.events
.SelectionEvent
;
78 import org
.eclipse
.swt
.graphics
.GC
;
79 import org
.eclipse
.swt
.graphics
.Image
;
80 import org
.eclipse
.swt
.graphics
.Point
;
81 import org
.eclipse
.swt
.layout
.GridLayout
;
82 import org
.eclipse
.swt
.widgets
.Composite
;
83 import org
.eclipse
.swt
.widgets
.Display
;
84 import org
.eclipse
.swt
.widgets
.Menu
;
85 import org
.eclipse
.swt
.widgets
.MenuItem
;
86 import org
.eclipse
.swt
.widgets
.Tree
;
87 import org
.eclipse
.swt
.widgets
.TreeColumn
;
88 import org
.eclipse
.swt
.widgets
.TreeItem
;
89 import org
.eclipse
.ui
.ISharedImages
;
90 import org
.eclipse
.ui
.PlatformUI
;
91 import org
.eclipse
.ui
.ide
.IDE
.SharedImages
;
92 import org
.eclipse
.ui
.part
.ViewPart
;
93 import org
.eclipse
.ui
.progress
.IWorkbenchSiteProgressService
;
94 import org
.eclipse
.ui
.statushandlers
.StatusManager
;
95 import org
.eclipse
.ui
.wizards
.datatransfer
.ExternalProjectImportWizard
;
96 import org
.osgi
.service
.prefs
.BackingStoreException
;
100 * The Git Repositories view.
102 * This keeps track of a bunch of local directory names each of which represent
103 * a Git Repository. This list is stored in some Preferences object and used to
104 * build the tree in the view.
108 * <li>String externalization</li>
109 * <li>Clarification whether to show projects, perhaps configurable switch</li>
112 public class RepositoriesView
extends ViewPart
{
114 private static final String PREFS_DIRECTORIES
= "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$
116 private static final ImageDescriptor CHECKEDOUT_OVERLAY
= Activator
117 .getDefault().getImageRegistry().getDescriptor(
118 Activator
.ICON_CHECKEDOUT_OVR
);
120 private Job scheduledJob
;
122 private TreeViewer tv
;
124 private IAction importAction
;
126 private IAction addAction
;
128 private IAction refreshAction
;
130 enum RepositoryTreeNodeType
{
132 REPO(Activator
.ICON_REPOSITORY
), REF(PlatformUI
.getWorkbench()
133 .getSharedImages().getImage(ISharedImages
.IMG_OBJ_FOLDER
)), PROJ(
134 PlatformUI
.getWorkbench().getSharedImages().getImage(
135 SharedImages
.IMG_OBJ_PROJECT_CLOSED
)), BRANCHES(
136 Activator
.ICON_BRANCHES
), PROJECTS(PlatformUI
.getWorkbench()
137 .getSharedImages().getImage(ISharedImages
.IMG_OBJ_FOLDER
));
139 private final Image myImage
;
141 private RepositoryTreeNodeType(String iconName
) {
143 if (iconName
!= null) {
144 myImage
= Activator
.getDefault().getImageRegistry().get(
152 private RepositoryTreeNodeType(Image icon
) {
157 public Image
getIcon() {
163 private static final class RepositoryTreeNode
<T
> {
165 private final Repository myRepository
;
167 private final T myObject
;
169 private final RepositoryTreeNodeType myType
;
171 private final RepositoryTreeNode myParent
;
173 private String branch
;
175 public RepositoryTreeNode(RepositoryTreeNode parent
,
176 RepositoryTreeNodeType type
, Repository repository
, T treeObject
) {
178 myRepository
= repository
;
180 myObject
= treeObject
;
183 @SuppressWarnings("unchecked")
184 private RepositoryTreeNode
<Repository
> getRepositoryNode() {
185 if (myType
== RepositoryTreeNodeType
.REPO
) {
186 return (RepositoryTreeNode
<Repository
>) this;
188 return getParent().getRepositoryNode();
193 * We keep this cached in the repository node to avoid repeated lookups
195 * @return the full branch
196 * @throws IOException
198 public String
getBranch() throws IOException
{
199 if (myType
!= RepositoryTreeNodeType
.REPO
) {
200 return getRepositoryNode().getBranch();
202 if (branch
== null) {
203 branch
= getRepository().getBranch();
208 public RepositoryTreeNode
getParent() {
212 public RepositoryTreeNodeType
getType() {
216 public Repository
getRepository() {
220 public T
getObject() {
225 public int hashCode() {
226 final int prime
= 31;
234 + ((myObject
== null) ?
0 : ((Repository
) myObject
)
235 .getDirectory().hashCode());
240 + ((myObject
== null) ?
0 : ((Ref
) myObject
).getName()
246 + ((myObject
== null) ?
0 : ((File
) myObject
).getPath()
254 result
= prime
* result
255 + ((myParent
== null) ?
0 : myParent
.hashCode());
258 + ((myRepository
== null) ?
0 : myRepository
.getDirectory()
260 result
= prime
* result
261 + ((myType
== null) ?
0 : myType
.hashCode());
266 public boolean equals(Object obj
) {
271 if (getClass() != obj
.getClass())
274 RepositoryTreeNode other
= (RepositoryTreeNode
) obj
;
276 if (myType
== null) {
277 if (other
.myType
!= null)
279 } else if (!myType
.equals(other
.myType
))
281 if (myParent
== null) {
282 if (other
.myParent
!= null)
284 } else if (!myParent
.equals(other
.myParent
))
286 if (myRepository
== null) {
287 if (other
.myRepository
!= null)
289 } else if (!myRepository
.getDirectory().equals(
290 other
.myRepository
.getDirectory()))
292 if (myObject
== null) {
293 if (other
.myObject
!= null)
295 } else if (!checkObjectsEqual(other
.myObject
))
301 private boolean checkObjectsEqual(Object otherObject
) {
306 return ((Repository
) myObject
).getDirectory().equals(
307 ((Repository
) otherObject
).getDirectory());
309 return ((Ref
) myObject
).getName().equals(
310 ((Ref
) otherObject
).getName());
312 return ((File
) myObject
).getPath().equals(
313 ((File
) otherObject
).getPath());
320 private static final class ContentProvider
implements ITreeContentProvider
{
322 @SuppressWarnings("unchecked")
323 public Object
[] getElements(Object inputElement
) {
325 Comparator
<RepositoryTreeNode
<Repository
>> sorter
= new Comparator
<RepositoryTreeNode
<Repository
>>() {
327 public int compare(RepositoryTreeNode
<Repository
> o1
,
328 RepositoryTreeNode
<Repository
> o2
) {
329 return getRepositoryName(o1
.getObject()).compareTo(
330 getRepositoryName(o2
.getObject()));
335 Set
<RepositoryTreeNode
<Repository
>> output
= new TreeSet
<RepositoryTreeNode
<Repository
>>(
338 for (Repository repo
: ((List
<Repository
>) inputElement
)) {
339 output
.add(new RepositoryTreeNode
<Repository
>(null,
340 RepositoryTreeNodeType
.REPO
, repo
, repo
));
343 return output
.toArray();
346 public void dispose() {
350 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
354 public Object
[] getChildren(Object parentElement
) {
356 RepositoryTreeNode node
= (RepositoryTreeNode
) parentElement
;
357 Repository repo
= node
.getRepository();
359 switch (node
.getType()) {
363 List
<RepositoryTreeNode
<Ref
>> refs
= new ArrayList
<RepositoryTreeNode
<Ref
>>();
365 Repository rep
= node
.getRepository();
366 for (Ref ref
: rep
.getAllRefs().values()) {
367 refs
.add(new RepositoryTreeNode
<Ref
>(node
,
368 RepositoryTreeNodeType
.REF
, repo
, ref
));
371 return refs
.toArray();
375 List
<RepositoryTreeNode
<Repository
>> branches
= new ArrayList
<RepositoryTreeNode
<Repository
>>();
377 branches
.add(new RepositoryTreeNode
<Repository
>(node
,
378 RepositoryTreeNodeType
.BRANCHES
, node
.getRepository(),
379 node
.getRepository()));
381 branches
.add(new RepositoryTreeNode
<Repository
>(node
,
382 RepositoryTreeNodeType
.PROJECTS
, node
.getRepository(),
383 node
.getRepository()));
385 return branches
.toArray();
389 List
<RepositoryTreeNode
<File
>> projects
= new ArrayList
<RepositoryTreeNode
<File
>>();
391 // TODO do we want to show the projects here?
392 Collection
<File
> result
= new HashSet
<File
>();
393 Set
<String
> traversed
= new HashSet
<String
>();
394 collectProjectFilesFromDirectory(result
, repo
.getDirectory()
395 .getParentFile(), traversed
, new NullProgressMonitor());
396 for (File file
: result
) {
397 projects
.add(new RepositoryTreeNode
<File
>(node
,
398 RepositoryTreeNodeType
.PROJ
, repo
, file
));
401 Comparator
<RepositoryTreeNode
<File
>> sorter
= new Comparator
<RepositoryTreeNode
<File
>>() {
403 public int compare(RepositoryTreeNode
<File
> o1
,
404 RepositoryTreeNode
<File
> o2
) {
405 return o1
.getObject().getName().compareTo(
406 o2
.getObject().getName());
409 Collections
.sort(projects
, sorter
);
411 return projects
.toArray();
419 public Object
getParent(Object element
) {
421 return ((RepositoryTreeNode
) element
).getParent();
424 public boolean hasChildren(Object element
) {
425 Object
[] children
= getChildren(element
);
426 return children
!= null && children
.length
> 0;
429 private boolean collectProjectFilesFromDirectory(
430 Collection
<File
> files
, File directory
,
431 Set
<String
> directoriesVisited
, IProgressMonitor monitor
) {
433 // stolen from the GitCloneWizard; perhaps we should completely drop
434 // the projects from this view, though
435 if (monitor
.isCanceled()) {
438 monitor
.subTask(NLS
.bind(
439 RepositoryViewUITexts
.RepositoriesView_Checking_Message
,
440 directory
.getPath()));
441 File
[] contents
= directory
.listFiles();
442 if (contents
== null)
445 // first look for project description files
446 final String dotProject
= IProjectDescription
.DESCRIPTION_FILE_NAME
;
447 for (int i
= 0; i
< contents
.length
; i
++) {
448 File file
= contents
[i
];
449 if (file
.isFile() && file
.getName().equals(dotProject
)) {
450 files
.add(file
.getParentFile());
451 // don't search sub-directories since we can't have nested
456 // no project description found, so recurse into sub-directories
457 for (int i
= 0; i
< contents
.length
; i
++) {
458 if (contents
[i
].isDirectory()) {
459 if (!contents
[i
].getName().equals(
460 GitProjectsImportPage
.METADATA_FOLDER
)) {
462 String canonicalPath
= contents
[i
]
464 if (!directoriesVisited
.add(canonicalPath
)) {
465 // already been here --> do not recurse
468 } catch (IOException exception
) {
469 StatusManager
.getManager().handle(
470 new Status(IStatus
.ERROR
, Activator
471 .getPluginId(), exception
472 .getLocalizedMessage(), exception
));
475 collectProjectFilesFromDirectory(files
, contents
[i
],
476 directoriesVisited
, monitor
);
485 private static final class LabelProvider
extends BaseLabelProvider
486 implements ITableLabelProvider
{
488 private DefaultInformationControl infoControl
;
494 LabelProvider(final TreeViewer viewer
) {
496 viewer
.setLabelProvider(this);
497 Tree tree
= viewer
.getTree();
498 TreeColumn col
= new TreeColumn(tree
, SWT
.NONE
);
500 viewer
.getTree().addMouseTrackListener(new MouseTrackAdapter() {
503 public void mouseHover(MouseEvent e
) {
505 Point eventPoint
= new Point(e
.x
, e
.y
);
507 TreeItem item
= viewer
.getTree().getItem(eventPoint
);
510 RepositoryTreeNode node
= (RepositoryTreeNode
) item
512 String text
= node
.getRepository().getDirectory()
515 final ViewerCell cell
= viewer
.getCell(eventPoint
);
517 if (infoControl
!= null && infoControl
.isVisible()) {
518 infoControl
.setVisible(false);
521 GC testGc
= new GC(cell
.getControl());
522 final Point textExtent
= testGc
.textExtent(text
);
525 if (infoControl
== null || !infoControl
.isVisible()) {
527 IInformationPresenter ips
= new IInformationPresenter() {
529 public String
updatePresentation(
530 Display display
, String hoverInfo
,
531 TextPresentation presentation
,
532 int maxWidth
, int maxHeight
) {
538 infoControl
= new DefaultInformationControl(Display
539 .getCurrent().getActiveShell().getShell(),
543 public void setInformation(String content
) {
544 super.setInformation(content
);
545 super.setSize(textExtent
.x
, textExtent
.y
);
551 Point dispPoint
= viewer
.getControl().toDisplay(
554 infoControl
.setLocation(dispPoint
);
556 // the default info provider works better with \r ...
557 infoControl
.setInformation(text
);
559 final MouseMoveListener moveListener
= new MouseMoveListener() {
561 public void mouseMove(MouseEvent evt
) {
562 infoControl
.setVisible(false);
563 cell
.getControl().removeMouseMoveListener(this);
568 cell
.getControl().addMouseMoveListener(moveListener
);
570 infoControl
.setVisible(true);
580 public Image
getColumnImage(Object element
, int columnIndex
) {
581 return decorateImage(((RepositoryTreeNode
) element
).getType()
582 .getIcon(), element
);
585 public String
getColumnText(Object element
, int columnIndex
) {
587 RepositoryTreeNode node
= (RepositoryTreeNode
) element
;
588 switch (node
.getType()) {
590 File directory
= ((Repository
) node
.getObject()).getDirectory()
592 return (directory
.getName() + " - " + directory
//$NON-NLS-1$
595 return RepositoryViewUITexts
.RepositoriesView_Branches_Nodetext
;
597 return RepositoryViewUITexts
.RepositoriesView_ExistingProjects_Nodetext
;
599 Ref ref
= (Ref
) node
.getObject();
601 String refName
= node
.getRepository().shortenRefName(
603 if (ref
.isSymbolic()) {
605 + " - " //$NON-NLS-1$
606 + node
.getRepository().shortenRefName(
607 ref
.getLeaf().getName());
612 File file
= (File
) node
.getObject();
613 return file
.getName();
620 public Image
decorateImage(final Image image
, Object element
) {
622 RepositoryTreeNode node
= (RepositoryTreeNode
) element
;
623 switch (node
.getType()) {
626 Ref ref
= (Ref
) node
.getObject();
628 String refName
= node
.getRepository().shortenRefName(
631 String branch
= node
.getBranch();
632 if (refName
.equals(branch
)) {
633 CompositeImageDescriptor cd
= new CompositeImageDescriptor() {
636 protected Point
getSize() {
637 return new Point(image
.getBounds().width
, image
642 protected void drawCompositeImage(int width
,
644 drawImage(image
.getImageData(), 0, 0);
645 drawImage(CHECKEDOUT_OVERLAY
.getImageData(), 0,
650 return cd
.createImage();
652 } catch (IOException e1
) {
653 // simply ignore here
659 File file
= (File
) node
.getObject();
661 for (IProject proj
: ResourcesPlugin
.getWorkspace().getRoot()
663 if (proj
.getLocation().equals(
664 new Path(file
.getAbsolutePath()))) {
665 CompositeImageDescriptor cd
= new CompositeImageDescriptor() {
668 protected Point
getSize() {
669 return new Point(image
.getBounds().width
, image
674 protected void drawCompositeImage(int width
,
676 drawImage(image
.getImageData(), 0, 0);
677 drawImage(CHECKEDOUT_OVERLAY
.getImageData(), 0,
682 return cd
.createImage();
694 private List
<String
> getGitDirs() {
695 List
<String
> resultStrings
= new ArrayList
<String
>();
696 String dirs
= new InstanceScope().getNode(Activator
.getPluginId()).get(
697 PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
698 if (dirs
!= null && dirs
.length() > 0) {
699 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
700 while (tok
.hasMoreTokens()) {
701 String dirName
= tok
.nextToken();
702 File testFile
= new File(dirName
);
703 if (testFile
.exists()) {
704 resultStrings
.add(dirName
);
708 Collections
.sort(resultStrings
);
709 return resultStrings
;
712 private void removeDir(String dir
) {
714 IEclipsePreferences prefs
= new InstanceScope().getNode(Activator
717 TreeSet
<String
> resultStrings
= new TreeSet
<String
>();
718 String dirs
= prefs
.get(PREFS_DIRECTORIES
, ""); //$NON-NLS-1$
719 if (dirs
!= null && dirs
.length() > 0) {
720 StringTokenizer tok
= new StringTokenizer(dirs
, File
.pathSeparator
);
721 while (tok
.hasMoreTokens()) {
722 String dirName
= tok
.nextToken();
723 File testFile
= new File(dirName
);
724 if (testFile
.exists()) {
725 resultStrings
.add(dirName
);
730 if (resultStrings
.remove(dir
)) {
731 StringBuilder sb
= new StringBuilder();
732 for (String gitDirString
: resultStrings
) {
733 sb
.append(gitDirString
);
734 sb
.append(File
.pathSeparatorChar
);
737 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
740 } catch (BackingStoreException e
) {
741 IStatus error
= new Status(IStatus
.ERROR
, Activator
742 .getPluginId(), e
.getMessage(), e
);
743 Activator
.getDefault().getLog().log(error
);
750 public Object
getAdapter(Class adapter
) {
751 return super.getAdapter(adapter
);
755 public void createPartControl(Composite parent
) {
757 Composite main
= new Composite(parent
, SWT
.NONE
);
758 GridDataFactory
.fillDefaults().grab(true, true).applyTo(main
);
759 main
.setLayout(new GridLayout(1, false));
761 tv
= new TreeViewer(main
);
762 tv
.setContentProvider(new ContentProvider());
763 new LabelProvider(tv
);
765 GridDataFactory
.fillDefaults().grab(true, true).applyTo(tv
.getTree());
769 addActionsToToolbar();
774 private void addContextMenu() {
775 tv
.getTree().addMenuDetectListener(new MenuDetectListener() {
777 public void menuDetected(MenuDetectEvent e
) {
779 tv
.getTree().setMenu(null);
780 Menu men
= new Menu(tv
.getTree());
782 TreeItem testItem
= tv
.getTree().getItem(
783 tv
.getTree().toControl(new Point(e
.x
, e
.y
)));
784 if (testItem
== null) {
785 addMenuItemsForPanel(men
);
787 addMenuItemsForTreeSelection(men
);
788 if (men
.getItemCount() > 0)
789 new MenuItem(men
, SWT
.SEPARATOR
);
790 addMenuItemsForPanel(men
);
793 tv
.getTree().setMenu(men
);
798 private void addMenuItemsForPanel(Menu men
) {
800 MenuItem importItem
= new MenuItem(men
, SWT
.PUSH
);
802 .setText(RepositoryViewUITexts
.RepositoriesView_ImportRepository_MenuItem
);
803 importItem
.addSelectionListener(new SelectionAdapter() {
806 public void widgetSelected(SelectionEvent e
) {
812 MenuItem addItem
= new MenuItem(men
, SWT
.PUSH
);
814 .setText(RepositoryViewUITexts
.RepositoriesView_AddRepository_MenuItem
);
815 addItem
.addSelectionListener(new SelectionAdapter() {
818 public void widgetSelected(SelectionEvent e
) {
824 MenuItem refreshItem
= new MenuItem(men
, SWT
.PUSH
);
825 refreshItem
.setText(refreshAction
.getText());
826 refreshItem
.addSelectionListener(new SelectionAdapter() {
829 public void widgetSelected(SelectionEvent e
) {
837 @SuppressWarnings("unchecked")
838 private void addMenuItemsForTreeSelection(Menu men
) {
839 final List
<RepositoryTreeNode
<Ref
>> refs
= new ArrayList
<RepositoryTreeNode
<Ref
>>();
840 final List
<RepositoryTreeNode
<File
>> projects
= new ArrayList
<RepositoryTreeNode
<File
>>();
841 final List
<RepositoryTreeNode
<Repository
>> repos
= new ArrayList
<RepositoryTreeNode
<Repository
>>();
843 TreeItem
[] selectedItems
= tv
.getTree().getSelection();
844 for (TreeItem item
: selectedItems
) {
845 RepositoryTreeNode node
= (RepositoryTreeNode
) item
.getData();
846 switch (node
.getType()) {
861 boolean importableProjectsOnly
= !projects
.isEmpty() && repos
.isEmpty()
864 for (RepositoryTreeNode
<File
> prj
: projects
) {
865 if (!importableProjectsOnly
)
868 for (IProject proj
: ResourcesPlugin
.getWorkspace().getRoot()
870 if (proj
.getLocation().equals(
871 new Path(prj
.getObject().getAbsolutePath())))
872 importableProjectsOnly
= false;
878 boolean singleRef
= refs
.size() == 1 && projects
.isEmpty()
880 boolean singleRepo
= repos
.size() == 1 && projects
.isEmpty()
884 singleRef
= singleRef
885 && !refs
.get(0).getObject().getName()
886 .equals(Constants
.HEAD
)
887 && (refs
.get(0).getRepository().mapCommit(
888 refs
.get(0).getObject().getLeaf().getObjectId()) != null);
889 } catch (IOException e2
) {
893 if (importableProjectsOnly
) {
894 MenuItem sync
= new MenuItem(men
, SWT
.PUSH
);
896 .setText(RepositoryViewUITexts
.RepositoriesView_ImportProject_MenuItem
);
898 sync
.addSelectionListener(new SelectionAdapter() {
901 public void widgetSelected(SelectionEvent e
) {
903 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
905 public void run(IProgressMonitor monitor
)
906 throws CoreException
{
908 for (RepositoryTreeNode
<File
> projectNode
: projects
) {
909 File file
= projectNode
.getObject();
911 IProjectDescription pd
= ResourcesPlugin
912 .getWorkspace().newProjectDescription(
914 IPath locationPath
= new Path(file
917 pd
.setLocation(locationPath
);
919 ResourcesPlugin
.getWorkspace().getRoot()
920 .getProject(pd
.getName()).create(pd
,
922 IProject project
= ResourcesPlugin
923 .getWorkspace().getRoot().getProject(
925 project
.open(monitor
);
927 File gitDir
= projectNode
.getRepository()
930 ConnectProviderOperation connectProviderOperation
= new ConnectProviderOperation(
932 connectProviderOperation
933 .run(new SubProgressMonitor(monitor
, 20));
942 ResourcesPlugin
.getWorkspace().run(wsr
,
943 ResourcesPlugin
.getWorkspace().getRoot(),
944 IWorkspace
.AVOID_UPDATE
,
945 new NullProgressMonitor());
948 } catch (CoreException e1
) {
949 Activator
.getDefault().getLog().log(e1
.getStatus());
959 MenuItem checkout
= new MenuItem(men
, SWT
.PUSH
);
961 .setText(RepositoryViewUITexts
.RepositoriesView_CheckOut_MenuItem
);
962 checkout
.addSelectionListener(new SelectionAdapter() {
965 public void widgetSelected(SelectionEvent e
) {
966 Repository repo
= refs
.get(0).getRepository();
967 String refName
= refs
.get(0).myObject
.getLeaf().getName();
968 final BranchOperation op
= new BranchOperation(repo
,
970 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
972 public void run(IProgressMonitor monitor
)
973 throws CoreException
{
978 ResourcesPlugin
.getWorkspace().run(wsr
,
979 ResourcesPlugin
.getWorkspace().getRoot(),
980 IWorkspace
.AVOID_UPDATE
,
981 new NullProgressMonitor());
983 } catch (CoreException e1
) {
986 getSite().getShell(),
987 RepositoryViewUITexts
.RepositoriesView_Error_WindowTitle
,
998 MenuItem importProjects
= new MenuItem(men
, SWT
.PUSH
);
1000 .setText(RepositoryViewUITexts
.RepositoriesView_ImportExistingProjects_MenuItem
);
1001 importProjects
.addSelectionListener(new SelectionAdapter() {
1004 public void widgetSelected(SelectionEvent e
) {
1005 Wizard wiz
= new ExternalProjectImportWizard(repos
.get(0)
1006 .getRepository().getWorkDir().getAbsolutePath()) {
1009 public void addPages() {
1011 // we could add some page with a single
1012 // checkbox to indicate if we wan
1013 // addPage(new WizardPage("Share") {
1015 // public void createControl(
1016 // Composite parent) {
1017 // Composite main = new Composite(
1018 // parent, SWT.NONE);
1019 // main.setLayout(new GridLayout(1,
1021 // GridDataFactory.fillDefaults()
1022 // .grab(true, true).applyTo(
1024 // Button but = new Button(main,
1026 // but.setText("Push me");
1027 // setControl(main);
1034 public boolean performFinish() {
1036 final Set
<IPath
> previousLocations
= new HashSet
<IPath
>();
1037 // we want to share only new projects
1038 for (IProject project
: ResourcesPlugin
1039 .getWorkspace().getRoot().getProjects()) {
1040 previousLocations
.add(project
.getLocation());
1043 boolean success
= super.performFinish();
1045 // IWizardPage page = getPage("Share");
1046 // TODO evaluate checkbox or such, but
1048 // always, we don't even need another
1051 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
1053 public void run(IProgressMonitor monitor
)
1054 throws CoreException
{
1055 File gitDir
= repos
.get(0)
1056 .getRepository().getDirectory();
1057 File gitWorkDir
= repos
.get(0)
1058 .getRepository().getWorkDir();
1059 Path workPath
= new Path(gitWorkDir
1060 .getAbsolutePath());
1062 // we check which projects are
1064 // pointing to a location in the
1066 // working directory
1068 for (IProject prj
: ResourcesPlugin
1069 .getWorkspace().getRoot()
1072 if (workPath
.isPrefixOf(prj
1074 if (previousLocations
1079 ConnectProviderOperation connectProviderOperation
= new ConnectProviderOperation(
1081 connectProviderOperation
1082 .run(new SubProgressMonitor(
1092 ResourcesPlugin
.getWorkspace().run(
1094 ResourcesPlugin
.getWorkspace()
1096 IWorkspace
.AVOID_UPDATE
,
1097 new NullProgressMonitor());
1099 } catch (CoreException ce
) {
1103 RepositoryViewUITexts
.RepositoriesView_Error_WindowTitle
,
1113 WizardDialog dlg
= new WizardDialog(getSite().getShell(),
1120 // TODO "import existing plug-in" menu item
1121 // TODO "configure" menu item
1123 MenuItem remove
= new MenuItem(men
, SWT
.PUSH
);
1125 .setText(RepositoryViewUITexts
.RepositoriesView_Remove_MenuItem
);
1126 remove
.addSelectionListener(new SelectionAdapter() {
1129 public void widgetSelected(SelectionEvent e
) {
1131 List
<IProject
> projectsToDelete
= new ArrayList
<IProject
>();
1132 File workDir
= repos
.get(0).getRepository().getWorkDir();
1133 final IPath wdPath
= new Path(workDir
.getAbsolutePath());
1134 for (IProject prj
: ResourcesPlugin
.getWorkspace()
1135 .getRoot().getProjects()) {
1136 if (wdPath
.isPrefixOf(prj
.getLocation())) {
1137 projectsToDelete
.add(prj
);
1141 if (!projectsToDelete
.isEmpty()) {
1143 confirmed
= confirmProjectDeletion(projectsToDelete
);
1149 IWorkspaceRunnable wsr
= new IWorkspaceRunnable() {
1151 public void run(IProgressMonitor monitor
)
1152 throws CoreException
{
1154 for (IProject prj
: ResourcesPlugin
.getWorkspace()
1155 .getRoot().getProjects()) {
1156 if (wdPath
.isPrefixOf(prj
.getLocation())) {
1157 prj
.delete(false, false, monitor
);
1161 Repository repo
= repos
.get(0).getRepository();
1162 removeDir(repo
.getDirectory().getAbsolutePath());
1168 ResourcesPlugin
.getWorkspace().run(wsr
,
1169 ResourcesPlugin
.getWorkspace().getRoot(),
1170 IWorkspace
.AVOID_UPDATE
,
1171 new NullProgressMonitor());
1172 } catch (CoreException e1
) {
1173 Activator
.getDefault().getLog().log(e1
.getStatus());
1180 // TODO delete does not work because of file locks on .pack-files
1181 // Shawn Pearce has added the following thoughts:
1183 // Hmm. We probably can't active detect file locks on pack files on
1185 // It would be nice if we could support a delete, but only if the
1187 // reasonably believed to be not-in-use right now.
1189 // Within EGit you might be able to check GitProjectData and its
1190 // repositoryCache to
1191 // see if the repository is open by this workspace. If it is, then
1192 // we know we shouldn't
1193 // try to delete it.
1195 // Some coding might look like this:
1197 // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH);
1198 // deleteRepo.setText("Delete");
1199 // deleteRepo.addSelectionListener(new SelectionAdapter() {
1202 // public void widgetSelected(SelectionEvent e) {
1204 // boolean confirmed = MessageDialog.openConfirm(getSite()
1205 // .getShell(), "Confirm",
1206 // "This will delete the repository, continue?");
1211 // IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
1213 // public void run(IProgressMonitor monitor)
1214 // throws CoreException {
1215 // File workDir = repos.get(0).getRepository()
1218 // File gitDir = repos.get(0).getRepository()
1221 // IPath wdPath = new Path(workDir.getAbsolutePath());
1222 // for (IProject prj : ResourcesPlugin.getWorkspace()
1223 // .getRoot().getProjects()) {
1224 // if (wdPath.isPrefixOf(prj.getLocation())) {
1225 // prj.delete(false, false, monitor);
1229 // repos.get(0).getRepository().close();
1231 // boolean deleted = deleteRecursively(gitDir, monitor);
1233 // MessageDialog.openError(getSite().getShell(),
1235 // "Could not delete Git Repository");
1238 // deleted = deleteRecursively(workDir, monitor);
1241 // .openError(getSite().getShell(),
1243 // "Could not delete Git Working Directory");
1246 // scheduleRefresh();
1249 // private boolean deleteRecursively(File fileToDelete,
1250 // IProgressMonitor monitor) {
1251 // if (fileToDelete.isDirectory()) {
1252 // for (File file : fileToDelete.listFiles()) {
1253 // if (!deleteRecursively(file, monitor)) {
1258 // monitor.setTaskName(fileToDelete.getAbsolutePath());
1259 // boolean deleted = fileToDelete.delete();
1261 // System.err.println("Could not delete "
1262 // + fileToDelete.getAbsolutePath());
1269 // ResourcesPlugin.getWorkspace().run(wsr,
1270 // ResourcesPlugin.getWorkspace().getRoot(),
1271 // IWorkspace.AVOID_UPDATE,
1272 // new NullProgressMonitor());
1273 // } catch (CoreException e1) {
1274 // // TODO Exception handling
1275 // e1.printStackTrace();
1284 private void addActionsToToolbar() {
1285 importAction
= new Action(
1286 RepositoryViewUITexts
.RepositoriesView_Import_Button
) {
1290 GitCloneWizard wiz
= new GitCloneWizard();
1291 wiz
.init(null, null);
1292 new WizardDialog(getSite().getShell(), wiz
).open();
1293 updateDirStrings(new NullProgressMonitor());
1297 .setToolTipText(RepositoryViewUITexts
.RepositoriesView_Clone_Tooltip
);
1299 getViewSite().getActionBars().getToolBarManager().add(importAction
);
1301 addAction
= new Action(
1302 RepositoryViewUITexts
.RepositoriesView_Add_Button
) {
1306 RepositorySearchDialog sd
= new RepositorySearchDialog(
1307 getSite().getShell(), ResourcesPlugin
.getWorkspace()
1308 .getRoot().getLocation().toOSString(),
1310 if (sd
.open() == Window
.OK
) {
1311 Set
<String
> dirs
= new HashSet
<String
>();
1312 dirs
.addAll(getGitDirs());
1313 if (dirs
.addAll(sd
.getDirectories()))
1314 saveDirStrings(dirs
);
1321 .setToolTipText(RepositoryViewUITexts
.RepositoriesView_AddRepository_Tooltip
);
1323 getViewSite().getActionBars().getToolBarManager().add(addAction
);
1325 // TODO if we don't show projects, then we probably don't need refresh
1327 refreshAction
= new Action(
1328 RepositoryViewUITexts
.RepositoriesView_Refresh_Button
) {
1336 getViewSite().getActionBars().getToolBarManager().add(refreshAction
);
1340 public void dispose() {
1341 // make sure to cancel the refresh job
1342 if (this.scheduledJob
!= null) {
1343 this.scheduledJob
.cancel();
1344 this.scheduledJob
= null;
1349 private void scheduleRefresh() {
1351 Job job
= new Job("Refreshing Git Repositories view") { //$NON-NLS-1$
1354 protected IStatus
run(IProgressMonitor monitor
) {
1356 final List
<Repository
> input
;
1358 input
= getRepositoriesFromDirs(monitor
);
1359 } catch (InterruptedException e
) {
1360 return new Status(IStatus
.ERROR
, Activator
.getPluginId(), e
1364 Display
.getDefault().syncExec(new Runnable() {
1367 // keep expansion state and selection so that we can
1370 Object
[] expanded
= tv
.getExpandedElements();
1371 IStructuredSelection sel
= (IStructuredSelection
) tv
1374 tv
.setExpandedElements(expanded
);
1376 Object selected
= sel
.getFirstElement();
1377 if (selected
!= null)
1378 tv
.reveal(selected
);
1382 return new Status(IStatus
.OK
, Activator
.getPluginId(), ""); //$NON-NLS-1$
1387 job
.setSystem(true);
1389 IWorkbenchSiteProgressService service
= (IWorkbenchSiteProgressService
) getSite()
1390 .getService(IWorkbenchSiteProgressService
.class);
1392 service
.schedule(job
);
1398 private List
<Repository
> getRepositoriesFromDirs(IProgressMonitor monitor
)
1399 throws InterruptedException
{
1401 List
<String
> gitDirStrings
= getGitDirs();
1402 List
<Repository
> input
= new ArrayList
<Repository
>();
1403 for (String dirString
: gitDirStrings
) {
1404 if (monitor
.isCanceled()) {
1405 throw new InterruptedException(
1406 RepositoryViewUITexts
.RepositoriesView_ActionCanceled_Message
);
1409 File dir
= new File(dirString
);
1410 if (dir
.exists() && dir
.isDirectory()) {
1411 input
.add(new Repository(dir
));
1413 } catch (IOException e
) {
1414 IStatus error
= new Status(IStatus
.ERROR
, Activator
1415 .getPluginId(), e
.getMessage(), e
);
1416 Activator
.getDefault().getLog().log(error
);
1422 private void updateDirStrings(IProgressMonitor monitor
) {
1424 IPath path
= ResourcesPlugin
.getWorkspace().getRoot().getLocation();
1425 File root
= path
.toFile();
1426 TreeSet
<String
> dirStrings
= new TreeSet
<String
>();
1427 recurseDir(root
, dirStrings
, monitor
);
1428 saveDirStrings(dirStrings
);
1433 private void saveDirStrings(Set
<String
> gitDirStrings
) {
1434 StringBuilder sb
= new StringBuilder();
1435 for (String gitDirString
: gitDirStrings
) {
1436 sb
.append(gitDirString
);
1437 sb
.append(File
.pathSeparatorChar
);
1440 IEclipsePreferences prefs
= new InstanceScope().getNode(Activator
1442 prefs
.put(PREFS_DIRECTORIES
, sb
.toString());
1445 } catch (BackingStoreException e
) {
1446 IStatus error
= new Status(IStatus
.ERROR
, Activator
.getPluginId(),
1448 Activator
.getDefault().getLog().log(error
);
1458 public static void recurseDir(File root
, TreeSet
<String
> strings
,
1459 IProgressMonitor monitor
) {
1461 if (!root
.exists() || !root
.isDirectory()) {
1464 File
[] children
= root
.listFiles();
1465 for (File child
: children
) {
1466 if (monitor
.isCanceled()) {
1470 if (child
.exists() && child
.isDirectory()
1471 && RepositoryCache
.FileKey
.isGitRepository(child
)) {
1472 strings
.add(child
.getAbsolutePath());
1475 if (child
.isDirectory()) {
1476 monitor
.setTaskName(child
.getPath());
1477 recurseDir(child
, strings
, monitor
);
1483 private static String
getRepositoryName(Repository repository
) {
1484 return repository
.getDirectory().getParentFile().getName();
1488 public void setFocus() {
1492 @SuppressWarnings("boxing")
1493 private boolean confirmProjectDeletion(List
<IProject
> projectsToDelete
) {
1495 confirmed
= MessageDialog
1497 getSite().getShell(),
1498 RepositoryViewUITexts
.RepositoriesView_ConfirmProjectDeletion_WindowTitle
,
1501 RepositoryViewUITexts
.RepositoriesView_ConfirmProjectDeletion_Question
,
1502 projectsToDelete
.size()));