Merge branch 'jc/shortstatus'
* jc/shortstatus: git commit --dry-run -v: show diff in color when asked Documentation/git-commit.txt: describe --dry-run wt-status: collect untracked files in a separate "collect" phase Make git_status_config() file scope static to builtin-commit.c wt-status: move wt_status_colors[] into wt_status structure wt-status: move many global settings to wt_status structure commit: --dry-run status: show worktree status of conflicted paths separately wt-status.c: rework the way changes to the index and work tree are summarized diff-index: keep the original index intact diff-index: report unmerged new entries
This commit is contained in:
commit
433233e0b6
@ -8,8 +8,8 @@ git-commit - Record changes to the repository
|
|||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
[verse]
|
[verse]
|
||||||
'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend]
|
'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend] [--dry-run]
|
||||||
[(-c | -C) <commit>] [-F <file> | -m <msg>]
|
[(-c | -C) <commit>] [-F <file> | -m <msg>] [--dry-run]
|
||||||
[--allow-empty] [--no-verify] [-e] [--author=<author>]
|
[--allow-empty] [--no-verify] [-e] [--author=<author>]
|
||||||
[--cleanup=<mode>] [--] [[-i | -o ]<file>...]
|
[--cleanup=<mode>] [--] [[-i | -o ]<file>...]
|
||||||
|
|
||||||
@ -42,10 +42,9 @@ The content to be added can be specified in several ways:
|
|||||||
by one which files should be part of the commit, before finalizing the
|
by one which files should be part of the commit, before finalizing the
|
||||||
operation. Currently, this is done by invoking 'git-add --interactive'.
|
operation. Currently, this is done by invoking 'git-add --interactive'.
|
||||||
|
|
||||||
The 'git-status' command can be used to obtain a
|
The `--dry-run` option can be used to obtain a
|
||||||
summary of what is included by any of the above for the next
|
summary of what is included by any of the above for the next
|
||||||
commit by giving the same set of parameters you would give to
|
commit by giving the same set of parameters (options and paths).
|
||||||
this command.
|
|
||||||
|
|
||||||
If you make a commit and then find a mistake immediately after
|
If you make a commit and then find a mistake immediately after
|
||||||
that, you can recover from it with 'git-reset'.
|
that, you can recover from it with 'git-reset'.
|
||||||
@ -70,6 +69,12 @@ OPTIONS
|
|||||||
Like '-C', but with '-c' the editor is invoked, so that
|
Like '-C', but with '-c' the editor is invoked, so that
|
||||||
the user can further edit the commit message.
|
the user can further edit the commit message.
|
||||||
|
|
||||||
|
--dry-run::
|
||||||
|
Do not actually make a commit, but show the list of paths
|
||||||
|
with updates in the index, paths with changes in the work tree,
|
||||||
|
and paths that are untracked, similar to the one that is given
|
||||||
|
in the commit log editor.
|
||||||
|
|
||||||
-F <file>::
|
-F <file>::
|
||||||
--file=<file>::
|
--file=<file>::
|
||||||
Take the commit message from the given file. Use '-' to
|
Take the commit message from the given file. Use '-' to
|
||||||
@ -198,6 +203,11 @@ specified.
|
|||||||
--quiet::
|
--quiet::
|
||||||
Suppress commit summary message.
|
Suppress commit summary message.
|
||||||
|
|
||||||
|
--dry-run::
|
||||||
|
Do not create a commit, but show a list of paths that are
|
||||||
|
to be committed, paths with local changes that will be left
|
||||||
|
uncommitted and paths that are untracked.
|
||||||
|
|
||||||
\--::
|
\--::
|
||||||
Do not interpret any more arguments as options.
|
Do not interpret any more arguments as options.
|
||||||
|
|
||||||
|
183
builtin-commit.c
183
builtin-commit.c
@ -51,7 +51,7 @@ static const char *template_file;
|
|||||||
static char *edit_message, *use_message;
|
static char *edit_message, *use_message;
|
||||||
static char *author_name, *author_email, *author_date;
|
static char *author_name, *author_email, *author_date;
|
||||||
static int all, edit_flag, also, interactive, only, amend, signoff;
|
static int all, edit_flag, also, interactive, only, amend, signoff;
|
||||||
static int quiet, verbose, no_verify, allow_empty;
|
static int quiet, verbose, no_verify, allow_empty, dry_run;
|
||||||
static char *untracked_files_arg;
|
static char *untracked_files_arg;
|
||||||
/*
|
/*
|
||||||
* The default commit message cleanup mode will remove the lines
|
* The default commit message cleanup mode will remove the lines
|
||||||
@ -103,6 +103,7 @@ static struct option builtin_commit_options[] = {
|
|||||||
OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
|
OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
|
||||||
OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
|
OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
|
||||||
OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
|
OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
|
||||||
|
OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
|
||||||
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
|
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
|
||||||
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
|
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
|
||||||
OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
|
OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
|
||||||
@ -217,12 +218,15 @@ static void create_base_index(void)
|
|||||||
exit(128); /* We've already reported the error, finish dying */
|
exit(128); /* We've already reported the error, finish dying */
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *prepare_index(int argc, const char **argv, const char *prefix)
|
static char *prepare_index(int argc, const char **argv, const char *prefix, int is_status)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
struct string_list partial;
|
struct string_list partial;
|
||||||
const char **pathspec = NULL;
|
const char **pathspec = NULL;
|
||||||
|
int refresh_flags = REFRESH_QUIET;
|
||||||
|
|
||||||
|
if (is_status)
|
||||||
|
refresh_flags |= REFRESH_UNMERGED;
|
||||||
if (interactive) {
|
if (interactive) {
|
||||||
if (interactive_add(argc, argv, prefix) != 0)
|
if (interactive_add(argc, argv, prefix) != 0)
|
||||||
die("interactive add failed");
|
die("interactive add failed");
|
||||||
@ -253,7 +257,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
|
|||||||
if (all || (also && pathspec && *pathspec)) {
|
if (all || (also && pathspec && *pathspec)) {
|
||||||
int fd = hold_locked_index(&index_lock, 1);
|
int fd = hold_locked_index(&index_lock, 1);
|
||||||
add_files_to_cache(also ? prefix : NULL, pathspec, 0);
|
add_files_to_cache(also ? prefix : NULL, pathspec, 0);
|
||||||
refresh_cache(REFRESH_QUIET);
|
refresh_cache(refresh_flags);
|
||||||
if (write_cache(fd, active_cache, active_nr) ||
|
if (write_cache(fd, active_cache, active_nr) ||
|
||||||
close_lock_file(&index_lock))
|
close_lock_file(&index_lock))
|
||||||
die("unable to write new_index file");
|
die("unable to write new_index file");
|
||||||
@ -272,7 +276,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
|
|||||||
*/
|
*/
|
||||||
if (!pathspec || !*pathspec) {
|
if (!pathspec || !*pathspec) {
|
||||||
fd = hold_locked_index(&index_lock, 1);
|
fd = hold_locked_index(&index_lock, 1);
|
||||||
refresh_cache(REFRESH_QUIET);
|
refresh_cache(refresh_flags);
|
||||||
if (write_cache(fd, active_cache, active_nr) ||
|
if (write_cache(fd, active_cache, active_nr) ||
|
||||||
commit_locked_index(&index_lock))
|
commit_locked_index(&index_lock))
|
||||||
die("unable to write new_index file");
|
die("unable to write new_index file");
|
||||||
@ -339,27 +343,24 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
|
|||||||
return false_lock.filename;
|
return false_lock.filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn)
|
static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
|
||||||
|
struct wt_status *s)
|
||||||
{
|
{
|
||||||
struct wt_status s;
|
if (s->relative_paths)
|
||||||
|
s->prefix = prefix;
|
||||||
wt_status_prepare(&s);
|
|
||||||
if (wt_status_relative_paths)
|
|
||||||
s.prefix = prefix;
|
|
||||||
|
|
||||||
if (amend) {
|
if (amend) {
|
||||||
s.amend = 1;
|
s->amend = 1;
|
||||||
s.reference = "HEAD^1";
|
s->reference = "HEAD^1";
|
||||||
}
|
}
|
||||||
s.verbose = verbose;
|
s->verbose = verbose;
|
||||||
s.untracked = (show_untracked_files == SHOW_ALL_UNTRACKED_FILES);
|
s->index_file = index_file;
|
||||||
s.index_file = index_file;
|
s->fp = fp;
|
||||||
s.fp = fp;
|
s->nowarn = nowarn;
|
||||||
s.nowarn = nowarn;
|
|
||||||
|
|
||||||
wt_status_print(&s);
|
wt_status_print(s);
|
||||||
|
|
||||||
return s.commitable;
|
return s->commitable;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_a_merge(const unsigned char *sha1)
|
static int is_a_merge(const unsigned char *sha1)
|
||||||
@ -413,7 +414,8 @@ static void determine_author_info(void)
|
|||||||
author_date = date;
|
author_date = date;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int prepare_to_commit(const char *index_file, const char *prefix)
|
static int prepare_to_commit(const char *index_file, const char *prefix,
|
||||||
|
struct wt_status *s)
|
||||||
{
|
{
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
int commitable, saved_color_setting;
|
int commitable, saved_color_setting;
|
||||||
@ -555,10 +557,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
|
|||||||
if (ident_shown)
|
if (ident_shown)
|
||||||
fprintf(fp, "#\n");
|
fprintf(fp, "#\n");
|
||||||
|
|
||||||
saved_color_setting = wt_status_use_color;
|
saved_color_setting = s->use_color;
|
||||||
wt_status_use_color = 0;
|
s->use_color = 0;
|
||||||
commitable = run_status(fp, index_file, prefix, 1);
|
commitable = run_status(fp, index_file, prefix, 1, s);
|
||||||
wt_status_use_color = saved_color_setting;
|
s->use_color = saved_color_setting;
|
||||||
} else {
|
} else {
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
const char *parent = "HEAD";
|
const char *parent = "HEAD";
|
||||||
@ -579,7 +581,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
|
|||||||
|
|
||||||
if (!commitable && !in_merge && !allow_empty &&
|
if (!commitable && !in_merge && !allow_empty &&
|
||||||
!(amend && is_a_merge(head_sha1))) {
|
!(amend && is_a_merge(head_sha1))) {
|
||||||
run_status(stdout, index_file, prefix, 0);
|
run_status(stdout, index_file, prefix, 0, s);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,7 +693,8 @@ static const char *find_author_by_nickname(const char *name)
|
|||||||
|
|
||||||
static int parse_and_validate_options(int argc, const char *argv[],
|
static int parse_and_validate_options(int argc, const char *argv[],
|
||||||
const char * const usage[],
|
const char * const usage[],
|
||||||
const char *prefix)
|
const char *prefix,
|
||||||
|
struct wt_status *s)
|
||||||
{
|
{
|
||||||
int f = 0;
|
int f = 0;
|
||||||
|
|
||||||
@ -794,11 +797,11 @@ static int parse_and_validate_options(int argc, const char *argv[],
|
|||||||
if (!untracked_files_arg)
|
if (!untracked_files_arg)
|
||||||
; /* default already initialized */
|
; /* default already initialized */
|
||||||
else if (!strcmp(untracked_files_arg, "no"))
|
else if (!strcmp(untracked_files_arg, "no"))
|
||||||
show_untracked_files = SHOW_NO_UNTRACKED_FILES;
|
s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
|
||||||
else if (!strcmp(untracked_files_arg, "normal"))
|
else if (!strcmp(untracked_files_arg, "normal"))
|
||||||
show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
|
s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
|
||||||
else if (!strcmp(untracked_files_arg, "all"))
|
else if (!strcmp(untracked_files_arg, "all"))
|
||||||
show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
|
s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
|
||||||
else
|
else
|
||||||
die("Invalid untracked files mode '%s'", untracked_files_arg);
|
die("Invalid untracked files mode '%s'", untracked_files_arg);
|
||||||
|
|
||||||
@ -810,30 +813,95 @@ static int parse_and_validate_options(int argc, const char *argv[],
|
|||||||
return argc;
|
return argc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cmd_status(int argc, const char **argv, const char *prefix)
|
static int dry_run_commit(int argc, const char **argv, const char *prefix,
|
||||||
|
struct wt_status *s)
|
||||||
{
|
{
|
||||||
const char *index_file;
|
|
||||||
int commitable;
|
int commitable;
|
||||||
|
const char *index_file;
|
||||||
|
|
||||||
git_config(git_status_config, NULL);
|
index_file = prepare_index(argc, argv, prefix, 1);
|
||||||
|
commitable = run_status(stdout, index_file, prefix, 0, s);
|
||||||
if (wt_status_use_color == -1)
|
|
||||||
wt_status_use_color = git_use_color_default;
|
|
||||||
|
|
||||||
if (diff_use_color_default == -1)
|
|
||||||
diff_use_color_default = git_use_color_default;
|
|
||||||
|
|
||||||
argc = parse_and_validate_options(argc, argv, builtin_status_usage, prefix);
|
|
||||||
|
|
||||||
index_file = prepare_index(argc, argv, prefix);
|
|
||||||
|
|
||||||
commitable = run_status(stdout, index_file, prefix, 0);
|
|
||||||
|
|
||||||
rollback_index_files();
|
rollback_index_files();
|
||||||
|
|
||||||
return commitable ? 0 : 1;
|
return commitable ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_status_slot(const char *var, int offset)
|
||||||
|
{
|
||||||
|
if (!strcasecmp(var+offset, "header"))
|
||||||
|
return WT_STATUS_HEADER;
|
||||||
|
if (!strcasecmp(var+offset, "updated")
|
||||||
|
|| !strcasecmp(var+offset, "added"))
|
||||||
|
return WT_STATUS_UPDATED;
|
||||||
|
if (!strcasecmp(var+offset, "changed"))
|
||||||
|
return WT_STATUS_CHANGED;
|
||||||
|
if (!strcasecmp(var+offset, "untracked"))
|
||||||
|
return WT_STATUS_UNTRACKED;
|
||||||
|
if (!strcasecmp(var+offset, "nobranch"))
|
||||||
|
return WT_STATUS_NOBRANCH;
|
||||||
|
if (!strcasecmp(var+offset, "unmerged"))
|
||||||
|
return WT_STATUS_UNMERGED;
|
||||||
|
die("bad config variable '%s'", var);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int git_status_config(const char *k, const char *v, void *cb)
|
||||||
|
{
|
||||||
|
struct wt_status *s = cb;
|
||||||
|
|
||||||
|
if (!strcmp(k, "status.submodulesummary")) {
|
||||||
|
int is_bool;
|
||||||
|
s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
|
||||||
|
if (is_bool && s->submodule_summary)
|
||||||
|
s->submodule_summary = -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
|
||||||
|
s->use_color = git_config_colorbool(k, v, -1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
|
||||||
|
int slot = parse_status_slot(k, 13);
|
||||||
|
if (!v)
|
||||||
|
return config_error_nonbool(k);
|
||||||
|
color_parse(v, k, s->color_palette[slot]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!strcmp(k, "status.relativepaths")) {
|
||||||
|
s->relative_paths = git_config_bool(k, v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!strcmp(k, "status.showuntrackedfiles")) {
|
||||||
|
if (!v)
|
||||||
|
return config_error_nonbool(k);
|
||||||
|
else if (!strcmp(v, "no"))
|
||||||
|
s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
|
||||||
|
else if (!strcmp(v, "normal"))
|
||||||
|
s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
|
||||||
|
else if (!strcmp(v, "all"))
|
||||||
|
s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
|
||||||
|
else
|
||||||
|
return error("Invalid untracked files mode '%s'", v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return git_diff_ui_config(k, v, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmd_status(int argc, const char **argv, const char *prefix)
|
||||||
|
{
|
||||||
|
struct wt_status s;
|
||||||
|
|
||||||
|
wt_status_prepare(&s);
|
||||||
|
git_config(git_status_config, &s);
|
||||||
|
if (s.use_color == -1)
|
||||||
|
s.use_color = git_use_color_default;
|
||||||
|
if (diff_use_color_default == -1)
|
||||||
|
diff_use_color_default = git_use_color_default;
|
||||||
|
|
||||||
|
argc = parse_and_validate_options(argc, argv, builtin_status_usage,
|
||||||
|
prefix, &s);
|
||||||
|
return dry_run_commit(argc, argv, prefix, &s);
|
||||||
|
}
|
||||||
|
|
||||||
static void print_summary(const char *prefix, const unsigned char *sha1)
|
static void print_summary(const char *prefix, const unsigned char *sha1)
|
||||||
{
|
{
|
||||||
struct rev_info rev;
|
struct rev_info rev;
|
||||||
@ -883,10 +951,12 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
|
|||||||
|
|
||||||
static int git_commit_config(const char *k, const char *v, void *cb)
|
static int git_commit_config(const char *k, const char *v, void *cb)
|
||||||
{
|
{
|
||||||
|
struct wt_status *s = cb;
|
||||||
|
|
||||||
if (!strcmp(k, "commit.template"))
|
if (!strcmp(k, "commit.template"))
|
||||||
return git_config_string(&template_file, k, v);
|
return git_config_string(&template_file, k, v);
|
||||||
|
|
||||||
return git_status_config(k, v, cb);
|
return git_status_config(k, v, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cmd_commit(int argc, const char **argv, const char *prefix)
|
int cmd_commit(int argc, const char **argv, const char *prefix)
|
||||||
@ -899,19 +969,26 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
|
|||||||
struct commit_list *parents = NULL, **pptr = &parents;
|
struct commit_list *parents = NULL, **pptr = &parents;
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
int allow_fast_forward = 1;
|
int allow_fast_forward = 1;
|
||||||
|
struct wt_status s;
|
||||||
|
|
||||||
git_config(git_commit_config, NULL);
|
wt_status_prepare(&s);
|
||||||
|
git_config(git_commit_config, &s);
|
||||||
|
|
||||||
if (wt_status_use_color == -1)
|
if (s.use_color == -1)
|
||||||
wt_status_use_color = git_use_color_default;
|
s.use_color = git_use_color_default;
|
||||||
|
|
||||||
argc = parse_and_validate_options(argc, argv, builtin_commit_usage, prefix);
|
argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
|
||||||
|
prefix, &s);
|
||||||
index_file = prepare_index(argc, argv, prefix);
|
if (dry_run) {
|
||||||
|
if (diff_use_color_default == -1)
|
||||||
|
diff_use_color_default = git_use_color_default;
|
||||||
|
return dry_run_commit(argc, argv, prefix, &s);
|
||||||
|
}
|
||||||
|
index_file = prepare_index(argc, argv, prefix, 0);
|
||||||
|
|
||||||
/* Set up everything for writing the commit object. This includes
|
/* Set up everything for writing the commit object. This includes
|
||||||
running hooks, writing the trees, and interacting with the user. */
|
running hooks, writing the trees, and interacting with the user. */
|
||||||
if (!prepare_to_commit(index_file, prefix)) {
|
if (!prepare_to_commit(index_file, prefix, &s)) {
|
||||||
rollback_index_files();
|
rollback_index_files();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
22
diff-lib.c
22
diff-lib.c
@ -309,22 +309,6 @@ static int show_modified(struct rev_info *revs,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* This turns all merge entries into "stage 3". That guarantees that
|
|
||||||
* when we read in the new tree (into "stage 1"), we won't lose sight
|
|
||||||
* of the fact that we had unmerged entries.
|
|
||||||
*/
|
|
||||||
static void mark_merge_entries(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < active_nr; i++) {
|
|
||||||
struct cache_entry *ce = active_cache[i];
|
|
||||||
if (!ce_stage(ce))
|
|
||||||
continue;
|
|
||||||
ce->ce_flags |= CE_STAGEMASK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This gets a mix of an existing index and a tree, one pathname entry
|
* This gets a mix of an existing index and a tree, one pathname entry
|
||||||
* at a time. The index entry may be a single stage-0 one, but it could
|
* at a time. The index entry may be a single stage-0 one, but it could
|
||||||
@ -350,8 +334,8 @@ static void do_oneway_diff(struct unpack_trees_options *o,
|
|||||||
match_missing = !revs->ignore_merges;
|
match_missing = !revs->ignore_merges;
|
||||||
|
|
||||||
if (cached && idx && ce_stage(idx)) {
|
if (cached && idx && ce_stage(idx)) {
|
||||||
if (tree)
|
diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode,
|
||||||
diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode, idx->sha1);
|
idx->sha1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,8 +421,6 @@ int run_diff_index(struct rev_info *revs, int cached)
|
|||||||
struct unpack_trees_options opts;
|
struct unpack_trees_options opts;
|
||||||
struct tree_desc t;
|
struct tree_desc t;
|
||||||
|
|
||||||
mark_merge_entries();
|
|
||||||
|
|
||||||
ent = revs->pending.objects[0].item;
|
ent = revs->pending.objects[0].item;
|
||||||
tree_name = revs->pending.objects[0].name;
|
tree_name = revs->pending.objects[0].name;
|
||||||
tree = parse_tree_indirect(ent->sha1);
|
tree = parse_tree_indirect(ent->sha1);
|
||||||
|
58
t/t7060-wtstatus.sh
Executable file
58
t/t7060-wtstatus.sh
Executable file
@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='basic work tree status reporting'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_expect_success setup '
|
||||||
|
test_commit A &&
|
||||||
|
test_commit B oneside added &&
|
||||||
|
git checkout A^0 &&
|
||||||
|
test_commit C oneside created
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'A/A conflict' '
|
||||||
|
git checkout B^0 &&
|
||||||
|
test_must_fail git merge C
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'Report path with conflict' '
|
||||||
|
git diff --cached --name-status >actual &&
|
||||||
|
echo "U oneside" >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'Report new path with conflict' '
|
||||||
|
git diff --cached --name-status HEAD^ >actual &&
|
||||||
|
echo "U oneside" >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >expect <<EOF
|
||||||
|
# On branch side
|
||||||
|
# Unmerged paths:
|
||||||
|
# (use "git reset HEAD <file>..." to unstage)
|
||||||
|
# (use "git add <file>..." to mark resolution)
|
||||||
|
#
|
||||||
|
# deleted by us: foo
|
||||||
|
#
|
||||||
|
no changes added to commit (use "git add" and/or "git commit -a")
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'M/D conflict does not segfault' '
|
||||||
|
mkdir mdconflict &&
|
||||||
|
(
|
||||||
|
cd mdconflict &&
|
||||||
|
git init &&
|
||||||
|
test_commit initial foo "" &&
|
||||||
|
test_commit modify foo foo &&
|
||||||
|
git checkout -b side HEAD^ &&
|
||||||
|
git rm foo &&
|
||||||
|
git commit -m delete &&
|
||||||
|
test_must_fail git merge master &&
|
||||||
|
test_must_fail git status > ../actual
|
||||||
|
) &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
479
wt-status.c
479
wt-status.c
@ -1,6 +1,5 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "wt-status.h"
|
#include "wt-status.h"
|
||||||
#include "color.h"
|
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
@ -11,38 +10,18 @@
|
|||||||
#include "run-command.h"
|
#include "run-command.h"
|
||||||
#include "remote.h"
|
#include "remote.h"
|
||||||
|
|
||||||
int wt_status_relative_paths = 1;
|
static char default_wt_status_colors[][COLOR_MAXLEN] = {
|
||||||
int wt_status_use_color = -1;
|
|
||||||
static int wt_status_submodule_summary;
|
|
||||||
static char wt_status_colors[][COLOR_MAXLEN] = {
|
|
||||||
GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
|
GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
|
||||||
GIT_COLOR_GREEN, /* WT_STATUS_UPDATED */
|
GIT_COLOR_GREEN, /* WT_STATUS_UPDATED */
|
||||||
GIT_COLOR_RED, /* WT_STATUS_CHANGED */
|
GIT_COLOR_RED, /* WT_STATUS_CHANGED */
|
||||||
GIT_COLOR_RED, /* WT_STATUS_UNTRACKED */
|
GIT_COLOR_RED, /* WT_STATUS_UNTRACKED */
|
||||||
GIT_COLOR_RED, /* WT_STATUS_NOBRANCH */
|
GIT_COLOR_RED, /* WT_STATUS_NOBRANCH */
|
||||||
|
GIT_COLOR_RED, /* WT_STATUS_UNMERGED */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
|
static const char *color(int slot, struct wt_status *s)
|
||||||
|
|
||||||
static int parse_status_slot(const char *var, int offset)
|
|
||||||
{
|
{
|
||||||
if (!strcasecmp(var+offset, "header"))
|
return s->use_color > 0 ? s->color_palette[slot] : "";
|
||||||
return WT_STATUS_HEADER;
|
|
||||||
if (!strcasecmp(var+offset, "updated")
|
|
||||||
|| !strcasecmp(var+offset, "added"))
|
|
||||||
return WT_STATUS_UPDATED;
|
|
||||||
if (!strcasecmp(var+offset, "changed"))
|
|
||||||
return WT_STATUS_CHANGED;
|
|
||||||
if (!strcasecmp(var+offset, "untracked"))
|
|
||||||
return WT_STATUS_UNTRACKED;
|
|
||||||
if (!strcasecmp(var+offset, "nobranch"))
|
|
||||||
return WT_STATUS_NOBRANCH;
|
|
||||||
die("bad config variable '%s'", var);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *color(int slot)
|
|
||||||
{
|
|
||||||
return wt_status_use_color > 0 ? wt_status_colors[slot] : "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wt_status_prepare(struct wt_status *s)
|
void wt_status_prepare(struct wt_status *s)
|
||||||
@ -51,16 +30,35 @@ void wt_status_prepare(struct wt_status *s)
|
|||||||
const char *head;
|
const char *head;
|
||||||
|
|
||||||
memset(s, 0, sizeof(*s));
|
memset(s, 0, sizeof(*s));
|
||||||
|
memcpy(s->color_palette, default_wt_status_colors,
|
||||||
|
sizeof(default_wt_status_colors));
|
||||||
|
s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
|
||||||
|
s->use_color = -1;
|
||||||
|
s->relative_paths = 1;
|
||||||
head = resolve_ref("HEAD", sha1, 0, NULL);
|
head = resolve_ref("HEAD", sha1, 0, NULL);
|
||||||
s->branch = head ? xstrdup(head) : NULL;
|
s->branch = head ? xstrdup(head) : NULL;
|
||||||
s->reference = "HEAD";
|
s->reference = "HEAD";
|
||||||
s->fp = stdout;
|
s->fp = stdout;
|
||||||
s->index_file = get_index_file();
|
s->index_file = get_index_file();
|
||||||
|
s->change.strdup_strings = 1;
|
||||||
|
s->untracked.strdup_strings = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wt_status_print_unmerged_header(struct wt_status *s)
|
||||||
|
{
|
||||||
|
const char *c = color(WT_STATUS_HEADER, s);
|
||||||
|
color_fprintf_ln(s->fp, c, "# Unmerged paths:");
|
||||||
|
if (!s->is_initial)
|
||||||
|
color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
|
||||||
|
else
|
||||||
|
color_fprintf_ln(s->fp, c, "# (use \"git rm --cached <file>...\" to unstage)");
|
||||||
|
color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to mark resolution)");
|
||||||
|
color_fprintf_ln(s->fp, c, "#");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wt_status_print_cached_header(struct wt_status *s)
|
static void wt_status_print_cached_header(struct wt_status *s)
|
||||||
{
|
{
|
||||||
const char *c = color(WT_STATUS_HEADER);
|
const char *c = color(WT_STATUS_HEADER, s);
|
||||||
color_fprintf_ln(s->fp, c, "# Changes to be committed:");
|
color_fprintf_ln(s->fp, c, "# Changes to be committed:");
|
||||||
if (!s->is_initial) {
|
if (!s->is_initial) {
|
||||||
color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
|
color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
|
||||||
@ -73,7 +71,7 @@ static void wt_status_print_cached_header(struct wt_status *s)
|
|||||||
static void wt_status_print_dirty_header(struct wt_status *s,
|
static void wt_status_print_dirty_header(struct wt_status *s,
|
||||||
int has_deleted)
|
int has_deleted)
|
||||||
{
|
{
|
||||||
const char *c = color(WT_STATUS_HEADER);
|
const char *c = color(WT_STATUS_HEADER, s);
|
||||||
color_fprintf_ln(s->fp, c, "# Changed but not updated:");
|
color_fprintf_ln(s->fp, c, "# Changed but not updated:");
|
||||||
if (!has_deleted)
|
if (!has_deleted)
|
||||||
color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to update what will be committed)");
|
color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to update what will be committed)");
|
||||||
@ -85,7 +83,7 @@ static void wt_status_print_dirty_header(struct wt_status *s,
|
|||||||
|
|
||||||
static void wt_status_print_untracked_header(struct wt_status *s)
|
static void wt_status_print_untracked_header(struct wt_status *s)
|
||||||
{
|
{
|
||||||
const char *c = color(WT_STATUS_HEADER);
|
const char *c = color(WT_STATUS_HEADER, s);
|
||||||
color_fprintf_ln(s->fp, c, "# Untracked files:");
|
color_fprintf_ln(s->fp, c, "# Untracked files:");
|
||||||
color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to include in what will be committed)");
|
color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to include in what will be committed)");
|
||||||
color_fprintf_ln(s->fp, c, "#");
|
color_fprintf_ln(s->fp, c, "#");
|
||||||
@ -93,23 +91,63 @@ static void wt_status_print_untracked_header(struct wt_status *s)
|
|||||||
|
|
||||||
static void wt_status_print_trailer(struct wt_status *s)
|
static void wt_status_print_trailer(struct wt_status *s)
|
||||||
{
|
{
|
||||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
|
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
|
||||||
}
|
}
|
||||||
|
|
||||||
#define quote_path quote_path_relative
|
#define quote_path quote_path_relative
|
||||||
|
|
||||||
static void wt_status_print_filepair(struct wt_status *s,
|
static void wt_status_print_unmerged_data(struct wt_status *s,
|
||||||
int t, struct diff_filepair *p)
|
struct string_list_item *it)
|
||||||
{
|
{
|
||||||
const char *c = color(t);
|
const char *c = color(WT_STATUS_UNMERGED, s);
|
||||||
|
struct wt_status_change_data *d = it->util;
|
||||||
|
struct strbuf onebuf = STRBUF_INIT;
|
||||||
|
const char *one, *how = "bug";
|
||||||
|
|
||||||
|
one = quote_path(it->string, -1, &onebuf, s->prefix);
|
||||||
|
color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
|
||||||
|
switch (d->stagemask) {
|
||||||
|
case 1: how = "both deleted:"; break;
|
||||||
|
case 2: how = "added by us:"; break;
|
||||||
|
case 3: how = "deleted by them:"; break;
|
||||||
|
case 4: how = "added by them:"; break;
|
||||||
|
case 5: how = "deleted by us:"; break;
|
||||||
|
case 6: how = "both added:"; break;
|
||||||
|
case 7: how = "both modified:"; break;
|
||||||
|
}
|
||||||
|
color_fprintf(s->fp, c, "%-20s%s\n", how, one);
|
||||||
|
strbuf_release(&onebuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wt_status_print_change_data(struct wt_status *s,
|
||||||
|
int change_type,
|
||||||
|
struct string_list_item *it)
|
||||||
|
{
|
||||||
|
struct wt_status_change_data *d = it->util;
|
||||||
|
const char *c = color(change_type, s);
|
||||||
|
int status = status;
|
||||||
|
char *one_name;
|
||||||
|
char *two_name;
|
||||||
const char *one, *two;
|
const char *one, *two;
|
||||||
struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
|
struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
|
||||||
|
|
||||||
one = quote_path(p->one->path, -1, &onebuf, s->prefix);
|
one_name = two_name = it->string;
|
||||||
two = quote_path(p->two->path, -1, &twobuf, s->prefix);
|
switch (change_type) {
|
||||||
|
case WT_STATUS_UPDATED:
|
||||||
|
status = d->index_status;
|
||||||
|
if (d->head_path)
|
||||||
|
one_name = d->head_path;
|
||||||
|
break;
|
||||||
|
case WT_STATUS_CHANGED:
|
||||||
|
status = d->worktree_status;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
|
one = quote_path(one_name, -1, &onebuf, s->prefix);
|
||||||
switch (p->status) {
|
two = quote_path(two_name, -1, &twobuf, s->prefix);
|
||||||
|
|
||||||
|
color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
|
||||||
|
switch (status) {
|
||||||
case DIFF_STATUS_ADDED:
|
case DIFF_STATUS_ADDED:
|
||||||
color_fprintf(s->fp, c, "new file: %s", one);
|
color_fprintf(s->fp, c, "new file: %s", one);
|
||||||
break;
|
break;
|
||||||
@ -135,64 +173,114 @@ static void wt_status_print_filepair(struct wt_status *s,
|
|||||||
color_fprintf(s->fp, c, "unmerged: %s", one);
|
color_fprintf(s->fp, c, "unmerged: %s", one);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
die("bug: unhandled diff status %c", p->status);
|
die("bug: unhandled diff status %c", status);
|
||||||
}
|
}
|
||||||
fprintf(s->fp, "\n");
|
fprintf(s->fp, "\n");
|
||||||
strbuf_release(&onebuf);
|
strbuf_release(&onebuf);
|
||||||
strbuf_release(&twobuf);
|
strbuf_release(&twobuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wt_status_print_updated_cb(struct diff_queue_struct *q,
|
static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
|
||||||
struct diff_options *options,
|
struct diff_options *options,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
struct wt_status *s = data;
|
struct wt_status *s = data;
|
||||||
int shown_header = 0;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (!q->nr)
|
||||||
|
return;
|
||||||
|
s->workdir_dirty = 1;
|
||||||
for (i = 0; i < q->nr; i++) {
|
for (i = 0; i < q->nr; i++) {
|
||||||
if (q->queue[i]->status == 'U')
|
struct diff_filepair *p;
|
||||||
continue;
|
struct string_list_item *it;
|
||||||
if (!shown_header) {
|
struct wt_status_change_data *d;
|
||||||
wt_status_print_cached_header(s);
|
|
||||||
s->commitable = 1;
|
p = q->queue[i];
|
||||||
shown_header = 1;
|
it = string_list_insert(p->one->path, &s->change);
|
||||||
|
d = it->util;
|
||||||
|
if (!d) {
|
||||||
|
d = xcalloc(1, sizeof(*d));
|
||||||
|
it->util = d;
|
||||||
}
|
}
|
||||||
wt_status_print_filepair(s, WT_STATUS_UPDATED, q->queue[i]);
|
if (!d->worktree_status)
|
||||||
|
d->worktree_status = p->status;
|
||||||
}
|
}
|
||||||
if (shown_header)
|
|
||||||
wt_status_print_trailer(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wt_status_print_changed_cb(struct diff_queue_struct *q,
|
static int unmerged_mask(const char *path)
|
||||||
struct diff_options *options,
|
{
|
||||||
void *data)
|
int pos, mask;
|
||||||
|
struct cache_entry *ce;
|
||||||
|
|
||||||
|
pos = cache_name_pos(path, strlen(path));
|
||||||
|
if (0 <= pos)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mask = 0;
|
||||||
|
pos = -pos-1;
|
||||||
|
while (pos < active_nr) {
|
||||||
|
ce = active_cache[pos++];
|
||||||
|
if (strcmp(ce->name, path) || !ce_stage(ce))
|
||||||
|
break;
|
||||||
|
mask |= (1 << (ce_stage(ce) - 1));
|
||||||
|
}
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
|
||||||
|
struct diff_options *options,
|
||||||
|
void *data)
|
||||||
{
|
{
|
||||||
struct wt_status *s = data;
|
struct wt_status *s = data;
|
||||||
int i;
|
int i;
|
||||||
if (q->nr) {
|
|
||||||
int has_deleted = 0;
|
for (i = 0; i < q->nr; i++) {
|
||||||
s->workdir_dirty = 1;
|
struct diff_filepair *p;
|
||||||
for (i = 0; i < q->nr; i++)
|
struct string_list_item *it;
|
||||||
if (q->queue[i]->status == DIFF_STATUS_DELETED) {
|
struct wt_status_change_data *d;
|
||||||
has_deleted = 1;
|
|
||||||
break;
|
p = q->queue[i];
|
||||||
}
|
it = string_list_insert(p->two->path, &s->change);
|
||||||
wt_status_print_dirty_header(s, has_deleted);
|
d = it->util;
|
||||||
|
if (!d) {
|
||||||
|
d = xcalloc(1, sizeof(*d));
|
||||||
|
it->util = d;
|
||||||
|
}
|
||||||
|
if (!d->index_status)
|
||||||
|
d->index_status = p->status;
|
||||||
|
switch (p->status) {
|
||||||
|
case DIFF_STATUS_COPIED:
|
||||||
|
case DIFF_STATUS_RENAMED:
|
||||||
|
d->head_path = xstrdup(p->one->path);
|
||||||
|
break;
|
||||||
|
case DIFF_STATUS_UNMERGED:
|
||||||
|
d->stagemask = unmerged_mask(p->two->path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (i = 0; i < q->nr; i++)
|
|
||||||
wt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]);
|
|
||||||
if (q->nr)
|
|
||||||
wt_status_print_trailer(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wt_status_print_updated(struct wt_status *s)
|
static void wt_status_collect_changes_worktree(struct wt_status *s)
|
||||||
{
|
{
|
||||||
struct rev_info rev;
|
struct rev_info rev;
|
||||||
|
|
||||||
|
init_revisions(&rev, NULL);
|
||||||
|
setup_revisions(0, NULL, &rev, NULL);
|
||||||
|
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
|
||||||
|
rev.diffopt.format_callback = wt_status_collect_changed_cb;
|
||||||
|
rev.diffopt.format_callback_data = s;
|
||||||
|
run_diff_files(&rev, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wt_status_collect_changes_index(struct wt_status *s)
|
||||||
|
{
|
||||||
|
struct rev_info rev;
|
||||||
|
|
||||||
init_revisions(&rev, NULL);
|
init_revisions(&rev, NULL);
|
||||||
setup_revisions(0, NULL, &rev,
|
setup_revisions(0, NULL, &rev,
|
||||||
s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
|
s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
|
||||||
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
|
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
|
||||||
rev.diffopt.format_callback = wt_status_print_updated_cb;
|
rev.diffopt.format_callback = wt_status_collect_updated_cb;
|
||||||
rev.diffopt.format_callback_data = s;
|
rev.diffopt.format_callback_data = s;
|
||||||
rev.diffopt.detect_rename = 1;
|
rev.diffopt.detect_rename = 1;
|
||||||
rev.diffopt.rename_limit = 200;
|
rev.diffopt.rename_limit = 200;
|
||||||
@ -200,15 +288,155 @@ static void wt_status_print_updated(struct wt_status *s)
|
|||||||
run_diff_index(&rev, 1);
|
run_diff_index(&rev, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wt_status_collect_changes_initial(struct wt_status *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < active_nr; i++) {
|
||||||
|
struct string_list_item *it;
|
||||||
|
struct wt_status_change_data *d;
|
||||||
|
struct cache_entry *ce = active_cache[i];
|
||||||
|
|
||||||
|
it = string_list_insert(ce->name, &s->change);
|
||||||
|
d = it->util;
|
||||||
|
if (!d) {
|
||||||
|
d = xcalloc(1, sizeof(*d));
|
||||||
|
it->util = d;
|
||||||
|
}
|
||||||
|
if (ce_stage(ce)) {
|
||||||
|
d->index_status = DIFF_STATUS_UNMERGED;
|
||||||
|
d->stagemask |= (1 << (ce_stage(ce) - 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
d->index_status = DIFF_STATUS_ADDED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wt_status_collect_untracked(struct wt_status *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct dir_struct dir;
|
||||||
|
|
||||||
|
if (!s->show_untracked_files)
|
||||||
|
return;
|
||||||
|
memset(&dir, 0, sizeof(dir));
|
||||||
|
if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
|
||||||
|
dir.flags |=
|
||||||
|
DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
|
||||||
|
setup_standard_excludes(&dir);
|
||||||
|
|
||||||
|
fill_directory(&dir, NULL);
|
||||||
|
for(i = 0; i < dir.nr; i++) {
|
||||||
|
struct dir_entry *ent = dir.entries[i];
|
||||||
|
if (!cache_name_is_other(ent->name, ent->len))
|
||||||
|
continue;
|
||||||
|
s->workdir_untracked = 1;
|
||||||
|
string_list_insert(ent->name, &s->untracked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wt_status_collect(struct wt_status *s)
|
||||||
|
{
|
||||||
|
wt_status_collect_changes_worktree(s);
|
||||||
|
|
||||||
|
if (s->is_initial)
|
||||||
|
wt_status_collect_changes_initial(s);
|
||||||
|
else
|
||||||
|
wt_status_collect_changes_index(s);
|
||||||
|
wt_status_collect_untracked(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wt_status_print_unmerged(struct wt_status *s)
|
||||||
|
{
|
||||||
|
int shown_header = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < s->change.nr; i++) {
|
||||||
|
struct wt_status_change_data *d;
|
||||||
|
struct string_list_item *it;
|
||||||
|
it = &(s->change.items[i]);
|
||||||
|
d = it->util;
|
||||||
|
if (!d->stagemask)
|
||||||
|
continue;
|
||||||
|
if (!shown_header) {
|
||||||
|
wt_status_print_unmerged_header(s);
|
||||||
|
shown_header = 1;
|
||||||
|
}
|
||||||
|
wt_status_print_unmerged_data(s, it);
|
||||||
|
}
|
||||||
|
if (shown_header)
|
||||||
|
wt_status_print_trailer(s);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wt_status_print_updated(struct wt_status *s)
|
||||||
|
{
|
||||||
|
int shown_header = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < s->change.nr; i++) {
|
||||||
|
struct wt_status_change_data *d;
|
||||||
|
struct string_list_item *it;
|
||||||
|
it = &(s->change.items[i]);
|
||||||
|
d = it->util;
|
||||||
|
if (!d->index_status ||
|
||||||
|
d->index_status == DIFF_STATUS_UNMERGED)
|
||||||
|
continue;
|
||||||
|
if (!shown_header) {
|
||||||
|
wt_status_print_cached_header(s);
|
||||||
|
s->commitable = 1;
|
||||||
|
shown_header = 1;
|
||||||
|
}
|
||||||
|
wt_status_print_change_data(s, WT_STATUS_UPDATED, it);
|
||||||
|
}
|
||||||
|
if (shown_header)
|
||||||
|
wt_status_print_trailer(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -1 : has delete
|
||||||
|
* 0 : no change
|
||||||
|
* 1 : some change but no delete
|
||||||
|
*/
|
||||||
|
static int wt_status_check_worktree_changes(struct wt_status *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int changes = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < s->change.nr; i++) {
|
||||||
|
struct wt_status_change_data *d;
|
||||||
|
d = s->change.items[i].util;
|
||||||
|
if (!d->worktree_status ||
|
||||||
|
d->worktree_status == DIFF_STATUS_UNMERGED)
|
||||||
|
continue;
|
||||||
|
changes = 1;
|
||||||
|
if (d->worktree_status == DIFF_STATUS_DELETED)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
static void wt_status_print_changed(struct wt_status *s)
|
static void wt_status_print_changed(struct wt_status *s)
|
||||||
{
|
{
|
||||||
struct rev_info rev;
|
int i;
|
||||||
init_revisions(&rev, "");
|
int worktree_changes = wt_status_check_worktree_changes(s);
|
||||||
setup_revisions(0, NULL, &rev, NULL);
|
|
||||||
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
|
if (!worktree_changes)
|
||||||
rev.diffopt.format_callback = wt_status_print_changed_cb;
|
return;
|
||||||
rev.diffopt.format_callback_data = s;
|
|
||||||
run_diff_files(&rev, 0);
|
wt_status_print_dirty_header(s, worktree_changes < 0);
|
||||||
|
|
||||||
|
for (i = 0; i < s->change.nr; i++) {
|
||||||
|
struct wt_status_change_data *d;
|
||||||
|
struct string_list_item *it;
|
||||||
|
it = &(s->change.items[i]);
|
||||||
|
d = it->util;
|
||||||
|
if (!d->worktree_status ||
|
||||||
|
d->worktree_status == DIFF_STATUS_UNMERGED)
|
||||||
|
continue;
|
||||||
|
wt_status_print_change_data(s, WT_STATUS_CHANGED, it);
|
||||||
|
}
|
||||||
|
wt_status_print_trailer(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wt_status_print_submodule_summary(struct wt_status *s)
|
static void wt_status_print_submodule_summary(struct wt_status *s)
|
||||||
@ -228,7 +456,7 @@ static void wt_status_print_submodule_summary(struct wt_status *s)
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
sprintf(summary_limit, "%d", wt_status_submodule_summary);
|
sprintf(summary_limit, "%d", s->submodule_summary);
|
||||||
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
|
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
|
||||||
|
|
||||||
memset(&sm_summary, 0, sizeof(sm_summary));
|
memset(&sm_summary, 0, sizeof(sm_summary));
|
||||||
@ -243,32 +471,20 @@ static void wt_status_print_submodule_summary(struct wt_status *s)
|
|||||||
|
|
||||||
static void wt_status_print_untracked(struct wt_status *s)
|
static void wt_status_print_untracked(struct wt_status *s)
|
||||||
{
|
{
|
||||||
struct dir_struct dir;
|
|
||||||
int i;
|
int i;
|
||||||
int shown_header = 0;
|
|
||||||
struct strbuf buf = STRBUF_INIT;
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
|
||||||
memset(&dir, 0, sizeof(dir));
|
if (!s->untracked.nr)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!s->untracked)
|
wt_status_print_untracked_header(s);
|
||||||
dir.flags |=
|
for (i = 0; i < s->untracked.nr; i++) {
|
||||||
DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
|
struct string_list_item *it;
|
||||||
setup_standard_excludes(&dir);
|
it = &(s->untracked.items[i]);
|
||||||
|
color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
|
||||||
fill_directory(&dir, NULL);
|
color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED, s), "%s",
|
||||||
for(i = 0; i < dir.nr; i++) {
|
quote_path(it->string, strlen(it->string),
|
||||||
struct dir_entry *ent = dir.entries[i];
|
&buf, s->prefix));
|
||||||
if (!cache_name_is_other(ent->name, ent->len))
|
|
||||||
continue;
|
|
||||||
if (!shown_header) {
|
|
||||||
s->workdir_untracked = 1;
|
|
||||||
wt_status_print_untracked_header(s);
|
|
||||||
shown_header = 1;
|
|
||||||
}
|
|
||||||
color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
|
|
||||||
color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%s",
|
|
||||||
quote_path(ent->name, ent->len,
|
|
||||||
&buf, s->prefix));
|
|
||||||
}
|
}
|
||||||
strbuf_release(&buf);
|
strbuf_release(&buf);
|
||||||
}
|
}
|
||||||
@ -310,15 +526,15 @@ static void wt_status_print_tracking(struct wt_status *s)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
|
for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
|
||||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER),
|
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
|
||||||
"# %.*s", (int)(ep - cp), cp);
|
"# %.*s", (int)(ep - cp), cp);
|
||||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
|
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
|
||||||
}
|
}
|
||||||
|
|
||||||
void wt_status_print(struct wt_status *s)
|
void wt_status_print(struct wt_status *s)
|
||||||
{
|
{
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
const char *branch_color = color(WT_STATUS_HEADER);
|
const char *branch_color = color(WT_STATUS_HEADER, s);
|
||||||
|
|
||||||
s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
|
s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
|
||||||
if (s->branch) {
|
if (s->branch) {
|
||||||
@ -328,26 +544,29 @@ void wt_status_print(struct wt_status *s)
|
|||||||
branch_name += 11;
|
branch_name += 11;
|
||||||
else if (!strcmp(branch_name, "HEAD")) {
|
else if (!strcmp(branch_name, "HEAD")) {
|
||||||
branch_name = "";
|
branch_name = "";
|
||||||
branch_color = color(WT_STATUS_NOBRANCH);
|
branch_color = color(WT_STATUS_NOBRANCH, s);
|
||||||
on_what = "Not currently on any branch.";
|
on_what = "Not currently on any branch.";
|
||||||
}
|
}
|
||||||
color_fprintf(s->fp, color(WT_STATUS_HEADER), "# ");
|
color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# ");
|
||||||
color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
|
color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
|
||||||
if (!s->is_initial)
|
if (!s->is_initial)
|
||||||
wt_status_print_tracking(s);
|
wt_status_print_tracking(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wt_status_collect(s);
|
||||||
|
|
||||||
if (s->is_initial) {
|
if (s->is_initial) {
|
||||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
|
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
|
||||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "# Initial commit");
|
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit");
|
||||||
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
|
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wt_status_print_unmerged(s);
|
||||||
wt_status_print_updated(s);
|
wt_status_print_updated(s);
|
||||||
wt_status_print_changed(s);
|
wt_status_print_changed(s);
|
||||||
if (wt_status_submodule_summary)
|
if (s->submodule_summary)
|
||||||
wt_status_print_submodule_summary(s);
|
wt_status_print_submodule_summary(s);
|
||||||
if (show_untracked_files)
|
if (s->show_untracked_files)
|
||||||
wt_status_print_untracked(s);
|
wt_status_print_untracked(s);
|
||||||
else if (s->commitable)
|
else if (s->commitable)
|
||||||
fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
|
fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
|
||||||
@ -361,53 +580,13 @@ void wt_status_print(struct wt_status *s)
|
|||||||
; /* nothing */
|
; /* nothing */
|
||||||
else if (s->workdir_dirty)
|
else if (s->workdir_dirty)
|
||||||
printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
|
printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
|
||||||
else if (s->workdir_untracked)
|
else if (s->untracked.nr)
|
||||||
printf("nothing added to commit but untracked files present (use \"git add\" to track)\n");
|
printf("nothing added to commit but untracked files present (use \"git add\" to track)\n");
|
||||||
else if (s->is_initial)
|
else if (s->is_initial)
|
||||||
printf("nothing to commit (create/copy files and use \"git add\" to track)\n");
|
printf("nothing to commit (create/copy files and use \"git add\" to track)\n");
|
||||||
else if (!show_untracked_files)
|
else if (!s->show_untracked_files)
|
||||||
printf("nothing to commit (use -u to show untracked files)\n");
|
printf("nothing to commit (use -u to show untracked files)\n");
|
||||||
else
|
else
|
||||||
printf("nothing to commit (working directory clean)\n");
|
printf("nothing to commit (working directory clean)\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_status_config(const char *k, const char *v, void *cb)
|
|
||||||
{
|
|
||||||
if (!strcmp(k, "status.submodulesummary")) {
|
|
||||||
int is_bool;
|
|
||||||
wt_status_submodule_summary = git_config_bool_or_int(k, v, &is_bool);
|
|
||||||
if (is_bool && wt_status_submodule_summary)
|
|
||||||
wt_status_submodule_summary = -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
|
|
||||||
wt_status_use_color = git_config_colorbool(k, v, -1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
|
|
||||||
int slot = parse_status_slot(k, 13);
|
|
||||||
if (!v)
|
|
||||||
return config_error_nonbool(k);
|
|
||||||
color_parse(v, k, wt_status_colors[slot]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!strcmp(k, "status.relativepaths")) {
|
|
||||||
wt_status_relative_paths = git_config_bool(k, v);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!strcmp(k, "status.showuntrackedfiles")) {
|
|
||||||
if (!v)
|
|
||||||
return config_error_nonbool(k);
|
|
||||||
else if (!strcmp(v, "no"))
|
|
||||||
show_untracked_files = SHOW_NO_UNTRACKED_FILES;
|
|
||||||
else if (!strcmp(v, "normal"))
|
|
||||||
show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
|
|
||||||
else if (!strcmp(v, "all"))
|
|
||||||
show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
|
|
||||||
else
|
|
||||||
return error("Invalid untracked files mode '%s'", v);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return git_diff_ui_config(k, v, cb);
|
|
||||||
}
|
|
||||||
|
26
wt-status.h
26
wt-status.h
@ -2,13 +2,16 @@
|
|||||||
#define STATUS_H
|
#define STATUS_H
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "string-list.h"
|
||||||
|
#include "color.h"
|
||||||
|
|
||||||
enum color_wt_status {
|
enum color_wt_status {
|
||||||
WT_STATUS_HEADER,
|
WT_STATUS_HEADER = 0,
|
||||||
WT_STATUS_UPDATED,
|
WT_STATUS_UPDATED,
|
||||||
WT_STATUS_CHANGED,
|
WT_STATUS_CHANGED,
|
||||||
WT_STATUS_UNTRACKED,
|
WT_STATUS_UNTRACKED,
|
||||||
WT_STATUS_NOBRANCH,
|
WT_STATUS_NOBRANCH,
|
||||||
|
WT_STATUS_UNMERGED,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum untracked_status_type {
|
enum untracked_status_type {
|
||||||
@ -16,7 +19,13 @@ enum untracked_status_type {
|
|||||||
SHOW_NORMAL_UNTRACKED_FILES,
|
SHOW_NORMAL_UNTRACKED_FILES,
|
||||||
SHOW_ALL_UNTRACKED_FILES
|
SHOW_ALL_UNTRACKED_FILES
|
||||||
};
|
};
|
||||||
extern enum untracked_status_type show_untracked_files;
|
|
||||||
|
struct wt_status_change_data {
|
||||||
|
int worktree_status;
|
||||||
|
int index_status;
|
||||||
|
int stagemask;
|
||||||
|
char *head_path;
|
||||||
|
};
|
||||||
|
|
||||||
struct wt_status {
|
struct wt_status {
|
||||||
int is_initial;
|
int is_initial;
|
||||||
@ -24,8 +33,13 @@ struct wt_status {
|
|||||||
const char *reference;
|
const char *reference;
|
||||||
int verbose;
|
int verbose;
|
||||||
int amend;
|
int amend;
|
||||||
int untracked;
|
|
||||||
int nowarn;
|
int nowarn;
|
||||||
|
int use_color;
|
||||||
|
int relative_paths;
|
||||||
|
int submodule_summary;
|
||||||
|
enum untracked_status_type show_untracked_files;
|
||||||
|
char color_palette[WT_STATUS_UNMERGED+1][COLOR_MAXLEN];
|
||||||
|
|
||||||
/* These are computed during processing of the individual sections */
|
/* These are computed during processing of the individual sections */
|
||||||
int commitable;
|
int commitable;
|
||||||
int workdir_dirty;
|
int workdir_dirty;
|
||||||
@ -33,12 +47,12 @@ struct wt_status {
|
|||||||
const char *index_file;
|
const char *index_file;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
const char *prefix;
|
const char *prefix;
|
||||||
|
struct string_list change;
|
||||||
|
struct string_list untracked;
|
||||||
};
|
};
|
||||||
|
|
||||||
int git_status_config(const char *var, const char *value, void *cb);
|
|
||||||
extern int wt_status_use_color;
|
|
||||||
extern int wt_status_relative_paths;
|
|
||||||
void wt_status_prepare(struct wt_status *s);
|
void wt_status_prepare(struct wt_status *s);
|
||||||
void wt_status_print(struct wt_status *s);
|
void wt_status_print(struct wt_status *s);
|
||||||
|
void wt_status_collect(struct wt_status *s);
|
||||||
|
|
||||||
#endif /* STATUS_H */
|
#endif /* STATUS_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user