Merge branch 'bc/reread-attributes-during-rebase'
The "git am" based backend of "git rebase" ignored the result of updating ".gitattributes" done in one step when replaying subsequent steps. * bc/reread-attributes-during-rebase: am: reload .gitattributes after patching it path: add a function to check for path suffixmaint
						commit
						c8ada15456
					
				
							
								
								
									
										11
									
								
								apply.c
								
								
								
								
							
							
						
						
									
										11
									
								
								apply.c
								
								
								
								
							|  | @ -4643,6 +4643,7 @@ static int apply_patch(struct apply_state *state, | ||||||
| 	struct patch *list = NULL, **listp = &list; | 	struct patch *list = NULL, **listp = &list; | ||||||
| 	int skipped_patch = 0; | 	int skipped_patch = 0; | ||||||
| 	int res = 0; | 	int res = 0; | ||||||
|  | 	int flush_attributes = 0; | ||||||
|  |  | ||||||
| 	state->patch_input_file = filename; | 	state->patch_input_file = filename; | ||||||
| 	if (read_patch_file(&buf, fd) < 0) | 	if (read_patch_file(&buf, fd) < 0) | ||||||
|  | @ -4670,6 +4671,14 @@ static int apply_patch(struct apply_state *state, | ||||||
| 			patch_stats(state, patch); | 			patch_stats(state, patch); | ||||||
| 			*listp = patch; | 			*listp = patch; | ||||||
| 			listp = &patch->next; | 			listp = &patch->next; | ||||||
|  |  | ||||||
|  | 			if ((patch->new_name && | ||||||
|  | 			     ends_with_path_components(patch->new_name, | ||||||
|  | 						       GITATTRIBUTES_FILE)) || | ||||||
|  | 			    (patch->old_name && | ||||||
|  | 			     ends_with_path_components(patch->old_name, | ||||||
|  | 						       GITATTRIBUTES_FILE))) | ||||||
|  | 				flush_attributes = 1; | ||||||
| 		} | 		} | ||||||
| 		else { | 		else { | ||||||
| 			if (state->apply_verbosity > verbosity_normal) | 			if (state->apply_verbosity > verbosity_normal) | ||||||
|  | @ -4746,6 +4755,8 @@ static int apply_patch(struct apply_state *state, | ||||||
| 	if (state->summary && state->apply_verbosity > verbosity_silent) | 	if (state->summary && state->apply_verbosity > verbosity_silent) | ||||||
| 		summary_patch_list(list); | 		summary_patch_list(list); | ||||||
|  |  | ||||||
|  | 	if (flush_attributes) | ||||||
|  | 		reset_parsed_attributes(); | ||||||
| end: | end: | ||||||
| 	free_patch_list(list); | 	free_patch_list(list); | ||||||
| 	strbuf_release(&buf); | 	strbuf_release(&buf); | ||||||
|  |  | ||||||
							
								
								
									
										21
									
								
								convert.c
								
								
								
								
							
							
						
						
									
										21
									
								
								convert.c
								
								
								
								
							|  | @ -8,6 +8,7 @@ | ||||||
| #include "pkt-line.h" | #include "pkt-line.h" | ||||||
| #include "sub-process.h" | #include "sub-process.h" | ||||||
| #include "utf8.h" | #include "utf8.h" | ||||||
|  | #include "ll-merge.h" | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * convert.c - convert a file when checking it out and checking it in. |  * convert.c - convert a file when checking it out and checking it in. | ||||||
|  | @ -1293,10 +1294,11 @@ struct conv_attrs { | ||||||
| 	const char *working_tree_encoding; /* Supported encoding or default encoding if NULL */ | 	const char *working_tree_encoding; /* Supported encoding or default encoding if NULL */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | static struct attr_check *check; | ||||||
|  |  | ||||||
| static void convert_attrs(const struct index_state *istate, | static void convert_attrs(const struct index_state *istate, | ||||||
| 			  struct conv_attrs *ca, const char *path) | 			  struct conv_attrs *ca, const char *path) | ||||||
| { | { | ||||||
| 	static struct attr_check *check; |  | ||||||
| 	struct attr_check_item *ccheck = NULL; | 	struct attr_check_item *ccheck = NULL; | ||||||
|  |  | ||||||
| 	if (!check) { | 	if (!check) { | ||||||
|  | @ -1339,6 +1341,23 @@ static void convert_attrs(const struct index_state *istate, | ||||||
| 		ca->crlf_action = CRLF_AUTO_INPUT; | 		ca->crlf_action = CRLF_AUTO_INPUT; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void reset_parsed_attributes(void) | ||||||
|  | { | ||||||
|  | 	struct convert_driver *drv, *next; | ||||||
|  |  | ||||||
|  | 	attr_check_free(check); | ||||||
|  | 	check = NULL; | ||||||
|  | 	reset_merge_attributes(); | ||||||
|  |  | ||||||
|  | 	for (drv = user_convert; drv; drv = next) { | ||||||
|  | 		next = drv->next; | ||||||
|  | 		free((void *)drv->name); | ||||||
|  | 		free(drv); | ||||||
|  | 	} | ||||||
|  | 	user_convert = NULL; | ||||||
|  | 	user_convert_tail = NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
| int would_convert_to_git_filter_fd(const struct index_state *istate, const char *path) | int would_convert_to_git_filter_fd(const struct index_state *istate, const char *path) | ||||||
| { | { | ||||||
| 	struct conv_attrs ca; | 	struct conv_attrs ca; | ||||||
|  |  | ||||||
|  | @ -94,6 +94,12 @@ void convert_to_git_filter_fd(const struct index_state *istate, | ||||||
| int would_convert_to_git_filter_fd(const struct index_state *istate, | int would_convert_to_git_filter_fd(const struct index_state *istate, | ||||||
| 				   const char *path); | 				   const char *path); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Reset the internal list of attributes used by convert_to_git and | ||||||
|  |  * convert_to_working_tree. | ||||||
|  |  */ | ||||||
|  | void reset_parsed_attributes(void); | ||||||
|  |  | ||||||
| /***************************************************************** | /***************************************************************** | ||||||
|  * |  * | ||||||
|  * Streaming conversion support |  * Streaming conversion support | ||||||
|  |  | ||||||
							
								
								
									
										19
									
								
								ll-merge.c
								
								
								
								
							
							
						
						
									
										19
									
								
								ll-merge.c
								
								
								
								
							|  | @ -32,6 +32,20 @@ struct ll_merge_driver { | ||||||
| 	char *cmdline; | 	char *cmdline; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | static struct attr_check *merge_attributes; | ||||||
|  | static struct attr_check *load_merge_attributes(void) | ||||||
|  | { | ||||||
|  | 	if (!merge_attributes) | ||||||
|  | 		merge_attributes = attr_check_initl("merge", "conflict-marker-size", NULL); | ||||||
|  | 	return merge_attributes; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void reset_merge_attributes(void) | ||||||
|  | { | ||||||
|  | 	attr_check_free(merge_attributes); | ||||||
|  | 	merge_attributes = NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Built-in low-levels |  * Built-in low-levels | ||||||
|  */ |  */ | ||||||
|  | @ -354,7 +368,7 @@ int ll_merge(mmbuffer_t *result_buf, | ||||||
| 	     struct index_state *istate, | 	     struct index_state *istate, | ||||||
| 	     const struct ll_merge_options *opts) | 	     const struct ll_merge_options *opts) | ||||||
| { | { | ||||||
| 	static struct attr_check *check; | 	struct attr_check *check = load_merge_attributes(); | ||||||
| 	static const struct ll_merge_options default_opts; | 	static const struct ll_merge_options default_opts; | ||||||
| 	const char *ll_driver_name = NULL; | 	const char *ll_driver_name = NULL; | ||||||
| 	int marker_size = DEFAULT_CONFLICT_MARKER_SIZE; | 	int marker_size = DEFAULT_CONFLICT_MARKER_SIZE; | ||||||
|  | @ -369,9 +383,6 @@ int ll_merge(mmbuffer_t *result_buf, | ||||||
| 		normalize_file(theirs, path, istate); | 		normalize_file(theirs, path, istate); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (!check) |  | ||||||
| 		check = attr_check_initl("merge", "conflict-marker-size", NULL); |  | ||||||
|  |  | ||||||
| 	git_check_attr(istate, path, check); | 	git_check_attr(istate, path, check); | ||||||
| 	ll_driver_name = check->items[0].value; | 	ll_driver_name = check->items[0].value; | ||||||
| 	if (check->items[1].value) { | 	if (check->items[1].value) { | ||||||
|  |  | ||||||
|  | @ -26,5 +26,6 @@ int ll_merge(mmbuffer_t *result_buf, | ||||||
| 	     const struct ll_merge_options *opts); | 	     const struct ll_merge_options *opts); | ||||||
|  |  | ||||||
| int ll_merge_marker_size(struct index_state *istate, const char *path); | int ll_merge_marker_size(struct index_state *istate, const char *path); | ||||||
|  | void reset_merge_attributes(void); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
							
								
								
									
										57
									
								
								path.c
								
								
								
								
							
							
						
						
									
										57
									
								
								path.c
								
								
								
								
							|  | @ -1220,6 +1220,43 @@ static inline int chomp_trailing_dir_sep(const char *path, int len) | ||||||
| 	return len; | 	return len; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * If path ends with suffix (complete path components), returns the offset of | ||||||
|  |  * the last character in the path before the suffix (sans trailing directory | ||||||
|  |  * separators), and -1 otherwise. | ||||||
|  |  */ | ||||||
|  | static ssize_t stripped_path_suffix_offset(const char *path, const char *suffix) | ||||||
|  | { | ||||||
|  | 	int path_len = strlen(path), suffix_len = strlen(suffix); | ||||||
|  |  | ||||||
|  | 	while (suffix_len) { | ||||||
|  | 		if (!path_len) | ||||||
|  | 			return -1; | ||||||
|  |  | ||||||
|  | 		if (is_dir_sep(path[path_len - 1])) { | ||||||
|  | 			if (!is_dir_sep(suffix[suffix_len - 1])) | ||||||
|  | 				return -1; | ||||||
|  | 			path_len = chomp_trailing_dir_sep(path, path_len); | ||||||
|  | 			suffix_len = chomp_trailing_dir_sep(suffix, suffix_len); | ||||||
|  | 		} | ||||||
|  | 		else if (path[--path_len] != suffix[--suffix_len]) | ||||||
|  | 			return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (path_len && !is_dir_sep(path[path_len - 1])) | ||||||
|  | 		return -1; | ||||||
|  | 	return chomp_trailing_dir_sep(path, path_len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Returns true if the path ends with components, considering only complete path | ||||||
|  |  * components, and false otherwise. | ||||||
|  |  */ | ||||||
|  | int ends_with_path_components(const char *path, const char *components) | ||||||
|  | { | ||||||
|  | 	return stripped_path_suffix_offset(path, components) != -1; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * If path ends with suffix (complete path components), returns the |  * If path ends with suffix (complete path components), returns the | ||||||
|  * part before suffix (sans trailing directory separators). |  * part before suffix (sans trailing directory separators). | ||||||
|  | @ -1227,25 +1264,9 @@ static inline int chomp_trailing_dir_sep(const char *path, int len) | ||||||
|  */ |  */ | ||||||
| char *strip_path_suffix(const char *path, const char *suffix) | char *strip_path_suffix(const char *path, const char *suffix) | ||||||
| { | { | ||||||
| 	int path_len = strlen(path), suffix_len = strlen(suffix); | 	ssize_t offset = stripped_path_suffix_offset(path, suffix); | ||||||
|  |  | ||||||
| 	while (suffix_len) { | 	return offset == -1 ? NULL : xstrndup(path, offset); | ||||||
| 		if (!path_len) |  | ||||||
| 			return NULL; |  | ||||||
|  |  | ||||||
| 		if (is_dir_sep(path[path_len - 1])) { |  | ||||||
| 			if (!is_dir_sep(suffix[suffix_len - 1])) |  | ||||||
| 				return NULL; |  | ||||||
| 			path_len = chomp_trailing_dir_sep(path, path_len); |  | ||||||
| 			suffix_len = chomp_trailing_dir_sep(suffix, suffix_len); |  | ||||||
| 		} |  | ||||||
| 		else if (path[--path_len] != suffix[--suffix_len]) |  | ||||||
| 			return NULL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (path_len && !is_dir_sep(path[path_len - 1])) |  | ||||||
| 		return NULL; |  | ||||||
| 	return xstrndup(path, chomp_trailing_dir_sep(path, path_len)); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int daemon_avoid_alias(const char *p) | int daemon_avoid_alias(const char *p) | ||||||
|  |  | ||||||
							
								
								
									
										3
									
								
								path.h
								
								
								
								
							
							
						
						
									
										3
									
								
								path.h
								
								
								
								
							|  | @ -193,4 +193,7 @@ const char *git_path_merge_head(struct repository *r); | ||||||
| const char *git_path_fetch_head(struct repository *r); | const char *git_path_fetch_head(struct repository *r); | ||||||
| const char *git_path_shallow(struct repository *r); | const char *git_path_shallow(struct repository *r); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int ends_with_path_components(const char *path, const char *components); | ||||||
|  |  | ||||||
| #endif /* PATH_H */ | #endif /* PATH_H */ | ||||||
|  |  | ||||||
|  | @ -301,6 +301,42 @@ test_expect_success 'rebase --am and --show-current-patch' ' | ||||||
| 	) | 	) | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | test_expect_success 'rebase --am and .gitattributes' ' | ||||||
|  | 	test_create_repo attributes && | ||||||
|  | 	( | ||||||
|  | 		cd attributes && | ||||||
|  | 		test_commit init && | ||||||
|  | 		git config filter.test.clean "sed -e '\''s/smudged/clean/g'\''" && | ||||||
|  | 		git config filter.test.smudge "sed -e '\''s/clean/smudged/g'\''" && | ||||||
|  |  | ||||||
|  | 		test_commit second && | ||||||
|  | 		git checkout -b test HEAD^ && | ||||||
|  |  | ||||||
|  | 		echo "*.txt filter=test" >.gitattributes && | ||||||
|  | 		git add .gitattributes && | ||||||
|  | 		test_commit third && | ||||||
|  |  | ||||||
|  | 		echo "This text is smudged." >a.txt && | ||||||
|  | 		git add a.txt && | ||||||
|  | 		test_commit fourth && | ||||||
|  |  | ||||||
|  | 		git checkout -b removal HEAD^ && | ||||||
|  | 		git rm .gitattributes && | ||||||
|  | 		git add -u && | ||||||
|  | 		test_commit fifth && | ||||||
|  | 		git cherry-pick test && | ||||||
|  |  | ||||||
|  | 		git checkout test && | ||||||
|  | 		git rebase master && | ||||||
|  | 		grep "smudged" a.txt && | ||||||
|  |  | ||||||
|  | 		git checkout removal && | ||||||
|  | 		git reset --hard && | ||||||
|  | 		git rebase master && | ||||||
|  | 		grep "clean" a.txt | ||||||
|  | 	) | ||||||
|  | ' | ||||||
|  |  | ||||||
| test_expect_success 'rebase--merge.sh and --show-current-patch' ' | test_expect_success 'rebase--merge.sh and --show-current-patch' ' | ||||||
| 	test_create_repo conflict-merge && | 	test_create_repo conflict-merge && | ||||||
| 	( | 	( | ||||||
|  |  | ||||||
|  | @ -1061,4 +1061,56 @@ test_expect_success 'am --quit keeps HEAD where it is' ' | ||||||
| 	test_cmp expected actual | 	test_cmp expected actual | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | test_expect_success 'am and .gitattibutes' ' | ||||||
|  | 	test_create_repo attributes && | ||||||
|  | 	( | ||||||
|  | 		cd attributes && | ||||||
|  | 		test_commit init && | ||||||
|  | 		git config filter.test.clean "sed -e '\''s/smudged/clean/g'\''" && | ||||||
|  | 		git config filter.test.smudge "sed -e '\''s/clean/smudged/g'\''" && | ||||||
|  |  | ||||||
|  | 		test_commit second && | ||||||
|  | 		git checkout -b test HEAD^ && | ||||||
|  |  | ||||||
|  | 		echo "*.txt filter=test conflict-marker-size=10" >.gitattributes && | ||||||
|  | 		git add .gitattributes && | ||||||
|  | 		test_commit third && | ||||||
|  |  | ||||||
|  | 		echo "This text is smudged." >a.txt && | ||||||
|  | 		git add a.txt && | ||||||
|  | 		test_commit fourth && | ||||||
|  |  | ||||||
|  | 		git checkout -b removal HEAD^ && | ||||||
|  | 		git rm .gitattributes && | ||||||
|  | 		git add -u && | ||||||
|  | 		test_commit fifth && | ||||||
|  | 		git cherry-pick test && | ||||||
|  |  | ||||||
|  | 		git checkout -b conflict third && | ||||||
|  | 		echo "This text is different." >a.txt && | ||||||
|  | 		git add a.txt && | ||||||
|  | 		test_commit sixth && | ||||||
|  |  | ||||||
|  | 		git checkout test && | ||||||
|  | 		git format-patch --stdout master..HEAD >patches && | ||||||
|  | 		git reset --hard master && | ||||||
|  | 		git am patches && | ||||||
|  | 		grep "smudged" a.txt && | ||||||
|  |  | ||||||
|  | 		git checkout removal && | ||||||
|  | 		git reset --hard && | ||||||
|  | 		git format-patch --stdout master..HEAD >patches && | ||||||
|  | 		git reset --hard master && | ||||||
|  | 		git am patches && | ||||||
|  | 		grep "clean" a.txt && | ||||||
|  |  | ||||||
|  | 		git checkout conflict && | ||||||
|  | 		git reset --hard && | ||||||
|  | 		git format-patch --stdout master..HEAD >patches && | ||||||
|  | 		git reset --hard fourth && | ||||||
|  | 		test_must_fail git am -3 patches && | ||||||
|  | 		grep "<<<<<<<<<<" a.txt | ||||||
|  | 	) | ||||||
|  | ' | ||||||
|  |  | ||||||
| test_done | test_done | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano