[log] Prohibit actions for external log

Check if the repository is external to the project, and don't allow
such actions as Cherry-Pick, Reset, New Branch.

Additionally rewrite the Cherry-Pick action update() not to consider
the old log.
This commit is contained in:
Kirill Likhodedov
2014-10-15 16:54:02 +04:00
parent 81f1c4217b
commit 0fc1981bbe
8 changed files with 99 additions and 132 deletions
@@ -39,6 +39,8 @@ public interface RepositoryManager<T extends Repository> {
@Nullable
T getRepositoryForRoot(@Nullable VirtualFile root);
boolean isExternal(@NotNull T repository);
/**
* Returns the {@link Repository} which the given file belongs to, or {@code null} if the file is not under any Git or Hg repository.
*/
@@ -27,12 +27,10 @@ import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsKey;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.StatusBar;
@@ -44,8 +42,6 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.storage.HeavyProcessLatch;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.vcs.log.TimedVcsCommit;
import com.intellij.vcs.log.VcsLog;
import com.intellij.vcs.log.VcsLogProvider;
import org.intellij.images.editor.ImageFileEditor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -142,18 +138,6 @@ public class DvcsUtil {
repositoryManager.getRepositories()));
}
/**
* Checks if there are hg roots in the VCS log.
*/
public static boolean logHasRootForVcs(@NotNull VcsLog log, @Nullable final VcsKey vcsKey) {
return ContainerUtil.find(log.getLogProviders(), new Condition<VcsLogProvider>() {
@Override
public boolean value(VcsLogProvider logProvider) {
return logProvider.getSupportedVcs().equals(vcsKey);
}
}) != null;
}
@Nullable
public static String joinMessagesOrNull(@NotNull Collection<String> messages) {
String joined = StringUtil.join(messages, "\n");
@@ -105,6 +105,17 @@ public abstract class AbstractRepositoryManager<T extends Repository> extends Ab
}
}
@Override
public boolean isExternal(@NotNull T repository) {
try {
REPO_LOCK.readLock().lock();
return !myRepositories.containsValue(repository) && myExternalRepositories.containsValue(repository);
}
finally {
REPO_LOCK.readLock().unlock();
}
}
@Override
@Nullable
public T getRepositoryForFile(@NotNull VirtualFile file) {
@@ -15,12 +15,16 @@
*/
package com.intellij.dvcs.ui;
import com.intellij.dvcs.repo.AbstractRepositoryManager;
import com.intellij.dvcs.repo.Repository;
import com.intellij.dvcs.repo.RepositoryManager;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.vcs.log.VcsFullCommitDetails;
import com.intellij.vcs.log.VcsLog;
@@ -33,7 +37,7 @@ import java.util.List;
public abstract class VcsLogAction<Repo extends Repository> extends DumbAwareAction {
@Override
public void actionPerformed(AnActionEvent e) {
public void actionPerformed(@NotNull AnActionEvent e) {
Project project = e.getRequiredData(CommonDataKeys.PROJECT);
VcsLog log = e.getRequiredData(VcsLogDataKeys.VCS_LOG);
List<VcsFullCommitDetails> details = log.getSelectedDetails();
@@ -43,7 +47,7 @@ public abstract class VcsLogAction<Repo extends Repository> extends DumbAwareAct
}
@Override
public void update(AnActionEvent e) {
public void update(@NotNull AnActionEvent e) {
Project project = e.getProject();
VcsLog log = e.getData(VcsLogDataKeys.VCS_LOG);
if (project == null || log == null) {
@@ -57,7 +61,7 @@ public abstract class VcsLogAction<Repo extends Repository> extends DumbAwareAct
e.getPresentation().setEnabledAndVisible(false);
}
else {
e.getPresentation().setVisible(true);
e.getPresentation().setVisible(isVisible(project, grouped));
e.getPresentation().setEnabled(!grouped.isEmpty() && isEnabled(grouped));
}
}
@@ -66,6 +70,19 @@ public abstract class VcsLogAction<Repo extends Repository> extends DumbAwareAct
protected abstract boolean isEnabled(@NotNull MultiMap<Repo, VcsFullCommitDetails> grouped);
protected boolean isVisible(@NotNull final Project project, @NotNull MultiMap<Repo, VcsFullCommitDetails> grouped) {
return ContainerUtil.and(grouped.keySet(), new Condition<Repo>() {
@Override
public boolean value(Repo repo) {
RepositoryManager<Repo> manager = getRepositoryManager(project);
return !manager.isExternal(repo);
}
});
}
@NotNull
protected abstract AbstractRepositoryManager<Repo> getRepositoryManager(@NotNull Project project);
@Nullable
protected abstract Repo getRepositoryForRoot(@NotNull Project project, @NotNull VirtualFile root);
@@ -15,6 +15,7 @@
*/
package git4idea.actions;
import com.intellij.dvcs.repo.AbstractRepositoryManager;
import com.intellij.dvcs.ui.VcsLogSingleCommitAction;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
@@ -26,10 +27,16 @@ import org.jetbrains.annotations.Nullable;
public abstract class GitLogSingleCommitAction extends VcsLogSingleCommitAction<GitRepository> {
@NotNull
@Override
protected AbstractRepositoryManager<GitRepository> getRepositoryManager(@NotNull Project project) {
return ServiceManager.getService(project, GitRepositoryManager.class);
}
@Override
@Nullable
protected GitRepository getRepositoryForRoot(@NotNull Project project, @NotNull VirtualFile root) {
return ServiceManager.getService(project, GitRepositoryManager.class).getRepositoryForRoot(root);
return getRepositoryManager(project).getRepositoryForRoot(root);
}
}
@@ -15,8 +15,9 @@
*/
package git4idea.cherrypick;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.dvcs.repo.RepositoryManager;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
@@ -25,9 +26,7 @@ import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.util.Function;
import com.intellij.openapi.util.Condition;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.*;
import git4idea.GitLocalBranch;
@@ -35,13 +34,9 @@ import git4idea.GitPlatformFacade;
import git4idea.GitVcs;
import git4idea.commands.Git;
import git4idea.config.GitVcsSettings;
import git4idea.history.browser.GitHeavyCommit;
import git4idea.history.wholeTree.AbstractHash;
import git4idea.history.wholeTree.GitCommitDetailsProvider;
import git4idea.repo.GitRepository;
import icons.Git4ideaIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -66,12 +61,9 @@ public class GitCherryPickAction extends DumbAwareAction {
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = e.getProject();
final List<? extends VcsFullCommitDetails> commits = getSelectedCommits(e);
if (project == null || commits == null || commits.isEmpty()) {
LOG.info(String.format("Cherry-pick action should be disabled. Project: %s, commits: %s", project, commits));
return;
}
final Project project = e.getRequiredData(CommonDataKeys.PROJECT);
VcsLog log = e.getRequiredData(VcsLogDataKeys.VCS_LOG);
final List<? extends VcsFullCommitDetails> commits = log.getSelectedDetails();
for (VcsFullCommitDetails commit : commits) {
myIdsInProgress.add(commit.getId());
@@ -136,24 +128,47 @@ public class GitCherryPickAction extends DumbAwareAction {
}
@Override
public void update(AnActionEvent e) {
public void update(@NotNull AnActionEvent e) {
super.update(e);
VcsLog log = getVcsLog(e);
VcsLog log = e.getData(VcsLogDataKeys.VCS_LOG);
Project project = getEventProject(e);
if (project == null || log == null || !DvcsUtil.logHasRootForVcs(log, GitVcs.getKey())) {
if (project == null || log == null || !logHasGitRoots(log)) {
e.getPresentation().setEnabledAndVisible(false);
return;
}
else {
e.getPresentation().setEnabled(enabled(e));
e.getPresentation().setText(isAutoCommit(project) ? NAME : NAME + "...");
List<VcsFullCommitDetails> details = log.getSelectedDetails();
if (notFromGitAndProject(project, details)) {
e.getPresentation().setEnabledAndVisible(false);
return;
}
e.getPresentation().setEnabled(enabled(project, log, details));
e.getPresentation().setText(isAutoCommit(project) ? NAME : NAME + "...");
}
private boolean enabled(AnActionEvent e) {
final List<? extends VcsFullCommitDetails> commits = getSelectedCommits(e);
final Project project = e.getProject();
public static boolean logHasGitRoots(@NotNull VcsLog log) {
return ContainerUtil.find(log.getLogProviders(), new Condition<VcsLogProvider>() {
@Override
public boolean value(VcsLogProvider logProvider) {
return logProvider.getSupportedVcs().equals(GitVcs.getKey());
}
}) != null;
}
if (commits == null || commits.isEmpty() || project == null) {
private boolean notFromGitAndProject(@NotNull Project project, @NotNull List<VcsFullCommitDetails> details) {
final RepositoryManager<GitRepository> manager = myPlatformFacade.getRepositoryManager(project);
return ContainerUtil.and(details, new Condition<VcsFullCommitDetails>() {
@Override
public boolean value(VcsFullCommitDetails commit) {
GitRepository repository = manager.getRepositoryForRoot(commit.getRoot());
return repository != null && manager.isExternal(repository);
}
});
}
private boolean enabled(@NotNull Project project, @NotNull VcsLog log, @NotNull List<VcsFullCommitDetails> commits) {
if (commits.isEmpty()) {
return false;
}
@@ -162,11 +177,9 @@ public class GitCherryPickAction extends DumbAwareAction {
return false;
}
GitRepository repository = myPlatformFacade.getRepositoryManager(project).getRepositoryForRoot(commit.getRoot());
if (repository == null) {
return false;
}
assert repository != null;
GitLocalBranch currentBranch = repository.getCurrentBranch();
Collection<String> containingBranches = getContainingBranches(e, commit, repository);
Collection<String> containingBranches = log.getContainingBranches(commit.getId());
if (currentBranch != null && containingBranches != null && containingBranches.contains(currentBranch.getName())) {
// already is contained in the current branch
return false;
@@ -175,85 +188,4 @@ public class GitCherryPickAction extends DumbAwareAction {
return true;
}
// TODO remove after removing the old Vcs Log implementation
@Nullable
private List<? extends VcsFullCommitDetails> getSelectedCommits(AnActionEvent e) {
final Project project = e.getProject();
if (project == null) {
return null;
}
List<GitHeavyCommit> commits = e.getData(GitVcs.SELECTED_COMMITS);
if (commits != null) {
return convertHeavyCommitToFullDetails(commits, project);
}
final VcsLog log = getVcsLog(e);
if (log == null) {
return null;
}
List<Hash> selectedCommits = log.getSelectedCommits();
List<VcsFullCommitDetails> selectedDetails = ContainerUtil.newArrayList();
for (Hash commit : selectedCommits) {
VcsFullCommitDetails details = log.getDetailsIfAvailable(commit);
if (details == null) { // let the action be unavailable until all details are loaded
return null;
}
GitRepository root = myPlatformFacade.getRepositoryManager(project).getRepositoryForRoot(details.getRoot());
// don't allow to cherry-pick if a non-Git commit was selected
// we could cherry-pick just Git commits filtered from the list, but it might provide confusion
if (root == null) {
return null;
}
selectedDetails.add(details);
}
return selectedDetails;
}
private static List<? extends VcsFullCommitDetails> convertHeavyCommitToFullDetails(List<GitHeavyCommit> commits, final Project project) {
return ContainerUtil.map(commits, new Function<GitHeavyCommit, VcsFullCommitDetails>() {
@Override
public VcsFullCommitDetails fun(GitHeavyCommit commit) {
final VcsLogObjectsFactory factory = ServiceManager.getService(project, VcsLogObjectsFactory.class);
List<Hash> parents = ContainerUtil.map(commit.getParentsHashes(), new Function<String, Hash>() {
@Override
public Hash fun(String hashValue) {
return factory.createHash(hashValue);
}
});
final List<Change> changes = commit.getChanges();
return factory.createFullDetails(
factory.createHash(commit.getHash().getValue()), parents, commit.getAuthorTime(), commit.getRoot(), commit.getSubject(),
commit.getAuthor(), commit.getAuthorEmail(), commit.getDescription(), commit.getCommitter(), commit.getCommitterEmail(),
commit.getDate().getTime(), new ThrowableComputable<Collection<Change>, Exception>() {
@Override
public Collection<Change> compute() throws Exception {
return changes;
}
}
);
}
});
}
private static VcsLog getVcsLog(@NotNull AnActionEvent event) {
return event.getData(VcsLogDataKeys.VCS_LOG);
}
// TODO remove after removing the old Vcs Log implementation
@Nullable
private static Collection<String> getContainingBranches(AnActionEvent event, VcsFullCommitDetails commit, GitRepository repository) {
GitCommitDetailsProvider detailsProvider = event.getData(GitVcs.COMMIT_DETAILS_PROVIDER);
if (detailsProvider != null) {
return detailsProvider.getContainingBranches(repository.getRoot(), AbstractHash.create(commit.getId().toShortString()));
}
if (event.getProject() == null) {
return null;
}
VcsLog log = getVcsLog(event);
if (log == null) {
return null;
}
return log.getContainingBranches(commit.getId());
}
}
@@ -15,7 +15,9 @@
*/
package git4idea.reset;
import com.intellij.dvcs.repo.AbstractRepositoryManager;
import com.intellij.dvcs.ui.VcsLogOneCommitPerRepoAction;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
@@ -24,15 +26,20 @@ import com.intellij.util.ObjectUtils;
import com.intellij.vcs.log.VcsFullCommitDetails;
import git4idea.config.GitVcsSettings;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import static git4idea.GitUtil.getRepositoryManager;
public class GitResetAction extends VcsLogOneCommitPerRepoAction<GitRepository> {
@NotNull
@Override
protected AbstractRepositoryManager<GitRepository> getRepositoryManager(@NotNull Project project) {
return ServiceManager.getService(project, GitRepositoryManager.class);
}
@Nullable
@Override
protected GitRepository getRepositoryForRoot(@NotNull Project project, @NotNull VirtualFile root) {
@@ -16,6 +16,7 @@
package org.zmlx.hg4idea.action;
import com.intellij.dvcs.repo.AbstractRepositoryManager;
import com.intellij.dvcs.ui.VcsLogSingleCommitAction;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
@@ -27,10 +28,16 @@ import org.zmlx.hg4idea.repo.HgRepositoryManager;
public abstract class HgLogSingleCommitAction extends VcsLogSingleCommitAction<HgRepository> {
@NotNull
@Override
protected AbstractRepositoryManager<HgRepository> getRepositoryManager(@NotNull Project project) {
return ServiceManager.getService(project, HgRepositoryManager.class);
}
@Nullable
@Override
protected HgRepository getRepositoryForRoot(@NotNull Project project, @NotNull VirtualFile root) {
return ServiceManager.getService(project, HgRepositoryManager.class).getRepositoryForRoot(root);
return getRepositoryManager(project).getRepositoryForRoot(root);
}
}