2 * Copyright 2000-2007 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package git4idea
.changes
;
18 import com
.intellij
.openapi
.project
.Project
;
19 import com
.intellij
.openapi
.vcs
.FileStatus
;
20 import com
.intellij
.openapi
.vcs
.VcsException
;
21 import com
.intellij
.openapi
.vcs
.changes
.Change
;
22 import com
.intellij
.openapi
.vcs
.changes
.ContentRevision
;
23 import com
.intellij
.openapi
.vfs
.VirtualFile
;
24 import git4idea
.GitContentRevision
;
25 import git4idea
.GitRevisionNumber
;
26 import git4idea
.GitUtil
;
27 import git4idea
.commands
.GitHandler
;
28 import git4idea
.commands
.GitSimpleHandler
;
33 * A collector for changes in the Git. It is introduced because changes are not
34 * cannot be got as a sum of stateless operations.
36 class ChangeCollector
{
38 * a vcs root for changes
40 private final VirtualFile myVcsRoot
;
42 * a project for change collection
44 private final Project myProject
;
48 private final List
<VirtualFile
> myUnversioned
= new ArrayList
<VirtualFile
>();
50 * Names that are listed as unmerged
52 private final Set
<String
> myUnmergedNames
= new HashSet
<String
>();
54 * Names that are listed as unmerged
56 private final List
<Change
> myChanges
= new ArrayList
<Change
>();
58 * This flag indicates that collecting changes has been failed.
60 private boolean myIsFailed
= true;
62 * This flag indicates that collecting changes has been started
64 private boolean myIsCollected
= false;
69 * @param project a project
70 * @param vcsRoot a vcs root
72 public ChangeCollector(final Project project
, final VirtualFile vcsRoot
) {
78 * Get unversioned files
80 * @return an unversioned files
81 * @throws VcsException if there is a problem with executing Git
83 public Collection
<VirtualFile
> unversioned() throws VcsException
{
91 * @return an unversioned files
92 * @throws VcsException if there is a problem with executing Git
94 public Collection
<Change
> changes() throws VcsException
{
101 * Ensure that changes has been collected.
103 * @throws VcsException an exception
105 private void ensureCollected() throws VcsException
{
108 throw new IllegalStateException("The method should not be called after after exception has been thrown.");
114 myIsCollected
= true;
115 collectUnmergedAndUnversioned();
116 collectDiffChanges();
121 * Collect diff with head
123 * @throws VcsException if there is a problem with running git
125 private void collectDiffChanges() throws VcsException
{
126 GitSimpleHandler handler
= new GitSimpleHandler(myProject
, myVcsRoot
, GitHandler
.DIFF
);
127 handler
.addParameters("--name-status", "--diff-filter=ADCMRUX", "-M", "HEAD");
128 //handler.endOptions();
129 handler
.setNoSSH(true);
130 handler
.setSilent(true);
131 handler
.setStdoutSuppressed(true);
132 handler
.endOptions();
134 String output
= handler
.run();
135 GitChangeUtils
.parseChanges(myProject
, myVcsRoot
, null, GitChangeUtils
.loadRevision(myProject
, myVcsRoot
, "HEAD"), output
, myChanges
,
138 catch (VcsException ex
) {
139 if (!GitChangeUtils
.isHeadMissing(ex
)) {
142 handler
= new GitSimpleHandler(myProject
, myVcsRoot
, GitHandler
.LS_FILES
);
143 handler
.addParameters("--cached");
144 handler
.setNoSSH(true);
145 handler
.setSilent(true);
146 handler
.setStdoutSuppressed(true);
147 // During init diff does not works because HEAD
148 // will appear only after the first commit.
149 // In that case added files are cached in index.
150 String output
= handler
.run();
151 if (output
.length() > 0) {
152 StringTokenizer tokenizer
= new StringTokenizer(output
, "\n\r");
153 while (tokenizer
.hasMoreTokens()) {
154 final String s
= tokenizer
.nextToken();
155 Change ch
= new Change(null, GitContentRevision
.createRevision(myVcsRoot
, s
, null, myProject
, false), FileStatus
.ADDED
);
163 * Collect unversioned and unmerged files
165 * @throws VcsException if there is a problem with running git
167 private void collectUnmergedAndUnversioned() throws VcsException
{
169 GitSimpleHandler handler
= new GitSimpleHandler(myProject
, myVcsRoot
, GitHandler
.LS_FILES
);
170 handler
.addParameters("-v", "--others", "--unmerged", "--exclude-standard");
171 handler
.setSilent(true);
172 handler
.setNoSSH(true);
173 handler
.setStdoutSuppressed(true);
174 // run handler and collect changes
175 String list
= handler
.run();
176 for (String line
: list
.split("\n")) {
177 if (line
.length() == 0) {
180 String
[] tokens
= line
.split("[\t ]+");
181 String file
= GitUtil
.unescapePath(tokens
[tokens
.length
- 1]);
182 if ("?".equals(tokens
[0])) {
183 myUnversioned
.add(myVcsRoot
.findFileByRelativePath(file
));
185 else { //noinspection HardCodedStringLiteral
186 if ("M".equals(tokens
[0])) {
187 if (!myUnmergedNames
.add(file
)) {
190 // TODO handle conflict rename-modify
191 // TODO handle conflict copy-modify
192 // TODO handle conflict delete-modify
193 // TODO handle conflict rename-delete
194 // assume modify-modify conflict
195 ContentRevision before
= GitContentRevision
.createRevision(myVcsRoot
, file
, new GitRevisionNumber("orig_head"), myProject
, false);
196 ContentRevision after
= GitContentRevision
.createRevision(myVcsRoot
, file
, null, myProject
, false);
197 myChanges
.add(new Change(before
, after
, FileStatus
.MERGED_WITH_CONFLICTS
));
200 throw new VcsException("Unsupported type of the merge conflict detected: " + line
);