Merge branch 'nd/ita-wt-renames-in-status'
"git status" after moving a path in the working tree (hence making it appear "removed") and then adding with the -N option (hence making that appear "added") detected it as a rename, but did not report the old and new pathnames correctly. * nd/ita-wt-renames-in-status: wt-status.c: handle worktree renames wt-status.c: rename rename-related fields in wt_status_change_data wt-status.c: catch unhandled diff status codes wt-status.c: coding style fix Use DIFF_DETECT_RENAME for detect_rename assignments t2203: test status output with porcelain v2 formatmaint
						commit
						bc3dca07f4
					
				|  | @ -149,14 +149,15 @@ the status.relativePaths config option below. | |||
| Short Format | ||||
| ~~~~~~~~~~~~ | ||||
|  | ||||
| In the short-format, the status of each path is shown as | ||||
| In the short-format, the status of each path is shown as one of these | ||||
| forms | ||||
|  | ||||
| 	XY PATH1 -> PATH2 | ||||
| 	XY PATH | ||||
| 	XY ORIG_PATH -> PATH | ||||
|  | ||||
| where `PATH1` is the path in the `HEAD`, and the " `-> PATH2`" part is | ||||
| shown only when `PATH1` corresponds to a different path in the | ||||
| index/worktree (i.e. the file is renamed). The `XY` is a two-letter | ||||
| status code. | ||||
| where `ORIG_PATH` is where the renamed/copied contents came | ||||
| from. `ORIG_PATH` is only shown when the entry is renamed or | ||||
| copied. The `XY` is a two-letter status code. | ||||
|  | ||||
| The fields (including the `->`) are separated from each other by a | ||||
| single space. If a filename contains whitespace or other nonprintable | ||||
|  | @ -192,6 +193,8 @@ in which case `XY` are `!!`. | |||
|     [MARC]           index and work tree matches | ||||
|     [ MARC]     M    work tree changed since index | ||||
|     [ MARC]     D    deleted in work tree | ||||
|     [ D]        R    renamed in work tree | ||||
|     [ D]        C    copied in work tree | ||||
|     ------------------------------------------------- | ||||
|     D           D    unmerged, both deleted | ||||
|     A           U    unmerged, added by us | ||||
|  | @ -309,13 +312,13 @@ Renamed or copied entries have the following format: | |||
| 		of similarity between the source and target of the | ||||
| 		move or copy). For example "R100" or "C75". | ||||
|     <path>      The pathname.  In a renamed/copied entry, this | ||||
| 		is the path in the index and in the working tree. | ||||
| 		is the target path. | ||||
|     <sep>       When the `-z` option is used, the 2 pathnames are separated | ||||
| 		with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09) | ||||
| 		byte separates them. | ||||
|     <origPath>  The pathname in the commit at HEAD.  This is only | ||||
| 		present in a renamed/copied entry, and tells | ||||
| 		where the renamed/copied contents came from. | ||||
|     <origPath>  The pathname in the commit at HEAD or in the index. | ||||
| 		This is only present in a renamed/copied entry, and | ||||
| 		tells where the renamed/copied contents came from. | ||||
|     -------------------------------------------------------- | ||||
|  | ||||
| Unmerged entries have the following format; the first character is | ||||
|  |  | |||
|  | @ -1507,7 +1507,7 @@ static void print_summary(const char *prefix, const struct object_id *oid, | |||
| 	rev.show_root_diff = 1; | ||||
| 	get_commit_format(format.buf, &rev); | ||||
| 	rev.always_show_header = 0; | ||||
| 	rev.diffopt.detect_rename = 1; | ||||
| 	rev.diffopt.detect_rename = DIFF_DETECT_RENAME; | ||||
| 	rev.diffopt.break_opt = 0; | ||||
| 	diff_setup_done(&rev.diffopt); | ||||
|  | ||||
|  |  | |||
							
								
								
									
										2
									
								
								diff.c
								
								
								
								
							
							
						
						
									
										2
									
								
								diff.c
								
								
								
								
							|  | @ -246,7 +246,7 @@ static int parse_ws_error_highlight(const char *arg) | |||
|  */ | ||||
| void init_diff_ui_defaults(void) | ||||
| { | ||||
| 	diff_detect_rename_default = 1; | ||||
| 	diff_detect_rename_default = DIFF_DETECT_RENAME; | ||||
| } | ||||
|  | ||||
| int git_diff_heuristic_config(const char *var, const char *value, void *cb) | ||||
|  |  | |||
|  | @ -25,6 +25,18 @@ test_expect_success 'git status' ' | |||
| 	test_cmp expect actual | ||||
| ' | ||||
|  | ||||
| test_expect_success 'git status with porcelain v2' ' | ||||
| 	git status --porcelain=v2 | grep -v "^?" >actual && | ||||
| 	nam1=d00491fd7e5bb6fa28c517a0bb32b8b506539d4d && | ||||
| 	nam2=ce013625030ba8dba906f756967f9e9ca394464a && | ||||
| 	cat >expect <<-EOF && | ||||
| 	1 DA N... 100644 000000 100644 $nam1 $_z40 1.t | ||||
| 	1 A. N... 000000 100644 100644 $_z40 $nam2 elif | ||||
| 	1 .A N... 000000 000000 100644 $_z40 $_z40 file | ||||
| 	EOF | ||||
| 	test_cmp expect actual | ||||
| ' | ||||
|  | ||||
| test_expect_success 'check result of "add -N"' ' | ||||
| 	git ls-files -s file >actual && | ||||
| 	empty=$(git hash-object --stdin </dev/null) && | ||||
|  | @ -150,5 +162,65 @@ test_expect_success 'commit: ita entries ignored in empty commit check' ' | |||
| 	) | ||||
| ' | ||||
|  | ||||
| test_expect_success 'rename detection finds the right names' ' | ||||
| 	git init rename-detection && | ||||
| 	( | ||||
| 		cd rename-detection && | ||||
| 		echo contents >first && | ||||
| 		git add first && | ||||
| 		git commit -m first && | ||||
| 		mv first third && | ||||
| 		git add -N third && | ||||
|  | ||||
| 		git status | grep -v "^?" >actual.1 && | ||||
| 		test_i18ngrep "renamed: *first -> third" actual.1 && | ||||
|  | ||||
| 		git status --porcelain | grep -v "^?" >actual.2 && | ||||
| 		cat >expected.2 <<-\EOF && | ||||
| 		 R first -> third | ||||
| 		EOF | ||||
| 		test_cmp expected.2 actual.2 && | ||||
|  | ||||
| 		hash=12f00e90b6ef79117ce6e650416b8cf517099b78 && | ||||
| 		git status --porcelain=v2 | grep -v "^?" >actual.3 && | ||||
| 		cat >expected.3 <<-EOF && | ||||
| 		2 .R N... 100644 100644 100644 $hash $hash R100 third	first | ||||
| 		EOF | ||||
| 		test_cmp expected.3 actual.3 | ||||
| 	) | ||||
| ' | ||||
|  | ||||
| test_expect_success 'double rename detection in status' ' | ||||
| 	git init rename-detection-2 && | ||||
| 	( | ||||
| 		cd rename-detection-2 && | ||||
| 		echo contents >first && | ||||
| 		git add first && | ||||
| 		git commit -m first && | ||||
| 		git mv first second && | ||||
| 		mv second third && | ||||
| 		git add -N third && | ||||
|  | ||||
| 		git status | grep -v "^?" >actual.1 && | ||||
| 		test_i18ngrep "renamed: *first -> second" actual.1 && | ||||
| 		test_i18ngrep "renamed: *second -> third" actual.1 && | ||||
|  | ||||
| 		git status --porcelain | grep -v "^?" >actual.2 && | ||||
| 		cat >expected.2 <<-\EOF && | ||||
| 		R  first -> second | ||||
| 		 R second -> third | ||||
| 		EOF | ||||
| 		test_cmp expected.2 actual.2 && | ||||
|  | ||||
| 		hash=12f00e90b6ef79117ce6e650416b8cf517099b78 && | ||||
| 		git status --porcelain=v2 | grep -v "^?" >actual.3 && | ||||
| 		cat >expected.3 <<-EOF && | ||||
| 		2 R. N... 100644 100644 100644 $hash $hash R100 second	first | ||||
| 		2 .R N... 100644 100644 100644 $hash $hash R100 third	second | ||||
| 		EOF | ||||
| 		test_cmp expected.3 actual.3 | ||||
| 	) | ||||
| ' | ||||
|  | ||||
| test_done | ||||
|  | ||||
|  |  | |||
							
								
								
									
										83
									
								
								wt-status.c
								
								
								
								
							
							
						
						
									
										83
									
								
								wt-status.c
								
								
								
								
							|  | @ -360,8 +360,6 @@ static void wt_longstatus_print_change_data(struct wt_status *s, | |||
| 	switch (change_type) { | ||||
| 	case WT_STATUS_UPDATED: | ||||
| 		status = d->index_status; | ||||
| 		if (d->head_path) | ||||
| 			one_name = d->head_path; | ||||
| 		break; | ||||
| 	case WT_STATUS_CHANGED: | ||||
| 		if (d->new_submodule_commits || d->dirty_submodule) { | ||||
|  | @ -382,6 +380,14 @@ static void wt_longstatus_print_change_data(struct wt_status *s, | |||
| 		    change_type); | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Only pick up the rename it's relevant. If the rename is for | ||||
| 	 * the changed section and we're printing the updated section, | ||||
| 	 * ignore it. | ||||
| 	 */ | ||||
| 	if (d->rename_status == status) | ||||
| 		one_name = d->rename_source; | ||||
|  | ||||
| 	one = quote_path(one_name, s->prefix, &onebuf); | ||||
| 	two = quote_path(two_name, s->prefix, &twobuf); | ||||
|  | ||||
|  | @ -391,7 +397,7 @@ static void wt_longstatus_print_change_data(struct wt_status *s, | |||
| 		die("BUG: unhandled diff status %c", status); | ||||
| 	len = label_width - utf8_strwidth(what); | ||||
| 	assert(len >= 0); | ||||
| 	if (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED) | ||||
| 	if (one_name != two_name) | ||||
| 		status_printf_more(s, c, "%s%.*s%s -> %s", | ||||
| 				   what, len, padding, one, two); | ||||
| 	else | ||||
|  | @ -406,7 +412,8 @@ static void wt_longstatus_print_change_data(struct wt_status *s, | |||
| 	strbuf_release(&twobuf); | ||||
| } | ||||
|  | ||||
| static char short_submodule_status(struct wt_status_change_data *d) { | ||||
| static char short_submodule_status(struct wt_status_change_data *d) | ||||
| { | ||||
| 	if (d->new_submodule_commits) | ||||
| 		return 'M'; | ||||
| 	if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED) | ||||
|  | @ -432,7 +439,7 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q, | |||
| 		struct wt_status_change_data *d; | ||||
|  | ||||
| 		p = q->queue[i]; | ||||
| 		it = string_list_insert(&s->change, p->one->path); | ||||
| 		it = string_list_insert(&s->change, p->two->path); | ||||
| 		d = it->util; | ||||
| 		if (!d) { | ||||
| 			d = xcalloc(1, sizeof(*d)); | ||||
|  | @ -459,6 +466,14 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q, | |||
| 			/* mode_worktree is zero for a delete. */ | ||||
| 			break; | ||||
|  | ||||
| 		case DIFF_STATUS_COPIED: | ||||
| 		case DIFF_STATUS_RENAMED: | ||||
| 			if (d->rename_status) | ||||
| 				die("BUG: multiple renames on the same target? how?"); | ||||
| 			d->rename_source = xstrdup(p->one->path); | ||||
| 			d->rename_score = p->score * 100 / MAX_SCORE; | ||||
| 			d->rename_status = p->status; | ||||
| 			/* fallthru */ | ||||
| 		case DIFF_STATUS_MODIFIED: | ||||
| 		case DIFF_STATUS_TYPE_CHANGED: | ||||
| 		case DIFF_STATUS_UNMERGED: | ||||
|  | @ -467,8 +482,8 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q, | |||
| 			oidcpy(&d->oid_index, &p->one->oid); | ||||
| 			break; | ||||
|  | ||||
| 		case DIFF_STATUS_UNKNOWN: | ||||
| 			die("BUG: worktree status unknown???"); | ||||
| 		default: | ||||
| 			die("BUG: unhandled diff-files status '%c'", p->status); | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
|  | @ -530,8 +545,11 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q, | |||
|  | ||||
| 		case DIFF_STATUS_COPIED: | ||||
| 		case DIFF_STATUS_RENAMED: | ||||
| 			d->head_path = xstrdup(p->one->path); | ||||
| 			d->score = p->score * 100 / MAX_SCORE; | ||||
| 			if (d->rename_status) | ||||
| 				die("BUG: multiple renames on the same target? how?"); | ||||
| 			d->rename_source = xstrdup(p->one->path); | ||||
| 			d->rename_score = p->score * 100 / MAX_SCORE; | ||||
| 			d->rename_status = p->status; | ||||
| 			/* fallthru */ | ||||
| 		case DIFF_STATUS_MODIFIED: | ||||
| 		case DIFF_STATUS_TYPE_CHANGED: | ||||
|  | @ -548,6 +566,10 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q, | |||
| 			 * values in these fields. | ||||
| 			 */ | ||||
| 			break; | ||||
|  | ||||
| 		default: | ||||
| 			die("BUG: unhandled diff-index status '%c'", p->status); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -602,7 +624,7 @@ static void wt_status_collect_changes_index(struct wt_status *s) | |||
| 	rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; | ||||
| 	rev.diffopt.format_callback = wt_status_collect_updated_cb; | ||||
| 	rev.diffopt.format_callback_data = s; | ||||
| 	rev.diffopt.detect_rename = 1; | ||||
| 	rev.diffopt.detect_rename = DIFF_DETECT_RENAME; | ||||
| 	rev.diffopt.rename_limit = 200; | ||||
| 	rev.diffopt.break_opt = 0; | ||||
| 	copy_pathspec(&rev.prune_data, &s->pathspec); | ||||
|  | @ -962,7 +984,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s) | |||
| 	setup_revisions(0, NULL, &rev, &opt); | ||||
|  | ||||
| 	rev.diffopt.output_format |= DIFF_FORMAT_PATCH; | ||||
| 	rev.diffopt.detect_rename = 1; | ||||
| 	rev.diffopt.detect_rename = DIFF_DETECT_RENAME; | ||||
| 	rev.diffopt.file = s->fp; | ||||
| 	rev.diffopt.close_file = 0; | ||||
| 	/* | ||||
|  | @ -1719,13 +1741,14 @@ static void wt_shortstatus_status(struct string_list_item *it, | |||
| 	putchar(' '); | ||||
| 	if (s->null_termination) { | ||||
| 		fprintf(stdout, "%s%c", it->string, 0); | ||||
| 		if (d->head_path) | ||||
| 			fprintf(stdout, "%s%c", d->head_path, 0); | ||||
| 		if (d->rename_source) | ||||
| 			fprintf(stdout, "%s%c", d->rename_source, 0); | ||||
| 	} else { | ||||
| 		struct strbuf onebuf = STRBUF_INIT; | ||||
| 		const char *one; | ||||
| 		if (d->head_path) { | ||||
| 			one = quote_path(d->head_path, s->prefix, &onebuf); | ||||
|  | ||||
| 		if (d->rename_source) { | ||||
| 			one = quote_path(d->rename_source, s->prefix, &onebuf); | ||||
| 			if (*one != '"' && strchr(one, ' ') != NULL) { | ||||
| 				putchar('"'); | ||||
| 				strbuf_addch(&onebuf, '"'); | ||||
|  | @ -2030,10 +2053,10 @@ static void wt_porcelain_v2_print_changed_entry( | |||
| 	struct wt_status *s) | ||||
| { | ||||
| 	struct wt_status_change_data *d = it->util; | ||||
| 	struct strbuf buf_index = STRBUF_INIT; | ||||
| 	struct strbuf buf_head = STRBUF_INIT; | ||||
| 	const char *path_index = NULL; | ||||
| 	const char *path_head = NULL; | ||||
| 	struct strbuf buf = STRBUF_INIT; | ||||
| 	struct strbuf buf_from = STRBUF_INIT; | ||||
| 	const char *path = NULL; | ||||
| 	const char *path_from = NULL; | ||||
| 	char key[3]; | ||||
| 	char submodule_token[5]; | ||||
| 	char sep_char, eol_char; | ||||
|  | @ -2052,8 +2075,8 @@ static void wt_porcelain_v2_print_changed_entry( | |||
| 		 */ | ||||
| 		sep_char = '\0'; | ||||
| 		eol_char = '\0'; | ||||
| 		path_index = it->string; | ||||
| 		path_head = d->head_path; | ||||
| 		path = it->string; | ||||
| 		path_from = d->rename_source; | ||||
| 	} else { | ||||
| 		/* | ||||
| 		 * Path(s) are C-quoted if necessary. Current path is ALWAYS first. | ||||
|  | @ -2063,27 +2086,27 @@ static void wt_porcelain_v2_print_changed_entry( | |||
| 		 */ | ||||
| 		sep_char = '\t'; | ||||
| 		eol_char = '\n'; | ||||
| 		path_index = quote_path(it->string, s->prefix, &buf_index); | ||||
| 		if (d->head_path) | ||||
| 			path_head = quote_path(d->head_path, s->prefix, &buf_head); | ||||
| 		path = quote_path(it->string, s->prefix, &buf); | ||||
| 		if (d->rename_source) | ||||
| 			path_from = quote_path(d->rename_source, s->prefix, &buf_from); | ||||
| 	} | ||||
|  | ||||
| 	if (path_head) | ||||
| 	if (path_from) | ||||
| 		fprintf(s->fp, "2 %s %s %06o %06o %06o %s %s %c%d %s%c%s%c", | ||||
| 				key, submodule_token, | ||||
| 				d->mode_head, d->mode_index, d->mode_worktree, | ||||
| 				oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index), | ||||
| 				key[0], d->score, | ||||
| 				path_index, sep_char, path_head, eol_char); | ||||
| 				d->rename_status, d->rename_score, | ||||
| 				path, sep_char, path_from, eol_char); | ||||
| 	else | ||||
| 		fprintf(s->fp, "1 %s %s %06o %06o %06o %s %s %s%c", | ||||
| 				key, submodule_token, | ||||
| 				d->mode_head, d->mode_index, d->mode_worktree, | ||||
| 				oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index), | ||||
| 				path_index, eol_char); | ||||
| 				path, eol_char); | ||||
|  | ||||
| 	strbuf_release(&buf_index); | ||||
| 	strbuf_release(&buf_head); | ||||
| 	strbuf_release(&buf); | ||||
| 	strbuf_release(&buf_from); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  |  | |||
|  | @ -44,10 +44,11 @@ struct wt_status_change_data { | |||
| 	int worktree_status; | ||||
| 	int index_status; | ||||
| 	int stagemask; | ||||
| 	int score; | ||||
| 	int mode_head, mode_index, mode_worktree; | ||||
| 	struct object_id oid_head, oid_index; | ||||
| 	char *head_path; | ||||
| 	int rename_status; | ||||
| 	int rename_score; | ||||
| 	char *rename_source; | ||||
| 	unsigned dirty_submodule       : 2; | ||||
| 	unsigned new_submodule_commits : 1; | ||||
| }; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano