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