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