Package org.eclipse.egit.ui.internal.staging

Source Code of org.eclipse.egit.ui.internal.staging.StagingView$ReplaceAction

/*******************************************************************************
* Copyright (C) 2011, 2013 Bernard Leach <leachbj@bouncycastle.org> and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.egit.ui.internal.staging;

import static org.eclipse.egit.ui.internal.CommonUtils.runCommand;

import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.egit.core.AdapterUtils;
import org.eclipse.egit.core.RepositoryUtil;
import org.eclipse.egit.core.internal.indexdiff.IndexDiffCacheEntry;
import org.eclipse.egit.core.internal.indexdiff.IndexDiffChangedListener;
import org.eclipse.egit.core.internal.indexdiff.IndexDiffData;
import org.eclipse.egit.core.op.CommitOperation;
import org.eclipse.egit.core.project.RepositoryMapping;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.CommonUtils;
import org.eclipse.egit.ui.internal.EgitUiEditorUtils;
import org.eclipse.egit.ui.internal.GitLabels;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.actions.ActionCommands;
import org.eclipse.egit.ui.internal.actions.BooleanPrefAction;
import org.eclipse.egit.ui.internal.commands.shared.AbortRebaseCommand;
import org.eclipse.egit.ui.internal.commands.shared.AbstractRebaseCommandHandler;
import org.eclipse.egit.ui.internal.commands.shared.ContinueRebaseCommand;
import org.eclipse.egit.ui.internal.commands.shared.SkipRebaseCommand;
import org.eclipse.egit.ui.internal.commit.CommitHelper;
import org.eclipse.egit.ui.internal.commit.CommitJob;
import org.eclipse.egit.ui.internal.commit.CommitMessageHistory;
import org.eclipse.egit.ui.internal.commit.CommitProposalProcessor;
import org.eclipse.egit.ui.internal.components.ToggleableWarningLabel;
import org.eclipse.egit.ui.internal.decorators.ProblemLabelDecorator;
import org.eclipse.egit.ui.internal.dialogs.CommitMessageArea;
import org.eclipse.egit.ui.internal.dialogs.CommitMessageComponent;
import org.eclipse.egit.ui.internal.dialogs.CommitMessageComponentState;
import org.eclipse.egit.ui.internal.dialogs.CommitMessageComponentStateManager;
import org.eclipse.egit.ui.internal.dialogs.ICommitMessageComponentNotifications;
import org.eclipse.egit.ui.internal.dialogs.SpellcheckableMessageArea;
import org.eclipse.egit.ui.internal.gerrit.GerritUtil;
import org.eclipse.egit.ui.internal.operations.DeletePathsOperationUI;
import org.eclipse.egit.ui.internal.operations.IgnoreOperationUI;
import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ControlContribution;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.preference.IPersistentPreferenceStore;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ContentViewer;
import org.eclipse.jface.viewers.DecoratingLabelProvider;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ResetCommand;
import org.eclipse.jgit.api.RmCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.events.ListenerHandle;
import org.eclipse.jgit.events.RefsChangedEvent;
import org.eclipse.jgit.events.RefsChangedListener;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.Form;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.operations.UndoRedoActionGroup;
import org.eclipse.ui.part.IShowInSource;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;

/**
* A GitX style staging view with embedded commit dialog.
*/
public class StagingView extends ViewPart implements IShowInSource {

  /**
   * Staging view id
   */
  public static final String VIEW_ID = "org.eclipse.egit.ui.StagingView"; //$NON-NLS-1$

  private static final String EMPTY_STRING = ""; //$NON-NLS-1$

  private FormToolkit toolkit;

  private Form form;

  private Section stagedSection;

  private Section unstagedSection;

  private Section commitMessageSection;

  private TreeViewer stagedViewer;

  private TreeViewer unstagedViewer;

  private ToggleableWarningLabel warningLabel;

  private Text filterText;

  private SpellcheckableMessageArea commitMessageText;

  private Text committerText;

  private Text authorText;

  private CommitMessageComponent commitMessageComponent;

  private boolean reactOnSelection = true;

  private ISelectionListener selectionChangedListener;

  private ToolBarManager unstagedToolBarManager;

  private ToolBarManager stagedToolBarManager;

  private Action listPresentationAction;

  private Action treePresentationAction;

  private Action compactTreePresentationAction;

  private Action unstagedExpandAllAction;

  private Action unstagedCollapseAllAction;

  private Action stagedExpandAllAction;

  private Action stagedCollapseAllAction;

  private Repository currentRepository;

  private Presentation presentation = Presentation.LIST;

  private Set<IPath> pathsToExpandInStaged = new HashSet<IPath>();

  private Set<IPath> pathsToExpandInUnstaged = new HashSet<IPath>();

  /**
   * Presentation mode of the staged/unstaged files.
   */
  public enum Presentation {
    /** Show files in flat list */
    LIST,
    /** Show folder structure in full tree */
    TREE,
    /**
     * Show folder structure in compact tree (folders with only one child
     * are folded into parent)
     */
    COMPACT_TREE;
  }

  static class StagingViewUpdate {
    Repository repository;
    IndexDiffData indexDiff;
    Collection<String> changedResources;

    StagingViewUpdate(Repository theRepository,
        IndexDiffData theIndexDiff, Collection<String> theChanges) {
      this.repository = theRepository;
      this.indexDiff = theIndexDiff;
      this.changedResources = theChanges;
    }
  }

  static class StagingDragListener extends DragSourceAdapter {

    private ISelectionProvider provider;

    public StagingDragListener(ISelectionProvider provider) {
      this.provider = provider;
    }

    public void dragStart(DragSourceEvent event) {
      event.doit = !provider.getSelection().isEmpty();
    }

    public void dragFinished(DragSourceEvent event) {
      if (LocalSelectionTransfer.getTransfer().isSupportedType(
          event.dataType))
        LocalSelectionTransfer.getTransfer().setSelection(null);
    }

    public void dragSetData(DragSourceEvent event) {
      IStructuredSelection selection = (IStructuredSelection) provider
          .getSelection();
      if (selection.isEmpty())
        return;

      if (LocalSelectionTransfer.getTransfer().isSupportedType(
          event.dataType)) {
        LocalSelectionTransfer.getTransfer().setSelection(selection);
        return;
      }

      if (FileTransfer.getInstance().isSupportedType(event.dataType)) {
        List<String> files = new ArrayList<String>();
        for (Object selected : selection.toList())
          if (selected instanceof StagingEntry) {
            StagingEntry entry = (StagingEntry) selected;
            File file = new File(
                entry.getRepository().getWorkTree(),
                entry.getPath());
            if (file.exists())
              files.add(file.getAbsolutePath());
          }
        if (!files.isEmpty()) {
          event.data = files.toArray(new String[files.size()]);
          return;
        }
      }
    }
  }

  static class TreeDecoratingLabelProvider extends DecoratingLabelProvider {

    ILabelProvider provider;

    ILabelDecorator decorator;

    public TreeDecoratingLabelProvider(ILabelProvider provider,
        ILabelDecorator decorator) {
      super(provider, decorator);
      this.provider = provider;
      this.decorator = decorator;
    }

    public Image getColumnImage(Object element) {
      Image image = provider.getImage(element);
      if (image != null && decorator != null) {
        Image decorated = decorator.decorateImage(image, element);
        if (decorated != null)
          return decorated;
      }
      return image;
    }

    public String getText(Object element) {
      return provider.getText(element);
    }
  }

  static class StagingViewSearchThread extends Thread {
    private StagingView stagingView;

    private static final Object lock = new Object();

    private volatile static int globalThreadIndex = 0;

    private int currentThreadIx;

    public StagingViewSearchThread(StagingView stagingView) {
      super("staging_view_filter_thread" + ++globalThreadIndex); //$NON-NLS-1$
      this.stagingView = stagingView;
      currentThreadIx = globalThreadIndex;
    }

    public void run() {
      synchronized (lock) {
        if (currentThreadIx < globalThreadIndex)
          return;
        stagingView.refreshViewersPreservingExpandedElements();
      }
    }

  }

  private final IPreferenceChangeListener prefListener = new IPreferenceChangeListener() {

    public void preferenceChange(PreferenceChangeEvent event) {
      if (!RepositoryUtil.PREFS_DIRECTORIES.equals(event.getKey()))
        return;

      final Repository repo = currentRepository;
      if (repo == null)
        return;

      if (Activator.getDefault().getRepositoryUtil().contains(repo))
        return;

      reload(null);
    }

  };

  private Action signedOffByAction;

  private Action addChangeIdAction;

  private Action amendPreviousCommitAction;

  private Action openNewCommitsAction;

  private Action columnLayoutAction;

  private Action fileNameModeAction;

  private Action refreshAction;

  private SashForm stagingSashForm;

  private IndexDiffChangedListener myIndexDiffListener = new IndexDiffChangedListener() {
    public void indexDiffChanged(Repository repository,
        IndexDiffData indexDiffData) {
      reload(repository);
    }
  };

  private IndexDiffCacheEntry cacheEntry;

  private UndoRedoActionGroup undoRedoActionGroup;

  private Button commitButton;

  private Button commitAndPushButton;

  private Section rebaseSection;

  private Button rebaseContinueButton;

  private Button rebaseSkipButton;

  private Button rebaseAbortButton;

  private ListenerHandle refsChangedListener;

  private LocalResourceManager resources = new LocalResourceManager(
      JFaceResources.getResources());

  private boolean disposed;

  private Image getImage(ImageDescriptor descriptor) {
    return (Image) this.resources.get(descriptor);
  }

  @Override
  public void createPartControl(Composite parent) {
    GridLayoutFactory.fillDefaults().applyTo(parent);

    toolkit = new FormToolkit(parent.getDisplay());
    parent.addDisposeListener(new DisposeListener() {

      public void widgetDisposed(DisposeEvent e) {
        if (!commitMessageComponent.isAmending()
            && userEnteredCommitMessage())
          saveCommitMessageComponentState();
        else
          deleteCommitMessageComponentState();
        resources.dispose();
        toolkit.dispose();
      }
    });

    form = toolkit.createForm(parent);

    form.setImage(getImage(UIIcons.REPOSITORY));
    form.setText(UIText.StagingView_NoSelectionTitle);
    GridDataFactory.fillDefaults().grab(true, true).applyTo(form);
    toolkit.decorateFormHeading(form);
    GridLayoutFactory.swtDefaults().applyTo(form.getBody());

    SashForm horizontalSashForm = new SashForm(form.getBody(), SWT.NONE);
    toolkit.adapt(horizontalSashForm, true, true);
    GridDataFactory.fillDefaults().grab(true, true)
        .applyTo(horizontalSashForm);

    stagingSashForm = new SashForm(horizontalSashForm,
        getStagingFormOrientation());
    toolkit.adapt(stagingSashForm, true, true);
    GridDataFactory.fillDefaults().grab(true, true)
        .applyTo(stagingSashForm);

    unstagedSection = toolkit.createSection(stagingSashForm,
        ExpandableComposite.TITLE_BAR);

    createUnstagedToolBarComposite();

    Composite unstagedComposite = toolkit
        .createComposite(unstagedSection);
    toolkit.paintBordersFor(unstagedComposite);
    unstagedSection.setClient(unstagedComposite);
    GridLayoutFactory.fillDefaults().extendedMargins(2, 2, 2, 2)
        .applyTo(unstagedComposite);

    unstagedViewer = createTree(unstagedComposite);
    GridDataFactory.fillDefaults().grab(true, true)
        .applyTo(unstagedViewer.getControl());
    unstagedViewer.getTree().setData(FormToolkit.KEY_DRAW_BORDER,
        FormToolkit.TREE_BORDER);
    unstagedViewer.setLabelProvider(createLabelProvider(unstagedViewer));
    unstagedViewer.setContentProvider(createStagingContentProvider(true));
    unstagedViewer.addDragSupport(DND.DROP_MOVE | DND.DROP_COPY
        | DND.DROP_LINK,
        new Transfer[] { LocalSelectionTransfer.getTransfer(),
            FileTransfer.getInstance() }, new StagingDragListener(
            unstagedViewer));
    unstagedViewer.addDropSupport(DND.DROP_MOVE,
        new Transfer[] { LocalSelectionTransfer.getTransfer() },
        new DropTargetAdapter() {
          public void drop(DropTargetEvent event) {
            // Bug 411466: It is very important that detail is set
            // to DND.DROP_COPY. If it was left as DND.DROP_MOVE and
            // the drag comes from the Navigator view, the code in
            // NavigatorDragAdapter would delete the resources.
            event.detail = DND.DROP_COPY;
            if (event.data instanceof IStructuredSelection) {
              final IStructuredSelection selection = (IStructuredSelection) event.data;
              unstage(selection);
            }
          }

          public void dragOver(DropTargetEvent event) {
            event.detail = DND.DROP_MOVE;
          }
        });
    unstagedViewer.addOpenListener(new IOpenListener() {
      public void open(OpenEvent event) {
        compareWith(event);
      }
    });
    enableAutoExpand(unstagedViewer);
    addListenerToDisableAutoExpandOnCollapse(unstagedViewer);

    Composite rebaseAndCommitComposite = toolkit.createComposite(horizontalSashForm);
    rebaseAndCommitComposite.setLayout(GridLayoutFactory.fillDefaults().create());

    rebaseSection = toolkit.createSection(rebaseAndCommitComposite,
        ExpandableComposite.TITLE_BAR);
    rebaseSection.setText(UIText.StagingView_RebaseLabel);

    Composite rebaseComposite = toolkit.createComposite(rebaseSection);
    toolkit.paintBordersFor(rebaseComposite);
    rebaseSection.setClient(rebaseComposite);

    rebaseSection.setLayoutData(GridDataFactory.fillDefaults().create());
    rebaseComposite.setLayout(GridLayoutFactory.fillDefaults()
        .numColumns(3).equalWidth(true).create());
    GridDataFactory buttonGridData = GridDataFactory.fillDefaults().align(
        SWT.FILL, SWT.CENTER);

    this.rebaseAbortButton = toolkit.createButton(rebaseComposite,
        UIText.StagingView_RebaseAbort, SWT.PUSH);
    rebaseAbortButton.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        rebaseAbort();
      }
    });
    rebaseAbortButton.setImage(getImage(UIIcons.REBASE_ABORT));
    buttonGridData.applyTo(rebaseAbortButton);

    this.rebaseSkipButton = toolkit.createButton(rebaseComposite,
        UIText.StagingView_RebaseSkip, SWT.PUSH);
    rebaseSkipButton.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        rebaseSkip();
      }
    });
    rebaseSkipButton.setImage(getImage(UIIcons.REBASE_SKIP));
    buttonGridData.applyTo(rebaseSkipButton);

    this.rebaseContinueButton = toolkit.createButton(rebaseComposite,
        UIText.StagingView_RebaseContinue, SWT.PUSH);
    rebaseContinueButton.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        rebaseContinue();
      }
    });
    rebaseContinueButton.setImage(getImage(UIIcons.REBASE_CONTINUE));
    buttonGridData.applyTo(rebaseContinueButton);

    showControl(rebaseSection, false);

    commitMessageSection = toolkit.createSection(rebaseAndCommitComposite,
        ExpandableComposite.TITLE_BAR);
    commitMessageSection.setText(UIText.StagingView_CommitMessage);
    commitMessageSection.setLayoutData(GridDataFactory.fillDefaults()
        .grab(true, true).create());

    Composite commitMessageToolbarComposite = toolkit
        .createComposite(commitMessageSection);
    commitMessageToolbarComposite.setBackground(null);
    commitMessageToolbarComposite.setLayout(createRowLayoutWithoutMargin());
    commitMessageSection.setTextClient(commitMessageToolbarComposite);
    ToolBarManager commitMessageToolBarManager = new ToolBarManager(
        SWT.FLAT | SWT.HORIZONTAL);

    amendPreviousCommitAction = new Action(
        UIText.StagingView_Ammend_Previous_Commit, IAction.AS_CHECK_BOX) {

      public void run() {
        commitMessageComponent.setAmendingButtonSelection(isChecked());
        updateMessage();
      }
    };
    amendPreviousCommitAction.setImageDescriptor(UIIcons.AMEND_COMMIT);
    commitMessageToolBarManager.add(amendPreviousCommitAction);

    signedOffByAction = new Action(UIText.StagingView_Add_Signed_Off_By,
        IAction.AS_CHECK_BOX) {

      public void run() {
        commitMessageComponent.setSignedOffButtonSelection(isChecked());
      }
    };
    signedOffByAction.setImageDescriptor(UIIcons.SIGNED_OFF);
    commitMessageToolBarManager.add(signedOffByAction);

    addChangeIdAction = new Action(UIText.StagingView_Add_Change_ID,
        IAction.AS_CHECK_BOX) {

      public void run() {
        commitMessageComponent.setChangeIdButtonSelection(isChecked());
      }
    };
    addChangeIdAction.setImageDescriptor(UIIcons.GERRIT);
    commitMessageToolBarManager.add(addChangeIdAction);

    commitMessageToolBarManager
        .createControl(commitMessageToolbarComposite);

    Composite commitMessageComposite = toolkit
        .createComposite(commitMessageSection);
    commitMessageSection.setClient(commitMessageComposite);
    GridLayoutFactory.fillDefaults().numColumns(1)
        .applyTo(commitMessageComposite);

    warningLabel = new ToggleableWarningLabel(commitMessageComposite,
        SWT.NONE);
    GridDataFactory.fillDefaults().grab(true, false).exclude(true)
        .applyTo(warningLabel);

    Composite commitMessageTextComposite = toolkit
        .createComposite(commitMessageComposite);
    toolkit.paintBordersFor(commitMessageTextComposite);
    GridDataFactory.fillDefaults().grab(true, true)
        .applyTo(commitMessageTextComposite);
    GridLayoutFactory.fillDefaults().numColumns(1)
        .extendedMargins(2, 2, 2, 2)
        .applyTo(commitMessageTextComposite);

    final CommitProposalProcessor commitProposalProcessor = new CommitProposalProcessor() {
      @Override
      protected Collection<String> computeFileNameProposals() {
        return getStagedFileNames();
      }

      @Override
      protected Collection<String> computeMessageProposals() {
        return CommitMessageHistory.getCommitHistory();
      }
    };
    commitMessageText = new CommitMessageArea(commitMessageTextComposite,
        EMPTY_STRING, toolkit.getBorderStyle()) {
      @Override
      protected CommitProposalProcessor getCommitProposalProcessor() {
        return commitProposalProcessor;
      }
      @Override
      protected IHandlerService getHandlerService() {
        return CommonUtils.getService(getSite(), IHandlerService.class);
      }
    };
    commitMessageText.setData(FormToolkit.KEY_DRAW_BORDER,
        FormToolkit.TEXT_BORDER);
    GridDataFactory.fillDefaults().grab(true, true)
        .applyTo(commitMessageText);
    UIUtils.addBulbDecorator(commitMessageText.getTextWidget(),
        UIText.CommitDialog_ContentAssist);

    Composite composite = toolkit.createComposite(commitMessageComposite);
    toolkit.paintBordersFor(composite);
    GridDataFactory.fillDefaults().grab(true, false).applyTo(composite);
    GridLayoutFactory.swtDefaults().numColumns(2).applyTo(composite);

    toolkit.createLabel(composite, UIText.StagingView_Author)
        .setForeground(
            toolkit.getColors().getColor(IFormColors.TB_TOGGLE));
    authorText = toolkit.createText(composite, null);
    authorText
        .setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
    authorText.setLayoutData(GridDataFactory.fillDefaults()
        .grab(true, false).create());

    toolkit.createLabel(composite, UIText.StagingView_Committer)
        .setForeground(
            toolkit.getColors().getColor(IFormColors.TB_TOGGLE));
    committerText = toolkit.createText(composite, null);
    committerText.setData(FormToolkit.KEY_DRAW_BORDER,
        FormToolkit.TEXT_BORDER);
    committerText.setLayoutData(GridDataFactory.fillDefaults()
        .grab(true, false).create());

    Composite buttonsContainer = toolkit.createComposite(composite);
    GridDataFactory.fillDefaults().grab(true, false).span(2, 1)
        .indent(0, 8).applyTo(buttonsContainer);
    GridLayoutFactory.fillDefaults().numColumns(2)
        .applyTo(buttonsContainer);

    Label filler = toolkit.createLabel(buttonsContainer, ""); //$NON-NLS-1$
    GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL)
        .grab(true, true).applyTo(filler);

    Composite commitButtonsContainer = toolkit
        .createComposite(buttonsContainer);
    GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
        .applyTo(commitButtonsContainer);
    GridLayoutFactory.fillDefaults().numColumns(2).equalWidth(true)
        .applyTo(commitButtonsContainer);

    this.commitAndPushButton = toolkit.createButton(commitButtonsContainer,
        UIText.StagingView_CommitAndPush, SWT.PUSH);
    commitAndPushButton.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        commit(true);
      }
    });
    GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
        .applyTo(commitAndPushButton);

    this.commitButton = toolkit.createButton(commitButtonsContainer,
        UIText.StagingView_Commit, SWT.PUSH);
    commitButton.setImage(getImage(UIIcons.COMMIT));
    commitButton.setText(UIText.StagingView_Commit);
    commitButton.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        commit(false);
      }
    });
    GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
        .applyTo(commitButton);

    stagedSection = toolkit.createSection(stagingSashForm,
        ExpandableComposite.TITLE_BAR);

    createStagedToolBarComposite();

    Composite stagedComposite = toolkit.createComposite(stagedSection);
    toolkit.paintBordersFor(stagedComposite);
    stagedSection.setClient(stagedComposite);
    GridLayoutFactory.fillDefaults().extendedMargins(2, 2, 2, 2)
        .applyTo(stagedComposite);

    stagedViewer = createTree(stagedComposite);
    GridDataFactory.fillDefaults().grab(true, true)
        .applyTo(stagedViewer.getControl());
    stagedViewer.getTree().setData(FormToolkit.KEY_DRAW_BORDER,
        FormToolkit.TREE_BORDER);
    stagedViewer.setLabelProvider(createLabelProvider(stagedViewer));
    stagedViewer.setContentProvider(createStagingContentProvider(false));
    stagedViewer.addDragSupport(
        DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK,
        new Transfer[] { LocalSelectionTransfer.getTransfer(),
            FileTransfer.getInstance() }, new StagingDragListener(
            stagedViewer));
    stagedViewer.addDropSupport(DND.DROP_MOVE,
        new Transfer[] { LocalSelectionTransfer.getTransfer() },
        new DropTargetAdapter() {
          public void drop(DropTargetEvent event) {
            // Bug 411466: It is very important that detail is set
            // to DND.DROP_COPY. If it was left as DND.DROP_MOVE and
            // the drag comes from the Navigator view, the code in
            // NavigatorDragAdapter would delete the resources.
            event.detail = DND.DROP_COPY;
            if (event.data instanceof IStructuredSelection) {
              final IStructuredSelection selection = (IStructuredSelection) event.data;
              stage(selection);
            }
          }

          public void dragOver(DropTargetEvent event) {
            event.detail = DND.DROP_MOVE;
          }
        });
    stagedViewer.addOpenListener(new IOpenListener() {
      public void open(OpenEvent event) {
        compareWith(event);
      }
    });
    enableAutoExpand(stagedViewer);
    addListenerToDisableAutoExpandOnCollapse(stagedViewer);

    selectionChangedListener = new ISelectionListener() {
      public void selectionChanged(IWorkbenchPart part,
          ISelection selection) {
        if (!reactOnSelection || part == getSite().getPart())
          return;

        // this may happen if we switch between editors
        if (part instanceof IEditorPart) {
          IEditorInput input = ((IEditorPart) part).getEditorInput();
          if (input instanceof IFileEditorInput)
            reactOnSelection(new StructuredSelection(
                ((IFileEditorInput) input).getFile()));
        } else
          reactOnSelection(selection);
      }
    };

    IPreferenceStore preferenceStore = getPreferenceStore();
    if (preferenceStore.contains(UIPreferences.STAGING_VIEW_SYNC_SELECTION))
      reactOnSelection = preferenceStore.getBoolean(
          UIPreferences.STAGING_VIEW_SYNC_SELECTION);
    else
      preferenceStore.setDefault(UIPreferences.STAGING_VIEW_SYNC_SELECTION, true);

    InstanceScope.INSTANCE.getNode(
        org.eclipse.egit.core.Activator.getPluginId())
        .addPreferenceChangeListener(prefListener);

    updateSectionText();
    updateToolbar();
    enableCommitWidgets(false);
    refreshAction.setEnabled(false);

    createPopupMenu(unstagedViewer);
    createPopupMenu(stagedViewer);

    final ICommitMessageComponentNotifications listener = new ICommitMessageComponentNotifications() {

      public void updateSignedOffToggleSelection(boolean selection) {
        signedOffByAction.setChecked(selection);
      }

      public void updateChangeIdToggleSelection(boolean selection) {
        addChangeIdAction.setChecked(selection);
      }

      public void statusUpdated() {
        updateMessage();
      }
    };
    commitMessageComponent = new CommitMessageComponent(listener);
    commitMessageComponent.attachControls(commitMessageText, authorText,
        committerText);

    // allow to commit with ctrl-enter
    commitMessageText.getTextWidget().addVerifyKeyListener(new VerifyKeyListener() {
      public void verifyKey(VerifyEvent event) {
        if (UIUtils.isSubmitKeyEvent(event)) {
          event.doit = false;
          commit(false);
        }
      }
    });

    commitMessageText.getTextWidget().addFocusListener(new FocusListener() {
      public void focusGained(FocusEvent e) {
        // Ctrl+Enter shortcut only works when the focus is on the commit message text
        String commitButtonTooltip = MessageFormat.format(
            UIText.StagingView_CommitToolTip,
            UIUtils.SUBMIT_KEY_STROKE.format());
        commitButton.setToolTipText(commitButtonTooltip);
      }

      public void focusLost(FocusEvent e) {
        commitButton.setToolTipText(null);
      }
    });

    // react on selection changes
    IWorkbenchPartSite site = getSite();
    ISelectionService srv = CommonUtils.getService(site, ISelectionService.class);
    srv.addPostSelectionListener(selectionChangedListener);

    // Use current selection to populate staging view
    UIUtils.notifySelectionChangedWithCurrentSelection(
        selectionChangedListener, site);

    site.setSelectionProvider(unstagedViewer);

    ViewerFilter filter = new ViewerFilter() {
      @Override
      public boolean select(Viewer viewer, Object parentElement,
          Object element) {
        StagingViewContentProvider contentProvider = getContentProvider((TreeViewer) viewer);
        if (element instanceof StagingEntry)
          return contentProvider.isInFilter((StagingEntry) element);
        else if (element instanceof StagingFolderEntry)
          return contentProvider
              .hasVisibleChildren((StagingFolderEntry) element);
        return true;
      }
    };
    unstagedViewer.addFilter(filter);
    stagedViewer.addFilter(filter);
  }

  private void executeRebaseOperation(AbstractRebaseCommandHandler command) {
    try {
      command.execute(currentRepository);
    } catch (ExecutionException e) {
      Activator.showError(e.getMessage(), e);
    }
  }

  /**
   * Abort rebase command in progress
   */
  protected void rebaseAbort() {
    AbortRebaseCommand abortCommand = new AbortRebaseCommand();
    executeRebaseOperation(abortCommand);
  }

  /**
   * Rebase next commit and continue rebase in progress
   */
  protected void rebaseSkip() {
    SkipRebaseCommand skipCommand = new SkipRebaseCommand();
    executeRebaseOperation(skipCommand);
  }

  /**
   * Continue rebase command in progress
   */
  protected void rebaseContinue() {
    ContinueRebaseCommand continueCommand = new ContinueRebaseCommand();
    executeRebaseOperation(continueCommand);
  }

  private void createUnstagedToolBarComposite() {
    Composite unstagedToolbarComposite = toolkit
        .createComposite(unstagedSection);
    unstagedToolbarComposite.setBackground(null);
    unstagedToolbarComposite.setLayout(createRowLayoutWithoutMargin());
    unstagedSection.setTextClient(unstagedToolbarComposite);
    unstagedExpandAllAction = new Action(UIText.UIUtils_ExpandAll,
        IAction.AS_PUSH_BUTTON) {
      public void run() {
        unstagedViewer.expandAll();
        enableAutoExpand(unstagedViewer);
      }
    };
    unstagedExpandAllAction.setImageDescriptor(UIIcons.EXPAND_ALL);

    unstagedCollapseAllAction = new Action(UIText.UIUtils_CollapseAll,
        IAction.AS_PUSH_BUTTON) {
      public void run() {
        unstagedViewer.collapseAll();
        disableAutoExpand(unstagedViewer);
      }
    };
    unstagedCollapseAllAction.setImageDescriptor(UIIcons.COLLAPSEALL);

    unstagedToolBarManager = new ToolBarManager(SWT.FLAT | SWT.HORIZONTAL);

    unstagedToolBarManager.add(unstagedExpandAllAction);
    unstagedToolBarManager.add(unstagedCollapseAllAction);

    unstagedToolBarManager.update(true);
    unstagedToolBarManager.createControl(unstagedToolbarComposite);
  }

  private void createStagedToolBarComposite() {
    Composite stagedToolbarComposite = toolkit
        .createComposite(stagedSection);
    stagedToolbarComposite.setBackground(null);
    stagedToolbarComposite.setLayout(createRowLayoutWithoutMargin());
    stagedSection.setTextClient(stagedToolbarComposite);
    stagedExpandAllAction = new Action(UIText.UIUtils_ExpandAll,
        IAction.AS_PUSH_BUTTON) {
      public void run() {
        stagedViewer.expandAll();
        enableAutoExpand(stagedViewer);
      }
    };
    stagedExpandAllAction.setImageDescriptor(UIIcons.EXPAND_ALL);

    stagedCollapseAllAction = new Action(UIText.UIUtils_CollapseAll,
        IAction.AS_PUSH_BUTTON) {
      public void run() {
        stagedViewer.collapseAll();
        disableAutoExpand(stagedViewer);
      }
    };
    stagedCollapseAllAction.setImageDescriptor(UIIcons.COLLAPSEALL);

    stagedToolBarManager = new ToolBarManager(SWT.FLAT | SWT.HORIZONTAL);

    stagedToolBarManager.add(stagedExpandAllAction);
    stagedToolBarManager.add(stagedCollapseAllAction);
    stagedToolBarManager.update(true);
    stagedToolBarManager.createControl(stagedToolbarComposite);
  }

  private static RowLayout createRowLayoutWithoutMargin() {
    RowLayout layout = new RowLayout();
    layout.marginHeight = 0;
    layout.marginWidth = 0;
    layout.marginTop = 0;
    layout.marginBottom = 0;
    layout.marginLeft = 0;
    layout.marginRight = 0;
    return layout;
  }

  private static void addListenerToDisableAutoExpandOnCollapse(
      TreeViewer treeViewer) {
    treeViewer.addTreeListener(new ITreeViewerListener() {
      public void treeCollapsed(TreeExpansionEvent event) {
        disableAutoExpand(event.getTreeViewer());
      }

      public void treeExpanded(TreeExpansionEvent event) {
        // Nothing to do
      }
    });
  }

  private static void enableAutoExpand(AbstractTreeViewer treeViewer) {
    treeViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
  }

  private static void disableAutoExpand(AbstractTreeViewer treeViewer) {
    treeViewer.setAutoExpandLevel(0);
  }

  /**
   * @return selected repository
   */
  public Repository getCurrentRepository() {
    return currentRepository;
  }

  public ShowInContext getShowInContext() {
    if (stagedViewer != null && stagedViewer.getTree().isFocusControl())
      return getShowInContext(stagedViewer);
    else if (unstagedViewer != null
        && unstagedViewer.getTree().isFocusControl())
      return getShowInContext(unstagedViewer);
    else
      return null;
  }

  private ShowInContext getShowInContext(TreeViewer treeViewer) {
    IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection();
    List<Object> elements = new ArrayList<Object>();
    for (Object selectedElement : selection.toList()) {
      if (selectedElement instanceof StagingEntry) {
        StagingEntry entry = (StagingEntry) selectedElement;
        IFile file = entry.getFile();
        if (file != null)
          elements.add(file);
        else
          elements.add(entry.getLocation());
      } else if (selectedElement instanceof StagingFolderEntry) {
        StagingFolderEntry entry = (StagingFolderEntry) selectedElement;
        IContainer container = entry.getContainer();
        if (container != null)
          elements.add(container);
        else
          elements.add(entry.getLocation());
      }
    }
    return new ShowInContext(null, new StructuredSelection(elements));
  }

  private int getStagingFormOrientation() {
    boolean columnLayout = Activator.getDefault().getPreferenceStore()
        .getBoolean(UIPreferences.STAGING_VIEW_COLUMN_LAYOUT);
    if (columnLayout)
      return SWT.HORIZONTAL;
    else
      return SWT.VERTICAL;
  }

  private void enableAllWidgets(boolean enabled) {
    if (isDisposed())
      return;
    enableCommitWidgets(enabled);
    enableStagingWidgets(enabled);
  }

  private void enableStagingWidgets(boolean enabled) {
    if (isDisposed())
      return;
    unstagedViewer.getControl().setEnabled(enabled);
    stagedViewer.getControl().setEnabled(enabled);
  }

  private void enableCommitWidgets(boolean enabled) {
    if (isDisposed())
      return;

    commitMessageText.setEnabled(enabled);
    committerText.setEnabled(enabled);
    enableAuthorText(enabled);
    amendPreviousCommitAction.setEnabled(enabled);
    signedOffByAction.setEnabled(enabled);
    addChangeIdAction.setEnabled(enabled);
    commitButton.setEnabled(enabled);
    commitAndPushButton.setEnabled(enabled);

    if (!enabled) {
      commitMessageText.setText(""); //$NON-NLS-1$
      committerText.setText(""); //$NON-NLS-1$
      authorText.setText(""); //$NON-NLS-1$
    }
  }

  private void enableAuthorText(boolean enabled) {
    if (currentRepository != null
        && currentRepository.getRepositoryState().equals(
        RepositoryState.CHERRY_PICKING_RESOLVED))
      authorText.setEnabled(false);
    else
      authorText.setEnabled(enabled);
  }

  private void updateToolbar() {

    ControlContribution controlContribution = new ControlContribution(
        "StagingView.searchText") { //$NON-NLS-1$
      @Override
      protected Control createControl(Composite parent) {
        Composite toolbarComposite = toolkit.createComposite(parent,
            SWT.NONE);
        toolbarComposite.setBackground(null);
        GridLayout headLayout = new GridLayout();
        headLayout.numColumns = 2;
        headLayout.marginHeight = 0;
        headLayout.marginWidth = 0;
        headLayout.marginTop = 0;
        headLayout.marginBottom = 0;
        headLayout.marginLeft = 0;
        headLayout.marginRight = 0;
        toolbarComposite.setLayout(headLayout);

        filterText = new Text(toolbarComposite, SWT.SEARCH
            | SWT.ICON_CANCEL | SWT.ICON_SEARCH);
        filterText.setMessage(UIText.StagingView_Find);
        GridData data = new GridData(GridData.FILL_HORIZONTAL);
        data.widthHint = 150;
        filterText.setLayoutData(data);
        final Display display = Display.getCurrent();
        filterText.addModifyListener(new ModifyListener() {
          public void modifyText(ModifyEvent e) {
            final StagingViewSearchThread searchThread = new StagingViewSearchThread(
                StagingView.this);
            display.timerExec(200, new Runnable() {
              public void run() {
                searchThread.start();
              }
            });
          }
        });
        return toolbarComposite;
      }
    };

    IActionBars actionBars = getViewSite().getActionBars();
    IToolBarManager toolbar = actionBars.getToolBarManager();

    toolbar.add(controlContribution);

    refreshAction = new Action(UIText.StagingView_Refresh, IAction.AS_PUSH_BUTTON) {
      public void run() {
        if(cacheEntry != null)
          cacheEntry.refreshResourcesAndIndexDiff();
      }
    };
    refreshAction.setImageDescriptor(UIIcons.ELCL16_REFRESH);
    toolbar.add(refreshAction);

    // link with selection
    Action linkSelectionAction = new BooleanPrefAction(
        (IPersistentPreferenceStore) getPreferenceStore(),
        UIPreferences.STAGING_VIEW_SYNC_SELECTION,
        UIText.StagingView_LinkSelection) {
      @Override
      public void apply(boolean value) {
        reactOnSelection = value;
      }
    };
    linkSelectionAction.setImageDescriptor(UIIcons.ELCL16_SYNCED);
    toolbar.add(linkSelectionAction);

    toolbar.add(new Separator());

    openNewCommitsAction = new Action(UIText.StagingView_OpenNewCommits,
        IAction.AS_CHECK_BOX) {

      public void run() {
        getPreferenceStore().setValue(
            UIPreferences.STAGING_VIEW_SHOW_NEW_COMMITS, isChecked());
      }
    };
    openNewCommitsAction.setChecked(getPreferenceStore().getBoolean(
        UIPreferences.STAGING_VIEW_SHOW_NEW_COMMITS));

    columnLayoutAction = new Action(UIText.StagingView_ColumnLayout,
        IAction.AS_CHECK_BOX) {

      public void run() {
        getPreferenceStore().setValue(
            UIPreferences.STAGING_VIEW_COLUMN_LAYOUT, isChecked());
        stagingSashForm.setOrientation(isChecked() ? SWT.HORIZONTAL
            : SWT.VERTICAL);
      }
    };
    columnLayoutAction.setChecked(getPreferenceStore().getBoolean(
        UIPreferences.STAGING_VIEW_COLUMN_LAYOUT));

    fileNameModeAction = new Action(UIText.StagingView_ShowFileNamesFirst,
        IAction.AS_CHECK_BOX) {

      public void run() {
        final boolean enable = isChecked();
        getLabelProvider(stagedViewer).setFileNameMode(enable);
        getLabelProvider(unstagedViewer).setFileNameMode(enable);
        getContentProvider(stagedViewer).setFileNameMode(enable);
        getContentProvider(unstagedViewer).setFileNameMode(enable);
        refreshViewersPreservingExpandedElements();
        getPreferenceStore().setValue(
            UIPreferences.STAGING_VIEW_FILENAME_MODE, enable);
      }
    };
    fileNameModeAction.setChecked(getPreferenceStore().getBoolean(
        UIPreferences.STAGING_VIEW_FILENAME_MODE));

    IMenuManager dropdownMenu = actionBars.getMenuManager();
    MenuManager presentationMenu = new MenuManager(
        UIText.StagingView_Presentation);
    listPresentationAction = new Action(UIText.StagingView_List,
        IAction.AS_RADIO_BUTTON) {
      public void run() {
        if (!isChecked())
          return;
        presentation = Presentation.LIST;
        getPreferenceStore().setValue(
            UIPreferences.STAGING_VIEW_PRESENTATION,
            Presentation.LIST.name());
        treePresentationAction.setChecked(false);
        compactTreePresentationAction.setChecked(false);
        setExpandCollapseActionsVisible(false);
        refreshViewers();
      }
    };
    listPresentationAction.setImageDescriptor(UIIcons.FLAT);
    presentationMenu.add(listPresentationAction);

    treePresentationAction = new Action(UIText.StagingView_Tree,
        IAction.AS_RADIO_BUTTON) {
      public void run() {
        if (!isChecked())
          return;
        presentation = Presentation.TREE;
        getPreferenceStore().setValue(
            UIPreferences.STAGING_VIEW_PRESENTATION,
            Presentation.TREE.name());
        listPresentationAction.setChecked(false);
        compactTreePresentationAction.setChecked(false);
        setExpandCollapseActionsVisible(true);
        refreshViewers();
      }
    };
    treePresentationAction.setImageDescriptor(UIIcons.HIERARCHY);
    presentationMenu.add(treePresentationAction);

    compactTreePresentationAction = new Action(UIText.StagingView_CompactTree,
        IAction.AS_RADIO_BUTTON) {
      public void run() {
        if (!isChecked())
          return;
        presentation = Presentation.COMPACT_TREE;
        getPreferenceStore().setValue(
            UIPreferences.STAGING_VIEW_PRESENTATION,
            Presentation.COMPACT_TREE.name());
        listPresentationAction.setChecked(false);
        treePresentationAction.setChecked(false);
        setExpandCollapseActionsVisible(true);
        refreshViewers();
      }
    };
    compactTreePresentationAction.setImageDescriptor(UIIcons.COMPACT);
    presentationMenu.add(compactTreePresentationAction);

    String presentationString = getPreferenceStore().getString(
        UIPreferences.STAGING_VIEW_PRESENTATION);
    if (presentationString.length() > 0) {
      try {
        presentation = Presentation.valueOf(presentationString);
      } catch (IllegalArgumentException e) {
        // Use already set value of presentation
      }
    }
    switch (presentation) {
    case LIST:
      listPresentationAction.setChecked(true);
      setExpandCollapseActionsVisible(false);
      break;
    case TREE:
      treePresentationAction.setChecked(true);
      break;
    case COMPACT_TREE:
      compactTreePresentationAction.setChecked(true);
      break;
    default:
      break;
    }
    dropdownMenu.add(presentationMenu);
    dropdownMenu.add(new Separator());
    dropdownMenu.add(openNewCommitsAction);
    dropdownMenu.add(columnLayoutAction);
    dropdownMenu.add(fileNameModeAction);

    actionBars.setGlobalActionHandler(ActionFactory.DELETE.getId(), new GlobalDeleteActionHandler());

    // For the normal resource undo/redo actions to be active, so that files
    // deleted via the "Delete" action in the staging view can be restored.
    IUndoContext workspaceContext = (IUndoContext) ResourcesPlugin.getWorkspace().getAdapter(IUndoContext.class);
    undoRedoActionGroup = new UndoRedoActionGroup(getViewSite(), workspaceContext, true);
    undoRedoActionGroup.fillActionBars(actionBars);

    actionBars.updateActionBars();
  }

  private void setExpandCollapseActionsVisible(boolean visible) {
    for (IContributionItem item : unstagedToolBarManager.getItems())
      item.setVisible(visible);
    for (IContributionItem item : stagedToolBarManager.getItems())
      item.setVisible(visible);
    unstagedExpandAllAction.setEnabled(visible);
    unstagedCollapseAllAction.setEnabled(visible);
    stagedExpandAllAction.setEnabled(visible);
    stagedCollapseAllAction.setEnabled(visible);
    unstagedToolBarManager.update(true);
    stagedToolBarManager.update(true);
  }

  private TreeViewer createTree(Composite composite) {
    Tree tree = toolkit.createTree(composite, SWT.FULL_SELECTION
        | SWT.MULTI);
    TreeViewer treeViewer = new TreeViewer(tree);
    return treeViewer;
  }

  private IBaseLabelProvider createLabelProvider(TreeViewer treeViewer) {
    StagingViewLabelProvider baseProvider = new StagingViewLabelProvider(
        this);
    baseProvider.setFileNameMode(getPreferenceStore().getBoolean(
        UIPreferences.STAGING_VIEW_FILENAME_MODE));

    ProblemLabelDecorator decorator = new ProblemLabelDecorator(treeViewer);
    return new TreeDecoratingLabelProvider(baseProvider, decorator);
  }

  private StagingViewContentProvider createStagingContentProvider(
      boolean unstaged) {
    StagingViewContentProvider provider = new StagingViewContentProvider(
        this, unstaged);
    provider.setFileNameMode(getPreferenceStore().getBoolean(
        UIPreferences.STAGING_VIEW_FILENAME_MODE));
    return provider;
  }

  private IPreferenceStore getPreferenceStore() {
    return Activator.getDefault().getPreferenceStore();
  }

  private StagingViewLabelProvider getLabelProvider(ContentViewer viewer) {
    IBaseLabelProvider base = viewer.getLabelProvider();
    ILabelProvider labelProvider = ((TreeDecoratingLabelProvider) base)
        .getLabelProvider();
    return (StagingViewLabelProvider) labelProvider;
  }

  private StagingViewContentProvider getContentProvider(ContentViewer viewer) {
    return (StagingViewContentProvider) viewer.getContentProvider();
  }

  private void updateSectionText() {
    stagedSection.setText(MessageFormat
        .format(UIText.StagingView_StagedChanges,
            getSectionCount(stagedViewer)));
    unstagedSection.setText(MessageFormat.format(
        UIText.StagingView_UnstagedChanges,
        getSectionCount(unstagedViewer)));
  }

  private String getSectionCount(TreeViewer viewer) {
    StagingViewContentProvider contentProvider = getContentProvider(viewer);
    int count = contentProvider.getCount();
    int shownCount = contentProvider.getShownCount();
    if (shownCount == count)
      return Integer.toString(count);
    else
      return shownCount + "/" + count; //$NON-NLS-1$
  }

  private void updateMessage() {
    String message = commitMessageComponent.getStatus().getMessage();
    boolean needsRedraw = false;
    if (message != null) {
      warningLabel.showMessage(message);
      needsRedraw = true;
    } else {
      needsRedraw = warningLabel.getVisible();
      warningLabel.hideMessage();
    }
    // Without this explicit redraw, the ControlDecoration of the
    // commit message area would not get updated and cause visual
    // corruption.
    if (needsRedraw)
      commitMessageSection.redraw();
  }

  private void compareWith(OpenEvent event) {
    IStructuredSelection selection = (IStructuredSelection) event
        .getSelection();
    if (selection.isEmpty()
        || !(selection.getFirstElement() instanceof StagingEntry))
      return;
    StagingEntry stagingEntry = (StagingEntry) selection.getFirstElement();
    if (stagingEntry.isSubmodule())
      return;
    switch (stagingEntry.getState()) {
    case ADDED:
    case CHANGED:
    case REMOVED:
      runCommand(ActionCommands.COMPARE_INDEX_WITH_HEAD_ACTION, selection);
      break;

    case MISSING:
    case MISSING_AND_CHANGED:
    case MODIFIED:
    case MODIFIED_AND_CHANGED:
    case MODIFIED_AND_ADDED:
    case CONFLICTING:
    case UNTRACKED:
    default:
      // compare with index
      runCommand(ActionCommands.COMPARE_WITH_INDEX_ACTION, selection);
    }
  }

  private void createPopupMenu(final TreeViewer treeViewer) {
    final MenuManager menuMgr = new MenuManager();
    menuMgr.setRemoveAllWhenShown(true);
    Control control = treeViewer.getControl();
    control.setMenu(menuMgr.createContextMenu(control));
    menuMgr.addMenuListener(new IMenuListener() {

      public void menuAboutToShow(IMenuManager manager) {
        final IStructuredSelection selection = (IStructuredSelection) treeViewer
            .getSelection();
        if (selection.isEmpty())
          return;

        List<StagingEntry> stagingEntryList = new ArrayList<StagingEntry>();

        boolean submoduleSelected = false;
        boolean folderSelected = false;
        for (Object element : selection.toArray()) {
          if (element instanceof StagingFolderEntry) {
            StagingFolderEntry folder = (StagingFolderEntry) element;
            folderSelected = true;
            StagingViewContentProvider contentProvider = getContentProvider(treeViewer);
            List<StagingEntry> stagingEntries = contentProvider
                .getStagingEntriesFiltered(folder);
            for (StagingEntry stagingEntry : stagingEntries) {
              if (!stagingEntryList.contains(stagingEntry))
                stagingEntryList.add(stagingEntry);
            }
          } else if (element instanceof StagingEntry) {
            StagingEntry entry = (StagingEntry) element;
            if (entry.isSubmodule())
              submoduleSelected = true;
            if (!stagingEntryList.contains(entry))
              stagingEntryList.add(entry);
          }
        }

        final IStructuredSelection fileSelection = new StructuredSelection(
            stagingEntryList);

        if (!folderSelected) {
          Action openWorkingTreeVersion = new Action(
              UIText.CommitFileDiffViewer_OpenWorkingTreeVersionInEditorMenuLabel) {
            @Override
            public void run() {
              openSelectionInEditor(fileSelection);
            }
          };
          openWorkingTreeVersion.setEnabled(!submoduleSelected);
          menuMgr.add(openWorkingTreeVersion);
        }

        Set<StagingEntry.Action> availableActions = getAvailableActions(fileSelection);

        boolean addReplaceWithFileInGitIndex = availableActions.contains(StagingEntry.Action.REPLACE_WITH_FILE_IN_GIT_INDEX);
        boolean addReplaceWithHeadRevision = availableActions.contains(StagingEntry.Action.REPLACE_WITH_HEAD_REVISION);
        boolean addStage = availableActions.contains(StagingEntry.Action.STAGE);
        boolean addUnstage = availableActions.contains(StagingEntry.Action.UNSTAGE);
        boolean addDelete = availableActions.contains(StagingEntry.Action.DELETE);
        boolean addIgnore = availableActions.contains(StagingEntry.Action.IGNORE);
        boolean addLaunchMergeTool = availableActions.contains(StagingEntry.Action.LAUNCH_MERGE_TOOL);

        if (addStage)
          menuMgr.add(new Action(UIText.StagingView_StageItemMenuLabel) {
            @Override
            public void run() {
              stage(selection);
            }
          });
        if (addUnstage)
          menuMgr.add(new Action(UIText.StagingView_UnstageItemMenuLabel) {
            @Override
            public void run() {
              unstage(selection);
            }
          });
        boolean selectionIncludesNonWorkspaceResources = selectionIncludesNonWorkspaceResources(fileSelection);
        if (addReplaceWithFileInGitIndex)
          if (selectionIncludesNonWorkspaceResources)
            menuMgr.add(new ReplaceAction(
                UIText.StagingView_replaceWithFileInGitIndex,
                fileSelection, false));
          else
            menuMgr.add(createItem(
                UIText.StagingView_replaceWithFileInGitIndex,
                ActionCommands.DISCARD_CHANGES_ACTION,
                fileSelection)); // replace with index
        if (addReplaceWithHeadRevision)
          if (selectionIncludesNonWorkspaceResources)
            menuMgr.add(new ReplaceAction(
                UIText.StagingView_replaceWithHeadRevision,
                fileSelection, true));
          else
            menuMgr.add(createItem(
                UIText.StagingView_replaceWithHeadRevision,
                ActionCommands.REPLACE_WITH_HEAD_ACTION,
                fileSelection));
        if (addIgnore)
          menuMgr.add(new IgnoreAction(fileSelection));
        if (addDelete)
          menuMgr.add(new DeleteAction(fileSelection));
        if (addLaunchMergeTool)
          menuMgr.add(createItem(UIText.StagingView_MergeTool,
              ActionCommands.MERGE_TOOL_ACTION,
              fileSelection));
        menuMgr.add(new Separator());
        menuMgr.add(createShowInMenu());
      }
    });

  }

  /**
   * @return selected presentation
   */
  Presentation getPresentation() {
    return presentation;
  }

  /**
   * @return the trimmed string which is the current filter, empty string for
   *         no filter
   */
  String getFilterString() {
    if (filterText != null && !filterText.isDisposed())
      return filterText.getText().trim();
    else
      return ""; //$NON-NLS-1$
  }

  /**
   * Refresh the unstaged and staged viewers without preserving expanded
   * elements
   */
  public void refreshViewers() {
    Display.getDefault().syncExec(new Runnable() {
      public void run() {
        refreshViewersInternal();
      }
    });
  }

  /**
   * Refresh the unstaged and staged viewers, preserving expanded elements
   */
  public void refreshViewersPreservingExpandedElements() {
    Display.getDefault().syncExec(new Runnable() {
      public void run() {
        Object[] unstagedExpanded = unstagedViewer
            .getExpandedElements();
        Object[] stagedExpanded = stagedViewer.getExpandedElements();
        refreshViewersInternal();
        unstagedViewer.setExpandedElements(unstagedExpanded);
        stagedViewer.setExpandedElements(stagedExpanded);
      }
    });
  }

  private void refreshViewersInternal() {
    unstagedViewer.refresh();
    stagedViewer.refresh();
    updateSectionText();
  }

  private IContributionItem createShowInMenu() {
    IWorkbenchWindow workbenchWindow = getSite().getWorkbenchWindow();
    return UIUtils.createShowInMenu(workbenchWindow);
  }

  private class ReplaceAction extends Action {

    IStructuredSelection selection;
    private final boolean headRevision;

    ReplaceAction(String text, IStructuredSelection selection, boolean headRevision) {
      super(text);
      this.selection = selection;
      this.headRevision = headRevision;
    }

    @Override
    public void run() {
      boolean performAction = MessageDialog.openConfirm(form.getShell(),
          UIText.DiscardChangesAction_confirmActionTitle,
          UIText.DiscardChangesAction_confirmActionMessage);
      if (!performAction)
        return ;
      String[] files = getSelectedFiles(selection);
      replaceWith(files, headRevision);
    }
  }

  private static class IgnoreAction extends Action {

    private final IStructuredSelection selection;

    IgnoreAction(IStructuredSelection selection) {
      super(UIText.StagingView_IgnoreItemMenuLabel);
      this.selection = selection;
    }

    @Override
    public void run() {
      IgnoreOperationUI operation = new IgnoreOperationUI(
          getSelectedPaths(selection));
      operation.run();
    }
  }

  private class DeleteAction extends Action {

    private final IStructuredSelection selection;

    DeleteAction(IStructuredSelection selection) {
      super(UIText.StagingView_DeleteItemMenuLabel);
      this.selection = selection;
    }

    @Override
    public void run() {
      DeletePathsOperationUI operation = new DeletePathsOperationUI(
          getSelectedPaths(selection), getSite());
      operation.run();
    }
  }

  private class GlobalDeleteActionHandler extends Action {

    @Override
    public void run() {
      DeletePathsOperationUI operation = new DeletePathsOperationUI(
          getSelectedPaths(getSelection()), getSite());
      operation.run();
    }

    @Override
    public boolean isEnabled() {
      if (!unstagedViewer.getTree().isFocusControl())
        return false;

      IStructuredSelection selection = getSelection();
      if (selection.isEmpty())
        return false;

      for (Object element : selection.toList()) {
        if (!(element instanceof StagingEntry))
          return false;
        StagingEntry entry = (StagingEntry) element;
        if (!entry.getAvailableActions().contains(StagingEntry.Action.DELETE))
          return false;
      }

      return true;
    }

    private IStructuredSelection getSelection() {
      return (IStructuredSelection) unstagedViewer.getSelection();
    }
  }

  private void replaceWith(String[] files, boolean headRevision) {
    if (files == null || files.length == 0)
      return;
    CheckoutCommand checkoutCommand = new Git(currentRepository).checkout();
    if (headRevision)
      checkoutCommand.setStartPoint(Constants.HEAD);
    for (String path : files)
      checkoutCommand.addPath(path);
    try {
      checkoutCommand.call();
    } catch (Exception e) {
      Activator.handleError(UIText.StagingView_checkoutFailed, e, true);
    }
  }

  private String[] getSelectedFiles(IStructuredSelection selection) {
    List<String> result = new ArrayList<String>();
    Iterator iterator = selection.iterator();
    while (iterator.hasNext()) {
      Object selectedItem = iterator.next();
      if (selectedItem instanceof StagingEntry) {
        StagingEntry stagingEntry = (StagingEntry) selectedItem;
        result.add(stagingEntry.getPath());
      }
    }
    return result.toArray(new String[result.size()]);
  }

  private static List<IPath> getSelectedPaths(IStructuredSelection selection) {
    List<IPath> paths = new ArrayList<IPath>();
    Iterator iterator = selection.iterator();
    while (iterator.hasNext()) {
      StagingEntry stagingEntry = (StagingEntry) iterator.next();
      paths.add(stagingEntry.getLocation());
    }
    return paths;
  }

  /**
   * @param selection
   * @return true if the selection includes a non-workspace resource, false otherwise
   */
  private boolean selectionIncludesNonWorkspaceResources(ISelection selection) {
    if (!(selection instanceof IStructuredSelection))
      return false;
    IStructuredSelection structuredSelection = (IStructuredSelection) selection;
    Iterator iterator = structuredSelection.iterator();
    while (iterator.hasNext()) {
      Object selectedObject = iterator.next();
      if (!(selectedObject instanceof StagingEntry))
        return false;
      StagingEntry stagingEntry = (StagingEntry) selectedObject;
      IFile file = stagingEntry.getFile();
      if (file == null)
        return true;
    }
    return false;
  }

  private void openSelectionInEditor(ISelection s) {
    if (s.isEmpty() || !(s instanceof IStructuredSelection))
      return;
    final IStructuredSelection iss = (IStructuredSelection) s;
    for (Object element : iss.toList()) {
      if (element instanceof StagingEntry) {
        StagingEntry entry = (StagingEntry) element;
        String relativePath = entry.getPath();
        String path = new Path(currentRepository.getWorkTree()
            .getAbsolutePath()).append(relativePath)
            .toOSString();
        openFileInEditor(path);
      }
    }
  }

  private void openFileInEditor(String filePath) {
    IWorkbenchWindow window = PlatformUI.getWorkbench()
        .getActiveWorkbenchWindow();
    File file = new File(filePath);
    if (!file.exists()) {
      String message = NLS.bind(UIText.CommitFileDiffViewer_FileDoesNotExist, filePath);
      Activator.showError(message, null);
    }
    IWorkbenchPage page = window.getActivePage();
    EgitUiEditorUtils.openEditor(file, page);
  }

  private static Set<StagingEntry.Action> getAvailableActions(IStructuredSelection selection) {
    Set<StagingEntry.Action> availableActions = EnumSet.noneOf(StagingEntry.Action.class);
    for (Iterator it = selection.iterator(); it.hasNext(); ) {
      StagingEntry stagingEntry = (StagingEntry) it.next();
      if (availableActions.isEmpty())
        availableActions.addAll(stagingEntry.getAvailableActions());
      else
        availableActions.retainAll(stagingEntry.getAvailableActions());
    }
    return availableActions;
  }

  private IAction createItem(String text, final String commandId,
      final IStructuredSelection selection) {
    return new Action(text) {
      @Override
      public void run() {
        CommonUtils.runCommand(commandId, selection);
      }
    };
  }

  private void reactOnSelection(ISelection selection) {
    if (selection instanceof StructuredSelection) {
      StructuredSelection ssel = (StructuredSelection) selection;
      if (ssel.size() != 1)
        return;
      Object firstElement = ssel.getFirstElement();
      if (firstElement instanceof IResource)
        showResource((IResource) firstElement);
      else if (firstElement instanceof RepositoryTreeNode) {
        RepositoryTreeNode repoNode = (RepositoryTreeNode) firstElement;
        if (currentRepository != repoNode.getRepository())
          reload(repoNode.getRepository());
      } else if (firstElement instanceof IAdaptable) {
        IResource adapted = (IResource) ((IAdaptable) firstElement).getAdapter(IResource.class);
        if (adapted != null)
          showResource(adapted);
      }
    }
  }

  private void showResource(final IResource resource) {
    IProject project = resource.getProject();
    RepositoryMapping mapping = RepositoryMapping.getMapping(project);
    if (mapping == null)
      return;
    if (mapping.getRepository() != currentRepository)
      reload(mapping.getRepository());
  }

  private void stage(IStructuredSelection selection) {
    StagingViewContentProvider contentProvider = getContentProvider(unstagedViewer);
    Git git = new Git(currentRepository);
    Iterator iterator = selection.iterator();
    List<String> addPaths = new ArrayList<String>();
    List<String> rmPaths = new ArrayList<String>();
    resetPathsToExpand();
    while (iterator.hasNext()) {
      Object element = iterator.next();
      if (element instanceof StagingEntry) {
        StagingEntry entry = (StagingEntry) element;
        selectEntryForStaging(entry, addPaths, rmPaths);
        addPathAndParentPaths(entry.getParentPath(), pathsToExpandInStaged);
      } else if (element instanceof StagingFolderEntry) {
        StagingFolderEntry folder = (StagingFolderEntry) element;
        List<StagingEntry> entries = contentProvider
            .getStagingEntriesFiltered(folder);
        for (StagingEntry entry : entries)
          selectEntryForStaging(entry, addPaths, rmPaths);
        addExpandedPathsBelowFolder(folder, unstagedViewer,
            pathsToExpandInStaged);
      } else {
        IResource resource = AdapterUtils.adapt(element, IResource.class);
        if (resource != null) {
          RepositoryMapping mapping = RepositoryMapping.getMapping(resource);
          if (mapping != null && mapping.getRepository() == currentRepository) {
            String path = mapping.getRepoRelativePath(resource);
            // If resource corresponds to root of working directory
            if ("".equals(path)) //$NON-NLS-1$
              addPaths.add("."); //$NON-NLS-1$
            else
              addPaths.add(path);
          }
        }
      }
    }

    if (!addPaths.isEmpty())
      try {
        AddCommand add = git.add();
        for (String addPath : addPaths)
          add.addFilepattern(addPath);
        add.call();
      } catch (NoFilepatternException e1) {
        // cannot happen
      } catch (JGitInternalException e1) {
        Activator.handleError(e1.getCause().getMessage(),
            e1.getCause(), true);
      } catch (Exception e1) {
        Activator.handleError(e1.getMessage(), e1, true);
      }
    if (!rmPaths.isEmpty())
      try {
        RmCommand rm = git.rm().setCached(true);
        for (String rmPath : rmPaths)
          rm.addFilepattern(rmPath);
        rm.call();
      } catch (NoFilepatternException e) {
        // cannot happen
      } catch (JGitInternalException e) {
        Activator.handleError(e.getCause().getMessage(), e.getCause(),
            true);
      } catch (Exception e) {
        Activator.handleError(e.getMessage(), e, true);
      }
  }

  private void selectEntryForStaging(StagingEntry entry,
      List<String> addPaths, List<String> rmPaths) {
    switch (entry.getState()) {
    case ADDED:
    case CHANGED:
    case REMOVED:
      // already staged
      break;
    case CONFLICTING:
    case MODIFIED:
    case MODIFIED_AND_CHANGED:
    case MODIFIED_AND_ADDED:
    case UNTRACKED:
      addPaths.add(entry.getPath());
      break;
    case MISSING:
    case MISSING_AND_CHANGED:
      rmPaths.add(entry.getPath());
      break;
    }
  }

  private void unstage(IStructuredSelection selection) {
    if (selection.isEmpty())
      return;

    List<String> paths = processUnstageSelection(selection);
    if (paths.isEmpty())
      return;

    try {
      Git git = new Git(currentRepository);
      ResetCommand reset = git.reset();
      for (String path : paths)
        reset.addPath(path);
      reset.call();
    } catch (GitAPIException e) {
      Activator.handleError(e.getMessage(), e, true);
    }
  }

  private List<String> processUnstageSelection(IStructuredSelection selection) {
    List<String> paths = new ArrayList<String>();
    resetPathsToExpand();
    for (Object element : selection.toList()) {
      if (element instanceof StagingEntry) {
        StagingEntry entry = (StagingEntry) element;
        addUnstagePath(entry, paths);
        addPathAndParentPaths(entry.getParentPath(), pathsToExpandInUnstaged);
      } else if (element instanceof StagingFolderEntry) {
        StagingFolderEntry folder = (StagingFolderEntry) element;
        List<StagingEntry> entries = getContentProvider(stagedViewer)
            .getStagingEntriesFiltered(folder);
        for (StagingEntry entry : entries)
          addUnstagePath(entry, paths);
        addExpandedPathsBelowFolder(folder, stagedViewer,
            pathsToExpandInUnstaged);
      }
    }
    return paths;
  }

  private void addUnstagePath(StagingEntry entry, List<String> paths) {
    switch (entry.getState()) {
    case ADDED:
    case CHANGED:
    case REMOVED:
      paths.add(entry.getPath());
      return;
    default:
      // unstaged
    }
  }

  private void resetPathsToExpand() {
    pathsToExpandInStaged = new HashSet<IPath>();
    pathsToExpandInUnstaged = new HashSet<IPath>();
  }

  private static void addExpandedPathsBelowFolder(StagingFolderEntry folder,
      TreeViewer treeViewer, Set<IPath> addToSet) {
    Object[] expandedElements = treeViewer.getExpandedElements();
    for (Object expandedElement : expandedElements) {
      if (expandedElement instanceof StagingFolderEntry) {
        StagingFolderEntry expandedFolder = (StagingFolderEntry) expandedElement;
        if (folder.getPath().isPrefixOf(
            expandedFolder.getPath()))
          addPathAndParentPaths(expandedFolder.getPath(), addToSet);
      }
    }
  }

  private static void addPathAndParentPaths(IPath initialPath, Set<IPath> addToSet) {
    for (IPath p = initialPath; p.segmentCount() >= 1; p = p
        .removeLastSegments(1))
      addToSet.add(p);
  }

  private boolean isValidRepo(final Repository repository) {
    return repository != null
        && !repository.isBare()
        && repository.getWorkTree().exists();
  }

  /**
   * Clear the view's state.
   * <p>
   * This method must be called from the UI-thread
   */
  private void clearRepository() {
    saveCommitMessageComponentState();
    currentRepository = null;
    StagingViewUpdate update = new StagingViewUpdate(null, null, null);
    unstagedViewer.setInput(update);
    stagedViewer.setInput(update);
    enableCommitWidgets(false);
    refreshAction.setEnabled(false);
    updateSectionText();
    form.setText(UIText.StagingView_NoSelectionTitle);
  }

  /**
   * Show rebase buttons only if a rebase operation is in progress
   *
   * @param isRebasing
   *            {@code}true if rebase is in progress
   */
  protected void updateRebaseButtonVisibility(final boolean isRebasing) {
    Display.getDefault().asyncExec(new Runnable() {
      public void run() {
        showControl(rebaseSection, isRebasing);
        rebaseSection.getParent().layout(true);
      }
    });
  }

  private static void showControl(Control c, final boolean show) {
    c.setVisible(show);
    GridData g = (GridData) c.getLayoutData();
    g.exclude = !show;
  }

  /**
   * @param isAmending
   *            if the current commit should be amended
   */
  public void setAmending(boolean isAmending) {
    if (amendPreviousCommitAction.isChecked() != isAmending) {
      amendPreviousCommitAction.setChecked(isAmending);
      amendPreviousCommitAction.run();
    }
  }

  /**
   * @param message
   *            commit message to set for current repository
   */
  public void setCommitMessage(String message) {
    commitMessageText.setText(message);
  }

  /**
   * Reload the staging view
   *
   * @param repository
   */
  public void reload(final Repository repository) {
    if (form.isDisposed())
      return;
    if (repository == null) {
      syncExec(new Runnable() {
        public void run() {
          clearRepository();
        }
      });
      return;
    }

    if (!isValidRepo(repository))
      return;

    final boolean repositoryChanged = currentRepository != repository;

    syncExec(new Runnable() {

      public void run() {
        if (form.isDisposed())
          return;

        final IndexDiffData indexDiff = doReload(repository);
        boolean indexDiffAvailable;
        boolean noConflicts;
        if (indexDiff == null) {
          indexDiffAvailable = false;
          noConflicts = true;
        } else {
          indexDiffAvailable = true;
          noConflicts = indexDiff.getConflicting().isEmpty();
        }

        if (repositoryChanged) {
          // Reset paths, they're from the old repository
          resetPathsToExpand();
          if (refsChangedListener != null)
            refsChangedListener.remove();
          refsChangedListener = repository.getListenerList()
              .addRefsChangedListener(new RefsChangedListener() {

                public void onRefsChanged(RefsChangedEvent event) {
                  updateRebaseButtonVisibility(repository
                      .getRepositoryState().isRebasing());
                }

              });
        }
        final StagingViewUpdate update = new StagingViewUpdate(currentRepository, indexDiff, null);
        Object[] unstagedExpanded = unstagedViewer
            .getExpandedElements();
        Object[] stagedExpanded = stagedViewer
            .getExpandedElements();
        unstagedViewer.setInput(update);
        stagedViewer.setInput(update);
        expandPreviousExpandedAndPaths(unstagedExpanded, unstagedViewer,
            pathsToExpandInUnstaged);
        expandPreviousExpandedAndPaths(stagedExpanded, stagedViewer,
            pathsToExpandInStaged);
        refreshAction.setEnabled(true);

        updateRebaseButtonVisibility(repository.getRepositoryState()
            .isRebasing());


        boolean commitEnabled = indexDiffAvailable
            && repository.getRepositoryState().canCommit()
            && noConflicts;
        commitButton.setEnabled(commitEnabled);

        boolean commitAndPushEnabled = commitEnabled
            && !repository.getRepositoryState().isRebasing();
        commitAndPushButton.setEnabled(commitAndPushEnabled);

        boolean rebaseContinueEnabled = indexDiffAvailable
            && repository.getRepositoryState().isRebasing()
            && noConflicts;
        rebaseContinueButton.setEnabled(rebaseContinueEnabled);

        form.setText(GitLabels.getStyledLabelSafe(repository).toString());
        updateCommitMessageComponent(repositoryChanged, indexDiffAvailable);
        enableCommitWidgets(indexDiffAvailable && noConflicts);
        updateSectionText();
      }
    });
  }

  private IndexDiffData doReload(final Repository repository) {
    currentRepository = repository;

    IndexDiffCacheEntry entry = org.eclipse.egit.core.Activator.getDefault().getIndexDiffCache().getIndexDiffCacheEntry(currentRepository);

    if(cacheEntry != null && cacheEntry != entry)
      cacheEntry.removeIndexDiffChangedListener(myIndexDiffListener);

    cacheEntry = entry;
    cacheEntry.addIndexDiffChangedListener(myIndexDiffListener);

    return cacheEntry.getIndexDiff();
  }

  private void expandPreviousExpandedAndPaths(Object[] previous,
      TreeViewer viewer, Set<IPath> additionalPaths) {
    // Auto-expand is on, so don't change expanded items
    if (viewer.getAutoExpandLevel() == AbstractTreeViewer.ALL_LEVELS)
      return;

    // No need to expand anything
    if (getPresentation() == Presentation.LIST)
      return;

    Set<IPath> paths = new HashSet<IPath>(additionalPaths);
    // Instead of just expanding the previous elements directly, also expand
    // all parent paths. This makes it work in case of "re-folding" of
    // compact tree.
    for (Object element : previous)
      if (element instanceof StagingFolderEntry)
        addPathAndParentPaths(((StagingFolderEntry) element).getPath(), paths);
    List<StagingFolderEntry> expand = new ArrayList<StagingFolderEntry>();
    StagingViewContentProvider stagedContentProvider = getContentProvider(viewer);
    calculateNodesToExpand(paths, stagedContentProvider.getElements(null),
        expand);
    viewer.setExpandedElements(expand.toArray());
  }

  private void calculateNodesToExpand(Set<IPath> paths, Object[] elements,
      List<StagingFolderEntry> result) {
    if (elements == null)
      return;

    for (Object element : elements) {
      if (element instanceof StagingFolderEntry) {
        StagingFolderEntry folder = (StagingFolderEntry) element;
        if (paths.contains(folder.getPath())) {
          result.add(folder);
          // Only recurs if folder matched (i.e. don't try to expand
          // children of unexpanded parents)
          calculateNodesToExpand(paths, folder.getChildren(), result);
        }
      }
    }
  }

  private void clearCommitMessageToggles() {
    amendPreviousCommitAction.setChecked(false);
    addChangeIdAction.setChecked(false);
    signedOffByAction.setChecked(false);
  }

  void updateCommitMessageComponent(boolean repositoryChanged, boolean indexDiffAvailable) {
    if (repositoryChanged)
      if (commitMessageComponent.isAmending()
          || userEnteredCommitMessage())
        saveCommitMessageComponentState();
      else
        deleteCommitMessageComponentState();
    if (!indexDiffAvailable)
      return; // only try to restore the stored repo commit message if
          // indexDiff is ready

    CommitHelper helper = new CommitHelper(currentRepository);
    CommitMessageComponentState oldState = null;
    if (repositoryChanged
        || commitMessageComponent.getRepository() != currentRepository) {
      oldState = loadCommitMessageComponentState();
      commitMessageComponent.setRepository(currentRepository);
      if (oldState == null)
        loadInitialState(helper);
      else
        loadExistingState(helper, oldState);
    } else { // repository did not change
      if (!commitMessageComponent.isAmending()
          && userEnteredCommitMessage()) {
        if (!commitMessageComponent.getHeadCommit().equals(
            helper.getPreviousCommit()))
          addHeadChangedWarning(commitMessageComponent
              .getCommitMessage());
      } else
        loadInitialState(helper);
    }
    amendPreviousCommitAction.setChecked(commitMessageComponent
        .isAmending());
    amendPreviousCommitAction.setEnabled(helper.amendAllowed());
    updateMessage();
  }

  private void loadExistingState(CommitHelper helper,
      CommitMessageComponentState oldState) {
    boolean headCommitChanged = !oldState.getHeadCommit().equals(
        getCommitId(helper.getPreviousCommit()));
    commitMessageComponent.enableListeners(false);
    commitMessageComponent.setAuthor(oldState.getAuthor());
    if (headCommitChanged)
      addHeadChangedWarning(oldState.getCommitMessage());
    else
      commitMessageComponent
          .setCommitMessage(oldState.getCommitMessage());
    commitMessageComponent.setCommitter(oldState.getCommitter());
    commitMessageComponent.setHeadCommit(getCommitId(helper
        .getPreviousCommit()));
    commitMessageComponent.setCommitAllowed(helper.canCommit());
    commitMessageComponent.setCannotCommitMessage(helper.getCannotCommitMessage());
    boolean amendAllowed = helper.amendAllowed();
    commitMessageComponent.setAmendAllowed(amendAllowed);
    if (!amendAllowed)
      commitMessageComponent.setAmending(false);
    else if (!headCommitChanged && oldState.getAmend())
      commitMessageComponent.setAmending(true);
    else
      commitMessageComponent.setAmending(false);
    commitMessageComponent.updateUIFromState();
    commitMessageComponent.updateSignedOffAndChangeIdButton();
    commitMessageComponent.enableListeners(true);
  }

  private void addHeadChangedWarning(String commitMessage) {
    if (!commitMessage.startsWith(UIText.StagingView_headCommitChanged)) {
      String message = UIText.StagingView_headCommitChanged
          + Text.DELIMITER + Text.DELIMITER + commitMessage;
      commitMessageComponent.setCommitMessage(message);
    }
  }

  private void loadInitialState(CommitHelper helper) {
    commitMessageComponent.enableListeners(false);
    commitMessageComponent.resetState();
    commitMessageComponent.setAuthor(helper.getAuthor());
    commitMessageComponent.setCommitMessage(helper.getCommitMessage());
    commitMessageComponent.setCommitter(helper.getCommitter());
    commitMessageComponent.setHeadCommit(getCommitId(helper
        .getPreviousCommit()));
    commitMessageComponent.setCommitAllowed(helper.canCommit());
    commitMessageComponent.setCannotCommitMessage(helper.getCannotCommitMessage());
    commitMessageComponent.setAmendAllowed(helper.amendAllowed());
    commitMessageComponent.setAmending(false);
    // set the defaults for change id and signed off buttons.
    commitMessageComponent.setDefaults();
    commitMessageComponent.updateUI();
    commitMessageComponent.enableListeners(true);
  }

  private boolean userEnteredCommitMessage() {
    if (commitMessageComponent.getRepository() == null)
      return false;
    String message = commitMessageComponent.getCommitMessage().replace(
        UIText.StagingView_headCommitChanged, ""); //$NON-NLS-1$
    if (message == null || message.trim().length() == 0)
      return false;

    String chIdLine = "Change-Id: I" + ObjectId.zeroId().name(); //$NON-NLS-1$

    if (GerritUtil.getCreateChangeId(currentRepository.getConfig())
        && commitMessageComponent.getCreateChangeId()) {
      if (message.trim().equals(chIdLine))
        return false;

      // change id was added automatically, but ther is more in the
      // message; strip the id, and check for the signed-off-by tag
      message = message.replace(chIdLine, ""); //$NON-NLS-1$
    }

    if (org.eclipse.egit.ui.Activator.getDefault().getPreferenceStore()
        .getBoolean(UIPreferences.COMMIT_DIALOG_SIGNED_OFF_BY)
        && commitMessageComponent.isSignedOff()
        && message.trim().equals(
            Constants.SIGNED_OFF_BY_TAG
                + commitMessageComponent.getCommitter()))
      return false;

    return true;
  }

  private ObjectId getCommitId(RevCommit commit) {
    if (commit == null)
      return ObjectId.zeroId();
    return commit.getId();
  }

  private void saveCommitMessageComponentState() {
    final Repository repo = commitMessageComponent.getRepository();
    if (repo != null)
      CommitMessageComponentStateManager.persistState(repo,
          commitMessageComponent.getState());
  }

  private void deleteCommitMessageComponentState() {
    if (commitMessageComponent.getRepository() != null)
      CommitMessageComponentStateManager
          .deleteState(commitMessageComponent.getRepository());
  }

  private CommitMessageComponentState loadCommitMessageComponentState() {
    return CommitMessageComponentStateManager.loadState(currentRepository);
  }

  private Collection<String> getStagedFileNames() {
    StagingViewContentProvider stagedContentProvider = getContentProvider(stagedViewer);
    StagingEntry[] entries = stagedContentProvider.getStagingEntries();
    List<String> files = new ArrayList<String>();
    for (StagingEntry entry : entries)
      files.add(entry.getPath());
    return files;
  }

  private void commit(boolean pushUpstream) {
    if (!isCommitWithoutFilesAllowed()) {
      MessageDialog.openError(getSite().getShell(),
          UIText.StagingView_committingNotPossible,
          UIText.StagingView_noStagedFiles);
      return;
    }
    if (!commitMessageComponent.checkCommitInfo())
      return;

    if (!UIUtils.saveAllEditors(currentRepository,
        UIText.StagingView_cancelCommitAfterSaving))
      return;

    String commitMessage = commitMessageComponent.getCommitMessage();
    CommitOperation commitOperation = null;
    try {
      commitOperation = new CommitOperation(currentRepository,
          commitMessageComponent.getAuthor(),
          commitMessageComponent.getCommitter(),
          commitMessage);
    } catch (CoreException e) {
      Activator.handleError(UIText.StagingView_commitFailed, e, true);
      return;
    }
    if (amendPreviousCommitAction.isChecked())
      commitOperation.setAmending(true);
    commitOperation.setComputeChangeId(addChangeIdAction.isChecked());
    Job commitJob = new CommitJob(currentRepository, commitOperation)
      .setOpenCommitEditor(openNewCommitsAction.isChecked())
      .setPushUpstream(pushUpstream);

    // don't allow to do anything as long as commit is in progress
    enableAllWidgets(false);
    commitJob.addJobChangeListener(new JobChangeAdapter() {
      @Override
      public void done(IJobChangeEvent event) {
        PlatformUI.getWorkbench().getDisplay()
            .asyncExec(new Runnable() {
              public void run() {
                enableAllWidgets(true);
              }
            });
      }
    });

    IWorkbenchSiteProgressService service = CommonUtils.getService(getSite(), IWorkbenchSiteProgressService.class);
    if (service != null)
      service.schedule(commitJob, 0, true);
    else
      commitJob.schedule();

    CommitMessageHistory.saveCommitHistory(commitMessage);
    clearCommitMessageToggles();
    commitMessageText.setText(EMPTY_STRING);
  }

  private boolean isCommitWithoutFilesAllowed() {
    if (stagedViewer.getTree().getItemCount() > 0)
      return true;

    if (amendPreviousCommitAction.isChecked())
      return true;

    return CommitHelper.isCommitWithoutFilesAllowed(currentRepository);
  }

  @Override
  public void setFocus() {
    unstagedViewer.getControl().setFocus();
  }

  @Override
  public void dispose() {
    super.dispose();

    ISelectionService srv = CommonUtils.getService(getSite(), ISelectionService.class);
    srv.removePostSelectionListener(selectionChangedListener);

    if(cacheEntry != null)
      cacheEntry.removeIndexDiffChangedListener(myIndexDiffListener);

    if (undoRedoActionGroup != null)
      undoRedoActionGroup.dispose();

    InstanceScope.INSTANCE.getNode(
        org.eclipse.egit.core.Activator.getPluginId())
        .removePreferenceChangeListener(prefListener);
    if (refsChangedListener != null)
      refsChangedListener.remove();
    disposed = true;
  }

  private boolean isDisposed() {
    return disposed;
  }

  private void syncExec(Runnable runnable) {
    PlatformUI.getWorkbench().getDisplay().syncExec(runnable);
  }

}
TOP

Related Classes of org.eclipse.egit.ui.internal.staging.StagingView$ReplaceAction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.