From: Junio C Hamano Date: Mon, 28 Dec 2009 07:01:32 +0000 (-0800) Subject: Merge branch 'jk/1.7.0-status' X-Git-Tag: v1.7.0-rc0~137 X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=c2ff10c98e22ae64d553273e6d67bb123a1c916f;hp=-c;p=git.git Merge branch 'jk/1.7.0-status' * jk/1.7.0-status: status/commit: do not suggest "reset HEAD " while merging commit/status: "git add " is not necessarily how to resolve commit/status: check $GIT_DIR/MERGE_HEAD only once t7508-status: test all modes with color t7508-status: status --porcelain ignores relative paths setting status: reduce duplicated setup code status: disable color for porcelain format status -s: obey color.status builtin-commit: refactor short-status code into wt-status.c t7508-status.sh: Add tests for status -s status -s: respect the status.relativePaths option docs: note that status configuration affects only long format commit: support alternate status formats status: add --porcelain output format status: refactor format option parsing status: refactor short-mode printing to its own function status: typo fix in usage git status: not "commit --dry-run" anymore git stat -s: short status output git stat: the beginning of "status that is not a dry-run of commit" Conflicts: t/t4034-diff-words.sh wt-status.c --- c2ff10c98e22ae64d553273e6d67bb123a1c916f diff --combined Documentation/git-commit.txt index d227cec9b,c45fbe4f9..c97c151ae --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@@ -9,7 -9,7 +9,7 @@@ SYNOPSI -------- [verse] 'git commit' [-a | --interactive] [-s] [-v] [-u] [--amend] [--dry-run] - [(-c | -C) ] [-F | -m ] [--dry-run] + [(-c | -C) ] [-F | -m ] [--reset-author] [--allow-empty] [--no-verify] [-e] [--author=] [--cleanup=] [--] [[-i | -o ]...] @@@ -69,11 -69,26 +69,25 @@@ OPTION Like '-C', but with '-c' the editor is invoked, so that 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. +--reset-author:: + When used with -C/-c/--amend options, declare that the + authorship of the resulting commit now belongs of the committer. + This also renews the author timestamp. + --short:: + When doing a dry-run, give the output in the short-format. See + linkgit:git-status[1] for details. Implies `--dry-run`. + + --porcelain:: + When doing a dry-run, give the output in a porcelain-ready + format. See linkgit:git-status[1] for details. Implies + `--dry-run`. + + -z:: + When showing `short` or `porcelain` status output, terminate + entries in the status output with NUL, instead of LF. If no + format is given, implies the `--porcelain` output format. + -F :: --file=:: Take the commit message from the given file. Use '-' to @@@ -328,7 -343,7 +342,7 @@@ ENVIRONMENT AND CONFIGURATION VARIABLE The editor used to edit the commit log message will be chosen from the GIT_EDITOR environment variable, the core.editor configuration variable, the VISUAL environment variable, or the EDITOR environment variable (in that -order). +order). See linkgit:git-var[1] for details. HOOKS ----- diff --combined builtin-commit.c index f54772f74,7218454d1..6822aa07c --- a/builtin-commit.c +++ b/builtin-commit.c @@@ -24,6 -24,7 +24,7 @@@ #include "string-list.h" #include "rerere.h" #include "unpack-trees.h" + #include "quote.h" static const char * const builtin_commit_usage[] = { "git commit [options] [--] ...", @@@ -35,7 -36,7 +36,7 @@@ static const char * const builtin_statu NULL }; - static unsigned char head_sha1[20], merge_head_sha1[20]; + static unsigned char head_sha1[20]; static char *use_message_buffer; static const char commit_editmsg[] = "COMMIT_EDITMSG"; static struct lock_file index_lock; /* real index */ @@@ -51,7 -52,7 +52,7 @@@ static const char *template_file static char *edit_message, *use_message; static char *author_name, *author_email, *author_date; static int all, edit_flag, also, interactive, only, amend, signoff; -static int quiet, verbose, no_verify, allow_empty, dry_run; +static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static char *untracked_files_arg; /* * The default commit message cleanup mode will remove the lines @@@ -71,6 -72,13 +72,13 @@@ static int use_editor = 1, initial_comm static const char *only_include_assumed; static struct strbuf message; + static int null_termination; + static enum { + STATUS_FORMAT_LONG, + STATUS_FORMAT_SHORT, + STATUS_FORMAT_PORCELAIN, + } status_format = STATUS_FORMAT_LONG; + static int opt_parse_m(const struct option *opt, const char *arg, int unset) { struct strbuf *buf = opt->value; @@@ -91,9 -99,8 +99,9 @@@ static struct option builtin_commit_opt OPT_FILENAME('F', "file", &logfile, "read log from file"), OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"), OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m), - OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "), + OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit"), OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"), + OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C-c/--amend)"), OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"), OPT_FILENAME('t', "template", &template_file, "use specified template file"), OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"), @@@ -105,6 -112,12 +113,12 @@@ OPT_BOOLEAN('o', "only", &only, "commit only specified files"), OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"), OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"), + OPT_SET_INT(0, "short", &status_format, "show status concisely", + STATUS_FORMAT_SHORT), + OPT_SET_INT(0, "porcelain", &status_format, + "show porcelain output format", STATUS_FORMAT_PORCELAIN), + OPT_BOOLEAN('z', "null", &null_termination, + "terminate entries with NUL"), 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" }, OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"), @@@ -306,7 -319,7 +320,7 @@@ static char *prepare_index(int argc, co */ commit_style = COMMIT_PARTIAL; - if (file_exists(git_path("MERGE_HEAD"))) + if (in_merge) die("cannot do a partial commit during a merge."); memset(&partial, 0, sizeof(partial)); @@@ -347,6 -360,8 +361,8 @@@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn, struct wt_status *s) { + unsigned char sha1[20]; + if (s->relative_paths) s->prefix = prefix; @@@ -358,8 -373,21 +374,21 @@@ s->index_file = index_file; s->fp = fp; s->nowarn = nowarn; + s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; - wt_status_print(s); + wt_status_collect(s); + + switch (status_format) { + case STATUS_FORMAT_SHORT: + wt_shortstatus_print(s, null_termination); + break; + case STATUS_FORMAT_PORCELAIN: + wt_porcelain_print(s, null_termination); + break; + case STATUS_FORMAT_LONG: + wt_status_print(s); + break; + } return s->commitable; } @@@ -382,7 -410,7 +411,7 @@@ static void determine_author_info(void email = getenv("GIT_AUTHOR_EMAIL"); date = getenv("GIT_AUTHOR_DATE"); - if (use_message) { + if (use_message && !renew_authorship) { const char *a, *lb, *rb, *eol; a = strstr(use_message_buffer, "\nauthor "); @@@ -415,47 -443,6 +444,47 @@@ author_date = date; } +static int ends_rfc2822_footer(struct strbuf *sb) +{ + int ch; + int hit = 0; + int i, j, k; + int len = sb->len; + int first = 1; + const char *buf = sb->buf; + + for (i = len - 1; i > 0; i--) { + if (hit && buf[i] == '\n') + break; + hit = (buf[i] == '\n'); + } + + while (i < len - 1 && buf[i] == '\n') + i++; + + for (; i < len; i = k) { + for (k = i; k < len && buf[k] != '\n'; k++) + ; /* do nothing */ + k++; + + if ((buf[k] == ' ' || buf[k] == '\t') && !first) + continue; + + first = 0; + + for (j = 0; i + j < len; j++) { + ch = buf[i + j]; + if (ch == ':') + break; + if (isalnum(ch) || + (ch == '-')) + continue; + return 0; + } + } + return 1; +} + static int prepare_to_commit(const char *index_file, const char *prefix, struct wt_status *s) { @@@ -531,7 -518,7 +560,7 @@@ for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--) ; /* do nothing */ if (prefixcmp(sb.buf + i, sob.buf)) { - if (prefixcmp(sb.buf + i, sign_off_header)) + if (!i || !ends_rfc2822_footer(&sb)) strbuf_addch(&sb, '\n'); strbuf_addbuf(&sb, &sob); } @@@ -726,15 -713,28 +755,30 @@@ static const char *find_author_by_nickn prepare_revision_walk(&revs); commit = get_revision(&revs); if (commit) { + struct pretty_print_context ctx = {0}; + ctx.date_mode = DATE_NORMAL; strbuf_release(&buf); - format_commit_message(commit, "%an <%ae>", &buf, DATE_NORMAL); + format_commit_message(commit, "%an <%ae>", &buf, &ctx); return strbuf_detach(&buf, NULL); } die("No existing author found with '%s'", name); } + + static void handle_untracked_files_arg(struct wt_status *s) + { + if (!untracked_files_arg) + ; /* default already initialized */ + else if (!strcmp(untracked_files_arg, "no")) + s->show_untracked_files = SHOW_NO_UNTRACKED_FILES; + else if (!strcmp(untracked_files_arg, "normal")) + s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; + else if (!strcmp(untracked_files_arg, "all")) + s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES; + else + die("Invalid untracked files mode '%s'", untracked_files_arg); + } + static int parse_and_validate_options(int argc, const char *argv[], const char * const usage[], const char *prefix, @@@ -748,9 -748,6 +792,9 @@@ if (force_author && !strchr(force_author, '>')) force_author = find_author_by_nickname(force_author); + if (force_author && renew_authorship) + die("Using both --reset-author and --author does not make sense"); + if (logfile || message.len || use_message) use_editor = 0; if (edit_flag) @@@ -761,9 -758,6 +805,6 @@@ if (get_sha1("HEAD", head_sha1)) initial_commit = 1; - if (!get_sha1("MERGE_HEAD", merge_head_sha1)) - in_merge = 1; - /* Sanity check options */ if (amend && initial_commit) die("You have nothing to amend."); @@@ -784,8 -778,6 +825,8 @@@ use_message = edit_message; if (amend && !use_message) use_message = "HEAD"; + if (!use_message && renew_authorship) + die("--reset-author can be used only with -C, -c or --amend."); if (use_message) { unsigned char sha1[20]; static char utf8[] = "UTF-8"; @@@ -843,22 -835,18 +884,18 @@@ else die("Invalid cleanup mode %s", cleanup_arg); - if (!untracked_files_arg) - ; /* default already initialized */ - else if (!strcmp(untracked_files_arg, "no")) - s->show_untracked_files = SHOW_NO_UNTRACKED_FILES; - else if (!strcmp(untracked_files_arg, "normal")) - s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; - else if (!strcmp(untracked_files_arg, "all")) - s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES; - else - die("Invalid untracked files mode '%s'", untracked_files_arg); + handle_untracked_files_arg(s); if (all && argc > 0) die("Paths with -a does not make sense."); else if (interactive && argc > 0) die("Paths with --interactive does not make sense."); + if (null_termination && status_format == STATUS_FORMAT_LONG) + status_format = STATUS_FORMAT_PORCELAIN; + if (status_format != STATUS_FORMAT_LONG) + dry_run = 1; + return argc; } @@@ -890,7 -878,7 +927,7 @@@ static int parse_status_slot(const cha return WT_STATUS_NOBRANCH; if (!strcasecmp(var+offset, "unmerged")) return WT_STATUS_UNMERGED; - die("bad config variable '%s'", var); + return -1; } static int git_status_config(const char *k, const char *v, void *cb) @@@ -910,8 -898,6 +947,8 @@@ } if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) { int slot = parse_status_slot(k, 13); + if (slot < 0) + return 0; if (!v) return config_error_nonbool(k); color_parse(v, k, s->color_palette[slot]); @@@ -940,17 -926,63 +977,63 @@@ int cmd_status(int argc, const char **argv, const char *prefix) { struct wt_status s; + unsigned char sha1[20]; + static struct option builtin_status_options[] = { + OPT__VERBOSE(&verbose), + OPT_SET_INT('s', "short", &status_format, + "show status concisely", STATUS_FORMAT_SHORT), + OPT_SET_INT(0, "porcelain", &status_format, + "show porcelain output format", + STATUS_FORMAT_PORCELAIN), + OPT_BOOLEAN('z', "null", &null_termination, + "terminate entries with NUL"), + { 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_END(), + }; + + if (null_termination && status_format == STATUS_FORMAT_LONG) + status_format = STATUS_FORMAT_PORCELAIN; wt_status_prepare(&s); git_config(git_status_config, &s); + in_merge = file_exists(git_path("MERGE_HEAD")); + argc = parse_options(argc, argv, prefix, + builtin_status_options, + builtin_status_usage, 0); + handle_untracked_files_arg(&s); + + if (*argv) + s.pathspec = get_pathspec(prefix, argv); + + read_cache(); + refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED); + s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0; + s.in_merge = in_merge; + wt_status_collect(&s); + + if (s.relative_paths) + s.prefix = prefix; 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); + switch (status_format) { + case STATUS_FORMAT_SHORT: + wt_shortstatus_print(&s, null_termination); + break; + case STATUS_FORMAT_PORCELAIN: + wt_porcelain_print(&s, null_termination); + break; + case STATUS_FORMAT_LONG: + s.verbose = verbose; + wt_status_print(&s); + break; + } + return 0; } static void print_summary(const char *prefix, const unsigned char *sha1) @@@ -993,10 -1025,8 +1076,10 @@@ initial_commit ? " (root-commit)" : ""); if (!log_tree_commit(&rev, commit)) { + struct pretty_print_context ctx = {0}; struct strbuf buf = STRBUF_INIT; - format_commit_message(commit, format + 7, &buf, DATE_NORMAL); + ctx.date_mode = DATE_NORMAL; + format_commit_message(commit, format + 7, &buf, &ctx); printf("%s\n", buf.buf); strbuf_release(&buf); } @@@ -1007,7 -1037,7 +1090,7 @@@ static int git_commit_config(const cha struct wt_status *s = cb; if (!strcmp(k, "commit.template")) - return git_config_string(&template_file, k, v); + return git_config_pathname(&template_file, k, v); return git_status_config(k, v, s); } @@@ -1026,10 -1056,11 +1109,11 @@@ int cmd_commit(int argc, const char **a wt_status_prepare(&s); git_config(git_commit_config, &s); + in_merge = file_exists(git_path("MERGE_HEAD")); + s.in_merge = in_merge; if (s.use_color == -1) s.use_color = git_use_color_default; - argc = parse_and_validate_options(argc, argv, builtin_commit_usage, prefix, &s); if (dry_run) { diff --combined t/t4034-diff-words.sh index 1c21276c5,17621dd5a..2e2e103b3 --- a/t/t4034-diff-words.sh +++ b/t/t4034-diff-words.sh @@@ -8,23 -8,12 +8,13 @@@ test_expect_success setup git config diff.color.old red git config diff.color.new green + git config diff.color.func magenta ' - decrypt_color () { - sed \ - -e 's/.\[1m//g' \ - -e 's/.\[31m//g' \ - -e 's/.\[32m//g' \ - -e 's/.\[35m//g' \ - -e 's/.\[36m//g' \ - -e 's/.\[m//g' - } - word_diff () { test_must_fail git diff --no-index "$@" pre post > output && - decrypt_color < output > output.decrypted && + test_decode_color output.decrypted && test_cmp expect output.decrypted } @@@ -49,9 -38,9 +39,9 @@@ cat > expect <<\EO index 330b04f..5ed8eff 100644 --- a/pre +++ b/post - @@ -1,3 +1,7 @@ + @@ -1,3 +1,7 @@ h(4)h(4),hh[44] - + a = b + c aa = a @@@ -65,34 -54,14 +55,34 @@@ test_expect_success 'word diff with run ' +cat > expect <<\EOF +diff --git a/pre b/post +index 330b04f..5ed8eff 100644 +--- a/pre ++++ b/post - @@ -1 +1 @@ ++@@ -1 +1 @@ +h(4)h(4),hh[44] - @@ -3,0 +4,4 @@ a = b + c ++@@ -3,0 +4,4 @@ a = b + c + +aa = a + +aeff = aeff * ( aaa ) +EOF + +test_expect_success 'word diff without context' ' + + word_diff --color-words --unified=0 + +' + cat > expect <<\EOF diff --git a/pre b/post index 330b04f..5ed8eff 100644 --- a/pre +++ b/post - @@ -1,3 +1,7 @@ + @@ -1,3 +1,7 @@ h(4),hh[44] - + a = b + c aa = a @@@ -126,9 -95,9 +116,9 @@@ cat > expect <<\EO index 330b04f..5ed8eff 100644 --- a/pre +++ b/post - @@ -1,3 +1,7 @@ + @@ -1,3 +1,7 @@ h(4),hh[44] - + a = b + c aa = a @@@ -168,9 -137,9 +158,9 @@@ cat > expect <<\EO index 330b04f..5ed8eff 100644 --- a/pre +++ b/post - @@ -1,3 +1,7 @@ + @@ -1,3 +1,7 @@ h(4),hh[44] - + a = b + c aa = a @@@ -190,7 -159,7 +180,7 @@@ cat > expect <<\EO index c29453b..be22f37 100644 --- a/pre +++ b/post - @@ -1 +1 @@ + @@ -1 +1 @@ aaa (aaa) aaa EOF @@@ -209,7 -178,7 +199,7 @@@ cat > expect <<\EO index 289cb9d..2d06f37 100644 --- a/pre +++ b/post - @@ -1 +1 @@ + @@ -1 +1 @@ (: EOF diff --combined t/test-lib.sh index 2d523fe0f,d63ad2d87..142f36f2e --- a/t/test-lib.sh +++ b/t/test-lib.sh @@@ -30,7 -30,7 +30,7 @@@ TZ=UT TERM=dumb export LANG LC_ALL PAGER TERM TZ EDITOR=: -VISUAL=: +unset VISUAL unset GIT_EDITOR unset AUTHOR_DATE unset AUTHOR_EMAIL @@@ -58,7 -58,7 +58,7 @@@ GIT_MERGE_VERBOSITY= export GIT_MERGE_VERBOSITY export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME -export EDITOR VISUAL +export EDITOR GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u} # Protect ourselves from common misconfiguration to export @@@ -114,9 -114,6 +114,9 @@@ d valgrind=t; verbose=t; shift ;; --tee) shift ;; # was handled already + --root=*) + root=$(expr "z$1" : 'z[^=]*=\(.*\)') + shift ;; *) echo "error: unknown test option '$1'" >&2; exit 1 ;; esac @@@ -207,10 -204,21 +207,21 @@@ trap 'die' EXI test_set_editor () { FAKE_EDITOR="$1" export FAKE_EDITOR - VISUAL='"$FAKE_EDITOR"' - export VISUAL + EDITOR='"$FAKE_EDITOR"' + export EDITOR } + test_decode_color () { + sed -e 's/.\[1m//g' \ + -e 's/.\[31m//g' \ + -e 's/.\[32m//g' \ + -e 's/.\[33m//g' \ + -e 's/.\[34m//g' \ + -e 's/.\[35m//g' \ + -e 's/.\[36m//g' \ + -e 's/.\[m//g' + } + test_tick () { if test -z "${test_tick+set}" then @@@ -632,37 -640,23 +643,37 @@@ GIT_CONFIG_NOSYSTEM= GIT_CONFIG_NOGLOBAL=1 export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOBAL +. ../GIT-BUILD-OPTIONS + GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git export GITPERLLIB test -d ../templates/blt || { error "You haven't built things yet, have you?" } +if test -z "$GIT_TEST_INSTALLED" && test -z "$NO_PYTHON" +then + GITPYTHONLIB="$(pwd)/../git_remote_helpers/build/lib" + export GITPYTHONLIB + test -d ../git_remote_helpers/build || { + error "You haven't built git_remote_helpers yet, have you?" + } +fi + if ! test -x ../test-chmtime; then echo >&2 'You need to build test-chmtime:' echo >&2 'Run "make test-chmtime" in the source (toplevel) directory' exit 1 fi -. ../GIT-BUILD-OPTIONS - # Test repository test="trash directory.$(basename "$0" .sh)" -test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test" +test -n "$root" && test="$root/$test" +case "$test" in +/*) TRASH_DIRECTORY="$test" ;; + *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;; +esac +test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY rm -fr "$test" || { GIT_EXIT_OK=t echo >&5 "FATAL: Cannot prepare test area" @@@ -694,21 -688,6 +705,21 @@@ d esac done +# Provide an implementation of the 'yes' utility +yes () { + if test $# = 0 + then + y=y + else + y="$*" + fi + + while echo "$y" + do + : + done +} + # Fix some commands on Windows case $(uname -s) in *MINGW*) @@@ -738,7 -717,6 +749,7 @@@ esac test -z "$NO_PERL" && test_set_prereq PERL +test -z "$NO_PYTHON" && test_set_prereq PYTHON # test whether the filesystem supports symbolic links ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS diff --combined wt-status.c index 38eb24536,c4589055b..5d5698801 --- a/wt-status.c +++ b/wt-status.c @@@ -47,28 -47,29 +47,33 @@@ void wt_status_prepare(struct wt_statu 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 (!advice_status_hints) + return; - if (!s->is_initial) + if (s->in_merge) + ; + else if (!s->is_initial) color_fprintf_ln(s->fp, c, "# (use \"git reset %s ...\" to unstage)", s->reference); else color_fprintf_ln(s->fp, c, "# (use \"git rm --cached ...\" to unstage)"); - color_fprintf_ln(s->fp, c, "# (use \"git add ...\" to mark resolution)"); + color_fprintf_ln(s->fp, c, "# (use \"git add/rm ...\" as appropriate to mark resolution)"); color_fprintf_ln(s->fp, c, "#"); } static void wt_status_print_cached_header(struct wt_status *s) { const char *c = color(WT_STATUS_HEADER, s); + color_fprintf_ln(s->fp, c, "# Changes to be committed:"); + if (!advice_status_hints) + return; - if (!s->is_initial) { + if (s->in_merge) + ; /* NEEDSWORK: use "git reset --unresolve"??? */ + else if (!s->is_initial) color_fprintf_ln(s->fp, c, "# (use \"git reset %s ...\" to unstage)", s->reference); - } else { + else color_fprintf_ln(s->fp, c, "# (use \"git rm --cached ...\" to unstage)"); - } color_fprintf_ln(s->fp, c, "#"); } @@@ -76,9 -77,8 +81,10 @@@ static void wt_status_print_dirty_heade int has_deleted) { const char *c = color(WT_STATUS_HEADER, s); + color_fprintf_ln(s->fp, c, "# Changed but not updated:"); + if (!advice_status_hints) + return; if (!has_deleted) color_fprintf_ln(s->fp, c, "# (use \"git add ...\" to update what will be committed)"); else @@@ -91,8 -91,6 +97,8 @@@ static void wt_status_print_untracked_h { const char *c = color(WT_STATUS_HEADER, s); color_fprintf_ln(s->fp, c, "# Untracked files:"); + if (!advice_status_hints) + return; color_fprintf_ln(s->fp, c, "# (use \"git add ...\" to include in what will be committed)"); color_fprintf_ln(s->fp, c, "#"); } @@@ -277,6 -275,7 +283,7 @@@ static void wt_status_collect_changes_w rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = wt_status_collect_changed_cb; rev.diffopt.format_callback_data = s; + rev.prune_data = s->pathspec; run_diff_files(&rev, 0); } @@@ -293,6 -292,7 +300,7 @@@ static void wt_status_collect_changes_i rev.diffopt.detect_rename = 1; rev.diffopt.rename_limit = 200; rev.diffopt.break_opt = 0; + rev.prune_data = s->pathspec; run_diff_index(&rev, 1); } @@@ -305,6 -305,8 +313,8 @@@ static void wt_status_collect_changes_i struct wt_status_change_data *d; struct cache_entry *ce = active_cache[i]; + if (!ce_path_match(ce, s->pathspec)) + continue; it = string_list_insert(ce->name, &s->change); d = it->util; if (!d) { @@@ -334,10 -336,12 +344,12 @@@ static void wt_status_collect_untracked setup_standard_excludes(&dir); fill_directory(&dir, NULL); - for(i = 0; i < dir.nr; i++) { + for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; if (!cache_name_is_other(ent->name, ent->len)) continue; + if (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) + continue; s->workdir_untracked = 1; string_list_insert(ent->name, &s->untracked); } @@@ -541,10 -545,8 +553,8 @@@ static void wt_status_print_tracking(st void wt_status_print(struct wt_status *s) { - unsigned char sha1[20]; const char *branch_color = color(WT_STATUS_HEADER, s); - s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; if (s->branch) { const char *on_what = "On branch "; const char *branch_name = s->branch; @@@ -561,16 -563,14 +571,14 @@@ wt_status_print_tracking(s); } - wt_status_collect(s); - if (s->is_initial) { color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit"); color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); } - wt_status_print_unmerged(s); wt_status_print_updated(s); + wt_status_print_unmerged(s); wt_status_print_changed(s); if (s->submodule_summary) wt_status_print_submodule_summary(s); @@@ -598,3 -598,107 +606,107 @@@ printf("nothing to commit (working directory clean)\n"); } } + + static void wt_shortstatus_unmerged(int null_termination, struct string_list_item *it, + struct wt_status *s) + { + struct wt_status_change_data *d = it->util; + const char *how = "??"; + + switch (d->stagemask) { + case 1: how = "DD"; break; /* both deleted */ + case 2: how = "AU"; break; /* added by us */ + case 3: how = "UD"; break; /* deleted by them */ + case 4: how = "UA"; break; /* added by them */ + case 5: how = "DU"; break; /* deleted by us */ + case 6: how = "AA"; break; /* both added */ + case 7: how = "UU"; break; /* both modified */ + } + color_fprintf(s->fp, color(WT_STATUS_UNMERGED, s), "%s", how); + if (null_termination) { + fprintf(stdout, " %s%c", it->string, 0); + } else { + struct strbuf onebuf = STRBUF_INIT; + const char *one; + one = quote_path(it->string, -1, &onebuf, s->prefix); + printf(" %s\n", one); + strbuf_release(&onebuf); + } + } + + static void wt_shortstatus_status(int null_termination, struct string_list_item *it, + struct wt_status *s) + { + struct wt_status_change_data *d = it->util; + + if (d->index_status) + color_fprintf(s->fp, color(WT_STATUS_UPDATED, s), "%c", d->index_status); + else + putchar(' '); + if (d->worktree_status) + color_fprintf(s->fp, color(WT_STATUS_CHANGED, s), "%c", d->worktree_status); + else + putchar(' '); + putchar(' '); + if (null_termination) { + fprintf(stdout, "%s%c", it->string, 0); + if (d->head_path) + fprintf(stdout, "%s%c", d->head_path, 0); + } else { + struct strbuf onebuf = STRBUF_INIT; + const char *one; + if (d->head_path) { + one = quote_path(d->head_path, -1, &onebuf, s->prefix); + printf("%s -> ", one); + strbuf_release(&onebuf); + } + one = quote_path(it->string, -1, &onebuf, s->prefix); + printf("%s\n", one); + strbuf_release(&onebuf); + } + } + + static void wt_shortstatus_untracked(int null_termination, struct string_list_item *it, + struct wt_status *s) + { + if (null_termination) { + fprintf(stdout, "?? %s%c", it->string, 0); + } else { + struct strbuf onebuf = STRBUF_INIT; + const char *one; + one = quote_path(it->string, -1, &onebuf, s->prefix); + color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "??"); + printf(" %s\n", one); + strbuf_release(&onebuf); + } + } + + void wt_shortstatus_print(struct wt_status *s, int null_termination) + { + 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) + wt_shortstatus_unmerged(null_termination, it, s); + else + wt_shortstatus_status(null_termination, it, s); + } + for (i = 0; i < s->untracked.nr; i++) { + struct string_list_item *it; + + it = &(s->untracked.items[i]); + wt_shortstatus_untracked(null_termination, it, s); + } + } + + void wt_porcelain_print(struct wt_status *s, int null_termination) + { + s->use_color = 0; + s->relative_paths = 0; + s->prefix = NULL; + wt_shortstatus_print(s, null_termination); + }