1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.pgm;
45
46 import java.io.IOException;
47 import java.text.MessageFormat;
48 import java.util.ArrayList;
49 import java.util.Collection;
50 import java.util.Collections;
51 import java.util.List;
52 import java.util.Map;
53 import java.util.TreeSet;
54
55 import org.eclipse.jgit.api.Git;
56 import org.eclipse.jgit.api.StatusCommand;
57 import org.eclipse.jgit.lib.Constants;
58 import org.eclipse.jgit.lib.IndexDiff.StageState;
59 import org.eclipse.jgit.lib.Ref;
60 import org.eclipse.jgit.lib.Repository;
61 import org.eclipse.jgit.pgm.internal.CLIText;
62 import org.eclipse.jgit.pgm.opt.UntrackedFilesHandler;
63 import org.kohsuke.args4j.Argument;
64 import org.kohsuke.args4j.Option;
65 import org.kohsuke.args4j.spi.RestOfArgumentsHandler;
66
67
68
69
70 @Command(usage = "usage_Status", common = true)
71 class Status extends TextBuiltin {
72
73 protected final String statusFileListFormat = CLIText.get().statusFileListFormat;
74
75 protected final String statusFileListFormatWithPrefix = CLIText.get().statusFileListFormatWithPrefix;
76
77 protected final String statusFileListFormatUnmerged = CLIText.get().statusFileListFormatUnmerged;
78
79 @Option(name = "--porcelain", usage = "usage_machineReadableOutput")
80 protected boolean porcelain;
81
82 @Option(name = "--untracked-files", aliases = { "-u", "-uno", "-uall" }, usage = "usage_untrackedFilesMode", handler = UntrackedFilesHandler.class)
83 protected String untrackedFilesMode = "all";
84
85 @Argument(required = false, index = 0, metaVar = "metaVar_paths")
86 @Option(name = "--", metaVar = "metaVar_paths", multiValued = true, handler = RestOfArgumentsHandler.class)
87 protected List<String> filterPaths;
88
89 @Override
90 protected void run() throws Exception {
91 try (Git git = new Git(db)) {
92 StatusCommand statusCommand = git.status();
93 if (filterPaths != null && filterPaths.size() > 0)
94 for (String path : filterPaths)
95 statusCommand.addPath(path);
96 org.eclipse.jgit.api.Status status = statusCommand.call();
97 printStatus(status);
98 }
99 }
100
101 private void printStatus(org.eclipse.jgit.api.Status status)
102 throws IOException {
103 if (porcelain)
104 printPorcelainStatus(status);
105 else
106 printLongStatus(status);
107 }
108
109 private void printPorcelainStatus(org.eclipse.jgit.api.Status status)
110 throws IOException {
111
112 Collection<String> added = status.getAdded();
113 Collection<String> changed = status.getChanged();
114 Collection<String> removed = status.getRemoved();
115 Collection<String> modified = status.getModified();
116 Collection<String> missing = status.getMissing();
117 Map<String, StageState> conflicting = status.getConflictingStageState();
118
119
120 TreeSet<String> sorted = new TreeSet<String>();
121 sorted.addAll(added);
122 sorted.addAll(changed);
123 sorted.addAll(removed);
124 sorted.addAll(modified);
125 sorted.addAll(missing);
126 sorted.addAll(conflicting.keySet());
127
128
129 for (String path : sorted) {
130 char x = ' ';
131 char y = ' ';
132
133 if (added.contains(path))
134 x = 'A';
135 else if (changed.contains(path))
136 x = 'M';
137 else if (removed.contains(path))
138 x = 'D';
139
140 if (modified.contains(path))
141 y = 'M';
142 else if (missing.contains(path))
143 y = 'D';
144
145 if (conflicting.containsKey(path)) {
146 StageState stageState = conflicting.get(path);
147
148 switch (stageState) {
149 case BOTH_DELETED:
150 x = 'D';
151 y = 'D';
152 break;
153 case ADDED_BY_US:
154 x = 'A';
155 y = 'U';
156 break;
157 case DELETED_BY_THEM:
158 x = 'U';
159 y = 'D';
160 break;
161 case ADDED_BY_THEM:
162 x = 'U';
163 y = 'A';
164 break;
165 case DELETED_BY_US:
166 x = 'D';
167 y = 'U';
168 break;
169 case BOTH_ADDED:
170 x = 'A';
171 y = 'A';
172 break;
173 case BOTH_MODIFIED:
174 x = 'U';
175 y = 'U';
176 break;
177 default:
178 throw new IllegalArgumentException("Unknown StageState: "
179 + stageState);
180 }
181 }
182
183 printPorcelainLine(x, y, path);
184 }
185
186
187 if ("all".equals(untrackedFilesMode)) {
188 TreeSet<String> untracked = new TreeSet<String>(
189 status.getUntracked());
190 for (String path : untracked)
191 printPorcelainLine('?', '?', path);
192 }
193 }
194
195 private void printPorcelainLine(char x, char y, String path)
196 throws IOException {
197 StringBuilder lineBuilder = new StringBuilder();
198 lineBuilder.append(x).append(y).append(' ').append(path);
199 outw.println(lineBuilder.toString());
200 }
201
202 private void printLongStatus(org.eclipse.jgit.api.Status status)
203 throws IOException {
204
205 final Ref head = db.exactRef(Constants.HEAD);
206 if (head != null && head.isSymbolic()) {
207 String branch = Repository.shortenRefName(head.getLeaf().getName());
208 outw.println(CLIText.formatLine(MessageFormat.format(
209 CLIText.get().onBranch, branch)));
210 } else
211 outw.println(CLIText.formatLine(CLIText.get().notOnAnyBranch));
212
213
214 boolean firstHeader = true;
215
216 Collection<String> added = status.getAdded();
217 Collection<String> changed = status.getChanged();
218 Collection<String> removed = status.getRemoved();
219 Collection<String> modified = status.getModified();
220 Collection<String> missing = status.getMissing();
221 Collection<String> untracked = status.getUntracked();
222 Map<String, StageState> unmergedStates = status
223 .getConflictingStageState();
224 Collection<String> toBeCommitted = new ArrayList<String>(added);
225 toBeCommitted.addAll(changed);
226 toBeCommitted.addAll(removed);
227 int nbToBeCommitted = toBeCommitted.size();
228 if (nbToBeCommitted > 0) {
229 printSectionHeader(CLIText.get().changesToBeCommitted);
230 printList(CLIText.get().statusNewFile,
231 CLIText.get().statusModified, CLIText.get().statusRemoved,
232 toBeCommitted, added, changed, removed);
233 firstHeader = false;
234 }
235 Collection<String> notStagedForCommit = new ArrayList<String>(modified);
236 notStagedForCommit.addAll(missing);
237 int nbNotStagedForCommit = notStagedForCommit.size();
238 if (nbNotStagedForCommit > 0) {
239 if (!firstHeader)
240 printSectionHeader("");
241 printSectionHeader(CLIText.get().changesNotStagedForCommit);
242 printList(CLIText.get().statusModified,
243 CLIText.get().statusRemoved, null, notStagedForCommit,
244 modified, missing, null);
245 firstHeader = false;
246 }
247 int nbUnmerged = unmergedStates.size();
248 if (nbUnmerged > 0) {
249 if (!firstHeader)
250 printSectionHeader("");
251 printSectionHeader(CLIText.get().unmergedPaths);
252 printUnmerged(unmergedStates);
253 firstHeader = false;
254 }
255 int nbUntracked = untracked.size();
256 if (nbUntracked > 0 && ("all".equals(untrackedFilesMode))) {
257 if (!firstHeader)
258 printSectionHeader("");
259 printSectionHeader(CLIText.get().untrackedFiles);
260 printList(untracked);
261 }
262 }
263
264 protected void printSectionHeader(String pattern, Object... arguments)
265 throws IOException {
266 if (!porcelain) {
267 outw.println(CLIText.formatLine(MessageFormat.format(pattern,
268 arguments)));
269 if (!pattern.equals(""))
270 outw.println(CLIText.formatLine(""));
271 outw.flush();
272 }
273 }
274
275 protected int printList(Collection<String> list) throws IOException {
276 if (!list.isEmpty()) {
277 List<String> sortedList = new ArrayList<String>(list);
278 java.util.Collections.sort(sortedList);
279 for (String filename : sortedList) {
280 outw.println(CLIText.formatLine(String.format(
281 statusFileListFormat, filename)));
282 }
283 outw.flush();
284 return list.size();
285 } else
286 return 0;
287 }
288
289 protected int printList(String status1, String status2, String status3,
290 Collection<String> list, Collection<String> set1,
291 Collection<String> set2,
292 @SuppressWarnings("unused") Collection<String> set3)
293 throws IOException {
294 List<String> sortedList = new ArrayList<String>(list);
295 java.util.Collections.sort(sortedList);
296 for (String filename : sortedList) {
297 String prefix;
298 if (set1.contains(filename))
299 prefix = status1;
300 else if (set2.contains(filename))
301 prefix = status2;
302 else
303
304 prefix = status3;
305 outw.println(CLIText.formatLine(String.format(
306 statusFileListFormatWithPrefix, prefix, filename)));
307 outw.flush();
308 }
309 return list.size();
310 }
311
312 private void printUnmerged(Map<String, StageState> unmergedStates)
313 throws IOException {
314 List<String> paths = new ArrayList<String>(unmergedStates.keySet());
315 Collections.sort(paths);
316 for (String path : paths) {
317 StageState state = unmergedStates.get(path);
318 String stateDescription = getStageStateDescription(state);
319 outw.println(CLIText.formatLine(String.format(
320 statusFileListFormatUnmerged, stateDescription, path)));
321 outw.flush();
322 }
323 }
324
325 private static String getStageStateDescription(StageState stageState) {
326 CLIText text = CLIText.get();
327 switch (stageState) {
328 case BOTH_DELETED:
329 return text.statusBothDeleted;
330 case ADDED_BY_US:
331 return text.statusAddedByUs;
332 case DELETED_BY_THEM:
333 return text.statusDeletedByThem;
334 case ADDED_BY_THEM:
335 return text.statusAddedByThem;
336 case DELETED_BY_US:
337 return text.statusDeletedByUs;
338 case BOTH_ADDED:
339 return text.statusBothAdded;
340 case BOTH_MODIFIED:
341 return text.statusBothModified;
342 default:
343 throw new IllegalArgumentException("Unknown StageState: "
344 + stageState);
345 }
346 }
347 }