blob: 30951670e079eaa6e2da5ae267f7e329e9848c0a [file] [log] [blame]
/*
* Copyright 2000-2012 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.RepositoryImpl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitLocalBranch;
import git4idea.GitPlatformFacade;
import git4idea.GitUtil;
import git4idea.GitVcs;
import git4idea.branch.GitBranchesCollection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Collection;
/**
* @author Kirill Likhodedov
*/
public class GitRepositoryImpl extends RepositoryImpl implements GitRepository {
@NotNull private final GitPlatformFacade myPlatformFacade;
@NotNull private final GitRepositoryReader myReader;
@NotNull private final VirtualFile myGitDir;
@Nullable private final GitUntrackedFilesHolder myUntrackedFilesHolder;
@NotNull private volatile GitRepoInfo myInfo;
/**
* Get the GitRepository instance from the {@link GitRepositoryManager}.
* If you need to have an instance of GitRepository for a repository outside the project, use
* {@link #getLightInstance(VirtualFile, Project, git4idea.GitPlatformFacade, Disposable)}
*/
@SuppressWarnings("ConstantConditions")
protected GitRepositoryImpl(@NotNull VirtualFile rootDir, @NotNull GitPlatformFacade facade, @NotNull Project project,
@NotNull Disposable parentDisposable, final boolean light) {
super(project, rootDir, parentDisposable);
myPlatformFacade = facade;
myGitDir = GitUtil.findGitDir(rootDir);
assert myGitDir != null : ".git directory wasn't found under " + rootDir.getPresentableUrl();
myReader = new GitRepositoryReader(VfsUtilCore.virtualToIoFile(myGitDir));
if (!light) {
myUntrackedFilesHolder = new GitUntrackedFilesHolder(this);
Disposer.register(this, myUntrackedFilesHolder);
}
else {
myUntrackedFilesHolder = null;
}
update();
}
/**
* Returns the temporary light instance of GitRepository.
* It lacks functionality of auto-updating GitRepository on Git internal files change, and also stored a stub instance of
* {@link GitUntrackedFilesHolder}.
*/
@NotNull
public static GitRepository getLightInstance(@NotNull VirtualFile root, @NotNull Project project, @NotNull GitPlatformFacade facade,
@NotNull Disposable parentDisposable) {
return new GitRepositoryImpl(root, facade, project, parentDisposable, true);
}
/**
* Returns the full-functional instance of GitRepository - with UntrackedFilesHolder and GitRepositoryUpdater.
* This is used for repositories registered in project, and should be optained via {@link GitRepositoryManager}.
*/
@NotNull
public static GitRepository getFullInstance(@NotNull VirtualFile root, @NotNull Project project, @NotNull GitPlatformFacade facade,
@NotNull Disposable parentDisposable) {
GitRepositoryImpl repository = new GitRepositoryImpl(root, facade, project, parentDisposable, false);
repository.myUntrackedFilesHolder.setupVfsListener(project); //myUntrackedFilesHolder cannot be null because it is not light instance
repository.setupUpdater();
return repository;
}
private void setupUpdater() {
GitRepositoryUpdater updater = new GitRepositoryUpdater(this);
Disposer.register(this, updater);
}
@NotNull
@Override
public VirtualFile getGitDir() {
return myGitDir;
}
@Override
@NotNull
public GitUntrackedFilesHolder getUntrackedFilesHolder() {
if (myUntrackedFilesHolder == null) {
throw new IllegalStateException("Using untracked files holder with light git repository instance " + this);
}
return myUntrackedFilesHolder;
}
@Override
@NotNull
public GitRepoInfo getInfo() {
return myInfo;
}
@Override
@Nullable
public GitLocalBranch getCurrentBranch() {
return myInfo.getCurrentBranch();
}
@Nullable
@Override
public String getCurrentRevision() {
return myInfo.getCurrentRevision();
}
@NotNull
@Override
public State getState() {
return myInfo.getState();
}
@Nullable
@Override
public String getCurrentBranchName() {
GitLocalBranch currentBranch = getCurrentBranch();
return currentBranch == null ? null : currentBranch.getName();
}
@Nullable
@Override
public AbstractVcs getVcs() {
return GitVcs.getInstance(getProject());
}
/**
* @return local and remote branches in this repository.
*/
@Override
@NotNull
public GitBranchesCollection getBranches() {
GitRepoInfo info = myInfo;
return new GitBranchesCollection(info.getLocalBranches(), info.getRemoteBranches());
}
@Override
@NotNull
public Collection<GitRemote> getRemotes() {
return myInfo.getRemotes();
}
@Override
@NotNull
public Collection<GitBranchTrackInfo> getBranchTrackInfos() {
return myInfo.getBranchTrackInfos();
}
@Override
public boolean isRebaseInProgress() {
return getState() == State.REBASING;
}
@Override
public boolean isOnBranch() {
return getState() != State.DETACHED && getState() != State.REBASING;
}
@Override
public boolean isFresh() {
return getCurrentRevision() == null;
}
@Override
public void update() {
GitRepoInfo previousInfo = myInfo;
myInfo = readRepoInfo();
notifyListeners(this, previousInfo, myInfo);
}
@NotNull
private GitRepoInfo readRepoInfo() {
File configFile = new File(VfsUtilCore.virtualToIoFile(getGitDir()), "config");
GitConfig config = GitConfig.read(myPlatformFacade, configFile);
Collection<GitRemote> remotes = config.parseRemotes();
TempState tempState = readRepository(remotes);
Collection<GitBranchTrackInfo> trackInfos = config.parseTrackInfos(tempState.myBranches.getLocalBranches(),
tempState.myBranches.getRemoteBranches());
return new GitRepoInfo(tempState.myCurrentBranch, tempState.myCurrentRevision, tempState.myState,
remotes, tempState.myBranches.getLocalBranches(),
tempState.myBranches.getRemoteBranches(), trackInfos);
}
private TempState readRepository(@NotNull Collection<GitRemote> remotes) {
return new TempState(myReader.readState(), myReader.readCurrentRevision(), myReader.readCurrentBranch(),
myReader.readBranches(remotes));
}
// previous info can be null before the first update
private static void notifyListeners(@NotNull GitRepository repository, @Nullable GitRepoInfo previousInfo, @NotNull GitRepoInfo info) {
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 " + 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;
}
}
}