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