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>::
--file=<file>::
Take the commit message from the given file. Use '-' to
SYNOPSIS
--------
-'git status' <options>...
+'git status' [<options>...] [--] [<pathspec>...]
DESCRIPTION
-----------
third are what you _could_ commit by running 'git-add' before running
`git commit`.
-The command takes the same set of options as 'git-commit'; it
-shows what would be committed if the same options are given to
-'git-commit'.
-
-If there is no path that is different between the index file and
-the current HEAD commit (i.e., there is nothing to commit by running
-`git commit`), the command exits with non-zero status.
+OPTIONS
+-------
+
+-s::
+--short::
+ Give the output in the short-format.
+
+--porcelain::
+ Give the output in a stable, easy-to-parse format for scripts.
+ Currently this is identical to --short output, but is guaranteed
+ not to change in the future, making it safe for scripts.
+
+-u[<mode>]::
+--untracked-files[=<mode>]::
+ Show untracked files (Default: 'all').
++
+The mode parameter is optional, and is used to specify
+the handling of untracked files. The possible options are:
++
+--
+ - 'no' - Show no untracked files
+ - 'normal' - Shows untracked files and directories
+ - 'all' - Also shows individual files in untracked directories.
+--
++
+See linkgit:git-config[1] for configuration variable
+used to change the default for when the option is not
+specified.
+
+-z::
+ Terminate entries with NUL, instead of LF. This implies
+ the `--porcelain` output format if no other format is given.
OUTPUT
------
The output from this command is designed to be used as a commit
template comment, and all the output lines are prefixed with '#'.
+The default, long format, is designed to be human readable,
+verbose and descriptive. They are subject to change in any time.
The paths mentioned in the output, unlike many other git commands, are
made relative to the current directory if you are working in a
subdirectory (this is on purpose, to help cutting and pasting). See
the status.relativePaths config option below.
+In short-format, the status of each path is shown as
+
+ XY PATH1 -> PATH2
+
+where `PATH1` is the path in the `HEAD`, and ` -> PATH2` part is
+shown only when `PATH1` corresponds to a different path in the
+index/worktree (i.e. renamed).
+
+For unmerged entries, `X` shows the status of stage #2 (i.e. ours) and `Y`
+shows the status of stage #3 (i.e. theirs).
+
+For entries that do not have conflicts, `X` shows the status of the index,
+and `Y` shows the status of the work tree. For untracked paths, `XY` are
+`??`.
+
+ X Y Meaning
+ -------------------------------------------------
+ [MD] not updated
+ M [ MD] updated in index
+ A [ MD] added to index
+ D [ MD] deleted from index
+ R [ MD] renamed in index
+ C [ MD] copied in index
+ [MARC] index and work tree matches
+ [ MARC] M work tree changed since index
+ [ MARC] D deleted in work tree
+ -------------------------------------------------
+ D D unmerged, both deleted
+ A U unmerged, added by us
+ U D unmerged, deleted by them
+ U A unmerged, added by them
+ D U unmerged, deleted by us
+ A A unmerged, both added
+ U U unmerged, both modified
+ -------------------------------------------------
+ ? ? untracked
+ -------------------------------------------------
+
CONFIGURATION
-------------
directory.
If `status.submodulesummary` is set to a non zero number or true (identical
-to -1 or an unlimited number), the submodule summary will be enabled and a
-summary of commits for modified submodules will be shown (see --summary-limit
-option of linkgit:git-submodule[1]).
+to -1 or an unlimited number), the submodule summary will be enabled for
+the long format and a summary of commits for modified submodules will be
+shown (see --summary-limit option of linkgit:git-submodule[1]).
SEE ALSO
--------
Author
------
-Written by Linus Torvalds <torvalds@osdl.org> and
-Junio C Hamano <gitster@pobox.com>.
+Written by Junio C Hamano <gitster@pobox.com>.
Documentation
--------------
#include "string-list.h"
#include "rerere.h"
#include "unpack-trees.h"
+#include "quote.h"
static const char * const builtin_commit_usage[] = {
"git commit [options] [--] <filepattern>...",
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 */
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;
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"),
*/
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));
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;
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;
}
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,
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.");
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;
}
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)
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) {
'
-decrypt_color () {
- sed \
- -e 's/.\[1m/<WHITE>/g' \
- -e 's/.\[31m/<RED>/g' \
- -e 's/.\[32m/<GREEN>/g' \
- -e 's/.\[35m/<MAGENTA>/g' \
- -e 's/.\[36m/<BROWN>/g' \
- -e 's/.\[m/<RESET>/g'
-}
-
word_diff () {
test_must_fail git diff --no-index "$@" pre post > output &&
- decrypt_color < output > output.decrypted &&
+ test_decode_color <output >output.decrypted &&
test_cmp expect output.decrypted
}
<WHITE>index 330b04f..5ed8eff 100644<RESET>
<WHITE>--- a/pre<RESET>
<WHITE>+++ b/post<RESET>
-<BROWN>@@ -1,3 +1,7 @@<RESET>
+<CYAN>@@ -1,3 +1,7 @@<RESET>
<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
a = b + c<RESET>
<WHITE>index 330b04f..5ed8eff 100644<RESET>
<WHITE>--- a/pre<RESET>
<WHITE>+++ b/post<RESET>
-<BROWN>@@ -1 +1 @@<RESET>
+<CYAN>@@ -1 +1 @@<RESET>
<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
-<BROWN>@@ -3,0 +4,4 @@<RESET> <RESET><MAGENTA>a = b + c<RESET>
+<CYAN>@@ -3,0 +4,4 @@<RESET> <RESET><MAGENTA>a = b + c<RESET>
<GREEN>aa = a<RESET>
<WHITE>index 330b04f..5ed8eff 100644<RESET>
<WHITE>--- a/pre<RESET>
<WHITE>+++ b/post<RESET>
-<BROWN>@@ -1,3 +1,7 @@<RESET>
+<CYAN>@@ -1,3 +1,7 @@<RESET>
h(4),<GREEN>hh<RESET>[44]
a = b + c<RESET>
<WHITE>index 330b04f..5ed8eff 100644<RESET>
<WHITE>--- a/pre<RESET>
<WHITE>+++ b/post<RESET>
-<BROWN>@@ -1,3 +1,7 @@<RESET>
+<CYAN>@@ -1,3 +1,7 @@<RESET>
h(4)<GREEN>,hh[44]<RESET>
a = b + c<RESET>
<WHITE>index 330b04f..5ed8eff 100644<RESET>
<WHITE>--- a/pre<RESET>
<WHITE>+++ b/post<RESET>
-<BROWN>@@ -1,3 +1,7 @@<RESET>
+<CYAN>@@ -1,3 +1,7 @@<RESET>
h(4),<GREEN>hh[44<RESET>]
a = b + c<RESET>
<WHITE>index c29453b..be22f37 100644<RESET>
<WHITE>--- a/pre<RESET>
<WHITE>+++ b/post<RESET>
-<BROWN>@@ -1 +1 @@<RESET>
+<CYAN>@@ -1 +1 @@<RESET>
aaa (aaa) <GREEN>aaa<RESET>
EOF
<WHITE>index 289cb9d..2d06f37 100644<RESET>
<WHITE>--- a/pre<RESET>
<WHITE>+++ b/post<RESET>
-<BROWN>@@ -1 +1 @@<RESET>
+<CYAN>@@ -1 +1 @@<RESET>
(<RED>:<RESET>
EOF
cd test &&
git checkout b1 >/dev/null &&
# reports nothing to commit
- test_must_fail git status
+ test_must_fail git commit --dry-run
) >actual &&
grep "have 1 and 1 different" actual
'
cat >expect <<EOF
# On branch side
# Unmerged paths:
-# (use "git reset HEAD <file>..." to unstage)
-# (use "git add <file>..." to mark resolution)
+# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# deleted by us: foo
#
git rm foo &&
git commit -m delete &&
test_must_fail git merge master &&
- test_must_fail git status > ../actual
- ) &&
- test_cmp expect actual
+ test_must_fail git commit --dry-run >../actual &&
+ test_cmp ../expect ../actual &&
+ git status >../actual &&
+ test_cmp ../expect ../actual
+ )
'
test_done
git status |
grep "nothing to commit"
'
-test_expect_success 'status -a clean' '
- git status -a |
+test_expect_success 'commit --dry-run -a clean' '
+ git commit --dry-run -a |
grep "nothing to commit"
'
test_expect_success 'rm submodule contents' '
grep "nothing to commit"
'
test_expect_success 'status -a clean (empty submodule dir)' '
- git status -a |
+ git commit --dry-run -a |
grep "nothing to commit"
'
. ./test-lib.sh
test_expect_success 'setup' '
- : > tracked &&
- : > modified &&
+ : >tracked &&
+ : >modified &&
mkdir dir1 &&
- : > dir1/tracked &&
- : > dir1/modified &&
+ : >dir1/tracked &&
+ : >dir1/modified &&
mkdir dir2 &&
- : > dir1/tracked &&
- : > dir1/modified &&
+ : >dir1/tracked &&
+ : >dir1/modified &&
git add . &&
git status >output &&
test_tick &&
git commit -m initial &&
- : > untracked &&
- : > dir1/untracked &&
- : > dir2/untracked &&
- echo 1 > dir1/modified &&
- echo 2 > dir2/modified &&
- echo 3 > dir2/added &&
+ : >untracked &&
+ : >dir1/untracked &&
+ : >dir2/untracked &&
+ echo 1 >dir1/modified &&
+ echo 2 >dir2/modified &&
+ echo 3 >dir2/added &&
git add dir2/added
'
'
-cat > expect << \EOF
+cat >expect <<\EOF
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
test_expect_success 'status (2)' '
- git status > output &&
+ git status >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+ M dir1/modified
+A dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status -s (2)' '
+
+ git status -s >output &&
test_cmp expect output
'
EOF
test_expect_success 'status -uno' '
mkdir dir3 &&
- : > dir3/untracked1 &&
- : > dir3/untracked2 &&
+ : >dir3/untracked1 &&
+ : >dir3/untracked2 &&
git status -uno >output &&
test_cmp expect output
'
test_cmp expect output
'
+cat >expect << EOF
+ M dir1/modified
+A dir2/added
+EOF
+test_expect_success 'status -s -uno' '
+ git config --unset status.showuntrackedfiles
+ git status -s -uno >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'status -s (status.showUntrackedFiles no)' '
+ git config status.showuntrackedfiles no
+ git status -s >output &&
+ test_cmp expect output
+'
+
cat >expect <<EOF
# On branch master
# Changes to be committed:
test_cmp expect output
'
+cat >expect <<EOF
+ M dir1/modified
+A dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? dir3/
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s -unormal' '
+ git config --unset status.showuntrackedfiles
+ git status -s -unormal >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'status -s (status.showUntrackedFiles normal)' '
+ git config status.showuntrackedfiles normal
+ git status -s >output &&
+ test_cmp expect output
+'
+
cat >expect <<EOF
# On branch master
# Changes to be committed:
test_cmp expect output
'
-cat > expect << \EOF
+cat >expect <<EOF
+ M dir1/modified
+A dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s -uall' '
+ git config --unset status.showuntrackedfiles
+ git status -s -uall >output &&
+ test_cmp expect output
+'
+test_expect_success 'status -s (status.showUntrackedFiles all)' '
+ git config status.showuntrackedfiles all
+ git status -s >output &&
+ rm -rf dir3 &&
+ git config --unset status.showuntrackedfiles &&
+ test_cmp expect output
+'
+
+cat >expect <<\EOF
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
test_expect_success 'status with relative paths' '
- (cd dir1 && git status) > output &&
+ (cd dir1 && git status) >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+ M modified
+A ../dir2/added
+?? untracked
+?? ../dir2/modified
+?? ../dir2/untracked
+?? ../expect
+?? ../output
+?? ../untracked
+EOF
+test_expect_success 'status -s with relative paths' '
+
+ (cd dir1 && git status -s) >output &&
test_cmp expect output
'
-cat > expect << \EOF
+cat >expect <<\EOF
+ M dir1/modified
+A dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status --porcelain ignores relative paths setting' '
+
+ (cd dir1 && git status --porcelain) >output &&
+ test_cmp expect output
+
+'
+
+test_expect_success 'setup unique colors' '
+
+ git config status.color.untracked blue
+
+'
+
+cat >expect <<\EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# <GREEN>new file: dir2/added<RESET>
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# <RED>modified: dir1/modified<RESET>
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# <BLUE>dir1/untracked<RESET>
+# <BLUE>dir2/modified<RESET>
+# <BLUE>dir2/untracked<RESET>
+# <BLUE>expect<RESET>
+# <BLUE>output<RESET>
+# <BLUE>untracked<RESET>
+EOF
+
+test_expect_success 'status with color.ui' '
+
+ git config color.ui always &&
+ git status | test_decode_color >output &&
+ test_cmp expect output
+
+'
+
+test_expect_success 'status with color.status' '
+
+ git config --unset color.ui &&
+ git config color.status always &&
+ git status | test_decode_color >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+ <RED>M<RESET> dir1/modified
+<GREEN>A<RESET> dir2/added
+<BLUE>??<RESET> dir1/untracked
+<BLUE>??<RESET> dir2/modified
+<BLUE>??<RESET> dir2/untracked
+<BLUE>??<RESET> expect
+<BLUE>??<RESET> output
+<BLUE>??<RESET> untracked
+EOF
+
+test_expect_success 'status -s with color.ui' '
+
+ git config --unset color.status &&
+ git config color.ui always &&
+ git status -s | test_decode_color >output &&
+ test_cmp expect output
+
+'
+
+test_expect_success 'status -s with color.status' '
+
+ git config --unset color.ui &&
+ git config color.status always &&
+ git status -s | test_decode_color >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+ M dir1/modified
+A dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status --porcelain ignores color.ui' '
+
+ git config --unset color.status &&
+ git config color.ui always &&
+ git status --porcelain | test_decode_color >output &&
+ test_cmp expect output
+
+'
+
+test_expect_success 'status --porcelain ignores color.status' '
+
+ git config --unset color.ui &&
+ git config color.status always &&
+ git status --porcelain | test_decode_color >output &&
+ test_cmp expect output
+
+'
+
+# recover unconditionally from color tests
+git config --unset color.status
+git config --unset color.ui
+
+cat >expect <<\EOF
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
# untracked
EOF
+
test_expect_success 'status without relative paths' '
git config status.relativePaths false
- (cd dir1 && git status) > output &&
+ (cd dir1 && git status) >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+ M dir1/modified
+A dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status -s without relative paths' '
+
+ (cd dir1 && git status -s) >output &&
test_cmp expect output
'
# output
# untracked
EOF
-test_expect_success 'status of partial commit excluding new file in index' '
- git status dir1/modified >output &&
+test_expect_success 'dry-run of partial commit excluding new file in index' '
+ git commit --dry-run dir1/modified >output &&
test_cmp expect output
'
test_cmp expect output
'
+cat >expect <<EOF
+ M dir1/modified
+A dir2/added
+A sm
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s submodule summary is disabled by default' '
+ git status -s >output &&
+ test_cmp expect output
+'
+
+# we expect the same as the previous test
+test_expect_success 'status -s --untracked-files=all does not show submodule' '
+ git status -s --untracked-files=all >output &&
+ test_cmp expect output
+'
+
head=$(cd sm && git rev-parse --short=7 --verify HEAD)
cat >expect <<EOF
test_cmp expect output
'
+cat >expect <<EOF
+ M dir1/modified
+A dir2/added
+A sm
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s submodule summary' '
+ git status -s >output &&
+ test_cmp expect output
+'
cat >expect <<EOF
# On branch master
test_expect_success 'status submodule summary (clean submodule)' '
git commit -m "commit submodule" &&
git config status.submodulesummary 10 &&
- test_must_fail git status >output &&
+ test_must_fail git commit --dry-run >output &&
+ test_cmp expect output &&
+ git status >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+ M dir1/modified
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s submodule summary (clean submodule)' '
+ git status -s >output &&
test_cmp expect output
'
# output
# untracked
EOF
-test_expect_success 'status submodule summary (--amend)' '
+test_expect_success 'commit --dry-run submodule summary (--amend)' '
git config status.submodulesummary 10 &&
- git status --amend >output &&
+ git commit --dry-run --amend >output &&
test_cmp expect output
'
export EDITOR
}
+test_decode_color () {
+ sed -e 's/.\[1m/<WHITE>/g' \
+ -e 's/.\[31m/<RED>/g' \
+ -e 's/.\[32m/<GREEN>/g' \
+ -e 's/.\[33m/<YELLOW>/g' \
+ -e 's/.\[34m/<BLUE>/g' \
+ -e 's/.\[35m/<MAGENTA>/g' \
+ -e 's/.\[36m/<CYAN>/g' \
+ -e 's/.\[m/<RESET>/g'
+}
+
test_tick () {
if test -z "${test_tick+set}"
then
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 <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, "# (use \"git add/rm <file>...\" 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 <file>...\" to unstage)", s->reference);
- } else {
+ else
color_fprintf_ln(s->fp, c, "# (use \"git rm --cached <file>...\" to unstage)");
- }
color_fprintf_ln(s->fp, c, "#");
}
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;
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);
}
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);
}
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) {
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);
}
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;
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");
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);
+}
int is_initial;
char *branch;
const char *reference;
+ const char **pathspec;
int verbose;
int amend;
+ int in_merge;
int nowarn;
int use_color;
int relative_paths;
void wt_status_print(struct wt_status *s);
void wt_status_collect(struct wt_status *s);
+void wt_shortstatus_print(struct wt_status *s, int null_termination);
+void wt_porcelain_print(struct wt_status *s, int null_termination);
+
#endif /* STATUS_H */