com.microsoft.tfs.client.common.ui.teamexplorer.helpers.PendingChangesHelpers.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.tfs.client.common.ui.teamexplorer.helpers.PendingChangesHelpers.java

Source

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the repository root.

package com.microsoft.tfs.client.common.ui.teamexplorer.helpers;

import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;

import com.microsoft.tfs.checkinpolicies.ExtensionPointPolicyLoader;
import com.microsoft.tfs.client.common.codemarker.CodeMarker;
import com.microsoft.tfs.client.common.codemarker.CodeMarkerDispatch;
import com.microsoft.tfs.client.common.commands.vc.PromoteCandidateChangesCommand;
import com.microsoft.tfs.client.common.commands.vc.QueryShelvesetsCommand;
import com.microsoft.tfs.client.common.commands.vc.ScanLocalWorkspaceCommand;
import com.microsoft.tfs.client.common.connectionconflict.ConnectionConflictHandler;
import com.microsoft.tfs.client.common.framework.command.ICommandExecutor;
import com.microsoft.tfs.client.common.framework.resources.ResourceType;
import com.microsoft.tfs.client.common.repository.RepositoryConflictException;
import com.microsoft.tfs.client.common.repository.RepositoryManager;
import com.microsoft.tfs.client.common.repository.TFSRepository;
import com.microsoft.tfs.client.common.ui.Messages;
import com.microsoft.tfs.client.common.ui.TFSCommonUIClientPlugin;
import com.microsoft.tfs.client.common.ui.compare.BaselineItemByPendingChangeGenerator;
import com.microsoft.tfs.client.common.ui.compare.ServerItemByItemVersionGenerator;
import com.microsoft.tfs.client.common.ui.compare.TFSItemContentComparator;
import com.microsoft.tfs.client.common.ui.compare.UserPreferenceExternalCompareHandler;
import com.microsoft.tfs.client.common.ui.controls.vc.changes.ChangeItem;
import com.microsoft.tfs.client.common.ui.controls.vc.changes.ChangeItemType;
import com.microsoft.tfs.client.common.ui.controls.vc.changes.RepositoryChangeItemProvider;
import com.microsoft.tfs.client.common.ui.controls.vc.checkin.actions.UndoPendingChangesAction;
import com.microsoft.tfs.client.common.ui.controls.vc.checkin.actions.UndoUnchangedPendingChangesAction;
import com.microsoft.tfs.client.common.ui.dialogs.vc.PromoteCandidateChangesDialog;
import com.microsoft.tfs.client.common.ui.dialogs.vc.ShelvesetDetailsDialog;
import com.microsoft.tfs.client.common.ui.dialogs.workspaces.WorkspacesDialog;
import com.microsoft.tfs.client.common.ui.framework.command.UICommandExecutorFactory;
import com.microsoft.tfs.client.common.ui.framework.compare.Compare;
import com.microsoft.tfs.client.common.ui.framework.compare.CompareUIType;
import com.microsoft.tfs.client.common.ui.framework.compare.CustomCompareConfiguration;
import com.microsoft.tfs.client.common.ui.helpers.ToggleMessageHelper;
import com.microsoft.tfs.client.common.ui.helpers.UIConnectionPersistence;
import com.microsoft.tfs.client.common.ui.prefs.UIPreferenceConstants;
import com.microsoft.tfs.client.common.ui.tasks.vc.ConflictResolutionTask;
import com.microsoft.tfs.client.common.ui.tasks.vc.UnshelveTask;
import com.microsoft.tfs.client.common.ui.teamexplorer.internal.pendingchanges.PendingChangesViewModel;
import com.microsoft.tfs.core.checkinpolicies.PolicyEvaluator;
import com.microsoft.tfs.core.clients.versioncontrol.GatedCheckinUtils;
import com.microsoft.tfs.core.clients.versioncontrol.PropertyConstants;
import com.microsoft.tfs.core.clients.versioncontrol.PropertyUtils;
import com.microsoft.tfs.core.clients.versioncontrol.VersionControlConstants;
import com.microsoft.tfs.core.clients.versioncontrol.WorkspaceLocation;
import com.microsoft.tfs.core.clients.versioncontrol.exceptions.ServerPathFormatException;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.ChangeType;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Changeset;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.CheckinNote;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.ItemType;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.PendingChange;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Shelveset;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.WorkItemCheckinInfo;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace;
import com.microsoft.tfs.core.clients.versioncontrol.specs.WorkspaceSpec;
import com.microsoft.tfs.core.clients.versioncontrol.specs.version.ChangesetVersionSpec;
import com.microsoft.tfs.core.clients.versioncontrol.specs.version.LatestVersionSpec;
import com.microsoft.tfs.core.clients.versioncontrol.specs.version.WorkspaceVersionSpec;
import com.microsoft.tfs.core.clients.workitem.WorkItem;
import com.microsoft.tfs.core.clients.workitem.WorkItemClient;
import com.microsoft.tfs.core.exceptions.TECoreException;
import com.microsoft.tfs.core.pendingcheckin.PendingCheckin;
import com.microsoft.tfs.core.pendingcheckin.StandardPendingCheckin;
import com.microsoft.tfs.jni.FileSystemUtils;
import com.microsoft.tfs.util.Check;

public class PendingChangesHelpers {
    public static final CodeMarker BEFORE_CONFIRM_DIALOG = new CodeMarker(
            "com.microsoft.tfs.client.common.ui.views.PendingChangesView#beforeConfirmDialog"); //$NON-NLS-1$

    public static void unshelve(final Shell shell, final TFSRepository repository) {
        final UnshelveTask task = new UnshelveTask(shell, repository);
        task.run();
    }

    public static void resolveConflicts(final Shell shell, final TFSRepository repository) {
        final ConflictResolutionTask resolveTask = new ConflictResolutionTask(shell, repository, null);
        resolveTask.run();
    }

    public static void undoAll(final Shell shell, final TFSRepository repository) {
        final RepositoryChangeItemProvider provider = new RepositoryChangeItemProvider(repository);
        UndoPendingChangesAction.undoPendingChanges(shell, repository, provider.getChangeItems());
    }

    public static void undoUnchangedPendingChanges(final Shell shell, final TFSRepository repository) {
        final RepositoryChangeItemProvider provider = new RepositoryChangeItemProvider(repository);
        UndoUnchangedPendingChangesAction.undoUnchangedChanges(shell, repository, provider.getChangeItems());
    }

    public static void evaluatePolicies(final Shell shell, final PendingChangesViewModel model) {
        final PendingCheckin pendingCheckin = getPendingCheckin(shell, model, false);
        if (pendingCheckin == null) {
            model.clearPolicyWarnings();
            return;
        }

        model.evaluateCheckinPolicies(pendingCheckin);
    }

    public static void detectLocalChanges(final Shell shell, final TFSRepository repository) {
        Check.isTrue(repository.getWorkspace().getLocation() == WorkspaceLocation.LOCAL, "local workspaces only"); //$NON-NLS-1$

        final ICommandExecutor executor = UICommandExecutorFactory.newUICommandExecutor(shell);
        final IStatus status = executor.execute(new ScanLocalWorkspaceCommand(repository));

        if (!status.isOK()) {
            // TODO: NYI
        }
    }

    public static void showShelvesetDetails(final Shell shell, final TFSRepository repository,
            final String shelvesetName) {
        final String fallbackUser = repository.getVersionControlClient().getConnection().getAuthorizedAccountName();
        final WorkspaceSpec spec = WorkspaceSpec.parse(shelvesetName, fallbackUser);

        final QueryShelvesetsCommand queryCommand = new QueryShelvesetsCommand(repository.getVersionControlClient(),
                spec.getName(), spec.getOwner());

        final ICommandExecutor executor = UICommandExecutorFactory.newUICommandExecutor(shell);
        final IStatus status = executor.execute(queryCommand);

        if (status.isOK()) {
            final Shelveset[] shelvesets = queryCommand.getShelvesets();
            if (shelvesets.length > 0) {
                final ShelvesetDetailsDialog detailsDialog = new ShelvesetDetailsDialog(shell, shelvesets[0],
                        repository, false);
                detailsDialog.open();
            }
        }
    }

    public static boolean canCompareWithWorkspaceVersion(final TFSRepository repository,
            final PendingChange pendingChange) {
        if (pendingChange.getItemType() != ItemType.FILE) {
            return false;
        }

        if (containsSymlinkChange(pendingChange)) {
            return false;
        }

        String mappedLocalPath;
        try {
            mappedLocalPath = repository.getWorkspace().getMappedLocalPath(pendingChange.getServerItem());
        } catch (final ServerPathFormatException e) {
            return false;
        }
        if (mappedLocalPath == null || !new File(mappedLocalPath).exists()) {
            return false;
        }

        if (pendingChange.getChangeType().contains(ChangeType.DELETE)
                || pendingChange.getChangeType().contains(ChangeType.ADD)) {
            return false;
        }

        if (pendingChange.getChangeType().contains(ChangeType.BRANCH)
                && !pendingChange.getChangeType().contains(ChangeType.EDIT)) {
            return false;
        }

        return true;
    }

    public static boolean canCompareWithLatestVersion(final PendingChange pendingChange) {
        if (pendingChange.getItemType() != ItemType.FILE) {
            return false;
        }

        if (containsSymlinkChange(pendingChange)) {
            return false;
        }

        if (pendingChange.getChangeType().contains(ChangeType.DELETE)
                || pendingChange.getChangeType().contains(ChangeType.ADD)) {
            return false;
        }

        /*
         * Note: Microsoft's implementation disables this action when the change
         * type contains Rename or Branch. I think it makes more sense to enable
         * it under these conditions, as long as there is also an Edit.
         */

        if (pendingChange.getChangeType().contains(ChangeType.RENAME)
                && !pendingChange.getChangeType().contains(ChangeType.EDIT)) {
            return false;
        }

        if (pendingChange.getChangeType().contains(ChangeType.BRANCH)
                && !pendingChange.getChangeType().contains(ChangeType.EDIT)) {
            return false;
        }

        /* Ensure that the local file exists. */
        if (!new File(pendingChange.getLocalItem()).exists()) {
            return false;
        }

        return true;
    }

    public static boolean containsSymlinkChange(final PendingChange pendingChange) {
        if (pendingChange == null || pendingChange.getLocalItem() == null) {
            return false;
        }

        final boolean symlink = PropertyConstants.IS_SYMLINK.equals(
                PropertyUtils.selectMatching(pendingChange.getPropertyValues(), PropertyConstants.SYMBOLIC_KEY))
                || FileSystemUtils.getInstance().getAttributes(pendingChange.getLocalItem()).isSymbolicLink();
        return symlink;
    }

    public static void compareWithLatestVersion(final Shell shell, final TFSRepository repository,
            final PendingChange pendingChange, final CompareUIType compareUIType) {
        final Compare compare = new Compare();

        compare.setModifiedLocalPath(pendingChange.getLocalItem(), ResourceType.FILE);

        compare.setOriginal(new ServerItemByItemVersionGenerator(repository, pendingChange.getServerItem(),
                new WorkspaceVersionSpec(repository.getWorkspace()), LatestVersionSpec.INSTANCE));

        compare.setAncestor(new ServerItemByItemVersionGenerator(repository, pendingChange.getServerItem(),
                new WorkspaceVersionSpec(repository.getWorkspace()),
                new ChangesetVersionSpec(pendingChange.getVersion())));

        compare.addComparator(TFSItemContentComparator.INSTANCE);

        compare.getCompareConfiguration().setProperty(CustomCompareConfiguration.LEFT_LABEL_SUFFIX_PROPERTY,
                Messages.getString("ComparePendingChangeWithLatestVersionAction.LocalSuffix")); //$NON-NLS-1$

        compare.setUIType(compareUIType);

        compare.setExternalCompareHandler(new UserPreferenceExternalCompareHandler(shell));
        compare.open();
    }

    public static void compareWithWorkspaceVersion(final Shell shell, final TFSRepository repository,
            final PendingChange pendingChange, final CompareUIType compareUIType) {
        final Compare compare = new Compare();

        compare.setModifiedLocalPath(pendingChange.getLocalItem(), ResourceType.FILE);

        if (repository.getWorkspace().getLocation() == WorkspaceLocation.LOCAL) {
            /*
             * For a local workspace, use a generator which doesn't query the
             * server to resolve the workspace version into a changeset version,
             * because we probably have a local baseline file to get content
             * from. It's possible the baseline file is missing, and the server
             * will be contacted in that case.
             */
            compare.setOriginal(new BaselineItemByPendingChangeGenerator(repository.getWorkspace(), pendingChange));
        } else {
            compare.setOriginal(new ServerItemByItemVersionGenerator(repository, pendingChange.getServerItem(),
                    new WorkspaceVersionSpec(repository.getWorkspace()),
                    new WorkspaceVersionSpec(repository.getWorkspace())));
        }

        compare.addComparator(TFSItemContentComparator.INSTANCE);

        compare.getCompareConfiguration().setProperty(CustomCompareConfiguration.LEFT_LABEL_SUFFIX_PROPERTY,
                Messages.getString("ComparePendingChangeWithWorkspaceVersionAction.LocalSuffix")); //$NON-NLS-1$

        compare.setUIType(compareUIType);

        compare.setExternalCompareHandler(new UserPreferenceExternalCompareHandler(shell));
        compare.open();
    }

    public static void manageWorkspaces(final Shell shell) {
        final RepositoryManager repositoryManager = TFSCommonUIClientPlugin.getDefault().getProductPlugin()
                .getRepositoryManager();

        final WorkspacesDialog dialog = new WorkspacesDialog(shell,
                repositoryManager.getDefaultRepository().getWorkspace().getClient().getConnection(), false, false,
                Messages.getString("WorkspaceToolbarPulldownAction.WorkspacesDialogTitle")); //$NON-NLS-1$

        /*
         * Don't allow the user to modify some data about the active workspace
         * (name, server, owner, etc.). The user can still always modify working
         * folder mappings.
         */
        final TFSRepository[] immutableRepositories = TFSCommonUIClientPlugin.getDefault().getProductPlugin()
                .getRepositoryManager().getRepositories();

        if (immutableRepositories != null && immutableRepositories.length > 0) {
            /* Convert to Workspaces */
            final Workspace[] immutableWorkspaces = new Workspace[immutableRepositories.length];

            for (int i = 0; i < immutableRepositories.length; i++) {
                immutableWorkspaces[i] = immutableRepositories[i].getWorkspace();
            }

            dialog.getWorkspacesControl().setImmutableWorkspaces(immutableWorkspaces);
        }

        dialog.open();
    }

    public static void showPromoteCandidateChanges(final Shell shell, final TFSRepository repository,
            final ChangeItem[] candidates) {
        Check.notNull(candidates, "candidates"); //$NON-NLS-1$

        final PromoteCandidateChangesDialog dialog = new PromoteCandidateChangesDialog(shell, repository,
                candidates);

        if (dialog.open() == IDialogConstants.OK_ID) {
            final PromoteCandidateChangesCommand command = new PromoteCandidateChangesCommand(repository,
                    ChangeItem.getPendingChanges(dialog.getCheckedCandidates()));

            UICommandExecutorFactory.newUICommandExecutor(shell).execute(command);
        }
    }

    public static boolean confirmShelvesetCanBeWritten(final Shell shell, final TFSRepository repository,
            final String shelvesetName) {
        final QueryShelvesetsCommand queryCommand = new QueryShelvesetsCommand(repository.getVersionControlClient(),
                shelvesetName, VersionControlConstants.AUTHENTICATED_USER);

        final IStatus queryStatus = UICommandExecutorFactory.newUICommandExecutor(shell).execute(queryCommand);

        if (!queryStatus.isOK()) {
            return false;
        }

        final Shelveset[] existingShelvesets = queryCommand.getShelvesets();

        if (existingShelvesets != null && existingShelvesets.length > 0) {
            final String title = Messages.getString("ShelveDialog.OverwriteDialogTitle"); //$NON-NLS-1$
            final String messageFormat = Messages.getString("ShelveDialog.OverwriteDialogTextFormat"); //$NON-NLS-1$
            final String message = MessageFormat.format(messageFormat, shelvesetName);

            if (!MessageDialog.openQuestion(shell, title, message)) {
                return false;
            }
        }

        return true;
    }

    public static PendingCheckin getPendingCheckin(final Shell shell, final PendingChangesViewModel model,
            final boolean evaluatePolicies) {
        // Warn if there are on Included pending changes.
        final PendingChange[] changes = model.getIncludedUnfilteredPendingChanges();
        if (changes.length == 0) {
            MessageDialog.openWarning(shell, Messages.getString("PendingChangesView.NoChangesDialogTitle"), //$NON-NLS-1$
                    Messages.getString("PendingChangesView.NoChangesDialogText")); //$NON-NLS-1$
            return null;
        }

        final TFSRepository repository = model.getRepository();
        final Workspace workspace = model.getWorkspace();
        final PendingChange[] allChanges = model.getAllPendingChanges();

        final PolicyEvaluator policyEvaluator = evaluatePolicies
                ? new PolicyEvaluator(repository.getVersionControlClient(), new ExtensionPointPolicyLoader())
                : null;

        return new StandardPendingCheckin(workspace, allChanges, changes, model.getComment(),
                model.getCheckinNote(), model.getAssociatedWorkItems(), policyEvaluator);
    }

    public static boolean confirmCheckin(final Shell shell, final int changesCount) {
        String message;
        if (changesCount > 1) {
            final String messageFormat = Messages.getString("PendingChangesView.MultiChangesConfirmTextFormat"); //$NON-NLS-1$
            message = MessageFormat.format(messageFormat, changesCount);
        } else {
            message = Messages.getString("PendingChangesView.SingleChangeConfirmTextFormat"); //$NON-NLS-1$
        }

        CodeMarkerDispatch.dispatch(BEFORE_CONFIRM_DIALOG);
        if (!ToggleMessageHelper.openYesNoQuestion(shell,
                Messages.getString("PendingChangesView.ConfirmCheckinDialogTitle"), //$NON-NLS-1$
                message, Messages.getString("PendingChangesView.CheckinWithoutPromptCheckboxText"), //$NON-NLS-1$
                false, UIPreferenceConstants.PROMPT_BEFORE_CHECKIN)) {
            return false;
        }

        return true;
    }

    /**
     * Call after a gated build changeset has been reconciled (unmodified local
     * changes undone and new items fetched). Clears the comment, associated
     * work items, and notes if and only if the committed changeset and
     * associated items exactly matches the control's current state.
     *
     * @param changeset
     *        the committed changeset (must not be <code>null</code>)
     * @param associatedWorkItems
     *        the associated work items for the changeset (must not be
     *        <code>null</code>)
     */
    public static void afterReconcileGatedCheckin(final Changeset changeset, final WorkItem[] associatedWorkItems,
            final CheckinNote noteBeforeUndo) {
        Check.notNull(changeset, "changeset"); //$NON-NLS-1$
        Check.notNull(associatedWorkItems, "associatedWorkItems"); //$NON-NLS-1$
        Check.notNull(noteBeforeUndo, "noteBeforeUndo"); //$NON-NLS-1$

        final PendingChangesViewModel model = TFSCommonUIClientPlugin.getDefault().getPendingChangesViewModel();

        final boolean commentsMatch = GatedCheckinUtils.gatedCheckinCommentsMatch(changeset.getComment(),
                model.getComment());

        final boolean checkinNotesMatch = GatedCheckinUtils.gatedCheckinNotesMatch(changeset.getCheckinNote(),
                noteBeforeUndo);

        final int[] committedWorkItemIds = getIDsForWorkItems(associatedWorkItems);

        final WorkItemCheckinInfo[] workItemInfos = model.getAssociatedWorkItems();
        final int[] pendingWorkItemIds = getIDsForWorkItemCheckinInfos(workItemInfos);

        final boolean workItemsMatch = GatedCheckinUtils.gatedCheckinWorkItemsMatch(committedWorkItemIds,
                pendingWorkItemIds);

        if (commentsMatch && checkinNotesMatch && workItemsMatch) {
            // Resets the comments, notes, and work items
            model.clearComment();
            model.dissociateAllWorkItems();
            model.clearCheckinNotes();
        }
    }

    /**
     * Call after unshelving changes. Updates teh comment, associated work
     * items, and notes from the shelveset.
     *
     * @param shelveset
     *        the shelveset to update with (must not be <code>null</code>)
     */
    public static void afterUnshelve(final Shelveset shelveset) {
        Check.notNull(shelveset, "shelveset"); //$NON-NLS-1$

        final PendingChangesViewModel model = TFSCommonUIClientPlugin.getDefault().getPendingChangesViewModel();

        final WorkItemClient workItemClient = model.getWorkspace().getClient().getConnection().getWorkItemClient();

        model.setComment(shelveset.getComment());
        model.associateWorkItems(shelveset.getWorkItemInfo(workItemClient));
        model.setCheckinNoteFieldValues(shelveset.getCheckinNote().getValues());
    }

    public static ChangeItem[] pendingChangesToChangeItems(final TFSRepository repository,
            final PendingChange[] pendingChanges) {
        final List<ChangeItem> changes = new ArrayList<ChangeItem>();

        for (final PendingChange pendingChange : pendingChanges) {
            changes.add(new ChangeItem(pendingChange, ChangeItemType.PENDING, repository));
        }

        return changes.toArray(new ChangeItem[changes.size()]);
    }

    public static int[] getIDsForWorkItemCheckinInfos(final WorkItemCheckinInfo[] workItemInfos) {
        final int[] ids = new int[workItemInfos.length];
        for (int i = 0; i < workItemInfos.length; i++) {
            ids[i] = workItemInfos[i].getWorkItem().getID();
        }
        return ids;
    }

    public static int[] getIDsForWorkItems(final WorkItem[] workItems) {
        final int[] ids = new int[workItems.length];
        for (int i = 0; i < workItems.length; i++) {
            ids[i] = workItems[i].getID();
        }
        return ids;
    }

    public static void switchWorkspace(final Shell shell, final Workspace workspace) {
        final RepositoryManager repositoryManager = TFSCommonUIClientPlugin.getDefault().getProductPlugin()
                .getRepositoryManager();

        final ConnectionConflictHandler connectionConflictHandler = TFSCommonUIClientPlugin.getDefault()
                .getProductPlugin().getConnectionConflictHandler();

        /*
         * Ensure the workspace exists.
         */
        try {
            TFSCommonUIClientPlugin.getDefault().getProductPlugin().getServerManager().getDefaultServer()
                    .getConnection().getVersionControlClient()
                    .queryWorkspace(workspace.getName(), workspace.getOwnerName());
        } catch (final TECoreException e) {
            final String message = MessageFormat.format(
                    Messages.getString("WorkspaceToolbarPulldownAction.WorkspaceDoesNotExistFormat"), //$NON-NLS-1$
                    new WorkspaceSpec(workspace.getOwnerName(), workspace.getOwnerDisplayName()).toString());

            ErrorDialog.openError(shell, Messages.getString("WorkspaceToolbarPulldownAction.WorkspaceNotFound"), //$NON-NLS-1$
                    null, new Status(Status.WARNING, TFSCommonUIClientPlugin.PLUGIN_ID, 0, message, null));

            return;
        }

        /*
         * Switch to the selected workspace.
         */
        try {
            repositoryManager.getOrCreateRepository(workspace);
        } catch (final RepositoryConflictException conflictException) {
            /*
             * Another connection to a server already exists: allow the product
             * plugin's connection conflict handler to retry this.
             */
            if (connectionConflictHandler.resolveRepositoryConflict()) {
                /* Retry */
                try {
                    repositoryManager.getOrCreateRepository(workspace);
                } catch (final RepositoryConflictException f) {
                    connectionConflictHandler.notifyRepositoryConflict();
                }
            }
        }

        UIConnectionPersistence.getInstance().setLastUsedWorkspace(workspace);
    }

    public static boolean isPendingAdd(final TFSRepository repository, final String localPath) {
        final PendingChange pendingChange = repository.getPendingChangeCache()
                .getPendingChangeByLocalPath(localPath);
        return pendingChange != null && pendingChange.getChangeType().contains(ChangeType.ADD);
    }
}