1d5b7c3b1f37483153ca6f4ba28bb79ba55a78a0
[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       String filePath = stripFileProtocolPrefix(event.getFile().getPath());
96       if (isHeadFile(filePath)) {
97         headChanged = true;
98       } else if (isBranchFile(filePath)) {
99         branchFileChanged = true;
100       } else if (isPackedRefs(filePath)) {
101         packedRefsChanged = true;
102       } else if (isRebaseFile(filePath)) {
103         rebaseFileChanged = true;
104       } else if (isMergeFile(filePath)) {
105         mergeFileChanged = true;
106       }
107     }
108
109     // what should be updated in GitRepository
110     boolean updateCurrentBranch = false;
111     boolean updateCurrentRevision = false;
112     boolean updateState = false;
113     if (headChanged) {
114       updateCurrentBranch = true;
115       updateCurrentRevision = true;
116       updateState = true;
117     }
118     if (branchFileChanged) {
119       updateCurrentRevision = true;
120     }
121     if (rebaseFileChanged || mergeFileChanged) {
122       updateState = true;
123     }
124
125     // update GitRepository on pooled thread, because it requires reading from disk and parsing data.
126     if (updateCurrentBranch) {
127       ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
128         public void run() {
129           myRepository.updateCurrentBranch();
130         }
131       });
132     }
133     if (updateCurrentRevision) {
134       ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
135         public void run() {
136           myRepository.updateCurrentRevision();
137         }
138       });
139     }
140     if (updateState) {
141       ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
142         public void run() {
143           myRepository.updateState();
144         }
145       });
146     }
147   }
148
149   private boolean isHeadFile(String file) {
150     return file.equals(myHeadFilePath);
151   }
152
153   private boolean isBranchFile(String filePath) {
154     return filePath.startsWith(myRefsHeadsDirPath);
155   }
156
157   private boolean isRebaseFile(String path) {
158     return path.equals(myRebaseApplyPath) || path.equals(myRebaseMergePath);
159   }
160
161   private boolean isMergeFile(String file) {
162     return file.equals(myMergeHeadPath);
163   }
164
165   private boolean isPackedRefs(String file) {
166     return file.equals(myPackedRefsPath);
167   }
168
169 }