/* * Copyright 2000-2009 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.community.intellij.plugins.communitycase; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vfs.VirtualFile; import org.community.intellij.plugins.communitycase.commands.Command; import org.community.intellij.plugins.communitycase.commands.SimpleHandler; import org.community.intellij.plugins.communitycase.config.ConfigUtil; import org.community.intellij.plugins.communitycase.config.VcsSettings; import org.community.intellij.plugins.communitycase.history.HistoryUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.regex.Pattern; /** * This data class represents a branch */ public class Branch extends Reference { @NonNls public static final String NO_BRANCH_NAME = "(no branch)"; // The name that specifies that is on specific commit rather then on some branch ({@value}) @NonNls public static final String REFS_HEADS_PREFIX = "/main/"; // Prefix for local branches ({@value}) @NonNls public static final String REFS_REMOTES_PREFIX = "refs/remotes/"; // Prefix for remote branches ({@value}) private final boolean myRemote; private final boolean myActive; public Branch(@NotNull String name, boolean active, boolean remote) { super(name); myRemote = remote; myActive = active; } /** * @return true if the branch is remote */ public boolean isRemote() { return myRemote; } /** * @return true if the branch is active */ public boolean isActive() { return myActive; } @NotNull public String getFullName() { return (myRemote ? REFS_REMOTES_PREFIX : REFS_HEADS_PREFIX) + myName; } /** * Get tracked remote for the branch * * @param project the context project * @param root the VCS root to investigate * @return the remote name for tracked branch, "." meaning the current repository, or null if no branch is tracked * @throws VcsException if there is a problem with running version control */ @Nullable public String getTrackedRemoteName(Project project, VirtualFile root) throws VcsException { return ConfigUtil.getValue(project, root, trackedRemoteKey()); } /** * Get tracked the branch * * @param project the context project * @param root the VCS root to investigate * @return the name of tracked branch * @throws com.intellij.openapi.vcs.VcsException if there is a problem with running version control */ @Nullable public String getTrackedBranchName(Project project, VirtualFile root) throws VcsException { return ConfigUtil.getValue(project, root, trackedBranchKey()); } /** * Get current branch from . * * @param project a project * @param root vcs root * @return the current branch or null if there is no current branch or if specific commit has been checked out. * @throws com.intellij.openapi.vcs.VcsException if there is a problem running */ @Nullable public static Branch current(Project project, VirtualFile root) throws VcsException { return list(project, root, false, false, null, null); } /** * List branches for the root as strings. * @see #list(com.intellij.openapi.project.Project, com.intellij.openapi.vfs.VirtualFile, boolean, boolean, java.util.Collection, String) */ @Nullable public static Branch listAsStrings(final Project project, final VirtualFile root, final boolean remote, final boolean local, final Collection<String> branches, @Nullable final String containingCommit) throws VcsException { final Collection<Branch> Branches = new ArrayList<Branch>(); final Branch result = list(project, root, local, remote, Branches, containingCommit); for (Branch b : Branches) { branches.add(b.getName()); } return result; } /** * List branches in the repository. Supply a Collection to this method, and it will be filled by branches. * @param project the context project * @param root the root * @param localWanted should local branches be collected. * @param remoteWanted should remote branches be collected. * @param branches the collection which will be used to store branches. * Can be null - then the method does the same as {@link #current(com.intellij.openapi.project.Project, com.intellij.openapi.vfs.VirtualFile)} * @param containingCommit show only branches which contain the specified commit. If null, no commit filtering is performed. * @return current branch. May be null if no branch is active. * @throws com.intellij.openapi.vcs.VcsException if there is a problem with running */ @Nullable public static Branch list(final Project project, final VirtualFile root, final boolean localWanted, final boolean remoteWanted, @Nullable final Collection<Branch> branches, @Nullable final String containingCommit) throws VcsException { // preparing native command executor final SimpleHandler handler = new SimpleHandler(project, root, Command.BRANCH); handler.setRemote(true); handler.setSilent(true); //handler.addParameters("--no-color"); if (remoteWanted && localWanted) { // handler.addParameters("-a"); } else if (remoteWanted) { // handler.addParameters("-r"); } if (containingCommit != null) { // handler.addParameters("--contains", containingCommit); } final String output = handler.run(); if (output.trim().length() == 0) { // the case after init and before first commit - there is no branch and no output, and we'll take refs/heads/master String head; try { head = new String(FileUtil.loadFileText(new File(root.getPath(), "./HEAD"), Util.UTF8_ENCODING)).trim(); final String prefix = "ref: refs/heads/"; return head.startsWith(prefix) ? new Branch(head.substring(prefix.length()), true, false) : null; } catch (IOException e) { return null; } } // standard situation. output example: // master //* my_feature // remotes/origin/eap // remotes/origin/feature // remotes/origin/master // also possible: //* (no branch) final String[] split = output.split("\n"); Branch currentBranch = null; VcsSettings settings=VcsSettings.getInstance(project); String branchFilter=null; if(settings!=null) branchFilter=settings.getBranchFilter(); for (String b : split) { if(branchFilter==null || branchFilter.isEmpty() || Pattern.matches(branchFilter,b)) { final Branch branch = new Branch(b, false, false); //currentBranch = branch; if (branches != null) { branches.add(branch); } } } return null; } /** * Set tracked branch * * @param project the context project * @param root the root * @param remote the remote to track (null, for do not track anything, "." for local repository) * @param branch the branch to track */ public void setTrackedBranch(Project project, VirtualFile root, String remote, String branch) throws VcsException { if (remote == null || branch == null) { ConfigUtil.unsetValue(project, root, trackedRemoteKey()); ConfigUtil.unsetValue(project, root, trackedBranchKey()); } else { ConfigUtil.setValue(project, root, trackedRemoteKey(), remote); ConfigUtil.setValue(project, root, trackedBranchKey(), branch); } } /** * @return the key for the remote of the tracked branch */ private String trackedBranchKey() { return "branch." + getName() + ".merge"; } /** * @return the key for the tracked branch */ private String trackedRemoteKey() { return "branch." + getName() + ".remote"; } /** * Get tracked branch for the current branch * * @param project the project * @param root the vcs root * @return the tracked branch * @throws com.intellij.openapi.vcs.VcsException if there is a problem with accessing configuration file */ @Nullable public Branch tracked(Project project, VirtualFile root) throws VcsException { String remote = getTrackedRemoteName(project, root); if (remote == null) { return null; } String branch = getTrackedBranchName(project, root); if (branch == null) { return null; } if (branch.startsWith(REFS_HEADS_PREFIX)) { branch = branch.substring(REFS_HEADS_PREFIX.length()); } boolean remoteFlag; if (!".".equals(remote)) { branch = remote + "/" + branch; remoteFlag = true; } else { remoteFlag = false; } return new Branch(branch, false, remoteFlag); } /** * Get a merge base between the current branch and specified branch. * * @param project the current project * @param root the vcs root * @param branch the branch * @return the common commit or null if the there is no common commit * @throws com.intellij.openapi.vcs.VcsException the exception */ @Nullable public VcsRevisionNumber getMergeBase(@NotNull Project project, @NotNull VirtualFile root, @NotNull Branch branch) throws VcsException { SimpleHandler h = new SimpleHandler(project, root, Command.MERGE_BASE); h.setRemote(true); h.setSilent(true); h.addParameters(this.getFullName(), branch.getFullName()); String output = h.run().trim(); if (output.length() == 0) { return null; } else { return HistoryUtils.validateRevisionNumber(output); } } }