NPE in GitRepositoryUpdater
[idea/community.git] / plugins / git4idea / src / git4idea / repo / GitRepositoryUpdater.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package git4idea.repo;
17
18 import com.intellij.openapi.Disposable;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.vfs.VirtualFile;
21 import com.intellij.openapi.vfs.VirtualFileManager;
22 import com.intellij.openapi.vfs.newvfs.BulkFileListener;
23 import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
24 import com.intellij.util.messages.MessageBusConnection;
25
26 import java.util.List;
27
28 /**
29  * Listens to .git service files changes and updates {@link GitRepository} when needed.
30  * @author Kirill Likhodedov
31  */
32 public class GitRepositoryUpdater implements Disposable, BulkFileListener {
33
34   private final GitRepository myRepository;
35   private MessageBusConnection myMessageBusConnection;
36
37   private final String myHeadFilePath;
38   private final String myMergeHeadPath;
39   private final String myRebaseApplyPath;
40   private final String myRebaseMergePath;
41   private final String myPackedRefsPath;
42   private final String myRefsHeadsDirPath;
43
44   public GitRepositoryUpdater(GitRepository repository) {
45     myRepository = repository;
46
47     // add .git/ and .git/refs/heads to the VFS
48     VirtualFile gitDir = repository.getRoot().findChild(".git");
49     assert gitDir != null;
50     gitDir.getChildren();
51     final VirtualFile refsHeadsDir = gitDir.findFileByRelativePath("refs/heads");
52     assert refsHeadsDir != null;
53     refsHeadsDir.getChildren();
54
55     // save paths of the files, that we will watch
56     String gitDirPath = stripFileProtocolPrefix(gitDir.getPath());
57     myHeadFilePath = gitDirPath + "/HEAD";
58     myMergeHeadPath = gitDirPath + "/MERGE_HEAD";
59     myRebaseApplyPath = gitDirPath + "/rebase-apply";
60     myRebaseMergePath = gitDirPath + "/rebase-merge";
61     myPackedRefsPath = gitDirPath + "/packed-refs";
62     myRefsHeadsDirPath = gitDirPath + "/refs/heads";
63
64     myMessageBusConnection = ApplicationManager.getApplication().getMessageBus().connect();
65     myMessageBusConnection.subscribe(VirtualFileManager.VFS_CHANGES, this);
66   }
67
68   private static String stripFileProtocolPrefix(String path) {
69     final String FILE_PROTOCOL = "file://";
70     if (path.startsWith(FILE_PROTOCOL)) {
71       return path.substring(FILE_PROTOCOL.length());
72     }
73     return path;
74   }
75
76   @Override
77   public void dispose() {
78     myMessageBusConnection.disconnect();
79   }
80
81   @Override
82   public void before(List<? extends VFileEvent> events) {
83     // everything is handled in #after()
84   }
85
86   @Override
87   public void after(List<? extends VFileEvent> events) {
88     // which files in .git were changed
89     boolean headChanged = false;
90     boolean branchFileChanged = false;
91     boolean packedRefsChanged = false;
92     boolean rebaseFileChanged = false;
93     boolean mergeFileChanged = false;
94     for (VFileEvent event : events) {
95       final VirtualFile file = event.getFile();
96       if (file == null) {
97         continue;
98       }
99       String filePath = stripFileProtocolPrefix(file.getPath());
100       if (isHeadFile(filePath)) {
101         headChanged = true;
102       } else if (isBranchFile(filePath)) {
103         branchFileChanged = true;
104       } else if (isPackedRefs(filePath)) {
105         packedRefsChanged = true;
106       } else if (isRebaseFile(filePath)) {
107         rebaseFileChanged = true;
108       } else if (isMergeFile(filePath)) {
109         mergeFileChanged = true;
110       }
111     }
112
113     // what should be updated in GitRepository
114     boolean updateCurrentBranch = false;
115     boolean updateCurrentRevision = false;
116     boolean updateState = false;
117     if (headChanged) {
118       updateCurrentBranch = true;
119       updateCurrentRevision = true;
120       updateState = true;
121     }
122     if (branchFileChanged) {
123       updateCurrentRevision = true;
124     }
125     if (rebaseFileChanged || mergeFileChanged) {
126       updateState = true;
127     }
128
129     // update GitRepository on pooled thread, because it requires reading from disk and parsing data.
130     if (updateCurrentBranch) {
131       ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
132         public void run() {
133           myRepository.updateCurrentBranch();
134         }
135       });
136     }
137     if (updateCurrentRevision) {
138       ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
139         public void run() {
140           myRepository.updateCurrentRevision();
141         }
142       });
143     }
144     if (updateState) {
145       ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
146         public void run() {
147           myRepository.updateState();
148         }
149       });
150     }
151   }
152
153   private boolean isHeadFile(String file) {
154     return file.equals(myHeadFilePath);
155   }
156
157   private boolean isBranchFile(String filePath) {
158     return filePath.startsWith(myRefsHeadsDirPath);
159   }
160
161   private boolean isRebaseFile(String path) {
162     return path.equals(myRebaseApplyPath) || path.equals(myRebaseMergePath);
163   }
164
165   private boolean isMergeFile(String file) {
166     return file.equals(myMergeHeadPath);
167   }
168
169   private boolean isPackedRefs(String file) {
170     return file.equals(myPackedRefsPath);
171   }
172
173 }