git4idea: IDEADEV-35889: unversioned and unmerged files with space should be now...
[fedora-idea.git] / plugins / git4idea / src / git4idea / changes / ChangeCollector.java
blob7d48b09d76b7e0d11c1cfd36c5d431711aa40911
1 /*
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;
31 import java.util.*;
33 /**
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 {
38 /**
39 * a vcs root for changes
41 private final VirtualFile myVcsRoot;
42 /**
43 * a project for change collection
45 private final Project myProject;
46 /**
47 * Unversioned files
49 private final List<VirtualFile> myUnversioned = new ArrayList<VirtualFile>();
50 /**
51 * Names that are listed as unmerged
53 private final Set<String> myUnmergedNames = new HashSet<String>();
54 /**
55 * Names that are listed as unmerged
57 private final List<Change> myChanges = new ArrayList<Change>();
58 /**
59 * This flag indicates that collecting changes has been failed.
61 private boolean myIsFailed = true;
62 /**
63 * This flag indicates that collecting changes has been started
65 private boolean myIsCollected = false;
67 /**
68 * A constructor
70 * @param project a project
71 * @param vcsRoot a vcs root
73 public ChangeCollector(final Project project, final VirtualFile vcsRoot) {
74 myVcsRoot = vcsRoot;
75 myProject = project;
78 /**
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 {
85 ensureCollected();
86 return myUnversioned;
89 /**
90 * Get changes
92 * @return an unversioned files
93 * @throws VcsException if there is a problem with executing Git
95 public Collection<Change> changes() throws VcsException {
96 ensureCollected();
97 return myChanges;
102 * Ensure that changes has been collected.
104 * @throws VcsException an exception
106 private void ensureCollected() throws VcsException {
107 if (myIsCollected) {
108 if (myIsFailed) {
109 throw new IllegalStateException("The method should not be called after after exception has been thrown.");
111 else {
112 return;
115 myIsCollected = true;
116 collectUnmergedAndUnversioned();
117 collectDiffChanges();
118 myIsFailed = false;
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();
133 try {
134 String output = handler.run();
135 GitChangeUtils.parseChanges(myProject, myVcsRoot, null, GitChangeUtils.loadRevision(myProject, myVcsRoot, "HEAD"), output, myChanges,
136 myUnmergedNames);
138 catch (VcsException ex) {
139 if (!GitChangeUtils.isHeadMissing(ex)) {
140 throw 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);
156 myChanges.add(ch);
163 * Collect unversioned and unmerged files
165 * @throws VcsException if there is a problem with running git
167 private void collectUnmergedAndUnversioned() throws VcsException {
168 // prepare handler
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();) {
177 if (sc.isEol()) {
178 sc.nextLine();
179 continue;
181 char status = sc.peek();
182 sc.skipChars(2);
183 if ('?' == status) {
184 myUnversioned.add(myVcsRoot.findFileByRelativePath(GitUtil.unescapePath(sc.line())));
186 else { //noinspection HardCodedStringLiteral
187 if ('M' == status) {
188 sc.boundedToken('\t');
189 String file = GitUtil.unescapePath(sc.line());
190 if (!myUnmergedNames.add(file)) {
191 continue;
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));
202 else {
203 throw new VcsException("Unsupported type of the merge conflict detected: " + status);