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
;
29 import git4idea
.commands
.StringScanner
;
34 * A collector for changes in the Git. It is introduced because changes are not
35 * cannot be got as a sum of stateless operations.
37 class ChangeCollector
{
39 * a vcs root for changes
41 private final VirtualFile myVcsRoot
;
43 * a project for change collection
45 private final Project myProject
;
49 private final List
<VirtualFile
> myUnversioned
= new ArrayList
<VirtualFile
>();
51 * Names that are listed as unmerged
53 private final Set
<String
> myUnmergedNames
= new HashSet
<String
>();
55 * Names that are listed as unmerged
57 private final List
<Change
> myChanges
= new ArrayList
<Change
>();
59 * This flag indicates that collecting changes has been failed.
61 private boolean myIsFailed
= true;
63 * This flag indicates that collecting changes has been started
65 private boolean myIsCollected
= false;
70 * @param project a project
71 * @param vcsRoot a vcs root
73 public ChangeCollector(final Project project
, final VirtualFile vcsRoot
) {
79 * Get unversioned files
81 * @return an unversioned files
82 * @throws VcsException if there is a problem with executing Git
84 public Collection
<VirtualFile
> unversioned() throws VcsException
{
92 * @return an unversioned files
93 * @throws VcsException if there is a problem with executing Git
95 public Collection
<Change
> changes() throws VcsException
{
102 * Ensure that changes has been collected.
104 * @throws VcsException an exception
106 private void ensureCollected() throws VcsException
{
109 throw new IllegalStateException("The method should not be called after after exception has been thrown.");
115 myIsCollected
= true;
116 collectUnmergedAndUnversioned();
117 collectDiffChanges();
122 * Collect diff with head
124 * @throws VcsException if there is a problem with running git
126 private void collectDiffChanges() throws VcsException
{
127 GitSimpleHandler handler
= new GitSimpleHandler(myProject
, myVcsRoot
, GitHandler
.DIFF
);
128 handler
.addParameters("--name-status", "--diff-filter=ADCMRUX", "-M", "HEAD");
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 (StringScanner sc
= new StringScanner(list
); sc
.hasMoreData();) {
181 char status
= sc
.peek();
184 myUnversioned
.add(myVcsRoot
.findFileByRelativePath(GitUtil
.unescapePath(sc
.line())));
186 else { //noinspection HardCodedStringLiteral
188 sc
.boundedToken('\t');
189 String file
= GitUtil
.unescapePath(sc
.line());
190 if (!myUnmergedNames
.add(file
)) {
193 // TODO handle conflict rename-modify
194 // TODO handle conflict copy-modify
195 // TODO handle conflict delete-modify
196 // TODO handle conflict rename-delete
197 // assume modify-modify conflict
198 ContentRevision before
= GitContentRevision
.createRevision(myVcsRoot
, file
, new GitRevisionNumber("orig_head"), myProject
, false);
199 ContentRevision after
= GitContentRevision
.createRevision(myVcsRoot
, file
, null, myProject
, false);
200 myChanges
.add(new Change(before
, after
, FileStatus
.MERGED_WITH_CONFLICTS
));
203 throw new VcsException("Unsupported type of the merge conflict detected: " + status
);