Merge branch 'ds/doc-add-interactive-singlekey'
[alt-git.git] / wt-status.c
blobb1d3f85d728a80b987874c502fc363e42ba7e67d
1 #include "git-compat-util.h"
2 #include "advice.h"
3 #include "wt-status.h"
4 #include "object.h"
5 #include "dir.h"
6 #include "commit.h"
7 #include "diff.h"
8 #include "environment.h"
9 #include "gettext.h"
10 #include "hash.h"
11 #include "hex.h"
12 #include "object-name.h"
13 #include "path.h"
14 #include "revision.h"
15 #include "diffcore.h"
16 #include "quote.h"
17 #include "run-command.h"
18 #include "strvec.h"
19 #include "remote.h"
20 #include "refs.h"
21 #include "submodule.h"
22 #include "column.h"
23 #include "read-cache.h"
24 #include "setup.h"
25 #include "strbuf.h"
26 #include "trace.h"
27 #include "trace2.h"
28 #include "tree.h"
29 #include "utf8.h"
30 #include "worktree.h"
31 #include "lockfile.h"
32 #include "sequencer.h"
33 #include "fsmonitor-settings.h"
35 #define AB_DELAY_WARNING_IN_MS (2 * 1000)
36 #define UF_DELAY_WARNING_IN_MS (2 * 1000)
38 static const char cut_line[] =
39 "------------------------ >8 ------------------------\n";
41 static char default_wt_status_colors[][COLOR_MAXLEN] = {
42 GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
43 GIT_COLOR_GREEN, /* WT_STATUS_UPDATED */
44 GIT_COLOR_RED, /* WT_STATUS_CHANGED */
45 GIT_COLOR_RED, /* WT_STATUS_UNTRACKED */
46 GIT_COLOR_RED, /* WT_STATUS_NOBRANCH */
47 GIT_COLOR_RED, /* WT_STATUS_UNMERGED */
48 GIT_COLOR_GREEN, /* WT_STATUS_LOCAL_BRANCH */
49 GIT_COLOR_RED, /* WT_STATUS_REMOTE_BRANCH */
50 GIT_COLOR_NIL, /* WT_STATUS_ONBRANCH */
53 static const char *color(int slot, struct wt_status *s)
55 const char *c = "";
56 if (want_color(s->use_color))
57 c = s->color_palette[slot];
58 if (slot == WT_STATUS_ONBRANCH && color_is_nil(c))
59 c = s->color_palette[WT_STATUS_HEADER];
60 return c;
63 static void status_vprintf(struct wt_status *s, int at_bol, const char *color,
64 const char *fmt, va_list ap, const char *trail)
66 struct strbuf sb = STRBUF_INIT;
67 struct strbuf linebuf = STRBUF_INIT;
68 const char *line, *eol;
70 strbuf_vaddf(&sb, fmt, ap);
71 if (!sb.len) {
72 if (s->display_comment_prefix) {
73 strbuf_addstr(&sb, comment_line_str);
74 if (!trail)
75 strbuf_addch(&sb, ' ');
77 color_print_strbuf(s->fp, color, &sb);
78 if (trail)
79 fprintf(s->fp, "%s", trail);
80 strbuf_release(&sb);
81 return;
83 for (line = sb.buf; *line; line = eol + 1) {
84 eol = strchr(line, '\n');
86 strbuf_reset(&linebuf);
87 if (at_bol && s->display_comment_prefix) {
88 strbuf_addstr(&linebuf, comment_line_str);
89 if (*line != '\n' && *line != '\t')
90 strbuf_addch(&linebuf, ' ');
92 if (eol)
93 strbuf_add(&linebuf, line, eol - line);
94 else
95 strbuf_addstr(&linebuf, line);
96 color_print_strbuf(s->fp, color, &linebuf);
97 if (eol)
98 fprintf(s->fp, "\n");
99 else
100 break;
101 at_bol = 1;
103 if (trail)
104 fprintf(s->fp, "%s", trail);
105 strbuf_release(&linebuf);
106 strbuf_release(&sb);
109 void status_printf_ln(struct wt_status *s, const char *color,
110 const char *fmt, ...)
112 va_list ap;
114 va_start(ap, fmt);
115 status_vprintf(s, 1, color, fmt, ap, "\n");
116 va_end(ap);
119 void status_printf(struct wt_status *s, const char *color,
120 const char *fmt, ...)
122 va_list ap;
124 va_start(ap, fmt);
125 status_vprintf(s, 1, color, fmt, ap, NULL);
126 va_end(ap);
129 __attribute__((format (printf, 3, 4)))
130 static void status_printf_more(struct wt_status *s, const char *color,
131 const char *fmt, ...)
133 va_list ap;
135 va_start(ap, fmt);
136 status_vprintf(s, 0, color, fmt, ap, NULL);
137 va_end(ap);
140 void wt_status_prepare(struct repository *r, struct wt_status *s)
142 memset(s, 0, sizeof(*s));
143 s->repo = r;
144 memcpy(s->color_palette, default_wt_status_colors,
145 sizeof(default_wt_status_colors));
146 s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
147 s->use_color = -1;
148 s->relative_paths = 1;
149 s->branch = refs_resolve_refdup(get_main_ref_store(the_repository),
150 "HEAD", 0, NULL, NULL);
151 s->reference = "HEAD";
152 s->fp = stdout;
153 s->index_file = get_index_file();
154 s->change.strdup_strings = 1;
155 s->untracked.strdup_strings = 1;
156 s->ignored.strdup_strings = 1;
157 s->show_branch = -1; /* unspecified */
158 s->show_stash = 0;
159 s->ahead_behind_flags = AHEAD_BEHIND_UNSPECIFIED;
160 s->display_comment_prefix = 0;
161 s->detect_rename = -1;
162 s->rename_score = -1;
163 s->rename_limit = -1;
166 static void wt_longstatus_print_unmerged_header(struct wt_status *s)
168 int i;
169 int del_mod_conflict = 0;
170 int both_deleted = 0;
171 int not_deleted = 0;
172 const char *c = color(WT_STATUS_HEADER, s);
174 status_printf_ln(s, c, _("Unmerged paths:"));
176 for (i = 0; i < s->change.nr; i++) {
177 struct string_list_item *it = &(s->change.items[i]);
178 struct wt_status_change_data *d = it->util;
180 switch (d->stagemask) {
181 case 0:
182 break;
183 case 1:
184 both_deleted = 1;
185 break;
186 case 3:
187 case 5:
188 del_mod_conflict = 1;
189 break;
190 default:
191 not_deleted = 1;
192 break;
196 if (!s->hints)
197 return;
198 if (s->whence != FROM_COMMIT)
200 else if (!s->is_initial) {
201 if (!strcmp(s->reference, "HEAD"))
202 status_printf_ln(s, c,
203 _(" (use \"git restore --staged <file>...\" to unstage)"));
204 else
205 status_printf_ln(s, c,
206 _(" (use \"git restore --source=%s --staged <file>...\" to unstage)"),
207 s->reference);
208 } else
209 status_printf_ln(s, c, _(" (use \"git rm --cached <file>...\" to unstage)"));
211 if (!both_deleted) {
212 if (!del_mod_conflict)
213 status_printf_ln(s, c, _(" (use \"git add <file>...\" to mark resolution)"));
214 else
215 status_printf_ln(s, c, _(" (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
216 } else if (!del_mod_conflict && !not_deleted) {
217 status_printf_ln(s, c, _(" (use \"git rm <file>...\" to mark resolution)"));
218 } else {
219 status_printf_ln(s, c, _(" (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
223 static void wt_longstatus_print_cached_header(struct wt_status *s)
225 const char *c = color(WT_STATUS_HEADER, s);
227 status_printf_ln(s, c, _("Changes to be committed:"));
228 if (!s->hints)
229 return;
230 if (s->whence != FROM_COMMIT)
231 ; /* NEEDSWORK: use "git reset --unresolve"??? */
232 else if (!s->is_initial) {
233 if (!strcmp(s->reference, "HEAD"))
234 status_printf_ln(s, c
235 , _(" (use \"git restore --staged <file>...\" to unstage)"));
236 else
237 status_printf_ln(s, c,
238 _(" (use \"git restore --source=%s --staged <file>...\" to unstage)"),
239 s->reference);
240 } else
241 status_printf_ln(s, c, _(" (use \"git rm --cached <file>...\" to unstage)"));
244 static void wt_longstatus_print_dirty_header(struct wt_status *s,
245 int has_deleted,
246 int has_dirty_submodules)
248 const char *c = color(WT_STATUS_HEADER, s);
250 status_printf_ln(s, c, _("Changes not staged for commit:"));
251 if (!s->hints)
252 return;
253 if (!has_deleted)
254 status_printf_ln(s, c, _(" (use \"git add <file>...\" to update what will be committed)"));
255 else
256 status_printf_ln(s, c, _(" (use \"git add/rm <file>...\" to update what will be committed)"));
257 status_printf_ln(s, c, _(" (use \"git restore <file>...\" to discard changes in working directory)"));
258 if (has_dirty_submodules)
259 status_printf_ln(s, c, _(" (commit or discard the untracked or modified content in submodules)"));
262 static void wt_longstatus_print_other_header(struct wt_status *s,
263 const char *what,
264 const char *how)
266 const char *c = color(WT_STATUS_HEADER, s);
267 status_printf_ln(s, c, "%s:", what);
268 if (!s->hints)
269 return;
270 status_printf_ln(s, c, _(" (use \"git %s <file>...\" to include in what will be committed)"), how);
273 static void wt_longstatus_print_trailer(struct wt_status *s)
275 status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
278 static const char *wt_status_unmerged_status_string(int stagemask)
280 switch (stagemask) {
281 case 1:
282 return _("both deleted:");
283 case 2:
284 return _("added by us:");
285 case 3:
286 return _("deleted by them:");
287 case 4:
288 return _("added by them:");
289 case 5:
290 return _("deleted by us:");
291 case 6:
292 return _("both added:");
293 case 7:
294 return _("both modified:");
295 default:
296 BUG("unhandled unmerged status %x", stagemask);
300 static const char *wt_status_diff_status_string(int status)
302 switch (status) {
303 case DIFF_STATUS_ADDED:
304 return _("new file:");
305 case DIFF_STATUS_COPIED:
306 return _("copied:");
307 case DIFF_STATUS_DELETED:
308 return _("deleted:");
309 case DIFF_STATUS_MODIFIED:
310 return _("modified:");
311 case DIFF_STATUS_RENAMED:
312 return _("renamed:");
313 case DIFF_STATUS_TYPE_CHANGED:
314 return _("typechange:");
315 case DIFF_STATUS_UNKNOWN:
316 return _("unknown:");
317 case DIFF_STATUS_UNMERGED:
318 return _("unmerged:");
319 default:
320 return NULL;
324 static int maxwidth(const char *(*label)(int), int minval, int maxval)
326 int result = 0, i;
328 for (i = minval; i <= maxval; i++) {
329 const char *s = label(i);
330 int len = s ? utf8_strwidth(s) : 0;
331 if (len > result)
332 result = len;
334 return result;
337 static void wt_longstatus_print_unmerged_data(struct wt_status *s,
338 struct string_list_item *it)
340 const char *c = color(WT_STATUS_UNMERGED, s);
341 struct wt_status_change_data *d = it->util;
342 struct strbuf onebuf = STRBUF_INIT;
343 static char *padding;
344 static int label_width;
345 const char *one, *how;
346 int len;
348 if (!padding) {
349 label_width = maxwidth(wt_status_unmerged_status_string, 1, 7);
350 label_width += strlen(" ");
351 padding = xmallocz(label_width);
352 memset(padding, ' ', label_width);
355 one = quote_path(it->string, s->prefix, &onebuf, 0);
356 status_printf(s, color(WT_STATUS_HEADER, s), "\t");
358 how = wt_status_unmerged_status_string(d->stagemask);
359 len = label_width - utf8_strwidth(how);
360 status_printf_more(s, c, "%s%.*s%s\n", how, len, padding, one);
361 strbuf_release(&onebuf);
364 static void wt_longstatus_print_change_data(struct wt_status *s,
365 int change_type,
366 struct string_list_item *it)
368 struct wt_status_change_data *d = it->util;
369 const char *c = color(change_type, s);
370 int status;
371 char *one_name;
372 char *two_name;
373 const char *one, *two;
374 struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
375 struct strbuf extra = STRBUF_INIT;
376 static char *padding;
377 static int label_width;
378 const char *what;
379 int len;
381 if (!padding) {
382 /* If DIFF_STATUS_* uses outside the range [A..Z], we're in trouble */
383 label_width = maxwidth(wt_status_diff_status_string, 'A', 'Z');
384 label_width += strlen(" ");
385 padding = xmallocz(label_width);
386 memset(padding, ' ', label_width);
389 one_name = two_name = it->string;
390 switch (change_type) {
391 case WT_STATUS_UPDATED:
392 status = d->index_status;
393 break;
394 case WT_STATUS_CHANGED:
395 if (d->new_submodule_commits || d->dirty_submodule) {
396 strbuf_addstr(&extra, " (");
397 if (d->new_submodule_commits)
398 strbuf_addstr(&extra, _("new commits, "));
399 if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
400 strbuf_addstr(&extra, _("modified content, "));
401 if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
402 strbuf_addstr(&extra, _("untracked content, "));
403 strbuf_setlen(&extra, extra.len - 2);
404 strbuf_addch(&extra, ')');
406 status = d->worktree_status;
407 break;
408 default:
409 BUG("unhandled change_type %d in wt_longstatus_print_change_data",
410 change_type);
414 * Only pick up the rename it's relevant. If the rename is for
415 * the changed section and we're printing the updated section,
416 * ignore it.
418 if (d->rename_status == status)
419 one_name = d->rename_source;
421 one = quote_path(one_name, s->prefix, &onebuf, 0);
422 two = quote_path(two_name, s->prefix, &twobuf, 0);
424 status_printf(s, color(WT_STATUS_HEADER, s), "\t");
425 what = wt_status_diff_status_string(status);
426 if (!what)
427 BUG("unhandled diff status %c", status);
428 len = label_width - utf8_strwidth(what);
429 assert(len >= 0);
430 if (one_name != two_name)
431 status_printf_more(s, c, "%s%.*s%s -> %s",
432 what, len, padding, one, two);
433 else
434 status_printf_more(s, c, "%s%.*s%s",
435 what, len, padding, one);
436 if (extra.len) {
437 status_printf_more(s, color(WT_STATUS_HEADER, s), "%s", extra.buf);
438 strbuf_release(&extra);
440 status_printf_more(s, GIT_COLOR_NORMAL, "\n");
441 strbuf_release(&onebuf);
442 strbuf_release(&twobuf);
445 static char short_submodule_status(struct wt_status_change_data *d)
447 if (d->new_submodule_commits)
448 return 'M';
449 if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
450 return 'm';
451 if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
452 return '?';
453 return d->worktree_status;
456 static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
457 struct diff_options *options UNUSED,
458 void *data)
460 struct wt_status *s = data;
461 int i;
463 if (!q->nr)
464 return;
465 s->workdir_dirty = 1;
466 for (i = 0; i < q->nr; i++) {
467 struct diff_filepair *p;
468 struct string_list_item *it;
469 struct wt_status_change_data *d;
471 p = q->queue[i];
472 it = string_list_insert(&s->change, p->two->path);
473 d = it->util;
474 if (!d) {
475 CALLOC_ARRAY(d, 1);
476 it->util = d;
478 if (!d->worktree_status)
479 d->worktree_status = p->status;
480 if (S_ISGITLINK(p->two->mode)) {
481 d->dirty_submodule = p->two->dirty_submodule;
482 d->new_submodule_commits = !oideq(&p->one->oid,
483 &p->two->oid);
484 if (s->status_format == STATUS_FORMAT_SHORT)
485 d->worktree_status = short_submodule_status(d);
488 switch (p->status) {
489 case DIFF_STATUS_ADDED:
490 d->mode_worktree = p->two->mode;
491 break;
493 case DIFF_STATUS_DELETED:
494 d->mode_index = p->one->mode;
495 oidcpy(&d->oid_index, &p->one->oid);
496 /* mode_worktree is zero for a delete. */
497 break;
499 case DIFF_STATUS_COPIED:
500 case DIFF_STATUS_RENAMED:
501 if (d->rename_status)
502 BUG("multiple renames on the same target? how?");
503 d->rename_source = xstrdup(p->one->path);
504 d->rename_score = p->score * 100 / MAX_SCORE;
505 d->rename_status = p->status;
506 /* fallthru */
507 case DIFF_STATUS_MODIFIED:
508 case DIFF_STATUS_TYPE_CHANGED:
509 case DIFF_STATUS_UNMERGED:
510 d->mode_index = p->one->mode;
511 d->mode_worktree = p->two->mode;
512 oidcpy(&d->oid_index, &p->one->oid);
513 break;
515 default:
516 BUG("unhandled diff-files status '%c'", p->status);
517 break;
523 static int unmerged_mask(struct index_state *istate, const char *path)
525 int pos, mask;
526 const struct cache_entry *ce;
528 pos = index_name_pos(istate, path, strlen(path));
529 if (0 <= pos)
530 return 0;
532 mask = 0;
533 pos = -pos-1;
534 while (pos < istate->cache_nr) {
535 ce = istate->cache[pos++];
536 if (strcmp(ce->name, path) || !ce_stage(ce))
537 break;
538 mask |= (1 << (ce_stage(ce) - 1));
540 return mask;
543 static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
544 struct diff_options *options UNUSED,
545 void *data)
547 struct wt_status *s = data;
548 int i;
550 for (i = 0; i < q->nr; i++) {
551 struct diff_filepair *p;
552 struct string_list_item *it;
553 struct wt_status_change_data *d;
555 p = q->queue[i];
556 it = string_list_insert(&s->change, p->two->path);
557 d = it->util;
558 if (!d) {
559 CALLOC_ARRAY(d, 1);
560 it->util = d;
562 if (!d->index_status)
563 d->index_status = p->status;
564 switch (p->status) {
565 case DIFF_STATUS_ADDED:
566 /* Leave {mode,oid}_head zero for an add. */
567 d->mode_index = p->two->mode;
568 oidcpy(&d->oid_index, &p->two->oid);
569 s->committable = 1;
570 break;
571 case DIFF_STATUS_DELETED:
572 d->mode_head = p->one->mode;
573 oidcpy(&d->oid_head, &p->one->oid);
574 s->committable = 1;
575 /* Leave {mode,oid}_index zero for a delete. */
576 break;
578 case DIFF_STATUS_COPIED:
579 case DIFF_STATUS_RENAMED:
580 if (d->rename_status)
581 BUG("multiple renames on the same target? how?");
582 d->rename_source = xstrdup(p->one->path);
583 d->rename_score = p->score * 100 / MAX_SCORE;
584 d->rename_status = p->status;
585 /* fallthru */
586 case DIFF_STATUS_MODIFIED:
587 case DIFF_STATUS_TYPE_CHANGED:
588 d->mode_head = p->one->mode;
589 d->mode_index = p->two->mode;
590 oidcpy(&d->oid_head, &p->one->oid);
591 oidcpy(&d->oid_index, &p->two->oid);
592 s->committable = 1;
593 break;
594 case DIFF_STATUS_UNMERGED:
595 d->stagemask = unmerged_mask(s->repo->index,
596 p->two->path);
598 * Don't bother setting {mode,oid}_{head,index} since the print
599 * code will output the stage values directly and not use the
600 * values in these fields.
602 break;
604 default:
605 BUG("unhandled diff-index status '%c'", p->status);
606 break;
611 static void wt_status_collect_changes_worktree(struct wt_status *s)
613 struct rev_info rev;
615 repo_init_revisions(s->repo, &rev, NULL);
616 setup_revisions(0, NULL, &rev, NULL);
617 rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
618 rev.diffopt.flags.dirty_submodules = 1;
619 rev.diffopt.ita_invisible_in_index = 1;
620 if (!s->show_untracked_files)
621 rev.diffopt.flags.ignore_untracked_in_submodules = 1;
622 if (s->ignore_submodule_arg) {
623 rev.diffopt.flags.override_submodule_config = 1;
624 handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
625 } else if (!rev.diffopt.flags.ignore_submodule_set &&
626 s->show_untracked_files != SHOW_NO_UNTRACKED_FILES)
627 handle_ignore_submodules_arg(&rev.diffopt, "none");
628 rev.diffopt.format_callback = wt_status_collect_changed_cb;
629 rev.diffopt.format_callback_data = s;
630 rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
631 rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
632 rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
633 copy_pathspec(&rev.prune_data, &s->pathspec);
634 run_diff_files(&rev, 0);
635 release_revisions(&rev);
638 static void wt_status_collect_changes_index(struct wt_status *s)
640 struct rev_info rev;
641 struct setup_revision_opt opt;
643 repo_init_revisions(s->repo, &rev, NULL);
644 memset(&opt, 0, sizeof(opt));
645 opt.def = s->is_initial ? empty_tree_oid_hex() : s->reference;
646 setup_revisions(0, NULL, &rev, &opt);
648 rev.diffopt.flags.override_submodule_config = 1;
649 rev.diffopt.ita_invisible_in_index = 1;
650 if (s->ignore_submodule_arg) {
651 handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
652 } else {
654 * Unless the user did explicitly request a submodule ignore
655 * mode by passing a command line option we do not ignore any
656 * changed submodule SHA-1s when comparing index and HEAD, no
657 * matter what is configured. Otherwise the user won't be
658 * shown any submodules manually added (and which are
659 * staged to be committed), which would be really confusing.
661 handle_ignore_submodules_arg(&rev.diffopt, "dirty");
664 rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
665 rev.diffopt.format_callback = wt_status_collect_updated_cb;
666 rev.diffopt.format_callback_data = s;
667 rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
668 rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
669 rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
672 * The `recursive` option must be enabled to allow the diff to recurse
673 * into subdirectories of sparse directory index entries. If it is not
674 * enabled, a subdirectory containing file(s) with changes is reported
675 * as "modified", rather than the modified files themselves.
677 rev.diffopt.flags.recursive = 1;
679 copy_pathspec(&rev.prune_data, &s->pathspec);
680 run_diff_index(&rev, DIFF_INDEX_CACHED);
681 release_revisions(&rev);
684 static int add_file_to_list(const struct object_id *oid,
685 struct strbuf *base, const char *path,
686 unsigned int mode, void *context)
688 struct string_list_item *it;
689 struct wt_status_change_data *d;
690 struct wt_status *s = context;
691 struct strbuf full_name = STRBUF_INIT;
693 if (S_ISDIR(mode))
694 return READ_TREE_RECURSIVE;
696 strbuf_add(&full_name, base->buf, base->len);
697 strbuf_addstr(&full_name, path);
698 it = string_list_insert(&s->change, full_name.buf);
699 d = it->util;
700 if (!d) {
701 CALLOC_ARRAY(d, 1);
702 it->util = d;
705 d->index_status = DIFF_STATUS_ADDED;
706 /* Leave {mode,oid}_head zero for adds. */
707 d->mode_index = mode;
708 oidcpy(&d->oid_index, oid);
709 s->committable = 1;
710 strbuf_release(&full_name);
711 return 0;
714 static void wt_status_collect_changes_initial(struct wt_status *s)
716 struct index_state *istate = s->repo->index;
717 int i;
719 for (i = 0; i < istate->cache_nr; i++) {
720 struct string_list_item *it;
721 struct wt_status_change_data *d;
722 const struct cache_entry *ce = istate->cache[i];
724 if (!ce_path_match(istate, ce, &s->pathspec, NULL))
725 continue;
726 if (ce_intent_to_add(ce))
727 continue;
728 if (S_ISSPARSEDIR(ce->ce_mode)) {
730 * This is a sparse directory entry, so we want to collect all
731 * of the added files within the tree. This requires recursively
732 * expanding the trees to find the elements that are new in this
733 * tree and marking them with DIFF_STATUS_ADDED.
735 struct strbuf base = STRBUF_INIT;
736 struct pathspec ps = { 0 };
737 struct tree *tree = lookup_tree(istate->repo, &ce->oid);
739 ps.recursive = 1;
740 ps.has_wildcard = 1;
741 ps.max_depth = -1;
743 strbuf_add(&base, ce->name, ce->ce_namelen);
744 read_tree_at(istate->repo, tree, &base, 0, &ps,
745 add_file_to_list, s);
746 continue;
749 it = string_list_insert(&s->change, ce->name);
750 d = it->util;
751 if (!d) {
752 CALLOC_ARRAY(d, 1);
753 it->util = d;
755 if (ce_stage(ce)) {
756 d->index_status = DIFF_STATUS_UNMERGED;
757 d->stagemask |= (1 << (ce_stage(ce) - 1));
759 * Don't bother setting {mode,oid}_{head,index} since the print
760 * code will output the stage values directly and not use the
761 * values in these fields.
763 s->committable = 1;
764 } else {
765 d->index_status = DIFF_STATUS_ADDED;
766 /* Leave {mode,oid}_head zero for adds. */
767 d->mode_index = ce->ce_mode;
768 oidcpy(&d->oid_index, &ce->oid);
769 s->committable = 1;
774 static void wt_status_collect_untracked(struct wt_status *s)
776 int i;
777 struct dir_struct dir = DIR_INIT;
778 uint64_t t_begin = getnanotime();
779 struct index_state *istate = s->repo->index;
781 if (!s->show_untracked_files)
782 return;
784 if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
785 dir.flags |=
786 DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
787 if (s->show_ignored_mode) {
788 dir.flags |= DIR_SHOW_IGNORED_TOO;
790 if (s->show_ignored_mode == SHOW_MATCHING_IGNORED)
791 dir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;
792 } else {
793 dir.untracked = istate->untracked;
796 setup_standard_excludes(&dir);
798 fill_directory(&dir, istate, &s->pathspec);
800 for (i = 0; i < dir.nr; i++) {
801 struct dir_entry *ent = dir.entries[i];
802 if (index_name_is_other(istate, ent->name, ent->len))
803 string_list_insert(&s->untracked, ent->name);
806 for (i = 0; i < dir.ignored_nr; i++) {
807 struct dir_entry *ent = dir.ignored[i];
808 if (index_name_is_other(istate, ent->name, ent->len))
809 string_list_insert(&s->ignored, ent->name);
812 dir_clear(&dir);
814 if (advice_enabled(ADVICE_STATUS_U_OPTION))
815 s->untracked_in_ms = (getnanotime() - t_begin) / 1000000;
818 static int has_unmerged(struct wt_status *s)
820 int i;
822 for (i = 0; i < s->change.nr; i++) {
823 struct wt_status_change_data *d;
824 d = s->change.items[i].util;
825 if (d->stagemask)
826 return 1;
828 return 0;
831 void wt_status_collect(struct wt_status *s)
833 trace2_region_enter("status", "worktrees", s->repo);
834 wt_status_collect_changes_worktree(s);
835 trace2_region_leave("status", "worktrees", s->repo);
837 if (s->is_initial) {
838 trace2_region_enter("status", "initial", s->repo);
839 wt_status_collect_changes_initial(s);
840 trace2_region_leave("status", "initial", s->repo);
841 } else {
842 trace2_region_enter("status", "index", s->repo);
843 wt_status_collect_changes_index(s);
844 trace2_region_leave("status", "index", s->repo);
847 trace2_region_enter("status", "untracked", s->repo);
848 wt_status_collect_untracked(s);
849 trace2_region_leave("status", "untracked", s->repo);
851 wt_status_get_state(s->repo, &s->state, s->branch && !strcmp(s->branch, "HEAD"));
852 if (s->state.merge_in_progress && !has_unmerged(s))
853 s->committable = 1;
856 void wt_status_collect_free_buffers(struct wt_status *s)
858 wt_status_state_free_buffers(&s->state);
861 void wt_status_state_free_buffers(struct wt_status_state *state)
863 FREE_AND_NULL(state->branch);
864 FREE_AND_NULL(state->onto);
865 FREE_AND_NULL(state->detached_from);
866 FREE_AND_NULL(state->bisecting_from);
869 static void wt_longstatus_print_unmerged(struct wt_status *s)
871 int shown_header = 0;
872 int i;
874 for (i = 0; i < s->change.nr; i++) {
875 struct wt_status_change_data *d;
876 struct string_list_item *it;
877 it = &(s->change.items[i]);
878 d = it->util;
879 if (!d->stagemask)
880 continue;
881 if (!shown_header) {
882 wt_longstatus_print_unmerged_header(s);
883 shown_header = 1;
885 wt_longstatus_print_unmerged_data(s, it);
887 if (shown_header)
888 wt_longstatus_print_trailer(s);
892 static void wt_longstatus_print_updated(struct wt_status *s)
894 int shown_header = 0;
895 int i;
897 for (i = 0; i < s->change.nr; i++) {
898 struct wt_status_change_data *d;
899 struct string_list_item *it;
900 it = &(s->change.items[i]);
901 d = it->util;
902 if (!d->index_status ||
903 d->index_status == DIFF_STATUS_UNMERGED)
904 continue;
905 if (!shown_header) {
906 wt_longstatus_print_cached_header(s);
907 shown_header = 1;
909 wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it);
911 if (shown_header)
912 wt_longstatus_print_trailer(s);
916 * -1 : has delete
917 * 0 : no change
918 * 1 : some change but no delete
920 static int wt_status_check_worktree_changes(struct wt_status *s,
921 int *dirty_submodules)
923 int i;
924 int changes = 0;
926 *dirty_submodules = 0;
928 for (i = 0; i < s->change.nr; i++) {
929 struct wt_status_change_data *d;
930 d = s->change.items[i].util;
931 if (!d->worktree_status ||
932 d->worktree_status == DIFF_STATUS_UNMERGED)
933 continue;
934 if (!changes)
935 changes = 1;
936 if (d->dirty_submodule)
937 *dirty_submodules = 1;
938 if (d->worktree_status == DIFF_STATUS_DELETED)
939 changes = -1;
941 return changes;
944 static void wt_longstatus_print_changed(struct wt_status *s)
946 int i, dirty_submodules;
947 int worktree_changes = wt_status_check_worktree_changes(s, &dirty_submodules);
949 if (!worktree_changes)
950 return;
952 wt_longstatus_print_dirty_header(s, worktree_changes < 0, dirty_submodules);
954 for (i = 0; i < s->change.nr; i++) {
955 struct wt_status_change_data *d;
956 struct string_list_item *it;
957 it = &(s->change.items[i]);
958 d = it->util;
959 if (!d->worktree_status ||
960 d->worktree_status == DIFF_STATUS_UNMERGED)
961 continue;
962 wt_longstatus_print_change_data(s, WT_STATUS_CHANGED, it);
964 wt_longstatus_print_trailer(s);
967 static int stash_count_refs(struct object_id *ooid UNUSED,
968 struct object_id *noid UNUSED,
969 const char *email UNUSED,
970 timestamp_t timestamp UNUSED, int tz UNUSED,
971 const char *message UNUSED, void *cb_data)
973 int *c = cb_data;
974 (*c)++;
975 return 0;
978 static int count_stash_entries(void)
980 int n = 0;
981 refs_for_each_reflog_ent(get_main_ref_store(the_repository),
982 "refs/stash", stash_count_refs, &n);
983 return n;
986 static void wt_longstatus_print_stash_summary(struct wt_status *s)
988 int stash_count = count_stash_entries();
990 if (stash_count > 0)
991 status_printf_ln(s, GIT_COLOR_NORMAL,
992 Q_("Your stash currently has %d entry",
993 "Your stash currently has %d entries", stash_count),
994 stash_count);
997 static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncommitted)
999 struct child_process sm_summary = CHILD_PROCESS_INIT;
1000 struct strbuf cmd_stdout = STRBUF_INIT;
1001 struct strbuf summary = STRBUF_INIT;
1002 char *summary_content;
1004 strvec_pushf(&sm_summary.env, "GIT_INDEX_FILE=%s", s->index_file);
1006 strvec_push(&sm_summary.args, "submodule");
1007 strvec_push(&sm_summary.args, "summary");
1008 strvec_push(&sm_summary.args, uncommitted ? "--files" : "--cached");
1009 strvec_push(&sm_summary.args, "--for-status");
1010 strvec_push(&sm_summary.args, "--summary-limit");
1011 strvec_pushf(&sm_summary.args, "%d", s->submodule_summary);
1012 if (!uncommitted)
1013 strvec_push(&sm_summary.args, s->amend ? "HEAD^" : "HEAD");
1015 sm_summary.git_cmd = 1;
1016 sm_summary.no_stdin = 1;
1018 capture_command(&sm_summary, &cmd_stdout, 1024);
1020 /* prepend header, only if there's an actual output */
1021 if (cmd_stdout.len) {
1022 if (uncommitted)
1023 strbuf_addstr(&summary, _("Submodules changed but not updated:"));
1024 else
1025 strbuf_addstr(&summary, _("Submodule changes to be committed:"));
1026 strbuf_addstr(&summary, "\n\n");
1028 strbuf_addbuf(&summary, &cmd_stdout);
1029 strbuf_release(&cmd_stdout);
1031 if (s->display_comment_prefix) {
1032 size_t len;
1033 summary_content = strbuf_detach(&summary, &len);
1034 strbuf_add_commented_lines(&summary, summary_content, len, comment_line_str);
1035 free(summary_content);
1038 fputs(summary.buf, s->fp);
1039 strbuf_release(&summary);
1042 static void wt_longstatus_print_other(struct wt_status *s,
1043 struct string_list *l,
1044 const char *what,
1045 const char *how)
1047 int i;
1048 struct strbuf buf = STRBUF_INIT;
1049 static struct string_list output = STRING_LIST_INIT_DUP;
1050 struct column_options copts;
1052 if (!l->nr)
1053 return;
1055 wt_longstatus_print_other_header(s, what, how);
1057 for (i = 0; i < l->nr; i++) {
1058 struct string_list_item *it;
1059 const char *path;
1060 it = &(l->items[i]);
1061 path = quote_path(it->string, s->prefix, &buf, 0);
1062 if (column_active(s->colopts)) {
1063 string_list_append(&output, path);
1064 continue;
1066 status_printf(s, color(WT_STATUS_HEADER, s), "\t");
1067 status_printf_more(s, color(WT_STATUS_UNTRACKED, s),
1068 "%s\n", path);
1071 strbuf_release(&buf);
1072 if (!column_active(s->colopts))
1073 goto conclude;
1075 strbuf_addf(&buf, "%s%s\t%s",
1076 color(WT_STATUS_HEADER, s),
1077 s->display_comment_prefix ? "#" : "",
1078 color(WT_STATUS_UNTRACKED, s));
1079 memset(&copts, 0, sizeof(copts));
1080 copts.padding = 1;
1081 copts.indent = buf.buf;
1082 if (want_color(s->use_color))
1083 copts.nl = GIT_COLOR_RESET "\n";
1084 print_columns(&output, s->colopts, &copts);
1085 string_list_clear(&output, 0);
1086 strbuf_release(&buf);
1087 conclude:
1088 status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
1091 size_t wt_status_locate_end(const char *s, size_t len)
1093 const char *p;
1094 struct strbuf pattern = STRBUF_INIT;
1096 strbuf_addf(&pattern, "\n%s %s", comment_line_str, cut_line);
1097 if (starts_with(s, pattern.buf + 1))
1098 len = 0;
1099 else if ((p = strstr(s, pattern.buf))) {
1100 size_t newlen = p - s + 1;
1101 if (newlen < len)
1102 len = newlen;
1104 strbuf_release(&pattern);
1105 return len;
1108 void wt_status_append_cut_line(struct strbuf *buf)
1110 const char *explanation = _("Do not modify or remove the line above.\nEverything below it will be ignored.");
1112 strbuf_commented_addf(buf, comment_line_str, "%s", cut_line);
1113 strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_str);
1116 void wt_status_add_cut_line(struct wt_status *s)
1118 struct strbuf buf = STRBUF_INIT;
1120 if (s->added_cut_line)
1121 return;
1122 s->added_cut_line = 1;
1123 wt_status_append_cut_line(&buf);
1124 fputs(buf.buf, s->fp);
1125 strbuf_release(&buf);
1128 static void wt_longstatus_print_verbose(struct wt_status *s)
1130 struct rev_info rev;
1131 struct setup_revision_opt opt;
1132 int dirty_submodules;
1133 const char *c = color(WT_STATUS_HEADER, s);
1135 repo_init_revisions(s->repo, &rev, NULL);
1136 rev.diffopt.flags.allow_textconv = 1;
1137 rev.diffopt.ita_invisible_in_index = 1;
1139 memset(&opt, 0, sizeof(opt));
1140 opt.def = s->is_initial ? empty_tree_oid_hex() : s->reference;
1141 setup_revisions(0, NULL, &rev, &opt);
1143 rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
1144 rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
1145 rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
1146 rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
1147 rev.diffopt.file = s->fp;
1148 rev.diffopt.close_file = 0;
1150 * If we're not going to stdout, then we definitely don't
1151 * want color, since we are going to the commit message
1152 * file (and even the "auto" setting won't work, since it
1153 * will have checked isatty on stdout). But we then do want
1154 * to insert the scissor line here to reliably remove the
1155 * diff before committing, if we didn't already include one
1156 * before.
1158 if (s->fp != stdout) {
1159 rev.diffopt.use_color = 0;
1160 wt_status_add_cut_line(s);
1162 if (s->verbose > 1 && s->committable) {
1163 /* print_updated() printed a header, so do we */
1164 if (s->fp != stdout)
1165 wt_longstatus_print_trailer(s);
1166 status_printf_ln(s, c, _("Changes to be committed:"));
1167 rev.diffopt.a_prefix = "c/";
1168 rev.diffopt.b_prefix = "i/";
1169 } /* else use prefix as per user config */
1170 run_diff_index(&rev, DIFF_INDEX_CACHED);
1171 if (s->verbose > 1 &&
1172 wt_status_check_worktree_changes(s, &dirty_submodules)) {
1173 status_printf_ln(s, c,
1174 "--------------------------------------------------");
1175 status_printf_ln(s, c, _("Changes not staged for commit:"));
1176 setup_work_tree();
1177 rev.diffopt.a_prefix = "i/";
1178 rev.diffopt.b_prefix = "w/";
1179 run_diff_files(&rev, 0);
1181 release_revisions(&rev);
1184 static void wt_longstatus_print_tracking(struct wt_status *s)
1186 struct strbuf sb = STRBUF_INIT;
1187 const char *cp, *ep, *branch_name;
1188 struct branch *branch;
1189 uint64_t t_begin = 0;
1191 assert(s->branch && !s->is_initial);
1192 if (!skip_prefix(s->branch, "refs/heads/", &branch_name))
1193 return;
1194 branch = branch_get(branch_name);
1196 t_begin = getnanotime();
1198 if (!format_tracking_info(branch, &sb, s->ahead_behind_flags,
1199 !s->commit_template))
1200 return;
1202 if (advice_enabled(ADVICE_STATUS_AHEAD_BEHIND_WARNING) &&
1203 s->ahead_behind_flags == AHEAD_BEHIND_FULL) {
1204 uint64_t t_delta_in_ms = (getnanotime() - t_begin) / 1000000;
1205 if (t_delta_in_ms > AB_DELAY_WARNING_IN_MS) {
1206 strbuf_addf(&sb, _("\n"
1207 "It took %.2f seconds to compute the branch ahead/behind values.\n"
1208 "You can use '--no-ahead-behind' to avoid this.\n"),
1209 t_delta_in_ms / 1000.0);
1213 for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
1214 color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
1215 "%s%s%.*s",
1216 s->display_comment_prefix ? comment_line_str : "",
1217 s->display_comment_prefix ? " " : "",
1218 (int)(ep - cp), cp);
1219 if (s->display_comment_prefix)
1220 color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%s",
1221 comment_line_str);
1222 else
1223 fputs("\n", s->fp);
1224 strbuf_release(&sb);
1227 static int uf_was_slow(struct wt_status *s)
1229 if (getenv("GIT_TEST_UF_DELAY_WARNING"))
1230 s->untracked_in_ms = 3250;
1231 return UF_DELAY_WARNING_IN_MS < s->untracked_in_ms;
1234 static void show_merge_in_progress(struct wt_status *s,
1235 const char *color)
1237 if (has_unmerged(s)) {
1238 status_printf_ln(s, color, _("You have unmerged paths."));
1239 if (s->hints) {
1240 status_printf_ln(s, color,
1241 _(" (fix conflicts and run \"git commit\")"));
1242 status_printf_ln(s, color,
1243 _(" (use \"git merge --abort\" to abort the merge)"));
1245 } else {
1246 status_printf_ln(s, color,
1247 _("All conflicts fixed but you are still merging."));
1248 if (s->hints)
1249 status_printf_ln(s, color,
1250 _(" (use \"git commit\" to conclude merge)"));
1252 wt_longstatus_print_trailer(s);
1255 static void show_am_in_progress(struct wt_status *s,
1256 const char *color)
1258 int am_empty_patch;
1260 status_printf_ln(s, color,
1261 _("You are in the middle of an am session."));
1262 if (s->state.am_empty_patch)
1263 status_printf_ln(s, color,
1264 _("The current patch is empty."));
1265 if (s->hints) {
1266 am_empty_patch = s->state.am_empty_patch;
1267 if (!am_empty_patch)
1268 status_printf_ln(s, color,
1269 _(" (fix conflicts and then run \"git am --continue\")"));
1270 status_printf_ln(s, color,
1271 _(" (use \"git am --skip\" to skip this patch)"));
1272 if (am_empty_patch)
1273 status_printf_ln(s, color,
1274 _(" (use \"git am --allow-empty\" to record this patch as an empty commit)"));
1275 status_printf_ln(s, color,
1276 _(" (use \"git am --abort\" to restore the original branch)"));
1278 wt_longstatus_print_trailer(s);
1281 static char *read_line_from_git_path(const char *filename)
1283 struct strbuf buf = STRBUF_INIT;
1284 FILE *fp = fopen_or_warn(git_path("%s", filename), "r");
1286 if (!fp) {
1287 strbuf_release(&buf);
1288 return NULL;
1290 strbuf_getline_lf(&buf, fp);
1291 if (!fclose(fp)) {
1292 return strbuf_detach(&buf, NULL);
1293 } else {
1294 strbuf_release(&buf);
1295 return NULL;
1299 static int split_commit_in_progress(struct wt_status *s)
1301 int split_in_progress = 0;
1302 struct object_id head_oid, orig_head_oid;
1303 char *rebase_amend, *rebase_orig_head;
1304 int head_flags, orig_head_flags;
1306 if ((!s->amend && !s->nowarn && !s->workdir_dirty) ||
1307 !s->branch || strcmp(s->branch, "HEAD"))
1308 return 0;
1310 if (refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
1311 &head_oid, &head_flags) ||
1312 refs_read_ref_full(get_main_ref_store(the_repository), "ORIG_HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
1313 &orig_head_oid, &orig_head_flags))
1314 return 0;
1315 if (head_flags & REF_ISSYMREF || orig_head_flags & REF_ISSYMREF)
1316 return 0;
1318 rebase_amend = read_line_from_git_path("rebase-merge/amend");
1319 rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
1321 if (!rebase_amend || !rebase_orig_head)
1322 ; /* fall through, no split in progress */
1323 else if (!strcmp(rebase_amend, rebase_orig_head))
1324 split_in_progress = !!strcmp(oid_to_hex(&head_oid), rebase_amend);
1325 else if (strcmp(oid_to_hex(&orig_head_oid), rebase_orig_head))
1326 split_in_progress = 1;
1328 free(rebase_amend);
1329 free(rebase_orig_head);
1331 return split_in_progress;
1335 * Turn
1336 * "pick d6a2f0303e897ec257dd0e0a39a5ccb709bc2047 some message"
1337 * into
1338 * "pick d6a2f03 some message"
1340 * The function assumes that the line does not contain useless spaces
1341 * before or after the command.
1343 static void abbrev_oid_in_line(struct strbuf *line)
1345 struct strbuf **split;
1346 int i;
1348 if (starts_with(line->buf, "exec ") ||
1349 starts_with(line->buf, "x ") ||
1350 starts_with(line->buf, "label ") ||
1351 starts_with(line->buf, "l "))
1352 return;
1354 split = strbuf_split_max(line, ' ', 3);
1355 if (split[0] && split[1]) {
1356 struct object_id oid;
1359 * strbuf_split_max left a space. Trim it and re-add
1360 * it after abbreviation.
1362 strbuf_trim(split[1]);
1363 if (!repo_get_oid(the_repository, split[1]->buf, &oid)) {
1364 strbuf_reset(split[1]);
1365 strbuf_add_unique_abbrev(split[1], &oid,
1366 DEFAULT_ABBREV);
1367 strbuf_addch(split[1], ' ');
1368 strbuf_reset(line);
1369 for (i = 0; split[i]; i++)
1370 strbuf_addbuf(line, split[i]);
1373 strbuf_list_free(split);
1376 static int read_rebase_todolist(const char *fname, struct string_list *lines)
1378 struct strbuf line = STRBUF_INIT;
1379 FILE *f = fopen(git_path("%s", fname), "r");
1381 if (!f) {
1382 if (errno == ENOENT)
1383 return -1;
1384 die_errno("Could not open file %s for reading",
1385 git_path("%s", fname));
1387 while (!strbuf_getline_lf(&line, f)) {
1388 if (starts_with(line.buf, comment_line_str))
1389 continue;
1390 strbuf_trim(&line);
1391 if (!line.len)
1392 continue;
1393 abbrev_oid_in_line(&line);
1394 string_list_append(lines, line.buf);
1396 fclose(f);
1397 strbuf_release(&line);
1398 return 0;
1401 static void show_rebase_information(struct wt_status *s,
1402 const char *color)
1404 if (s->state.rebase_interactive_in_progress) {
1405 int i;
1406 int nr_lines_to_show = 2;
1408 struct string_list have_done = STRING_LIST_INIT_DUP;
1409 struct string_list yet_to_do = STRING_LIST_INIT_DUP;
1411 read_rebase_todolist("rebase-merge/done", &have_done);
1412 if (read_rebase_todolist("rebase-merge/git-rebase-todo",
1413 &yet_to_do))
1414 status_printf_ln(s, color,
1415 _("git-rebase-todo is missing."));
1416 if (have_done.nr == 0)
1417 status_printf_ln(s, color, _("No commands done."));
1418 else {
1419 status_printf_ln(s, color,
1420 Q_("Last command done (%"PRIuMAX" command done):",
1421 "Last commands done (%"PRIuMAX" commands done):",
1422 have_done.nr),
1423 (uintmax_t)have_done.nr);
1424 for (i = (have_done.nr > nr_lines_to_show)
1425 ? have_done.nr - nr_lines_to_show : 0;
1426 i < have_done.nr;
1427 i++)
1428 status_printf_ln(s, color, " %s", have_done.items[i].string);
1429 if (have_done.nr > nr_lines_to_show && s->hints)
1430 status_printf_ln(s, color,
1431 _(" (see more in file %s)"), git_path("rebase-merge/done"));
1434 if (yet_to_do.nr == 0)
1435 status_printf_ln(s, color,
1436 _("No commands remaining."));
1437 else {
1438 status_printf_ln(s, color,
1439 Q_("Next command to do (%"PRIuMAX" remaining command):",
1440 "Next commands to do (%"PRIuMAX" remaining commands):",
1441 yet_to_do.nr),
1442 (uintmax_t)yet_to_do.nr);
1443 for (i = 0; i < nr_lines_to_show && i < yet_to_do.nr; i++)
1444 status_printf_ln(s, color, " %s", yet_to_do.items[i].string);
1445 if (s->hints)
1446 status_printf_ln(s, color,
1447 _(" (use \"git rebase --edit-todo\" to view and edit)"));
1449 string_list_clear(&yet_to_do, 0);
1450 string_list_clear(&have_done, 0);
1454 static void print_rebase_state(struct wt_status *s,
1455 const char *color)
1457 if (s->state.branch)
1458 status_printf_ln(s, color,
1459 _("You are currently rebasing branch '%s' on '%s'."),
1460 s->state.branch,
1461 s->state.onto);
1462 else
1463 status_printf_ln(s, color,
1464 _("You are currently rebasing."));
1467 static void show_rebase_in_progress(struct wt_status *s,
1468 const char *color)
1470 struct stat st;
1472 show_rebase_information(s, color);
1473 if (has_unmerged(s)) {
1474 print_rebase_state(s, color);
1475 if (s->hints) {
1476 status_printf_ln(s, color,
1477 _(" (fix conflicts and then run \"git rebase --continue\")"));
1478 status_printf_ln(s, color,
1479 _(" (use \"git rebase --skip\" to skip this patch)"));
1480 status_printf_ln(s, color,
1481 _(" (use \"git rebase --abort\" to check out the original branch)"));
1483 } else if (s->state.rebase_in_progress ||
1484 !stat(git_path_merge_msg(s->repo), &st)) {
1485 print_rebase_state(s, color);
1486 if (s->hints)
1487 status_printf_ln(s, color,
1488 _(" (all conflicts fixed: run \"git rebase --continue\")"));
1489 } else if (split_commit_in_progress(s)) {
1490 if (s->state.branch)
1491 status_printf_ln(s, color,
1492 _("You are currently splitting a commit while rebasing branch '%s' on '%s'."),
1493 s->state.branch,
1494 s->state.onto);
1495 else
1496 status_printf_ln(s, color,
1497 _("You are currently splitting a commit during a rebase."));
1498 if (s->hints)
1499 status_printf_ln(s, color,
1500 _(" (Once your working directory is clean, run \"git rebase --continue\")"));
1501 } else {
1502 if (s->state.branch)
1503 status_printf_ln(s, color,
1504 _("You are currently editing a commit while rebasing branch '%s' on '%s'."),
1505 s->state.branch,
1506 s->state.onto);
1507 else
1508 status_printf_ln(s, color,
1509 _("You are currently editing a commit during a rebase."));
1510 if (s->hints && !s->amend) {
1511 status_printf_ln(s, color,
1512 _(" (use \"git commit --amend\" to amend the current commit)"));
1513 status_printf_ln(s, color,
1514 _(" (use \"git rebase --continue\" once you are satisfied with your changes)"));
1517 wt_longstatus_print_trailer(s);
1520 static void show_cherry_pick_in_progress(struct wt_status *s,
1521 const char *color)
1523 if (is_null_oid(&s->state.cherry_pick_head_oid))
1524 status_printf_ln(s, color,
1525 _("Cherry-pick currently in progress."));
1526 else
1527 status_printf_ln(s, color,
1528 _("You are currently cherry-picking commit %s."),
1529 repo_find_unique_abbrev(the_repository, &s->state.cherry_pick_head_oid,
1530 DEFAULT_ABBREV));
1532 if (s->hints) {
1533 if (has_unmerged(s))
1534 status_printf_ln(s, color,
1535 _(" (fix conflicts and run \"git cherry-pick --continue\")"));
1536 else if (is_null_oid(&s->state.cherry_pick_head_oid))
1537 status_printf_ln(s, color,
1538 _(" (run \"git cherry-pick --continue\" to continue)"));
1539 else
1540 status_printf_ln(s, color,
1541 _(" (all conflicts fixed: run \"git cherry-pick --continue\")"));
1542 status_printf_ln(s, color,
1543 _(" (use \"git cherry-pick --skip\" to skip this patch)"));
1544 status_printf_ln(s, color,
1545 _(" (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"));
1547 wt_longstatus_print_trailer(s);
1550 static void show_revert_in_progress(struct wt_status *s,
1551 const char *color)
1553 if (is_null_oid(&s->state.revert_head_oid))
1554 status_printf_ln(s, color,
1555 _("Revert currently in progress."));
1556 else
1557 status_printf_ln(s, color,
1558 _("You are currently reverting commit %s."),
1559 repo_find_unique_abbrev(the_repository, &s->state.revert_head_oid,
1560 DEFAULT_ABBREV));
1561 if (s->hints) {
1562 if (has_unmerged(s))
1563 status_printf_ln(s, color,
1564 _(" (fix conflicts and run \"git revert --continue\")"));
1565 else if (is_null_oid(&s->state.revert_head_oid))
1566 status_printf_ln(s, color,
1567 _(" (run \"git revert --continue\" to continue)"));
1568 else
1569 status_printf_ln(s, color,
1570 _(" (all conflicts fixed: run \"git revert --continue\")"));
1571 status_printf_ln(s, color,
1572 _(" (use \"git revert --skip\" to skip this patch)"));
1573 status_printf_ln(s, color,
1574 _(" (use \"git revert --abort\" to cancel the revert operation)"));
1576 wt_longstatus_print_trailer(s);
1579 static void show_bisect_in_progress(struct wt_status *s,
1580 const char *color)
1582 if (s->state.bisecting_from)
1583 status_printf_ln(s, color,
1584 _("You are currently bisecting, started from branch '%s'."),
1585 s->state.bisecting_from);
1586 else
1587 status_printf_ln(s, color,
1588 _("You are currently bisecting."));
1589 if (s->hints)
1590 status_printf_ln(s, color,
1591 _(" (use \"git bisect reset\" to get back to the original branch)"));
1592 wt_longstatus_print_trailer(s);
1595 static void show_sparse_checkout_in_use(struct wt_status *s,
1596 const char *color)
1598 if (s->state.sparse_checkout_percentage == SPARSE_CHECKOUT_DISABLED)
1599 return;
1601 if (s->state.sparse_checkout_percentage == SPARSE_CHECKOUT_SPARSE_INDEX)
1602 status_printf_ln(s, color, _("You are in a sparse checkout."));
1603 else
1604 status_printf_ln(s, color,
1605 _("You are in a sparse checkout with %d%% of tracked files present."),
1606 s->state.sparse_checkout_percentage);
1607 wt_longstatus_print_trailer(s);
1611 * Extract branch information from rebase/bisect
1613 static char *get_branch(const struct worktree *wt, const char *path)
1615 struct strbuf sb = STRBUF_INIT;
1616 struct object_id oid;
1617 const char *branch_name;
1619 if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0)
1620 goto got_nothing;
1622 while (sb.len && sb.buf[sb.len - 1] == '\n')
1623 strbuf_setlen(&sb, sb.len - 1);
1624 if (!sb.len)
1625 goto got_nothing;
1626 if (skip_prefix(sb.buf, "refs/heads/", &branch_name))
1627 strbuf_remove(&sb, 0, branch_name - sb.buf);
1628 else if (starts_with(sb.buf, "refs/"))
1630 else if (!get_oid_hex(sb.buf, &oid)) {
1631 strbuf_reset(&sb);
1632 strbuf_add_unique_abbrev(&sb, &oid, DEFAULT_ABBREV);
1633 } else if (!strcmp(sb.buf, "detached HEAD")) /* rebase */
1634 goto got_nothing;
1635 else /* bisect */
1637 return strbuf_detach(&sb, NULL);
1639 got_nothing:
1640 strbuf_release(&sb);
1641 return NULL;
1644 struct grab_1st_switch_cbdata {
1645 struct strbuf buf;
1646 struct object_id noid;
1649 static int grab_1st_switch(struct object_id *ooid UNUSED,
1650 struct object_id *noid,
1651 const char *email UNUSED,
1652 timestamp_t timestamp UNUSED, int tz UNUSED,
1653 const char *message, void *cb_data)
1655 struct grab_1st_switch_cbdata *cb = cb_data;
1656 const char *target = NULL, *end;
1658 if (!skip_prefix(message, "checkout: moving from ", &message))
1659 return 0;
1660 target = strstr(message, " to ");
1661 if (!target)
1662 return 0;
1663 target += strlen(" to ");
1664 strbuf_reset(&cb->buf);
1665 oidcpy(&cb->noid, noid);
1666 end = strchrnul(target, '\n');
1667 strbuf_add(&cb->buf, target, end - target);
1668 if (!strcmp(cb->buf.buf, "HEAD")) {
1669 /* HEAD is relative. Resolve it to the right reflog entry. */
1670 strbuf_reset(&cb->buf);
1671 strbuf_add_unique_abbrev(&cb->buf, noid, DEFAULT_ABBREV);
1673 return 1;
1676 static void wt_status_get_detached_from(struct repository *r,
1677 struct wt_status_state *state)
1679 struct grab_1st_switch_cbdata cb;
1680 struct commit *commit;
1681 struct object_id oid;
1682 char *ref = NULL;
1684 strbuf_init(&cb.buf, 0);
1685 if (refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository), "HEAD", grab_1st_switch, &cb) <= 0) {
1686 strbuf_release(&cb.buf);
1687 return;
1690 if (repo_dwim_ref(r, cb.buf.buf, cb.buf.len, &oid, &ref,
1691 1) == 1 &&
1692 /* oid is a commit? match without further lookup */
1693 (oideq(&cb.noid, &oid) ||
1694 /* perhaps oid is a tag, try to dereference to a commit */
1695 ((commit = lookup_commit_reference_gently(r, &oid, 1)) != NULL &&
1696 oideq(&cb.noid, &commit->object.oid)))) {
1697 const char *from = ref;
1698 if (!skip_prefix(from, "refs/tags/", &from))
1699 skip_prefix(from, "refs/remotes/", &from);
1700 state->detached_from = xstrdup(from);
1701 } else
1702 state->detached_from =
1703 xstrdup(repo_find_unique_abbrev(r, &cb.noid, DEFAULT_ABBREV));
1704 oidcpy(&state->detached_oid, &cb.noid);
1705 state->detached_at = !repo_get_oid(r, "HEAD", &oid) &&
1706 oideq(&oid, &state->detached_oid);
1708 free(ref);
1709 strbuf_release(&cb.buf);
1712 int wt_status_check_rebase(const struct worktree *wt,
1713 struct wt_status_state *state)
1715 struct stat st;
1717 if (!stat(worktree_git_path(wt, "rebase-apply"), &st)) {
1718 if (!stat(worktree_git_path(wt, "rebase-apply/applying"), &st)) {
1719 state->am_in_progress = 1;
1720 if (!stat(worktree_git_path(wt, "rebase-apply/patch"), &st) && !st.st_size)
1721 state->am_empty_patch = 1;
1722 } else {
1723 state->rebase_in_progress = 1;
1724 state->branch = get_branch(wt, "rebase-apply/head-name");
1725 state->onto = get_branch(wt, "rebase-apply/onto");
1727 } else if (!stat(worktree_git_path(wt, "rebase-merge"), &st)) {
1728 if (!stat(worktree_git_path(wt, "rebase-merge/interactive"), &st))
1729 state->rebase_interactive_in_progress = 1;
1730 else
1731 state->rebase_in_progress = 1;
1732 state->branch = get_branch(wt, "rebase-merge/head-name");
1733 state->onto = get_branch(wt, "rebase-merge/onto");
1734 } else
1735 return 0;
1736 return 1;
1739 int wt_status_check_bisect(const struct worktree *wt,
1740 struct wt_status_state *state)
1742 struct stat st;
1744 if (!stat(worktree_git_path(wt, "BISECT_LOG"), &st)) {
1745 state->bisect_in_progress = 1;
1746 state->bisecting_from = get_branch(wt, "BISECT_START");
1747 return 1;
1749 return 0;
1752 static void wt_status_check_sparse_checkout(struct repository *r,
1753 struct wt_status_state *state)
1755 int skip_worktree = 0;
1756 int i;
1758 if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
1760 * Don't compute percentage of checked out files if we
1761 * aren't in a sparse checkout or would get division by 0.
1763 state->sparse_checkout_percentage = SPARSE_CHECKOUT_DISABLED;
1764 return;
1767 if (r->index->sparse_index) {
1768 state->sparse_checkout_percentage = SPARSE_CHECKOUT_SPARSE_INDEX;
1769 return;
1772 for (i = 0; i < r->index->cache_nr; i++) {
1773 struct cache_entry *ce = r->index->cache[i];
1774 if (ce_skip_worktree(ce))
1775 skip_worktree++;
1778 state->sparse_checkout_percentage =
1779 100 - (100 * skip_worktree)/r->index->cache_nr;
1782 void wt_status_get_state(struct repository *r,
1783 struct wt_status_state *state,
1784 int get_detached_from)
1786 struct stat st;
1787 struct object_id oid;
1788 enum replay_action action;
1790 if (!stat(git_path_merge_head(r), &st)) {
1791 wt_status_check_rebase(NULL, state);
1792 state->merge_in_progress = 1;
1793 } else if (wt_status_check_rebase(NULL, state)) {
1794 ; /* all set */
1795 } else if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
1796 !repo_get_oid(r, "CHERRY_PICK_HEAD", &oid)) {
1797 state->cherry_pick_in_progress = 1;
1798 oidcpy(&state->cherry_pick_head_oid, &oid);
1800 wt_status_check_bisect(NULL, state);
1801 if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") &&
1802 !repo_get_oid(r, "REVERT_HEAD", &oid)) {
1803 state->revert_in_progress = 1;
1804 oidcpy(&state->revert_head_oid, &oid);
1806 if (!sequencer_get_last_command(r, &action)) {
1807 if (action == REPLAY_PICK && !state->cherry_pick_in_progress) {
1808 state->cherry_pick_in_progress = 1;
1809 oidcpy(&state->cherry_pick_head_oid, null_oid());
1810 } else if (action == REPLAY_REVERT && !state->revert_in_progress) {
1811 state->revert_in_progress = 1;
1812 oidcpy(&state->revert_head_oid, null_oid());
1815 if (get_detached_from)
1816 wt_status_get_detached_from(r, state);
1817 wt_status_check_sparse_checkout(r, state);
1820 static void wt_longstatus_print_state(struct wt_status *s)
1822 const char *state_color = color(WT_STATUS_HEADER, s);
1823 struct wt_status_state *state = &s->state;
1825 if (state->merge_in_progress) {
1826 if (state->rebase_interactive_in_progress) {
1827 show_rebase_information(s, state_color);
1828 fputs("\n", s->fp);
1830 show_merge_in_progress(s, state_color);
1831 } else if (state->am_in_progress)
1832 show_am_in_progress(s, state_color);
1833 else if (state->rebase_in_progress || state->rebase_interactive_in_progress)
1834 show_rebase_in_progress(s, state_color);
1835 else if (state->cherry_pick_in_progress)
1836 show_cherry_pick_in_progress(s, state_color);
1837 else if (state->revert_in_progress)
1838 show_revert_in_progress(s, state_color);
1839 if (state->bisect_in_progress)
1840 show_bisect_in_progress(s, state_color);
1842 if (state->sparse_checkout_percentage != SPARSE_CHECKOUT_DISABLED)
1843 show_sparse_checkout_in_use(s, state_color);
1846 static void wt_longstatus_print(struct wt_status *s)
1848 const char *branch_color = color(WT_STATUS_ONBRANCH, s);
1849 const char *branch_status_color = color(WT_STATUS_HEADER, s);
1850 enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(s->repo);
1852 if (s->branch) {
1853 const char *on_what = _("On branch ");
1854 const char *branch_name = s->branch;
1855 if (!strcmp(branch_name, "HEAD")) {
1856 branch_status_color = color(WT_STATUS_NOBRANCH, s);
1857 if (s->state.rebase_in_progress ||
1858 s->state.rebase_interactive_in_progress) {
1859 if (s->state.rebase_interactive_in_progress)
1860 on_what = _("interactive rebase in progress; onto ");
1861 else
1862 on_what = _("rebase in progress; onto ");
1863 branch_name = s->state.onto;
1864 } else if (s->state.detached_from) {
1865 branch_name = s->state.detached_from;
1866 if (s->state.detached_at)
1867 on_what = _("HEAD detached at ");
1868 else
1869 on_what = _("HEAD detached from ");
1870 } else {
1871 branch_name = "";
1872 on_what = _("Not currently on any branch.");
1874 } else
1875 skip_prefix(branch_name, "refs/heads/", &branch_name);
1876 status_printf(s, color(WT_STATUS_HEADER, s), "%s", "");
1877 status_printf_more(s, branch_status_color, "%s", on_what);
1878 status_printf_more(s, branch_color, "%s\n", branch_name);
1879 if (!s->is_initial)
1880 wt_longstatus_print_tracking(s);
1883 wt_longstatus_print_state(s);
1885 if (s->is_initial) {
1886 status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
1887 status_printf_ln(s, color(WT_STATUS_HEADER, s),
1888 s->commit_template
1889 ? _("Initial commit")
1890 : _("No commits yet"));
1891 status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
1894 wt_longstatus_print_updated(s);
1895 wt_longstatus_print_unmerged(s);
1896 wt_longstatus_print_changed(s);
1897 if (s->submodule_summary &&
1898 (!s->ignore_submodule_arg ||
1899 strcmp(s->ignore_submodule_arg, "all"))) {
1900 wt_longstatus_print_submodule_summary(s, 0); /* staged */
1901 wt_longstatus_print_submodule_summary(s, 1); /* unstaged */
1903 if (s->show_untracked_files) {
1904 wt_longstatus_print_other(s, &s->untracked, _("Untracked files"), "add");
1905 if (s->show_ignored_mode)
1906 wt_longstatus_print_other(s, &s->ignored, _("Ignored files"), "add -f");
1907 if (advice_enabled(ADVICE_STATUS_U_OPTION) && uf_was_slow(s)) {
1908 status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
1909 if (fsm_mode > FSMONITOR_MODE_DISABLED) {
1910 status_printf_ln(s, GIT_COLOR_NORMAL,
1911 _("It took %.2f seconds to enumerate untracked files,\n"
1912 "but the results were cached, and subsequent runs may be faster."),
1913 s->untracked_in_ms / 1000.0);
1914 } else {
1915 status_printf_ln(s, GIT_COLOR_NORMAL,
1916 _("It took %.2f seconds to enumerate untracked files."),
1917 s->untracked_in_ms / 1000.0);
1919 status_printf_ln(s, GIT_COLOR_NORMAL,
1920 _("See 'git help status' for information on how to improve this."));
1921 status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
1923 } else if (s->committable)
1924 status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
1925 s->hints
1926 ? _(" (use -u option to show untracked files)") : "");
1928 if (s->verbose)
1929 wt_longstatus_print_verbose(s);
1930 if (!s->committable) {
1931 if (s->amend)
1932 status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes"));
1933 else if (s->nowarn)
1934 ; /* nothing */
1935 else if (s->workdir_dirty) {
1936 if (s->hints)
1937 fprintf(s->fp, _("no changes added to commit "
1938 "(use \"git add\" and/or "
1939 "\"git commit -a\")\n"));
1940 else
1941 fprintf(s->fp, _("no changes added to "
1942 "commit\n"));
1943 } else if (s->untracked.nr) {
1944 if (s->hints)
1945 fprintf(s->fp, _("nothing added to commit but "
1946 "untracked files present (use "
1947 "\"git add\" to track)\n"));
1948 else
1949 fprintf(s->fp, _("nothing added to commit but "
1950 "untracked files present\n"));
1951 } else if (s->is_initial) {
1952 if (s->hints)
1953 fprintf(s->fp, _("nothing to commit (create/"
1954 "copy files and use \"git "
1955 "add\" to track)\n"));
1956 else
1957 fprintf(s->fp, _("nothing to commit\n"));
1958 } else if (!s->show_untracked_files) {
1959 if (s->hints)
1960 fprintf(s->fp, _("nothing to commit (use -u to "
1961 "show untracked files)\n"));
1962 else
1963 fprintf(s->fp, _("nothing to commit\n"));
1964 } else
1965 fprintf(s->fp, _("nothing to commit, working tree "
1966 "clean\n"));
1968 if(s->show_stash)
1969 wt_longstatus_print_stash_summary(s);
1972 static void wt_shortstatus_unmerged(struct string_list_item *it,
1973 struct wt_status *s)
1975 struct wt_status_change_data *d = it->util;
1976 const char *how = "??";
1978 switch (d->stagemask) {
1979 case 1: how = "DD"; break; /* both deleted */
1980 case 2: how = "AU"; break; /* added by us */
1981 case 3: how = "UD"; break; /* deleted by them */
1982 case 4: how = "UA"; break; /* added by them */
1983 case 5: how = "DU"; break; /* deleted by us */
1984 case 6: how = "AA"; break; /* both added */
1985 case 7: how = "UU"; break; /* both modified */
1987 color_fprintf(s->fp, color(WT_STATUS_UNMERGED, s), "%s", how);
1988 if (s->null_termination) {
1989 fprintf(s->fp, " %s%c", it->string, 0);
1990 } else {
1991 struct strbuf onebuf = STRBUF_INIT;
1992 const char *one;
1993 one = quote_path(it->string, s->prefix, &onebuf, QUOTE_PATH_QUOTE_SP);
1994 fprintf(s->fp, " %s\n", one);
1995 strbuf_release(&onebuf);
1999 static void wt_shortstatus_status(struct string_list_item *it,
2000 struct wt_status *s)
2002 struct wt_status_change_data *d = it->util;
2004 if (d->index_status)
2005 color_fprintf(s->fp, color(WT_STATUS_UPDATED, s), "%c", d->index_status);
2006 else
2007 fputc(' ', s->fp);
2008 if (d->worktree_status)
2009 color_fprintf(s->fp, color(WT_STATUS_CHANGED, s), "%c", d->worktree_status);
2010 else
2011 fputc(' ', s->fp);
2012 fputc(' ', s->fp);
2013 if (s->null_termination) {
2014 fprintf(s->fp, "%s%c", it->string, 0);
2015 if (d->rename_source)
2016 fprintf(s->fp, "%s%c", d->rename_source, 0);
2017 } else {
2018 struct strbuf onebuf = STRBUF_INIT;
2019 const char *one;
2021 if (d->rename_source) {
2022 one = quote_path(d->rename_source, s->prefix, &onebuf,
2023 QUOTE_PATH_QUOTE_SP);
2024 fprintf(s->fp, "%s -> ", one);
2025 strbuf_release(&onebuf);
2027 one = quote_path(it->string, s->prefix, &onebuf, QUOTE_PATH_QUOTE_SP);
2028 fprintf(s->fp, "%s\n", one);
2029 strbuf_release(&onebuf);
2033 static void wt_shortstatus_other(struct string_list_item *it,
2034 struct wt_status *s, const char *sign)
2036 if (s->null_termination) {
2037 fprintf(s->fp, "%s %s%c", sign, it->string, 0);
2038 } else {
2039 struct strbuf onebuf = STRBUF_INIT;
2040 const char *one;
2041 one = quote_path(it->string, s->prefix, &onebuf, QUOTE_PATH_QUOTE_SP);
2042 color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign);
2043 fprintf(s->fp, " %s\n", one);
2044 strbuf_release(&onebuf);
2048 static void wt_shortstatus_print_tracking(struct wt_status *s)
2050 struct branch *branch;
2051 const char *header_color = color(WT_STATUS_HEADER, s);
2052 const char *branch_color_local = color(WT_STATUS_LOCAL_BRANCH, s);
2053 const char *branch_color_remote = color(WT_STATUS_REMOTE_BRANCH, s);
2055 const char *base;
2056 char *short_base;
2057 const char *branch_name;
2058 int num_ours, num_theirs, sti;
2059 int upstream_is_gone = 0;
2061 color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## ");
2063 if (!s->branch)
2064 return;
2065 branch_name = s->branch;
2067 #define LABEL(string) (s->no_gettext ? (string) : _(string))
2069 if (s->is_initial)
2070 color_fprintf(s->fp, header_color, LABEL(N_("No commits yet on ")));
2072 if (!strcmp(s->branch, "HEAD")) {
2073 color_fprintf(s->fp, color(WT_STATUS_NOBRANCH, s), "%s",
2074 LABEL(N_("HEAD (no branch)")));
2075 goto conclude;
2078 skip_prefix(branch_name, "refs/heads/", &branch_name);
2080 branch = branch_get(branch_name);
2082 color_fprintf(s->fp, branch_color_local, "%s", branch_name);
2084 sti = stat_tracking_info(branch, &num_ours, &num_theirs, &base,
2085 0, s->ahead_behind_flags);
2086 if (sti < 0) {
2087 if (!base)
2088 goto conclude;
2090 upstream_is_gone = 1;
2093 short_base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
2094 base, 0);
2095 color_fprintf(s->fp, header_color, "...");
2096 color_fprintf(s->fp, branch_color_remote, "%s", short_base);
2097 free(short_base);
2099 if (!upstream_is_gone && !sti)
2100 goto conclude;
2102 color_fprintf(s->fp, header_color, " [");
2103 if (upstream_is_gone) {
2104 color_fprintf(s->fp, header_color, LABEL(N_("gone")));
2105 } else if (s->ahead_behind_flags == AHEAD_BEHIND_QUICK) {
2106 color_fprintf(s->fp, header_color, LABEL(N_("different")));
2107 } else if (!num_ours) {
2108 color_fprintf(s->fp, header_color, LABEL(N_("behind ")));
2109 color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
2110 } else if (!num_theirs) {
2111 color_fprintf(s->fp, header_color, LABEL(N_("ahead ")));
2112 color_fprintf(s->fp, branch_color_local, "%d", num_ours);
2113 } else {
2114 color_fprintf(s->fp, header_color, LABEL(N_("ahead ")));
2115 color_fprintf(s->fp, branch_color_local, "%d", num_ours);
2116 color_fprintf(s->fp, header_color, ", %s", LABEL(N_("behind ")));
2117 color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
2120 color_fprintf(s->fp, header_color, "]");
2121 conclude:
2122 fputc(s->null_termination ? '\0' : '\n', s->fp);
2125 static void wt_shortstatus_print(struct wt_status *s)
2127 struct string_list_item *it;
2129 if (s->show_branch)
2130 wt_shortstatus_print_tracking(s);
2132 for_each_string_list_item(it, &s->change) {
2133 struct wt_status_change_data *d = it->util;
2135 if (d->stagemask)
2136 wt_shortstatus_unmerged(it, s);
2137 else
2138 wt_shortstatus_status(it, s);
2140 for_each_string_list_item(it, &s->untracked)
2141 wt_shortstatus_other(it, s, "??");
2143 for_each_string_list_item(it, &s->ignored)
2144 wt_shortstatus_other(it, s, "!!");
2147 static void wt_porcelain_print(struct wt_status *s)
2149 s->use_color = 0;
2150 s->relative_paths = 0;
2151 s->prefix = NULL;
2152 s->no_gettext = 1;
2153 wt_shortstatus_print(s);
2157 * Print branch information for porcelain v2 output. These lines
2158 * are printed when the '--branch' parameter is given.
2160 * # branch.oid <commit><eol>
2161 * # branch.head <head><eol>
2162 * [# branch.upstream <upstream><eol>
2163 * [# branch.ab +<ahead> -<behind><eol>]]
2165 * <commit> ::= the current commit hash or the literal
2166 * "(initial)" to indicate an initialized repo
2167 * with no commits.
2169 * <head> ::= <branch_name> the current branch name or
2170 * "(detached)" literal when detached head or
2171 * "(unknown)" when something is wrong.
2173 * <upstream> ::= the upstream branch name, when set.
2175 * <ahead> ::= integer ahead value or '?'.
2177 * <behind> ::= integer behind value or '?'.
2179 * The end-of-line is defined by the -z flag.
2181 * <eol> ::= NUL when -z,
2182 * LF when NOT -z.
2184 * When an upstream is set and present, the 'branch.ab' line will
2185 * be printed with the ahead/behind counts for the branch and the
2186 * upstream. When AHEAD_BEHIND_QUICK is requested and the branches
2187 * are different, '?' will be substituted for the actual count.
2189 static void wt_porcelain_v2_print_tracking(struct wt_status *s)
2191 struct branch *branch;
2192 const char *base;
2193 const char *branch_name;
2194 int ab_info, nr_ahead, nr_behind;
2195 char eol = s->null_termination ? '\0' : '\n';
2197 fprintf(s->fp, "# branch.oid %s%c",
2198 (s->is_initial ? "(initial)" : oid_to_hex(&s->oid_commit)),
2199 eol);
2201 if (!s->branch)
2202 fprintf(s->fp, "# branch.head %s%c", "(unknown)", eol);
2203 else {
2204 if (!strcmp(s->branch, "HEAD")) {
2205 fprintf(s->fp, "# branch.head %s%c", "(detached)", eol);
2207 if (s->state.rebase_in_progress ||
2208 s->state.rebase_interactive_in_progress)
2209 branch_name = s->state.onto;
2210 else if (s->state.detached_from)
2211 branch_name = s->state.detached_from;
2212 else
2213 branch_name = "";
2214 } else {
2215 branch_name = NULL;
2216 skip_prefix(s->branch, "refs/heads/", &branch_name);
2218 fprintf(s->fp, "# branch.head %s%c", branch_name, eol);
2221 /* Lookup stats on the upstream tracking branch, if set. */
2222 branch = branch_get(branch_name);
2223 base = NULL;
2224 ab_info = stat_tracking_info(branch, &nr_ahead, &nr_behind,
2225 &base, 0, s->ahead_behind_flags);
2226 if (base) {
2227 base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
2228 base, 0);
2229 fprintf(s->fp, "# branch.upstream %s%c", base, eol);
2230 free((char *)base);
2232 if (ab_info > 0) {
2233 /* different */
2234 if (nr_ahead || nr_behind)
2235 fprintf(s->fp, "# branch.ab +%d -%d%c",
2236 nr_ahead, nr_behind, eol);
2237 else
2238 fprintf(s->fp, "# branch.ab +? -?%c",
2239 eol);
2240 } else if (!ab_info) {
2241 /* same */
2242 fprintf(s->fp, "# branch.ab +0 -0%c", eol);
2249 * Print the stash count in a porcelain-friendly format
2251 static void wt_porcelain_v2_print_stash(struct wt_status *s)
2253 int stash_count = count_stash_entries();
2254 char eol = s->null_termination ? '\0' : '\n';
2256 if (stash_count > 0)
2257 fprintf(s->fp, "# stash %d%c", stash_count, eol);
2261 * Convert various submodule status values into a
2262 * fixed-length string of characters in the buffer provided.
2264 static void wt_porcelain_v2_submodule_state(
2265 struct wt_status_change_data *d,
2266 char sub[5])
2268 if (S_ISGITLINK(d->mode_head) ||
2269 S_ISGITLINK(d->mode_index) ||
2270 S_ISGITLINK(d->mode_worktree)) {
2271 sub[0] = 'S';
2272 sub[1] = d->new_submodule_commits ? 'C' : '.';
2273 sub[2] = (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED) ? 'M' : '.';
2274 sub[3] = (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) ? 'U' : '.';
2275 } else {
2276 sub[0] = 'N';
2277 sub[1] = '.';
2278 sub[2] = '.';
2279 sub[3] = '.';
2281 sub[4] = 0;
2285 * Fix-up changed entries before we print them.
2287 static void wt_porcelain_v2_fix_up_changed(struct string_list_item *it)
2289 struct wt_status_change_data *d = it->util;
2291 if (!d->index_status) {
2293 * This entry is unchanged in the index (relative to the head).
2294 * Therefore, the collect_updated_cb was never called for this
2295 * entry (during the head-vs-index scan) and so the head column
2296 * fields were never set.
2298 * We must have data for the index column (from the
2299 * index-vs-worktree scan (otherwise, this entry should not be
2300 * in the list of changes)).
2302 * Copy index column fields to the head column, so that our
2303 * output looks complete.
2305 assert(d->mode_head == 0);
2306 d->mode_head = d->mode_index;
2307 oidcpy(&d->oid_head, &d->oid_index);
2310 if (!d->worktree_status) {
2312 * This entry is unchanged in the worktree (relative to the index).
2313 * Therefore, the collect_changed_cb was never called for this entry
2314 * (during the index-vs-worktree scan) and so the worktree column
2315 * fields were never set.
2317 * We must have data for the index column (from the head-vs-index
2318 * scan).
2320 * Copy the index column fields to the worktree column so that
2321 * our output looks complete.
2323 * Note that we only have a mode field in the worktree column
2324 * because the scan code tries really hard to not have to compute it.
2326 assert(d->mode_worktree == 0);
2327 d->mode_worktree = d->mode_index;
2332 * Print porcelain v2 info for tracked entries with changes.
2334 static void wt_porcelain_v2_print_changed_entry(
2335 struct string_list_item *it,
2336 struct wt_status *s)
2338 struct wt_status_change_data *d = it->util;
2339 struct strbuf buf = STRBUF_INIT;
2340 struct strbuf buf_from = STRBUF_INIT;
2341 const char *path = NULL;
2342 const char *path_from = NULL;
2343 char key[3];
2344 char submodule_token[5];
2345 char sep_char, eol_char;
2347 wt_porcelain_v2_fix_up_changed(it);
2348 wt_porcelain_v2_submodule_state(d, submodule_token);
2350 key[0] = d->index_status ? d->index_status : '.';
2351 key[1] = d->worktree_status ? d->worktree_status : '.';
2352 key[2] = 0;
2354 if (s->null_termination) {
2356 * In -z mode, we DO NOT C-quote pathnames. Current path is ALWAYS first.
2357 * A single NUL character separates them.
2359 sep_char = '\0';
2360 eol_char = '\0';
2361 path = it->string;
2362 path_from = d->rename_source;
2363 } else {
2365 * Path(s) are C-quoted if necessary. Current path is ALWAYS first.
2366 * The source path is only present when necessary.
2367 * A single TAB separates them (because paths can contain spaces
2368 * which are not escaped and C-quoting does escape TAB characters).
2370 sep_char = '\t';
2371 eol_char = '\n';
2372 path = quote_path(it->string, s->prefix, &buf, 0);
2373 if (d->rename_source)
2374 path_from = quote_path(d->rename_source, s->prefix, &buf_from, 0);
2377 if (path_from)
2378 fprintf(s->fp, "2 %s %s %06o %06o %06o %s %s %c%d %s%c%s%c",
2379 key, submodule_token,
2380 d->mode_head, d->mode_index, d->mode_worktree,
2381 oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index),
2382 d->rename_status, d->rename_score,
2383 path, sep_char, path_from, eol_char);
2384 else
2385 fprintf(s->fp, "1 %s %s %06o %06o %06o %s %s %s%c",
2386 key, submodule_token,
2387 d->mode_head, d->mode_index, d->mode_worktree,
2388 oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index),
2389 path, eol_char);
2391 strbuf_release(&buf);
2392 strbuf_release(&buf_from);
2396 * Print porcelain v2 status info for unmerged entries.
2398 static void wt_porcelain_v2_print_unmerged_entry(
2399 struct string_list_item *it,
2400 struct wt_status *s)
2402 struct wt_status_change_data *d = it->util;
2403 struct index_state *istate = s->repo->index;
2404 const struct cache_entry *ce;
2405 struct strbuf buf_index = STRBUF_INIT;
2406 const char *path_index = NULL;
2407 int pos, stage, sum;
2408 struct {
2409 int mode;
2410 struct object_id oid;
2411 } stages[3];
2412 const char *key;
2413 char submodule_token[5];
2414 char unmerged_prefix = 'u';
2415 char eol_char = s->null_termination ? '\0' : '\n';
2417 wt_porcelain_v2_submodule_state(d, submodule_token);
2419 switch (d->stagemask) {
2420 case 1: key = "DD"; break; /* both deleted */
2421 case 2: key = "AU"; break; /* added by us */
2422 case 3: key = "UD"; break; /* deleted by them */
2423 case 4: key = "UA"; break; /* added by them */
2424 case 5: key = "DU"; break; /* deleted by us */
2425 case 6: key = "AA"; break; /* both added */
2426 case 7: key = "UU"; break; /* both modified */
2427 default:
2428 BUG("unhandled unmerged status %x", d->stagemask);
2432 * Disregard d.aux.porcelain_v2 data that we accumulated
2433 * for the head and index columns during the scans and
2434 * replace with the actual stage data.
2436 * Note that this is a last-one-wins for each the individual
2437 * stage [123] columns in the event of multiple cache entries
2438 * for same stage.
2440 memset(stages, 0, sizeof(stages));
2441 sum = 0;
2442 pos = index_name_pos(istate, it->string, strlen(it->string));
2443 assert(pos < 0);
2444 pos = -pos-1;
2445 while (pos < istate->cache_nr) {
2446 ce = istate->cache[pos++];
2447 stage = ce_stage(ce);
2448 if (strcmp(ce->name, it->string) || !stage)
2449 break;
2450 stages[stage - 1].mode = ce->ce_mode;
2451 oidcpy(&stages[stage - 1].oid, &ce->oid);
2452 sum |= (1 << (stage - 1));
2454 if (sum != d->stagemask)
2455 BUG("observed stagemask 0x%x != expected stagemask 0x%x", sum, d->stagemask);
2457 if (s->null_termination)
2458 path_index = it->string;
2459 else
2460 path_index = quote_path(it->string, s->prefix, &buf_index, 0);
2462 fprintf(s->fp, "%c %s %s %06o %06o %06o %06o %s %s %s %s%c",
2463 unmerged_prefix, key, submodule_token,
2464 stages[0].mode, /* stage 1 */
2465 stages[1].mode, /* stage 2 */
2466 stages[2].mode, /* stage 3 */
2467 d->mode_worktree,
2468 oid_to_hex(&stages[0].oid), /* stage 1 */
2469 oid_to_hex(&stages[1].oid), /* stage 2 */
2470 oid_to_hex(&stages[2].oid), /* stage 3 */
2471 path_index,
2472 eol_char);
2474 strbuf_release(&buf_index);
2478 * Print porcelain V2 status info for untracked and ignored entries.
2480 static void wt_porcelain_v2_print_other(
2481 struct string_list_item *it,
2482 struct wt_status *s,
2483 char prefix)
2485 struct strbuf buf = STRBUF_INIT;
2486 const char *path;
2487 char eol_char;
2489 if (s->null_termination) {
2490 path = it->string;
2491 eol_char = '\0';
2492 } else {
2493 path = quote_path(it->string, s->prefix, &buf, 0);
2494 eol_char = '\n';
2497 fprintf(s->fp, "%c %s%c", prefix, path, eol_char);
2499 strbuf_release(&buf);
2503 * Print porcelain V2 status.
2505 * [<v2_branch>]
2506 * [<v2_changed_items>]*
2507 * [<v2_unmerged_items>]*
2508 * [<v2_untracked_items>]*
2509 * [<v2_ignored_items>]*
2512 static void wt_porcelain_v2_print(struct wt_status *s)
2514 struct wt_status_change_data *d;
2515 struct string_list_item *it;
2516 int i;
2518 if (s->show_branch)
2519 wt_porcelain_v2_print_tracking(s);
2521 if (s->show_stash)
2522 wt_porcelain_v2_print_stash(s);
2524 for (i = 0; i < s->change.nr; i++) {
2525 it = &(s->change.items[i]);
2526 d = it->util;
2527 if (!d->stagemask)
2528 wt_porcelain_v2_print_changed_entry(it, s);
2531 for (i = 0; i < s->change.nr; i++) {
2532 it = &(s->change.items[i]);
2533 d = it->util;
2534 if (d->stagemask)
2535 wt_porcelain_v2_print_unmerged_entry(it, s);
2538 for (i = 0; i < s->untracked.nr; i++) {
2539 it = &(s->untracked.items[i]);
2540 wt_porcelain_v2_print_other(it, s, '?');
2543 for (i = 0; i < s->ignored.nr; i++) {
2544 it = &(s->ignored.items[i]);
2545 wt_porcelain_v2_print_other(it, s, '!');
2549 void wt_status_print(struct wt_status *s)
2551 trace2_data_intmax("status", s->repo, "count/changed", s->change.nr);
2552 trace2_data_intmax("status", s->repo, "count/untracked",
2553 s->untracked.nr);
2554 trace2_data_intmax("status", s->repo, "count/ignored", s->ignored.nr);
2556 trace2_region_enter("status", "print", s->repo);
2558 switch (s->status_format) {
2559 case STATUS_FORMAT_SHORT:
2560 wt_shortstatus_print(s);
2561 break;
2562 case STATUS_FORMAT_PORCELAIN:
2563 wt_porcelain_print(s);
2564 break;
2565 case STATUS_FORMAT_PORCELAIN_V2:
2566 wt_porcelain_v2_print(s);
2567 break;
2568 case STATUS_FORMAT_UNSPECIFIED:
2569 BUG("finalize_deferred_config() should have been called");
2570 break;
2571 case STATUS_FORMAT_NONE:
2572 case STATUS_FORMAT_LONG:
2573 wt_longstatus_print(s);
2574 break;
2577 trace2_region_leave("status", "print", s->repo);
2581 * Returns 1 if there are unstaged changes, 0 otherwise.
2583 int has_unstaged_changes(struct repository *r, int ignore_submodules)
2585 struct rev_info rev_info;
2586 int result;
2588 repo_init_revisions(r, &rev_info, NULL);
2589 if (ignore_submodules) {
2590 rev_info.diffopt.flags.ignore_submodules = 1;
2591 rev_info.diffopt.flags.override_submodule_config = 1;
2593 rev_info.diffopt.flags.quick = 1;
2594 diff_setup_done(&rev_info.diffopt);
2595 run_diff_files(&rev_info, 0);
2596 result = diff_result_code(&rev_info.diffopt);
2597 release_revisions(&rev_info);
2598 return result;
2602 * Returns 1 if there are uncommitted changes, 0 otherwise.
2604 int has_uncommitted_changes(struct repository *r,
2605 int ignore_submodules)
2607 struct rev_info rev_info;
2608 int result;
2610 if (is_index_unborn(r->index))
2611 return 0;
2613 repo_init_revisions(r, &rev_info, NULL);
2614 if (ignore_submodules)
2615 rev_info.diffopt.flags.ignore_submodules = 1;
2616 rev_info.diffopt.flags.quick = 1;
2618 add_head_to_pending(&rev_info);
2619 if (!rev_info.pending.nr) {
2621 * We have no head (or it's corrupt); use the empty tree,
2622 * which will complain if the index is non-empty.
2624 struct tree *tree = lookup_tree(r, the_hash_algo->empty_tree);
2625 add_pending_object(&rev_info, &tree->object, "");
2628 diff_setup_done(&rev_info.diffopt);
2629 run_diff_index(&rev_info, DIFF_INDEX_CACHED);
2630 result = diff_result_code(&rev_info.diffopt);
2631 release_revisions(&rev_info);
2632 return result;
2636 * If the work tree has unstaged or uncommitted changes, dies with the
2637 * appropriate message.
2639 int require_clean_work_tree(struct repository *r,
2640 const char *action,
2641 const char *hint,
2642 int ignore_submodules,
2643 int gently)
2645 struct lock_file lock_file = LOCK_INIT;
2646 int err = 0, fd;
2648 fd = repo_hold_locked_index(r, &lock_file, 0);
2649 refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
2650 if (0 <= fd)
2651 repo_update_index_if_able(r, &lock_file);
2652 rollback_lock_file(&lock_file);
2654 if (has_unstaged_changes(r, ignore_submodules)) {
2655 /* TRANSLATORS: the action is e.g. "pull with rebase" */
2656 error(_("cannot %s: You have unstaged changes."), _(action));
2657 err = 1;
2660 if (has_uncommitted_changes(r, ignore_submodules)) {
2661 if (err)
2662 error(_("additionally, your index contains uncommitted changes."));
2663 else
2664 error(_("cannot %s: Your index contains uncommitted changes."),
2665 _(action));
2666 err = 1;
2669 if (err) {
2670 if (hint) {
2671 if (!*hint)
2672 BUG("empty hint passed to require_clean_work_tree();"
2673 " use NULL instead");
2674 error("%s", hint);
2676 if (!gently)
2677 exit(128);
2680 return err;