com.microsoft.alm.plugin.idea.common.utils.VcsHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.alm.plugin.idea.common.utils.VcsHelper.java

Source

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

package com.microsoft.alm.plugin.idea.common.utils;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.microsoft.alm.common.utils.ArgumentHelper;
import com.microsoft.alm.plugin.context.RepositoryContext;
import com.microsoft.alm.plugin.context.RepositoryContextManager;
import com.microsoft.alm.plugin.external.commands.FindWorkspaceCommand;
import com.microsoft.alm.plugin.external.models.Workspace;
import com.microsoft.alm.plugin.idea.git.utils.TfGitHelper;
import com.microsoft.alm.plugin.idea.tfvc.core.TFSVcs;
import git4idea.GitUtil;
import git4idea.GitVcs;
import git4idea.branch.GitBranchUtil;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class VcsHelper {
    private static final Logger logger = LoggerFactory.getLogger(VcsHelper.class);

    public static final String GIT_BRANCH_PREFIX = "refs/heads/";
    public static final String TFVC_ROOT = "$/";
    public static final String TFVC_SEPARATOR = "/";

    private static Pattern pattern = null;

    /**
     * Use this method to see if the given project is using Git as its version control system.
     */
    public static boolean isGitVcs(final Project project) {
        ArgumentHelper.checkNotNull(project, "project");
        final ProjectLevelVcsManager projectLevelVcsManager = ProjectLevelVcsManager.getInstance(project);
        return projectLevelVcsManager.checkVcsIsActive(GitVcs.NAME);
    }

    /**
     * Use this method to see if the given project is using TFVC as its version control system.
     */
    public static boolean isTfVcs(final Project project) {
        ArgumentHelper.checkNotNull(project, "project");
        final ProjectLevelVcsManager projectLevelVcsManager = ProjectLevelVcsManager.getInstance(project);
        return projectLevelVcsManager.checkVcsIsActive(TFSVcs.TFVC_NAME);
    }

    /**
     * Returns the Git repository object for the project or null if this is not a Git repo project.
     *
     * @param project
     * @return
     */
    public static GitRepository getGitRepository(final Project project) {
        if (isGitVcs(project)) {
            final GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
            GitRepository repository = manager.getRepositoryForRoot(project.getBaseDir());

            // in the case where the base dir of the Git repo and the base dir of IDEA project don't match this can be null
            if (repository == null) {
                final List<GitRepository> repos = manager.getRepositories();
                if (repos.size() > 0) {
                    repository = repos.get(0);
                    if (repos.size() > 1) {
                        logger.warn("More than 1 Git repo was found. Defaulting to the first returned: "
                                + repository.getRoot().getPath());
                    }
                } else {
                    logger.warn(
                            "We are in a Git project that does not have any Git repos. (We may be asking too early.)");
                }
            }

            return repository;
        }
        return null;
    }

    /**
     * This method creates a RepositoryContext object from the local project context.
     * It works for TF Git or TFVC repositories. Any other type of repo will return null.
     *
     * @param project
     * @return
     */
    public static RepositoryContext getRepositoryContext(final Project project) {
        ArgumentHelper.checkNotNull(project, "project");
        try {
            final String projectRootFolder = project.getBasePath();

            // Check the manager first since that's where we cache these things
            //TODO this cache doesn't include the current branch info that could have changed. We should probably only cache stuff for TFVC
            RepositoryContext context = RepositoryContextManager.getInstance().get(projectRootFolder);
            if (context != null) {
                logger.info("getRepositoryContext: cache hit: " + projectRootFolder);
                return context;
            }
            logger.info("getRepositoryContext: cache miss: " + projectRootFolder);

            final ProjectLevelVcsManager projectLevelVcsManager = ProjectLevelVcsManager.getInstance(project);
            // Check for Git, then TFVC
            if (projectLevelVcsManager.checkVcsIsActive(GitVcs.NAME)) {
                // It's Git, so get the repository and remote url to create the context from
                final GitRepository repository = getGitRepository(project);
                if (repository != null && TfGitHelper.isTfGitRepository(repository)) {
                    final GitRemote gitRemote = TfGitHelper.getTfGitRemote(repository);
                    final String gitRemoteUrl = gitRemote.getFirstUrl();
                    // TODO: Fix this HACK. There doesn't seem to be a clear way to get the full name of the current branch
                    final String branch = GIT_BRANCH_PREFIX + GitBranchUtil.getDisplayableBranchText(repository);
                    context = RepositoryContext.createGitContext(projectRootFolder, repository.getRoot().getName(),
                            branch, gitRemoteUrl);
                }
            } else if (projectLevelVcsManager.checkVcsIsActive(TFSVcs.TFVC_NAME)) {
                // It's TFVC so run the FindWorkspace command to get the workspace object which as the server info
                final FindWorkspaceCommand command = new FindWorkspaceCommand(projectRootFolder);
                final Workspace workspace = command.runSynchronously();
                if (workspace != null) {
                    final String projectName = getTeamProjectFromTfvcServerPath(
                            workspace.getMappings().size() > 0 ? workspace.getMappings().get(0).getServerPath()
                                    : null);
                    context = RepositoryContext.createTfvcContext(projectRootFolder, workspace.getName(),
                            projectName, workspace.getServer());
                }
            }

            if (context != null) {
                RepositoryContextManager.getInstance().add(context);
                return context;
            }
        } catch (Throwable t) {
            // Don't let errors bubble out here, just return null if something goes wrong
            logger.warn("Unable to get repository context for the project.", t);
        }

        logger.info("getRepositoryContext: We couldn't determine the VCS provider, so returning null.");
        return null;
    }

    /**
     * Use this method to get the team project name from a TFVC server path.
     * The team project name is always the first folder in the path.
     * If no team project name is found an empty string is returned.
     *
     * @param serverPath
     * @return
     */
    public static String getTeamProjectFromTfvcServerPath(final String serverPath) {
        if (StringUtils.isNotEmpty(serverPath) && StringUtils.startsWith(serverPath, TFVC_ROOT)
                && serverPath.length() > 2) {
            // Find the next separator after the $/
            final int index = serverPath.indexOf(TFVC_SEPARATOR, 2);
            if (index >= 0) {
                return serverPath.substring(2, index);
            } else {
                return serverPath.substring(2);
            }
        }

        logger.info("getTeamProjectFromTfvcServerPath: No project was found.");
        return StringUtils.EMPTY;
    }

    public static List<Integer> getWorkItemIdsFromMessage(final String commitMessage) {
        logger.info("getWorkItemIdsFromMessage: commitMessage = " + commitMessage);
        final List<Integer> workItems = new ArrayList<Integer>(10);
        // We cache the compiled pattern for later use
        if (pattern == null) {
            pattern = Pattern.compile("#(\\d+)");
        }
        final Matcher matcher = pattern.matcher(commitMessage);
        // finds all matches in the string where it is a # followed by an int
        while (matcher.find()) {
            try {
                final int workItemId = Integer.parseInt(StringUtils.removeStart(matcher.group(), "#"));
                workItems.add(workItemId);
            } catch (NumberFormatException e) {
                logger.warn("Error converting work item id into integer: " + matcher.group(1));
            }
        }
        return workItems;
    }
}