Support Git submodules as nested roots

Submodules in Git don't have a .git dir.
Instead they have .git file referencing to the actual Git repository located in <super_project>/.git/modules/<module_name>

To get the .git dir use GitUtil.findGitDir

This fixes IDEA-80047 and IDEA-79674
This commit is contained in:
Kirill Likhodedov
2012-03-15 16:56:08 +04:00
parent 38be73e714
commit 9e0294c49e
5 changed files with 62 additions and 18 deletions
@@ -48,6 +48,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.*;
@@ -93,6 +94,43 @@ public class GitUtil {
// do nothing
}
@Nullable
public static VirtualFile findGitDir(@NotNull VirtualFile rootDir) {
VirtualFile child = rootDir.findChild(".git");
if (child == null) {
return null;
}
if (child.isDirectory()) {
return child;
}
// this is standard for submodules, although probably it can
String content = readFile(child);
String pathToDir;
String prefix = "gitdir:";
if (content.startsWith(prefix)) {
pathToDir = content.substring(prefix.length()).trim();
}
else {
pathToDir = content;
}
return VcsUtil.getVirtualFile(pathToDir);
}
@NotNull
private static String readFile(@NotNull VirtualFile file) {
Exception exception = null;
for (int attempts = 0; attempts < 3; attempts++) {
try {
return new String(file.contentsToByteArray());
}
catch (IOException e) {
exception = e;
}
}
throw new RuntimeException("Couldn't read " + file, exception);
}
/**
* Sort files by Git root
*
@@ -26,6 +26,7 @@ import com.intellij.util.messages.MessageBus;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.messages.Topic;
import git4idea.GitBranch;
import git4idea.GitUtil;
import git4idea.branch.GitBranchesCollection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -174,12 +175,12 @@ public final class GitRepository implements Disposable {
myProject = project;
Disposer.register(parentDisposable, this);
myGitDir = myRootDir.findChild(".git");
myGitDir = GitUtil.findGitDir(myRootDir);
assert myGitDir != null : ".git directory wasn't found under " + rootDir.getPresentableUrl();
myReader = new GitRepositoryReader(VfsUtil.virtualToIoFile(myGitDir));
myUntrackedFilesHolder = new GitUntrackedFilesHolder(rootDir, project);
myUntrackedFilesHolder = new GitUntrackedFilesHolder(this);
Disposer.register(this, myUntrackedFilesHolder);
myMessageBus = project.getMessageBus();
@@ -222,6 +223,11 @@ public final class GitRepository implements Disposable {
return myRootDir;
}
@NotNull
public VirtualFile getGitDir() {
return myGitDir;
}
@NotNull
public String getPresentableUrl() {
return getRoot().getPresentableUrl();
@@ -45,16 +45,13 @@ public class GitRepositoryFiles {
private final String myCommitMessagePath;
private final String myExcludePath;
public static GitRepositoryFiles getInstance(@NotNull VirtualFile root) {
public static GitRepositoryFiles getInstance(@NotNull VirtualFile gitDir) {
// maybe will be cached later to store a single GitRepositoryFiles for a root.
return new GitRepositoryFiles(root);
return new GitRepositoryFiles(gitDir);
}
private GitRepositoryFiles(@NotNull VirtualFile root) {
private GitRepositoryFiles(@NotNull VirtualFile gitDir) {
// add .git/ and .git/refs/heads to the VFS
VirtualFile gitDir = root.findChild(".git");
assert gitDir != null;
// save paths of the files, that we will watch
String gitDirPath = GitFileUtils.stripFileProtocolPrefix(gitDir.getPath());
myConfigFilePath = gitDirPath + "/config";
@@ -50,11 +50,10 @@ final class GitRepositoryUpdater implements Disposable, BulkFileListener {
myRepository = repository;
VirtualFile root = repository.getRoot();
VirtualFile gitDir = root.findChild(".git");
assert gitDir != null;
VirtualFile gitDir = repository.getGitDir();
LocalFileSystem.getInstance().addRootToWatch(gitDir.getPath(), true);
myRepositoryFiles = GitRepositoryFiles.getInstance(root);
myRepositoryFiles = GitRepositoryFiles.getInstance(gitDir);
visitGitDirVfs(gitDir);
myHeadsDir = VcsUtil.getVirtualFile(myRepositoryFiles.getRefsHeadsPath());
myRemotesDir = VcsUtil.getVirtualFile(myRepositoryFiles.getRefsRemotesPath());
@@ -89,14 +89,18 @@ public class GitUntrackedFilesHolder implements Disposable, BulkFileListener {
private Set<VirtualFile> myPossiblyTrackedFiles = new HashSet<VirtualFile>();
private boolean myReady; // if false, total refresh is needed
private final Object LOCK = new Object();
private final GitRepositoryManager myRepositoryManager;
GitUntrackedFilesHolder(@NotNull VirtualFile root, @NotNull Project project) {
myProject = project;
myRoot = root;
myRepositoryFiles = GitRepositoryFiles.getInstance(root);
myChangeListManager = ChangeListManager.getInstance(project);
myDirtyScopeManager = VcsDirtyScopeManager.getInstance(project);
GitUntrackedFilesHolder(@NotNull GitRepository repository) {
myProject = repository.getProject();
myRoot = repository.getRoot();
myChangeListManager = ChangeListManager.getInstance(myProject);
myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject);
myGit = ServiceManager.getService(Git.class);
myRepositoryManager = GitUtil.getRepositoryManager(myProject);
assert myRepositoryManager != null;
myRepositoryFiles = GitRepositoryFiles.getInstance(repository.getGitDir());
}
void setupVfsListener(@NotNull Project project) {
@@ -302,7 +306,7 @@ public class GitUntrackedFilesHolder implements Disposable, BulkFileListener {
}
private boolean belongsToThisRepository(VirtualFile file) {
final GitRepository repository = GitUtil.getRepositoryManager(myProject).getRepositoryForFile(file);
final GitRepository repository = myRepositoryManager.getRepositoryForFile(file);
return repository != null && repository.getRoot().equals(myRoot);
}