org.jboss.tools.openshift.express.internal.ui.server.CommitDialog.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.tools.openshift.express.internal.ui.server.CommitDialog.java

Source

/*******************************************************************************
 * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
 * Copyright (C) 2007, Robin Rosenberg <me@lathund.dewire.com.dewire.com>
 * Copyright (C) 2007, Robin Rosenberg <me@lathund.dewire.com>
 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
 * Copyright (C) 2007, Shawn O. Pearce <spearce@spearce.org>
 * Copyright (C) 2011, Mathias Kinzler <mathias.kinzler@sap.com>
 * Copyright (C) 2012, Daniel Megert <daniel_megert@ch.ibm.com>
 * Copyright (C) 2012, 2013 Robin Stocker <robin@nibor.org>
 * Copyright (C) 2012, IBM Corporation (Markus Keller <markus_keller@ch.ibm.com>)
 * Copyright (C) 2013, Franois Rey <eclipse.org_@_francois_._rey_._name>
 *
 * 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
 * 
 * This is more or less a copy of the original code in org.eclipse.egit.ui which had 
 * to be copied so that we can achieve minimal customizations not possible otherwise.
 *******************************************************************************/
package org.jboss.tools.openshift.express.internal.ui.server;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.egit.core.internal.util.ResourceUtil;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.CompareUtils;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.commit.CommitHelper;
import org.eclipse.egit.ui.internal.commit.CommitMessageHistory;
import org.eclipse.egit.ui.internal.commit.CommitProposalProcessor;
import org.eclipse.egit.ui.internal.components.CachedCheckboxTreeViewer;
import org.eclipse.egit.ui.internal.components.FilteredCheckboxTree;
import org.eclipse.egit.ui.internal.decorators.IProblemDecoratable;
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.CommitMessageComponent.CommitStatus;
import org.eclipse.egit.ui.internal.dialogs.ICommitMessageComponentNotifications;
import org.eclipse.egit.ui.internal.dialogs.SpellcheckableMessageArea;
import org.eclipse.egit.ui.internal.staging.StagingView;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
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.resource.ResourceManager;
import org.eclipse.jface.viewers.BaseLabelProvider;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DecorationOverlayIcon;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IDecoration;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.IndexDiff;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.model.BaseWorkbenchContentProvider;
import org.eclipse.ui.progress.WorkbenchJob;
import org.jboss.tools.openshift.egit.core.EGitUtils;
import org.jboss.tools.openshift.express.internal.ui.ExpressImages;
import org.jboss.tools.openshift.express.internal.ui.server.CommitItem.Status;

/**
 * Dialog is shown to user when they request to commit files. Changes in the
 * selected portion of the tree are shown.
 */
public class CommitDialog extends TitleAreaDialog {

    private static IPreferenceStore getPreferenceStore() {
        return org.eclipse.egit.ui.Activator.getDefault().getPreferenceStore();
    }

    static class CommitFileContentProvider extends BaseWorkbenchContentProvider {
        @Override
        public Object[] getElements(Object element) {
            if (element instanceof Object[])
                return (Object[]) element;
            if (element instanceof Collection)
                return ((Collection) element).toArray();
            return new Object[0];
        }

        @Override
        public Object[] getChildren(Object parentElement) {
            return new Object[0];
        }

        @Override
        public Object getParent(Object element) {
            return null;
        }

        @Override
        public boolean hasChildren(Object element) {
            return false;
        }
    }

    static class CommitStatusLabelProvider extends BaseLabelProvider implements IStyledLabelProvider {

        private Image DEFAULT = PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE);

        private ResourceManager resourceManager = new LocalResourceManager(JFaceResources.getResources());

        private final Image SUBMODULE = UIIcons.REPOSITORY.createImage();

        private Image getEditorImage(CommitItem item) {
            if (!item.submodule) {
                Image image = DEFAULT;
                String name = new Path(item.path).lastSegment();
                if (name != null) {
                    ImageDescriptor descriptor = PlatformUI.getWorkbench().getEditorRegistry()
                            .getImageDescriptor(name);
                    image = (Image) this.resourceManager.get(descriptor);
                }
                return image;
            } else
                return SUBMODULE;
        }

        private Image getDecoratedImage(Image base, ImageDescriptor decorator) {
            DecorationOverlayIcon decorated = new DecorationOverlayIcon(base, decorator, IDecoration.BOTTOM_RIGHT);
            return (Image) this.resourceManager.get(decorated);
        }

        @Override
        public StyledString getStyledText(Object element) {
            return new StyledString();
        }

        @Override
        public Image getImage(Object element) {
            CommitItem item = (CommitItem) element;
            ImageDescriptor decorator = null;
            switch (item.status) {
            case UNTRACKED:
                decorator = UIIcons.OVR_UNTRACKED;
                break;
            case ADDED:
            case ADDED_INDEX_DIFF:
                decorator = UIIcons.OVR_STAGED_ADD;
                break;
            case REMOVED:
            case REMOVED_NOT_STAGED:
            case REMOVED_UNTRACKED:
                decorator = UIIcons.OVR_STAGED_REMOVE;
                break;
            default:
                break;
            }
            return decorator != null ? getDecoratedImage(getEditorImage(item), decorator) : getEditorImage(item);
        }

        @Override
        public void dispose() {
            SUBMODULE.dispose();
            resourceManager.dispose();
            super.dispose();
        }
    }

    static class CommitPathLabelProvider extends ColumnLabelProvider {

        @Override
        public String getText(Object obj) {
            return ((CommitItem) obj).path;
        }

        @Override
        public String getToolTipText(Object element) {
            return ((CommitItem) element).status.getText();
        }

    }

    class HeaderSelectionListener extends SelectionAdapter {

        private CommitItem.Order order;

        private Boolean reversed;

        public HeaderSelectionListener(CommitItem.Order order) {
            this.order = order;
        }

        @Override
        public void widgetSelected(SelectionEvent e) {
            TreeColumn column = (TreeColumn) e.widget;
            Tree tree = column.getParent();

            if (column == tree.getSortColumn()) {
                int currentDirection = tree.getSortDirection();
                switch (currentDirection) {
                case SWT.NONE:
                    reversed = Boolean.FALSE;
                    break;
                case SWT.UP:
                    reversed = Boolean.TRUE;
                    break;
                case SWT.DOWN:
                    // fall through
                default:
                    reversed = null;
                    break;
                }
            } else
                reversed = Boolean.FALSE;

            if (reversed == null) {
                tree.setSortColumn(null);
                tree.setSortDirection(SWT.NONE);
                filesViewer.setComparator(null);
                return;
            }
            tree.setSortColumn(column);

            Comparator<CommitItem> comparator;
            if (reversed.booleanValue()) {
                comparator = order.descending();
                tree.setSortDirection(SWT.DOWN);
            } else {
                comparator = order;
                tree.setSortDirection(SWT.UP);
            }

            filesViewer.setComparator(new CommitViewerComparator(comparator));
        }

    }

    class CommitItemSelectionListener extends SelectionAdapter {

        @Override
        public void widgetDefaultSelected(SelectionEvent e) {
            IStructuredSelection selection = (IStructuredSelection) filesViewer.getSelection();
            CommitItem commitItem = (CommitItem) selection.getFirstElement();
            if (commitItem == null) {
                return;
            }
            compare(commitItem);
        }

    }

    private final class CommitItemFilter extends ViewerFilter {
        @Override
        public boolean select(Viewer viewer, Object parentElement, Object element) {
            boolean result = true;
            if (!showUntracked || !allowToChangeSelection) {
                if (element instanceof CommitItem) {
                    CommitItem item = (CommitItem) element;
                    if (item.status == Status.UNTRACKED)
                        result = false;
                }
            }
            return result;
        }
    }

    private static final String SHOW_UNTRACKED_PREF = "CommitDialog.showUntracked"; //$NON-NLS-1$

    private static final String DIALOG_SETTINGS_SECTION_NAME = Activator.getPluginId() + ".COMMIT_DIALOG_SECTION"; //$NON-NLS-1$

    /**
     * A constant used for the 'commit and push button' button
     */
    //   public static final int COMMIT_AND_PUSH_ID = 30;

    FormToolkit toolkit;

    CommitMessageComponent commitMessageComponent;

    SpellcheckableMessageArea commitText;

    Text authorText;

    Text committerText;

    ToolItem amendingItem;

    ToolItem signedOffItem;

    ToolItem changeIdItem;

    ToolItem showUntrackedItem;

    CachedCheckboxTreeViewer filesViewer;

    Section filesSection;

    Button commitButton;

    //   Button commitAndPushButton;

    ArrayList<CommitItem> items = new ArrayList<>();

    private String commitMessage = null;

    private String author = null;

    private String committer = null;

    /**
     * A collection of files that should be already checked in the table.
     */
    private Set<String> preselectedFiles = Collections.emptySet();

    private boolean preselectAll = false;

    private ArrayList<String> selectedFiles = new ArrayList<>();

    private boolean amending = false;

    private boolean amendAllowed = true;

    private boolean showUntracked = true;

    private boolean createChangeId = false;

    private boolean allowToChangeSelection = true;

    private Repository repository;

    //   private boolean isPushRequested = false;

    private boolean isAhead;

    private boolean canCommit;

    private String applicationName;

    private String remote;

    /**
     * @param parentShell
     */
    //   public CommitDialog(Shell parentShell) {
    //      super(parentShell);
    //   }

    public CommitDialog(String remote, String applicationName, Shell parentShell) {
        super(parentShell);
        this.remote = remote;
        this.applicationName = applicationName;
    }

    /**
     * @return The message the user entered
     */
    public String getCommitMessage() {
        return commitMessage;
    }

    /**
     * Preset a commit message. This might be for amending a commit.
     * @param s the commit message
     */
    public void setCommitMessage(String s) {
        this.commitMessage = s;
    }

    /**
     * @return the files selected by the user to commit.
     */
    public Collection<String> getSelectedFiles() {
        return selectedFiles;
    }

    /**
     * Sets the files that should be checked in this table.
     *
     * @param preselectedFiles
     *            the files to be checked in the dialog's table, must not be
     *            <code>null</code>
     */
    public void setPreselectedFiles(Set<String> preselectedFiles) {
        Assert.isNotNull(preselectedFiles);
        this.preselectedFiles = preselectedFiles;
    }

    /**
     * Preselect all changed files in the commit dialog.
     * Untracked files are not preselected.
     * @param preselectAll
     */
    public void setPreselectAll(boolean preselectAll) {
        this.preselectAll = preselectAll;
    }

    /**
     * Set the total set of changed files, including additions and
     * removals
     * @param repository
     * @param paths paths of files potentially affected by a new commit
     * @param indexDiff IndexDiff of the related repository
     * @throws URISyntaxException 
     * @throws InvocationTargetException 
     * @throws IOException 
     */
    public void setFiles(Repository repository, Set<String> paths, IndexDiff indexDiff) {
        this.repository = repository;
        items.clear();
        for (String path : paths) {
            CommitItem item = new CommitItem();
            item.status = getFileStatus(path, indexDiff);
            item.submodule = FileMode.GITLINK == indexDiff.getIndexMode(path);
            item.path = path;
            item.problemSeverity = getProblemSeverity(repository, path);
            items.add(item);
        }

        // initially, we sort by status plus path
        Collections.sort(items, new Comparator<CommitItem>() {
            @Override
            public int compare(CommitItem o1, CommitItem o2) {
                int diff = o1.status.ordinal() - o2.status.ordinal();
                if (diff != 0)
                    return diff;
                return o1.path.compareTo(o2.path);
            }
        });
        this.isAhead = isAhead(remote, repository);
    }

    private boolean isAhead(String remote, Repository repository) {
        if (remote != null && remote.length() > 0) {
            try {
                return EGitUtils.isAhead(repository, remote, new NullProgressMonitor());
            } catch (IOException e) {
                Activator.logError(NLS.bind("Could not check if local branch is ahead of remote {0}", remote), e);
                return false;
            } catch (URISyntaxException e) {
                Activator.logError(NLS.bind("Could not check if local branch is ahead of remote {0}", remote), e);
                return false;
            } catch (InvocationTargetException e) {
                Activator.logError(NLS.bind("Could not check if local branch is ahead of remote {0}", remote), e);
                return false;
            }
        }
        return false;
    }

    /**
     * @return The author to set for the commit
     */
    public String getAuthor() {
        return author;
    }

    /**
     * Pre-set author for the commit
     *
     * @param author
     */
    public void setAuthor(String author) {
        this.author = author;
    }

    /**
     * @return The committer to set for the commit
     */
    public String getCommitter() {
        return committer;
    }

    /**
     * Pre-set committer for the commit
     *
     * @param committer
     */
    public void setCommitter(String committer) {
        this.committer = committer;
    }

    /**
     * @return whether the last commit is to be amended
     */
    public boolean isAmending() {
        return amending;
    }

    /**
     * Pre-set whether the last commit is going to be amended
     *
     * @param amending
     */
    public void setAmending(boolean amending) {
        this.amending = amending;
    }

    /**
     * Set whether the previous commit may be amended
     *
     * @param amendAllowed
     */
    public void setAmendAllowed(boolean amendAllowed) {
        this.amendAllowed = amendAllowed;
    }

    /**
     * Set whether is is allowed to change the set of selected files
     * @param allowToChangeSelection
     */
    public void setAllowToChangeSelection(boolean allowToChangeSelection) {
        this.allowToChangeSelection = allowToChangeSelection;
    }

    /**
     * @return true if a Change-Id line for Gerrit should be created
     */
    public boolean getCreateChangeId() {
        return createChangeId;
    }

    /**
     * @return true if push shall be executed
     */
    public boolean isPushRequested() {
        //      return isPushRequested;
        return canCommit;
    }

    public boolean isPushOnlyRequested() {
        return !canCommit && isAhead;
    }

    private boolean isDirty() {
        return items != null && items.size() > 0;
    }

    @Override
    protected void createButtonsForButtonBar(Composite parent) {
        toolkit.adapt(parent, false, false);
        //      commitAndPushButton = createButton(parent, COMMIT_AND_PUSH_ID,
        //            UIText.CommitDialog_CommitAndPush, false);
        //      commitButton = createButton(parent, IDialogConstants.OK_ID,
        //            UIText.CommitDialog_Commit, true);
        commitButton = createButton(parent, IDialogConstants.OK_ID, "Commit and Publish", true);
        createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
        updateMessage();
    }

    @Override
    protected void buttonPressed(int buttonId) {
        //      if (IDialogConstants.OK_ID == buttonId)
        //         okPressed();
        //      else if (COMMIT_AND_PUSH_ID == buttonId) {
        //         isPushRequested = true;
        //         okPressed();
        super.buttonPressed(buttonId);
    }

    @Override
    protected Control createButtonBar(Composite parent) {
        toolkit.adapt(parent, false, false);
        return super.createButtonBar(parent);
    }

    @Override
    protected Control createHelpControl(Composite parent) {
        toolkit.adapt(parent, false, false);
        Link link = new Link(parent, SWT.WRAP | SWT.NO_FOCUS);
        ((GridLayout) parent.getLayout()).numColumns++;
        link.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER));
        link.setText(UIText.CommitDialog_OpenStagingViewLink);
        link.setToolTipText(UIText.CommitDialog_OpenStagingViewToolTip);
        link.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                openStagingViewLinkClicked();
            }
        });

        toolkit.adapt(link, false, false);
        return link;
    }

    private void openStagingViewLinkClicked() {
        IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
        IWorkbenchPage workbenchPage = workbenchWindow.getActivePage();
        try {
            StagingView view = (StagingView) workbenchPage.showView(StagingView.VIEW_ID);
            view.reload(repository);
            String message = commitMessageComponent.getCommitMessage();
            if (message != null && message.length() > 0)
                view.setCommitMessage(message);
            setReturnCode(CANCEL);
            close();
        } catch (PartInitException e) {
            Activator.handleError(UIText.CommitDialog_OpenStagingViewError, e, true);
        }
    }

    @Override
    protected IDialogSettings getDialogBoundsSettings() {
        IDialogSettings settings = Activator.getDefault().getDialogSettings();
        IDialogSettings section = settings.getSection(DIALOG_SETTINGS_SECTION_NAME);
        if (section == null)
            section = settings.addNewSection(DIALOG_SETTINGS_SECTION_NAME);
        return section;
    }

    /**
     * Add message drop down toolbar item
     *
     * @param parent
     * @return toolbar
     */
    protected ToolBar addMessageDropDown(Composite parent) {
        final ToolBar dropDownBar = new ToolBar(parent, SWT.FLAT | SWT.RIGHT);
        final ToolItem dropDownItem = new ToolItem(dropDownBar, SWT.PUSH);
        dropDownItem.setImage(PlatformUI.getWorkbench().getSharedImages().getImage("IMG_LCL_RENDERED_VIEW_MENU")); //$NON-NLS-1$
        final Menu menu = new Menu(dropDownBar);
        dropDownItem.addDisposeListener(new DisposeListener() {

            @Override
            public void widgetDisposed(DisposeEvent e) {
                menu.dispose();
            }
        });
        MenuItem preferencesItem = new MenuItem(menu, SWT.PUSH);
        preferencesItem.setText(UIText.CommitDialog_ConfigureLink);
        preferencesItem.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                String[] pages = new String[] { UIPreferences.PAGE_COMMIT_PREFERENCES };
                PreferencesUtil.createPreferenceDialogOn(getShell(), pages[0], pages, null).open();
            }

        });
        dropDownItem.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                Rectangle b = dropDownItem.getBounds();
                Point p = dropDownItem.getParent().toDisplay(new Point(b.x, b.y + b.height));
                menu.setLocation(p.x, p.y);
                menu.setVisible(true);
            }

        });
        return dropDownBar;
    }

    @Override
    protected Control createContents(Composite parent) {
        toolkit = new FormToolkit(parent.getDisplay());
        parent.addDisposeListener(new DisposeListener() {

            @Override
            public void widgetDisposed(DisposeEvent e) {
                toolkit.dispose();
            }
        });
        return super.createContents(parent);
    }

    @Override
    protected Control createDialogArea(Composite parent) {
        Composite container = (Composite) super.createDialogArea(parent);
        //      parent.getShell().setText(UIText.CommitDialog_CommitChanges);
        parent.getShell().setText("Publish Changes");

        container = toolkit.createComposite(container);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(container);
        toolkit.paintBordersFor(container);
        GridLayoutFactory.swtDefaults().applyTo(container);

        final SashForm sashForm = new SashForm(container, SWT.VERTICAL | SWT.FILL);
        toolkit.adapt(sashForm, true, true);
        sashForm.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
        createMessageAndPersonArea(sashForm);
        filesSection = createFileSection(sashForm);
        sashForm.setWeights(new int[] { 50, 50 });

        applyDialogFont(container);
        container.pack();
        commitText.setFocus();
        //      Image titleImage = UIIcons.WIZBAN_CONNECT_REPO.createImage();
        //      UIUtils.hookDisposal(parent, titleImage);
        //      setTitleImage(titleImage);
        setTitleImage(ExpressImages.OPENSHIFT_LOGO_WHITE_MEDIUM_IMG);
        //      setTitle(UIText.CommitDialog_Title);
        setTitle("Commit and push local changes to OpenShift");
        setMessage(UIText.CommitDialog_Message, IMessageProvider.INFORMATION);

        filesViewer.addCheckStateListener(new ICheckStateListener() {

            @Override
            public void checkStateChanged(CheckStateChangedEvent event) {
                updateMessage();
            }
        });

        updateFileSectionText();
        return container;
    }

    private Section createFileSection(Composite container) {
        Section filesSection = toolkit.createSection(container,
                ExpandableComposite.TITLE_BAR | ExpandableComposite.CLIENT_INDENT);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(filesSection);
        Composite filesArea = toolkit.createComposite(filesSection);
        filesSection.setClient(filesArea);
        toolkit.paintBordersFor(filesArea);
        GridLayoutFactory.fillDefaults().extendedMargins(2, 2, 2, 2).applyTo(filesArea);

        ToolBar filesToolbar = new ToolBar(filesSection, SWT.FLAT);

        filesSection.setTextClient(filesToolbar);

        PatternFilter patternFilter = new PatternFilter() {
            @Override
            protected boolean isLeafMatch(Viewer viewer, Object element) {
                if (element instanceof CommitItem) {
                    CommitItem commitItem = (CommitItem) element;
                    return wordMatches(commitItem.path);
                }
                return super.isLeafMatch(viewer, element);
            }
        };
        patternFilter.setIncludeLeadingWildcard(true);
        FilteredCheckboxTree resourcesTreeComposite = new FilteredCheckboxTree(filesArea, toolkit,
                SWT.FULL_SELECTION, patternFilter) {
            @Override
            protected WorkbenchJob doCreateRefreshJob() {
                // workaround for file filter not having an explicit change
                // listener
                WorkbenchJob filterJob = super.doCreateRefreshJob();
                filterJob.addJobChangeListener(new JobChangeAdapter() {
                    @Override
                    public void done(IJobChangeEvent event) {
                        if (event.getResult().isOK()) {
                            getDisplay().asyncExec(new Runnable() {
                                @Override
                                public void run() {
                                    updateFileSectionText();
                                }
                            });
                        }
                    }
                });
                return filterJob;
            }
        };
        Tree resourcesTree = resourcesTreeComposite.getViewer().getTree();
        resourcesTree.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER);
        resourcesTreeComposite
                .setLayoutData(GridDataFactory.fillDefaults().hint(600, 200).grab(true, true).create());

        resourcesTree.addSelectionListener(new CommitItemSelectionListener());
        resourcesTree.setHeaderVisible(true);

        TreeColumn statCol = new TreeColumn(resourcesTree, SWT.LEFT);
        statCol.setText(UIText.CommitDialog_Status);
        statCol.setWidth(150);
        statCol.addSelectionListener(new HeaderSelectionListener(CommitItem.Order.ByStatus));

        TreeColumn resourceCol = new TreeColumn(resourcesTree, SWT.LEFT);
        resourceCol.setText(UIText.CommitDialog_Path);
        resourceCol.setWidth(415);
        resourceCol.addSelectionListener(new HeaderSelectionListener(CommitItem.Order.ByFile));

        filesViewer = resourcesTreeComposite.getCheckboxTreeViewer();
        new TreeViewerColumn(filesViewer, statCol).setLabelProvider(createStatusLabelProvider());
        new TreeViewerColumn(filesViewer, resourceCol).setLabelProvider(new CommitPathLabelProvider());
        ColumnViewerToolTipSupport.enableFor(filesViewer);
        filesViewer.setContentProvider(new CommitFileContentProvider());
        filesViewer.setUseHashlookup(true);
        IDialogSettings settings = org.eclipse.egit.ui.Activator.getDefault().getDialogSettings();
        if (settings.get(SHOW_UNTRACKED_PREF) != null) {
            // note, no matter how the dialog settings are, if
            // the preferences force us to include untracked files
            // we must show them
            showUntracked = Boolean.valueOf(settings.get(SHOW_UNTRACKED_PREF)).booleanValue()
                    || getPreferenceStore().getBoolean(UIPreferences.COMMIT_DIALOG_INCLUDE_UNTRACKED);
        }

        filesViewer.addFilter(new CommitItemFilter());
        filesViewer.setInput(items.toArray());
        MenuManager menuManager = new MenuManager();
        menuManager.setRemoveAllWhenShown(true);
        menuManager.addMenuListener(createContextMenuListener());
        filesViewer.getTree().setMenu(menuManager.createContextMenu(filesViewer.getTree()));
        filesViewer.addCheckStateListener(new ICheckStateListener() {

            @Override
            public void checkStateChanged(CheckStateChangedEvent event) {
                updateFileSectionText();
            }
        });

        showUntrackedItem = new ToolItem(filesToolbar, SWT.CHECK);
        Image showUntrackedImage = UIIcons.UNTRACKED_FILE.createImage();
        UIUtils.hookDisposal(showUntrackedItem, showUntrackedImage);
        showUntrackedItem.setImage(showUntrackedImage);
        showUntrackedItem.setToolTipText(UIText.CommitDialog_ShowUntrackedFiles);
        showUntrackedItem.setSelection(showUntracked);
        showUntrackedItem.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                showUntracked = showUntrackedItem.getSelection();
                filesViewer.refresh(true);
                updateFileSectionText();
                updateMessage();
            }

        });

        ToolItem checkAllItem = new ToolItem(filesToolbar, SWT.PUSH);
        Image checkImage = UIIcons.CHECK_ALL.createImage();
        UIUtils.hookDisposal(checkAllItem, checkImage);
        checkAllItem.setImage(checkImage);
        checkAllItem.setToolTipText(UIText.CommitDialog_SelectAll);
        checkAllItem.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                filesViewer.setAllChecked(true);
                updateFileSectionText();
                updateMessage();
            }

        });

        ToolItem uncheckAllItem = new ToolItem(filesToolbar, SWT.PUSH);
        Image uncheckImage = UIIcons.UNCHECK_ALL.createImage();
        UIUtils.hookDisposal(uncheckAllItem, uncheckImage);
        uncheckAllItem.setImage(uncheckImage);
        uncheckAllItem.setToolTipText(UIText.CommitDialog_DeselectAll);
        uncheckAllItem.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                filesViewer.setAllChecked(false);
                updateFileSectionText();
                updateMessage();
            }

        });

        if (!allowToChangeSelection) {
            amendingItem.setSelection(false);
            amendingItem.setEnabled(false);
            showUntrackedItem.setSelection(false);
            showUntrackedItem.setEnabled(false);
            checkAllItem.setEnabled(false);
            uncheckAllItem.setEnabled(false);

            filesViewer.addCheckStateListener(new ICheckStateListener() {

                @Override
                public void checkStateChanged(CheckStateChangedEvent event) {
                    if (!event.getChecked())
                        filesViewer.setAllChecked(true);
                    updateFileSectionText();
                }
            });
            filesViewer.setAllChecked(true);
        } else {
            final boolean includeUntracked = getPreferenceStore()
                    .getBoolean(UIPreferences.COMMIT_DIALOG_INCLUDE_UNTRACKED);
            for (CommitItem item : items) {
                if (!preselectAll && !preselectedFiles.contains(item.path))
                    continue;
                if (item.status == Status.ASSUME_UNCHANGED)
                    continue;
                if (!includeUntracked && item.status == Status.UNTRACKED)
                    continue;
                filesViewer.setChecked(item, true);
            }
        }
        statCol.pack();
        resourceCol.pack();
        return filesSection;
    }

    private Composite createMessageAndPersonArea(Composite container) {

        Composite messageAndPersonArea = toolkit.createComposite(container);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(messageAndPersonArea);
        GridLayoutFactory.swtDefaults().margins(0, 0).spacing(0, 0).applyTo(messageAndPersonArea);

        Section messageSection = toolkit.createSection(messageAndPersonArea,
                ExpandableComposite.TITLE_BAR | ExpandableComposite.CLIENT_INDENT);
        messageSection.setText(UIText.CommitDialog_CommitMessage);
        Composite messageArea = toolkit.createComposite(messageSection);
        GridLayoutFactory.fillDefaults().spacing(0, 0).extendedMargins(2, 2, 2, 2).applyTo(messageArea);
        toolkit.paintBordersFor(messageArea);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(messageSection);
        GridLayoutFactory.swtDefaults().applyTo(messageSection);

        Composite headerArea = new Composite(messageSection, SWT.NONE);
        GridLayoutFactory.fillDefaults().spacing(0, 0).numColumns(2).applyTo(headerArea);

        ToolBar messageToolbar = new ToolBar(headerArea, SWT.FLAT | SWT.HORIZONTAL);
        GridDataFactory.fillDefaults().align(SWT.END, SWT.FILL).grab(true, false).applyTo(messageToolbar);

        addMessageDropDown(headerArea);

        messageSection.setTextClient(headerArea);

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

            @Override
            protected Collection<String> computeMessageProposals() {
                return CommitMessageHistory.getCommitHistory();
            }
        };
        commitText = new CommitMessageArea(messageArea, commitMessage, SWT.NONE) {
            @Override
            protected CommitProposalProcessor getCommitProposalProcessor() {
                return commitProposalProcessor;
            }
        };
        commitText.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
        messageSection.setClient(messageArea);
        Point size = commitText.getTextWidget().getSize();
        int minHeight = commitText.getTextWidget().getLineHeight() * 3;
        commitText.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).hint(size)
                .minSize(size.x, minHeight).align(SWT.FILL, SWT.FILL).create());

        UIUtils.addBulbDecorator(commitText.getTextWidget(), UIText.CommitDialog_ContentAssist);

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

        toolkit.createLabel(personArea, UIText.CommitDialog_Author)
                .setForeground(toolkit.getColors().getColor(IFormColors.TB_TOGGLE));
        authorText = toolkit.createText(personArea, null);
        authorText.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
        authorText.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
        if (repository != null && repository.getRepositoryState().equals(RepositoryState.CHERRY_PICKING_RESOLVED))
            authorText.setEnabled(false);

        toolkit.createLabel(personArea, UIText.CommitDialog_Committer)
                .setForeground(toolkit.getColors().getColor(IFormColors.TB_TOGGLE));
        committerText = toolkit.createText(personArea, null);
        committerText.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
        if (committer != null)
            committerText.setText(committer);

        amendingItem = new ToolItem(messageToolbar, SWT.CHECK);
        amendingItem.setSelection(amending);
        if (amending)
            amendingItem.setEnabled(false); // if already set, don't allow any
        // changes
        else if (!amendAllowed)
            amendingItem.setEnabled(false);
        amendingItem.setToolTipText(UIText.CommitDialog_AmendPreviousCommit);
        Image amendImage = UIIcons.AMEND_COMMIT.createImage();
        UIUtils.hookDisposal(amendingItem, amendImage);
        amendingItem.setImage(amendImage);

        signedOffItem = new ToolItem(messageToolbar, SWT.CHECK);

        signedOffItem.setToolTipText(UIText.CommitDialog_AddSOB);
        Image signedOffImage = UIIcons.SIGNED_OFF.createImage();
        UIUtils.hookDisposal(signedOffItem, signedOffImage);
        signedOffItem.setImage(signedOffImage);

        changeIdItem = new ToolItem(messageToolbar, SWT.CHECK);
        Image changeIdImage = UIIcons.GERRIT.createImage();
        UIUtils.hookDisposal(changeIdItem, changeIdImage);
        changeIdItem.setImage(changeIdImage);
        changeIdItem.setToolTipText(UIText.CommitDialog_AddChangeIdLabel);

        final ICommitMessageComponentNotifications listener = new ICommitMessageComponentNotifications() {

            @Override
            public void updateSignedOffToggleSelection(boolean selection) {
                signedOffItem.setSelection(selection);
            }

            @Override
            public void updateChangeIdToggleSelection(boolean selection) {
                changeIdItem.setSelection(selection);
            }

            @Override
            public void statusUpdated() {
                updateMessage();
            }
        };

        commitMessageComponent = new CommitMessageComponent(repository, listener);
        commitMessageComponent.enableListeners(false);
        commitMessageComponent.setDefaults();
        commitMessageComponent.attachControls(commitText, authorText, committerText);
        commitMessageComponent.setCommitMessage(commitMessage);
        commitMessageComponent.setAuthor(author);
        commitMessageComponent.setCommitter(committer);
        commitMessageComponent.setAmending(amending);
        commitMessageComponent.setFilesToCommit(getFileList());

        amendingItem.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent arg0) {
                commitMessageComponent.setAmendingButtonSelection(amendingItem.getSelection());
            }
        });

        changeIdItem.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent arg0) {
                commitMessageComponent.setChangeIdButtonSelection(changeIdItem.getSelection());
            }
        });

        signedOffItem.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent arg0) {
                commitMessageComponent.setSignedOffButtonSelection(signedOffItem.getSelection());
            }
        });

        commitMessageComponent.updateUI();
        commitMessageComponent.enableListeners(true);

        return messageAndPersonArea;
    }

    private static CellLabelProvider createStatusLabelProvider() {
        CommitStatusLabelProvider baseProvider = new CommitStatusLabelProvider();
        ProblemLabelDecorator decorator = new ProblemLabelDecorator(null);
        return new DecoratingStyledCellLabelProvider(baseProvider, decorator, null) {
            @Override
            public String getToolTipText(Object element) {
                return ((CommitItem) element).status.getText();
            }
        };
    }

    private void updateMessage() {
        if (commitButton == null)
            // Not yet fully initialized.
            return;

        String message = null;
        int type = IMessageProvider.NONE;

        String commitMsg = commitMessageComponent.getCommitMessage();
        if (commitMsg == null || commitMsg.trim().length() == 0) {
            message = UIText.CommitDialog_Message;
            type = IMessageProvider.INFORMATION;
        } else if (!isCommitWithoutFilesAllowed()) {
            message = UIText.CommitDialog_MessageNoFilesSelected;
            type = IMessageProvider.INFORMATION;
        } else {
            CommitStatus status = commitMessageComponent.getStatus();
            message = status.getMessage();
            type = status.getMessageType();
        }

        //      setMessage(message, type);
        //      boolean commitEnabled = type == IMessageProvider.WARNING
        //            || type == IMessageProvider.NONE;
        this.canCommit = type == IMessageProvider.WARNING || type == IMessageProvider.NONE;
        commitButton.setEnabled(canCommit);
        //      commitAndPushButton.setEnabled(commitEnabled);
        if (canCommit) {
            commitButton.setText("Commit and Publish");
            commitButton.setEnabled(canCommit);
        } else {
            if (isAhead) {
                commitButton.setText("Publish Only");
                commitButton.setEnabled(true);
            } else {
                commitButton.setText("Commit and Publish");
                commitButton.setEnabled(false);
            }
        }

        if (!canCommit && isAhead && isDirty()) {
            message += NLS.bind(
                    "\nIf you publish now, uncommitted changes will not be present in your OpenShift application {0}",
                    applicationName);
        }

        setMessage(message, type);
    }

    private boolean isCommitWithoutFilesAllowed() {
        if (filesViewer.getCheckedElements().length > 0)
            return true;

        if (amendingItem.getSelection())
            return true;

        return CommitHelper.isCommitWithoutFilesAllowed(repository);
    }

    private Collection<String> getFileList() {
        Collection<String> result = new ArrayList<>();
        for (CommitItem item : items) {
            result.add(item.path);
        }
        return result;
    }

    private void updateFileSectionText() {
        filesSection.setText(MessageFormat.format(UIText.CommitDialog_Files,
                Integer.valueOf(filesViewer.getCheckedElements().length),
                Integer.valueOf(filesViewer.getTree().getItemCount())));
    }

    private IMenuListener createContextMenuListener() {
        return new IMenuListener() {
            @Override
            public void menuAboutToShow(IMenuManager manager) {
                if (!allowToChangeSelection)
                    return;

                final IStructuredSelection selection = (IStructuredSelection) filesViewer.getSelection();
                if (selection.isEmpty())
                    return;

                if (selection.size() == 1 && selection.getFirstElement() instanceof CommitItem) {
                    CommitItem commitItem = (CommitItem) selection.getFirstElement();
                    manager.add(createCompareAction(commitItem));
                }

                manager.add(createAddAction(selection));
            }
        };
    }

    private Action createCompareAction(final CommitItem commitItem) {
        return new Action(UIText.CommitDialog_CompareWithHeadRevision) {
            @Override
            public void run() {
                compare(commitItem);
            }
        };
    }

    private Action createAddAction(final IStructuredSelection selection) {
        return new Action(UIText.CommitDialog_AddFileOnDiskToIndex) {
            @Override
            public void run() {
                AddCommand addCommand = new Git(repository).add();
                for (Iterator<?> it = selection.iterator(); it.hasNext();) {
                    CommitItem commitItem = (CommitItem) it.next();
                    addCommand.addFilepattern(commitItem.path);
                }
                try {
                    addCommand.call();
                } catch (Exception e) {
                    Activator.logError(UIText.CommitDialog_ErrorAddingFiles, e);
                }
                for (Iterator<?> it = selection.iterator(); it.hasNext();) {
                    CommitItem commitItem = (CommitItem) it.next();
                    try {
                        commitItem.status = getFileStatus(commitItem.path);
                    } catch (IOException e) {
                        Activator.logError(UIText.CommitDialog_ErrorAddingFiles, e);
                    }
                }
                filesViewer.refresh(true);
            }
        };
    }

    private IAction createSelectAction(final IStructuredSelection selection) {
        return new Action(UIText.CommitDialog_SelectForCommit) {
            @Override
            public void run() {
                for (Object item : selection.toList())
                    filesViewer.setChecked(item, true);
            }
        };
    }

    /** Retrieve file status
     * @param path
     * @return file status
     * @throws IOException
     */
    private Status getFileStatus(String path) throws IOException {
        FileTreeIterator fileTreeIterator = new FileTreeIterator(repository);
        IndexDiff indexDiff = new IndexDiff(repository, Constants.HEAD, fileTreeIterator);
        Set<String> repositoryPaths = Collections.singleton(path);
        indexDiff.setFilter(PathFilterGroup.createFromStrings(repositoryPaths));
        indexDiff.diff(null, 0, 0, ""); //$NON-NLS-1$
        return getFileStatus(path, indexDiff);
    }

    /** Retrieve file status from an already calculated IndexDiff
     * @param path
     * @param indexDiff
     * @return file status
     */
    private static Status getFileStatus(String path, IndexDiff indexDiff) {
        if (indexDiff.getAssumeUnchanged().contains(path)) {
            return Status.ASSUME_UNCHANGED;
        } else if (indexDiff.getAdded().contains(path)) {
            // added
            if (indexDiff.getModified().contains(path))
                return Status.ADDED_INDEX_DIFF;
            else
                return Status.ADDED;
        } else if (indexDiff.getChanged().contains(path)) {
            // changed
            if (indexDiff.getModified().contains(path))
                return Status.MODIFIED_INDEX_DIFF;
            else
                return Status.MODIFIED;
        } else if (indexDiff.getUntracked().contains(path)) {
            // untracked
            if (indexDiff.getRemoved().contains(path))
                return Status.REMOVED_UNTRACKED;
            else
                return Status.UNTRACKED;
        } else if (indexDiff.getRemoved().contains(path)) {
            // removed
            return Status.REMOVED;
        } else if (indexDiff.getMissing().contains(path)) {
            // missing
            return Status.REMOVED_NOT_STAGED;
        } else if (indexDiff.getModified().contains(path)) {
            // modified (and not changed!)
            return Status.MODIFIED_NOT_STAGED;
        }
        return Status.UNKNOWN;
    }

    private static int getProblemSeverity(Repository repository, String path) {
        IFile file = ResourceUtil.getFileForLocation(repository, path, false);
        if (file != null) {
            try {
                int severity = file.findMaxProblemSeverity(IMarker.PROBLEM, true, IResource.DEPTH_ONE);
                return severity;
            } catch (CoreException e) {
                // Fall back to below
            }
        }
        return IProblemDecoratable.SEVERITY_NONE;
    }

    @Override
    protected void okPressed() {
        if (canCommit) {
            // we're committing (not only pushing)

            if (!isCommitWithoutFilesAllowed()) {
                MessageDialog.openWarning(getShell(), UIText.CommitDialog_ErrorNoItemsSelected,
                        UIText.CommitDialog_ErrorNoItemsSelectedToBeCommitted);
                return;
            }

            if (!commitMessageComponent.checkCommitInfo())
                return;

            Object[] checkedElements = filesViewer.getCheckedElements();
            selectedFiles.clear();
            for (Object obj : checkedElements)
                selectedFiles.add(((CommitItem) obj).path);

            amending = commitMessageComponent.isAmending();
            commitMessage = commitMessageComponent.getCommitMessage();
            author = commitMessageComponent.getAuthor();
            committer = commitMessageComponent.getCommitter();
            createChangeId = changeIdItem.getSelection();

            IDialogSettings settings = org.eclipse.egit.ui.Activator.getDefault().getDialogSettings();
            settings.put(SHOW_UNTRACKED_PREF, showUntracked);
            CommitMessageHistory.saveCommitHistory(getCommitMessage());
        }
        super.okPressed();
    }

    @Override
    protected int getShellStyle() {
        return super.getShellStyle() | SWT.RESIZE;
    }

    private void compare(CommitItem commitItem) {
        IFile file = findFile(commitItem.path);
        if (file == null || !ResourceUtil.isSharedWithGit(file.getProject())) {
            CompareUtils.compareHeadWithWorkingTree(repository, commitItem.path);
        } else {
            CompareUtils.compareHeadWithWorkspace(repository, file);
        }
    }

    private IFile findFile(String path) {
        return ResourceUtil.getFileForLocation(repository, path, false);
    }
}

class CommitItem implements IProblemDecoratable {

    Status status;

    String path;

    boolean submodule;

    int problemSeverity;

    @Override
    public int getProblemSeverity() {
        return problemSeverity;
    }

    /** The ordinal of this {@link Enum} is used to provide the "native" sorting of the list */
    public static enum Status {
        /** */
        ADDED(UIText.CommitDialog_StatusAdded),
        /** */
        MODIFIED(UIText.CommitDialog_StatusModified),
        /** */
        REMOVED(UIText.CommitDialog_StatusRemoved),
        /** */
        ADDED_INDEX_DIFF(UIText.CommitDialog_StatusAddedIndexDiff),
        /** */
        MODIFIED_INDEX_DIFF(UIText.CommitDialog_StatusModifiedIndexDiff),
        /** */
        MODIFIED_NOT_STAGED(UIText.CommitDialog_StatusModifiedNotStaged),
        /** */
        REMOVED_NOT_STAGED(UIText.CommitDialog_StatusRemovedNotStaged),
        /** */
        UNTRACKED(UIText.CommitDialog_StatusUntracked),
        /** */
        REMOVED_UNTRACKED(UIText.CommitDialog_StatusRemovedUntracked),
        /** */
        ASSUME_UNCHANGED(UIText.CommitDialog_StatusAssumeUnchaged),
        /** */
        UNKNOWN(UIText.CommitDialog_StatusUnknown);

        public String getText() {
            return myText;
        }

        private final String myText;

        private Status(String text) {
            myText = text;
        }
    }

    public static enum Order implements Comparator<CommitItem> {
        ByStatus() {

            @Override
            public int compare(CommitItem o1, CommitItem o2) {
                return o1.status.compareTo(o2.status);
            }

        },

        ByFile() {

            @Override
            public int compare(CommitItem o1, CommitItem o2) {
                return o1.path.compareTo(o2.path);
            }

        };

        public Comparator<CommitItem> ascending() {
            return this;
        }

        public Comparator<CommitItem> descending() {
            return Collections.reverseOrder(this);
        }
    }
}

class CommitViewerComparator extends ViewerComparator {

    private Comparator<CommitItem> itemComparator;

    public CommitViewerComparator(Comparator<CommitItem> comparator) {
        this.itemComparator = comparator;
    }

    @Override
    public int compare(Viewer viewer, Object e1, Object e2) {
        return itemComparator.compare((CommitItem) e1, (CommitItem) e2);
    }

}