i18n: merge-recursive: mark strings for translation
[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 "remote.h"
12 #include "refs.h"
13 #include "submodule.h"
14 #include "column.h"
15 #include "strbuf.h"
16
17 static char default_wt_status_colors[][COLOR_MAXLEN] = {
18         GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
19         GIT_COLOR_GREEN,  /* WT_STATUS_UPDATED */
20         GIT_COLOR_RED,    /* WT_STATUS_CHANGED */
21         GIT_COLOR_RED,    /* WT_STATUS_UNTRACKED */
22         GIT_COLOR_RED,    /* WT_STATUS_NOBRANCH */
23         GIT_COLOR_RED,    /* WT_STATUS_UNMERGED */
24         GIT_COLOR_GREEN,  /* WT_STATUS_LOCAL_BRANCH */
25         GIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */
26         GIT_COLOR_NIL,    /* WT_STATUS_ONBRANCH */
27         GIT_COLOR_NORMAL, /* WT_STATUS_IN_PROGRESS */
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                 strbuf_addch(&sb, '#');
50                 if (!trail)
51                         strbuf_addch(&sb, ' ');
52                 color_print_strbuf(s->fp, color, &sb);
53                 if (trail)
54                         fprintf(s->fp, "%s", trail);
55                 strbuf_release(&sb);
56                 return;
57         }
58         for (line = sb.buf; *line; line = eol + 1) {
59                 eol = strchr(line, '\n');
60
61                 strbuf_reset(&linebuf);
62                 if (at_bol) {
63                         strbuf_addch(&linebuf, '#');
64                         if (*line != '\n' && *line != '\t')
65                                 strbuf_addch(&linebuf, ' ');
66                 }
67                 if (eol)
68                         strbuf_add(&linebuf, line, eol - line);
69                 else
70                         strbuf_addstr(&linebuf, line);
71                 color_print_strbuf(s->fp, color, &linebuf);
72                 if (eol)
73                         fprintf(s->fp, "\n");
74                 else
75                         break;
76                 at_bol = 1;
77         }
78         if (trail)
79                 fprintf(s->fp, "%s", trail);
80         strbuf_release(&linebuf);
81         strbuf_release(&sb);
82 }
83
84 void status_printf_ln(struct wt_status *s, const char *color,
85                         const char *fmt, ...)
86 {
87         va_list ap;
88
89         va_start(ap, fmt);
90         status_vprintf(s, 1, color, fmt, ap, "\n");
91         va_end(ap);
92 }
93
94 void status_printf(struct wt_status *s, const char *color,
95                         const char *fmt, ...)
96 {
97         va_list ap;
98
99         va_start(ap, fmt);
100         status_vprintf(s, 1, color, fmt, ap, NULL);
101         va_end(ap);
102 }
103
104 void status_printf_more(struct wt_status *s, const char *color,
105                         const char *fmt, ...)
106 {
107         va_list ap;
108
109         va_start(ap, fmt);
110         status_vprintf(s, 0, color, fmt, ap, NULL);
111         va_end(ap);
112 }
113
114 void wt_status_prepare(struct wt_status *s)
115 {
116         unsigned char sha1[20];
117
118         memset(s, 0, sizeof(*s));
119         memcpy(s->color_palette, default_wt_status_colors,
120                sizeof(default_wt_status_colors));
121         s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
122         s->use_color = -1;
123         s->relative_paths = 1;
124         s->branch = resolve_refdup("HEAD", sha1, 0, NULL);
125         s->reference = "HEAD";
126         s->fp = stdout;
127         s->index_file = get_index_file();
128         s->change.strdup_strings = 1;
129         s->untracked.strdup_strings = 1;
130         s->ignored.strdup_strings = 1;
131 }
132
133 static void wt_status_print_unmerged_header(struct wt_status *s)
134 {
135         int i;
136         int del_mod_conflict = 0;
137         int both_deleted = 0;
138         int not_deleted = 0;
139         const char *c = color(WT_STATUS_HEADER, s);
140
141         status_printf_ln(s, c, _("Unmerged paths:"));
142
143         for (i = 0; i < s->change.nr; i++) {
144                 struct string_list_item *it = &(s->change.items[i]);
145                 struct wt_status_change_data *d = it->util;
146
147                 switch (d->stagemask) {
148                 case 0:
149                         break;
150                 case 1:
151                         both_deleted = 1;
152                         break;
153                 case 3:
154                 case 5:
155                         del_mod_conflict = 1;
156                         break;
157                 default:
158                         not_deleted = 1;
159                         break;
160                 }
161         }
162
163         if (!advice_status_hints)
164                 return;
165         if (s->whence != FROM_COMMIT)
166                 ;
167         else if (!s->is_initial)
168                 status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
169         else
170                 status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
171
172         if (!both_deleted) {
173                 if (!del_mod_conflict)
174                         status_printf_ln(s, c, _("  (use \"git add <file>...\" to mark resolution)"));
175                 else
176                         status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
177         } else if (!del_mod_conflict && !not_deleted) {
178                 status_printf_ln(s, c, _("  (use \"git rm <file>...\" to mark resolution)"));
179         } else {
180                 status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
181         }
182         status_printf_ln(s, c, "");
183 }
184
185 static void wt_status_print_cached_header(struct wt_status *s)
186 {
187         const char *c = color(WT_STATUS_HEADER, s);
188
189         status_printf_ln(s, c, _("Changes to be committed:"));
190         if (!advice_status_hints)
191                 return;
192         if (s->whence != FROM_COMMIT)
193                 ; /* NEEDSWORK: use "git reset --unresolve"??? */
194         else if (!s->is_initial)
195                 status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
196         else
197                 status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
198         status_printf_ln(s, c, "");
199 }
200
201 static void wt_status_print_dirty_header(struct wt_status *s,
202                                          int has_deleted,
203                                          int has_dirty_submodules)
204 {
205         const char *c = color(WT_STATUS_HEADER, s);
206
207         status_printf_ln(s, c, _("Changes not staged for commit:"));
208         if (!advice_status_hints)
209                 return;
210         if (!has_deleted)
211                 status_printf_ln(s, c, _("  (use \"git add <file>...\" to update what will be committed)"));
212         else
213                 status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" to update what will be committed)"));
214         status_printf_ln(s, c, _("  (use \"git checkout -- <file>...\" to discard changes in working directory)"));
215         if (has_dirty_submodules)
216                 status_printf_ln(s, c, _("  (commit or discard the untracked or modified content in submodules)"));
217         status_printf_ln(s, c, "");
218 }
219
220 static void wt_status_print_other_header(struct wt_status *s,
221                                          const char *what,
222                                          const char *how)
223 {
224         const char *c = color(WT_STATUS_HEADER, s);
225         status_printf_ln(s, c, _("%s files:"), what);
226         if (!advice_status_hints)
227                 return;
228         status_printf_ln(s, c, _("  (use \"git %s <file>...\" to include in what will be committed)"), how);
229         status_printf_ln(s, c, "");
230 }
231
232 static void wt_status_print_trailer(struct wt_status *s)
233 {
234         status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
235 }
236
237 #define quote_path quote_path_relative
238
239 static void wt_status_print_unmerged_data(struct wt_status *s,
240                                           struct string_list_item *it)
241 {
242         const char *c = color(WT_STATUS_UNMERGED, s);
243         struct wt_status_change_data *d = it->util;
244         struct strbuf onebuf = STRBUF_INIT;
245         const char *one, *how = _("bug");
246
247         one = quote_path(it->string, -1, &onebuf, s->prefix);
248         status_printf(s, color(WT_STATUS_HEADER, s), "\t");
249         switch (d->stagemask) {
250         case 1: how = _("both deleted:"); break;
251         case 2: how = _("added by us:"); break;
252         case 3: how = _("deleted by them:"); break;
253         case 4: how = _("added by them:"); break;
254         case 5: how = _("deleted by us:"); break;
255         case 6: how = _("both added:"); break;
256         case 7: how = _("both modified:"); break;
257         }
258         status_printf_more(s, c, "%-20s%s\n", how, one);
259         strbuf_release(&onebuf);
260 }
261
262 static void wt_status_print_change_data(struct wt_status *s,
263                                         int change_type,
264                                         struct string_list_item *it)
265 {
266         struct wt_status_change_data *d = it->util;
267         const char *c = color(change_type, s);
268         int status = status;
269         char *one_name;
270         char *two_name;
271         const char *one, *two;
272         struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
273         struct strbuf extra = STRBUF_INIT;
274
275         one_name = two_name = it->string;
276         switch (change_type) {
277         case WT_STATUS_UPDATED:
278                 status = d->index_status;
279                 if (d->head_path)
280                         one_name = d->head_path;
281                 break;
282         case WT_STATUS_CHANGED:
283                 if (d->new_submodule_commits || d->dirty_submodule) {
284                         strbuf_addstr(&extra, " (");
285                         if (d->new_submodule_commits)
286                                 strbuf_addf(&extra, _("new commits, "));
287                         if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
288                                 strbuf_addf(&extra, _("modified content, "));
289                         if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
290                                 strbuf_addf(&extra, _("untracked content, "));
291                         strbuf_setlen(&extra, extra.len - 2);
292                         strbuf_addch(&extra, ')');
293                 }
294                 status = d->worktree_status;
295                 break;
296         }
297
298         one = quote_path(one_name, -1, &onebuf, s->prefix);
299         two = quote_path(two_name, -1, &twobuf, s->prefix);
300
301         status_printf(s, color(WT_STATUS_HEADER, s), "\t");
302         switch (status) {
303         case DIFF_STATUS_ADDED:
304                 status_printf_more(s, c, _("new file:   %s"), one);
305                 break;
306         case DIFF_STATUS_COPIED:
307                 status_printf_more(s, c, _("copied:     %s -> %s"), one, two);
308                 break;
309         case DIFF_STATUS_DELETED:
310                 status_printf_more(s, c, _("deleted:    %s"), one);
311                 break;
312         case DIFF_STATUS_MODIFIED:
313                 status_printf_more(s, c, _("modified:   %s"), one);
314                 break;
315         case DIFF_STATUS_RENAMED:
316                 status_printf_more(s, c, _("renamed:    %s -> %s"), one, two);
317                 break;
318         case DIFF_STATUS_TYPE_CHANGED:
319                 status_printf_more(s, c, _("typechange: %s"), one);
320                 break;
321         case DIFF_STATUS_UNKNOWN:
322                 status_printf_more(s, c, _("unknown:    %s"), one);
323                 break;
324         case DIFF_STATUS_UNMERGED:
325                 status_printf_more(s, c, _("unmerged:   %s"), one);
326                 break;
327         default:
328                 die(_("bug: unhandled diff status %c"), status);
329         }
330         if (extra.len) {
331                 status_printf_more(s, color(WT_STATUS_HEADER, s), "%s", extra.buf);
332                 strbuf_release(&extra);
333         }
334         status_printf_more(s, GIT_COLOR_NORMAL, "\n");
335         strbuf_release(&onebuf);
336         strbuf_release(&twobuf);
337 }
338
339 static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
340                                          struct diff_options *options,
341                                          void *data)
342 {
343         struct wt_status *s = data;
344         int i;
345
346         if (!q->nr)
347                 return;
348         s->workdir_dirty = 1;
349         for (i = 0; i < q->nr; i++) {
350                 struct diff_filepair *p;
351                 struct string_list_item *it;
352                 struct wt_status_change_data *d;
353
354                 p = q->queue[i];
355                 it = string_list_insert(&s->change, p->one->path);
356                 d = it->util;
357                 if (!d) {
358                         d = xcalloc(1, sizeof(*d));
359                         it->util = d;
360                 }
361                 if (!d->worktree_status)
362                         d->worktree_status = p->status;
363                 d->dirty_submodule = p->two->dirty_submodule;
364                 if (S_ISGITLINK(p->two->mode))
365                         d->new_submodule_commits = !!hashcmp(p->one->sha1, p->two->sha1);
366         }
367 }
368
369 static int unmerged_mask(const char *path)
370 {
371         int pos, mask;
372         struct cache_entry *ce;
373
374         pos = cache_name_pos(path, strlen(path));
375         if (0 <= pos)
376                 return 0;
377
378         mask = 0;
379         pos = -pos-1;
380         while (pos < active_nr) {
381                 ce = active_cache[pos++];
382                 if (strcmp(ce->name, path) || !ce_stage(ce))
383                         break;
384                 mask |= (1 << (ce_stage(ce) - 1));
385         }
386         return mask;
387 }
388
389 static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
390                                          struct diff_options *options,
391                                          void *data)
392 {
393         struct wt_status *s = data;
394         int i;
395
396         for (i = 0; i < q->nr; i++) {
397                 struct diff_filepair *p;
398                 struct string_list_item *it;
399                 struct wt_status_change_data *d;
400
401                 p = q->queue[i];
402                 it = string_list_insert(&s->change, p->two->path);
403                 d = it->util;
404                 if (!d) {
405                         d = xcalloc(1, sizeof(*d));
406                         it->util = d;
407                 }
408                 if (!d->index_status)
409                         d->index_status = p->status;
410                 switch (p->status) {
411                 case DIFF_STATUS_COPIED:
412                 case DIFF_STATUS_RENAMED:
413                         d->head_path = xstrdup(p->one->path);
414                         break;
415                 case DIFF_STATUS_UNMERGED:
416                         d->stagemask = unmerged_mask(p->two->path);
417                         break;
418                 }
419         }
420 }
421
422 static void wt_status_collect_changes_worktree(struct wt_status *s)
423 {
424         struct rev_info rev;
425
426         init_revisions(&rev, NULL);
427         setup_revisions(0, NULL, &rev, NULL);
428         rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
429         DIFF_OPT_SET(&rev.diffopt, DIRTY_SUBMODULES);
430         if (!s->show_untracked_files)
431                 DIFF_OPT_SET(&rev.diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
432         if (s->ignore_submodule_arg) {
433                 DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
434                 handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
435         }
436         rev.diffopt.format_callback = wt_status_collect_changed_cb;
437         rev.diffopt.format_callback_data = s;
438         init_pathspec(&rev.prune_data, s->pathspec);
439         run_diff_files(&rev, 0);
440 }
441
442 static void wt_status_collect_changes_index(struct wt_status *s)
443 {
444         struct rev_info rev;
445         struct setup_revision_opt opt;
446
447         init_revisions(&rev, NULL);
448         memset(&opt, 0, sizeof(opt));
449         opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
450         setup_revisions(0, NULL, &rev, &opt);
451
452         if (s->ignore_submodule_arg) {
453                 DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
454                 handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
455         }
456
457         rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
458         rev.diffopt.format_callback = wt_status_collect_updated_cb;
459         rev.diffopt.format_callback_data = s;
460         rev.diffopt.detect_rename = 1;
461         rev.diffopt.rename_limit = 200;
462         rev.diffopt.break_opt = 0;
463         init_pathspec(&rev.prune_data, s->pathspec);
464         run_diff_index(&rev, 1);
465 }
466
467 static void wt_status_collect_changes_initial(struct wt_status *s)
468 {
469         struct pathspec pathspec;
470         int i;
471
472         init_pathspec(&pathspec, s->pathspec);
473         for (i = 0; i < active_nr; i++) {
474                 struct string_list_item *it;
475                 struct wt_status_change_data *d;
476                 struct cache_entry *ce = active_cache[i];
477
478                 if (!ce_path_match(ce, &pathspec))
479                         continue;
480                 it = string_list_insert(&s->change, ce->name);
481                 d = it->util;
482                 if (!d) {
483                         d = xcalloc(1, sizeof(*d));
484                         it->util = d;
485                 }
486                 if (ce_stage(ce)) {
487                         d->index_status = DIFF_STATUS_UNMERGED;
488                         d->stagemask |= (1 << (ce_stage(ce) - 1));
489                 }
490                 else
491                         d->index_status = DIFF_STATUS_ADDED;
492         }
493         free_pathspec(&pathspec);
494 }
495
496 static void wt_status_collect_untracked(struct wt_status *s)
497 {
498         int i;
499         struct dir_struct dir;
500
501         if (!s->show_untracked_files)
502                 return;
503         memset(&dir, 0, sizeof(dir));
504         if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
505                 dir.flags |=
506                         DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
507         setup_standard_excludes(&dir);
508
509         fill_directory(&dir, s->pathspec);
510         for (i = 0; i < dir.nr; i++) {
511                 struct dir_entry *ent = dir.entries[i];
512                 if (cache_name_is_other(ent->name, ent->len) &&
513                     match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
514                         string_list_insert(&s->untracked, ent->name);
515                 free(ent);
516         }
517
518         if (s->show_ignored_files) {
519                 dir.nr = 0;
520                 dir.flags = DIR_SHOW_IGNORED | DIR_SHOW_OTHER_DIRECTORIES;
521                 fill_directory(&dir, s->pathspec);
522                 for (i = 0; i < dir.nr; i++) {
523                         struct dir_entry *ent = dir.entries[i];
524                         if (cache_name_is_other(ent->name, ent->len) &&
525                             match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
526                                 string_list_insert(&s->ignored, ent->name);
527                         free(ent);
528                 }
529         }
530
531         free(dir.entries);
532 }
533
534 void wt_status_collect(struct wt_status *s)
535 {
536         wt_status_collect_changes_worktree(s);
537
538         if (s->is_initial)
539                 wt_status_collect_changes_initial(s);
540         else
541                 wt_status_collect_changes_index(s);
542         wt_status_collect_untracked(s);
543 }
544
545 static void wt_status_print_unmerged(struct wt_status *s)
546 {
547         int shown_header = 0;
548         int i;
549
550         for (i = 0; i < s->change.nr; i++) {
551                 struct wt_status_change_data *d;
552                 struct string_list_item *it;
553                 it = &(s->change.items[i]);
554                 d = it->util;
555                 if (!d->stagemask)
556                         continue;
557                 if (!shown_header) {
558                         wt_status_print_unmerged_header(s);
559                         shown_header = 1;
560                 }
561                 wt_status_print_unmerged_data(s, it);
562         }
563         if (shown_header)
564                 wt_status_print_trailer(s);
565
566 }
567
568 static void wt_status_print_updated(struct wt_status *s)
569 {
570         int shown_header = 0;
571         int i;
572
573         for (i = 0; i < s->change.nr; i++) {
574                 struct wt_status_change_data *d;
575                 struct string_list_item *it;
576                 it = &(s->change.items[i]);
577                 d = it->util;
578                 if (!d->index_status ||
579                     d->index_status == DIFF_STATUS_UNMERGED)
580                         continue;
581                 if (!shown_header) {
582                         wt_status_print_cached_header(s);
583                         s->commitable = 1;
584                         shown_header = 1;
585                 }
586                 wt_status_print_change_data(s, WT_STATUS_UPDATED, it);
587         }
588         if (shown_header)
589                 wt_status_print_trailer(s);
590 }
591
592 /*
593  * -1 : has delete
594  *  0 : no change
595  *  1 : some change but no delete
596  */
597 static int wt_status_check_worktree_changes(struct wt_status *s,
598                                              int *dirty_submodules)
599 {
600         int i;
601         int changes = 0;
602
603         *dirty_submodules = 0;
604
605         for (i = 0; i < s->change.nr; i++) {
606                 struct wt_status_change_data *d;
607                 d = s->change.items[i].util;
608                 if (!d->worktree_status ||
609                     d->worktree_status == DIFF_STATUS_UNMERGED)
610                         continue;
611                 if (!changes)
612                         changes = 1;
613                 if (d->dirty_submodule)
614                         *dirty_submodules = 1;
615                 if (d->worktree_status == DIFF_STATUS_DELETED)
616                         changes = -1;
617         }
618         return changes;
619 }
620
621 static void wt_status_print_changed(struct wt_status *s)
622 {
623         int i, dirty_submodules;
624         int worktree_changes = wt_status_check_worktree_changes(s, &dirty_submodules);
625
626         if (!worktree_changes)
627                 return;
628
629         wt_status_print_dirty_header(s, worktree_changes < 0, dirty_submodules);
630
631         for (i = 0; i < s->change.nr; i++) {
632                 struct wt_status_change_data *d;
633                 struct string_list_item *it;
634                 it = &(s->change.items[i]);
635                 d = it->util;
636                 if (!d->worktree_status ||
637                     d->worktree_status == DIFF_STATUS_UNMERGED)
638                         continue;
639                 wt_status_print_change_data(s, WT_STATUS_CHANGED, it);
640         }
641         wt_status_print_trailer(s);
642 }
643
644 static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted)
645 {
646         struct child_process sm_summary;
647         char summary_limit[64];
648         char index[PATH_MAX];
649         const char *env[] = { NULL, NULL };
650         const char *argv[8];
651
652         env[0] =        index;
653         argv[0] =       "submodule";
654         argv[1] =       "summary";
655         argv[2] =       uncommitted ? "--files" : "--cached";
656         argv[3] =       "--for-status";
657         argv[4] =       "--summary-limit";
658         argv[5] =       summary_limit;
659         argv[6] =       uncommitted ? NULL : (s->amend ? "HEAD^" : "HEAD");
660         argv[7] =       NULL;
661
662         sprintf(summary_limit, "%d", s->submodule_summary);
663         snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
664
665         memset(&sm_summary, 0, sizeof(sm_summary));
666         sm_summary.argv = argv;
667         sm_summary.env = env;
668         sm_summary.git_cmd = 1;
669         sm_summary.no_stdin = 1;
670         fflush(s->fp);
671         sm_summary.out = dup(fileno(s->fp));    /* run_command closes it */
672         run_command(&sm_summary);
673 }
674
675 static void wt_status_print_other(struct wt_status *s,
676                                   struct string_list *l,
677                                   const char *what,
678                                   const char *how)
679 {
680         int i;
681         struct strbuf buf = STRBUF_INIT;
682         static struct string_list output = STRING_LIST_INIT_DUP;
683         struct column_options copts;
684
685         if (!l->nr)
686                 return;
687
688         wt_status_print_other_header(s, what, how);
689
690         for (i = 0; i < l->nr; i++) {
691                 struct string_list_item *it;
692                 const char *path;
693                 it = &(l->items[i]);
694                 path = quote_path(it->string, strlen(it->string),
695                                   &buf, s->prefix);
696                 if (column_active(s->colopts)) {
697                         string_list_append(&output, path);
698                         continue;
699                 }
700                 status_printf(s, color(WT_STATUS_HEADER, s), "\t");
701                 status_printf_more(s, color(WT_STATUS_UNTRACKED, s),
702                                    "%s\n", path);
703         }
704
705         strbuf_release(&buf);
706         if (!column_active(s->colopts))
707                 return;
708
709         strbuf_addf(&buf, "%s#\t%s",
710                     color(WT_STATUS_HEADER, s),
711                     color(WT_STATUS_UNTRACKED, s));
712         memset(&copts, 0, sizeof(copts));
713         copts.padding = 1;
714         copts.indent = buf.buf;
715         if (want_color(s->use_color))
716                 copts.nl = GIT_COLOR_RESET "\n";
717         print_columns(&output, s->colopts, &copts);
718         string_list_clear(&output, 0);
719         strbuf_release(&buf);
720 }
721
722 static void wt_status_print_verbose(struct wt_status *s)
723 {
724         struct rev_info rev;
725         struct setup_revision_opt opt;
726
727         init_revisions(&rev, NULL);
728         DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
729
730         memset(&opt, 0, sizeof(opt));
731         opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
732         setup_revisions(0, NULL, &rev, &opt);
733
734         rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
735         rev.diffopt.detect_rename = 1;
736         rev.diffopt.file = s->fp;
737         rev.diffopt.close_file = 0;
738         /*
739          * If we're not going to stdout, then we definitely don't
740          * want color, since we are going to the commit message
741          * file (and even the "auto" setting won't work, since it
742          * will have checked isatty on stdout).
743          */
744         if (s->fp != stdout)
745                 rev.diffopt.use_color = 0;
746         run_diff_index(&rev, 1);
747 }
748
749 static void wt_status_print_tracking(struct wt_status *s)
750 {
751         struct strbuf sb = STRBUF_INIT;
752         const char *cp, *ep;
753         struct branch *branch;
754
755         assert(s->branch && !s->is_initial);
756         if (prefixcmp(s->branch, "refs/heads/"))
757                 return;
758         branch = branch_get(s->branch + 11);
759         if (!format_tracking_info(branch, &sb))
760                 return;
761
762         for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
763                 color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
764                                  "# %.*s", (int)(ep - cp), cp);
765         color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
766 }
767
768 static int has_unmerged(struct wt_status *s)
769 {
770         int i;
771
772         for (i = 0; i < s->change.nr; i++) {
773                 struct wt_status_change_data *d;
774                 d = s->change.items[i].util;
775                 if (d->stagemask)
776                         return 1;
777         }
778         return 0;
779 }
780
781 static void show_merge_in_progress(struct wt_status *s,
782                                 struct wt_status_state *state,
783                                 const char *color)
784 {
785         if (has_unmerged(s)) {
786                 status_printf_ln(s, color, _("You have unmerged paths."));
787                 if (advice_status_hints)
788                         status_printf_ln(s, color,
789                                 _("  (fix conflicts and run \"git commit\")"));
790         } else {
791                 status_printf_ln(s, color,
792                         _("All conflicts fixed but you are still merging."));
793                 if (advice_status_hints)
794                         status_printf_ln(s, color,
795                                 _("  (use \"git commit\" to conclude merge)"));
796         }
797         wt_status_print_trailer(s);
798 }
799
800 static void show_am_in_progress(struct wt_status *s,
801                                 struct wt_status_state *state,
802                                 const char *color)
803 {
804         status_printf_ln(s, color,
805                 _("You are in the middle of an am session."));
806         if (state->am_empty_patch)
807                 status_printf_ln(s, color,
808                         _("The current patch is empty."));
809         if (advice_status_hints) {
810                 if (!state->am_empty_patch)
811                         status_printf_ln(s, color,
812                                 _("  (fix conflicts and then run \"git am --resolved\")"));
813                 status_printf_ln(s, color,
814                         _("  (use \"git am --skip\" to skip this patch)"));
815                 status_printf_ln(s, color,
816                         _("  (use \"git am --abort\" to restore the original branch)"));
817         }
818         wt_status_print_trailer(s);
819 }
820
821 static char *read_line_from_git_path(const char *filename)
822 {
823         struct strbuf buf = STRBUF_INIT;
824         FILE *fp = fopen(git_path("%s", filename), "r");
825         if (!fp) {
826                 strbuf_release(&buf);
827                 return NULL;
828         }
829         strbuf_getline(&buf, fp, '\n');
830         if (!fclose(fp)) {
831                 return strbuf_detach(&buf, NULL);
832         } else {
833                 strbuf_release(&buf);
834                 return NULL;
835         }
836 }
837
838 static int split_commit_in_progress(struct wt_status *s)
839 {
840         int split_in_progress = 0;
841         char *head = read_line_from_git_path("HEAD");
842         char *orig_head = read_line_from_git_path("ORIG_HEAD");
843         char *rebase_amend = read_line_from_git_path("rebase-merge/amend");
844         char *rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
845
846         if (!head || !orig_head || !rebase_amend || !rebase_orig_head ||
847             !s->branch || strcmp(s->branch, "HEAD"))
848                 return split_in_progress;
849
850         if (!strcmp(rebase_amend, rebase_orig_head)) {
851                 if (strcmp(head, rebase_amend))
852                         split_in_progress = 1;
853         } else if (strcmp(orig_head, rebase_orig_head)) {
854                 split_in_progress = 1;
855         }
856
857         if (!s->amend && !s->nowarn && !s->workdir_dirty)
858                 split_in_progress = 0;
859
860         free(head);
861         free(orig_head);
862         free(rebase_amend);
863         free(rebase_orig_head);
864         return split_in_progress;
865 }
866
867 static void show_rebase_in_progress(struct wt_status *s,
868                                 struct wt_status_state *state,
869                                 const char *color)
870 {
871         struct stat st;
872
873         if (has_unmerged(s)) {
874                 status_printf_ln(s, color, _("You are currently rebasing."));
875                 if (advice_status_hints) {
876                         status_printf_ln(s, color,
877                                 _("  (fix conflicts and then run \"git rebase --continue\")"));
878                         status_printf_ln(s, color,
879                                 _("  (use \"git rebase --skip\" to skip this patch)"));
880                         status_printf_ln(s, color,
881                                 _("  (use \"git rebase --abort\" to check out the original branch)"));
882                 }
883         } else if (state->rebase_in_progress || !stat(git_path("MERGE_MSG"), &st)) {
884                 status_printf_ln(s, color, _("You are currently rebasing."));
885                 if (advice_status_hints)
886                         status_printf_ln(s, color,
887                                 _("  (all conflicts fixed: run \"git rebase --continue\")"));
888         } else if (split_commit_in_progress(s)) {
889                 status_printf_ln(s, color, _("You are currently splitting a commit during a rebase."));
890                 if (advice_status_hints)
891                         status_printf_ln(s, color,
892                                 _("  (Once your working directory is clean, run \"git rebase --continue\")"));
893         } else {
894                 status_printf_ln(s, color, _("You are currently editing a commit during a rebase."));
895                 if (advice_status_hints && !s->amend) {
896                         status_printf_ln(s, color,
897                                 _("  (use \"git commit --amend\" to amend the current commit)"));
898                         status_printf_ln(s, color,
899                                 _("  (use \"git rebase --continue\" once you are satisfied with your changes)"));
900                 }
901         }
902         wt_status_print_trailer(s);
903 }
904
905 static void show_cherry_pick_in_progress(struct wt_status *s,
906                                         struct wt_status_state *state,
907                                         const char *color)
908 {
909         status_printf_ln(s, color, _("You are currently cherry-picking."));
910         if (advice_status_hints) {
911                 if (has_unmerged(s))
912                         status_printf_ln(s, color,
913                                 _("  (fix conflicts and run \"git commit\")"));
914                 else
915                         status_printf_ln(s, color,
916                                 _("  (all conflicts fixed: run \"git commit\")"));
917         }
918         wt_status_print_trailer(s);
919 }
920
921 static void show_bisect_in_progress(struct wt_status *s,
922                                 struct wt_status_state *state,
923                                 const char *color)
924 {
925         status_printf_ln(s, color, _("You are currently bisecting."));
926         if (advice_status_hints)
927                 status_printf_ln(s, color,
928                         _("  (use \"git bisect reset\" to get back to the original branch)"));
929         wt_status_print_trailer(s);
930 }
931
932 static void wt_status_print_state(struct wt_status *s)
933 {
934         const char *state_color = color(WT_STATUS_IN_PROGRESS, s);
935         struct wt_status_state state;
936         struct stat st;
937
938         memset(&state, 0, sizeof(state));
939
940         if (!stat(git_path("MERGE_HEAD"), &st)) {
941                 state.merge_in_progress = 1;
942         } else if (!stat(git_path("rebase-apply"), &st)) {
943                 if (!stat(git_path("rebase-apply/applying"), &st)) {
944                         state.am_in_progress = 1;
945                         if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
946                                 state.am_empty_patch = 1;
947                 } else {
948                         state.rebase_in_progress = 1;
949                 }
950         } else if (!stat(git_path("rebase-merge"), &st)) {
951                 if (!stat(git_path("rebase-merge/interactive"), &st))
952                         state.rebase_interactive_in_progress = 1;
953                 else
954                         state.rebase_in_progress = 1;
955         } else if (!stat(git_path("CHERRY_PICK_HEAD"), &st)) {
956                 state.cherry_pick_in_progress = 1;
957         }
958         if (!stat(git_path("BISECT_LOG"), &st))
959                 state.bisect_in_progress = 1;
960
961         if (state.merge_in_progress)
962                 show_merge_in_progress(s, &state, state_color);
963         else if (state.am_in_progress)
964                 show_am_in_progress(s, &state, state_color);
965         else if (state.rebase_in_progress || state.rebase_interactive_in_progress)
966                 show_rebase_in_progress(s, &state, state_color);
967         else if (state.cherry_pick_in_progress)
968                 show_cherry_pick_in_progress(s, &state, state_color);
969         if (state.bisect_in_progress)
970                 show_bisect_in_progress(s, &state, state_color);
971 }
972
973 void wt_status_print(struct wt_status *s)
974 {
975         const char *branch_color = color(WT_STATUS_ONBRANCH, s);
976         const char *branch_status_color = color(WT_STATUS_HEADER, s);
977
978         if (s->branch) {
979                 const char *on_what = _("On branch ");
980                 const char *branch_name = s->branch;
981                 if (!prefixcmp(branch_name, "refs/heads/"))
982                         branch_name += 11;
983                 else if (!strcmp(branch_name, "HEAD")) {
984                         branch_name = "";
985                         branch_status_color = color(WT_STATUS_NOBRANCH, s);
986                         on_what = _("Not currently on any branch.");
987                 }
988                 status_printf(s, color(WT_STATUS_HEADER, s), "");
989                 status_printf_more(s, branch_status_color, "%s", on_what);
990                 status_printf_more(s, branch_color, "%s\n", branch_name);
991                 if (!s->is_initial)
992                         wt_status_print_tracking(s);
993         }
994
995         wt_status_print_state(s);
996         if (s->is_initial) {
997                 status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
998                 status_printf_ln(s, color(WT_STATUS_HEADER, s), _("Initial commit"));
999                 status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
1000         }
1001
1002         wt_status_print_updated(s);
1003         wt_status_print_unmerged(s);
1004         wt_status_print_changed(s);
1005         if (s->submodule_summary &&
1006             (!s->ignore_submodule_arg ||
1007              strcmp(s->ignore_submodule_arg, "all"))) {
1008                 wt_status_print_submodule_summary(s, 0);  /* staged */
1009                 wt_status_print_submodule_summary(s, 1);  /* unstaged */
1010         }
1011         if (s->show_untracked_files) {
1012                 wt_status_print_other(s, &s->untracked, _("Untracked"), "add");
1013                 if (s->show_ignored_files)
1014                         wt_status_print_other(s, &s->ignored, _("Ignored"), "add -f");
1015         } else if (s->commitable)
1016                 status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
1017                         advice_status_hints
1018                         ? _(" (use -u option to show untracked files)") : "");
1019
1020         if (s->verbose)
1021                 wt_status_print_verbose(s);
1022         if (!s->commitable) {
1023                 if (s->amend)
1024                         status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes"));
1025                 else if (s->nowarn)
1026                         ; /* nothing */
1027                 else if (s->workdir_dirty)
1028                         printf(_("no changes added to commit%s\n"),
1029                                 advice_status_hints
1030                                 ? _(" (use \"git add\" and/or \"git commit -a\")") : "");
1031                 else if (s->untracked.nr)
1032                         printf(_("nothing added to commit but untracked files present%s\n"),
1033                                 advice_status_hints
1034                                 ? _(" (use \"git add\" to track)") : "");
1035                 else if (s->is_initial)
1036                         printf(_("nothing to commit%s\n"), advice_status_hints
1037                                 ? _(" (create/copy files and use \"git add\" to track)") : "");
1038                 else if (!s->show_untracked_files)
1039                         printf(_("nothing to commit%s\n"), advice_status_hints
1040                                 ? _(" (use -u to show untracked files)") : "");
1041                 else
1042                         printf(_("nothing to commit%s\n"), advice_status_hints
1043                                 ? _(" (working directory clean)") : "");
1044         }
1045 }
1046
1047 static void wt_shortstatus_unmerged(struct string_list_item *it,
1048                            struct wt_status *s)
1049 {
1050         struct wt_status_change_data *d = it->util;
1051         const char *how = "??";
1052
1053         switch (d->stagemask) {
1054         case 1: how = "DD"; break; /* both deleted */
1055         case 2: how = "AU"; break; /* added by us */
1056         case 3: how = "UD"; break; /* deleted by them */
1057         case 4: how = "UA"; break; /* added by them */
1058         case 5: how = "DU"; break; /* deleted by us */
1059         case 6: how = "AA"; break; /* both added */
1060         case 7: how = "UU"; break; /* both modified */
1061         }
1062         color_fprintf(s->fp, color(WT_STATUS_UNMERGED, s), "%s", how);
1063         if (s->null_termination) {
1064                 fprintf(stdout, " %s%c", it->string, 0);
1065         } else {
1066                 struct strbuf onebuf = STRBUF_INIT;
1067                 const char *one;
1068                 one = quote_path(it->string, -1, &onebuf, s->prefix);
1069                 printf(" %s\n", one);
1070                 strbuf_release(&onebuf);
1071         }
1072 }
1073
1074 static void wt_shortstatus_status(struct string_list_item *it,
1075                          struct wt_status *s)
1076 {
1077         struct wt_status_change_data *d = it->util;
1078
1079         if (d->index_status)
1080                 color_fprintf(s->fp, color(WT_STATUS_UPDATED, s), "%c", d->index_status);
1081         else
1082                 putchar(' ');
1083         if (d->worktree_status)
1084                 color_fprintf(s->fp, color(WT_STATUS_CHANGED, s), "%c", d->worktree_status);
1085         else
1086                 putchar(' ');
1087         putchar(' ');
1088         if (s->null_termination) {
1089                 fprintf(stdout, "%s%c", it->string, 0);
1090                 if (d->head_path)
1091                         fprintf(stdout, "%s%c", d->head_path, 0);
1092         } else {
1093                 struct strbuf onebuf = STRBUF_INIT;
1094                 const char *one;
1095                 if (d->head_path) {
1096                         one = quote_path(d->head_path, -1, &onebuf, s->prefix);
1097                         if (*one != '"' && strchr(one, ' ') != NULL) {
1098                                 putchar('"');
1099                                 strbuf_addch(&onebuf, '"');
1100                                 one = onebuf.buf;
1101                         }
1102                         printf("%s -> ", one);
1103                         strbuf_release(&onebuf);
1104                 }
1105                 one = quote_path(it->string, -1, &onebuf, s->prefix);
1106                 if (*one != '"' && strchr(one, ' ') != NULL) {
1107                         putchar('"');
1108                         strbuf_addch(&onebuf, '"');
1109                         one = onebuf.buf;
1110                 }
1111                 printf("%s\n", one);
1112                 strbuf_release(&onebuf);
1113         }
1114 }
1115
1116 static void wt_shortstatus_other(struct string_list_item *it,
1117                                  struct wt_status *s, const char *sign)
1118 {
1119         if (s->null_termination) {
1120                 fprintf(stdout, "%s %s%c", sign, it->string, 0);
1121         } else {
1122                 struct strbuf onebuf = STRBUF_INIT;
1123                 const char *one;
1124                 one = quote_path(it->string, -1, &onebuf, s->prefix);
1125                 color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign);
1126                 printf(" %s\n", one);
1127                 strbuf_release(&onebuf);
1128         }
1129 }
1130
1131 static void wt_shortstatus_print_tracking(struct wt_status *s)
1132 {
1133         struct branch *branch;
1134         const char *header_color = color(WT_STATUS_HEADER, s);
1135         const char *branch_color_local = color(WT_STATUS_LOCAL_BRANCH, s);
1136         const char *branch_color_remote = color(WT_STATUS_REMOTE_BRANCH, s);
1137
1138         const char *base;
1139         const char *branch_name;
1140         int num_ours, num_theirs;
1141
1142         color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## ");
1143
1144         if (!s->branch)
1145                 return;
1146         branch_name = s->branch;
1147
1148         if (!prefixcmp(branch_name, "refs/heads/"))
1149                 branch_name += 11;
1150         else if (!strcmp(branch_name, "HEAD")) {
1151                 branch_name = _("HEAD (no branch)");
1152                 branch_color_local = color(WT_STATUS_NOBRANCH, s);
1153         }
1154
1155         branch = branch_get(s->branch + 11);
1156         if (s->is_initial)
1157                 color_fprintf(s->fp, header_color, _("Initial commit on "));
1158         if (!stat_tracking_info(branch, &num_ours, &num_theirs)) {
1159                 color_fprintf(s->fp, branch_color_local, "%s", branch_name);
1160                 fputc(s->null_termination ? '\0' : '\n', s->fp);
1161                 return;
1162         }
1163
1164         base = branch->merge[0]->dst;
1165         base = shorten_unambiguous_ref(base, 0);
1166         color_fprintf(s->fp, branch_color_local, "%s", branch_name);
1167         color_fprintf(s->fp, header_color, "...");
1168         color_fprintf(s->fp, branch_color_remote, "%s", base);
1169
1170         color_fprintf(s->fp, header_color, " [");
1171         if (!num_ours) {
1172                 color_fprintf(s->fp, header_color, _("behind "));
1173                 color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
1174         } else if (!num_theirs) {
1175                 color_fprintf(s->fp, header_color, _("ahead "));
1176                 color_fprintf(s->fp, branch_color_local, "%d", num_ours);
1177         } else {
1178                 color_fprintf(s->fp, header_color, _("ahead "));
1179                 color_fprintf(s->fp, branch_color_local, "%d", num_ours);
1180                 color_fprintf(s->fp, header_color, _(", behind "));
1181                 color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
1182         }
1183
1184         color_fprintf(s->fp, header_color, "]");
1185         fputc(s->null_termination ? '\0' : '\n', s->fp);
1186 }
1187
1188 void wt_shortstatus_print(struct wt_status *s)
1189 {
1190         int i;
1191
1192         if (s->show_branch)
1193                 wt_shortstatus_print_tracking(s);
1194
1195         for (i = 0; i < s->change.nr; i++) {
1196                 struct wt_status_change_data *d;
1197                 struct string_list_item *it;
1198
1199                 it = &(s->change.items[i]);
1200                 d = it->util;
1201                 if (d->stagemask)
1202                         wt_shortstatus_unmerged(it, s);
1203                 else
1204                         wt_shortstatus_status(it, s);
1205         }
1206         for (i = 0; i < s->untracked.nr; i++) {
1207                 struct string_list_item *it;
1208
1209                 it = &(s->untracked.items[i]);
1210                 wt_shortstatus_other(it, s, "??");
1211         }
1212         for (i = 0; i < s->ignored.nr; i++) {
1213                 struct string_list_item *it;
1214
1215                 it = &(s->ignored.items[i]);
1216                 wt_shortstatus_other(it, s, "!!");
1217         }
1218 }
1219
1220 void wt_porcelain_print(struct wt_status *s)
1221 {
1222         s->use_color = 0;
1223         s->relative_paths = 0;
1224         s->prefix = NULL;
1225         wt_shortstatus_print(s);
1226 }