10 #include "run-command.h"
13 #include "submodule.h"
15 static char default_wt_status_colors
[][COLOR_MAXLEN
] = {
16 GIT_COLOR_NORMAL
, /* WT_STATUS_HEADER */
17 GIT_COLOR_GREEN
, /* WT_STATUS_UPDATED */
18 GIT_COLOR_RED
, /* WT_STATUS_CHANGED */
19 GIT_COLOR_RED
, /* WT_STATUS_UNTRACKED */
20 GIT_COLOR_RED
, /* WT_STATUS_NOBRANCH */
21 GIT_COLOR_RED
, /* WT_STATUS_UNMERGED */
22 GIT_COLOR_GREEN
, /* WT_STATUS_LOCAL_BRANCH */
23 GIT_COLOR_RED
, /* WT_STATUS_REMOTE_BRANCH */
24 GIT_COLOR_NORMAL
, /* WT_STATUS_ONBRANCH */
27 static const char *color(int slot
, struct wt_status
*s
)
29 return s
->use_color
> 0 ? s
->color_palette
[slot
] : "";
32 void wt_status_prepare(struct wt_status
*s
)
34 unsigned char sha1
[20];
37 memset(s
, 0, sizeof(*s
));
38 memcpy(s
->color_palette
, default_wt_status_colors
,
39 sizeof(default_wt_status_colors
));
40 s
->show_untracked_files
= SHOW_NORMAL_UNTRACKED_FILES
;
42 s
->relative_paths
= 1;
43 head
= resolve_ref("HEAD", sha1
, 0, NULL
);
44 s
->branch
= head
? xstrdup(head
) : NULL
;
45 s
->reference
= "HEAD";
47 s
->index_file
= get_index_file();
48 s
->change
.strdup_strings
= 1;
49 s
->untracked
.strdup_strings
= 1;
50 s
->ignored
.strdup_strings
= 1;
53 static void wt_status_print_unmerged_header(struct wt_status
*s
)
55 const char *c
= color(WT_STATUS_HEADER
, s
);
57 color_fprintf_ln(s
->fp
, c
, "# Unmerged paths:");
58 if (!advice_status_hints
)
62 else if (!s
->is_initial
)
63 color_fprintf_ln(s
->fp
, c
, "# (use \"git reset %s <file>...\" to unstage)", s
->reference
);
65 color_fprintf_ln(s
->fp
, c
, "# (use \"git rm --cached <file>...\" to unstage)");
66 color_fprintf_ln(s
->fp
, c
, "# (use \"git add/rm <file>...\" as appropriate to mark resolution)");
67 color_fprintf_ln(s
->fp
, c
, "#");
70 static void wt_status_print_cached_header(struct wt_status
*s
)
72 const char *c
= color(WT_STATUS_HEADER
, s
);
74 color_fprintf_ln(s
->fp
, c
, "# Changes to be committed:");
75 if (!advice_status_hints
)
78 ; /* NEEDSWORK: use "git reset --unresolve"??? */
79 else if (!s
->is_initial
)
80 color_fprintf_ln(s
->fp
, c
, "# (use \"git reset %s <file>...\" to unstage)", s
->reference
);
82 color_fprintf_ln(s
->fp
, c
, "# (use \"git rm --cached <file>...\" to unstage)");
83 color_fprintf_ln(s
->fp
, c
, "#");
86 static void wt_status_print_dirty_header(struct wt_status
*s
,
88 int has_dirty_submodules
)
90 const char *c
= color(WT_STATUS_HEADER
, s
);
92 color_fprintf_ln(s
->fp
, c
, "# Changes not staged for commit:");
93 if (!advice_status_hints
)
96 color_fprintf_ln(s
->fp
, c
, "# (use \"git add <file>...\" to update what will be committed)");
98 color_fprintf_ln(s
->fp
, c
, "# (use \"git add/rm <file>...\" to update what will be committed)");
99 color_fprintf_ln(s
->fp
, c
, "# (use \"git checkout -- <file>...\" to discard changes in working directory)");
100 if (has_dirty_submodules
)
101 color_fprintf_ln(s
->fp
, c
, "# (commit or discard the untracked or modified content in submodules)");
102 color_fprintf_ln(s
->fp
, c
, "#");
105 static void wt_status_print_other_header(struct wt_status
*s
,
109 const char *c
= color(WT_STATUS_HEADER
, s
);
110 color_fprintf_ln(s
->fp
, c
, "# %s files:", what
);
111 if (!advice_status_hints
)
113 color_fprintf_ln(s
->fp
, c
, "# (use \"git %s <file>...\" to include in what will be committed)", how
);
114 color_fprintf_ln(s
->fp
, c
, "#");
117 static void wt_status_print_trailer(struct wt_status
*s
)
119 color_fprintf_ln(s
->fp
, color(WT_STATUS_HEADER
, s
), "#");
122 #define quote_path quote_path_relative
124 static void wt_status_print_unmerged_data(struct wt_status
*s
,
125 struct string_list_item
*it
)
127 const char *c
= color(WT_STATUS_UNMERGED
, s
);
128 struct wt_status_change_data
*d
= it
->util
;
129 struct strbuf onebuf
= STRBUF_INIT
;
130 const char *one
, *how
= "bug";
132 one
= quote_path(it
->string
, -1, &onebuf
, s
->prefix
);
133 color_fprintf(s
->fp
, color(WT_STATUS_HEADER
, s
), "#\t");
134 switch (d
->stagemask
) {
135 case 1: how
= "both deleted:"; break;
136 case 2: how
= "added by us:"; break;
137 case 3: how
= "deleted by them:"; break;
138 case 4: how
= "added by them:"; break;
139 case 5: how
= "deleted by us:"; break;
140 case 6: how
= "both added:"; break;
141 case 7: how
= "both modified:"; break;
143 color_fprintf(s
->fp
, c
, "%-20s%s\n", how
, one
);
144 strbuf_release(&onebuf
);
147 static void wt_status_print_change_data(struct wt_status
*s
,
149 struct string_list_item
*it
)
151 struct wt_status_change_data
*d
= it
->util
;
152 const char *c
= color(change_type
, s
);
156 const char *one
, *two
;
157 struct strbuf onebuf
= STRBUF_INIT
, twobuf
= STRBUF_INIT
;
158 struct strbuf extra
= STRBUF_INIT
;
160 one_name
= two_name
= it
->string
;
161 switch (change_type
) {
162 case WT_STATUS_UPDATED
:
163 status
= d
->index_status
;
165 one_name
= d
->head_path
;
167 case WT_STATUS_CHANGED
:
168 if (d
->new_submodule_commits
|| d
->dirty_submodule
) {
169 strbuf_addstr(&extra
, " (");
170 if (d
->new_submodule_commits
)
171 strbuf_addf(&extra
, "new commits, ");
172 if (d
->dirty_submodule
& DIRTY_SUBMODULE_MODIFIED
)
173 strbuf_addf(&extra
, "modified content, ");
174 if (d
->dirty_submodule
& DIRTY_SUBMODULE_UNTRACKED
)
175 strbuf_addf(&extra
, "untracked content, ");
176 strbuf_setlen(&extra
, extra
.len
- 2);
177 strbuf_addch(&extra
, ')');
179 status
= d
->worktree_status
;
183 one
= quote_path(one_name
, -1, &onebuf
, s
->prefix
);
184 two
= quote_path(two_name
, -1, &twobuf
, s
->prefix
);
186 color_fprintf(s
->fp
, color(WT_STATUS_HEADER
, s
), "#\t");
188 case DIFF_STATUS_ADDED
:
189 color_fprintf(s
->fp
, c
, "new file: %s", one
);
191 case DIFF_STATUS_COPIED
:
192 color_fprintf(s
->fp
, c
, "copied: %s -> %s", one
, two
);
194 case DIFF_STATUS_DELETED
:
195 color_fprintf(s
->fp
, c
, "deleted: %s", one
);
197 case DIFF_STATUS_MODIFIED
:
198 color_fprintf(s
->fp
, c
, "modified: %s", one
);
200 case DIFF_STATUS_RENAMED
:
201 color_fprintf(s
->fp
, c
, "renamed: %s -> %s", one
, two
);
203 case DIFF_STATUS_TYPE_CHANGED
:
204 color_fprintf(s
->fp
, c
, "typechange: %s", one
);
206 case DIFF_STATUS_UNKNOWN
:
207 color_fprintf(s
->fp
, c
, "unknown: %s", one
);
209 case DIFF_STATUS_UNMERGED
:
210 color_fprintf(s
->fp
, c
, "unmerged: %s", one
);
213 die("bug: unhandled diff status %c", status
);
216 color_fprintf(s
->fp
, color(WT_STATUS_HEADER
, s
), "%s", extra
.buf
);
217 strbuf_release(&extra
);
219 fprintf(s
->fp
, "\n");
220 strbuf_release(&onebuf
);
221 strbuf_release(&twobuf
);
224 static void wt_status_collect_changed_cb(struct diff_queue_struct
*q
,
225 struct diff_options
*options
,
228 struct wt_status
*s
= data
;
233 s
->workdir_dirty
= 1;
234 for (i
= 0; i
< q
->nr
; i
++) {
235 struct diff_filepair
*p
;
236 struct string_list_item
*it
;
237 struct wt_status_change_data
*d
;
240 it
= string_list_insert(&s
->change
, p
->one
->path
);
243 d
= xcalloc(1, sizeof(*d
));
246 if (!d
->worktree_status
)
247 d
->worktree_status
= p
->status
;
248 d
->dirty_submodule
= p
->two
->dirty_submodule
;
249 if (S_ISGITLINK(p
->two
->mode
))
250 d
->new_submodule_commits
= !!hashcmp(p
->one
->sha1
, p
->two
->sha1
);
254 static int unmerged_mask(const char *path
)
257 struct cache_entry
*ce
;
259 pos
= cache_name_pos(path
, strlen(path
));
265 while (pos
< active_nr
) {
266 ce
= active_cache
[pos
++];
267 if (strcmp(ce
->name
, path
) || !ce_stage(ce
))
269 mask
|= (1 << (ce_stage(ce
) - 1));
274 static void wt_status_collect_updated_cb(struct diff_queue_struct
*q
,
275 struct diff_options
*options
,
278 struct wt_status
*s
= data
;
281 for (i
= 0; i
< q
->nr
; i
++) {
282 struct diff_filepair
*p
;
283 struct string_list_item
*it
;
284 struct wt_status_change_data
*d
;
287 it
= string_list_insert(&s
->change
, p
->two
->path
);
290 d
= xcalloc(1, sizeof(*d
));
293 if (!d
->index_status
)
294 d
->index_status
= p
->status
;
296 case DIFF_STATUS_COPIED
:
297 case DIFF_STATUS_RENAMED
:
298 d
->head_path
= xstrdup(p
->one
->path
);
300 case DIFF_STATUS_UNMERGED
:
301 d
->stagemask
= unmerged_mask(p
->two
->path
);
307 static void wt_status_collect_changes_worktree(struct wt_status
*s
)
311 init_revisions(&rev
, NULL
);
312 setup_revisions(0, NULL
, &rev
, NULL
);
313 rev
.diffopt
.output_format
|= DIFF_FORMAT_CALLBACK
;
314 DIFF_OPT_SET(&rev
.diffopt
, DIRTY_SUBMODULES
);
315 if (!s
->show_untracked_files
)
316 DIFF_OPT_SET(&rev
.diffopt
, IGNORE_UNTRACKED_IN_SUBMODULES
);
317 if (s
->ignore_submodule_arg
) {
318 DIFF_OPT_SET(&rev
.diffopt
, OVERRIDE_SUBMODULE_CONFIG
);
319 handle_ignore_submodules_arg(&rev
.diffopt
, s
->ignore_submodule_arg
);
321 rev
.diffopt
.format_callback
= wt_status_collect_changed_cb
;
322 rev
.diffopt
.format_callback_data
= s
;
323 rev
.prune_data
= s
->pathspec
;
324 run_diff_files(&rev
, 0);
327 static void wt_status_collect_changes_index(struct wt_status
*s
)
330 struct setup_revision_opt opt
;
332 init_revisions(&rev
, NULL
);
333 memset(&opt
, 0, sizeof(opt
));
334 opt
.def
= s
->is_initial
? EMPTY_TREE_SHA1_HEX
: s
->reference
;
335 setup_revisions(0, NULL
, &rev
, &opt
);
337 if (s
->ignore_submodule_arg
) {
338 DIFF_OPT_SET(&rev
.diffopt
, OVERRIDE_SUBMODULE_CONFIG
);
339 handle_ignore_submodules_arg(&rev
.diffopt
, s
->ignore_submodule_arg
);
342 rev
.diffopt
.output_format
|= DIFF_FORMAT_CALLBACK
;
343 rev
.diffopt
.format_callback
= wt_status_collect_updated_cb
;
344 rev
.diffopt
.format_callback_data
= s
;
345 rev
.diffopt
.detect_rename
= 1;
346 rev
.diffopt
.rename_limit
= 200;
347 rev
.diffopt
.break_opt
= 0;
348 rev
.prune_data
= s
->pathspec
;
349 run_diff_index(&rev
, 1);
352 static void wt_status_collect_changes_initial(struct wt_status
*s
)
356 for (i
= 0; i
< active_nr
; i
++) {
357 struct string_list_item
*it
;
358 struct wt_status_change_data
*d
;
359 struct cache_entry
*ce
= active_cache
[i
];
361 if (!ce_path_match(ce
, s
->pathspec
))
363 it
= string_list_insert(&s
->change
, ce
->name
);
366 d
= xcalloc(1, sizeof(*d
));
370 d
->index_status
= DIFF_STATUS_UNMERGED
;
371 d
->stagemask
|= (1 << (ce_stage(ce
) - 1));
374 d
->index_status
= DIFF_STATUS_ADDED
;
378 static void wt_status_collect_untracked(struct wt_status
*s
)
381 struct dir_struct dir
;
383 if (!s
->show_untracked_files
)
385 memset(&dir
, 0, sizeof(dir
));
386 if (s
->show_untracked_files
!= SHOW_ALL_UNTRACKED_FILES
)
388 DIR_SHOW_OTHER_DIRECTORIES
| DIR_HIDE_EMPTY_DIRECTORIES
;
389 setup_standard_excludes(&dir
);
391 fill_directory(&dir
, s
->pathspec
);
392 for (i
= 0; i
< dir
.nr
; i
++) {
393 struct dir_entry
*ent
= dir
.entries
[i
];
394 if (cache_name_is_other(ent
->name
, ent
->len
) &&
395 match_pathspec(s
->pathspec
, ent
->name
, ent
->len
, 0, NULL
))
396 string_list_insert(&s
->untracked
, ent
->name
);
400 if (s
->show_ignored_files
) {
402 dir
.flags
= DIR_SHOW_IGNORED
| DIR_SHOW_OTHER_DIRECTORIES
;
403 fill_directory(&dir
, s
->pathspec
);
404 for (i
= 0; i
< dir
.nr
; i
++) {
405 struct dir_entry
*ent
= dir
.entries
[i
];
406 if (cache_name_is_other(ent
->name
, ent
->len
) &&
407 match_pathspec(s
->pathspec
, ent
->name
, ent
->len
, 0, NULL
))
408 string_list_insert(&s
->ignored
, ent
->name
);
416 void wt_status_collect(struct wt_status
*s
)
418 wt_status_collect_changes_worktree(s
);
421 wt_status_collect_changes_initial(s
);
423 wt_status_collect_changes_index(s
);
424 wt_status_collect_untracked(s
);
427 static void wt_status_print_unmerged(struct wt_status
*s
)
429 int shown_header
= 0;
432 for (i
= 0; i
< s
->change
.nr
; i
++) {
433 struct wt_status_change_data
*d
;
434 struct string_list_item
*it
;
435 it
= &(s
->change
.items
[i
]);
440 wt_status_print_unmerged_header(s
);
443 wt_status_print_unmerged_data(s
, it
);
446 wt_status_print_trailer(s
);
450 static void wt_status_print_updated(struct wt_status
*s
)
452 int shown_header
= 0;
455 for (i
= 0; i
< s
->change
.nr
; i
++) {
456 struct wt_status_change_data
*d
;
457 struct string_list_item
*it
;
458 it
= &(s
->change
.items
[i
]);
460 if (!d
->index_status
||
461 d
->index_status
== DIFF_STATUS_UNMERGED
)
464 wt_status_print_cached_header(s
);
468 wt_status_print_change_data(s
, WT_STATUS_UPDATED
, it
);
471 wt_status_print_trailer(s
);
477 * 1 : some change but no delete
479 static int wt_status_check_worktree_changes(struct wt_status
*s
,
480 int *dirty_submodules
)
485 *dirty_submodules
= 0;
487 for (i
= 0; i
< s
->change
.nr
; i
++) {
488 struct wt_status_change_data
*d
;
489 d
= s
->change
.items
[i
].util
;
490 if (!d
->worktree_status
||
491 d
->worktree_status
== DIFF_STATUS_UNMERGED
)
495 if (d
->dirty_submodule
)
496 *dirty_submodules
= 1;
497 if (d
->worktree_status
== DIFF_STATUS_DELETED
)
503 static void wt_status_print_changed(struct wt_status
*s
)
505 int i
, dirty_submodules
;
506 int worktree_changes
= wt_status_check_worktree_changes(s
, &dirty_submodules
);
508 if (!worktree_changes
)
511 wt_status_print_dirty_header(s
, worktree_changes
< 0, dirty_submodules
);
513 for (i
= 0; i
< s
->change
.nr
; i
++) {
514 struct wt_status_change_data
*d
;
515 struct string_list_item
*it
;
516 it
= &(s
->change
.items
[i
]);
518 if (!d
->worktree_status
||
519 d
->worktree_status
== DIFF_STATUS_UNMERGED
)
521 wt_status_print_change_data(s
, WT_STATUS_CHANGED
, it
);
523 wt_status_print_trailer(s
);
526 static void wt_status_print_submodule_summary(struct wt_status
*s
, int uncommitted
)
528 struct child_process sm_summary
;
529 char summary_limit
[64];
530 char index
[PATH_MAX
];
531 const char *env
[] = { NULL
, NULL
};
535 argv
[0] = "submodule";
537 argv
[2] = uncommitted
? "--files" : "--cached";
538 argv
[3] = "--for-status";
539 argv
[4] = "--summary-limit";
540 argv
[5] = summary_limit
;
541 argv
[6] = uncommitted
? NULL
: (s
->amend
? "HEAD^" : "HEAD");
544 sprintf(summary_limit
, "%d", s
->submodule_summary
);
545 snprintf(index
, sizeof(index
), "GIT_INDEX_FILE=%s", s
->index_file
);
547 memset(&sm_summary
, 0, sizeof(sm_summary
));
548 sm_summary
.argv
= argv
;
549 sm_summary
.env
= env
;
550 sm_summary
.git_cmd
= 1;
551 sm_summary
.no_stdin
= 1;
553 sm_summary
.out
= dup(fileno(s
->fp
)); /* run_command closes it */
554 run_command(&sm_summary
);
557 static void wt_status_print_other(struct wt_status
*s
,
558 struct string_list
*l
,
563 struct strbuf buf
= STRBUF_INIT
;
565 if (!s
->untracked
.nr
)
568 wt_status_print_other_header(s
, what
, how
);
570 for (i
= 0; i
< l
->nr
; i
++) {
571 struct string_list_item
*it
;
573 color_fprintf(s
->fp
, color(WT_STATUS_HEADER
, s
), "#\t");
574 color_fprintf_ln(s
->fp
, color(WT_STATUS_UNTRACKED
, s
), "%s",
575 quote_path(it
->string
, strlen(it
->string
),
578 strbuf_release(&buf
);
581 static void wt_status_print_verbose(struct wt_status
*s
)
584 struct setup_revision_opt opt
;
586 init_revisions(&rev
, NULL
);
587 DIFF_OPT_SET(&rev
.diffopt
, ALLOW_TEXTCONV
);
589 memset(&opt
, 0, sizeof(opt
));
590 opt
.def
= s
->is_initial
? EMPTY_TREE_SHA1_HEX
: s
->reference
;
591 setup_revisions(0, NULL
, &rev
, &opt
);
593 rev
.diffopt
.output_format
|= DIFF_FORMAT_PATCH
;
594 rev
.diffopt
.detect_rename
= 1;
595 rev
.diffopt
.file
= s
->fp
;
596 rev
.diffopt
.close_file
= 0;
598 * If we're not going to stdout, then we definitely don't
599 * want color, since we are going to the commit message
600 * file (and even the "auto" setting won't work, since it
601 * will have checked isatty on stdout).
604 DIFF_OPT_CLR(&rev
.diffopt
, COLOR_DIFF
);
605 run_diff_index(&rev
, 1);
608 static void wt_status_print_tracking(struct wt_status
*s
)
610 struct strbuf sb
= STRBUF_INIT
;
612 struct branch
*branch
;
614 assert(s
->branch
&& !s
->is_initial
);
615 if (prefixcmp(s
->branch
, "refs/heads/"))
617 branch
= branch_get(s
->branch
+ 11);
618 if (!format_tracking_info(branch
, &sb
))
621 for (cp
= sb
.buf
; (ep
= strchr(cp
, '\n')) != NULL
; cp
= ep
+ 1)
622 color_fprintf_ln(s
->fp
, color(WT_STATUS_HEADER
, s
),
623 "# %.*s", (int)(ep
- cp
), cp
);
624 color_fprintf_ln(s
->fp
, color(WT_STATUS_HEADER
, s
), "#");
627 void wt_status_print(struct wt_status
*s
)
629 const char *branch_color
= color(WT_STATUS_ONBRANCH
, s
);
630 const char *branch_status_color
= color(WT_STATUS_HEADER
, s
);
633 const char *on_what
= "On branch ";
634 const char *branch_name
= s
->branch
;
635 if (!prefixcmp(branch_name
, "refs/heads/"))
637 else if (!strcmp(branch_name
, "HEAD")) {
639 branch_status_color
= color(WT_STATUS_NOBRANCH
, s
);
640 on_what
= "Not currently on any branch.";
642 color_fprintf(s
->fp
, color(WT_STATUS_HEADER
, s
), "# ");
643 color_fprintf(s
->fp
, branch_status_color
, "%s", on_what
);
644 color_fprintf_ln(s
->fp
, branch_color
, "%s", branch_name
);
646 wt_status_print_tracking(s
);
650 color_fprintf_ln(s
->fp
, color(WT_STATUS_HEADER
, s
), "#");
651 color_fprintf_ln(s
->fp
, color(WT_STATUS_HEADER
, s
), "# Initial commit");
652 color_fprintf_ln(s
->fp
, color(WT_STATUS_HEADER
, s
), "#");
655 wt_status_print_updated(s
);
656 wt_status_print_unmerged(s
);
657 wt_status_print_changed(s
);
658 if (s
->submodule_summary
&&
659 (!s
->ignore_submodule_arg
||
660 strcmp(s
->ignore_submodule_arg
, "all"))) {
661 wt_status_print_submodule_summary(s
, 0); /* staged */
662 wt_status_print_submodule_summary(s
, 1); /* unstaged */
664 if (s
->show_untracked_files
) {
665 wt_status_print_other(s
, &s
->untracked
, "Untracked", "add");
666 if (s
->show_ignored_files
)
667 wt_status_print_other(s
, &s
->ignored
, "Ignored", "add -f");
668 } else if (s
->commitable
)
669 fprintf(s
->fp
, "# Untracked files not listed%s\n",
671 ? " (use -u option to show untracked files)" : "");
674 wt_status_print_verbose(s
);
675 if (!s
->commitable
) {
677 fprintf(s
->fp
, "# No changes\n");
680 else if (s
->workdir_dirty
)
681 printf("no changes added to commit%s\n",
683 ? " (use \"git add\" and/or \"git commit -a\")" : "");
684 else if (s
->untracked
.nr
)
685 printf("nothing added to commit but untracked files present%s\n",
687 ? " (use \"git add\" to track)" : "");
688 else if (s
->is_initial
)
689 printf("nothing to commit%s\n", advice_status_hints
690 ? " (create/copy files and use \"git add\" to track)" : "");
691 else if (!s
->show_untracked_files
)
692 printf("nothing to commit%s\n", advice_status_hints
693 ? " (use -u to show untracked files)" : "");
695 printf("nothing to commit%s\n", advice_status_hints
696 ? " (working directory clean)" : "");
700 static void wt_shortstatus_unmerged(int null_termination
, struct string_list_item
*it
,
703 struct wt_status_change_data
*d
= it
->util
;
704 const char *how
= "??";
706 switch (d
->stagemask
) {
707 case 1: how
= "DD"; break; /* both deleted */
708 case 2: how
= "AU"; break; /* added by us */
709 case 3: how
= "UD"; break; /* deleted by them */
710 case 4: how
= "UA"; break; /* added by them */
711 case 5: how
= "DU"; break; /* deleted by us */
712 case 6: how
= "AA"; break; /* both added */
713 case 7: how
= "UU"; break; /* both modified */
715 color_fprintf(s
->fp
, color(WT_STATUS_UNMERGED
, s
), "%s", how
);
716 if (null_termination
) {
717 fprintf(stdout
, " %s%c", it
->string
, 0);
719 struct strbuf onebuf
= STRBUF_INIT
;
721 one
= quote_path(it
->string
, -1, &onebuf
, s
->prefix
);
722 printf(" %s\n", one
);
723 strbuf_release(&onebuf
);
727 static void wt_shortstatus_status(int null_termination
, struct string_list_item
*it
,
730 struct wt_status_change_data
*d
= it
->util
;
733 color_fprintf(s
->fp
, color(WT_STATUS_UPDATED
, s
), "%c", d
->index_status
);
736 if (d
->worktree_status
)
737 color_fprintf(s
->fp
, color(WT_STATUS_CHANGED
, s
), "%c", d
->worktree_status
);
741 if (null_termination
) {
742 fprintf(stdout
, "%s%c", it
->string
, 0);
744 fprintf(stdout
, "%s%c", d
->head_path
, 0);
746 struct strbuf onebuf
= STRBUF_INIT
;
749 one
= quote_path(d
->head_path
, -1, &onebuf
, s
->prefix
);
750 if (*one
!= '"' && strchr(one
, ' ') != NULL
) {
752 strbuf_addch(&onebuf
, '"');
755 printf("%s -> ", one
);
756 strbuf_release(&onebuf
);
758 one
= quote_path(it
->string
, -1, &onebuf
, s
->prefix
);
759 if (*one
!= '"' && strchr(one
, ' ') != NULL
) {
761 strbuf_addch(&onebuf
, '"');
765 strbuf_release(&onebuf
);
769 static void wt_shortstatus_other(int null_termination
, struct string_list_item
*it
,
770 struct wt_status
*s
, const char *sign
)
772 if (null_termination
) {
773 fprintf(stdout
, "%s %s%c", sign
, it
->string
, 0);
775 struct strbuf onebuf
= STRBUF_INIT
;
777 one
= quote_path(it
->string
, -1, &onebuf
, s
->prefix
);
778 color_fprintf(s
->fp
, color(WT_STATUS_UNTRACKED
, s
), "%s", sign
);
779 printf(" %s\n", one
);
780 strbuf_release(&onebuf
);
784 static void wt_shortstatus_print_tracking(struct wt_status
*s
)
786 struct branch
*branch
;
787 const char *header_color
= color(WT_STATUS_HEADER
, s
);
788 const char *branch_color_local
= color(WT_STATUS_LOCAL_BRANCH
, s
);
789 const char *branch_color_remote
= color(WT_STATUS_REMOTE_BRANCH
, s
);
792 const char *branch_name
;
793 int num_ours
, num_theirs
;
795 color_fprintf(s
->fp
, color(WT_STATUS_HEADER
, s
), "## ");
799 branch_name
= s
->branch
;
801 if (!prefixcmp(branch_name
, "refs/heads/"))
803 else if (!strcmp(branch_name
, "HEAD")) {
804 branch_name
= "HEAD (no branch)";
805 branch_color_local
= color(WT_STATUS_NOBRANCH
, s
);
808 branch
= branch_get(s
->branch
+ 11);
810 color_fprintf(s
->fp
, header_color
, "Initial commit on ");
811 if (!stat_tracking_info(branch
, &num_ours
, &num_theirs
)) {
812 color_fprintf_ln(s
->fp
, branch_color_local
,
817 base
= branch
->merge
[0]->dst
;
818 base
= shorten_unambiguous_ref(base
, 0);
819 color_fprintf(s
->fp
, branch_color_local
, "%s", branch_name
);
820 color_fprintf(s
->fp
, header_color
, "...");
821 color_fprintf(s
->fp
, branch_color_remote
, "%s", base
);
823 color_fprintf(s
->fp
, header_color
, " [");
825 color_fprintf(s
->fp
, header_color
, "behind ");
826 color_fprintf(s
->fp
, branch_color_remote
, "%d", num_theirs
);
827 } else if (!num_theirs
) {
828 color_fprintf(s
->fp
, header_color
, "ahead ");
829 color_fprintf(s
->fp
, branch_color_local
, "%d", num_ours
);
831 color_fprintf(s
->fp
, header_color
, "ahead ");
832 color_fprintf(s
->fp
, branch_color_local
, "%d", num_ours
);
833 color_fprintf(s
->fp
, header_color
, ", behind ");
834 color_fprintf(s
->fp
, branch_color_remote
, "%d", num_theirs
);
837 color_fprintf_ln(s
->fp
, header_color
, "]");
840 void wt_shortstatus_print(struct wt_status
*s
, int null_termination
, int show_branch
)
845 wt_shortstatus_print_tracking(s
);
847 for (i
= 0; i
< s
->change
.nr
; i
++) {
848 struct wt_status_change_data
*d
;
849 struct string_list_item
*it
;
851 it
= &(s
->change
.items
[i
]);
854 wt_shortstatus_unmerged(null_termination
, it
, s
);
856 wt_shortstatus_status(null_termination
, it
, s
);
858 for (i
= 0; i
< s
->untracked
.nr
; i
++) {
859 struct string_list_item
*it
;
861 it
= &(s
->untracked
.items
[i
]);
862 wt_shortstatus_other(null_termination
, it
, s
, "??");
864 for (i
= 0; i
< s
->ignored
.nr
; i
++) {
865 struct string_list_item
*it
;
867 it
= &(s
->ignored
.items
[i
]);
868 wt_shortstatus_other(null_termination
, it
, s
, "!!");
872 void wt_porcelain_print(struct wt_status
*s
, int null_termination
)
875 s
->relative_paths
= 0;
877 wt_shortstatus_print(s
, null_termination
, 0);