]> git.ipfire.org Git - thirdparty/git.git/blame - wt-status.c
Documentation/git-archive: spell --worktree-attributes correctly
[thirdparty/git.git] / wt-status.c
CommitLineData
85023577 1#include "cache.h"
c91f0d92 2#include "wt-status.h"
c91f0d92
JK
3#include "object.h"
4#include "dir.h"
5#include "commit.h"
6#include "diff.h"
7#include "revision.h"
8#include "diffcore.h"
a734d0b1 9#include "quote.h"
ac8d5afc 10#include "run-command.h"
b6975ab5 11#include "remote.h"
c91f0d92 12
23900a96 13static char default_wt_status_colors[][COLOR_MAXLEN] = {
dc6ebd4c
AL
14 GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
15 GIT_COLOR_GREEN, /* WT_STATUS_UPDATED */
16 GIT_COLOR_RED, /* WT_STATUS_CHANGED */
17 GIT_COLOR_RED, /* WT_STATUS_UNTRACKED */
18 GIT_COLOR_RED, /* WT_STATUS_NOBRANCH */
4d4d5726 19 GIT_COLOR_RED, /* WT_STATUS_UNMERGED */
c91f0d92 20};
4d229653 21
d249b098 22static const char *color(int slot, struct wt_status *s)
c91f0d92 23{
23900a96 24 return s->use_color > 0 ? s->color_palette[slot] : "";
c91f0d92
JK
25}
26
27void wt_status_prepare(struct wt_status *s)
28{
29 unsigned char sha1[20];
30 const char *head;
31
cc46a743 32 memset(s, 0, sizeof(*s));
23900a96
JH
33 memcpy(s->color_palette, default_wt_status_colors,
34 sizeof(default_wt_status_colors));
d249b098
JH
35 s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
36 s->use_color = -1;
37 s->relative_paths = 1;
8da19775 38 head = resolve_ref("HEAD", sha1, 0, NULL);
f62363fb 39 s->branch = head ? xstrdup(head) : NULL;
c91f0d92 40 s->reference = "HEAD";
f26a0012 41 s->fp = stdout;
0f729f21 42 s->index_file = get_index_file();
50b7e70f 43 s->change.strdup_strings = 1;
76378683 44 s->untracked.strdup_strings = 1;
c91f0d92
JK
45}
46
4d4d5726
JH
47static void wt_status_print_unmerged_header(struct wt_status *s)
48{
d249b098 49 const char *c = color(WT_STATUS_HEADER, s);
3c588453 50
4d4d5726 51 color_fprintf_ln(s->fp, c, "# Unmerged paths:");
edf563fb
JK
52 if (!advice_status_hints)
53 return;
3c588453
JH
54 if (s->in_merge)
55 ;
56 else if (!s->is_initial)
4d4d5726
JH
57 color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
58 else
59 color_fprintf_ln(s->fp, c, "# (use \"git rm --cached <file>...\" to unstage)");
dd20f8af 60 color_fprintf_ln(s->fp, c, "# (use \"git add/rm <file>...\" as appropriate to mark resolution)");
4d4d5726
JH
61 color_fprintf_ln(s->fp, c, "#");
62}
63
f26a0012 64static void wt_status_print_cached_header(struct wt_status *s)
3c1eb9cb 65{
d249b098 66 const char *c = color(WT_STATUS_HEADER, s);
3c588453 67
f26a0012 68 color_fprintf_ln(s->fp, c, "# Changes to be committed:");
edf563fb
JK
69 if (!advice_status_hints)
70 return;
3c588453
JH
71 if (s->in_merge)
72 ; /* NEEDSWORK: use "git reset --unresolve"??? */
73 else if (!s->is_initial)
f26a0012 74 color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
3c588453 75 else
f26a0012 76 color_fprintf_ln(s->fp, c, "# (use \"git rm --cached <file>...\" to unstage)");
f26a0012 77 color_fprintf_ln(s->fp, c, "#");
3c1eb9cb
JR
78}
79
bb914b14
AM
80static void wt_status_print_dirty_header(struct wt_status *s,
81 int has_deleted)
c91f0d92 82{
d249b098 83 const char *c = color(WT_STATUS_HEADER, s);
3c588453 84
bb914b14 85 color_fprintf_ln(s->fp, c, "# Changed but not updated:");
edf563fb
JK
86 if (!advice_status_hints)
87 return;
bb914b14
AM
88 if (!has_deleted)
89 color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to update what will be committed)");
90 else
91 color_fprintf_ln(s->fp, c, "# (use \"git add/rm <file>...\" to update what will be committed)");
4d6e4c4d 92 color_fprintf_ln(s->fp, c, "# (use \"git checkout -- <file>...\" to discard changes in working directory)");
bb914b14
AM
93 color_fprintf_ln(s->fp, c, "#");
94}
95
96static void wt_status_print_untracked_header(struct wt_status *s)
97{
d249b098 98 const char *c = color(WT_STATUS_HEADER, s);
bb914b14 99 color_fprintf_ln(s->fp, c, "# Untracked files:");
edf563fb
JK
100 if (!advice_status_hints)
101 return;
bb914b14 102 color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to include in what will be committed)");
f26a0012 103 color_fprintf_ln(s->fp, c, "#");
c91f0d92
JK
104}
105
f26a0012 106static void wt_status_print_trailer(struct wt_status *s)
c91f0d92 107{
d249b098 108 color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
c91f0d92
JK
109}
110
a734d0b1 111#define quote_path quote_path_relative
3a946802 112
4d4d5726
JH
113static void wt_status_print_unmerged_data(struct wt_status *s,
114 struct string_list_item *it)
115{
d249b098 116 const char *c = color(WT_STATUS_UNMERGED, s);
4d4d5726
JH
117 struct wt_status_change_data *d = it->util;
118 struct strbuf onebuf = STRBUF_INIT;
119 const char *one, *how = "bug";
120
121 one = quote_path(it->string, -1, &onebuf, s->prefix);
d249b098 122 color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
4d4d5726
JH
123 switch (d->stagemask) {
124 case 1: how = "both deleted:"; break;
125 case 2: how = "added by us:"; break;
126 case 3: how = "deleted by them:"; break;
127 case 4: how = "added by them:"; break;
128 case 5: how = "deleted by us:"; break;
129 case 6: how = "both added:"; break;
130 case 7: how = "both modified:"; break;
131 }
132 color_fprintf(s->fp, c, "%-20s%s\n", how, one);
133 strbuf_release(&onebuf);
134}
135
50b7e70f
JH
136static void wt_status_print_change_data(struct wt_status *s,
137 int change_type,
138 struct string_list_item *it)
c91f0d92 139{
50b7e70f 140 struct wt_status_change_data *d = it->util;
d249b098 141 const char *c = color(change_type, s);
50b7e70f
JH
142 int status = status;
143 char *one_name;
144 char *two_name;
3a946802 145 const char *one, *two;
f285a2d7 146 struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
3a946802 147
50b7e70f
JH
148 one_name = two_name = it->string;
149 switch (change_type) {
150 case WT_STATUS_UPDATED:
151 status = d->index_status;
152 if (d->head_path)
153 one_name = d->head_path;
154 break;
155 case WT_STATUS_CHANGED:
156 status = d->worktree_status;
157 break;
158 }
159
160 one = quote_path(one_name, -1, &onebuf, s->prefix);
161 two = quote_path(two_name, -1, &twobuf, s->prefix);
3a946802 162
d249b098 163 color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
50b7e70f 164 switch (status) {
c91f0d92 165 case DIFF_STATUS_ADDED:
f26a0012 166 color_fprintf(s->fp, c, "new file: %s", one);
3a946802 167 break;
c91f0d92 168 case DIFF_STATUS_COPIED:
f26a0012 169 color_fprintf(s->fp, c, "copied: %s -> %s", one, two);
c91f0d92
JK
170 break;
171 case DIFF_STATUS_DELETED:
f26a0012 172 color_fprintf(s->fp, c, "deleted: %s", one);
3a946802 173 break;
c91f0d92 174 case DIFF_STATUS_MODIFIED:
f26a0012 175 color_fprintf(s->fp, c, "modified: %s", one);
3a946802 176 break;
c91f0d92 177 case DIFF_STATUS_RENAMED:
f26a0012 178 color_fprintf(s->fp, c, "renamed: %s -> %s", one, two);
c91f0d92
JK
179 break;
180 case DIFF_STATUS_TYPE_CHANGED:
f26a0012 181 color_fprintf(s->fp, c, "typechange: %s", one);
3a946802 182 break;
c91f0d92 183 case DIFF_STATUS_UNKNOWN:
f26a0012 184 color_fprintf(s->fp, c, "unknown: %s", one);
3a946802 185 break;
c91f0d92 186 case DIFF_STATUS_UNMERGED:
f26a0012 187 color_fprintf(s->fp, c, "unmerged: %s", one);
3a946802 188 break;
c91f0d92 189 default:
50b7e70f 190 die("bug: unhandled diff status %c", status);
c91f0d92 191 }
f26a0012 192 fprintf(s->fp, "\n");
367c9886
JS
193 strbuf_release(&onebuf);
194 strbuf_release(&twobuf);
c91f0d92
JK
195}
196
50b7e70f
JH
197static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
198 struct diff_options *options,
199 void *data)
c91f0d92
JK
200{
201 struct wt_status *s = data;
c91f0d92 202 int i;
50b7e70f
JH
203
204 if (!q->nr)
205 return;
206 s->workdir_dirty = 1;
c91f0d92 207 for (i = 0; i < q->nr; i++) {
50b7e70f
JH
208 struct diff_filepair *p;
209 struct string_list_item *it;
210 struct wt_status_change_data *d;
211
212 p = q->queue[i];
213 it = string_list_insert(p->one->path, &s->change);
214 d = it->util;
215 if (!d) {
216 d = xcalloc(1, sizeof(*d));
217 it->util = d;
c91f0d92 218 }
50b7e70f
JH
219 if (!d->worktree_status)
220 d->worktree_status = p->status;
c91f0d92 221 }
c91f0d92
JK
222}
223
4d4d5726
JH
224static int unmerged_mask(const char *path)
225{
226 int pos, mask;
227 struct cache_entry *ce;
228
229 pos = cache_name_pos(path, strlen(path));
230 if (0 <= pos)
231 return 0;
232
233 mask = 0;
234 pos = -pos-1;
235 while (pos < active_nr) {
236 ce = active_cache[pos++];
237 if (strcmp(ce->name, path) || !ce_stage(ce))
238 break;
239 mask |= (1 << (ce_stage(ce) - 1));
240 }
241 return mask;
242}
243
50b7e70f
JH
244static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
245 struct diff_options *options,
246 void *data)
c91f0d92 247{
6e458bf6 248 struct wt_status *s = data;
c91f0d92 249 int i;
50b7e70f
JH
250
251 for (i = 0; i < q->nr; i++) {
252 struct diff_filepair *p;
253 struct string_list_item *it;
254 struct wt_status_change_data *d;
255
256 p = q->queue[i];
257 it = string_list_insert(p->two->path, &s->change);
258 d = it->util;
259 if (!d) {
260 d = xcalloc(1, sizeof(*d));
261 it->util = d;
262 }
263 if (!d->index_status)
264 d->index_status = p->status;
265 switch (p->status) {
266 case DIFF_STATUS_COPIED:
267 case DIFF_STATUS_RENAMED:
268 d->head_path = xstrdup(p->one->path);
269 break;
4d4d5726
JH
270 case DIFF_STATUS_UNMERGED:
271 d->stagemask = unmerged_mask(p->two->path);
272 break;
50b7e70f 273 }
6e458bf6 274 }
c91f0d92
JK
275}
276
50b7e70f 277static void wt_status_collect_changes_worktree(struct wt_status *s)
c91f0d92
JK
278{
279 struct rev_info rev;
50b7e70f
JH
280
281 init_revisions(&rev, NULL);
282 setup_revisions(0, NULL, &rev, NULL);
283 rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
284 rev.diffopt.format_callback = wt_status_collect_changed_cb;
285 rev.diffopt.format_callback_data = s;
76e2f7ce 286 rev.prune_data = s->pathspec;
50b7e70f
JH
287 run_diff_files(&rev, 0);
288}
289
290static void wt_status_collect_changes_index(struct wt_status *s)
291{
292 struct rev_info rev;
293
c91f0d92 294 init_revisions(&rev, NULL);
c1e255b7
JK
295 setup_revisions(0, NULL, &rev,
296 s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
c91f0d92 297 rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
50b7e70f 298 rev.diffopt.format_callback = wt_status_collect_updated_cb;
c91f0d92
JK
299 rev.diffopt.format_callback_data = s;
300 rev.diffopt.detect_rename = 1;
50705915 301 rev.diffopt.rename_limit = 200;
f714fb84 302 rev.diffopt.break_opt = 0;
76e2f7ce 303 rev.prune_data = s->pathspec;
c91f0d92
JK
304 run_diff_index(&rev, 1);
305}
306
50b7e70f
JH
307static void wt_status_collect_changes_initial(struct wt_status *s)
308{
309 int i;
310
311 for (i = 0; i < active_nr; i++) {
312 struct string_list_item *it;
313 struct wt_status_change_data *d;
314 struct cache_entry *ce = active_cache[i];
315
76e2f7ce
JH
316 if (!ce_path_match(ce, s->pathspec))
317 continue;
50b7e70f
JH
318 it = string_list_insert(ce->name, &s->change);
319 d = it->util;
320 if (!d) {
321 d = xcalloc(1, sizeof(*d));
322 it->util = d;
323 }
4d4d5726 324 if (ce_stage(ce)) {
50b7e70f 325 d->index_status = DIFF_STATUS_UNMERGED;
4d4d5726
JH
326 d->stagemask |= (1 << (ce_stage(ce) - 1));
327 }
50b7e70f
JH
328 else
329 d->index_status = DIFF_STATUS_ADDED;
330 }
331}
332
76378683
JH
333static void wt_status_collect_untracked(struct wt_status *s)
334{
335 int i;
336 struct dir_struct dir;
337
338 if (!s->show_untracked_files)
339 return;
340 memset(&dir, 0, sizeof(dir));
341 if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
342 dir.flags |=
343 DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
344 setup_standard_excludes(&dir);
345
688cd6d2 346 fill_directory(&dir, s->pathspec);
eeefa7c9 347 for (i = 0; i < dir.nr; i++) {
76378683
JH
348 struct dir_entry *ent = dir.entries[i];
349 if (!cache_name_is_other(ent->name, ent->len))
350 continue;
76e2f7ce
JH
351 if (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
352 continue;
76378683
JH
353 s->workdir_untracked = 1;
354 string_list_insert(ent->name, &s->untracked);
355 }
356}
357
358void wt_status_collect(struct wt_status *s)
50b7e70f
JH
359{
360 wt_status_collect_changes_worktree(s);
361
362 if (s->is_initial)
363 wt_status_collect_changes_initial(s);
364 else
365 wt_status_collect_changes_index(s);
76378683 366 wt_status_collect_untracked(s);
50b7e70f
JH
367}
368
4d4d5726
JH
369static void wt_status_print_unmerged(struct wt_status *s)
370{
371 int shown_header = 0;
372 int i;
373
374 for (i = 0; i < s->change.nr; i++) {
375 struct wt_status_change_data *d;
376 struct string_list_item *it;
377 it = &(s->change.items[i]);
378 d = it->util;
379 if (!d->stagemask)
380 continue;
381 if (!shown_header) {
382 wt_status_print_unmerged_header(s);
383 shown_header = 1;
384 }
385 wt_status_print_unmerged_data(s, it);
386 }
387 if (shown_header)
388 wt_status_print_trailer(s);
389
390}
391
50b7e70f
JH
392static void wt_status_print_updated(struct wt_status *s)
393{
394 int shown_header = 0;
395 int i;
396
397 for (i = 0; i < s->change.nr; i++) {
398 struct wt_status_change_data *d;
399 struct string_list_item *it;
400 it = &(s->change.items[i]);
401 d = it->util;
402 if (!d->index_status ||
403 d->index_status == DIFF_STATUS_UNMERGED)
404 continue;
405 if (!shown_header) {
406 wt_status_print_cached_header(s);
407 s->commitable = 1;
408 shown_header = 1;
409 }
410 wt_status_print_change_data(s, WT_STATUS_UPDATED, it);
411 }
412 if (shown_header)
413 wt_status_print_trailer(s);
414}
415
416/*
417 * -1 : has delete
418 * 0 : no change
419 * 1 : some change but no delete
420 */
421static int wt_status_check_worktree_changes(struct wt_status *s)
422{
423 int i;
424 int changes = 0;
425
426 for (i = 0; i < s->change.nr; i++) {
427 struct wt_status_change_data *d;
428 d = s->change.items[i].util;
4d4d5726
JH
429 if (!d->worktree_status ||
430 d->worktree_status == DIFF_STATUS_UNMERGED)
50b7e70f
JH
431 continue;
432 changes = 1;
433 if (d->worktree_status == DIFF_STATUS_DELETED)
434 return -1;
435 }
436 return changes;
437}
438
c91f0d92
JK
439static void wt_status_print_changed(struct wt_status *s)
440{
50b7e70f
JH
441 int i;
442 int worktree_changes = wt_status_check_worktree_changes(s);
443
444 if (!worktree_changes)
445 return;
446
447 wt_status_print_dirty_header(s, worktree_changes < 0);
448
449 for (i = 0; i < s->change.nr; i++) {
450 struct wt_status_change_data *d;
451 struct string_list_item *it;
452 it = &(s->change.items[i]);
453 d = it->util;
4d4d5726
JH
454 if (!d->worktree_status ||
455 d->worktree_status == DIFF_STATUS_UNMERGED)
50b7e70f
JH
456 continue;
457 wt_status_print_change_data(s, WT_STATUS_CHANGED, it);
458 }
459 wt_status_print_trailer(s);
c91f0d92
JK
460}
461
f17a5d34 462static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted)
ac8d5afc
PY
463{
464 struct child_process sm_summary;
465 char summary_limit[64];
466 char index[PATH_MAX];
467 const char *env[] = { index, NULL };
468 const char *argv[] = {
469 "submodule",
470 "summary",
f17a5d34 471 uncommitted ? "--files" : "--cached",
ac8d5afc
PY
472 "--for-status",
473 "--summary-limit",
474 summary_limit,
f17a5d34 475 uncommitted ? NULL : (s->amend ? "HEAD^" : "HEAD"),
ac8d5afc
PY
476 NULL
477 };
478
d249b098 479 sprintf(summary_limit, "%d", s->submodule_summary);
ac8d5afc
PY
480 snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
481
482 memset(&sm_summary, 0, sizeof(sm_summary));
483 sm_summary.argv = argv;
484 sm_summary.env = env;
485 sm_summary.git_cmd = 1;
486 sm_summary.no_stdin = 1;
487 fflush(s->fp);
488 sm_summary.out = dup(fileno(s->fp)); /* run_command closes it */
489 run_command(&sm_summary);
490}
491
6e458bf6 492static void wt_status_print_untracked(struct wt_status *s)
c91f0d92 493{
c91f0d92 494 int i;
f285a2d7 495 struct strbuf buf = STRBUF_INIT;
c91f0d92 496
76378683
JH
497 if (!s->untracked.nr)
498 return;
c91f0d92 499
76378683
JH
500 wt_status_print_untracked_header(s);
501 for (i = 0; i < s->untracked.nr; i++) {
502 struct string_list_item *it;
503 it = &(s->untracked.items[i]);
d249b098
JH
504 color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
505 color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED, s), "%s",
76378683
JH
506 quote_path(it->string, strlen(it->string),
507 &buf, s->prefix));
c91f0d92 508 }
367c9886 509 strbuf_release(&buf);
c91f0d92
JK
510}
511
512static void wt_status_print_verbose(struct wt_status *s)
513{
514 struct rev_info rev;
99a12694 515
c91f0d92 516 init_revisions(&rev, NULL);
5ec11af6 517 DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
1324fb6f
JK
518 setup_revisions(0, NULL, &rev,
519 s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
c91f0d92
JK
520 rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
521 rev.diffopt.detect_rename = 1;
4ba0cb27
KH
522 rev.diffopt.file = s->fp;
523 rev.diffopt.close_file = 0;
4f672ad6
JK
524 /*
525 * If we're not going to stdout, then we definitely don't
526 * want color, since we are going to the commit message
527 * file (and even the "auto" setting won't work, since it
528 * will have checked isatty on stdout).
529 */
530 if (s->fp != stdout)
531 DIFF_OPT_CLR(&rev.diffopt, COLOR_DIFF);
c91f0d92
JK
532 run_diff_index(&rev, 1);
533}
534
b6975ab5
JH
535static void wt_status_print_tracking(struct wt_status *s)
536{
537 struct strbuf sb = STRBUF_INIT;
538 const char *cp, *ep;
539 struct branch *branch;
540
541 assert(s->branch && !s->is_initial);
542 if (prefixcmp(s->branch, "refs/heads/"))
543 return;
544 branch = branch_get(s->branch + 11);
545 if (!format_tracking_info(branch, &sb))
546 return;
547
548 for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
d249b098 549 color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
b6975ab5 550 "# %.*s", (int)(ep - cp), cp);
d249b098 551 color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
b6975ab5
JH
552}
553
c91f0d92
JK
554void wt_status_print(struct wt_status *s)
555{
d249b098 556 const char *branch_color = color(WT_STATUS_HEADER, s);
98bf8a47 557
bda324cf
JH
558 if (s->branch) {
559 const char *on_what = "On branch ";
560 const char *branch_name = s->branch;
cc44c765 561 if (!prefixcmp(branch_name, "refs/heads/"))
bda324cf
JH
562 branch_name += 11;
563 else if (!strcmp(branch_name, "HEAD")) {
564 branch_name = "";
d249b098 565 branch_color = color(WT_STATUS_NOBRANCH, s);
bda324cf
JH
566 on_what = "Not currently on any branch.";
567 }
d249b098 568 color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# ");
950ce2e2 569 color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
b6975ab5
JH
570 if (!s->is_initial)
571 wt_status_print_tracking(s);
bda324cf 572 }
c91f0d92
JK
573
574 if (s->is_initial) {
d249b098
JH
575 color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
576 color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit");
577 color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
c91f0d92
JK
578 }
579
c1e255b7 580 wt_status_print_updated(s);
228e7b5d 581 wt_status_print_unmerged(s);
c91f0d92 582 wt_status_print_changed(s);
f17a5d34
JL
583 if (s->submodule_summary) {
584 wt_status_print_submodule_summary(s, 0); /* staged */
585 wt_status_print_submodule_summary(s, 1); /* unstaged */
586 }
d249b098 587 if (s->show_untracked_files)
6c2ce048
MSO
588 wt_status_print_untracked(s);
589 else if (s->commitable)
590 fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
c91f0d92 591
1324fb6f 592 if (s->verbose)
c91f0d92 593 wt_status_print_verbose(s);
6e458bf6
JR
594 if (!s->commitable) {
595 if (s->amend)
f26a0012 596 fprintf(s->fp, "# No changes\n");
37d07f8f
JH
597 else if (s->nowarn)
598 ; /* nothing */
2a3a3c24 599 else if (s->workdir_dirty)
dcbc7bbe 600 printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
76378683 601 else if (s->untracked.nr)
2a3a3c24
JR
602 printf("nothing added to commit but untracked files present (use \"git add\" to track)\n");
603 else if (s->is_initial)
604 printf("nothing to commit (create/copy files and use \"git add\" to track)\n");
d249b098 605 else if (!s->show_untracked_files)
6c2ce048 606 printf("nothing to commit (use -u to show untracked files)\n");
2a3a3c24
JR
607 else
608 printf("nothing to commit (working directory clean)\n");
6e458bf6 609 }
c91f0d92 610}
84dbe7b8
MG
611
612static void wt_shortstatus_unmerged(int null_termination, struct string_list_item *it,
613 struct wt_status *s)
614{
615 struct wt_status_change_data *d = it->util;
616 const char *how = "??";
617
618 switch (d->stagemask) {
619 case 1: how = "DD"; break; /* both deleted */
620 case 2: how = "AU"; break; /* added by us */
621 case 3: how = "UD"; break; /* deleted by them */
622 case 4: how = "UA"; break; /* added by them */
623 case 5: how = "DU"; break; /* deleted by us */
624 case 6: how = "AA"; break; /* both added */
625 case 7: how = "UU"; break; /* both modified */
626 }
3fe2a894 627 color_fprintf(s->fp, color(WT_STATUS_UNMERGED, s), "%s", how);
84dbe7b8 628 if (null_termination) {
3fe2a894 629 fprintf(stdout, " %s%c", it->string, 0);
84dbe7b8
MG
630 } else {
631 struct strbuf onebuf = STRBUF_INIT;
632 const char *one;
633 one = quote_path(it->string, -1, &onebuf, s->prefix);
3fe2a894 634 printf(" %s\n", one);
84dbe7b8
MG
635 strbuf_release(&onebuf);
636 }
637}
638
639static void wt_shortstatus_status(int null_termination, struct string_list_item *it,
640 struct wt_status *s)
641{
642 struct wt_status_change_data *d = it->util;
643
3fe2a894
MG
644 if (d->index_status)
645 color_fprintf(s->fp, color(WT_STATUS_UPDATED, s), "%c", d->index_status);
646 else
647 putchar(' ');
648 if (d->worktree_status)
649 color_fprintf(s->fp, color(WT_STATUS_CHANGED, s), "%c", d->worktree_status);
650 else
651 putchar(' ');
652 putchar(' ');
84dbe7b8
MG
653 if (null_termination) {
654 fprintf(stdout, "%s%c", it->string, 0);
655 if (d->head_path)
656 fprintf(stdout, "%s%c", d->head_path, 0);
657 } else {
658 struct strbuf onebuf = STRBUF_INIT;
659 const char *one;
660 if (d->head_path) {
661 one = quote_path(d->head_path, -1, &onebuf, s->prefix);
662 printf("%s -> ", one);
663 strbuf_release(&onebuf);
664 }
665 one = quote_path(it->string, -1, &onebuf, s->prefix);
666 printf("%s\n", one);
667 strbuf_release(&onebuf);
668 }
669}
670
671static void wt_shortstatus_untracked(int null_termination, struct string_list_item *it,
672 struct wt_status *s)
673{
674 if (null_termination) {
675 fprintf(stdout, "?? %s%c", it->string, 0);
676 } else {
677 struct strbuf onebuf = STRBUF_INIT;
678 const char *one;
679 one = quote_path(it->string, -1, &onebuf, s->prefix);
3fe2a894
MG
680 color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "??");
681 printf(" %s\n", one);
84dbe7b8
MG
682 strbuf_release(&onebuf);
683 }
684}
685
686void wt_shortstatus_print(struct wt_status *s, int null_termination)
687{
688 int i;
689 for (i = 0; i < s->change.nr; i++) {
690 struct wt_status_change_data *d;
691 struct string_list_item *it;
692
693 it = &(s->change.items[i]);
694 d = it->util;
695 if (d->stagemask)
696 wt_shortstatus_unmerged(null_termination, it, s);
697 else
698 wt_shortstatus_status(null_termination, it, s);
699 }
700 for (i = 0; i < s->untracked.nr; i++) {
701 struct string_list_item *it;
702
703 it = &(s->untracked.items[i]);
704 wt_shortstatus_untracked(null_termination, it, s);
705 }
706}
4a7cc2fd
JK
707
708void wt_porcelain_print(struct wt_status *s, int null_termination)
709{
710 s->use_color = 0;
8661768f
JK
711 s->relative_paths = 0;
712 s->prefix = NULL;
4a7cc2fd
JK
713 wt_shortstatus_print(s, null_termination);
714}