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