1 /*******************************************************************************
2 * Copyright (C) 2011, 2015 Bernard Leach <leachbj@bouncycastle.org> and others.
3 * Copyright (C) 2015 SAP SE (Christian Georgi <christian.georgi@sap.com>)
4 * Copyright (C) 2015 Denis Zygann <d.zygann@web.de>
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
12 * Tobias Baumann <tobbaumann@gmail.com> - Bug 373969, 473544
13 * Thomas Wolf <thomas.wolf@paranor.ch> - Bug 481683
14 *******************************************************************************/
15 package org
.eclipse
.egit
.ui
.internal
.staging
;
17 import static org
.eclipse
.egit
.ui
.internal
.CommonUtils
.runCommand
;
20 import java
.text
.MessageFormat
;
21 import java
.util
.ArrayList
;
22 import java
.util
.Collection
;
23 import java
.util
.Comparator
;
24 import java
.util
.EnumSet
;
25 import java
.util
.HashSet
;
26 import java
.util
.Iterator
;
27 import java
.util
.List
;
30 import org
.eclipse
.core
.commands
.ExecutionException
;
31 import org
.eclipse
.core
.commands
.operations
.IUndoContext
;
32 import org
.eclipse
.core
.resources
.IContainer
;
33 import org
.eclipse
.core
.resources
.IFile
;
34 import org
.eclipse
.core
.resources
.IMarker
;
35 import org
.eclipse
.core
.resources
.IResource
;
36 import org
.eclipse
.core
.resources
.ResourcesPlugin
;
37 import org
.eclipse
.core
.runtime
.CoreException
;
38 import org
.eclipse
.core
.runtime
.IPath
;
39 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
40 import org
.eclipse
.core
.runtime
.IStatus
;
41 import org
.eclipse
.core
.runtime
.Path
;
42 import org
.eclipse
.core
.runtime
.Status
;
43 import org
.eclipse
.core
.runtime
.jobs
.IJobChangeEvent
;
44 import org
.eclipse
.core
.runtime
.jobs
.Job
;
45 import org
.eclipse
.core
.runtime
.jobs
.JobChangeAdapter
;
46 import org
.eclipse
.core
.runtime
.preferences
.IEclipsePreferences
.IPreferenceChangeListener
;
47 import org
.eclipse
.core
.runtime
.preferences
.IEclipsePreferences
.PreferenceChangeEvent
;
48 import org
.eclipse
.core
.runtime
.preferences
.InstanceScope
;
49 import org
.eclipse
.egit
.core
.AdapterUtils
;
50 import org
.eclipse
.egit
.core
.RepositoryUtil
;
51 import org
.eclipse
.egit
.core
.internal
.gerrit
.GerritUtil
;
52 import org
.eclipse
.egit
.core
.internal
.indexdiff
.IndexDiffCacheEntry
;
53 import org
.eclipse
.egit
.core
.internal
.indexdiff
.IndexDiffChangedListener
;
54 import org
.eclipse
.egit
.core
.internal
.indexdiff
.IndexDiffData
;
55 import org
.eclipse
.egit
.core
.internal
.job
.RuleUtil
;
56 import org
.eclipse
.egit
.core
.op
.CommitOperation
;
57 import org
.eclipse
.egit
.core
.project
.RepositoryMapping
;
58 import org
.eclipse
.egit
.ui
.Activator
;
59 import org
.eclipse
.egit
.ui
.JobFamilies
;
60 import org
.eclipse
.egit
.ui
.UIPreferences
;
61 import org
.eclipse
.egit
.ui
.UIUtils
;
62 import org
.eclipse
.egit
.ui
.internal
.CommonUtils
;
63 import org
.eclipse
.egit
.ui
.internal
.EgitUiEditorUtils
;
64 import org
.eclipse
.egit
.ui
.internal
.GitLabels
;
65 import org
.eclipse
.egit
.ui
.internal
.UIIcons
;
66 import org
.eclipse
.egit
.ui
.internal
.UIText
;
67 import org
.eclipse
.egit
.ui
.internal
.actions
.ActionCommands
;
68 import org
.eclipse
.egit
.ui
.internal
.actions
.BooleanPrefAction
;
69 import org
.eclipse
.egit
.ui
.internal
.actions
.ReplaceWithOursTheirsMenu
;
70 import org
.eclipse
.egit
.ui
.internal
.commands
.shared
.AbortRebaseCommand
;
71 import org
.eclipse
.egit
.ui
.internal
.commands
.shared
.AbstractRebaseCommandHandler
;
72 import org
.eclipse
.egit
.ui
.internal
.commands
.shared
.ContinueRebaseCommand
;
73 import org
.eclipse
.egit
.ui
.internal
.commands
.shared
.SkipRebaseCommand
;
74 import org
.eclipse
.egit
.ui
.internal
.commit
.CommitHelper
;
75 import org
.eclipse
.egit
.ui
.internal
.commit
.CommitJob
;
76 import org
.eclipse
.egit
.ui
.internal
.commit
.CommitMessageHistory
;
77 import org
.eclipse
.egit
.ui
.internal
.commit
.CommitProposalProcessor
;
78 import org
.eclipse
.egit
.ui
.internal
.commit
.CommitJob
.PushMode
;
79 import org
.eclipse
.egit
.ui
.internal
.components
.ToggleableWarningLabel
;
80 import org
.eclipse
.egit
.ui
.internal
.decorators
.IProblemDecoratable
;
81 import org
.eclipse
.egit
.ui
.internal
.decorators
.ProblemLabelDecorator
;
82 import org
.eclipse
.egit
.ui
.internal
.dialogs
.CommitMessageArea
;
83 import org
.eclipse
.egit
.ui
.internal
.dialogs
.CommitMessageComponent
;
84 import org
.eclipse
.egit
.ui
.internal
.dialogs
.CommitMessageComponentState
;
85 import org
.eclipse
.egit
.ui
.internal
.dialogs
.CommitMessageComponentStateManager
;
86 import org
.eclipse
.egit
.ui
.internal
.dialogs
.ICommitMessageComponentNotifications
;
87 import org
.eclipse
.egit
.ui
.internal
.dialogs
.SpellcheckableMessageArea
;
88 import org
.eclipse
.egit
.ui
.internal
.operations
.DeletePathsOperationUI
;
89 import org
.eclipse
.egit
.ui
.internal
.operations
.IgnoreOperationUI
;
90 import org
.eclipse
.egit
.ui
.internal
.repository
.tree
.RepositoryTreeNode
;
91 import org
.eclipse
.jface
.action
.Action
;
92 import org
.eclipse
.jface
.action
.ControlContribution
;
93 import org
.eclipse
.jface
.action
.IAction
;
94 import org
.eclipse
.jface
.action
.IContributionItem
;
95 import org
.eclipse
.jface
.action
.IMenuListener
;
96 import org
.eclipse
.jface
.action
.IMenuManager
;
97 import org
.eclipse
.jface
.action
.IToolBarManager
;
98 import org
.eclipse
.jface
.action
.MenuManager
;
99 import org
.eclipse
.jface
.action
.Separator
;
100 import org
.eclipse
.jface
.action
.ToolBarManager
;
101 import org
.eclipse
.jface
.dialogs
.DialogSettings
;
102 import org
.eclipse
.jface
.dialogs
.IDialogSettings
;
103 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
104 import org
.eclipse
.jface
.layout
.GridDataFactory
;
105 import org
.eclipse
.jface
.layout
.GridLayoutFactory
;
106 import org
.eclipse
.jface
.preference
.IPersistentPreferenceStore
;
107 import org
.eclipse
.jface
.preference
.IPreferenceStore
;
108 import org
.eclipse
.jface
.resource
.ImageDescriptor
;
109 import org
.eclipse
.jface
.resource
.JFaceResources
;
110 import org
.eclipse
.jface
.resource
.LocalResourceManager
;
111 import org
.eclipse
.jface
.util
.IPropertyChangeListener
;
112 import org
.eclipse
.jface
.util
.LocalSelectionTransfer
;
113 import org
.eclipse
.jface
.util
.PropertyChangeEvent
;
114 import org
.eclipse
.jface
.viewers
.AbstractTreeViewer
;
115 import org
.eclipse
.jface
.viewers
.ContentViewer
;
116 import org
.eclipse
.jface
.viewers
.DecoratingLabelProvider
;
117 import org
.eclipse
.jface
.viewers
.IBaseLabelProvider
;
118 import org
.eclipse
.jface
.viewers
.ILabelDecorator
;
119 import org
.eclipse
.jface
.viewers
.ILabelProvider
;
120 import org
.eclipse
.jface
.viewers
.IOpenListener
;
121 import org
.eclipse
.jface
.viewers
.ISelection
;
122 import org
.eclipse
.jface
.viewers
.ISelectionProvider
;
123 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
124 import org
.eclipse
.jface
.viewers
.ITreeViewerListener
;
125 import org
.eclipse
.jface
.viewers
.OpenEvent
;
126 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
127 import org
.eclipse
.jface
.viewers
.TreeExpansionEvent
;
128 import org
.eclipse
.jface
.viewers
.TreeViewer
;
129 import org
.eclipse
.jface
.viewers
.Viewer
;
130 import org
.eclipse
.jface
.viewers
.ViewerComparator
;
131 import org
.eclipse
.jface
.viewers
.ViewerFilter
;
132 import org
.eclipse
.jgit
.api
.AddCommand
;
133 import org
.eclipse
.jgit
.api
.CheckoutCommand
;
134 import org
.eclipse
.jgit
.api
.Git
;
135 import org
.eclipse
.jgit
.api
.ResetCommand
;
136 import org
.eclipse
.jgit
.api
.RmCommand
;
137 import org
.eclipse
.jgit
.api
.errors
.GitAPIException
;
138 import org
.eclipse
.jgit
.api
.errors
.JGitInternalException
;
139 import org
.eclipse
.jgit
.api
.errors
.NoFilepatternException
;
140 import org
.eclipse
.jgit
.annotations
.NonNull
;
141 import org
.eclipse
.jgit
.annotations
.Nullable
;
142 import org
.eclipse
.jgit
.events
.ListenerHandle
;
143 import org
.eclipse
.jgit
.events
.RefsChangedEvent
;
144 import org
.eclipse
.jgit
.events
.RefsChangedListener
;
145 import org
.eclipse
.jgit
.lib
.Constants
;
146 import org
.eclipse
.jgit
.lib
.ObjectId
;
147 import org
.eclipse
.jgit
.lib
.Repository
;
148 import org
.eclipse
.jgit
.lib
.RepositoryState
;
149 import org
.eclipse
.jgit
.revwalk
.RevCommit
;
150 import org
.eclipse
.osgi
.util
.NLS
;
151 import org
.eclipse
.swt
.SWT
;
152 import org
.eclipse
.swt
.custom
.SashForm
;
153 import org
.eclipse
.swt
.custom
.VerifyKeyListener
;
154 import org
.eclipse
.swt
.dnd
.DND
;
155 import org
.eclipse
.swt
.dnd
.DragSourceAdapter
;
156 import org
.eclipse
.swt
.dnd
.DragSourceEvent
;
157 import org
.eclipse
.swt
.dnd
.DropTargetAdapter
;
158 import org
.eclipse
.swt
.dnd
.DropTargetEvent
;
159 import org
.eclipse
.swt
.dnd
.FileTransfer
;
160 import org
.eclipse
.swt
.dnd
.Transfer
;
161 import org
.eclipse
.swt
.events
.DisposeEvent
;
162 import org
.eclipse
.swt
.events
.DisposeListener
;
163 import org
.eclipse
.swt
.events
.FocusEvent
;
164 import org
.eclipse
.swt
.events
.FocusListener
;
165 import org
.eclipse
.swt
.events
.ModifyEvent
;
166 import org
.eclipse
.swt
.events
.ModifyListener
;
167 import org
.eclipse
.swt
.events
.SelectionAdapter
;
168 import org
.eclipse
.swt
.events
.SelectionEvent
;
169 import org
.eclipse
.swt
.events
.VerifyEvent
;
170 import org
.eclipse
.swt
.graphics
.Image
;
171 import org
.eclipse
.swt
.layout
.GridData
;
172 import org
.eclipse
.swt
.layout
.GridLayout
;
173 import org
.eclipse
.swt
.layout
.RowLayout
;
174 import org
.eclipse
.swt
.widgets
.Button
;
175 import org
.eclipse
.swt
.widgets
.Composite
;
176 import org
.eclipse
.swt
.widgets
.Control
;
177 import org
.eclipse
.swt
.widgets
.Display
;
178 import org
.eclipse
.swt
.widgets
.Label
;
179 import org
.eclipse
.swt
.widgets
.Text
;
180 import org
.eclipse
.swt
.widgets
.Tree
;
181 import org
.eclipse
.swt
.widgets
.TreeItem
;
182 import org
.eclipse
.ui
.IActionBars
;
183 import org
.eclipse
.ui
.IEditorInput
;
184 import org
.eclipse
.ui
.IEditorPart
;
185 import org
.eclipse
.ui
.IFileEditorInput
;
186 import org
.eclipse
.ui
.IMemento
;
187 import org
.eclipse
.ui
.IPartListener2
;
188 import org
.eclipse
.ui
.IPartService
;
189 import org
.eclipse
.ui
.ISelectionListener
;
190 import org
.eclipse
.ui
.ISelectionService
;
191 import org
.eclipse
.ui
.IURIEditorInput
;
192 import org
.eclipse
.ui
.IViewSite
;
193 import org
.eclipse
.ui
.IWorkbenchPage
;
194 import org
.eclipse
.ui
.IWorkbenchPart
;
195 import org
.eclipse
.ui
.IWorkbenchPartReference
;
196 import org
.eclipse
.ui
.IWorkbenchPartSite
;
197 import org
.eclipse
.ui
.IWorkbenchWindow
;
198 import org
.eclipse
.ui
.PartInitException
;
199 import org
.eclipse
.ui
.PlatformUI
;
200 import org
.eclipse
.ui
.actions
.ActionFactory
;
201 import org
.eclipse
.ui
.forms
.IFormColors
;
202 import org
.eclipse
.ui
.forms
.widgets
.ExpandableComposite
;
203 import org
.eclipse
.ui
.forms
.widgets
.Form
;
204 import org
.eclipse
.ui
.forms
.widgets
.FormToolkit
;
205 import org
.eclipse
.ui
.forms
.widgets
.Section
;
206 import org
.eclipse
.ui
.handlers
.IHandlerService
;
207 import org
.eclipse
.ui
.operations
.UndoRedoActionGroup
;
208 import org
.eclipse
.ui
.part
.IShowInSource
;
209 import org
.eclipse
.ui
.part
.ShowInContext
;
210 import org
.eclipse
.ui
.part
.ViewPart
;
211 import org
.eclipse
.ui
.progress
.IWorkbenchSiteProgressService
;
214 * A GitX style staging view with embedded commit dialog.
216 public class StagingView
extends ViewPart
implements IShowInSource
{
221 public static final String VIEW_ID
= "org.eclipse.egit.ui.StagingView"; //$NON-NLS-1$
223 private static final String EMPTY_STRING
= ""; //$NON-NLS-1$
225 private static final String SORT_ITEM_TOOLBAR_ID
= "sortItem"; //$NON-NLS-1$
227 private static final String STORE_SORT_STATE
= SORT_ITEM_TOOLBAR_ID
228 + "State"; //$NON-NLS-1$
230 private static final String HORIZONTAL_SASH_FORM_WEIGHT
= "HORIZONTAL_SASH_FORM_WEIGHT"; //$NON-NLS-1$
232 private static final String STAGING_SASH_FORM_WEIGHT
= "STAGING_SASH_FORM_WEIGHT"; //$NON-NLS-1$
234 private ISelection initialSelection
;
236 private FormToolkit toolkit
;
240 private SashForm horizontalSashForm
;
242 private Section stagedSection
;
244 private Section unstagedSection
;
246 private Section commitMessageSection
;
248 private TreeViewer stagedViewer
;
250 private TreeViewer unstagedViewer
;
252 private ToggleableWarningLabel warningLabel
;
254 private Text filterText
;
256 private SpellcheckableMessageArea commitMessageText
;
258 private Text committerText
;
260 private Text authorText
;
262 private CommitMessageComponent commitMessageComponent
;
264 private boolean reactOnSelection
= true;
266 private boolean isViewHidden
;
268 private ISelectionListener selectionChangedListener
;
270 private IPartListener2 partListener
;
272 private ToolBarManager unstagedToolBarManager
;
274 private ToolBarManager stagedToolBarManager
;
276 private Action listPresentationAction
;
278 private Action treePresentationAction
;
280 private Action compactTreePresentationAction
;
282 private Action unstagedExpandAllAction
;
284 private Action unstagedCollapseAllAction
;
286 private Action stagedExpandAllAction
;
288 private Action stagedCollapseAllAction
;
290 private Action compareModeAction
;
293 private Repository currentRepository
;
295 private Presentation presentation
= Presentation
.LIST
;
297 private Set
<IPath
> pathsToExpandInStaged
= new HashSet
<IPath
>();
299 private Set
<IPath
> pathsToExpandInUnstaged
= new HashSet
<IPath
>();
302 * Presentation mode of the staged/unstaged files.
304 public enum Presentation
{
305 /** Show files in flat list */
307 /** Show folder structure in full tree */
310 * Show folder structure in compact tree (folders with only one child
311 * are folded into parent)
316 static class StagingViewUpdate
{
317 Repository repository
;
318 IndexDiffData indexDiff
;
319 Collection
<String
> changedResources
;
321 StagingViewUpdate(Repository theRepository
,
322 IndexDiffData theIndexDiff
, Collection
<String
> theChanges
) {
323 this.repository
= theRepository
;
324 this.indexDiff
= theIndexDiff
;
325 this.changedResources
= theChanges
;
329 static class StagingDragListener
extends DragSourceAdapter
{
331 private ISelectionProvider provider
;
333 public StagingDragListener(ISelectionProvider provider
) {
334 this.provider
= provider
;
338 public void dragStart(DragSourceEvent event
) {
339 event
.doit
= !provider
.getSelection().isEmpty();
343 public void dragFinished(DragSourceEvent event
) {
344 if (LocalSelectionTransfer
.getTransfer().isSupportedType(
346 LocalSelectionTransfer
.getTransfer().setSelection(null);
350 public void dragSetData(DragSourceEvent event
) {
351 IStructuredSelection selection
= (IStructuredSelection
) provider
353 if (selection
.isEmpty())
356 if (LocalSelectionTransfer
.getTransfer().isSupportedType(
358 LocalSelectionTransfer
.getTransfer().setSelection(selection
);
362 if (FileTransfer
.getInstance().isSupportedType(event
.dataType
)) {
363 List
<String
> files
= new ArrayList
<String
>();
364 for (Object selected
: selection
.toList())
365 if (selected
instanceof StagingEntry
) {
366 StagingEntry entry
= (StagingEntry
) selected
;
367 File file
= new File(
368 entry
.getRepository().getWorkTree(),
371 files
.add(file
.getAbsolutePath());
373 if (!files
.isEmpty()) {
374 event
.data
= files
.toArray(new String
[files
.size()]);
381 private final class PartListener
implements IPartListener2
{
382 StructuredSelection lastSelection
;
385 public void partVisible(IWorkbenchPartReference partRef
) {
386 updateHiddenState(partRef
, false);
390 public void partOpened(IWorkbenchPartReference partRef
) {
391 updateHiddenState(partRef
, false);
395 public void partHidden(IWorkbenchPartReference partRef
) {
396 updateHiddenState(partRef
, true);
400 public void partClosed(IWorkbenchPartReference partRef
) {
401 updateHiddenState(partRef
, true);
405 public void partActivated(IWorkbenchPartReference partRef
) {
407 if (lastSelection
!= null) {
408 // view activated: synchronize with last active part
410 reactOnSelection(lastSelection
);
411 lastSelection
= null;
415 IWorkbenchPart part
= partRef
.getPart(false);
416 StructuredSelection sel
= getSelectionOfPart(part
);
418 // remember last selection in the part so that we can
419 // synchronize on it as soon as we will be visible
422 lastSelection
= null;
424 reactOnSelection(sel
);
430 private void updateHiddenState(IWorkbenchPartReference partRef
,
433 isViewHidden
= hidden
;
437 private boolean isMe(IWorkbenchPartReference partRef
) {
438 return partRef
.getPart(false) == StagingView
.this;
442 public void partDeactivated(IWorkbenchPartReference partRef
) {
447 public void partBroughtToTop(IWorkbenchPartReference partRef
) {
452 public void partInputChanged(IWorkbenchPartReference partRef
) {
457 static class TreeDecoratingLabelProvider
extends DecoratingLabelProvider
{
459 ILabelProvider provider
;
461 ILabelDecorator decorator
;
463 public TreeDecoratingLabelProvider(ILabelProvider provider
,
464 ILabelDecorator decorator
) {
465 super(provider
, decorator
);
466 this.provider
= provider
;
467 this.decorator
= decorator
;
470 public Image
getColumnImage(Object element
) {
471 Image image
= provider
.getImage(element
);
472 if (image
!= null && decorator
!= null) {
473 Image decorated
= decorator
.decorateImage(image
, element
);
474 if (decorated
!= null)
481 public String
getText(Object element
) {
482 return provider
.getText(element
);
486 static class StagingViewSearchThread
extends Thread
{
487 private StagingView stagingView
;
489 private static final Object lock
= new Object();
491 private volatile static int globalThreadIndex
= 0;
493 private int currentThreadIx
;
495 public StagingViewSearchThread(StagingView stagingView
) {
496 super("staging_view_filter_thread" + ++globalThreadIndex
); //$NON-NLS-1$
497 this.stagingView
= stagingView
;
498 currentThreadIx
= globalThreadIndex
;
503 synchronized (lock
) {
504 if (currentThreadIx
< globalThreadIndex
)
506 stagingView
.refreshViewersPreservingExpandedElements();
512 private final IPreferenceChangeListener prefListener
= new IPreferenceChangeListener() {
515 public void preferenceChange(PreferenceChangeEvent event
) {
516 if (!RepositoryUtil
.PREFS_DIRECTORIES_REL
.equals(event
.getKey())) {
520 final Repository repo
= currentRepository
;
524 if (Activator
.getDefault().getRepositoryUtil().contains(repo
))
532 private final IPropertyChangeListener uiPrefsListener
= new IPropertyChangeListener() {
534 public void propertyChange(PropertyChangeEvent event
) {
535 if (UIPreferences
.COMMIT_DIALOG_WARN_ABOUT_MESSAGE_SECOND_LINE
536 .equals(event
.getProperty())) {
537 asyncExec(new Runnable() {
540 if (!commitMessageSection
.isDisposed()) {
549 private Action signedOffByAction
;
551 private Action addChangeIdAction
;
553 private Action amendPreviousCommitAction
;
555 private Action openNewCommitsAction
;
557 private Action columnLayoutAction
;
559 private Action fileNameModeAction
;
561 private Action refreshAction
;
563 private Action sortAction
;
565 private SashForm stagingSashForm
;
567 private IndexDiffChangedListener myIndexDiffListener
= new IndexDiffChangedListener() {
569 public void indexDiffChanged(Repository repository
,
570 IndexDiffData indexDiffData
) {
575 private IndexDiffCacheEntry cacheEntry
;
577 private UndoRedoActionGroup undoRedoActionGroup
;
579 private Button commitButton
;
581 private Button commitAndPushButton
;
583 private Section rebaseSection
;
585 private Button rebaseContinueButton
;
587 private Button rebaseSkipButton
;
589 private Button rebaseAbortButton
;
591 private Button ignoreErrors
;
593 private ListenerHandle refsChangedListener
;
595 private LocalResourceManager resources
= new LocalResourceManager(
596 JFaceResources
.getResources());
598 private boolean disposed
;
600 private Image
getImage(ImageDescriptor descriptor
) {
601 return (Image
) this.resources
.get(descriptor
);
605 public void init(IViewSite site
, IMemento viewMemento
)
606 throws PartInitException
{
607 super.init(site
, viewMemento
);
608 this.initialSelection
= site
.getWorkbenchWindow().getSelectionService()
613 public void createPartControl(Composite parent
) {
614 GridLayoutFactory
.fillDefaults().applyTo(parent
);
616 toolkit
= new FormToolkit(parent
.getDisplay());
617 parent
.addDisposeListener(new DisposeListener() {
620 public void widgetDisposed(DisposeEvent e
) {
621 if (commitMessageComponent
.isAmending()
622 || userEnteredCommitMessage())
623 saveCommitMessageComponentState();
625 deleteCommitMessageComponentState();
631 form
= toolkit
.createForm(parent
);
633 form
.setImage(getImage(UIIcons
.REPOSITORY
));
634 form
.setText(UIText
.StagingView_NoSelectionTitle
);
635 GridDataFactory
.fillDefaults().grab(true, true).applyTo(form
);
636 toolkit
.decorateFormHeading(form
);
637 GridLayoutFactory
.swtDefaults().applyTo(form
.getBody());
639 horizontalSashForm
= new SashForm(form
.getBody(), SWT
.NONE
);
640 saveSashFormWeightsOnDisposal(horizontalSashForm
,
641 HORIZONTAL_SASH_FORM_WEIGHT
);
642 toolkit
.adapt(horizontalSashForm
, true, true);
643 GridDataFactory
.fillDefaults().grab(true, true)
644 .applyTo(horizontalSashForm
);
646 stagingSashForm
= new SashForm(horizontalSashForm
,
647 getStagingFormOrientation());
648 saveSashFormWeightsOnDisposal(stagingSashForm
,
649 STAGING_SASH_FORM_WEIGHT
);
650 toolkit
.adapt(stagingSashForm
, true, true);
651 GridDataFactory
.fillDefaults().grab(true, true)
652 .applyTo(stagingSashForm
);
654 unstagedSection
= toolkit
.createSection(stagingSashForm
,
655 ExpandableComposite
.TITLE_BAR
);
657 unstagedSection
.setLayoutData(
658 GridDataFactory
.fillDefaults().grab(true, true).create());
660 createUnstagedToolBarComposite();
662 Composite unstagedComposite
= toolkit
.createComposite(unstagedSection
);
663 toolkit
.paintBordersFor(unstagedComposite
);
664 unstagedSection
.setClient(unstagedComposite
);
665 GridLayoutFactory
.fillDefaults().extendedMargins(2, 2, 2, 2)
666 .applyTo(unstagedComposite
);
668 unstagedViewer
= createTree(unstagedComposite
);
669 GridDataFactory
.fillDefaults().grab(true, true)
670 .applyTo(unstagedViewer
.getControl());
671 unstagedViewer
.getTree().setData(FormToolkit
.KEY_DRAW_BORDER
,
672 FormToolkit
.TREE_BORDER
);
673 unstagedViewer
.setLabelProvider(createLabelProvider(unstagedViewer
));
674 unstagedViewer
.setContentProvider(createStagingContentProvider(true));
675 unstagedViewer
.addDragSupport(DND
.DROP_MOVE
| DND
.DROP_COPY
677 new Transfer
[] { LocalSelectionTransfer
.getTransfer(),
678 FileTransfer
.getInstance() }, new StagingDragListener(
680 unstagedViewer
.addDropSupport(DND
.DROP_MOVE
,
681 new Transfer
[] { LocalSelectionTransfer
.getTransfer() },
682 new DropTargetAdapter() {
684 public void drop(DropTargetEvent event
) {
685 // Bug 411466: It is very important that detail is set
686 // to DND.DROP_COPY. If it was left as DND.DROP_MOVE and
687 // the drag comes from the Navigator view, the code in
688 // NavigatorDragAdapter would delete the resources.
689 event
.detail
= DND
.DROP_COPY
;
690 if (event
.data
instanceof IStructuredSelection
) {
691 final IStructuredSelection selection
= (IStructuredSelection
) event
.data
;
697 public void dragOver(DropTargetEvent event
) {
698 event
.detail
= DND
.DROP_MOVE
;
701 unstagedViewer
.addOpenListener(new IOpenListener() {
703 public void open(OpenEvent event
) {
707 unstagedViewer
.setComparator(
708 new StagingEntryComparator(getSortCheckState(), getPreferenceStore()
709 .getBoolean(UIPreferences
.STAGING_VIEW_FILENAME_MODE
)));
710 enableAutoExpand(unstagedViewer
);
711 addListenerToDisableAutoExpandOnCollapse(unstagedViewer
);
713 Composite rebaseAndCommitComposite
= toolkit
.createComposite(horizontalSashForm
);
714 rebaseAndCommitComposite
.setLayout(GridLayoutFactory
.fillDefaults().create());
716 rebaseSection
= toolkit
.createSection(rebaseAndCommitComposite
,
717 ExpandableComposite
.TITLE_BAR
);
718 rebaseSection
.setText(UIText
.StagingView_RebaseLabel
);
720 Composite rebaseComposite
= toolkit
.createComposite(rebaseSection
);
721 toolkit
.paintBordersFor(rebaseComposite
);
722 rebaseSection
.setClient(rebaseComposite
);
724 rebaseSection
.setLayoutData(GridDataFactory
.fillDefaults().create());
725 rebaseComposite
.setLayout(GridLayoutFactory
.fillDefaults()
726 .numColumns(3).equalWidth(true).create());
727 GridDataFactory buttonGridData
= GridDataFactory
.fillDefaults().align(
728 SWT
.FILL
, SWT
.CENTER
);
730 this.rebaseAbortButton
= toolkit
.createButton(rebaseComposite
,
731 UIText
.StagingView_RebaseAbort
, SWT
.PUSH
);
732 rebaseAbortButton
.addSelectionListener(new SelectionAdapter() {
734 public void widgetSelected(SelectionEvent e
) {
738 rebaseAbortButton
.setImage(getImage(UIIcons
.REBASE_ABORT
));
739 buttonGridData
.applyTo(rebaseAbortButton
);
741 this.rebaseSkipButton
= toolkit
.createButton(rebaseComposite
,
742 UIText
.StagingView_RebaseSkip
, SWT
.PUSH
);
743 rebaseSkipButton
.addSelectionListener(new SelectionAdapter() {
745 public void widgetSelected(SelectionEvent e
) {
749 rebaseSkipButton
.setImage(getImage(UIIcons
.REBASE_SKIP
));
750 buttonGridData
.applyTo(rebaseSkipButton
);
752 this.rebaseContinueButton
= toolkit
.createButton(rebaseComposite
,
753 UIText
.StagingView_RebaseContinue
, SWT
.PUSH
);
754 rebaseContinueButton
.addSelectionListener(new SelectionAdapter() {
756 public void widgetSelected(SelectionEvent e
) {
760 rebaseContinueButton
.setImage(getImage(UIIcons
.REBASE_CONTINUE
));
761 buttonGridData
.applyTo(rebaseContinueButton
);
763 showControl(rebaseSection
, false);
765 commitMessageSection
= toolkit
.createSection(rebaseAndCommitComposite
,
766 ExpandableComposite
.TITLE_BAR
);
767 commitMessageSection
.setText(UIText
.StagingView_CommitMessage
);
768 commitMessageSection
.setLayoutData(GridDataFactory
.fillDefaults()
769 .grab(true, true).create());
771 Composite commitMessageToolbarComposite
= toolkit
772 .createComposite(commitMessageSection
);
773 commitMessageToolbarComposite
.setBackground(null);
774 commitMessageToolbarComposite
.setLayout(createRowLayoutWithoutMargin());
775 commitMessageSection
.setTextClient(commitMessageToolbarComposite
);
776 ToolBarManager commitMessageToolBarManager
= new ToolBarManager(
777 SWT
.FLAT
| SWT
.HORIZONTAL
);
779 amendPreviousCommitAction
= new Action(
780 UIText
.StagingView_Ammend_Previous_Commit
, IAction
.AS_CHECK_BOX
) {
784 commitMessageComponent
.setAmendingButtonSelection(isChecked());
788 amendPreviousCommitAction
.setImageDescriptor(UIIcons
.AMEND_COMMIT
);
789 commitMessageToolBarManager
.add(amendPreviousCommitAction
);
791 signedOffByAction
= new Action(UIText
.StagingView_Add_Signed_Off_By
,
792 IAction
.AS_CHECK_BOX
) {
796 commitMessageComponent
.setSignedOffButtonSelection(isChecked());
799 signedOffByAction
.setImageDescriptor(UIIcons
.SIGNED_OFF
);
800 commitMessageToolBarManager
.add(signedOffByAction
);
802 addChangeIdAction
= new Action(UIText
.StagingView_Add_Change_ID
,
803 IAction
.AS_CHECK_BOX
) {
807 commitMessageComponent
.setChangeIdButtonSelection(isChecked());
810 addChangeIdAction
.setImageDescriptor(UIIcons
.GERRIT
);
811 commitMessageToolBarManager
.add(addChangeIdAction
);
813 commitMessageToolBarManager
814 .createControl(commitMessageToolbarComposite
);
816 Composite commitMessageComposite
= toolkit
817 .createComposite(commitMessageSection
);
818 commitMessageSection
.setClient(commitMessageComposite
);
819 GridLayoutFactory
.fillDefaults().numColumns(1)
820 .applyTo(commitMessageComposite
);
822 warningLabel
= new ToggleableWarningLabel(commitMessageComposite
,
824 GridDataFactory
.fillDefaults().grab(true, false).exclude(true)
825 .applyTo(warningLabel
);
827 Composite commitMessageTextComposite
= toolkit
828 .createComposite(commitMessageComposite
);
829 toolkit
.paintBordersFor(commitMessageTextComposite
);
830 GridDataFactory
.fillDefaults().grab(true, true)
831 .applyTo(commitMessageTextComposite
);
832 GridLayoutFactory
.fillDefaults().numColumns(1)
833 .extendedMargins(2, 2, 2, 2)
834 .applyTo(commitMessageTextComposite
);
836 final CommitProposalProcessor commitProposalProcessor
= new CommitProposalProcessor() {
838 protected Collection
<String
> computeFileNameProposals() {
839 return getStagedFileNames();
843 protected Collection
<String
> computeMessageProposals() {
844 return CommitMessageHistory
.getCommitHistory();
847 commitMessageText
= new CommitMessageArea(commitMessageTextComposite
,
848 EMPTY_STRING
, toolkit
.getBorderStyle()) {
850 protected CommitProposalProcessor
getCommitProposalProcessor() {
851 return commitProposalProcessor
;
854 protected IHandlerService
getHandlerService() {
855 return CommonUtils
.getService(getSite(), IHandlerService
.class);
858 commitMessageText
.setData(FormToolkit
.KEY_DRAW_BORDER
,
859 FormToolkit
.TEXT_BORDER
);
860 GridDataFactory
.fillDefaults().grab(true, true)
861 .applyTo(commitMessageText
);
862 UIUtils
.addBulbDecorator(commitMessageText
.getTextWidget(),
863 UIText
.CommitDialog_ContentAssist
);
865 Composite composite
= toolkit
.createComposite(commitMessageComposite
);
866 toolkit
.paintBordersFor(composite
);
867 GridDataFactory
.fillDefaults().grab(true, false).applyTo(composite
);
868 GridLayoutFactory
.swtDefaults().numColumns(2).applyTo(composite
);
870 toolkit
.createLabel(composite
, UIText
.StagingView_Author
)
872 toolkit
.getColors().getColor(IFormColors
.TB_TOGGLE
));
873 authorText
= toolkit
.createText(composite
, null);
875 .setData(FormToolkit
.KEY_DRAW_BORDER
, FormToolkit
.TEXT_BORDER
);
876 authorText
.setLayoutData(GridDataFactory
.fillDefaults()
877 .grab(true, false).create());
879 toolkit
.createLabel(composite
, UIText
.StagingView_Committer
)
881 toolkit
.getColors().getColor(IFormColors
.TB_TOGGLE
));
882 committerText
= toolkit
.createText(composite
, null);
883 committerText
.setData(FormToolkit
.KEY_DRAW_BORDER
,
884 FormToolkit
.TEXT_BORDER
);
885 committerText
.setLayoutData(GridDataFactory
.fillDefaults()
886 .grab(true, false).create());
888 Composite buttonsContainer
= toolkit
.createComposite(composite
);
889 GridDataFactory
.fillDefaults().grab(true, false).span(2, 1)
890 .indent(0, 8).applyTo(buttonsContainer
);
891 GridLayoutFactory
.fillDefaults().numColumns(2)
892 .applyTo(buttonsContainer
);
894 ignoreErrors
= toolkit
.createButton(buttonsContainer
,
895 UIText
.StagingView_IgnoreErrors
, SWT
.CHECK
);
896 ignoreErrors
.setSelection(false);
897 ignoreErrors
.addSelectionListener(new SelectionAdapter() {
899 public void widgetSelected(SelectionEvent e
) {
901 updateCommitButtons();
905 .addPropertyChangeListener(new IPropertyChangeListener() {
907 public void propertyChange(PropertyChangeEvent event
) {
910 .removePropertyChangeListener(this);
913 asyncExec(new Runnable() {
916 updateIgnoreErrorsButtonVisibility();
918 updateCommitButtons();
924 GridDataFactory
.fillDefaults().align(SWT
.BEGINNING
, SWT
.BEGINNING
)
925 .grab(true, true).applyTo(ignoreErrors
);
926 updateIgnoreErrorsButtonVisibility();
928 Label filler
= toolkit
.createLabel(buttonsContainer
, ""); //$NON-NLS-1$
929 GridDataFactory
.fillDefaults().align(SWT
.FILL
, SWT
.FILL
)
930 .grab(true, true).applyTo(filler
);
932 Composite commitButtonsContainer
= toolkit
933 .createComposite(buttonsContainer
);
934 GridDataFactory
.fillDefaults().align(SWT
.FILL
, SWT
.CENTER
)
935 .applyTo(commitButtonsContainer
);
936 GridLayoutFactory
.fillDefaults().numColumns(2).equalWidth(true)
937 .applyTo(commitButtonsContainer
);
940 this.commitAndPushButton
= toolkit
.createButton(commitButtonsContainer
,
941 UIText
.StagingView_CommitAndPush
, SWT
.PUSH
);
942 commitAndPushButton
.addSelectionListener(new SelectionAdapter() {
944 public void widgetSelected(SelectionEvent e
) {
948 GridDataFactory
.fillDefaults().align(SWT
.FILL
, SWT
.CENTER
)
949 .applyTo(commitAndPushButton
);
951 this.commitButton
= toolkit
.createButton(commitButtonsContainer
,
952 UIText
.StagingView_Commit
, SWT
.PUSH
);
953 commitButton
.setImage(getImage(UIIcons
.COMMIT
));
954 commitButton
.setText(UIText
.StagingView_Commit
);
955 commitButton
.addSelectionListener(new SelectionAdapter() {
957 public void widgetSelected(SelectionEvent e
) {
961 GridDataFactory
.fillDefaults().align(SWT
.FILL
, SWT
.CENTER
)
962 .applyTo(commitButton
);
964 stagedSection
= toolkit
.createSection(stagingSashForm
,
965 ExpandableComposite
.TITLE_BAR
);
967 createStagedToolBarComposite();
969 Composite stagedComposite
= toolkit
.createComposite(stagedSection
);
970 toolkit
.paintBordersFor(stagedComposite
);
971 stagedSection
.setClient(stagedComposite
);
972 GridLayoutFactory
.fillDefaults().extendedMargins(2, 2, 2, 2)
973 .applyTo(stagedComposite
);
975 stagedViewer
= createTree(stagedComposite
);
976 GridDataFactory
.fillDefaults().grab(true, true)
977 .applyTo(stagedViewer
.getControl());
978 stagedViewer
.getTree().setData(FormToolkit
.KEY_DRAW_BORDER
,
979 FormToolkit
.TREE_BORDER
);
980 stagedViewer
.setLabelProvider(createLabelProvider(stagedViewer
));
981 stagedViewer
.setContentProvider(createStagingContentProvider(false));
982 stagedViewer
.addDragSupport(
983 DND
.DROP_MOVE
| DND
.DROP_COPY
| DND
.DROP_LINK
,
984 new Transfer
[] { LocalSelectionTransfer
.getTransfer(),
985 FileTransfer
.getInstance() }, new StagingDragListener(
987 stagedViewer
.addDropSupport(DND
.DROP_MOVE
,
988 new Transfer
[] { LocalSelectionTransfer
.getTransfer() },
989 new DropTargetAdapter() {
991 public void drop(DropTargetEvent event
) {
992 // Bug 411466: It is very important that detail is set
993 // to DND.DROP_COPY. If it was left as DND.DROP_MOVE and
994 // the drag comes from the Navigator view, the code in
995 // NavigatorDragAdapter would delete the resources.
996 event
.detail
= DND
.DROP_COPY
;
997 if (event
.data
instanceof IStructuredSelection
) {
998 final IStructuredSelection selection
= (IStructuredSelection
) event
.data
;
1004 public void dragOver(DropTargetEvent event
) {
1005 event
.detail
= DND
.DROP_MOVE
;
1008 stagedViewer
.addOpenListener(new IOpenListener() {
1010 public void open(OpenEvent event
) {
1014 stagedViewer
.setComparator(
1015 new StagingEntryComparator(getSortCheckState(), getPreferenceStore()
1016 .getBoolean(UIPreferences
.STAGING_VIEW_FILENAME_MODE
)));
1017 enableAutoExpand(stagedViewer
);
1018 addListenerToDisableAutoExpandOnCollapse(stagedViewer
);
1020 selectionChangedListener
= new ISelectionListener() {
1022 public void selectionChanged(IWorkbenchPart part
,
1023 ISelection selection
) {
1024 if (part
== getSite().getPart()) {
1027 // don't accept text selection, only structural one
1028 if (selection
instanceof StructuredSelection
) {
1029 reactOnSelection((StructuredSelection
) selection
);
1034 partListener
= new PartListener();
1036 IPreferenceStore preferenceStore
= getPreferenceStore();
1037 if (preferenceStore
.contains(UIPreferences
.STAGING_VIEW_SYNC_SELECTION
))
1038 reactOnSelection
= preferenceStore
.getBoolean(
1039 UIPreferences
.STAGING_VIEW_SYNC_SELECTION
);
1041 preferenceStore
.setDefault(UIPreferences
.STAGING_VIEW_SYNC_SELECTION
, true);
1043 preferenceStore
.addPropertyChangeListener(uiPrefsListener
);
1045 InstanceScope
.INSTANCE
.getNode(
1046 org
.eclipse
.egit
.core
.Activator
.getPluginId())
1047 .addPreferenceChangeListener(prefListener
);
1049 updateSectionText();
1051 enableCommitWidgets(false);
1052 refreshAction
.setEnabled(false);
1054 createPopupMenu(unstagedViewer
);
1055 createPopupMenu(stagedViewer
);
1057 final ICommitMessageComponentNotifications listener
= new ICommitMessageComponentNotifications() {
1060 public void updateSignedOffToggleSelection(boolean selection
) {
1061 signedOffByAction
.setChecked(selection
);
1065 public void updateChangeIdToggleSelection(boolean selection
) {
1066 addChangeIdAction
.setChecked(selection
);
1068 .setImage(selection ?
getImage(UIIcons
.GERRIT
) : null);
1072 public void statusUpdated() {
1076 commitMessageComponent
= new CommitMessageComponent(listener
);
1077 commitMessageComponent
.attachControls(commitMessageText
, authorText
,
1080 // allow to commit with ctrl-enter
1081 commitMessageText
.getTextWidget().addVerifyKeyListener(new VerifyKeyListener() {
1083 public void verifyKey(VerifyEvent event
) {
1084 if (UIUtils
.isSubmitKeyEvent(event
)) {
1091 commitMessageText
.getTextWidget().addFocusListener(new FocusListener() {
1093 public void focusGained(FocusEvent e
) {
1094 // Ctrl+Enter shortcut only works when the focus is on the commit message text
1095 String commitButtonTooltip
= MessageFormat
.format(
1096 UIText
.StagingView_CommitToolTip
,
1097 UIUtils
.SUBMIT_KEY_STROKE
.format());
1098 commitButton
.setToolTipText(commitButtonTooltip
);
1102 public void focusLost(FocusEvent e
) {
1103 commitButton
.setToolTipText(null);
1107 // react on selection changes
1108 IWorkbenchPartSite site
= getSite();
1109 ISelectionService srv
= CommonUtils
.getService(site
, ISelectionService
.class);
1110 srv
.addPostSelectionListener(selectionChangedListener
);
1111 CommonUtils
.getService(site
, IPartService
.class).addPartListener(
1114 // Use current selection to populate staging view
1115 UIUtils
.notifySelectionChangedWithCurrentSelection(
1116 selectionChangedListener
, site
);
1118 site
.setSelectionProvider(unstagedViewer
);
1120 ViewerFilter filter
= new ViewerFilter() {
1122 public boolean select(Viewer viewer
, Object parentElement
,
1124 StagingViewContentProvider contentProvider
= getContentProvider((TreeViewer
) viewer
);
1125 if (element
instanceof StagingEntry
)
1126 return contentProvider
.isInFilter((StagingEntry
) element
);
1127 else if (element
instanceof StagingFolderEntry
)
1128 return contentProvider
1129 .hasVisibleChildren((StagingFolderEntry
) element
);
1133 unstagedViewer
.addFilter(filter
);
1134 stagedViewer
.addFilter(filter
);
1136 restoreSashFormWeights();
1137 reactOnInitialSelection();
1139 IWorkbenchSiteProgressService service
= CommonUtils
.getService(
1140 getSite(), IWorkbenchSiteProgressService
.class);
1141 if (service
!= null && reactOnSelection
)
1142 // If we are linked, each time IndexDiffUpdateJob starts, indicate
1143 // that the view is busy (e.g. reload() will trigger this job in
1145 service
.showBusyForFamily(org
.eclipse
.egit
.core
.JobFamilies
.INDEX_DIFF_CACHE_UPDATE
);
1148 private boolean commitAndPushEnabled(boolean commitEnabled
) {
1149 Repository repo
= currentRepository
;
1153 return commitEnabled
&& !repo
.getRepositoryState().isRebasing();
1156 private void updateIgnoreErrorsButtonVisibility() {
1157 boolean visible
= getPreferenceStore()
1158 .getBoolean(UIPreferences
.WARN_BEFORE_COMMITTING
)
1159 && getPreferenceStore().getBoolean(UIPreferences
.BLOCK_COMMIT
);
1160 showControl(ignoreErrors
, visible
);
1161 ignoreErrors
.getParent().layout(true);
1164 private int getProblemsSeverity() {
1165 int result
= IProblemDecoratable
.SEVERITY_NONE
;
1166 StagingViewContentProvider stagedContentProvider
= getContentProvider(
1168 StagingEntry
[] entries
= stagedContentProvider
.getStagingEntries();
1169 for (StagingEntry entry
: entries
) {
1170 if (entry
.getProblemSeverity() >= IMarker
.SEVERITY_WARNING
) {
1171 if (result
< entry
.getProblemSeverity()) {
1172 result
= entry
.getProblemSeverity();
1179 private void updateCommitButtons() {
1180 IndexDiffData indexDiff
;
1181 if (cacheEntry
!= null) {
1182 indexDiff
= cacheEntry
.getIndexDiff();
1184 Repository repo
= currentRepository
;
1188 indexDiff
= doReload(repo
);
1191 boolean indexDiffAvailable
= indexDiffAvailable(indexDiff
);
1192 boolean noConflicts
= noConflicts(indexDiff
);
1194 boolean commitEnabled
= !isCommitBlocked() && noConflicts
1195 && indexDiffAvailable
;
1197 boolean commitAndPushEnabled
= commitAndPushEnabled(commitEnabled
);
1199 commitButton
.setEnabled(commitEnabled
);
1200 commitAndPushButton
.setEnabled(commitAndPushEnabled
);
1203 private void saveSashFormWeightsOnDisposal(final SashForm sashForm
,
1204 final String settingsKey
) {
1205 sashForm
.addDisposeListener(new DisposeListener() {
1207 public void widgetDisposed(DisposeEvent e
) {
1208 getDialogSettings().put(settingsKey
,
1209 intArrayToString(sashForm
.getWeights()));
1214 private IDialogSettings
getDialogSettings() {
1215 return DialogSettings
.getOrCreateSection(
1216 Activator
.getDefault().getDialogSettings(),
1217 StagingView
.class.getName());
1220 private static String
intArrayToString(int[] ints
) {
1221 StringBuilder res
= new StringBuilder();
1222 if (ints
!= null && ints
.length
> 0) {
1223 res
.append(String
.valueOf(ints
[0]));
1224 for (int i
= 1; i
< ints
.length
; i
++) {
1226 res
.append(String
.valueOf(ints
[i
]));
1229 return res
.toString();
1232 private void restoreSashFormWeights() {
1233 restoreSashFormWeights(horizontalSashForm
,
1234 HORIZONTAL_SASH_FORM_WEIGHT
);
1235 restoreSashFormWeights(stagingSashForm
,
1236 STAGING_SASH_FORM_WEIGHT
);
1239 private void restoreSashFormWeights(SashForm sashForm
, String settingsKey
) {
1240 IDialogSettings settings
= getDialogSettings();
1241 String weights
= settings
.get(settingsKey
);
1242 if (weights
!= null && !weights
.isEmpty()) {
1243 sashForm
.setWeights(stringToIntArray(weights
));
1247 private static int[] stringToIntArray(String s
) {
1248 String
[] parts
= s
.split(","); //$NON-NLS-1$
1249 int[] ints
= new int[parts
.length
];
1250 for (int i
= 0; i
< parts
.length
; i
++) {
1251 ints
[i
] = Integer
.valueOf(parts
[i
]).intValue();
1256 private void reactOnInitialSelection() {
1257 StructuredSelection sel
= null;
1258 if (initialSelection
instanceof StructuredSelection
) {
1259 sel
= (StructuredSelection
) initialSelection
;
1260 } else if (initialSelection
!= null && !initialSelection
.isEmpty()) {
1261 sel
= getSelectionOfActiveEditor();
1264 reactOnSelection(sel
);
1266 initialSelection
= null;
1269 private StructuredSelection
getSelectionOfActiveEditor() {
1270 IEditorPart activeEditor
= getSite().getPage().getActiveEditor();
1271 if (activeEditor
== null) {
1274 return getSelectionOfPart(activeEditor
);
1277 private static StructuredSelection
getSelectionOfPart(IWorkbenchPart part
) {
1278 StructuredSelection sel
= null;
1279 if (part
instanceof IEditorPart
) {
1280 IResource resource
= getResource((IEditorPart
) part
);
1281 if (resource
!= null) {
1282 sel
= new StructuredSelection(resource
);
1284 Repository repository
= getRepository((IEditorPart
) part
);
1285 if (repository
!= null) {
1286 sel
= new StructuredSelection(repository
);
1290 ISelection selection
= part
.getSite().getPage().getSelection();
1291 if (selection
instanceof StructuredSelection
) {
1292 sel
= (StructuredSelection
) selection
;
1299 private static Repository
getRepository(IEditorPart part
) {
1300 IEditorInput input
= part
.getEditorInput();
1301 if (!(input
instanceof IURIEditorInput
)) {
1304 return AdapterUtils
.adapt(input
, Repository
.class);
1307 private static IResource
getResource(IEditorPart part
) {
1308 IEditorInput input
= part
.getEditorInput();
1309 if (input
instanceof IFileEditorInput
) {
1310 return ((IFileEditorInput
) input
).getFile();
1312 return AdapterUtils
.adapt(input
, IResource
.class);
1316 private boolean getSortCheckState() {
1317 return getDialogSettings().getBoolean(STORE_SORT_STATE
);
1320 private void executeRebaseOperation(AbstractRebaseCommandHandler command
) {
1322 command
.execute(currentRepository
);
1323 } catch (ExecutionException e
) {
1324 Activator
.showError(e
.getMessage(), e
);
1329 * Abort rebase command in progress
1331 protected void rebaseAbort() {
1332 AbortRebaseCommand abortCommand
= new AbortRebaseCommand();
1333 executeRebaseOperation(abortCommand
);
1337 * Rebase next commit and continue rebase in progress
1339 protected void rebaseSkip() {
1340 SkipRebaseCommand skipCommand
= new SkipRebaseCommand();
1341 executeRebaseOperation(skipCommand
);
1345 * Continue rebase command in progress
1347 protected void rebaseContinue() {
1348 ContinueRebaseCommand continueCommand
= new ContinueRebaseCommand();
1349 executeRebaseOperation(continueCommand
);
1352 private void createUnstagedToolBarComposite() {
1353 Composite unstagedToolbarComposite
= toolkit
1354 .createComposite(unstagedSection
);
1355 unstagedToolbarComposite
.setBackground(null);
1356 unstagedToolbarComposite
.setLayout(createRowLayoutWithoutMargin());
1357 unstagedSection
.setTextClient(unstagedToolbarComposite
);
1358 unstagedExpandAllAction
= new Action(UIText
.UIUtils_ExpandAll
,
1359 IAction
.AS_PUSH_BUTTON
) {
1362 unstagedViewer
.expandAll();
1363 enableAutoExpand(unstagedViewer
);
1366 unstagedExpandAllAction
.setImageDescriptor(UIIcons
.EXPAND_ALL
);
1368 unstagedCollapseAllAction
= new Action(UIText
.UIUtils_CollapseAll
,
1369 IAction
.AS_PUSH_BUTTON
) {
1372 unstagedViewer
.collapseAll();
1373 disableAutoExpand(unstagedViewer
);
1376 unstagedCollapseAllAction
.setImageDescriptor(UIIcons
.COLLAPSEALL
);
1378 sortAction
= new Action(UIText
.StagingView_UnstagedSort
,
1379 IAction
.AS_CHECK_BOX
) {
1383 StagingEntryComparator comparator
= (StagingEntryComparator
) unstagedViewer
1385 comparator
.setAlphabeticSort(!isChecked());
1386 comparator
= (StagingEntryComparator
) stagedViewer
.getComparator();
1387 comparator
.setAlphabeticSort(!isChecked());
1388 unstagedViewer
.refresh();
1389 stagedViewer
.refresh();
1393 sortAction
.setImageDescriptor(UIIcons
.STATE_SORT
);
1394 sortAction
.setId(SORT_ITEM_TOOLBAR_ID
);
1395 sortAction
.setChecked(getSortCheckState());
1397 unstagedToolBarManager
= new ToolBarManager(SWT
.FLAT
| SWT
.HORIZONTAL
);
1399 unstagedToolBarManager
.add(sortAction
);
1400 unstagedToolBarManager
.add(unstagedExpandAllAction
);
1401 unstagedToolBarManager
.add(unstagedCollapseAllAction
);
1403 unstagedToolBarManager
.update(true);
1404 unstagedToolBarManager
.createControl(unstagedToolbarComposite
);
1407 private void createStagedToolBarComposite() {
1408 Composite stagedToolbarComposite
= toolkit
1409 .createComposite(stagedSection
);
1410 stagedToolbarComposite
.setBackground(null);
1411 stagedToolbarComposite
.setLayout(createRowLayoutWithoutMargin());
1412 stagedSection
.setTextClient(stagedToolbarComposite
);
1413 stagedExpandAllAction
= new Action(UIText
.UIUtils_ExpandAll
,
1414 IAction
.AS_PUSH_BUTTON
) {
1417 stagedViewer
.expandAll();
1418 enableAutoExpand(stagedViewer
);
1421 stagedExpandAllAction
.setImageDescriptor(UIIcons
.EXPAND_ALL
);
1423 stagedCollapseAllAction
= new Action(UIText
.UIUtils_CollapseAll
,
1424 IAction
.AS_PUSH_BUTTON
) {
1427 stagedViewer
.collapseAll();
1428 disableAutoExpand(stagedViewer
);
1431 stagedCollapseAllAction
.setImageDescriptor(UIIcons
.COLLAPSEALL
);
1433 stagedToolBarManager
= new ToolBarManager(SWT
.FLAT
| SWT
.HORIZONTAL
);
1435 stagedToolBarManager
.add(stagedExpandAllAction
);
1436 stagedToolBarManager
.add(stagedCollapseAllAction
);
1437 stagedToolBarManager
.update(true);
1438 stagedToolBarManager
.createControl(stagedToolbarComposite
);
1441 private static RowLayout
createRowLayoutWithoutMargin() {
1442 RowLayout layout
= new RowLayout();
1443 layout
.marginHeight
= 0;
1444 layout
.marginWidth
= 0;
1445 layout
.marginTop
= 0;
1446 layout
.marginBottom
= 0;
1447 layout
.marginLeft
= 0;
1448 layout
.marginRight
= 0;
1452 private static void addListenerToDisableAutoExpandOnCollapse(
1453 TreeViewer treeViewer
) {
1454 treeViewer
.addTreeListener(new ITreeViewerListener() {
1456 public void treeCollapsed(TreeExpansionEvent event
) {
1457 disableAutoExpand(event
.getTreeViewer());
1461 public void treeExpanded(TreeExpansionEvent event
) {
1467 private static void enableAutoExpand(AbstractTreeViewer treeViewer
) {
1468 treeViewer
.setAutoExpandLevel(AbstractTreeViewer
.ALL_LEVELS
);
1471 private static void disableAutoExpand(AbstractTreeViewer treeViewer
) {
1472 treeViewer
.setAutoExpandLevel(0);
1476 * @return selected repository
1478 public Repository
getCurrentRepository() {
1479 return currentRepository
;
1483 public ShowInContext
getShowInContext() {
1484 if (stagedViewer
!= null && stagedViewer
.getTree().isFocusControl())
1485 return getShowInContext(stagedViewer
);
1486 else if (unstagedViewer
!= null
1487 && unstagedViewer
.getTree().isFocusControl())
1488 return getShowInContext(unstagedViewer
);
1493 private ShowInContext
getShowInContext(TreeViewer treeViewer
) {
1494 IStructuredSelection selection
= (IStructuredSelection
) treeViewer
.getSelection();
1495 List
<Object
> elements
= new ArrayList
<Object
>();
1496 for (Object selectedElement
: selection
.toList()) {
1497 if (selectedElement
instanceof StagingEntry
) {
1498 StagingEntry entry
= (StagingEntry
) selectedElement
;
1499 IFile file
= entry
.getFile();
1503 elements
.add(entry
.getLocation());
1504 } else if (selectedElement
instanceof StagingFolderEntry
) {
1505 StagingFolderEntry entry
= (StagingFolderEntry
) selectedElement
;
1506 IContainer container
= entry
.getContainer();
1507 if (container
!= null)
1508 elements
.add(container
);
1510 elements
.add(entry
.getLocation());
1513 return new ShowInContext(null, new StructuredSelection(elements
));
1516 private int getStagingFormOrientation() {
1517 boolean columnLayout
= Activator
.getDefault().getPreferenceStore()
1518 .getBoolean(UIPreferences
.STAGING_VIEW_COLUMN_LAYOUT
);
1520 return SWT
.HORIZONTAL
;
1522 return SWT
.VERTICAL
;
1525 private void enableAllWidgets(boolean enabled
) {
1528 enableCommitWidgets(enabled
);
1529 enableStagingWidgets(enabled
);
1532 private void enableStagingWidgets(boolean enabled
) {
1535 unstagedViewer
.getControl().setEnabled(enabled
);
1536 stagedViewer
.getControl().setEnabled(enabled
);
1539 private void enableCommitWidgets(boolean enabled
) {
1543 commitMessageText
.setEnabled(enabled
);
1544 committerText
.setEnabled(enabled
);
1545 enableAuthorText(enabled
);
1546 amendPreviousCommitAction
.setEnabled(enabled
);
1547 signedOffByAction
.setEnabled(enabled
);
1548 addChangeIdAction
.setEnabled(enabled
);
1549 commitButton
.setEnabled(enabled
);
1550 commitAndPushButton
.setEnabled(enabled
);
1553 private void enableAuthorText(boolean enabled
) {
1554 Repository repo
= currentRepository
;
1555 if (repo
!= null && repo
.getRepositoryState()
1556 .equals(RepositoryState
.CHERRY_PICKING_RESOLVED
)) {
1557 authorText
.setEnabled(false);
1559 authorText
.setEnabled(enabled
);
1563 private void updateToolbar() {
1565 ControlContribution controlContribution
= new ControlContribution(
1566 "StagingView.searchText") { //$NON-NLS-1$
1568 protected Control
createControl(Composite parent
) {
1569 Composite toolbarComposite
= toolkit
.createComposite(parent
,
1571 toolbarComposite
.setBackground(null);
1572 GridLayout headLayout
= new GridLayout();
1573 headLayout
.numColumns
= 2;
1574 headLayout
.marginHeight
= 0;
1575 headLayout
.marginWidth
= 0;
1576 headLayout
.marginTop
= 0;
1577 headLayout
.marginBottom
= 0;
1578 headLayout
.marginLeft
= 0;
1579 headLayout
.marginRight
= 0;
1580 toolbarComposite
.setLayout(headLayout
);
1582 filterText
= new Text(toolbarComposite
, SWT
.SEARCH
1583 | SWT
.ICON_CANCEL
| SWT
.ICON_SEARCH
);
1584 filterText
.setMessage(UIText
.StagingView_Find
);
1585 GridData data
= new GridData(GridData
.FILL_HORIZONTAL
);
1586 data
.widthHint
= 150;
1587 filterText
.setLayoutData(data
);
1588 final Display display
= Display
.getCurrent();
1589 filterText
.addModifyListener(new ModifyListener() {
1591 public void modifyText(ModifyEvent e
) {
1592 final StagingViewSearchThread searchThread
= new StagingViewSearchThread(
1594 display
.timerExec(200, new Runnable() {
1597 searchThread
.start();
1602 return toolbarComposite
;
1606 IActionBars actionBars
= getViewSite().getActionBars();
1607 IToolBarManager toolbar
= actionBars
.getToolBarManager();
1609 toolbar
.add(controlContribution
);
1611 refreshAction
= new Action(UIText
.StagingView_Refresh
, IAction
.AS_PUSH_BUTTON
) {
1614 if (cacheEntry
!= null) {
1616 cacheEntry
.createRefreshResourcesAndIndexDiffJob(),
1621 refreshAction
.setImageDescriptor(UIIcons
.ELCL16_REFRESH
);
1622 toolbar
.add(refreshAction
);
1624 // link with selection
1625 Action linkSelectionAction
= new BooleanPrefAction(
1626 (IPersistentPreferenceStore
) getPreferenceStore(),
1627 UIPreferences
.STAGING_VIEW_SYNC_SELECTION
,
1628 UIText
.StagingView_LinkSelection
) {
1630 public void apply(boolean value
) {
1631 reactOnSelection
= value
;
1634 linkSelectionAction
.setImageDescriptor(UIIcons
.ELCL16_SYNCED
);
1635 toolbar
.add(linkSelectionAction
);
1637 toolbar
.add(new Separator());
1639 compareModeAction
= new Action(UIText
.StagingView_CompareMode
,
1640 IAction
.AS_CHECK_BOX
) {
1643 getPreferenceStore().setValue(
1644 UIPreferences
.STAGING_VIEW_COMPARE_MODE
, isChecked());
1647 compareModeAction
.setImageDescriptor(UIIcons
.ELCL16_COMPARE_VIEW
);
1648 compareModeAction
.setChecked(getPreferenceStore()
1649 .getBoolean(UIPreferences
.STAGING_VIEW_COMPARE_MODE
));
1651 toolbar
.add(compareModeAction
);
1652 toolbar
.add(new Separator());
1654 openNewCommitsAction
= new Action(UIText
.StagingView_OpenNewCommits
,
1655 IAction
.AS_CHECK_BOX
) {
1659 getPreferenceStore().setValue(
1660 UIPreferences
.STAGING_VIEW_SHOW_NEW_COMMITS
, isChecked());
1663 openNewCommitsAction
.setChecked(getPreferenceStore().getBoolean(
1664 UIPreferences
.STAGING_VIEW_SHOW_NEW_COMMITS
));
1666 columnLayoutAction
= new Action(UIText
.StagingView_ColumnLayout
,
1667 IAction
.AS_CHECK_BOX
) {
1671 getPreferenceStore().setValue(
1672 UIPreferences
.STAGING_VIEW_COLUMN_LAYOUT
, isChecked());
1673 stagingSashForm
.setOrientation(isChecked() ? SWT
.HORIZONTAL
1677 columnLayoutAction
.setChecked(getPreferenceStore().getBoolean(
1678 UIPreferences
.STAGING_VIEW_COLUMN_LAYOUT
));
1680 fileNameModeAction
= new Action(UIText
.StagingView_ShowFileNamesFirst
,
1681 IAction
.AS_CHECK_BOX
) {
1685 final boolean enable
= isChecked();
1686 getLabelProvider(stagedViewer
).setFileNameMode(enable
);
1687 getLabelProvider(unstagedViewer
).setFileNameMode(enable
);
1688 getContentProvider(stagedViewer
).setFileNameMode(enable
);
1689 getContentProvider(unstagedViewer
).setFileNameMode(enable
);
1690 StagingEntryComparator comparator
= (StagingEntryComparator
) unstagedViewer
1692 comparator
.setFileNamesFirst(enable
);
1693 comparator
= (StagingEntryComparator
) stagedViewer
.getComparator();
1694 comparator
.setFileNamesFirst(enable
);
1695 getPreferenceStore().setValue(
1696 UIPreferences
.STAGING_VIEW_FILENAME_MODE
, enable
);
1697 refreshViewersPreservingExpandedElements();
1700 fileNameModeAction
.setChecked(getPreferenceStore().getBoolean(
1701 UIPreferences
.STAGING_VIEW_FILENAME_MODE
));
1703 IMenuManager dropdownMenu
= actionBars
.getMenuManager();
1704 MenuManager presentationMenu
= new MenuManager(
1705 UIText
.StagingView_Presentation
);
1706 listPresentationAction
= new Action(UIText
.StagingView_List
,
1707 IAction
.AS_RADIO_BUTTON
) {
1713 presentation
= Presentation
.LIST
;
1714 setPresentation(presentation
, false);
1715 treePresentationAction
.setChecked(false);
1716 compactTreePresentationAction
.setChecked(false);
1717 setExpandCollapseActionsVisible(false);
1721 listPresentationAction
.setImageDescriptor(UIIcons
.FLAT
);
1722 presentationMenu
.add(listPresentationAction
);
1724 treePresentationAction
= new Action(UIText
.StagingView_Tree
,
1725 IAction
.AS_RADIO_BUTTON
) {
1731 presentation
= Presentation
.TREE
;
1732 setPresentation(presentation
, false);
1733 listPresentationAction
.setChecked(false);
1734 compactTreePresentationAction
.setChecked(false);
1735 setExpandCollapseActionsVisible(isExpandAllowed());
1739 treePresentationAction
.setImageDescriptor(UIIcons
.HIERARCHY
);
1740 presentationMenu
.add(treePresentationAction
);
1742 compactTreePresentationAction
= new Action(UIText
.StagingView_CompactTree
,
1743 IAction
.AS_RADIO_BUTTON
) {
1749 switchToCompactModeInternal(false);
1754 compactTreePresentationAction
.setImageDescriptor(UIIcons
.COMPACT
);
1755 presentationMenu
.add(compactTreePresentationAction
);
1757 presentation
= readPresentation(UIPreferences
.STAGING_VIEW_PRESENTATION
,
1759 switch (presentation
) {
1761 listPresentationAction
.setChecked(true);
1762 setExpandCollapseActionsVisible(false);
1765 treePresentationAction
.setChecked(true);
1768 compactTreePresentationAction
.setChecked(true);
1773 dropdownMenu
.add(presentationMenu
);
1774 dropdownMenu
.add(new Separator());
1775 dropdownMenu
.add(openNewCommitsAction
);
1776 dropdownMenu
.add(columnLayoutAction
);
1777 dropdownMenu
.add(fileNameModeAction
);
1778 dropdownMenu
.add(compareModeAction
);
1780 actionBars
.setGlobalActionHandler(ActionFactory
.DELETE
.getId(), new GlobalDeleteActionHandler());
1782 // For the normal resource undo/redo actions to be active, so that files
1783 // deleted via the "Delete" action in the staging view can be restored.
1784 IUndoContext workspaceContext
= AdapterUtils
.adapt(ResourcesPlugin
.getWorkspace(), IUndoContext
.class);
1785 undoRedoActionGroup
= new UndoRedoActionGroup(getViewSite(), workspaceContext
, true);
1786 undoRedoActionGroup
.fillActionBars(actionBars
);
1788 actionBars
.updateActionBars();
1791 private Presentation
readPresentation(String key
, Presentation def
) {
1792 String presentationString
= getPreferenceStore().getString(key
);
1793 if (presentationString
.length() > 0) {
1795 return Presentation
.valueOf(presentationString
);
1796 } catch (IllegalArgumentException e
) {
1797 // Use given default
1803 private void setPresentation(Presentation newOne
, boolean auto
) {
1804 Presentation old
= presentation
;
1805 presentation
= newOne
;
1806 IPreferenceStore store
= getPreferenceStore();
1807 store
.setValue(UIPreferences
.STAGING_VIEW_PRESENTATION
, newOne
.name());
1808 if (auto
&& old
!= newOne
) {
1809 // remember user choice if we switch mode automatically
1810 store
.setValue(UIPreferences
.STAGING_VIEW_PRESENTATION_CHANGED
,
1813 store
.setToDefault(UIPreferences
.STAGING_VIEW_PRESENTATION_CHANGED
);
1817 private void setExpandCollapseActionsVisible(boolean visible
) {
1818 for (IContributionItem item
: unstagedToolBarManager
.getItems()) {
1819 if (!SORT_ITEM_TOOLBAR_ID
.equals(item
.getId())) {
1820 item
.setVisible(visible
);
1823 for (IContributionItem item
: stagedToolBarManager
.getItems()) {
1824 if (!SORT_ITEM_TOOLBAR_ID
.equals(item
.getId())) {
1825 item
.setVisible(visible
);
1828 unstagedExpandAllAction
.setEnabled(visible
);
1829 unstagedCollapseAllAction
.setEnabled(visible
);
1830 stagedExpandAllAction
.setEnabled(visible
);
1831 stagedCollapseAllAction
.setEnabled(visible
);
1832 sortAction
.setEnabled(true);
1833 unstagedToolBarManager
.update(true);
1834 stagedToolBarManager
.update(true);
1837 private boolean isExpandAllowed() {
1838 StagingViewContentProvider contentProvider
= getContentProvider(
1840 if (contentProvider
.getCount() > getMaxLimitForListMode()) {
1843 contentProvider
= getContentProvider(unstagedViewer
);
1844 if (contentProvider
.getCount() > getMaxLimitForListMode()) {
1850 private TreeViewer
createTree(Composite composite
) {
1851 Tree tree
= toolkit
.createTree(composite
, SWT
.FULL_SELECTION
1853 TreeViewer treeViewer
= new TreeViewer(tree
);
1857 private IBaseLabelProvider
createLabelProvider(TreeViewer treeViewer
) {
1858 StagingViewLabelProvider baseProvider
= new StagingViewLabelProvider(
1860 baseProvider
.setFileNameMode(getPreferenceStore().getBoolean(
1861 UIPreferences
.STAGING_VIEW_FILENAME_MODE
));
1863 ProblemLabelDecorator decorator
= new ProblemLabelDecorator(treeViewer
);
1864 return new TreeDecoratingLabelProvider(baseProvider
, decorator
);
1867 private StagingViewContentProvider
createStagingContentProvider(
1869 StagingViewContentProvider provider
= new StagingViewContentProvider(
1871 provider
.setFileNameMode(getPreferenceStore().getBoolean(
1872 UIPreferences
.STAGING_VIEW_FILENAME_MODE
));
1876 private IPreferenceStore
getPreferenceStore() {
1877 return Activator
.getDefault().getPreferenceStore();
1880 private StagingViewLabelProvider
getLabelProvider(ContentViewer viewer
) {
1881 IBaseLabelProvider base
= viewer
.getLabelProvider();
1882 ILabelProvider labelProvider
= ((TreeDecoratingLabelProvider
) base
)
1883 .getLabelProvider();
1884 return (StagingViewLabelProvider
) labelProvider
;
1887 private StagingViewContentProvider
getContentProvider(ContentViewer viewer
) {
1888 return (StagingViewContentProvider
) viewer
.getContentProvider();
1891 private void updateSectionText() {
1892 stagedSection
.setText(MessageFormat
1893 .format(UIText
.StagingView_StagedChanges
,
1894 getSectionCount(stagedViewer
)));
1895 unstagedSection
.setText(MessageFormat
.format(
1896 UIText
.StagingView_UnstagedChanges
,
1897 getSectionCount(unstagedViewer
)));
1900 private String
getSectionCount(TreeViewer viewer
) {
1901 StagingViewContentProvider contentProvider
= getContentProvider(viewer
);
1902 int count
= contentProvider
.getCount();
1903 int shownCount
= contentProvider
.getShownCount();
1904 if (shownCount
== count
)
1905 return Integer
.toString(count
);
1907 return shownCount
+ "/" + count
; //$NON-NLS-1$
1910 private void updateMessage() {
1911 if (hasErrorsOrWarnings()) {
1912 warningLabel
.showMessage(UIText
.StagingView_MessageErrors
);
1913 commitMessageSection
.redraw();
1915 String message
= commitMessageComponent
.getStatus().getMessage();
1916 boolean needsRedraw
= false;
1917 if (message
!= null) {
1918 warningLabel
.showMessage(message
);
1921 needsRedraw
= warningLabel
.getVisible();
1922 warningLabel
.hideMessage();
1924 // Without this explicit redraw, the ControlDecoration of the
1925 // commit message area would not get updated and cause visual
1928 commitMessageSection
.redraw();
1932 private void compareWith(OpenEvent event
) {
1933 IStructuredSelection selection
= (IStructuredSelection
) event
1935 if (selection
.isEmpty()
1936 || !(selection
.getFirstElement() instanceof StagingEntry
))
1938 StagingEntry stagingEntry
= (StagingEntry
) selection
.getFirstElement();
1939 if (stagingEntry
.isSubmodule())
1941 switch (stagingEntry
.getState()) {
1945 runCommand(ActionCommands
.COMPARE_INDEX_WITH_HEAD_ACTION
, selection
);
1949 runCommand(ActionCommands
.MERGE_TOOL_ACTION
, selection
);
1953 case MISSING_AND_CHANGED
:
1955 case MODIFIED_AND_CHANGED
:
1956 case MODIFIED_AND_ADDED
:
1959 if (Activator
.getDefault().getPreferenceStore().getBoolean(
1960 UIPreferences
.STAGING_VIEW_COMPARE_MODE
)) {
1961 // compare with index
1962 runCommand(ActionCommands
.COMPARE_WITH_INDEX_ACTION
, selection
);
1964 openSelectionInEditor(selection
);
1970 private void createPopupMenu(final TreeViewer treeViewer
) {
1971 final MenuManager menuMgr
= new MenuManager();
1972 menuMgr
.setRemoveAllWhenShown(true);
1973 Control control
= treeViewer
.getControl();
1974 control
.setMenu(menuMgr
.createContextMenu(control
));
1975 menuMgr
.addMenuListener(new IMenuListener() {
1978 public void menuAboutToShow(IMenuManager manager
) {
1979 final IStructuredSelection selection
= (IStructuredSelection
) treeViewer
1981 if (selection
.isEmpty())
1984 List
<StagingEntry
> stagingEntryList
= new ArrayList
<StagingEntry
>();
1986 boolean submoduleSelected
= false;
1987 boolean folderSelected
= false;
1988 for (Object element
: selection
.toArray()) {
1989 if (element
instanceof StagingFolderEntry
) {
1990 StagingFolderEntry folder
= (StagingFolderEntry
) element
;
1991 folderSelected
= true;
1992 StagingViewContentProvider contentProvider
= getContentProvider(treeViewer
);
1993 List
<StagingEntry
> stagingEntries
= contentProvider
1994 .getStagingEntriesFiltered(folder
);
1995 for (StagingEntry stagingEntry
: stagingEntries
) {
1996 if (!stagingEntryList
.contains(stagingEntry
))
1997 stagingEntryList
.add(stagingEntry
);
1999 } else if (element
instanceof StagingEntry
) {
2000 StagingEntry entry
= (StagingEntry
) element
;
2001 if (entry
.isSubmodule())
2002 submoduleSelected
= true;
2003 if (!stagingEntryList
.contains(entry
))
2004 stagingEntryList
.add(entry
);
2008 final IStructuredSelection fileSelection
= new StructuredSelection(
2011 if (!folderSelected
) {
2012 Action openWorkingTreeVersion
= new Action(
2013 UIText
.CommitFileDiffViewer_OpenWorkingTreeVersionInEditorMenuLabel
) {
2016 openSelectionInEditor(fileSelection
);
2019 openWorkingTreeVersion
.setEnabled(!submoduleSelected
2020 && anyElementIsExistingFile(fileSelection
));
2021 menuMgr
.add(openWorkingTreeVersion
);
2022 String label
= stagingEntryList
.get(0).isStaged()
2023 ? UIText
.CommitFileDiffViewer_CompareWorkingDirectoryMenuLabel
2024 : UIText
.StagingView_CompareWithIndexMenuLabel
;
2025 Action openCompareWithIndex
= new Action(label
) {
2028 runCommand(ActionCommands
.COMPARE_WITH_INDEX_ACTION
,
2032 menuMgr
.add(openCompareWithIndex
);
2035 Set
<StagingEntry
.Action
> availableActions
= getAvailableActions(fileSelection
);
2037 boolean addReplaceWithFileInGitIndex
= availableActions
.contains(StagingEntry
.Action
.REPLACE_WITH_FILE_IN_GIT_INDEX
);
2038 boolean addReplaceWithHeadRevision
= availableActions
.contains(StagingEntry
.Action
.REPLACE_WITH_HEAD_REVISION
);
2039 boolean addStage
= availableActions
.contains(StagingEntry
.Action
.STAGE
);
2040 boolean addUnstage
= availableActions
.contains(StagingEntry
.Action
.UNSTAGE
);
2041 boolean addDelete
= availableActions
.contains(StagingEntry
.Action
.DELETE
);
2042 boolean addIgnore
= availableActions
.contains(StagingEntry
.Action
.IGNORE
);
2043 boolean addLaunchMergeTool
= availableActions
.contains(StagingEntry
.Action
.LAUNCH_MERGE_TOOL
);
2044 boolean addReplaceWithOursTheirsMenu
= availableActions
2045 .contains(StagingEntry
.Action
.REPLACE_WITH_OURS_THEIRS_MENU
);
2048 menuMgr
.add(new Action(UIText
.StagingView_StageItemMenuLabel
) {
2055 menuMgr
.add(new Action(UIText
.StagingView_UnstageItemMenuLabel
) {
2061 boolean selectionIncludesNonWorkspaceResources
= selectionIncludesNonWorkspaceResources(fileSelection
);
2062 if (addReplaceWithFileInGitIndex
)
2063 if (selectionIncludesNonWorkspaceResources
)
2064 menuMgr
.add(new ReplaceAction(
2065 UIText
.StagingView_replaceWithFileInGitIndex
,
2066 fileSelection
, false));
2068 menuMgr
.add(createItem(
2069 UIText
.StagingView_replaceWithFileInGitIndex
,
2070 ActionCommands
.DISCARD_CHANGES_ACTION
,
2071 fileSelection
)); // replace with index
2072 if (addReplaceWithHeadRevision
)
2073 if (selectionIncludesNonWorkspaceResources
)
2074 menuMgr
.add(new ReplaceAction(
2075 UIText
.StagingView_replaceWithHeadRevision
,
2076 fileSelection
, true));
2078 menuMgr
.add(createItem(
2079 UIText
.StagingView_replaceWithHeadRevision
,
2080 ActionCommands
.REPLACE_WITH_HEAD_ACTION
,
2083 menuMgr
.add(new IgnoreAction(fileSelection
));
2085 menuMgr
.add(new DeleteAction(fileSelection
));
2086 if (addLaunchMergeTool
)
2087 menuMgr
.add(createItem(UIText
.StagingView_MergeTool
,
2088 ActionCommands
.MERGE_TOOL_ACTION
,
2090 if (addReplaceWithOursTheirsMenu
) {
2091 MenuManager replaceWithMenu
= new MenuManager(
2092 UIText
.StagingView_ReplaceWith
);
2093 ReplaceWithOursTheirsMenu oursTheirsMenu
= new ReplaceWithOursTheirsMenu();
2094 oursTheirsMenu
.initialize(getSite());
2095 replaceWithMenu
.add(oursTheirsMenu
);
2096 menuMgr
.add(replaceWithMenu
);
2098 menuMgr
.add(new Separator());
2099 menuMgr
.add(createShowInMenu());
2105 private boolean anyElementIsExistingFile(IStructuredSelection s
) {
2106 for (Object element
: s
.toList()) {
2107 if (element
instanceof StagingEntry
) {
2108 StagingEntry entry
= (StagingEntry
) element
;
2109 if (entry
.getType() != IResource
.FILE
) {
2112 if (entry
.getLocation().toFile().exists()) {
2121 * @return selected presentation
2123 Presentation
getPresentation() {
2124 return presentation
;
2128 * @return the trimmed string which is the current filter, empty string for
2131 String
getFilterString() {
2132 if (filterText
!= null && !filterText
.isDisposed())
2133 return filterText
.getText().trim();
2135 return ""; //$NON-NLS-1$
2139 * Refresh the unstaged and staged viewers without preserving expanded
2142 public void refreshViewers() {
2143 syncExec(new Runnable() {
2146 refreshViewersInternal();
2152 * Refresh the unstaged and staged viewers, preserving expanded elements
2154 public void refreshViewersPreservingExpandedElements() {
2155 syncExec(new Runnable() {
2158 Object
[] unstagedExpanded
= unstagedViewer
2159 .getExpandedElements();
2160 Object
[] stagedExpanded
= stagedViewer
.getExpandedElements();
2161 refreshViewersInternal();
2162 unstagedViewer
.setExpandedElements(unstagedExpanded
);
2163 stagedViewer
.setExpandedElements(stagedExpanded
);
2168 private void refreshViewersInternal() {
2169 unstagedViewer
.refresh();
2170 stagedViewer
.refresh();
2171 updateSectionText();
2174 private IContributionItem
createShowInMenu() {
2175 IWorkbenchWindow workbenchWindow
= getSite().getWorkbenchWindow();
2176 return UIUtils
.createShowInMenu(workbenchWindow
);
2179 private class ReplaceAction
extends Action
{
2181 IStructuredSelection selection
;
2182 private final boolean headRevision
;
2184 ReplaceAction(String text
, @NonNull IStructuredSelection selection
,
2185 boolean headRevision
) {
2187 this.selection
= selection
;
2188 this.headRevision
= headRevision
;
2191 private void getSelectedFiles(@NonNull List
<String
> files
,
2192 @NonNull List
<String
> inaccessibleFiles
) {
2193 Iterator iterator
= selection
.iterator();
2194 while (iterator
.hasNext()) {
2195 Object selectedItem
= iterator
.next();
2196 if (selectedItem
instanceof StagingEntry
) {
2197 StagingEntry stagingEntry
= (StagingEntry
) selectedItem
;
2198 String path
= stagingEntry
.getPath();
2200 IFile resource
= stagingEntry
.getFile();
2201 if (resource
== null || !resource
.isAccessible()) {
2202 inaccessibleFiles
.add(path
);
2208 private void replaceWith(@NonNull List
<String
> files
,
2209 @NonNull List
<String
> inaccessibleFiles
) {
2210 Repository repository
= currentRepository
;
2211 if (files
.isEmpty() || repository
== null) {
2214 CheckoutCommand checkoutCommand
= new Git(repository
)
2217 checkoutCommand
.setStartPoint(Constants
.HEAD
);
2219 for (String path
: files
) {
2220 checkoutCommand
.addPath(path
);
2223 checkoutCommand
.call();
2224 if (!inaccessibleFiles
.isEmpty()) {
2225 IndexDiffCacheEntry indexDiffCacheForRepository
= org
.eclipse
.egit
.core
.Activator
2226 .getDefault().getIndexDiffCache()
2227 .getIndexDiffCacheEntry(repository
);
2228 if (indexDiffCacheForRepository
!= null) {
2229 indexDiffCacheForRepository
2230 .refreshFiles(inaccessibleFiles
);
2233 } catch (Exception e
) {
2234 Activator
.handleError(UIText
.StagingView_checkoutFailed
, e
,
2241 boolean performAction
= MessageDialog
.openConfirm(form
.getShell(),
2242 UIText
.DiscardChangesAction_confirmActionTitle
,
2243 UIText
.DiscardChangesAction_confirmActionMessage
);
2244 if (!performAction
) {
2247 List
<String
> files
= new ArrayList
<>();
2248 List
<String
> inaccessibleFiles
= new ArrayList
<>();
2249 getSelectedFiles(files
, inaccessibleFiles
);
2250 replaceWith(files
, inaccessibleFiles
);
2254 private static class IgnoreAction
extends Action
{
2256 private final IStructuredSelection selection
;
2258 IgnoreAction(IStructuredSelection selection
) {
2259 super(UIText
.StagingView_IgnoreItemMenuLabel
);
2260 this.selection
= selection
;
2265 IgnoreOperationUI operation
= new IgnoreOperationUI(
2266 getSelectedPaths(selection
));
2271 private class DeleteAction
extends Action
{
2273 private final IStructuredSelection selection
;
2275 DeleteAction(IStructuredSelection selection
) {
2276 super(UIText
.StagingView_DeleteItemMenuLabel
);
2277 this.selection
= selection
;
2282 DeletePathsOperationUI operation
= new DeletePathsOperationUI(
2283 getSelectedPaths(selection
), getSite());
2288 private class GlobalDeleteActionHandler
extends Action
{
2292 DeletePathsOperationUI operation
= new DeletePathsOperationUI(
2293 getSelectedPaths(getSelection()), getSite());
2298 public boolean isEnabled() {
2299 if (!unstagedViewer
.getTree().isFocusControl())
2302 IStructuredSelection selection
= getSelection();
2303 if (selection
.isEmpty())
2306 for (Object element
: selection
.toList()) {
2307 if (!(element
instanceof StagingEntry
))
2309 StagingEntry entry
= (StagingEntry
) element
;
2310 if (!entry
.getAvailableActions().contains(StagingEntry
.Action
.DELETE
))
2317 private IStructuredSelection
getSelection() {
2318 return (IStructuredSelection
) unstagedViewer
.getSelection();
2322 private static List
<IPath
> getSelectedPaths(IStructuredSelection selection
) {
2323 List
<IPath
> paths
= new ArrayList
<IPath
>();
2324 Iterator iterator
= selection
.iterator();
2325 while (iterator
.hasNext()) {
2326 StagingEntry stagingEntry
= (StagingEntry
) iterator
.next();
2327 paths
.add(stagingEntry
.getLocation());
2334 * @return true if the selection includes a non-workspace resource, false otherwise
2336 private boolean selectionIncludesNonWorkspaceResources(ISelection selection
) {
2337 if (!(selection
instanceof IStructuredSelection
))
2339 IStructuredSelection structuredSelection
= (IStructuredSelection
) selection
;
2340 Iterator iterator
= structuredSelection
.iterator();
2341 while (iterator
.hasNext()) {
2342 Object selectedObject
= iterator
.next();
2343 if (!(selectedObject
instanceof StagingEntry
))
2345 StagingEntry stagingEntry
= (StagingEntry
) selectedObject
;
2346 IFile file
= stagingEntry
.getFile();
2347 if (file
== null || !file
.isAccessible()) {
2354 private void openSelectionInEditor(ISelection s
) {
2355 Repository repo
= currentRepository
;
2356 if (repo
== null || s
.isEmpty() || !(s
instanceof IStructuredSelection
)) {
2359 final IStructuredSelection iss
= (IStructuredSelection
) s
;
2360 for (Object element
: iss
.toList()) {
2361 if (element
instanceof StagingEntry
) {
2362 StagingEntry entry
= (StagingEntry
) element
;
2363 String relativePath
= entry
.getPath();
2364 String path
= new Path(repo
.getWorkTree()
2365 .getAbsolutePath()).append(relativePath
)
2367 openFileInEditor(path
);
2372 private void openFileInEditor(String filePath
) {
2373 IWorkbenchWindow window
= PlatformUI
.getWorkbench()
2374 .getActiveWorkbenchWindow();
2375 File file
= new File(filePath
);
2376 if (!file
.exists()) {
2377 String message
= NLS
.bind(UIText
.CommitFileDiffViewer_FileDoesNotExist
, filePath
);
2378 Activator
.showError(message
, null);
2380 IWorkbenchPage page
= window
.getActivePage();
2381 EgitUiEditorUtils
.openEditor(file
, page
);
2384 private static Set
<StagingEntry
.Action
> getAvailableActions(IStructuredSelection selection
) {
2385 Set
<StagingEntry
.Action
> availableActions
= EnumSet
.noneOf(StagingEntry
.Action
.class);
2386 for (Iterator it
= selection
.iterator(); it
.hasNext(); ) {
2387 StagingEntry stagingEntry
= (StagingEntry
) it
.next();
2388 if (availableActions
.isEmpty())
2389 availableActions
.addAll(stagingEntry
.getAvailableActions());
2391 availableActions
.retainAll(stagingEntry
.getAvailableActions());
2393 return availableActions
;
2396 private IAction
createItem(String text
, final String commandId
,
2397 final IStructuredSelection selection
) {
2398 return new Action(text
) {
2401 CommonUtils
.runCommand(commandId
, selection
);
2406 private boolean shouldUpdateSelection() {
2407 return !isDisposed() && !isViewHidden
&& reactOnSelection
;
2410 private void reactOnSelection(StructuredSelection selection
) {
2411 if (selection
.size() != 1 || !shouldUpdateSelection()) {
2414 Object firstElement
= selection
.getFirstElement();
2415 if (firstElement
instanceof RepositoryTreeNode
) {
2416 RepositoryTreeNode repoNode
= (RepositoryTreeNode
) firstElement
;
2417 if (currentRepository
!= repoNode
.getRepository()) {
2418 reload(repoNode
.getRepository());
2420 } else if (firstElement
instanceof Repository
) {
2421 Repository repo
= (Repository
) firstElement
;
2422 if (currentRepository
!= repo
) {
2426 IResource resource
= AdapterUtils
.adapt(firstElement
,
2428 if (resource
!= null) {
2429 showResource(resource
);
2431 Repository repo
= AdapterUtils
.adapt(firstElement
,
2433 if (repo
!= null && currentRepository
!= repo
) {
2440 private void showResource(final IResource resource
) {
2441 if (resource
== null || !resource
.isAccessible()) {
2444 Job
.getJobManager().cancel(JobFamilies
.UPDATE_SELECTION
);
2445 Job job
= new Job(UIText
.StagingView_GetRepo
) {
2447 protected IStatus
run(IProgressMonitor monitor
) {
2448 if (monitor
.isCanceled()) {
2449 return Status
.CANCEL_STATUS
;
2451 RepositoryMapping mapping
= RepositoryMapping
2452 .getMapping(resource
);
2453 if (mapping
!= null) {
2454 Repository newRep
= mapping
.getRepository();
2455 if (newRep
!= null && newRep
!= currentRepository
) {
2456 if (monitor
.isCanceled()) {
2457 return Status
.CANCEL_STATUS
;
2462 return Status
.OK_STATUS
;
2466 public boolean belongsTo(Object family
) {
2467 return JobFamilies
.UPDATE_SELECTION
== family
;
2471 public boolean shouldRun() {
2472 return shouldUpdateSelection();
2475 job
.setSystem(true);
2476 schedule(job
, false);
2479 private void stage(IStructuredSelection selection
) {
2480 StagingViewContentProvider contentProvider
= getContentProvider(unstagedViewer
);
2481 final Git git
= new Git(currentRepository
);
2482 Iterator iterator
= selection
.iterator();
2483 final List
<String
> addPaths
= new ArrayList
<String
>();
2484 final List
<String
> rmPaths
= new ArrayList
<String
>();
2485 resetPathsToExpand();
2486 while (iterator
.hasNext()) {
2487 Object element
= iterator
.next();
2488 if (element
instanceof StagingEntry
) {
2489 StagingEntry entry
= (StagingEntry
) element
;
2490 selectEntryForStaging(entry
, addPaths
, rmPaths
);
2491 addPathAndParentPaths(entry
.getParentPath(), pathsToExpandInStaged
);
2492 } else if (element
instanceof StagingFolderEntry
) {
2493 StagingFolderEntry folder
= (StagingFolderEntry
) element
;
2494 List
<StagingEntry
> entries
= contentProvider
2495 .getStagingEntriesFiltered(folder
);
2496 for (StagingEntry entry
: entries
)
2497 selectEntryForStaging(entry
, addPaths
, rmPaths
);
2498 addExpandedPathsBelowFolder(folder
, unstagedViewer
,
2499 pathsToExpandInStaged
);
2501 IResource resource
= AdapterUtils
.adapt(element
, IResource
.class);
2502 if (resource
!= null) {
2503 RepositoryMapping mapping
= RepositoryMapping
.getMapping(resource
);
2504 // doesn't do anything if the current repository is a
2505 // submodule of the mapped repo
2506 if (mapping
!= null && mapping
.getRepository() == currentRepository
) {
2507 String path
= mapping
.getRepoRelativePath(resource
);
2508 // If resource corresponds to root of working directory
2509 if ("".equals(path
)) //$NON-NLS-1$
2510 addPaths
.add("."); //$NON-NLS-1$
2518 // start long running operations
2519 if (!addPaths
.isEmpty()) {
2520 Job addJob
= new Job(UIText
.StagingView_AddJob
) {
2522 protected IStatus
run(IProgressMonitor monitor
) {
2524 AddCommand add
= git
.add();
2525 for (String addPath
: addPaths
)
2526 add
.addFilepattern(addPath
);
2528 } catch (NoFilepatternException e1
) {
2530 } catch (JGitInternalException e1
) {
2531 Activator
.handleError(e1
.getCause().getMessage(),
2532 e1
.getCause(), true);
2533 } catch (Exception e1
) {
2534 Activator
.handleError(e1
.getMessage(), e1
, true);
2536 return Status
.OK_STATUS
;
2540 public boolean belongsTo(Object family
) {
2541 return family
== JobFamilies
.ADD_TO_INDEX
;
2545 schedule(addJob
, true);
2548 if (!rmPaths
.isEmpty()) {
2549 Job removeJob
= new Job(UIText
.StagingView_RemoveJob
) {
2551 protected IStatus
run(IProgressMonitor monitor
) {
2553 RmCommand rm
= git
.rm().setCached(true);
2554 for (String rmPath
: rmPaths
)
2555 rm
.addFilepattern(rmPath
);
2557 } catch (NoFilepatternException e
) {
2559 } catch (JGitInternalException e
) {
2560 Activator
.handleError(e
.getCause().getMessage(),
2561 e
.getCause(), true);
2562 } catch (Exception e
) {
2563 Activator
.handleError(e
.getMessage(), e
, true);
2565 return Status
.OK_STATUS
;
2569 public boolean belongsTo(Object family
) {
2570 return family
== JobFamilies
.REMOVE_FROM_INDEX
;
2574 schedule(removeJob
, true);
2578 private void selectEntryForStaging(StagingEntry entry
,
2579 List
<String
> addPaths
, List
<String
> rmPaths
) {
2580 switch (entry
.getState()) {
2588 case MODIFIED_AND_CHANGED
:
2589 case MODIFIED_AND_ADDED
:
2591 addPaths
.add(entry
.getPath());
2594 case MISSING_AND_CHANGED
:
2595 rmPaths
.add(entry
.getPath());
2600 private void unstage(IStructuredSelection selection
) {
2601 if (selection
.isEmpty())
2604 final List
<String
> paths
= processUnstageSelection(selection
);
2605 if (paths
.isEmpty())
2608 final Git git
= new Git(currentRepository
);
2610 Job resetJob
= new Job(UIText
.StagingView_ResetJob
) {
2612 protected IStatus
run(IProgressMonitor monitor
) {
2614 ResetCommand reset
= git
.reset();
2615 for (String path
: paths
)
2616 reset
.addPath(path
);
2618 } catch (GitAPIException e
) {
2619 Activator
.handleError(e
.getMessage(), e
, true);
2621 return Status
.OK_STATUS
;
2625 public boolean belongsTo(Object family
) {
2626 return family
== JobFamilies
.RESET
;
2629 schedule(resetJob
, true);
2632 private List
<String
> processUnstageSelection(IStructuredSelection selection
) {
2633 List
<String
> paths
= new ArrayList
<String
>();
2634 resetPathsToExpand();
2635 for (Object element
: selection
.toList()) {
2636 if (element
instanceof StagingEntry
) {
2637 StagingEntry entry
= (StagingEntry
) element
;
2638 addUnstagePath(entry
, paths
);
2639 addPathAndParentPaths(entry
.getParentPath(), pathsToExpandInUnstaged
);
2640 } else if (element
instanceof StagingFolderEntry
) {
2641 StagingFolderEntry folder
= (StagingFolderEntry
) element
;
2642 List
<StagingEntry
> entries
= getContentProvider(stagedViewer
)
2643 .getStagingEntriesFiltered(folder
);
2644 for (StagingEntry entry
: entries
)
2645 addUnstagePath(entry
, paths
);
2646 addExpandedPathsBelowFolder(folder
, stagedViewer
,
2647 pathsToExpandInUnstaged
);
2653 private void addUnstagePath(StagingEntry entry
, List
<String
> paths
) {
2654 switch (entry
.getState()) {
2658 paths
.add(entry
.getPath());
2665 private void resetPathsToExpand() {
2666 pathsToExpandInStaged
= new HashSet
<IPath
>();
2667 pathsToExpandInUnstaged
= new HashSet
<IPath
>();
2670 private static void addExpandedPathsBelowFolder(StagingFolderEntry folder
,
2671 TreeViewer treeViewer
, Set
<IPath
> addToSet
) {
2672 Object
[] expandedElements
= treeViewer
.getExpandedElements();
2673 for (Object expandedElement
: expandedElements
) {
2674 if (expandedElement
instanceof StagingFolderEntry
) {
2675 StagingFolderEntry expandedFolder
= (StagingFolderEntry
) expandedElement
;
2676 if (folder
.getPath().isPrefixOf(
2677 expandedFolder
.getPath()))
2678 addPathAndParentPaths(expandedFolder
.getPath(), addToSet
);
2683 private static void addPathAndParentPaths(IPath initialPath
, Set
<IPath
> addToSet
) {
2684 for (IPath p
= initialPath
; p
.segmentCount() >= 1; p
= p
2685 .removeLastSegments(1))
2689 private boolean isValidRepo(final Repository repository
) {
2690 return repository
!= null
2691 && !repository
.isBare()
2692 && repository
.getWorkTree().exists();
2696 * Clear the view's state.
2698 * This method must be called from the UI-thread
2702 private void clearRepository(@Nullable Repository repository
) {
2703 saveCommitMessageComponentState();
2704 currentRepository
= null;
2705 StagingViewUpdate update
= new StagingViewUpdate(null, null, null);
2706 unstagedViewer
.setInput(update
);
2707 stagedViewer
.setInput(update
);
2708 enableCommitWidgets(false);
2709 refreshAction
.setEnabled(false);
2710 updateSectionText();
2711 if (repository
!= null && repository
.isBare()) {
2712 form
.setText(UIText
.StagingView_BareRepoSelection
);
2714 form
.setText(UIText
.StagingView_NoSelectionTitle
);
2716 updateIgnoreErrorsButtonVisibility();
2720 * Show rebase buttons only if a rebase operation is in progress
2723 * {@code}true if rebase is in progress
2725 protected void updateRebaseButtonVisibility(final boolean isRebasing
) {
2726 asyncExec(new Runnable() {
2731 showControl(rebaseSection
, isRebasing
);
2732 rebaseSection
.getParent().layout(true);
2737 private static void showControl(Control c
, final boolean show
) {
2739 GridData g
= (GridData
) c
.getLayoutData();
2745 * if the current commit should be amended
2747 public void setAmending(boolean isAmending
) {
2750 if (amendPreviousCommitAction
.isChecked() != isAmending
) {
2751 amendPreviousCommitAction
.setChecked(isAmending
);
2752 amendPreviousCommitAction
.run();
2758 * commit message to set for current repository
2760 public void setCommitMessage(String message
) {
2761 commitMessageText
.setText(message
);
2765 * Reload the staging view asynchronously
2769 public void reload(final Repository repository
) {
2773 if (repository
== null) {
2774 asyncExec(new Runnable() {
2777 clearRepository(null);
2783 if (!isValidRepo(repository
)) {
2784 asyncExec(new Runnable() {
2787 clearRepository(repository
);
2793 final boolean repositoryChanged
= currentRepository
!= repository
;
2794 currentRepository
= repository
;
2796 asyncExec(new Runnable() {
2804 final IndexDiffData indexDiff
= doReload(repository
);
2805 boolean indexDiffAvailable
= indexDiffAvailable(indexDiff
);
2806 boolean noConflicts
= noConflicts(indexDiff
);
2808 if (repositoryChanged
) {
2809 // Reset paths, they're from the old repository
2810 resetPathsToExpand();
2811 if (refsChangedListener
!= null)
2812 refsChangedListener
.remove();
2813 refsChangedListener
= repository
.getListenerList()
2814 .addRefsChangedListener(new RefsChangedListener() {
2817 public void onRefsChanged(RefsChangedEvent event
) {
2818 updateRebaseButtonVisibility(repository
2819 .getRepositoryState().isRebasing());
2824 final StagingViewUpdate update
= new StagingViewUpdate(repository
, indexDiff
, null);
2825 Object
[] unstagedExpanded
= unstagedViewer
2826 .getExpandedElements();
2827 Object
[] stagedExpanded
= stagedViewer
2828 .getExpandedElements();
2830 int elementsCount
= updateAutoExpand(unstagedViewer
,
2831 getUnstaged(indexDiff
));
2832 elementsCount
+= updateAutoExpand(stagedViewer
,
2833 getStaged(indexDiff
));
2835 if (elementsCount
> getMaxLimitForListMode()) {
2836 listPresentationAction
.setEnabled(false);
2837 if (presentation
== Presentation
.LIST
) {
2838 compactTreePresentationAction
.setChecked(true);
2839 switchToCompactModeInternal(true);
2841 setExpandCollapseActionsVisible(false);
2844 listPresentationAction
.setEnabled(true);
2845 boolean changed
= getPreferenceStore().getBoolean(
2846 UIPreferences
.STAGING_VIEW_PRESENTATION_CHANGED
);
2848 listPresentationAction
.setChecked(true);
2849 listPresentationAction
.run();
2850 } else if (presentation
!= Presentation
.LIST
) {
2851 setExpandCollapseActionsVisible(true);
2855 unstagedViewer
.setInput(update
);
2856 stagedViewer
.setInput(update
);
2857 expandPreviousExpandedAndPaths(unstagedExpanded
, unstagedViewer
,
2858 pathsToExpandInUnstaged
);
2859 expandPreviousExpandedAndPaths(stagedExpanded
, stagedViewer
,
2860 pathsToExpandInStaged
);
2861 refreshAction
.setEnabled(true);
2863 updateRebaseButtonVisibility(repository
.getRepositoryState()
2866 updateIgnoreErrorsButtonVisibility();
2868 boolean rebaseContinueEnabled
= indexDiffAvailable
2869 && repository
.getRepositoryState().isRebasing()
2871 rebaseContinueButton
.setEnabled(rebaseContinueEnabled
);
2873 form
.setText(GitLabels
.getStyledLabelSafe(repository
).toString());
2874 updateCommitMessageComponent(repositoryChanged
, indexDiffAvailable
);
2875 enableCommitWidgets(indexDiffAvailable
&& noConflicts
);
2877 updateCommitButtons();
2878 updateSectionText();
2885 * The max number of changed files we can handle in the "list" presentation
2886 * without freezing Eclipse UI for a too long time.
2888 * @return default is 10000
2890 private int getMaxLimitForListMode() {
2891 return Activator
.getDefault().getPreferenceStore()
2892 .getInt(UIPreferences
.STAGING_VIEW_MAX_LIMIT_LIST_MODE
);
2895 private static int getUnstaged(@Nullable IndexDiffData indexDiff
) {
2896 if (indexDiff
== null) {
2899 int size
= indexDiff
.getUntracked().size();
2900 size
+= indexDiff
.getMissing().size();
2901 size
+= indexDiff
.getModified().size();
2902 size
+= indexDiff
.getConflicting().size();
2906 private static int getStaged(@Nullable IndexDiffData indexDiff
) {
2907 if (indexDiff
== null) {
2910 int size
= indexDiff
.getAdded().size();
2911 size
+= indexDiff
.getChanged().size();
2912 size
+= indexDiff
.getRemoved().size();
2916 private int updateAutoExpand(TreeViewer viewer
, int newSize
) {
2917 if (newSize
> getMaxLimitForListMode()) {
2918 // auto expand with too many nodes freezes eclipse
2919 disableAutoExpand(viewer
);
2924 private void switchToCompactModeInternal(boolean auto
) {
2925 setPresentation(Presentation
.COMPACT_TREE
, auto
);
2926 listPresentationAction
.setChecked(false);
2927 treePresentationAction
.setChecked(false);
2929 setExpandCollapseActionsVisible(false);
2931 setExpandCollapseActionsVisible(isExpandAllowed());
2935 private static boolean noConflicts(IndexDiffData indexDiff
) {
2936 return indexDiff
== null ?
true : indexDiff
.getConflicting().isEmpty();
2939 private static boolean indexDiffAvailable(IndexDiffData indexDiff
) {
2940 return indexDiff
== null ?
false : true;
2943 private boolean hasErrorsOrWarnings() {
2944 return getPreferenceStore()
2945 .getBoolean(UIPreferences
.WARN_BEFORE_COMMITTING
)
2946 ?
(getProblemsSeverity() >= Integer
.valueOf(getPreferenceStore()
2947 .getString(UIPreferences
.WARN_BEFORE_COMMITTING_LEVEL
))
2948 && !ignoreErrors
.getSelection()) : false;
2951 @SuppressWarnings("boxing")
2952 private boolean isCommitBlocked() {
2953 return getPreferenceStore()
2954 .getBoolean(UIPreferences
.WARN_BEFORE_COMMITTING
)
2955 && getPreferenceStore().getBoolean(UIPreferences
.BLOCK_COMMIT
)
2956 ?
(getProblemsSeverity() >= Integer
2957 .valueOf(getPreferenceStore().getString(
2958 UIPreferences
.BLOCK_COMMIT_LEVEL
))
2959 && !ignoreErrors
.getSelection())
2963 private IndexDiffData
doReload(@NonNull final Repository repository
) {
2964 IndexDiffCacheEntry entry
= org
.eclipse
.egit
.core
.Activator
.getDefault()
2965 .getIndexDiffCache().getIndexDiffCacheEntry(repository
);
2967 if(cacheEntry
!= null && cacheEntry
!= entry
)
2968 cacheEntry
.removeIndexDiffChangedListener(myIndexDiffListener
);
2971 cacheEntry
.addIndexDiffChangedListener(myIndexDiffListener
);
2973 return cacheEntry
.getIndexDiff();
2976 private void expandPreviousExpandedAndPaths(Object
[] previous
,
2977 TreeViewer viewer
, Set
<IPath
> additionalPaths
) {
2979 StagingViewContentProvider stagedContentProvider
= getContentProvider(
2981 int count
= stagedContentProvider
.getCount();
2982 updateAutoExpand(viewer
, count
);
2984 // Auto-expand is on, so don't change expanded items
2985 if (viewer
.getAutoExpandLevel() == AbstractTreeViewer
.ALL_LEVELS
) {
2989 // No need to expand anything
2990 if (getPresentation() == Presentation
.LIST
)
2993 Set
<IPath
> paths
= new HashSet
<IPath
>(additionalPaths
);
2994 // Instead of just expanding the previous elements directly, also expand
2995 // all parent paths. This makes it work in case of "re-folding" of
2997 for (Object element
: previous
)
2998 if (element
instanceof StagingFolderEntry
)
2999 addPathAndParentPaths(((StagingFolderEntry
) element
).getPath(), paths
);
3000 List
<StagingFolderEntry
> expand
= new ArrayList
<StagingFolderEntry
>();
3002 calculateNodesToExpand(paths
, stagedContentProvider
.getElements(null),
3004 viewer
.setExpandedElements(expand
.toArray());
3007 private void calculateNodesToExpand(Set
<IPath
> paths
, Object
[] elements
,
3008 List
<StagingFolderEntry
> result
) {
3009 if (elements
== null)
3012 for (Object element
: elements
) {
3013 if (element
instanceof StagingFolderEntry
) {
3014 StagingFolderEntry folder
= (StagingFolderEntry
) element
;
3015 if (paths
.contains(folder
.getPath())) {
3017 // Only recurs if folder matched (i.e. don't try to expand
3018 // children of unexpanded parents)
3019 calculateNodesToExpand(paths
, folder
.getChildren(), result
);
3025 private void clearCommitMessageToggles() {
3026 amendPreviousCommitAction
.setChecked(false);
3027 addChangeIdAction
.setChecked(false);
3028 signedOffByAction
.setChecked(false);
3031 void updateCommitMessageComponent(boolean repositoryChanged
, boolean indexDiffAvailable
) {
3032 if (repositoryChanged
)
3033 if (commitMessageComponent
.isAmending()
3034 || userEnteredCommitMessage())
3035 saveCommitMessageComponentState();
3037 deleteCommitMessageComponentState();
3038 if (!indexDiffAvailable
)
3039 return; // only try to restore the stored repo commit message if
3040 // indexDiff is ready
3042 CommitHelper helper
= new CommitHelper(currentRepository
);
3043 CommitMessageComponentState oldState
= null;
3044 if (repositoryChanged
3045 || commitMessageComponent
.getRepository() != currentRepository
) {
3046 oldState
= loadCommitMessageComponentState();
3047 commitMessageComponent
.setRepository(currentRepository
);
3048 if (oldState
== null)
3049 loadInitialState(helper
);
3051 loadExistingState(helper
, oldState
);
3052 } else { // repository did not change
3053 if (!commitMessageComponent
.getHeadCommit().equals(
3054 helper
.getPreviousCommit())) {
3055 if (!commitMessageComponent
.isAmending()
3056 && userEnteredCommitMessage())
3057 addHeadChangedWarning(commitMessageComponent
3058 .getCommitMessage());
3060 loadInitialState(helper
);
3063 amendPreviousCommitAction
.setChecked(commitMessageComponent
3065 amendPreviousCommitAction
.setEnabled(helper
.amendAllowed());
3069 private void loadExistingState(CommitHelper helper
,
3070 CommitMessageComponentState oldState
) {
3071 boolean headCommitChanged
= !oldState
.getHeadCommit().equals(
3072 getCommitId(helper
.getPreviousCommit()));
3073 commitMessageComponent
.enableListeners(false);
3074 commitMessageComponent
.setAuthor(oldState
.getAuthor());
3075 if (headCommitChanged
)
3076 addHeadChangedWarning(oldState
.getCommitMessage());
3078 commitMessageComponent
3079 .setCommitMessage(oldState
.getCommitMessage());
3080 commitMessageComponent
.setCommitter(oldState
.getCommitter());
3081 commitMessageComponent
.setHeadCommit(getCommitId(helper
3082 .getPreviousCommit()));
3083 commitMessageComponent
.setCommitAllowed(helper
.canCommit());
3084 commitMessageComponent
.setCannotCommitMessage(helper
.getCannotCommitMessage());
3085 boolean amendAllowed
= helper
.amendAllowed();
3086 commitMessageComponent
.setAmendAllowed(amendAllowed
);
3088 commitMessageComponent
.setAmending(false);
3089 else if (!headCommitChanged
&& oldState
.getAmend())
3090 commitMessageComponent
.setAmending(true);
3092 commitMessageComponent
.setAmending(false);
3093 commitMessageComponent
.updateUIFromState();
3094 commitMessageComponent
.updateSignedOffAndChangeIdButton();
3095 commitMessageComponent
.enableListeners(true);
3098 private void addHeadChangedWarning(String commitMessage
) {
3099 if (!commitMessage
.startsWith(UIText
.StagingView_headCommitChanged
)) {
3100 String message
= UIText
.StagingView_headCommitChanged
3101 + Text
.DELIMITER
+ Text
.DELIMITER
+ commitMessage
;
3102 commitMessageComponent
.setCommitMessage(message
);
3106 private void loadInitialState(CommitHelper helper
) {
3107 commitMessageComponent
.enableListeners(false);
3108 commitMessageComponent
.resetState();
3109 commitMessageComponent
.setAuthor(helper
.getAuthor());
3110 commitMessageComponent
.setCommitMessage(helper
.getCommitMessage());
3111 commitMessageComponent
.setCommitter(helper
.getCommitter());
3112 commitMessageComponent
.setHeadCommit(getCommitId(helper
3113 .getPreviousCommit()));
3114 commitMessageComponent
.setCommitAllowed(helper
.canCommit());
3115 commitMessageComponent
.setCannotCommitMessage(helper
.getCannotCommitMessage());
3116 commitMessageComponent
.setAmendAllowed(helper
.amendAllowed());
3117 commitMessageComponent
.setAmending(false);
3118 // set the defaults for change id and signed off buttons.
3119 commitMessageComponent
.setDefaults();
3120 commitMessageComponent
.updateUI();
3121 commitMessageComponent
.enableListeners(true);
3124 private boolean userEnteredCommitMessage() {
3125 if (commitMessageComponent
.getRepository() == null)
3127 String message
= commitMessageComponent
.getCommitMessage().replace(
3128 UIText
.StagingView_headCommitChanged
, ""); //$NON-NLS-1$
3129 if (message
== null || message
.trim().length() == 0)
3132 String chIdLine
= "Change-Id: I" + ObjectId
.zeroId().name(); //$NON-NLS-1$
3133 Repository repo
= currentRepository
;
3134 if (repo
!= null && GerritUtil
.getCreateChangeId(repo
.getConfig())
3135 && commitMessageComponent
.getCreateChangeId()) {
3136 if (message
.trim().equals(chIdLine
))
3139 // change id was added automatically, but ther is more in the
3140 // message; strip the id, and check for the signed-off-by tag
3141 message
= message
.replace(chIdLine
, ""); //$NON-NLS-1$
3144 if (org
.eclipse
.egit
.ui
.Activator
.getDefault().getPreferenceStore()
3145 .getBoolean(UIPreferences
.COMMIT_DIALOG_SIGNED_OFF_BY
)
3146 && commitMessageComponent
.isSignedOff()
3147 && message
.trim().equals(
3148 Constants
.SIGNED_OFF_BY_TAG
3149 + commitMessageComponent
.getCommitter()))
3155 private ObjectId
getCommitId(RevCommit commit
) {
3157 return ObjectId
.zeroId();
3158 return commit
.getId();
3161 private void saveCommitMessageComponentState() {
3162 final Repository repo
= commitMessageComponent
.getRepository();
3164 CommitMessageComponentStateManager
.persistState(repo
,
3165 commitMessageComponent
.getState());
3168 private void deleteCommitMessageComponentState() {
3169 if (commitMessageComponent
.getRepository() != null)
3170 CommitMessageComponentStateManager
3171 .deleteState(commitMessageComponent
.getRepository());
3174 private CommitMessageComponentState
loadCommitMessageComponentState() {
3175 return CommitMessageComponentStateManager
.loadState(currentRepository
);
3178 private Collection
<String
> getStagedFileNames() {
3179 StagingViewContentProvider stagedContentProvider
= getContentProvider(stagedViewer
);
3180 StagingEntry
[] entries
= stagedContentProvider
.getStagingEntries();
3181 List
<String
> files
= new ArrayList
<String
>();
3182 for (StagingEntry entry
: entries
)
3183 files
.add(entry
.getPath());
3187 private void commit(boolean pushUpstream
) {
3188 if (!isCommitWithoutFilesAllowed()) {
3189 MessageDialog
.openError(getSite().getShell(),
3190 UIText
.StagingView_committingNotPossible
,
3191 UIText
.StagingView_noStagedFiles
);
3194 if (!commitMessageComponent
.checkCommitInfo())
3197 if (!UIUtils
.saveAllEditors(currentRepository
,
3198 UIText
.StagingView_cancelCommitAfterSaving
))
3201 String commitMessage
= commitMessageComponent
.getCommitMessage();
3202 CommitOperation commitOperation
= null;
3204 commitOperation
= new CommitOperation(currentRepository
,
3205 commitMessageComponent
.getAuthor(),
3206 commitMessageComponent
.getCommitter(),
3208 } catch (CoreException e
) {
3209 Activator
.handleError(UIText
.StagingView_commitFailed
, e
, true);
3212 if (amendPreviousCommitAction
.isChecked())
3213 commitOperation
.setAmending(true);
3214 final boolean gerritMode
= addChangeIdAction
.isChecked();
3215 commitOperation
.setComputeChangeId(gerritMode
);
3217 PushMode pushMode
= null;
3219 pushMode
= gerritMode ? PushMode
.GERRIT
: PushMode
.UPSTREAM
;
3221 final Job commitJob
= new CommitJob(currentRepository
, commitOperation
)
3222 .setOpenCommitEditor(openNewCommitsAction
.isChecked())
3223 .setPushUpstream(pushMode
);
3225 // don't allow to do anything as long as commit is in progress
3226 enableAllWidgets(false);
3227 commitJob
.addJobChangeListener(new JobChangeAdapter() {
3229 public void done(IJobChangeEvent event
) {
3230 asyncExec(new Runnable() {
3233 enableAllWidgets(true);
3234 if (commitJob
.getResult().isOK()) {
3235 commitMessageText
.setText(EMPTY_STRING
);
3242 schedule(commitJob
, true);
3244 CommitMessageHistory
.saveCommitHistory(commitMessage
);
3245 clearCommitMessageToggles();
3249 * Schedule given job in context of the current view. The view will indicate
3250 * progress as long as job is running.
3254 * @param useRepositoryRule
3255 * true to use current repository rule for the given job, false
3256 * to not enforce any rule on the job
3258 private void schedule(Job job
, boolean useRepositoryRule
) {
3259 if (useRepositoryRule
)
3260 job
.setRule(RuleUtil
.getRule(currentRepository
));
3261 IWorkbenchSiteProgressService service
= CommonUtils
.getService(getSite(), IWorkbenchSiteProgressService
.class);
3262 if (service
!= null)
3263 service
.schedule(job
, 0, true);
3268 private boolean isCommitWithoutFilesAllowed() {
3269 if (stagedViewer
.getTree().getItemCount() > 0)
3272 if (amendPreviousCommitAction
.isChecked())
3275 return CommitHelper
.isCommitWithoutFilesAllowed(currentRepository
);
3279 public void setFocus() {
3280 unstagedViewer
.getControl().setFocus();
3284 public void dispose() {
3287 ISelectionService srv
= CommonUtils
.getService(getSite(), ISelectionService
.class);
3288 srv
.removePostSelectionListener(selectionChangedListener
);
3289 CommonUtils
.getService(getSite(), IPartService
.class)
3290 .removePartListener(partListener
);
3292 if (cacheEntry
!= null) {
3293 cacheEntry
.removeIndexDiffChangedListener(myIndexDiffListener
);
3296 if (undoRedoActionGroup
!= null) {
3297 undoRedoActionGroup
.dispose();
3300 InstanceScope
.INSTANCE
.getNode(
3301 org
.eclipse
.egit
.core
.Activator
.getPluginId())
3302 .removePreferenceChangeListener(prefListener
);
3303 if (refsChangedListener
!= null) {
3304 refsChangedListener
.remove();
3307 getPreferenceStore().removePropertyChangeListener(uiPrefsListener
);
3309 getDialogSettings().put(STORE_SORT_STATE
, sortAction
.isChecked());
3311 currentRepository
= null;
3316 private boolean isDisposed() {
3320 private static void syncExec(Runnable runnable
) {
3321 PlatformUI
.getWorkbench().getDisplay().syncExec(runnable
);
3324 private void asyncExec(Runnable runnable
) {
3325 PlatformUI
.getWorkbench().getDisplay().asyncExec(runnable
);
3329 * This comparator sorts the {@link StagingEntry}s alphabetically or groups
3330 * them by state. If grouped by state the entries in the same group are also
3331 * ordered alphabetically.
3333 private static class StagingEntryComparator
extends ViewerComparator
{
3335 private boolean alphabeticSort
;
3337 private Comparator
<String
> comparator
;
3339 private boolean fileNamesFirst
;
3341 private StagingEntryComparator(boolean alphabeticSort
,
3342 boolean fileNamesFirst
) {
3343 this.alphabeticSort
= alphabeticSort
;
3344 this.setFileNamesFirst(fileNamesFirst
);
3345 comparator
= CommonUtils
.STRING_ASCENDING_COMPARATOR
;
3348 public boolean isFileNamesFirst() {
3349 return fileNamesFirst
;
3352 public void setFileNamesFirst(boolean fileNamesFirst
) {
3353 this.fileNamesFirst
= fileNamesFirst
;
3356 private void setAlphabeticSort(boolean sort
) {
3357 this.alphabeticSort
= sort
;
3360 private boolean isAlphabeticSort() {
3361 return alphabeticSort
;
3365 public int category(Object element
) {
3366 if (!isAlphabeticSort()) {
3367 StagingEntry stagingEntry
= getStagingEntry(element
);
3368 if (stagingEntry
!= null) {
3369 return getState(stagingEntry
);
3372 return super.category(element
);
3376 public int compare(Viewer viewer
, Object e1
, Object e2
) {
3377 int cat1
= category(e1
);
3378 int cat2
= category(e2
);
3384 String name1
= getStagingEntryText(e1
);
3385 String name2
= getStagingEntryText(e2
);
3387 return comparator
.compare(name1
, name2
);
3390 private String
getStagingEntryText(Object element
) {
3391 String text
= ""; //$NON-NLS-1$
3392 StagingEntry stagingEntry
= getStagingEntry(element
);
3393 if (stagingEntry
!= null) {
3394 if (isFileNamesFirst()) {
3395 text
= stagingEntry
.getName();
3397 text
= stagingEntry
.getPath();
3404 private StagingEntry
getStagingEntry(Object element
) {
3405 StagingEntry entry
= null;
3406 if (element
instanceof StagingEntry
) {
3407 entry
= (StagingEntry
) element
;
3409 if (element
instanceof TreeItem
) {
3410 TreeItem item
= (TreeItem
) element
;
3411 if (item
.getData() instanceof StagingEntry
) {
3412 entry
= (StagingEntry
) item
.getData();
3418 private int getState(StagingEntry entry
) {
3419 switch (entry
.getState()) {
3424 case MODIFIED_AND_ADDED
:
3426 case MODIFIED_AND_CHANGED
:
3434 case MISSING_AND_CHANGED
:
3441 return super.category(entry
);