silence a bunch of format-zero-length warnings
[git] / wt-status.c
1 #include "cache.h"
2 #include "pathspec.h"
3 #include "wt-status.h"
4 #include "object.h"
5 #include "dir.h"
6 #include "commit.h"
7 #include "diff.h"
8 #include "revision.h"
9 #include "diffcore.h"
10 #include "quote.h"
11 #include "run-command.h"
12 #include "argv-array.h"
13 #include "remote.h"
14 #include "refs.h"
15 #include "submodule.h"
16 #include "column.h"
17 #include "strbuf.h"
18
19 static char cut_line[] =
20 "------------------------ >8 ------------------------\n";
21
22 static char default_wt_status_colors[][COLOR_MAXLEN] = {
23         GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
24         GIT_COLOR_GREEN,  /* WT_STATUS_UPDATED */
25         GIT_COLOR_RED,    /* WT_STATUS_CHANGED */
26         GIT_COLOR_RED,    /* WT_STATUS_UNTRACKED */
27         GIT_COLOR_RED,    /* WT_STATUS_NOBRANCH */
28         GIT_COLOR_RED,    /* WT_STATUS_UNMERGED */
29         GIT_COLOR_GREEN,  /* WT_STATUS_LOCAL_BRANCH */
30         GIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */
31         GIT_COLOR_NIL,    /* WT_STATUS_ONBRANCH */
32 };
33
34 static const char *color(int slot, struct wt_status *s)
35 {
36         const char *c = "";
37         if (want_color(s->use_color))
38                 c = s->color_palette[slot];
39         if (slot == WT_STATUS_ONBRANCH && color_is_nil(c))
40                 c = s->color_palette[WT_STATUS_HEADER];
41         return c;
42 }
43
44 static void status_vprintf(struct wt_status *s, int at_bol, const char *color,
45                 const char *fmt, va_list ap, const char *trail)
46 {
47         struct strbuf sb = STRBUF_INIT;
48         struct strbuf linebuf = STRBUF_INIT;
49         const char *line, *eol;
50
51         strbuf_vaddf(&sb, fmt, ap);
52         if (!sb.len) {
53                 if (s->display_comment_prefix) {
54                         strbuf_addch(&sb, comment_line_char);
55                         if (!trail)
56                                 strbuf_addch(&sb, ' ');
57                 }
58                 color_print_strbuf(s->fp, color, &sb);
59                 if (trail)
60                         fprintf(s->fp, "%s", trail);
61                 strbuf_release(&sb);
62                 return;
63         }
64         for (line = sb.buf; *line; line = eol + 1) {
65                 eol = strchr(line, '\n');
66
67                 strbuf_reset(&linebuf);
68                 if (at_bol && s->display_comment_prefix) {
69                         strbuf_addch(&linebuf, comment_line_char);
70                         if (*line != '\n' && *line != '\t')
71                                 strbuf_addch(&linebuf, ' ');
72                 }
73                 if (eol)
74                         strbuf_add(&linebuf, line, eol - line);
75                 else
76                         strbuf_addstr(&linebuf, line);
77                 color_print_strbuf(s->fp, color, &linebuf);
78                 if (eol)
79                         fprintf(s->fp, "\n");
80                 else
81                         break;
82                 at_bol = 1;
83         }
84         if (trail)
85                 fprintf(s->fp, "%s", trail);
86         strbuf_release(&linebuf);
87         strbuf_release(&sb);
88 }
89
90 void status_printf_ln(struct wt_status *s, const char *color,
91                         const char *fmt, ...)
92 {
93         va_list ap;
94
95         va_start(ap, fmt);
96         status_vprintf(s, 1, color, fmt, ap, "\n");
97         va_end(ap);
98 }
99
100 void status_printf(struct wt_status *s, const char *color,
101                         const char *fmt, ...)
102 {
103         va_list ap;
104
105         va_start(ap, fmt);
106         status_vprintf(s, 1, color, fmt, ap, NULL);
107         va_end(ap);
108 }
109
110 static void status_printf_more(struct wt_status *s, const char *color,
111                                const char *fmt, ...)
112 {
113         va_list ap;
114
115         va_start(ap, fmt);
116         status_vprintf(s, 0, color, fmt, ap, NULL);
117         va_end(ap);
118 }
119
120 void wt_status_prepare(struct wt_status *s)
121 {
122         unsigned char sha1[20];
123
124         memset(s, 0, sizeof(*s));
125         memcpy(s->color_palette, default_wt_status_colors,
126                sizeof(default_wt_status_colors));
127         s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
128         s->use_color = -1;
129         s->relative_paths = 1;
130         s->branch = resolve_refdup("HEAD", sha1, 0, NULL);
131         s->reference = "HEAD";
132         s->fp = stdout;
133         s->index_file = get_index_file();
134         s->change.strdup_strings = 1;
135         s->untracked.strdup_strings = 1;
136         s->ignored.strdup_strings = 1;
137         s->show_branch = -1;  /* unspecified */
138         s->display_comment_prefix = 0;
139 }
140
141 static void wt_status_print_unmerged_header(struct wt_status *s)
142 {
143         int i;
144         int del_mod_conflict = 0;
145         int both_deleted = 0;
146         int not_deleted = 0;
147         const char *c = color(WT_STATUS_HEADER, s);
148
149         status_printf_ln(s, c, _("Unmerged paths:"));
150
151         for (i = 0; i < s->change.nr; i++) {
152                 struct string_list_item *it = &(s->change.items[i]);
153                 struct wt_status_change_data *d = it->util;
154
155                 switch (d->stagemask) {
156                 case 0:
157                         break;
158                 case 1:
159                         both_deleted = 1;
160                         break;
161                 case 3:
162                 case 5:
163                         del_mod_conflict = 1;
164                         break;
165                 default:
166                         not_deleted = 1;
167                         break;
168                 }
169         }
170
171         if (!s->hints)
172                 return;
173         if (s->whence != FROM_COMMIT)
174                 ;
175         else if (!s->is_initial)
176                 status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
177         else
178                 status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
179
180         if (!both_deleted) {
181                 if (!del_mod_conflict)
182                         status_printf_ln(s, c, _("  (use \"git add <file>...\" to mark resolution)"));
183                 else
184                         status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
185         } else if (!del_mod_conflict && !not_deleted) {
186                 status_printf_ln(s, c, _("  (use \"git rm <file>...\" to mark resolution)"));
187         } else {
188                 status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
189         }
190         status_printf_ln(s, c, "%s", "");
191 }
192
193 static void wt_status_print_cached_header(struct wt_status *s)
194 {
195         const char *c = color(WT_STATUS_HEADER, s);
196
197         status_printf_ln(s, c, _("Changes to be committed:"));
198         if (!s->hints)
199                 return;
200         if (s->whence != FROM_COMMIT)
201                 ; /* NEEDSWORK: use "git reset --unresolve"??? */
202         else if (!s->is_initial)
203                 status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
204         else
205                 status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
206         status_printf_ln(s, c, "%s", "");
207 }
208
209 static void wt_status_print_dirty_header(struct wt_status *s,
210                                          int has_deleted,
211                                          int has_dirty_submodules)
212 {
213         const char *c = color(WT_STATUS_HEADER, s);
214
215         status_printf_ln(s, c, _("Changes not staged for commit:"));
216         if (!s->hints)
217                 return;
218         if (!has_deleted)
219                 status_printf_ln(s, c, _("  (use \"git add <file>...\" to update what will be committed)"));
220         else
221                 status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" to update what will be committed)"));
222         status_printf_ln(s, c, _("  (use \"git checkout -- <file>...\" to discard changes in working directory)"));
223         if (has_dirty_submodules)
224                 status_printf_ln(s, c, _("  (commit or discard the untracked or modified content in submodules)"));
225         status_printf_ln(s, c, "%s", "");
226 }
227
228 static void wt_status_print_other_header(struct wt_status *s,
229                                          const char *what,
230                                          const char *how)
231 {
232         const char *c = color(WT_STATUS_HEADER, s);
233         status_printf_ln(s, c, "%s:", what);
234         if (!s->hints)
235                 return;
236         status_printf_ln(s, c, _("  (use \"git %s <file>...\" to include in what will be committed)"), how);
237         status_printf_ln(s, c, "%s", "");
238 }
239
240 static void wt_status_print_trailer(struct wt_status *s)
241 {
242         status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
243 }
244
245 #define quote_path quote_path_relative
246
247 static void wt_status_print_unmerged_data(struct wt_status *s,
248                                           struct string_list_item *it)
249 {
250         const char *c = color(WT_STATUS_UNMERGED, s);
251         struct wt_status_change_data *d = it->util;
252         struct strbuf onebuf = STRBUF_INIT;
253         const char *one, *how = _("bug");
254
255         one = quote_path(it->string, s->prefix, &onebuf);
256         status_printf(s, color(WT_STATUS_HEADER, s), "\t");
257         switch (d->stagemask) {
258         case 1: how = _("both deleted:"); break;
259         case 2: how = _("added by us:"); break;
260         case 3: how = _("deleted by them:"); break;
261         case 4: how = _("added by them:"); break;
262         case 5: how = _("deleted by us:"); break;
263         case 6: how = _("both added:"); break;
264         case 7: how = _("both modified:"); break;
265         }
266         status_printf_more(s, c, "%-20s%s\n", how, one);
267         strbuf_release(&onebuf);
268 }
269
270 static void wt_status_print_change_data(struct wt_status *s,
271                                         int change_type,
272                                         struct string_list_item *it)
273 {
274         struct wt_status_change_data *d = it->util;
275         const char *c = color(change_type, s);
276         int status;
277         char *one_name;
278         char *two_name;
279         const char *one, *two;
280         struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
281         struct strbuf extra = STRBUF_INIT;
282
283         one_name = two_name = it->string;
284         switch (change_type) {
285         case WT_STATUS_UPDATED:
286                 status = d->index_status;
287                 if (d->head_path)
288                         one_name = d->head_path;
289                 break;
290         case WT_STATUS_CHANGED:
291                 if (d->new_submodule_commits || d->dirty_submodule) {
292                         strbuf_addstr(&extra, " (");
293                         if (d->new_submodule_commits)
294                                 strbuf_addf(&extra, _("new commits, "));
295                         if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
296                                 strbuf_addf(&extra, _("modified content, "));
297                         if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
298                                 strbuf_addf(&extra, _("untracked content, "));
299                         strbuf_setlen(&extra, extra.len - 2);
300                         strbuf_addch(&extra, ')');
301                 }
302                 status = d->worktree_status;
303                 break;
304         default:
305                 die("BUG: unhandled change_type %d in wt_status_print_change_data",
306                     change_type);
307         }
308
309         one = quote_path(one_name, s->prefix, &onebuf);
310         two = quote_path(two_name, s->prefix, &twobuf);
311
312         status_printf(s, color(WT_STATUS_HEADER, s), "\t");
313         switch (status) {
314         case DIFF_STATUS_ADDED:
315                 status_printf_more(s, c, _("new file:   %s"), one);
316                 break;
317         case DIFF_STATUS_COPIED:
318                 status_printf_more(s, c, _("copied:     %s -> %s"), one, two);
319                 break;
320         case DIFF_STATUS_DELETED:
321                 status_printf_more(s, c, _("deleted:    %s"), one);
322                 break;
323         case DIFF_STATUS_MODIFIED:
324                 status_printf_more(s, c, _("modified:   %s"), one);
325                 break;
326         case DIFF_STATUS_RENAMED:
327                 status_printf_more(s, c, _("renamed:    %s -> %s"), one, two);
328                 break;
329         case DIFF_STATUS_TYPE_CHANGED:
330                 status_printf_more(s, c, _("typechange: %s"), one);
331                 break;
332         case DIFF_STATUS_UNKNOWN:
333                 status_printf_more(s, c, _("unknown:    %s"), one);
334                 break;
335         case DIFF_STATUS_UNMERGED:
336                 status_printf_more(s, c, _("unmerged:   %s"), one);
337                 break;
338         default:
339                 die(_("bug: unhandled diff status %c"), status);
340         }
341         if (extra.len) {
342                 status_printf_more(s, color(WT_STATUS_HEADER, s), "%s", extra.buf);
343                 strbuf_release(&extra);
344         }
345         status_printf_more(s, GIT_COLOR_NORMAL, "\n");
346         strbuf_release(&onebuf);
347         strbuf_release(&twobuf);
348 }
349
350 static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
351                                          struct diff_options *options,
352                                          void *data)
353 {
354         struct wt_status *s = data;
355         int i;
356
357         if (!q->nr)
358                 return;
359         s->workdir_dirty = 1;
360         for (i = 0; i < q->nr; i++) {
361                 struct diff_filepair *p;
362                 struct string_list_item *it;
363                 struct wt_status_change_data *d;
364
365                 p = q->queue[i];
366                 it = string_list_insert(&s->change, p->one->path);
367                 d = it->util;
368                 if (!d) {
369                         d = xcalloc(1, sizeof(*d));
370                         it->util = d;
371                 }
372                 if (!d->worktree_status)
373                         d->worktree_status = p->status;
374                 d->dirty_submodule = p->two->dirty_submodule;
375                 if (S_ISGITLINK(p->two->mode))
376                         d->new_submodule_commits = !!hashcmp(p->one->sha1, p->two->sha1);
377         }
378 }
379
380 static int unmerged_mask(const char *path)
381 {
382         int pos, mask;
383         const struct cache_entry *ce;
384
385         pos = cache_name_pos(path, strlen(path));
386         if (0 <= pos)
387                 return 0;
388
389         mask = 0;
390         pos = -pos-1;
391         while (pos < active_nr) {
392                 ce = active_cache[pos++];
393                 if (strcmp(ce->name, path) || !ce_stage(ce))
394                         break;
395                 mask |= (1 << (ce_stage(ce) - 1));
396         }
397         return mask;
398 }
399
400 static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
401                                          struct diff_options *options,
402                                          void *data)
403 {
404         struct wt_status *s = data;
405         int i;
406
407         for (i = 0; i < q->nr; i++) {
408                 struct diff_filepair *p;
409                 struct string_list_item *it;
410                 struct wt_status_change_data *d;
411
412                 p = q->queue[i];
413                 it = string_list_insert(&s->change, p->two->path);
414                 d = it->util;
415                 if (!d) {
416                         d = xcalloc(1, sizeof(*d));
417                         it->util = d;
418                 }
419                 if (!d->index_status)
420                         d->index_status = p->status;
421                 switch (p->status) {
422                 case DIFF_STATUS_COPIED:
423                 case DIFF_STATUS_RENAMED:
424                         d->head_path = xstrdup(p->one->path);
425                         break;
426                 case DIFF_STATUS_UNMERGED:
427                         d->stagemask = unmerged_mask(p->two->path);
428                         break;
429                 }
430         }
431 }
432
433 static void wt_status_collect_changes_worktree(struct wt_status *s)
434 {
435         struct rev_info rev;
436
437         init_revisions(&rev, NULL);
438         setup_revisions(0, NULL, &rev, NULL);
439         rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
440         DIFF_OPT_SET(&rev.diffopt, DIRTY_SUBMODULES);
441         if (!s->show_untracked_files)
442                 DIFF_OPT_SET(&rev.diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
443         if (s->ignore_submodule_arg) {
444                 DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
445                 handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
446         }
447         rev.diffopt.format_callback = wt_status_collect_changed_cb;
448         rev.diffopt.format_callback_data = s;
449         copy_pathspec(&rev.prune_data, &s->pathspec);
450         run_diff_files(&rev, 0);
451 }
452
453 static void wt_status_collect_changes_index(struct wt_status *s)
454 {
455         struct rev_info rev;
456         struct setup_revision_opt opt;
457
458         init_revisions(&rev, NULL);
459         memset(&opt, 0, sizeof(opt));
460         opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
461         setup_revisions(0, NULL, &rev, &opt);
462
463         if (s->ignore_submodule_arg) {
464                 DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
465                 handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
466         }
467
468         rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
469         rev.diffopt.format_callback = wt_status_collect_updated_cb;
470         rev.diffopt.format_callback_data = s;
471         rev.diffopt.detect_rename = 1;
472         rev.diffopt.rename_limit = 200;
473         rev.diffopt.break_opt = 0;
474         copy_pathspec(&rev.prune_data, &s->pathspec);
475         run_diff_index(&rev, 1);
476 }
477
478 static void wt_status_collect_changes_initial(struct wt_status *s)
479 {
480         int i;
481
482         for (i = 0; i < active_nr; i++) {
483                 struct string_list_item *it;
484                 struct wt_status_change_data *d;
485                 const struct cache_entry *ce = active_cache[i];
486
487                 if (!ce_path_match(ce, &s->pathspec))
488                         continue;
489                 it = string_list_insert(&s->change, ce->name);
490                 d = it->util;
491                 if (!d) {
492                         d = xcalloc(1, sizeof(*d));
493                         it->util = d;
494                 }
495                 if (ce_stage(ce)) {
496                         d->index_status = DIFF_STATUS_UNMERGED;
497                         d->stagemask |= (1 << (ce_stage(ce) - 1));
498                 }
499                 else
500                         d->index_status = DIFF_STATUS_ADDED;
501         }
502 }
503
504 static void wt_status_collect_untracked(struct wt_status *s)
505 {
506         int i;
507         struct dir_struct dir;
508         struct timeval t_begin;
509
510         if (!s->show_untracked_files)
511                 return;
512
513         if (advice_status_u_option)
514                 gettimeofday(&t_begin, NULL);
515
516         memset(&dir, 0, sizeof(dir));
517         if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
518                 dir.flags |=
519                         DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
520         if (s->show_ignored_files)
521                 dir.flags |= DIR_SHOW_IGNORED_TOO;
522         setup_standard_excludes(&dir);
523
524         fill_directory(&dir, &s->pathspec);
525
526         for (i = 0; i < dir.nr; i++) {
527                 struct dir_entry *ent = dir.entries[i];
528                 if (cache_name_is_other(ent->name, ent->len) &&
529                     match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL))
530                         string_list_insert(&s->untracked, ent->name);
531                 free(ent);
532         }
533
534         for (i = 0; i < dir.ignored_nr; i++) {
535                 struct dir_entry *ent = dir.ignored[i];
536                 if (cache_name_is_other(ent->name, ent->len) &&
537                     match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL))
538                         string_list_insert(&s->ignored, ent->name);
539                 free(ent);
540         }
541
542         free(dir.entries);
543         free(dir.ignored);
544         clear_directory(&dir);
545
546         if (advice_status_u_option) {
547                 struct timeval t_end;
548                 gettimeofday(&t_end, NULL);
549                 s->untracked_in_ms =
550                         (uint64_t)t_end.tv_sec * 1000 + t_end.tv_usec / 1000 -
551                         ((uint64_t)t_begin.tv_sec * 1000 + t_begin.tv_usec / 1000);
552         }
553 }
554
555 void wt_status_collect(struct wt_status *s)
556 {
557         wt_status_collect_changes_worktree(s);
558
559         if (s->is_initial)
560                 wt_status_collect_changes_initial(s);
561         else
562                 wt_status_collect_changes_index(s);
563         wt_status_collect_untracked(s);
564 }
565
566 static void wt_status_print_unmerged(struct wt_status *s)
567 {
568         int shown_header = 0;
569         int i;
570
571         for (i = 0; i < s->change.nr; i++) {
572                 struct wt_status_change_data *d;
573                 struct string_list_item *it;
574                 it = &(s->change.items[i]);
575                 d = it->util;
576                 if (!d->stagemask)
577                         continue;
578                 if (!shown_header) {
579                         wt_status_print_unmerged_header(s);
580                         shown_header = 1;
581                 }
582                 wt_status_print_unmerged_data(s, it);
583         }
584         if (shown_header)
585                 wt_status_print_trailer(s);
586
587 }
588
589 static void wt_status_print_updated(struct wt_status *s)
590 {
591         int shown_header = 0;
592         int i;
593
594         for (i = 0; i < s->change.nr; i++) {
595                 struct wt_status_change_data *d;
596                 struct string_list_item *it;
597                 it = &(s->change.items[i]);
598                 d = it->util;
599                 if (!d->index_status ||
600                     d->index_status == DIFF_STATUS_UNMERGED)
601                         continue;
602                 if (!shown_header) {
603                         wt_status_print_cached_header(s);
604                         s->commitable = 1;
605                         shown_header = 1;
606                 }
607                 wt_status_print_change_data(s, WT_STATUS_UPDATED, it);
608         }
609         if (shown_header)
610                 wt_status_print_trailer(s);
611 }
612
613 /*
614  * -1 : has delete
615  *  0 : no change
616  *  1 : some change but no delete
617  */
618 static int wt_status_check_worktree_changes(struct wt_status *s,
619                                              int *dirty_submodules)
620 {
621         int i;
622         int changes = 0;
623
624         *dirty_submodules = 0;
625
626         for (i = 0; i < s->change.nr; i++) {
627                 struct wt_status_change_data *d;
628                 d = s->change.items[i].util;
629                 if (!d->worktree_status ||
630                     d->worktree_status == DIFF_STATUS_UNMERGED)
631                         continue;
632                 if (!changes)
633                         changes = 1;
634                 if (d->dirty_submodule)
635                         *dirty_submodules = 1;
636                 if (d->worktree_status == DIFF_STATUS_DELETED)
637                         changes = -1;
638         }
639         return changes;
640 }
641
642 static void wt_status_print_changed(struct wt_status *s)
643 {
644         int i, dirty_submodules;
645         int worktree_changes = wt_status_check_worktree_changes(s, &dirty_submodules);
646
647         if (!worktree_changes)
648                 return;
649
650         wt_status_print_dirty_header(s, worktree_changes < 0, dirty_submodules);
651
652         for (i = 0; i < s->change.nr; i++) {
653                 struct wt_status_change_data *d;
654                 struct string_list_item *it;
655                 it = &(s->change.items[i]);
656                 d = it->util;
657                 if (!d->worktree_status ||
658                     d->worktree_status == DIFF_STATUS_UNMERGED)
659                         continue;
660                 wt_status_print_change_data(s, WT_STATUS_CHANGED, it);
661         }
662         wt_status_print_trailer(s);
663 }
664
665 static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted)
666 {
667         struct child_process sm_summary;
668         char summary_limit[64];
669         char index[PATH_MAX];
670         const char *env[] = { NULL, NULL };
671         struct argv_array argv = ARGV_ARRAY_INIT;
672         struct strbuf cmd_stdout = STRBUF_INIT;
673         struct strbuf summary = STRBUF_INIT;
674         char *summary_content;
675         size_t len;
676
677         sprintf(summary_limit, "%d", s->submodule_summary);
678         snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
679
680         env[0] = index;
681         argv_array_push(&argv, "submodule");
682         argv_array_push(&argv, "summary");
683         argv_array_push(&argv, uncommitted ? "--files" : "--cached");
684         argv_array_push(&argv, "--for-status");
685         argv_array_push(&argv, "--summary-limit");
686         argv_array_push(&argv, summary_limit);
687         if (!uncommitted)
688                 argv_array_push(&argv, s->amend ? "HEAD^" : "HEAD");
689
690         memset(&sm_summary, 0, sizeof(sm_summary));
691         sm_summary.argv = argv.argv;
692         sm_summary.env = env;
693         sm_summary.git_cmd = 1;
694         sm_summary.no_stdin = 1;
695         fflush(s->fp);
696         sm_summary.out = -1;
697
698         run_command(&sm_summary);
699         argv_array_clear(&argv);
700
701         len = strbuf_read(&cmd_stdout, sm_summary.out, 1024);
702
703         /* prepend header, only if there's an actual output */
704         if (len) {
705                 if (uncommitted)
706                         strbuf_addstr(&summary, _("Submodules changed but not updated:"));
707                 else
708                         strbuf_addstr(&summary, _("Submodule changes to be committed:"));
709                 strbuf_addstr(&summary, "\n\n");
710         }
711         strbuf_addbuf(&summary, &cmd_stdout);
712         strbuf_release(&cmd_stdout);
713
714         if (s->display_comment_prefix) {
715                 summary_content = strbuf_detach(&summary, &len);
716                 strbuf_add_commented_lines(&summary, summary_content, len);
717                 free(summary_content);
718         }
719
720         fputs(summary.buf, s->fp);
721         strbuf_release(&summary);
722 }
723
724 static void wt_status_print_other(struct wt_status *s,
725                                   struct string_list *l,
726                                   const char *what,
727                                   const char *how)
728 {
729         int i;
730         struct strbuf buf = STRBUF_INIT;
731         static struct string_list output = STRING_LIST_INIT_DUP;
732         struct column_options copts;
733
734         if (!l->nr)
735                 return;
736
737         wt_status_print_other_header(s, what, how);
738
739         for (i = 0; i < l->nr; i++) {
740                 struct string_list_item *it;
741                 const char *path;
742                 it = &(l->items[i]);
743                 path = quote_path(it->string, s->prefix, &buf);
744                 if (column_active(s->colopts)) {
745                         string_list_append(&output, path);
746                         continue;
747                 }
748                 status_printf(s, color(WT_STATUS_HEADER, s), "\t");
749                 status_printf_more(s, color(WT_STATUS_UNTRACKED, s),
750                                    "%s\n", path);
751         }
752
753         strbuf_release(&buf);
754         if (!column_active(s->colopts))
755                 goto conclude;
756
757         strbuf_addf(&buf, "%s%s\t%s",
758                     color(WT_STATUS_HEADER, s),
759                     s->display_comment_prefix ? "#" : "",
760                     color(WT_STATUS_UNTRACKED, s));
761         memset(&copts, 0, sizeof(copts));
762         copts.padding = 1;
763         copts.indent = buf.buf;
764         if (want_color(s->use_color))
765                 copts.nl = GIT_COLOR_RESET "\n";
766         print_columns(&output, s->colopts, &copts);
767         string_list_clear(&output, 0);
768         strbuf_release(&buf);
769 conclude:
770         status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
771 }
772
773 void wt_status_truncate_message_at_cut_line(struct strbuf *buf)
774 {
775         const char *p;
776         struct strbuf pattern = STRBUF_INIT;
777
778         strbuf_addf(&pattern, "%c %s", comment_line_char, cut_line);
779         p = strstr(buf->buf, pattern.buf);
780         if (p && (p == buf->buf || p[-1] == '\n'))
781                 strbuf_setlen(buf, p - buf->buf);
782         strbuf_release(&pattern);
783 }
784
785 static void wt_status_print_verbose(struct wt_status *s)
786 {
787         struct rev_info rev;
788         struct setup_revision_opt opt;
789
790         init_revisions(&rev, NULL);
791         DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
792
793         memset(&opt, 0, sizeof(opt));
794         opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
795         setup_revisions(0, NULL, &rev, &opt);
796
797         rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
798         rev.diffopt.detect_rename = 1;
799         rev.diffopt.file = s->fp;
800         rev.diffopt.close_file = 0;
801         /*
802          * If we're not going to stdout, then we definitely don't
803          * want color, since we are going to the commit message
804          * file (and even the "auto" setting won't work, since it
805          * will have checked isatty on stdout). But we then do want
806          * to insert the scissor line here to reliably remove the
807          * diff before committing.
808          */
809         if (s->fp != stdout) {
810                 const char *explanation = _("Do not touch the line above.\nEverything below will be removed.");
811                 struct strbuf buf = STRBUF_INIT;
812
813                 rev.diffopt.use_color = 0;
814                 fprintf(s->fp, "%c %s", comment_line_char, cut_line);
815                 strbuf_add_commented_lines(&buf, explanation, strlen(explanation));
816                 fputs(buf.buf, s->fp);
817                 strbuf_release(&buf);
818         }
819         run_diff_index(&rev, 1);
820 }
821
822 static void wt_status_print_tracking(struct wt_status *s)
823 {
824         struct strbuf sb = STRBUF_INIT;
825         const char *cp, *ep;
826         struct branch *branch;
827         char comment_line_string[3];
828         int i;
829
830         assert(s->branch && !s->is_initial);
831         if (prefixcmp(s->branch, "refs/heads/"))
832                 return;
833         branch = branch_get(s->branch + 11);
834         if (!format_tracking_info(branch, &sb))
835                 return;
836
837         i = 0;
838         if (s->display_comment_prefix) {
839                 comment_line_string[i++] = comment_line_char;
840                 comment_line_string[i++] = ' ';
841         }
842         comment_line_string[i] = '\0';
843
844         for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
845                 color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
846                                  "%s%.*s", comment_line_string,
847                                  (int)(ep - cp), cp);
848         if (s->display_comment_prefix)
849                 color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c",
850                                  comment_line_char);
851         else
852                 fputs("", s->fp);
853 }
854
855 static int has_unmerged(struct wt_status *s)
856 {
857         int i;
858
859         for (i = 0; i < s->change.nr; i++) {
860                 struct wt_status_change_data *d;
861                 d = s->change.items[i].util;
862                 if (d->stagemask)
863                         return 1;
864         }
865         return 0;
866 }
867
868 static void show_merge_in_progress(struct wt_status *s,
869                                 struct wt_status_state *state,
870                                 const char *color)
871 {
872         if (has_unmerged(s)) {
873                 status_printf_ln(s, color, _("You have unmerged paths."));
874                 if (s->hints)
875                         status_printf_ln(s, color,
876                                 _("  (fix conflicts and run \"git commit\")"));
877         } else {
878                 status_printf_ln(s, color,
879                         _("All conflicts fixed but you are still merging."));
880                 if (s->hints)
881                         status_printf_ln(s, color,
882                                 _("  (use \"git commit\" to conclude merge)"));
883         }
884         wt_status_print_trailer(s);
885 }
886
887 static void show_am_in_progress(struct wt_status *s,
888                                 struct wt_status_state *state,
889                                 const char *color)
890 {
891         status_printf_ln(s, color,
892                 _("You are in the middle of an am session."));
893         if (state->am_empty_patch)
894                 status_printf_ln(s, color,
895                         _("The current patch is empty."));
896         if (s->hints) {
897                 if (!state->am_empty_patch)
898                         status_printf_ln(s, color,
899                                 _("  (fix conflicts and then run \"git am --continue\")"));
900                 status_printf_ln(s, color,
901                         _("  (use \"git am --skip\" to skip this patch)"));
902                 status_printf_ln(s, color,
903                         _("  (use \"git am --abort\" to restore the original branch)"));
904         }
905         wt_status_print_trailer(s);
906 }
907
908 static char *read_line_from_git_path(const char *filename)
909 {
910         struct strbuf buf = STRBUF_INIT;
911         FILE *fp = fopen(git_path("%s", filename), "r");
912         if (!fp) {
913                 strbuf_release(&buf);
914                 return NULL;
915         }
916         strbuf_getline(&buf, fp, '\n');
917         if (!fclose(fp)) {
918                 return strbuf_detach(&buf, NULL);
919         } else {
920                 strbuf_release(&buf);
921                 return NULL;
922         }
923 }
924
925 static int split_commit_in_progress(struct wt_status *s)
926 {
927         int split_in_progress = 0;
928         char *head = read_line_from_git_path("HEAD");
929         char *orig_head = read_line_from_git_path("ORIG_HEAD");
930         char *rebase_amend = read_line_from_git_path("rebase-merge/amend");
931         char *rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
932
933         if (!head || !orig_head || !rebase_amend || !rebase_orig_head ||
934             !s->branch || strcmp(s->branch, "HEAD"))
935                 return split_in_progress;
936
937         if (!strcmp(rebase_amend, rebase_orig_head)) {
938                 if (strcmp(head, rebase_amend))
939                         split_in_progress = 1;
940         } else if (strcmp(orig_head, rebase_orig_head)) {
941                 split_in_progress = 1;
942         }
943
944         if (!s->amend && !s->nowarn && !s->workdir_dirty)
945                 split_in_progress = 0;
946
947         free(head);
948         free(orig_head);
949         free(rebase_amend);
950         free(rebase_orig_head);
951         return split_in_progress;
952 }
953
954 static void show_rebase_in_progress(struct wt_status *s,
955                                 struct wt_status_state *state,
956                                 const char *color)
957 {
958         struct stat st;
959
960         if (has_unmerged(s)) {
961                 if (state->branch)
962                         status_printf_ln(s, color,
963                                          _("You are currently rebasing branch '%s' on '%s'."),
964                                          state->branch,
965                                          state->onto);
966                 else
967                         status_printf_ln(s, color,
968                                          _("You are currently rebasing."));
969                 if (s->hints) {
970                         status_printf_ln(s, color,
971                                 _("  (fix conflicts and then run \"git rebase --continue\")"));
972                         status_printf_ln(s, color,
973                                 _("  (use \"git rebase --skip\" to skip this patch)"));
974                         status_printf_ln(s, color,
975                                 _("  (use \"git rebase --abort\" to check out the original branch)"));
976                 }
977         } else if (state->rebase_in_progress || !stat(git_path("MERGE_MSG"), &st)) {
978                 if (state->branch)
979                         status_printf_ln(s, color,
980                                          _("You are currently rebasing branch '%s' on '%s'."),
981                                          state->branch,
982                                          state->onto);
983                 else
984                         status_printf_ln(s, color,
985                                          _("You are currently rebasing."));
986                 if (s->hints)
987                         status_printf_ln(s, color,
988                                 _("  (all conflicts fixed: run \"git rebase --continue\")"));
989         } else if (split_commit_in_progress(s)) {
990                 if (state->branch)
991                         status_printf_ln(s, color,
992                                          _("You are currently splitting a commit while rebasing branch '%s' on '%s'."),
993                                          state->branch,
994                                          state->onto);
995                 else
996                         status_printf_ln(s, color,
997                                          _("You are currently splitting a commit during a rebase."));
998                 if (s->hints)
999                         status_printf_ln(s, color,
1000                                 _("  (Once your working directory is clean, run \"git rebase --continue\")"));
1001         } else {
1002                 if (state->branch)
1003                         status_printf_ln(s, color,
1004                                          _("You are currently editing a commit while rebasing branch '%s' on '%s'."),
1005                                          state->branch,
1006                                          state->onto);
1007                 else
1008                         status_printf_ln(s, color,
1009                                          _("You are currently editing a commit during a rebase."));
1010                 if (s->hints && !s->amend) {
1011                         status_printf_ln(s, color,
1012                                 _("  (use \"git commit --amend\" to amend the current commit)"));
1013                         status_printf_ln(s, color,
1014                                 _("  (use \"git rebase --continue\" once you are satisfied with your changes)"));
1015                 }
1016         }
1017         wt_status_print_trailer(s);
1018 }
1019
1020 static void show_cherry_pick_in_progress(struct wt_status *s,
1021                                         struct wt_status_state *state,
1022                                         const char *color)
1023 {
1024         status_printf_ln(s, color, _("You are currently cherry-picking commit %s."),
1025                         find_unique_abbrev(state->cherry_pick_head_sha1, DEFAULT_ABBREV));
1026         if (s->hints) {
1027                 if (has_unmerged(s))
1028                         status_printf_ln(s, color,
1029                                 _("  (fix conflicts and run \"git cherry-pick --continue\")"));
1030                 else
1031                         status_printf_ln(s, color,
1032                                 _("  (all conflicts fixed: run \"git cherry-pick --continue\")"));
1033                 status_printf_ln(s, color,
1034                         _("  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"));
1035         }
1036         wt_status_print_trailer(s);
1037 }
1038
1039 static void show_revert_in_progress(struct wt_status *s,
1040                                         struct wt_status_state *state,
1041                                         const char *color)
1042 {
1043         status_printf_ln(s, color, _("You are currently reverting commit %s."),
1044                          find_unique_abbrev(state->revert_head_sha1, DEFAULT_ABBREV));
1045         if (s->hints) {
1046                 if (has_unmerged(s))
1047                         status_printf_ln(s, color,
1048                                 _("  (fix conflicts and run \"git revert --continue\")"));
1049                 else
1050                         status_printf_ln(s, color,
1051                                 _("  (all conflicts fixed: run \"git revert --continue\")"));
1052                 status_printf_ln(s, color,
1053                         _("  (use \"git revert --abort\" to cancel the revert operation)"));
1054         }
1055         wt_status_print_trailer(s);
1056 }
1057
1058 static void show_bisect_in_progress(struct wt_status *s,
1059                                 struct wt_status_state *state,
1060                                 const char *color)
1061 {
1062         if (state->branch)
1063                 status_printf_ln(s, color,
1064                                  _("You are currently bisecting, started from branch '%s'."),
1065                                  state->branch);
1066         else
1067                 status_printf_ln(s, color,
1068                                  _("You are currently bisecting."));
1069         if (s->hints)
1070                 status_printf_ln(s, color,
1071                         _("  (use \"git bisect reset\" to get back to the original branch)"));
1072         wt_status_print_trailer(s);
1073 }
1074
1075 /*
1076  * Extract branch information from rebase/bisect
1077  */
1078 static char *read_and_strip_branch(const char *path)
1079 {
1080         struct strbuf sb = STRBUF_INIT;
1081         unsigned char sha1[20];
1082
1083         if (strbuf_read_file(&sb, git_path("%s", path), 0) <= 0)
1084                 goto got_nothing;
1085
1086         while (&sb.len && sb.buf[sb.len - 1] == '\n')
1087                 strbuf_setlen(&sb, sb.len - 1);
1088         if (!sb.len)
1089                 goto got_nothing;
1090         if (!prefixcmp(sb.buf, "refs/heads/"))
1091                 strbuf_remove(&sb,0, strlen("refs/heads/"));
1092         else if (!prefixcmp(sb.buf, "refs/"))
1093                 ;
1094         else if (!get_sha1_hex(sb.buf, sha1)) {
1095                 const char *abbrev;
1096                 abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);
1097                 strbuf_reset(&sb);
1098                 strbuf_addstr(&sb, abbrev);
1099         } else if (!strcmp(sb.buf, "detached HEAD")) /* rebase */
1100                 goto got_nothing;
1101         else                    /* bisect */
1102                 ;
1103         return strbuf_detach(&sb, NULL);
1104
1105 got_nothing:
1106         strbuf_release(&sb);
1107         return NULL;
1108 }
1109
1110 struct grab_1st_switch_cbdata {
1111         struct strbuf buf;
1112         unsigned char nsha1[20];
1113 };
1114
1115 static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
1116                            const char *email, unsigned long timestamp, int tz,
1117                            const char *message, void *cb_data)
1118 {
1119         struct grab_1st_switch_cbdata *cb = cb_data;
1120         const char *target = NULL, *end;
1121
1122         if (prefixcmp(message, "checkout: moving from "))
1123                 return 0;
1124         message += strlen("checkout: moving from ");
1125         target = strstr(message, " to ");
1126         if (!target)
1127                 return 0;
1128         target += strlen(" to ");
1129         strbuf_reset(&cb->buf);
1130         hashcpy(cb->nsha1, nsha1);
1131         for (end = target; *end && *end != '\n'; end++)
1132                 ;
1133         strbuf_add(&cb->buf, target, end - target);
1134         return 1;
1135 }
1136
1137 static void wt_status_get_detached_from(struct wt_status_state *state)
1138 {
1139         struct grab_1st_switch_cbdata cb;
1140         struct commit *commit;
1141         unsigned char sha1[20];
1142         char *ref = NULL;
1143
1144         strbuf_init(&cb.buf, 0);
1145         if (for_each_reflog_ent_reverse("HEAD", grab_1st_switch, &cb) <= 0) {
1146                 strbuf_release(&cb.buf);
1147                 return;
1148         }
1149
1150         if (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 &&
1151             /* sha1 is a commit? match without further lookup */
1152             (!hashcmp(cb.nsha1, sha1) ||
1153              /* perhaps sha1 is a tag, try to dereference to a commit */
1154              ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&
1155               !hashcmp(cb.nsha1, commit->object.sha1)))) {
1156                 int ofs;
1157                 if (!prefixcmp(ref, "refs/tags/"))
1158                         ofs = strlen("refs/tags/");
1159                 else if (!prefixcmp(ref, "refs/remotes/"))
1160                         ofs = strlen("refs/remotes/");
1161                 else
1162                         ofs = 0;
1163                 state->detached_from = xstrdup(ref + ofs);
1164         } else
1165                 state->detached_from =
1166                         xstrdup(find_unique_abbrev(cb.nsha1, DEFAULT_ABBREV));
1167         hashcpy(state->detached_sha1, cb.nsha1);
1168
1169         free(ref);
1170         strbuf_release(&cb.buf);
1171 }
1172
1173 void wt_status_get_state(struct wt_status_state *state,
1174                          int get_detached_from)
1175 {
1176         struct stat st;
1177         unsigned char sha1[20];
1178
1179         if (!stat(git_path("MERGE_HEAD"), &st)) {
1180                 state->merge_in_progress = 1;
1181         } else if (!stat(git_path("rebase-apply"), &st)) {
1182                 if (!stat(git_path("rebase-apply/applying"), &st)) {
1183                         state->am_in_progress = 1;
1184                         if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
1185                                 state->am_empty_patch = 1;
1186                 } else {
1187                         state->rebase_in_progress = 1;
1188                         state->branch = read_and_strip_branch("rebase-apply/head-name");
1189                         state->onto = read_and_strip_branch("rebase-apply/onto");
1190                 }
1191         } else if (!stat(git_path("rebase-merge"), &st)) {
1192                 if (!stat(git_path("rebase-merge/interactive"), &st))
1193                         state->rebase_interactive_in_progress = 1;
1194                 else
1195                         state->rebase_in_progress = 1;
1196                 state->branch = read_and_strip_branch("rebase-merge/head-name");
1197                 state->onto = read_and_strip_branch("rebase-merge/onto");
1198         } else if (!stat(git_path("CHERRY_PICK_HEAD"), &st) &&
1199                         !get_sha1("CHERRY_PICK_HEAD", sha1)) {
1200                 state->cherry_pick_in_progress = 1;
1201                 hashcpy(state->cherry_pick_head_sha1, sha1);
1202         }
1203         if (!stat(git_path("BISECT_LOG"), &st)) {
1204                 state->bisect_in_progress = 1;
1205                 state->branch = read_and_strip_branch("BISECT_START");
1206         }
1207         if (!stat(git_path("REVERT_HEAD"), &st) &&
1208             !get_sha1("REVERT_HEAD", sha1)) {
1209                 state->revert_in_progress = 1;
1210                 hashcpy(state->revert_head_sha1, sha1);
1211         }
1212
1213         if (get_detached_from)
1214                 wt_status_get_detached_from(state);
1215 }
1216
1217 static void wt_status_print_state(struct wt_status *s,
1218                                   struct wt_status_state *state)
1219 {
1220         const char *state_color = color(WT_STATUS_HEADER, s);
1221         if (state->merge_in_progress)
1222                 show_merge_in_progress(s, state, state_color);
1223         else if (state->am_in_progress)
1224                 show_am_in_progress(s, state, state_color);
1225         else if (state->rebase_in_progress || state->rebase_interactive_in_progress)
1226                 show_rebase_in_progress(s, state, state_color);
1227         else if (state->cherry_pick_in_progress)
1228                 show_cherry_pick_in_progress(s, state, state_color);
1229         else if (state->revert_in_progress)
1230                 show_revert_in_progress(s, state, state_color);
1231         if (state->bisect_in_progress)
1232                 show_bisect_in_progress(s, state, state_color);
1233 }
1234
1235 void wt_status_print(struct wt_status *s)
1236 {
1237         const char *branch_color = color(WT_STATUS_ONBRANCH, s);
1238         const char *branch_status_color = color(WT_STATUS_HEADER, s);
1239         struct wt_status_state state;
1240
1241         memset(&state, 0, sizeof(state));
1242         wt_status_get_state(&state,
1243                             s->branch && !strcmp(s->branch, "HEAD"));
1244
1245         if (s->branch) {
1246                 const char *on_what = _("On branch ");
1247                 const char *branch_name = s->branch;
1248                 if (!prefixcmp(branch_name, "refs/heads/"))
1249                         branch_name += 11;
1250                 else if (!strcmp(branch_name, "HEAD")) {
1251                         branch_status_color = color(WT_STATUS_NOBRANCH, s);
1252                         if (state.rebase_in_progress || state.rebase_interactive_in_progress) {
1253                                 on_what = _("rebase in progress; onto ");
1254                                 branch_name = state.onto;
1255                         } else if (state.detached_from) {
1256                                 unsigned char sha1[20];
1257                                 branch_name = state.detached_from;
1258                                 if (!get_sha1("HEAD", sha1) &&
1259                                     !hashcmp(sha1, state.detached_sha1))
1260                                         on_what = _("HEAD detached at ");
1261                                 else
1262                                         on_what = _("HEAD detached from ");
1263                         } else {
1264                                 branch_name = "";
1265                                 on_what = _("Not currently on any branch.");
1266                         }
1267                 }
1268                 status_printf(s, color(WT_STATUS_HEADER, s), "%s", "");
1269                 status_printf_more(s, branch_status_color, "%s", on_what);
1270                 status_printf_more(s, branch_color, "%s\n", branch_name);
1271                 if (!s->is_initial)
1272                         wt_status_print_tracking(s);
1273         }
1274
1275         wt_status_print_state(s, &state);
1276         free(state.branch);
1277         free(state.onto);
1278         free(state.detached_from);
1279
1280         if (s->is_initial) {
1281                 status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
1282                 status_printf_ln(s, color(WT_STATUS_HEADER, s), _("Initial commit"));
1283                 status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
1284         }
1285
1286         wt_status_print_updated(s);
1287         wt_status_print_unmerged(s);
1288         wt_status_print_changed(s);
1289         if (s->submodule_summary &&
1290             (!s->ignore_submodule_arg ||
1291              strcmp(s->ignore_submodule_arg, "all"))) {
1292                 wt_status_print_submodule_summary(s, 0);  /* staged */
1293                 wt_status_print_submodule_summary(s, 1);  /* unstaged */
1294         }
1295         if (s->show_untracked_files) {
1296                 wt_status_print_other(s, &s->untracked, _("Untracked files"), "add");
1297                 if (s->show_ignored_files)
1298                         wt_status_print_other(s, &s->ignored, _("Ignored files"), "add -f");
1299                 if (advice_status_u_option && 2000 < s->untracked_in_ms) {
1300                         status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
1301                         status_printf_ln(s, GIT_COLOR_NORMAL,
1302                                          _("It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
1303                                            "may speed it up, but you have to be careful not to forget to add\n"
1304                                            "new files yourself (see 'git help status')."),
1305                                          s->untracked_in_ms / 1000.0);
1306                 }
1307         } else if (s->commitable)
1308                 status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
1309                         s->hints
1310                         ? _(" (use -u option to show untracked files)") : "");
1311
1312         if (s->verbose)
1313                 wt_status_print_verbose(s);
1314         if (!s->commitable) {
1315                 if (s->amend)
1316                         status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes"));
1317                 else if (s->nowarn)
1318                         ; /* nothing */
1319                 else if (s->workdir_dirty) {
1320                         if (s->hints)
1321                                 printf(_("no changes added to commit "
1322                                          "(use \"git add\" and/or \"git commit -a\")\n"));
1323                         else
1324                                 printf(_("no changes added to commit\n"));
1325                 } else if (s->untracked.nr) {
1326                         if (s->hints)
1327                                 printf(_("nothing added to commit but untracked files "
1328                                          "present (use \"git add\" to track)\n"));
1329                         else
1330                                 printf(_("nothing added to commit but untracked files present\n"));
1331                 } else if (s->is_initial) {
1332                         if (s->hints)
1333                                 printf(_("nothing to commit (create/copy files "
1334                                          "and use \"git add\" to track)\n"));
1335                         else
1336                                 printf(_("nothing to commit\n"));
1337                 } else if (!s->show_untracked_files) {
1338                         if (s->hints)
1339                                 printf(_("nothing to commit (use -u to show untracked files)\n"));
1340                         else
1341                                 printf(_("nothing to commit\n"));
1342                 } else
1343                         printf(_("nothing to commit, working directory clean\n"));
1344         }
1345 }
1346
1347 static void wt_shortstatus_unmerged(struct string_list_item *it,
1348                            struct wt_status *s)
1349 {
1350         struct wt_status_change_data *d = it->util;
1351         const char *how = "??";
1352
1353         switch (d->stagemask) {
1354         case 1: how = "DD"; break; /* both deleted */
1355         case 2: how = "AU"; break; /* added by us */
1356         case 3: how = "UD"; break; /* deleted by them */
1357         case 4: how = "UA"; break; /* added by them */
1358         case 5: how = "DU"; break; /* deleted by us */
1359         case 6: how = "AA"; break; /* both added */
1360         case 7: how = "UU"; break; /* both modified */
1361         }
1362         color_fprintf(s->fp, color(WT_STATUS_UNMERGED, s), "%s", how);
1363         if (s->null_termination) {
1364                 fprintf(stdout, " %s%c", it->string, 0);
1365         } else {
1366                 struct strbuf onebuf = STRBUF_INIT;
1367                 const char *one;
1368                 one = quote_path(it->string, s->prefix, &onebuf);
1369                 printf(" %s\n", one);
1370                 strbuf_release(&onebuf);
1371         }
1372 }
1373
1374 static void wt_shortstatus_status(struct string_list_item *it,
1375                          struct wt_status *s)
1376 {
1377         struct wt_status_change_data *d = it->util;
1378
1379         if (d->index_status)
1380                 color_fprintf(s->fp, color(WT_STATUS_UPDATED, s), "%c", d->index_status);
1381         else
1382                 putchar(' ');
1383         if (d->worktree_status)
1384                 color_fprintf(s->fp, color(WT_STATUS_CHANGED, s), "%c", d->worktree_status);
1385         else
1386                 putchar(' ');
1387         putchar(' ');
1388         if (s->null_termination) {
1389                 fprintf(stdout, "%s%c", it->string, 0);
1390                 if (d->head_path)
1391                         fprintf(stdout, "%s%c", d->head_path, 0);
1392         } else {
1393                 struct strbuf onebuf = STRBUF_INIT;
1394                 const char *one;
1395                 if (d->head_path) {
1396                         one = quote_path(d->head_path, s->prefix, &onebuf);
1397                         if (*one != '"' && strchr(one, ' ') != NULL) {
1398                                 putchar('"');
1399                                 strbuf_addch(&onebuf, '"');
1400                                 one = onebuf.buf;
1401                         }
1402                         printf("%s -> ", one);
1403                         strbuf_release(&onebuf);
1404                 }
1405                 one = quote_path(it->string, s->prefix, &onebuf);
1406                 if (*one != '"' && strchr(one, ' ') != NULL) {
1407                         putchar('"');
1408                         strbuf_addch(&onebuf, '"');
1409                         one = onebuf.buf;
1410                 }
1411                 printf("%s\n", one);
1412                 strbuf_release(&onebuf);
1413         }
1414 }
1415
1416 static void wt_shortstatus_other(struct string_list_item *it,
1417                                  struct wt_status *s, const char *sign)
1418 {
1419         if (s->null_termination) {
1420                 fprintf(stdout, "%s %s%c", sign, it->string, 0);
1421         } else {
1422                 struct strbuf onebuf = STRBUF_INIT;
1423                 const char *one;
1424                 one = quote_path(it->string, s->prefix, &onebuf);
1425                 color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign);
1426                 printf(" %s\n", one);
1427                 strbuf_release(&onebuf);
1428         }
1429 }
1430
1431 static void wt_shortstatus_print_tracking(struct wt_status *s)
1432 {
1433         struct branch *branch;
1434         const char *header_color = color(WT_STATUS_HEADER, s);
1435         const char *branch_color_local = color(WT_STATUS_LOCAL_BRANCH, s);
1436         const char *branch_color_remote = color(WT_STATUS_REMOTE_BRANCH, s);
1437
1438         const char *base;
1439         const char *branch_name;
1440         int num_ours, num_theirs;
1441         int upstream_is_gone = 0;
1442
1443         color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## ");
1444
1445         if (!s->branch)
1446                 return;
1447         branch_name = s->branch;
1448
1449         if (!prefixcmp(branch_name, "refs/heads/"))
1450                 branch_name += 11;
1451         else if (!strcmp(branch_name, "HEAD")) {
1452                 branch_name = _("HEAD (no branch)");
1453                 branch_color_local = color(WT_STATUS_NOBRANCH, s);
1454         }
1455
1456         branch = branch_get(s->branch + 11);
1457         if (s->is_initial)
1458                 color_fprintf(s->fp, header_color, _("Initial commit on "));
1459
1460         color_fprintf(s->fp, branch_color_local, "%s", branch_name);
1461
1462         switch (stat_tracking_info(branch, &num_ours, &num_theirs)) {
1463         case 0:
1464                 /* no base */
1465                 fputc(s->null_termination ? '\0' : '\n', s->fp);
1466                 return;
1467         case -1:
1468                 /* with "gone" base */
1469                 upstream_is_gone = 1;
1470                 break;
1471         default:
1472                 /* with base */
1473                 break;
1474         }
1475
1476         base = branch->merge[0]->dst;
1477         base = shorten_unambiguous_ref(base, 0);
1478         color_fprintf(s->fp, header_color, "...");
1479         color_fprintf(s->fp, branch_color_remote, "%s", base);
1480
1481         if (!upstream_is_gone && !num_ours && !num_theirs) {
1482                 fputc(s->null_termination ? '\0' : '\n', s->fp);
1483                 return;
1484         }
1485
1486         color_fprintf(s->fp, header_color, " [");
1487         if (upstream_is_gone) {
1488                 color_fprintf(s->fp, header_color, _("gone"));
1489         } else if (!num_ours) {
1490                 color_fprintf(s->fp, header_color, _("behind "));
1491                 color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
1492         } else if (!num_theirs) {
1493                 color_fprintf(s->fp, header_color, _("ahead "));
1494                 color_fprintf(s->fp, branch_color_local, "%d", num_ours);
1495         } else {
1496                 color_fprintf(s->fp, header_color, _("ahead "));
1497                 color_fprintf(s->fp, branch_color_local, "%d", num_ours);
1498                 color_fprintf(s->fp, header_color, _(", behind "));
1499                 color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
1500         }
1501
1502         color_fprintf(s->fp, header_color, "]");
1503         fputc(s->null_termination ? '\0' : '\n', s->fp);
1504 }
1505
1506 void wt_shortstatus_print(struct wt_status *s)
1507 {
1508         int i;
1509
1510         if (s->show_branch)
1511                 wt_shortstatus_print_tracking(s);
1512
1513         for (i = 0; i < s->change.nr; i++) {
1514                 struct wt_status_change_data *d;
1515                 struct string_list_item *it;
1516
1517                 it = &(s->change.items[i]);
1518                 d = it->util;
1519                 if (d->stagemask)
1520                         wt_shortstatus_unmerged(it, s);
1521                 else
1522                         wt_shortstatus_status(it, s);
1523         }
1524         for (i = 0; i < s->untracked.nr; i++) {
1525                 struct string_list_item *it;
1526
1527                 it = &(s->untracked.items[i]);
1528                 wt_shortstatus_other(it, s, "??");
1529         }
1530         for (i = 0; i < s->ignored.nr; i++) {
1531                 struct string_list_item *it;
1532
1533                 it = &(s->ignored.items[i]);
1534                 wt_shortstatus_other(it, s, "!!");
1535         }
1536 }
1537
1538 void wt_porcelain_print(struct wt_status *s)
1539 {
1540         s->use_color = 0;
1541         s->relative_paths = 0;
1542         s->prefix = NULL;
1543         wt_shortstatus_print(s);
1544 }