From 5ee51dc9c691a2cd019debed5117dc6ba24a4dd8 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Mon, 24 Jun 2013 17:35:28 +0400 Subject: [PATCH] [git] Synchronize GitRepository data update, notify only when needed. Encapsulate all repository data (current branch, remotes, branches, etc.) to GitRepoInfo; store the reference in a volatile field; just change the reference on update(). Otherwise the information could have been in an inconsistent state (e.g. current branch might have been deleted after it was included into myBranches). Check the state change before notification, not to notify after each update() even if nothing changed. --- .../src/git4idea/repo/GitRepoInfo.java | 117 ++++++++++++++++++ .../src/git4idea/repo/GitRepository.java | 3 + .../src/git4idea/repo/GitRepositoryImpl.java | 92 ++++++++++---- 3 files changed, 187 insertions(+), 25 deletions(-) create mode 100644 plugins/git4idea/src/git4idea/repo/GitRepoInfo.java diff --git a/plugins/git4idea/src/git4idea/repo/GitRepoInfo.java b/plugins/git4idea/src/git4idea/repo/GitRepoInfo.java new file mode 100644 index 000000000000..ea774663488d --- /dev/null +++ b/plugins/git4idea/src/git4idea/repo/GitRepoInfo.java @@ -0,0 +1,117 @@ +/* + * Copyright 2000-2013 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.repo; + +import com.intellij.dvcs.repo.Repository; +import git4idea.GitLocalBranch; +import git4idea.GitRemoteBranch; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +/** + * @author Kirill Likhodedov + */ +public class GitRepoInfo { + + @Nullable private final GitLocalBranch myCurrentBranch; + @Nullable private final String myCurrentRevision; + @NotNull private final Repository.State myState; + @NotNull private final Collection myRemotes; + @NotNull private final Collection myLocalBranches; + @NotNull private final Collection myRemoteBranches; + @NotNull private final Collection myBranchTrackInfos; + + public GitRepoInfo(@Nullable GitLocalBranch currentBranch, @Nullable String currentRevision, @NotNull Repository.State state, + @NotNull Collection remotes, @NotNull Collection localBranches, + @NotNull Collection remoteBranches, @NotNull Collection branchTrackInfos) { + myCurrentBranch = currentBranch; + myCurrentRevision = currentRevision; + myState = state; + myRemotes = remotes; + myLocalBranches = localBranches; + myRemoteBranches = remoteBranches; + myBranchTrackInfos = branchTrackInfos; + } + + @Nullable + public GitLocalBranch getCurrentBranch() { + return myCurrentBranch; + } + + @NotNull + public Collection getRemotes() { + return myRemotes; + } + + @NotNull + public Collection getLocalBranches() { + return myLocalBranches; + } + + @NotNull + public Collection getRemoteBranches() { + return myRemoteBranches; + } + + @NotNull + public Collection getBranchTrackInfos() { + return myBranchTrackInfos; + } + + @Nullable + public String getCurrentRevision() { + return myCurrentRevision; + } + + @NotNull + public Repository.State getState() { + return myState; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + GitRepoInfo info = (GitRepoInfo)o; + + if (!myBranchTrackInfos.equals(info.myBranchTrackInfos)) return false; + if (myCurrentBranch != null ? !myCurrentBranch.equals(info.myCurrentBranch) : info.myCurrentBranch != null) return false; + if (!myLocalBranches.equals(info.myLocalBranches)) return false; + if (!myRemoteBranches.equals(info.myRemoteBranches)) return false; + if (!myRemotes.equals(info.myRemotes)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myCurrentBranch != null ? myCurrentBranch.hashCode() : 0; + result = 31 * result + myRemotes.hashCode(); + result = 31 * result + myLocalBranches.hashCode(); + result = 31 * result + myRemoteBranches.hashCode(); + result = 31 * result + myBranchTrackInfos.hashCode(); + return result; + } + + @Override + public String toString() { + return String.format("GitRepoInfo{current=%s, remotes=%s, localBranches=%s, remoteBranches=%s, trackInfos=%s}", + myCurrentBranch, myRemotes, myLocalBranches, myRemoteBranches, myBranchTrackInfos); + } +} diff --git a/plugins/git4idea/src/git4idea/repo/GitRepository.java b/plugins/git4idea/src/git4idea/repo/GitRepository.java index 9b7dfa077d47..7a44b27e9db3 100644 --- a/plugins/git4idea/src/git4idea/repo/GitRepository.java +++ b/plugins/git4idea/src/git4idea/repo/GitRepository.java @@ -69,6 +69,9 @@ public interface GitRepository extends Repository { GitUntrackedFilesHolder getUntrackedFilesHolder(); + @NotNull + GitRepoInfo getInfo(); + /** * Returns the current branch of this Git repository. * If the repository is being rebased, then the current branch is the branch being rebased (which was current before the rebase diff --git a/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java b/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java index ac92fc3f3720..45c9ebccc540 100644 --- a/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java +++ b/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java @@ -30,7 +30,6 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.Collection; -import java.util.Collections; /** * @author Kirill Likhodedov @@ -42,10 +41,7 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository, @NotNull private final VirtualFile myGitDir; @Nullable private final GitUntrackedFilesHolder myUntrackedFilesHolder; - @Nullable private volatile GitLocalBranch myCurrentBranch; - @NotNull private volatile GitBranchesCollection myBranches = GitBranchesCollection.EMPTY; - @NotNull private volatile Collection myRemotes = Collections.emptyList(); - @NotNull private volatile Collection myBranchTrackInfos; + @NotNull private volatile GitRepoInfo myInfo; /** * Get the GitRepository instance from the {@link GitRepositoryManager}. @@ -115,10 +111,28 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository, return myUntrackedFilesHolder; } + @Override + @NotNull + public GitRepoInfo getInfo() { + return myInfo; + } + @Override @Nullable public GitLocalBranch getCurrentBranch() { - return myCurrentBranch; + return myInfo.getCurrentBranch(); + } + + @Nullable + @Override + public String getCurrentRevision() { + return myInfo.getCurrentRevision(); + } + + @NotNull + @Override + public State getState() { + return myInfo.getState(); } /** @@ -127,19 +141,20 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository, @Override @NotNull public GitBranchesCollection getBranches() { - return new GitBranchesCollection(myBranches); + GitRepoInfo info = myInfo; + return new GitBranchesCollection(info.getLocalBranches(), info.getRemoteBranches()); } @Override @NotNull public Collection getRemotes() { - return myRemotes; + return myInfo.getRemotes(); } @Override @NotNull public Collection getBranchTrackInfos() { - return myBranchTrackInfos; + return myInfo.getBranchTrackInfos(); } @Override @@ -159,31 +174,58 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository, @Override public void update() { - File configFile = new File(VfsUtilCore.virtualToIoFile(myGitDir), "config"); - GitConfig config = GitConfig.read(myPlatformFacade, configFile); - myRemotes = config.parseRemotes(); - readRepository(myRemotes); - myBranchTrackInfos = config.parseTrackInfos(myBranches.getLocalBranches(), myBranches.getRemoteBranches()); - notifyListeners(); + myInfo = readRepoInfo(this, myPlatformFacade, myReader, myInfo); } - private void readRepository(@NotNull Collection remotes) { - myState = myReader.readState(); - myCurrentRevision = myReader.readCurrentRevision(); - myCurrentBranch = myReader.readCurrentBranch(); - myBranches = myReader.readBranches(remotes); + @NotNull + private static GitRepoInfo readRepoInfo(@NotNull GitRepository repository, @NotNull GitPlatformFacade platformFacade, + @NotNull GitRepositoryReader reader, @Nullable GitRepoInfo previousInfo) { + File configFile = new File(VfsUtilCore.virtualToIoFile(repository.getGitDir()), "config"); + GitConfig config = GitConfig.read(platformFacade, configFile); + Collection remotes = config.parseRemotes(); + TempState tempState = readRepository(reader, remotes); + Collection trackInfos = config.parseTrackInfos(tempState.myBranches.getLocalBranches(), + tempState.myBranches.getRemoteBranches()); + GitRepoInfo info = new GitRepoInfo(tempState.myCurrentBranch, tempState.myCurrentRevision, tempState.myState, + remotes, tempState.myBranches.getLocalBranches(), + tempState.myBranches.getRemoteBranches(), trackInfos); + notifyListeners(repository, previousInfo, info); + return info; } - protected void notifyListeners() { - if (!Disposer.isDisposed(getProject())) { - getProject().getMessageBus().syncPublisher(GIT_REPO_CHANGE).repositoryChanged(this); + private static TempState readRepository(GitRepositoryReader reader, @NotNull Collection remotes) { + return new TempState(reader.readState(), reader.readCurrentRevision(), reader.readCurrentBranch(), reader.readBranches(remotes)); + } + + private static void notifyListeners(@NotNull GitRepository repository, @Nullable GitRepoInfo previousInfo, @NotNull GitRepoInfo info) { + // previous info can be null before the first update + if (Disposer.isDisposed(repository.getProject())) { + return; + } + if (!info.equals(previousInfo)) { + repository.getProject().getMessageBus().syncPublisher(GIT_REPO_CHANGE).repositoryChanged(repository); } } @NotNull @Override public String toLogString() { - return String.format("GitRepository{myCurrentBranch=%s, myCurrentRevision='%s', myState=%s, myRootDir=%s}", - myCurrentBranch, myCurrentRevision, myState, getRoot()); + return String.format("GitRepository " + getRoot() + " : " + myInfo); } + + private static class TempState { + private final State myState; + private final String myCurrentRevision; + private final GitLocalBranch myCurrentBranch; + private final GitBranchesCollection myBranches; + + public TempState(@NotNull State state, @Nullable String currentRevision, @Nullable GitLocalBranch currentBranch, + @NotNull GitBranchesCollection branches) { + myState = state; + myCurrentRevision = currentRevision; + myCurrentBranch = currentBranch; + myBranches = branches; + } + } + }