From 9bc60ea17f8958283796d9d3443ca23fd2cd20a8 Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Mon, 19 Dec 2016 18:59:04 +0300 Subject: [PATCH] [vcs]: implement favourite-branches serialization * add git realization --- .../intellij/dvcs/branch/BranchStorage.java | 66 ++++++++++++++++ .../intellij/dvcs/branch/DvcsBranchInfo.java | 40 ++++++++++ .../intellij/dvcs/branch/DvcsBranchUtil.java | 47 +++++++++++ .../src/git4idea/branch/GitBranchType.java | 21 +++++ .../src/git4idea/config/GitVcsSettings.java | 79 +++++++++++-------- .../ui/branch/GitBranchPopupActions.java | 32 ++++++++ 6 files changed, 252 insertions(+), 33 deletions(-) create mode 100644 platform/dvcs-impl/src/com/intellij/dvcs/branch/BranchStorage.java create mode 100644 platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchInfo.java create mode 100644 platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchUtil.java create mode 100644 plugins/git4idea/src/git4idea/branch/GitBranchType.java diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/branch/BranchStorage.java b/platform/dvcs-impl/src/com/intellij/dvcs/branch/BranchStorage.java new file mode 100644 index 000000000000..856633d674fd --- /dev/null +++ b/platform/dvcs-impl/src/com/intellij/dvcs/branch/BranchStorage.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2016 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 com.intellij.dvcs.branch; + +import com.intellij.dvcs.repo.Repository; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.xmlb.annotations.MapAnnotation; +import com.intellij.util.xmlb.annotations.Property; +import com.intellij.util.xmlb.annotations.Tag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +import static com.intellij.dvcs.branch.DvcsBranchUtil.find; +import static com.intellij.util.containers.ContainerUtil.newArrayList; + +@Tag("branch-storage") +public class BranchStorage { + + @Property(surroundWithTag = false) + @MapAnnotation(keyAttributeName = "type") + @NotNull public Map> myBranches = ContainerUtil.newHashMap(); + + public BranchStorage() { + } + + public boolean contains(@NotNull String typeName, @Nullable Repository repository, @NotNull String branchName) { + List branches = myBranches.get(typeName); + return branches != null && find(branches, repository, branchName) != null; + } + + public void add(@NotNull String typeName, @Nullable Repository repository, + @NotNull String branchName) { + if (contains(typeName, repository, branchName)) return; + List branchInfos = myBranches.computeIfAbsent(typeName, name -> newArrayList()); + branchInfos.add(new DvcsBranchInfo(DvcsBranchUtil.getPathFor(repository), branchName)); + } + + public void remove(@NotNull String typeName, @Nullable Repository repository, + @NotNull String branchName) { + + List branches = myBranches.get(typeName); + DvcsBranchInfo toDelete = find(branches, repository, branchName); + if (toDelete != null) { + branches.remove(toDelete); + if (branches.isEmpty()) { + myBranches.remove(typeName); + } + } + } +} diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchInfo.java b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchInfo.java new file mode 100644 index 000000000000..2f5c02fd7c49 --- /dev/null +++ b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchInfo.java @@ -0,0 +1,40 @@ +package com.intellij.dvcs.branch; + +import com.intellij.util.xmlb.annotations.Attribute; +import com.intellij.util.xmlb.annotations.Tag; + +import java.util.Objects; + +@Tag("branch-info") +public class DvcsBranchInfo { + @Attribute(value = "repo") public final String repoPath; + @Attribute(value = "source") public final String sourceName; + + @SuppressWarnings("unused") + public DvcsBranchInfo() { + this("", ""); + } + + public DvcsBranchInfo(String repositoryPath, String source) { + repoPath = repositoryPath; + sourceName = source; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DvcsBranchInfo info = (DvcsBranchInfo)o; + + if (repoPath != null ? !repoPath.equals(info.repoPath) : info.repoPath != null) return false; + if (sourceName != null ? !sourceName.equals(info.sourceName) : info.sourceName != null) return false; + + return true; + } + + @Override + public int hashCode() { + return Objects.hash(repoPath, sourceName); + } +} \ No newline at end of file diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchUtil.java b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchUtil.java new file mode 100644 index 000000000000..66c5d37bcd02 --- /dev/null +++ b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchUtil.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2016 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 com.intellij.dvcs.branch; + +import com.intellij.dvcs.repo.Repository; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +public class DvcsBranchUtil { + + + @Nullable + public static T find(@Nullable final Collection branches, + @Nullable Repository repository, + @NotNull String sourceBranch) { + if (branches == null) return null; + return ContainerUtil.find(branches, targetInfo -> repoAndSourceAreEqual(repository, sourceBranch, targetInfo)); + } + + private static boolean repoAndSourceAreEqual(@Nullable Repository repository, + @NotNull String sourceBranch, + @NotNull DvcsBranchInfo targetInfo) { + return getPathFor(repository).equals(targetInfo.repoPath) && StringUtil.equals(targetInfo.sourceName, sourceBranch); + } + + @NotNull + public static String getPathFor(@Nullable Repository repository) { + return repository == null ? "" : repository.getRoot().getPath(); + } +} diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchType.java b/plugins/git4idea/src/git4idea/branch/GitBranchType.java new file mode 100644 index 000000000000..cc9a23a5b650 --- /dev/null +++ b/plugins/git4idea/src/git4idea/branch/GitBranchType.java @@ -0,0 +1,21 @@ +/* + * Copyright 2000-2016 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 git4idea.branch; + + +public enum GitBranchType { + GIT_LOCAL, GIT_REMOTE +} diff --git a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java index 984b005295ad..2db8117aad56 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java +++ b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java @@ -15,7 +15,10 @@ */ package git4idea.config; +import com.intellij.dvcs.branch.BranchStorage; +import com.intellij.dvcs.branch.DvcsBranchInfo; import com.intellij.dvcs.branch.DvcsSyncSettings; +import com.intellij.dvcs.repo.Repository; import com.intellij.lifecycle.PeriodicalTasksCloser; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; @@ -30,16 +33,18 @@ import com.intellij.util.xmlb.annotations.Attribute; import com.intellij.util.xmlb.annotations.Tag; import git4idea.GitRemoteBranch; import git4idea.GitUtil; +import git4idea.branch.GitBranchType; import git4idea.push.GitPushTagMode; import git4idea.repo.GitRemote; import git4idea.repo.GitRepository; import git4idea.reset.GitResetMode; -import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; +import static com.intellij.dvcs.branch.DvcsBranchUtil.find; + /** * Git VCS settings */ @@ -83,6 +88,9 @@ public class GitVcsSettings implements PersistentStateComponent PUSH_TARGETS = ContainerUtil.newArrayList(); + + @Tag("favourite-branches") + public BranchStorage FAVOURITE_BRANCHES = new BranchStorage(); } public GitVcsSettings(GitVcsApplicationSettings appSettings) { @@ -265,50 +273,37 @@ public class GitVcsSettings implements PersistentStateComponent iterator = myState.PUSH_TARGETS.iterator(); - PushTargetInfo targetInfo = find(iterator, repository, sourceBranch); - if (targetInfo == null) { - return null; - } + PushTargetInfo targetInfo = find(myState.PUSH_TARGETS, repository, sourceBranch); + if (targetInfo == null) return null; GitRemote remote = GitUtil.findRemoteByName(repository, targetInfo.targetRemoteName); - if (remote == null) { - return null; - } + if (remote == null) return null; return GitUtil.findOrCreateRemoteBranch(repository, remote, targetInfo.targetBranchName); } public void setPushTarget(@NotNull GitRepository repository, @NotNull String sourceBranch, @NotNull String targetRemote, @NotNull String targetBranch) { String repositoryPath = repository.getRoot().getPath(); - List targets = new ArrayList<>(myState.PUSH_TARGETS); - Iterator iterator = targets.iterator(); - PushTargetInfo existingInfo = find(iterator, repository, sourceBranch); + PushTargetInfo existingInfo = find(myState.PUSH_TARGETS, repository, sourceBranch); if (existingInfo != null) { - iterator.remove(); + myState.PUSH_TARGETS.remove(existingInfo); } - PushTargetInfo newInfo = new PushTargetInfo(repositoryPath, sourceBranch, targetRemote, targetBranch); - targets.add(newInfo); - myState.PUSH_TARGETS = targets; + myState.PUSH_TARGETS.add(new PushTargetInfo(repositoryPath, sourceBranch, targetRemote, targetBranch)); } - @Nullable - @Contract(pure = false) - private static PushTargetInfo find(@NotNull Iterator iterator, - @NotNull GitRepository repository, - @NotNull String sourceBranch) { - while (iterator.hasNext()) { - PushTargetInfo targetInfo = iterator.next(); - if (targetInfo.repoPath.equals(repository.getRoot().getPath()) && targetInfo.sourceName.equals(sourceBranch)) { - return targetInfo; - } - } - return null; + public void addToFavourites(@NotNull GitBranchType type, @Nullable GitRepository repository, @NotNull String branchName) { + myState.FAVOURITE_BRANCHES.add(type.toString(), repository, branchName); + } + + public void removeFromFavourites(@NotNull GitBranchType type, @Nullable GitRepository repository, @NotNull String branchName) { + myState.FAVOURITE_BRANCHES.remove(type.toString(), repository, branchName); + } + + public boolean isFavourite(@NotNull GitBranchType type, @Nullable Repository repository, @NotNull String branchName) { + return myState.FAVOURITE_BRANCHES.contains(type.toString(), repository, branchName); } @Tag("push-target-info") - private static class PushTargetInfo { - @Attribute(value = "repo") public String repoPath; - @Attribute(value = "source") public String sourceName; + private static class PushTargetInfo extends DvcsBranchInfo { @Attribute(value = "target-remote") public String targetRemoteName; @Attribute(value = "target-branch") public String targetBranchName; @@ -318,10 +313,28 @@ public class GitVcsSettings implements PersistentStateComponent myRepositories; private final String myBranchName; @NotNull private final GitRepository mySelectedRepository; @@ -152,7 +156,9 @@ class GitBranchPopupActions { myRepositories = repositories; myBranchName = branchName; mySelectedRepository = selectedRepository; + myGitVcsSettings = GitVcsSettings.getInstance(project); getTemplatePresentation().setText(calcBranchText(), false); // no mnemonics + setFavourite(myGitVcsSettings.isFavourite(GIT_LOCAL, repositories.size() > 1 ? null : mySelectedRepository, myBranchName)); } @NotNull @@ -191,6 +197,17 @@ class GitBranchPopupActions { return new GitMultiRootBranchConfig(myRepositories).getTrackedBranch(myBranchName); } + @Override + public void toggle() { + super.toggle(); + if (isFavourite()) { + myGitVcsSettings.addToFavourites(GIT_LOCAL, myRepositories.size() > 1 ? null : mySelectedRepository, myBranchName); + } + else { + myGitVcsSettings.removeFromFavourites(GIT_LOCAL, myRepositories.size() > 1 ? null : mySelectedRepository, myBranchName); + } + } + static class CheckoutAction extends DumbAwareAction { private final Project myProject; private final List myRepositories; @@ -288,17 +305,32 @@ class GitBranchPopupActions { static class RemoteBranchActions extends BranchActionGroup { private final Project myProject; + private final GitVcsSettings myGitVcsSettings; private final List myRepositories; private final String myBranchName; @NotNull private final GitRepository mySelectedRepository; RemoteBranchActions(@NotNull Project project, @NotNull List repositories, @NotNull String branchName, @NotNull GitRepository selectedRepository) { + myProject = project; myRepositories = repositories; myBranchName = branchName; mySelectedRepository = selectedRepository; + myGitVcsSettings = GitVcsSettings.getInstance(project); getTemplatePresentation().setText(myBranchName, false); // no mnemonics + setFavourite(myGitVcsSettings.isFavourite(GIT_REMOTE, repositories.size() > 1 ? null : mySelectedRepository, myBranchName)); + } + + @Override + public void toggle() { + super.toggle(); + if (isFavourite()) { + myGitVcsSettings.addToFavourites(GIT_REMOTE, myRepositories.size() > 1 ? null : mySelectedRepository, myBranchName); + } + else { + myGitVcsSettings.removeFromFavourites(GIT_REMOTE, myRepositories.size() > 1 ? null : mySelectedRepository, myBranchName); + } } @NotNull