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