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