Merge branch 'jk/1.7.0-status'
* jk/1.7.0-status: status/commit: do not suggest "reset HEAD <path>" while merging commit/status: "git add <path>" 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.cmaint
						commit
						c2ff10c98e
					
				|  | @ -74,6 +74,20 @@ OPTIONS | ||||||
| 	authorship of the resulting commit now belongs of the committer. | 	authorship of the resulting commit now belongs of the committer. | ||||||
| 	This also renews the author timestamp. | 	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>:: | -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 | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ git-status - Show the working tree status | ||||||
|  |  | ||||||
| SYNOPSIS | SYNOPSIS | ||||||
| -------- | -------- | ||||||
| 'git status' <options>... | 'git status' [<options>...] [--] [<pathspec>...] | ||||||
|  |  | ||||||
| DESCRIPTION | DESCRIPTION | ||||||
| ----------- | ----------- | ||||||
|  | @ -20,25 +20,90 @@ are what you _would_ commit by running `git commit`; the second and | ||||||
| third are what you _could_ commit by running 'git-add' before running | third are what you _could_ commit by running 'git-add' before running | ||||||
| `git commit`. | `git commit`. | ||||||
|  |  | ||||||
| The command takes the same set of options as 'git-commit'; it | OPTIONS | ||||||
| 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 | -s:: | ||||||
| the current HEAD commit (i.e., there is nothing to commit by running | --short:: | ||||||
| `git commit`), the command exits with non-zero status. | 	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 | OUTPUT | ||||||
| ------ | ------ | ||||||
| The output from this command is designed to be used as a commit | The output from this command is designed to be used as a commit | ||||||
| template comment, and all the output lines are prefixed with '#'. | 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 | The paths mentioned in the output, unlike many other git commands, are | ||||||
| made relative to the current directory if you are working in a | made relative to the current directory if you are working in a | ||||||
| subdirectory (this is on purpose, to help cutting and pasting). See | subdirectory (this is on purpose, to help cutting and pasting). See | ||||||
| the status.relativePaths config option below. | 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 | CONFIGURATION | ||||||
| ------------- | ------------- | ||||||
|  | @ -53,9 +118,9 @@ paths shown are relative to the repository root, not to the current | ||||||
| directory. | directory. | ||||||
|  |  | ||||||
| If `status.submodulesummary` is set to a non zero number or true (identical | 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 | to -1 or an unlimited number), the submodule summary will be enabled for | ||||||
| summary of commits for modified submodules will be shown (see --summary-limit | the long format and a summary of commits for modified submodules will be | ||||||
| option of linkgit:git-submodule[1]). | shown (see --summary-limit option of linkgit:git-submodule[1]). | ||||||
|  |  | ||||||
| SEE ALSO | SEE ALSO | ||||||
| -------- | -------- | ||||||
|  | @ -63,8 +128,7 @@ linkgit:gitignore[5] | ||||||
|  |  | ||||||
| Author | Author | ||||||
| ------ | ------ | ||||||
| Written by Linus Torvalds <torvalds@osdl.org> and | Written by Junio C Hamano <gitster@pobox.com>. | ||||||
| Junio C Hamano <gitster@pobox.com>. |  | ||||||
|  |  | ||||||
| Documentation | Documentation | ||||||
| -------------- | -------------- | ||||||
|  |  | ||||||
							
								
								
									
										122
									
								
								builtin-commit.c
								
								
								
								
							
							
						
						
									
										122
									
								
								builtin-commit.c
								
								
								
								
							|  | @ -24,6 +24,7 @@ | ||||||
| #include "string-list.h" | #include "string-list.h" | ||||||
| #include "rerere.h" | #include "rerere.h" | ||||||
| #include "unpack-trees.h" | #include "unpack-trees.h" | ||||||
|  | #include "quote.h" | ||||||
|  |  | ||||||
| static const char * const builtin_commit_usage[] = { | static const char * const builtin_commit_usage[] = { | ||||||
| 	"git commit [options] [--] <filepattern>...", | 	"git commit [options] [--] <filepattern>...", | ||||||
|  | @ -35,7 +36,7 @@ static const char * const builtin_status_usage[] = { | ||||||
| 	NULL | 	NULL | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static unsigned char head_sha1[20], merge_head_sha1[20]; | static unsigned char head_sha1[20]; | ||||||
| static char *use_message_buffer; | static char *use_message_buffer; | ||||||
| static const char commit_editmsg[] = "COMMIT_EDITMSG"; | static const char commit_editmsg[] = "COMMIT_EDITMSG"; | ||||||
| static struct lock_file index_lock; /* real index */ | static struct lock_file index_lock; /* real index */ | ||||||
|  | @ -71,6 +72,13 @@ static int use_editor = 1, initial_commit, in_merge; | ||||||
| static const char *only_include_assumed; | static const char *only_include_assumed; | ||||||
| static struct strbuf message; | 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) | static int opt_parse_m(const struct option *opt, const char *arg, int unset) | ||||||
| { | { | ||||||
| 	struct strbuf *buf = opt->value; | 	struct strbuf *buf = opt->value; | ||||||
|  | @ -105,6 +113,12 @@ static struct option builtin_commit_options[] = { | ||||||
| 	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, "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"), | 	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"), | ||||||
|  | @ -306,7 +320,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int | ||||||
| 	 */ | 	 */ | ||||||
| 	commit_style = COMMIT_PARTIAL; | 	commit_style = COMMIT_PARTIAL; | ||||||
|  |  | ||||||
| 	if (file_exists(git_path("MERGE_HEAD"))) | 	if (in_merge) | ||||||
| 		die("cannot do a partial commit during a merge."); | 		die("cannot do a partial commit during a merge."); | ||||||
|  |  | ||||||
| 	memset(&partial, 0, sizeof(partial)); | 	memset(&partial, 0, sizeof(partial)); | ||||||
|  | @ -347,6 +361,8 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int | ||||||
| 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) | ||||||
| { | { | ||||||
|  | 	unsigned char sha1[20]; | ||||||
|  |  | ||||||
| 	if (s->relative_paths) | 	if (s->relative_paths) | ||||||
| 		s->prefix = prefix; | 		s->prefix = prefix; | ||||||
|  |  | ||||||
|  | @ -358,8 +374,21 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int | ||||||
| 	s->index_file = index_file; | 	s->index_file = index_file; | ||||||
| 	s->fp = fp; | 	s->fp = fp; | ||||||
| 	s->nowarn = nowarn; | 	s->nowarn = nowarn; | ||||||
|  | 	s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; | ||||||
|  |  | ||||||
|  | 	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); | 		wt_status_print(s); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return s->commitable; | 	return s->commitable; | ||||||
| } | } | ||||||
|  | @ -735,6 +764,21 @@ static const char *find_author_by_nickname(const char *name) | ||||||
| 	die("No existing author found with '%s'", name); | 	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[], | 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, | ||||||
|  | @ -761,9 +805,6 @@ static int parse_and_validate_options(int argc, const char *argv[], | ||||||
| 	if (get_sha1("HEAD", head_sha1)) | 	if (get_sha1("HEAD", head_sha1)) | ||||||
| 		initial_commit = 1; | 		initial_commit = 1; | ||||||
|  |  | ||||||
| 	if (!get_sha1("MERGE_HEAD", merge_head_sha1)) |  | ||||||
| 		in_merge = 1; |  | ||||||
|  |  | ||||||
| 	/* Sanity check options */ | 	/* Sanity check options */ | ||||||
| 	if (amend && initial_commit) | 	if (amend && initial_commit) | ||||||
| 		die("You have nothing to amend."); | 		die("You have nothing to amend."); | ||||||
|  | @ -843,22 +884,18 @@ static int parse_and_validate_options(int argc, const char *argv[], | ||||||
| 	else | 	else | ||||||
| 		die("Invalid cleanup mode %s", cleanup_arg); | 		die("Invalid cleanup mode %s", cleanup_arg); | ||||||
|  |  | ||||||
| 	if (!untracked_files_arg) | 	handle_untracked_files_arg(s); | ||||||
| 		; /* 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); |  | ||||||
|  |  | ||||||
| 	if (all && argc > 0) | 	if (all && argc > 0) | ||||||
| 		die("Paths with -a does not make sense."); | 		die("Paths with -a does not make sense."); | ||||||
| 	else if (interactive && argc > 0) | 	else if (interactive && argc > 0) | ||||||
| 		die("Paths with --interactive does not make sense."); | 		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; | 	return argc; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -940,17 +977,63 @@ static int git_status_config(const char *k, const char *v, void *cb) | ||||||
| int cmd_status(int argc, const char **argv, const char *prefix) | int cmd_status(int argc, const char **argv, const char *prefix) | ||||||
| { | { | ||||||
| 	struct wt_status s; | 	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); | 	wt_status_prepare(&s); | ||||||
| 	git_config(git_status_config, &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) | 	if (s.use_color == -1) | ||||||
| 		s.use_color = git_use_color_default; | 		s.use_color = git_use_color_default; | ||||||
| 	if (diff_use_color_default == -1) | 	if (diff_use_color_default == -1) | ||||||
| 		diff_use_color_default = git_use_color_default; | 		diff_use_color_default = git_use_color_default; | ||||||
|  |  | ||||||
| 	argc = parse_and_validate_options(argc, argv, builtin_status_usage, | 	switch (status_format) { | ||||||
| 					  prefix, &s); | 	case STATUS_FORMAT_SHORT: | ||||||
| 	return dry_run_commit(argc, argv, prefix, &s); | 		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) | static void print_summary(const char *prefix, const unsigned char *sha1) | ||||||
|  | @ -1026,10 +1109,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix) | ||||||
|  |  | ||||||
| 	wt_status_prepare(&s); | 	wt_status_prepare(&s); | ||||||
| 	git_config(git_commit_config, &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) | 	if (s.use_color == -1) | ||||||
| 		s.use_color = git_use_color_default; | 		s.use_color = git_use_color_default; | ||||||
|  |  | ||||||
| 	argc = parse_and_validate_options(argc, argv, builtin_commit_usage, | 	argc = parse_and_validate_options(argc, argv, builtin_commit_usage, | ||||||
| 					  prefix, &s); | 					  prefix, &s); | ||||||
| 	if (dry_run) { | 	if (dry_run) { | ||||||
|  |  | ||||||
|  | @ -12,19 +12,9 @@ test_expect_success setup ' | ||||||
|  |  | ||||||
| ' | ' | ||||||
|  |  | ||||||
| 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 () { | word_diff () { | ||||||
| 	test_must_fail git diff --no-index "$@" pre post > output && | 	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 | 	test_cmp expect output.decrypted | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -49,7 +39,7 @@ cat > expect <<\EOF | ||||||
| <WHITE>index 330b04f..5ed8eff 100644<RESET> | <WHITE>index 330b04f..5ed8eff 100644<RESET> | ||||||
| <WHITE>--- a/pre<RESET> | <WHITE>--- a/pre<RESET> | ||||||
| <WHITE>+++ b/post<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> | <RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET> | ||||||
|  |  | ||||||
| a = b + c<RESET> | a = b + c<RESET> | ||||||
|  | @ -70,9 +60,9 @@ cat > expect <<\EOF | ||||||
| <WHITE>index 330b04f..5ed8eff 100644<RESET> | <WHITE>index 330b04f..5ed8eff 100644<RESET> | ||||||
| <WHITE>--- a/pre<RESET> | <WHITE>--- a/pre<RESET> | ||||||
| <WHITE>+++ b/post<RESET> | <WHITE>+++ b/post<RESET> | ||||||
| <BROWN>@@ -1 +1 @@<RESET> | <CYAN>@@ -1 +1 @@<RESET> | ||||||
| <RED>h(4)<RESET><GREEN>h(4),hh[44]<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> | <GREEN>aa = a<RESET> | ||||||
|  |  | ||||||
|  | @ -90,7 +80,7 @@ cat > expect <<\EOF | ||||||
| <WHITE>index 330b04f..5ed8eff 100644<RESET> | <WHITE>index 330b04f..5ed8eff 100644<RESET> | ||||||
| <WHITE>--- a/pre<RESET> | <WHITE>--- a/pre<RESET> | ||||||
| <WHITE>+++ b/post<RESET> | <WHITE>+++ b/post<RESET> | ||||||
| <BROWN>@@ -1,3 +1,7 @@<RESET> | <CYAN>@@ -1,3 +1,7 @@<RESET> | ||||||
| h(4),<GREEN>hh<RESET>[44] | h(4),<GREEN>hh<RESET>[44] | ||||||
|  |  | ||||||
| a = b + c<RESET> | a = b + c<RESET> | ||||||
|  | @ -126,7 +116,7 @@ cat > expect <<\EOF | ||||||
| <WHITE>index 330b04f..5ed8eff 100644<RESET> | <WHITE>index 330b04f..5ed8eff 100644<RESET> | ||||||
| <WHITE>--- a/pre<RESET> | <WHITE>--- a/pre<RESET> | ||||||
| <WHITE>+++ b/post<RESET> | <WHITE>+++ b/post<RESET> | ||||||
| <BROWN>@@ -1,3 +1,7 @@<RESET> | <CYAN>@@ -1,3 +1,7 @@<RESET> | ||||||
| h(4)<GREEN>,hh[44]<RESET> | h(4)<GREEN>,hh[44]<RESET> | ||||||
|  |  | ||||||
| a = b + c<RESET> | a = b + c<RESET> | ||||||
|  | @ -168,7 +158,7 @@ cat > expect <<\EOF | ||||||
| <WHITE>index 330b04f..5ed8eff 100644<RESET> | <WHITE>index 330b04f..5ed8eff 100644<RESET> | ||||||
| <WHITE>--- a/pre<RESET> | <WHITE>--- a/pre<RESET> | ||||||
| <WHITE>+++ b/post<RESET> | <WHITE>+++ b/post<RESET> | ||||||
| <BROWN>@@ -1,3 +1,7 @@<RESET> | <CYAN>@@ -1,3 +1,7 @@<RESET> | ||||||
| h(4),<GREEN>hh[44<RESET>] | h(4),<GREEN>hh[44<RESET>] | ||||||
|  |  | ||||||
| a = b + c<RESET> | a = b + c<RESET> | ||||||
|  | @ -190,7 +180,7 @@ cat > expect <<\EOF | ||||||
| <WHITE>index c29453b..be22f37 100644<RESET> | <WHITE>index c29453b..be22f37 100644<RESET> | ||||||
| <WHITE>--- a/pre<RESET> | <WHITE>--- a/pre<RESET> | ||||||
| <WHITE>+++ b/post<RESET> | <WHITE>+++ b/post<RESET> | ||||||
| <BROWN>@@ -1 +1 @@<RESET> | <CYAN>@@ -1 +1 @@<RESET> | ||||||
| aaa (aaa) <GREEN>aaa<RESET> | aaa (aaa) <GREEN>aaa<RESET> | ||||||
| EOF | EOF | ||||||
|  |  | ||||||
|  | @ -209,7 +199,7 @@ cat > expect <<\EOF | ||||||
| <WHITE>index 289cb9d..2d06f37 100644<RESET> | <WHITE>index 289cb9d..2d06f37 100644<RESET> | ||||||
| <WHITE>--- a/pre<RESET> | <WHITE>--- a/pre<RESET> | ||||||
| <WHITE>+++ b/post<RESET> | <WHITE>+++ b/post<RESET> | ||||||
| <BROWN>@@ -1 +1 @@<RESET> | <CYAN>@@ -1 +1 @@<RESET> | ||||||
| (<RED>:<RESET> | (<RED>:<RESET> | ||||||
| EOF | EOF | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @ -69,7 +69,7 @@ test_expect_success 'status' ' | ||||||
| 		cd test && | 		cd test && | ||||||
| 		git checkout b1 >/dev/null && | 		git checkout b1 >/dev/null && | ||||||
| 		# reports nothing to commit | 		# reports nothing to commit | ||||||
| 		test_must_fail git status | 		test_must_fail git commit --dry-run | ||||||
| 	) >actual && | 	) >actual && | ||||||
| 	grep "have 1 and 1 different" actual | 	grep "have 1 and 1 different" actual | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -31,8 +31,7 @@ test_expect_success 'Report new path with conflict' ' | ||||||
| cat >expect <<EOF | cat >expect <<EOF | ||||||
| # On branch side | # On branch side | ||||||
| # Unmerged paths: | # Unmerged paths: | ||||||
| #   (use "git reset HEAD <file>..." to unstage) | #   (use "git add/rm <file>..." as appropriate to mark resolution) | ||||||
| #   (use "git add <file>..." to mark resolution) |  | ||||||
| # | # | ||||||
| #	deleted by us:      foo | #	deleted by us:      foo | ||||||
| # | # | ||||||
|  | @ -50,9 +49,11 @@ test_expect_success 'M/D conflict does not segfault' ' | ||||||
| 		git rm foo && | 		git rm foo && | ||||||
| 		git commit -m delete && | 		git commit -m delete && | ||||||
| 		test_must_fail git merge master && | 		test_must_fail git merge master && | ||||||
| 		test_must_fail git status > ../actual | 		test_must_fail git commit --dry-run >../actual && | ||||||
| 	) && | 		test_cmp ../expect ../actual && | ||||||
| 	test_cmp expect actual | 		git status >../actual && | ||||||
|  | 		test_cmp ../expect ../actual | ||||||
|  | 	) | ||||||
| ' | ' | ||||||
|  |  | ||||||
| test_done | test_done | ||||||
|  |  | ||||||
|  | @ -19,8 +19,8 @@ test_expect_success 'status clean' ' | ||||||
| 	git status | | 	git status | | ||||||
| 	grep "nothing to commit" | 	grep "nothing to commit" | ||||||
| ' | ' | ||||||
| test_expect_success 'status -a clean' ' | test_expect_success 'commit --dry-run -a clean' ' | ||||||
| 	git status -a | | 	git commit --dry-run -a | | ||||||
| 	grep "nothing to commit" | 	grep "nothing to commit" | ||||||
| ' | ' | ||||||
| test_expect_success 'rm submodule contents' ' | test_expect_success 'rm submodule contents' ' | ||||||
|  | @ -31,7 +31,7 @@ test_expect_success 'status clean (empty submodule dir)' ' | ||||||
| 	grep "nothing to commit" | 	grep "nothing to commit" | ||||||
| ' | ' | ||||||
| test_expect_success 'status -a clean (empty submodule dir)' ' | test_expect_success 'status -a clean (empty submodule dir)' ' | ||||||
| 	git status -a | | 	git commit --dry-run -a | | ||||||
| 	grep "nothing to commit" | 	grep "nothing to commit" | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @ -8,26 +8,26 @@ test_description='git status' | ||||||
| . ./test-lib.sh | . ./test-lib.sh | ||||||
|  |  | ||||||
| test_expect_success 'setup' ' | test_expect_success 'setup' ' | ||||||
| 	: > tracked && | 	: >tracked && | ||||||
| 	: > modified && | 	: >modified && | ||||||
| 	mkdir dir1 && | 	mkdir dir1 && | ||||||
| 	: > dir1/tracked && | 	: >dir1/tracked && | ||||||
| 	: > dir1/modified && | 	: >dir1/modified && | ||||||
| 	mkdir dir2 && | 	mkdir dir2 && | ||||||
| 	: > dir1/tracked && | 	: >dir1/tracked && | ||||||
| 	: > dir1/modified && | 	: >dir1/modified && | ||||||
| 	git add . && | 	git add . && | ||||||
|  |  | ||||||
| 	git status >output && | 	git status >output && | ||||||
|  |  | ||||||
| 	test_tick && | 	test_tick && | ||||||
| 	git commit -m initial && | 	git commit -m initial && | ||||||
| 	: > untracked && | 	: >untracked && | ||||||
| 	: > dir1/untracked && | 	: >dir1/untracked && | ||||||
| 	: > dir2/untracked && | 	: >dir2/untracked && | ||||||
| 	echo 1 > dir1/modified && | 	echo 1 >dir1/modified && | ||||||
| 	echo 2 > dir2/modified && | 	echo 2 >dir2/modified && | ||||||
| 	echo 3 > dir2/added && | 	echo 3 >dir2/added && | ||||||
| 	git add dir2/added | 	git add dir2/added | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -37,7 +37,7 @@ test_expect_success 'status (1)' ' | ||||||
|  |  | ||||||
| ' | ' | ||||||
|  |  | ||||||
| cat > expect << \EOF | cat >expect <<\EOF | ||||||
| # On branch master | # On branch master | ||||||
| # Changes to be committed: | # Changes to be committed: | ||||||
| #   (use "git reset HEAD <file>..." to unstage) | #   (use "git reset HEAD <file>..." to unstage) | ||||||
|  | @ -63,7 +63,25 @@ EOF | ||||||
|  |  | ||||||
| test_expect_success 'status (2)' ' | 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 | 	test_cmp expect output | ||||||
|  |  | ||||||
| ' | ' | ||||||
|  | @ -85,8 +103,8 @@ cat >expect <<EOF | ||||||
| EOF | EOF | ||||||
| test_expect_success 'status -uno' ' | test_expect_success 'status -uno' ' | ||||||
| 	mkdir dir3 && | 	mkdir dir3 && | ||||||
| 	: > dir3/untracked1 && | 	: >dir3/untracked1 && | ||||||
| 	: > dir3/untracked2 && | 	: >dir3/untracked2 && | ||||||
| 	git status -uno >output && | 	git status -uno >output && | ||||||
| 	test_cmp expect output | 	test_cmp expect output | ||||||
| ' | ' | ||||||
|  | @ -97,6 +115,22 @@ test_expect_success 'status (status.showUntrackedFiles no)' ' | ||||||
| 	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 | cat >expect <<EOF | ||||||
| # On branch master | # On branch master | ||||||
| # Changes to be committed: | # Changes to be committed: | ||||||
|  | @ -132,6 +166,29 @@ test_expect_success 'status (status.showUntrackedFiles normal)' ' | ||||||
| 	test_cmp expect output | 	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 | cat >expect <<EOF | ||||||
| # On branch master | # On branch master | ||||||
| # Changes to be committed: | # Changes to be committed: | ||||||
|  | @ -169,7 +226,30 @@ test_expect_success 'status (status.showUntrackedFiles all)' ' | ||||||
| 	test_cmp expect 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 -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 | # On branch master | ||||||
| # Changes to be committed: | # Changes to be committed: | ||||||
| #   (use "git reset HEAD <file>..." to unstage) | #   (use "git reset HEAD <file>..." to unstage) | ||||||
|  | @ -195,12 +275,156 @@ EOF | ||||||
|  |  | ||||||
| test_expect_success 'status with relative paths' ' | test_expect_success 'status with relative paths' ' | ||||||
|  |  | ||||||
| 	(cd dir1 && git status) > output && | 	(cd dir1 && git status) >output && | ||||||
| 	test_cmp expect output | 	test_cmp expect output | ||||||
|  |  | ||||||
| ' | ' | ||||||
|  |  | ||||||
| cat > expect << \EOF | 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 | ||||||
|  |  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 | # On branch master | ||||||
| # Changes to be committed: | # Changes to be committed: | ||||||
| #   (use "git reset HEAD <file>..." to unstage) | #   (use "git reset HEAD <file>..." to unstage) | ||||||
|  | @ -224,10 +448,29 @@ cat > expect << \EOF | ||||||
| #	untracked | #	untracked | ||||||
| EOF | EOF | ||||||
|  |  | ||||||
|  |  | ||||||
| test_expect_success 'status without relative paths' ' | test_expect_success 'status without relative paths' ' | ||||||
|  |  | ||||||
| 	git config status.relativePaths false | 	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 | 	test_cmp expect output | ||||||
|  |  | ||||||
| ' | ' | ||||||
|  | @ -248,8 +491,8 @@ cat <<EOF >expect | ||||||
| #	output | #	output | ||||||
| #	untracked | #	untracked | ||||||
| EOF | EOF | ||||||
| test_expect_success 'status of partial commit excluding new file in index' ' | test_expect_success 'dry-run of partial commit excluding new file in index' ' | ||||||
| 	git status dir1/modified >output && | 	git commit --dry-run dir1/modified >output && | ||||||
| 	test_cmp expect output | 	test_cmp expect output | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -298,6 +541,28 @@ test_expect_success 'status --untracked-files=all does not show submodule' ' | ||||||
| 	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) | head=$(cd sm && git rev-parse --short=7 --verify HEAD) | ||||||
|  |  | ||||||
| cat >expect <<EOF | cat >expect <<EOF | ||||||
|  | @ -335,6 +600,21 @@ test_expect_success 'status submodule summary' ' | ||||||
| 	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' ' | ||||||
|  | 	git status -s >output && | ||||||
|  | 	test_cmp expect output | ||||||
|  | ' | ||||||
|  |  | ||||||
| cat >expect <<EOF | cat >expect <<EOF | ||||||
| # On branch master | # On branch master | ||||||
|  | @ -358,7 +638,23 @@ EOF | ||||||
| test_expect_success 'status submodule summary (clean submodule)' ' | test_expect_success 'status submodule summary (clean submodule)' ' | ||||||
| 	git commit -m "commit submodule" && | 	git commit -m "commit submodule" && | ||||||
| 	git config status.submodulesummary 10 && | 	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 | 	test_cmp expect output | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -391,9 +687,9 @@ cat >expect <<EOF | ||||||
| #	output | #	output | ||||||
| #	untracked | #	untracked | ||||||
| EOF | EOF | ||||||
| test_expect_success 'status submodule summary (--amend)' ' | test_expect_success 'commit --dry-run submodule summary (--amend)' ' | ||||||
| 	git config status.submodulesummary 10 && | 	git config status.submodulesummary 10 && | ||||||
| 	git status --amend >output && | 	git commit --dry-run --amend >output && | ||||||
| 	test_cmp expect output | 	test_cmp expect output | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @ -211,6 +211,17 @@ test_set_editor () { | ||||||
| 	export EDITOR | 	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 () { | test_tick () { | ||||||
| 	if test -z "${test_tick+set}" | 	if test -z "${test_tick+set}" | ||||||
| 	then | 	then | ||||||
|  |  | ||||||
							
								
								
									
										130
									
								
								wt-status.c
								
								
								
								
							
							
						
						
									
										130
									
								
								wt-status.c
								
								
								
								
							|  | @ -47,28 +47,33 @@ void wt_status_prepare(struct wt_status *s) | ||||||
| static void wt_status_print_unmerged_header(struct wt_status *s) | static void wt_status_print_unmerged_header(struct wt_status *s) | ||||||
| { | { | ||||||
| 	const char *c = color(WT_STATUS_HEADER, s); | 	const char *c = color(WT_STATUS_HEADER, s); | ||||||
|  |  | ||||||
| 	color_fprintf_ln(s->fp, c, "# Unmerged paths:"); | 	color_fprintf_ln(s->fp, c, "# Unmerged paths:"); | ||||||
| 	if (!advice_status_hints) | 	if (!advice_status_hints) | ||||||
| 		return; | 		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); | 		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, "#   (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, "#"); | 	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, s); | 	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 (!advice_status_hints) | 	if (!advice_status_hints) | ||||||
| 		return; | 		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); | 		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, "#   (use \"git rm --cached <file>...\" to unstage)"); | ||||||
| 	} |  | ||||||
| 	color_fprintf_ln(s->fp, c, "#"); | 	color_fprintf_ln(s->fp, c, "#"); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -76,6 +81,7 @@ static void wt_status_print_dirty_header(struct wt_status *s, | ||||||
| 					 int has_deleted) | 					 int has_deleted) | ||||||
| { | { | ||||||
| 	const char *c = color(WT_STATUS_HEADER, s); | 	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 (!advice_status_hints) | 	if (!advice_status_hints) | ||||||
| 		return; | 		return; | ||||||
|  | @ -277,6 +283,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s) | ||||||
| 	rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; | 	rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; | ||||||
| 	rev.diffopt.format_callback = wt_status_collect_changed_cb; | 	rev.diffopt.format_callback = wt_status_collect_changed_cb; | ||||||
| 	rev.diffopt.format_callback_data = s; | 	rev.diffopt.format_callback_data = s; | ||||||
|  | 	rev.prune_data = s->pathspec; | ||||||
| 	run_diff_files(&rev, 0); | 	run_diff_files(&rev, 0); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -293,6 +300,7 @@ static void wt_status_collect_changes_index(struct wt_status *s) | ||||||
| 	rev.diffopt.detect_rename = 1; | 	rev.diffopt.detect_rename = 1; | ||||||
| 	rev.diffopt.rename_limit = 200; | 	rev.diffopt.rename_limit = 200; | ||||||
| 	rev.diffopt.break_opt = 0; | 	rev.diffopt.break_opt = 0; | ||||||
|  | 	rev.prune_data = s->pathspec; | ||||||
| 	run_diff_index(&rev, 1); | 	run_diff_index(&rev, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -305,6 +313,8 @@ static void wt_status_collect_changes_initial(struct wt_status *s) | ||||||
| 		struct wt_status_change_data *d; | 		struct wt_status_change_data *d; | ||||||
| 		struct cache_entry *ce = active_cache[i]; | 		struct cache_entry *ce = active_cache[i]; | ||||||
|  |  | ||||||
|  | 		if (!ce_path_match(ce, s->pathspec)) | ||||||
|  | 			continue; | ||||||
| 		it = string_list_insert(ce->name, &s->change); | 		it = string_list_insert(ce->name, &s->change); | ||||||
| 		d = it->util; | 		d = it->util; | ||||||
| 		if (!d) { | 		if (!d) { | ||||||
|  | @ -338,6 +348,8 @@ static void wt_status_collect_untracked(struct wt_status *s) | ||||||
| 		struct dir_entry *ent = dir.entries[i]; | 		struct dir_entry *ent = dir.entries[i]; | ||||||
| 		if (!cache_name_is_other(ent->name, ent->len)) | 		if (!cache_name_is_other(ent->name, ent->len)) | ||||||
| 			continue; | 			continue; | ||||||
|  | 		if (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) | ||||||
|  | 			continue; | ||||||
| 		s->workdir_untracked = 1; | 		s->workdir_untracked = 1; | ||||||
| 		string_list_insert(ent->name, &s->untracked); | 		string_list_insert(ent->name, &s->untracked); | ||||||
| 	} | 	} | ||||||
|  | @ -541,10 +553,8 @@ static void wt_status_print_tracking(struct wt_status *s) | ||||||
|  |  | ||||||
| void wt_status_print(struct wt_status *s) | void wt_status_print(struct wt_status *s) | ||||||
| { | { | ||||||
| 	unsigned char sha1[20]; |  | ||||||
| 	const char *branch_color = color(WT_STATUS_HEADER, s); | 	const char *branch_color = color(WT_STATUS_HEADER, s); | ||||||
|  |  | ||||||
| 	s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; |  | ||||||
| 	if (s->branch) { | 	if (s->branch) { | ||||||
| 		const char *on_what = "On branch "; | 		const char *on_what = "On branch "; | ||||||
| 		const char *branch_name = s->branch; | 		const char *branch_name = s->branch; | ||||||
|  | @ -561,8 +571,6 @@ void wt_status_print(struct wt_status *s) | ||||||
| 			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, s), "#"); | 		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), "# Initial commit"); | ||||||
|  | @ -598,3 +606,107 @@ void wt_status_print(struct wt_status *s) | ||||||
| 			printf("nothing to commit (working directory clean)\n"); | 			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); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -31,8 +31,10 @@ struct wt_status { | ||||||
| 	int is_initial; | 	int is_initial; | ||||||
| 	char *branch; | 	char *branch; | ||||||
| 	const char *reference; | 	const char *reference; | ||||||
|  | 	const char **pathspec; | ||||||
| 	int verbose; | 	int verbose; | ||||||
| 	int amend; | 	int amend; | ||||||
|  | 	int in_merge; | ||||||
| 	int nowarn; | 	int nowarn; | ||||||
| 	int use_color; | 	int use_color; | ||||||
| 	int relative_paths; | 	int relative_paths; | ||||||
|  | @ -55,4 +57,7 @@ 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); | 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 */ | #endif /* STATUS_H */ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano