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