Allow the user to control the verbosity of merge-recursive.
Junio C Hamano <junkio@cox.net> writes: > > I think the output from merge-recursive can be categorized into 5 > verbosity levels: > > 1. "CONFLICT", "Rename", "Adding here instead due to D/F conflict" > (outermost) > > 2. "Auto-merged successfully" (outermost) > > 3. The first "Merging X with Y". > > 4. outermost "Merging:\ntitle1\ntitle2". > > 5. outermost "found N common ancestors\nancestor1\nancestor2\n..." > and anything from inner merge. > > I would prefer the default verbosity level to be 2 (that is, show > both 1 and 2). and this change makes it so. I think level 3 is probably pointless as its only one line of output above level 2, but I can see how some users may want to view it but not view the slightly more verbose output of level 4. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Junio C Hamano <junkio@cox.net>maint
							parent
							
								
									63889639bb
								
							
						
					
					
						commit
						8c3275abca
					
				|  | @ -321,6 +321,13 @@ merge.summary:: | ||||||
| 	Whether to include summaries of merged commits in newly created | 	Whether to include summaries of merged commits in newly created | ||||||
| 	merge commit messages. False by default. | 	merge commit messages. False by default. | ||||||
|  |  | ||||||
|  | merge.verbosity:: | ||||||
|  | 	Controls the amount of output shown by the recursive merge | ||||||
|  | 	strategy.  Level 0 outputs nothing except a final error | ||||||
|  | 	message if conflicts were detected. Level 1 outputs only | ||||||
|  | 	conflicts, 2 outputs conflicts and file changes.  Level 5 and | ||||||
|  | 	above outputs debugging information.  The default is level 2. | ||||||
|  |  | ||||||
| pack.window:: | pack.window:: | ||||||
| 	The size of the window used by gitlink:git-pack-objects[1] when no | 	The size of the window used by gitlink:git-pack-objects[1] when no | ||||||
| 	window size is given on the command line. Defaults to 10. | 	window size is given on the command line. Defaults to 10. | ||||||
|  |  | ||||||
|  | @ -71,17 +71,25 @@ static struct path_list current_file_set = {NULL, 0, 0, 1}; | ||||||
| static struct path_list current_directory_set = {NULL, 0, 0, 1}; | static struct path_list current_directory_set = {NULL, 0, 0, 1}; | ||||||
|  |  | ||||||
| static int call_depth = 0; | static int call_depth = 0; | ||||||
|  | static int verbosity = 2; | ||||||
|  |  | ||||||
| static void output(const char *fmt, ...) | static int show (int v) | ||||||
|  | { | ||||||
|  | 	return (!call_depth && verbosity >= v) || verbosity >= 5; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void output(int v, const char *fmt, ...) | ||||||
| { | { | ||||||
| 	va_list args; | 	va_list args; | ||||||
| 	int i; |  | ||||||
| 	for (i = call_depth; i--;) |  | ||||||
| 		fputs("  ", stdout); |  | ||||||
| 	va_start(args, fmt); | 	va_start(args, fmt); | ||||||
| 	vfprintf(stdout, fmt, args); | 	if (show(v)) { | ||||||
|  | 		int i; | ||||||
|  | 		for (i = call_depth; i--;) | ||||||
|  | 			fputs("  ", stdout); | ||||||
|  | 		vfprintf(stdout, fmt, args); | ||||||
|  | 		fputc('\n', stdout); | ||||||
|  | 	} | ||||||
| 	va_end(args); | 	va_end(args); | ||||||
| 	fputc('\n', stdout); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void output_commit_title(struct commit *commit) | static void output_commit_title(struct commit *commit) | ||||||
|  | @ -640,13 +648,13 @@ static void conflict_rename_rename(struct rename *ren1, | ||||||
| 	const char *dst_name2 = ren2_dst; | 	const char *dst_name2 = ren2_dst; | ||||||
| 	if (path_list_has_path(¤t_directory_set, ren1_dst)) { | 	if (path_list_has_path(¤t_directory_set, ren1_dst)) { | ||||||
| 		dst_name1 = del[delp++] = unique_path(ren1_dst, branch1); | 		dst_name1 = del[delp++] = unique_path(ren1_dst, branch1); | ||||||
| 		output("%s is a directory in %s adding as %s instead", | 		output(1, "%s is a directory in %s adding as %s instead", | ||||||
| 		       ren1_dst, branch2, dst_name1); | 		       ren1_dst, branch2, dst_name1); | ||||||
| 		remove_file(0, ren1_dst, 0); | 		remove_file(0, ren1_dst, 0); | ||||||
| 	} | 	} | ||||||
| 	if (path_list_has_path(¤t_directory_set, ren2_dst)) { | 	if (path_list_has_path(¤t_directory_set, ren2_dst)) { | ||||||
| 		dst_name2 = del[delp++] = unique_path(ren2_dst, branch2); | 		dst_name2 = del[delp++] = unique_path(ren2_dst, branch2); | ||||||
| 		output("%s is a directory in %s adding as %s instead", | 		output(1, "%s is a directory in %s adding as %s instead", | ||||||
| 		       ren2_dst, branch1, dst_name2); | 		       ren2_dst, branch1, dst_name2); | ||||||
| 		remove_file(0, ren2_dst, 0); | 		remove_file(0, ren2_dst, 0); | ||||||
| 	} | 	} | ||||||
|  | @ -660,7 +668,7 @@ static void conflict_rename_dir(struct rename *ren1, | ||||||
| 				const char *branch1) | 				const char *branch1) | ||||||
| { | { | ||||||
| 	char *new_path = unique_path(ren1->pair->two->path, branch1); | 	char *new_path = unique_path(ren1->pair->two->path, branch1); | ||||||
| 	output("Renaming %s to %s instead", ren1->pair->one->path, new_path); | 	output(1, "Renaming %s to %s instead", ren1->pair->one->path, new_path); | ||||||
| 	remove_file(0, ren1->pair->two->path, 0); | 	remove_file(0, ren1->pair->two->path, 0); | ||||||
| 	update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path); | 	update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path); | ||||||
| 	free(new_path); | 	free(new_path); | ||||||
|  | @ -673,7 +681,7 @@ static void conflict_rename_rename_2(struct rename *ren1, | ||||||
| { | { | ||||||
| 	char *new_path1 = unique_path(ren1->pair->two->path, branch1); | 	char *new_path1 = unique_path(ren1->pair->two->path, branch1); | ||||||
| 	char *new_path2 = unique_path(ren2->pair->two->path, branch2); | 	char *new_path2 = unique_path(ren2->pair->two->path, branch2); | ||||||
| 	output("Renaming %s to %s and %s to %s instead", | 	output(1, "Renaming %s to %s and %s to %s instead", | ||||||
| 	       ren1->pair->one->path, new_path1, | 	       ren1->pair->one->path, new_path1, | ||||||
| 	       ren2->pair->one->path, new_path2); | 	       ren2->pair->one->path, new_path2); | ||||||
| 	remove_file(0, ren1->pair->two->path, 0); | 	remove_file(0, ren1->pair->two->path, 0); | ||||||
|  | @ -766,7 +774,7 @@ static int process_renames(struct path_list *a_renames, | ||||||
| 			ren2->processed = 1; | 			ren2->processed = 1; | ||||||
| 			if (strcmp(ren1_dst, ren2_dst) != 0) { | 			if (strcmp(ren1_dst, ren2_dst) != 0) { | ||||||
| 				clean_merge = 0; | 				clean_merge = 0; | ||||||
| 				output("CONFLICT (rename/rename): " | 				output(1, "CONFLICT (rename/rename): " | ||||||
| 				       "Rename %s->%s in branch %s " | 				       "Rename %s->%s in branch %s " | ||||||
| 				       "rename %s->%s in %s", | 				       "rename %s->%s in %s", | ||||||
| 				       src, ren1_dst, branch1, | 				       src, ren1_dst, branch1, | ||||||
|  | @ -781,13 +789,13 @@ static int process_renames(struct path_list *a_renames, | ||||||
| 						 branch1, | 						 branch1, | ||||||
| 						 branch2); | 						 branch2); | ||||||
| 				if (mfi.merge || !mfi.clean) | 				if (mfi.merge || !mfi.clean) | ||||||
| 					output("Renaming %s->%s", src, ren1_dst); | 					output(1, "Renaming %s->%s", src, ren1_dst); | ||||||
|  |  | ||||||
| 				if (mfi.merge) | 				if (mfi.merge) | ||||||
| 					output("Auto-merging %s", ren1_dst); | 					output(2, "Auto-merging %s", ren1_dst); | ||||||
|  |  | ||||||
| 				if (!mfi.clean) { | 				if (!mfi.clean) { | ||||||
| 					output("CONFLICT (content): merge conflict in %s", | 					output(1, "CONFLICT (content): merge conflict in %s", | ||||||
| 					       ren1_dst); | 					       ren1_dst); | ||||||
| 					clean_merge = 0; | 					clean_merge = 0; | ||||||
|  |  | ||||||
|  | @ -818,14 +826,14 @@ static int process_renames(struct path_list *a_renames, | ||||||
|  |  | ||||||
| 			if (path_list_has_path(¤t_directory_set, ren1_dst)) { | 			if (path_list_has_path(¤t_directory_set, ren1_dst)) { | ||||||
| 				clean_merge = 0; | 				clean_merge = 0; | ||||||
| 				output("CONFLICT (rename/directory): Rename %s->%s in %s " | 				output(1, "CONFLICT (rename/directory): Rename %s->%s in %s " | ||||||
| 				       " directory %s added in %s", | 				       " directory %s added in %s", | ||||||
| 				       ren1_src, ren1_dst, branch1, | 				       ren1_src, ren1_dst, branch1, | ||||||
| 				       ren1_dst, branch2); | 				       ren1_dst, branch2); | ||||||
| 				conflict_rename_dir(ren1, branch1); | 				conflict_rename_dir(ren1, branch1); | ||||||
| 			} else if (sha_eq(src_other.sha1, null_sha1)) { | 			} else if (sha_eq(src_other.sha1, null_sha1)) { | ||||||
| 				clean_merge = 0; | 				clean_merge = 0; | ||||||
| 				output("CONFLICT (rename/delete): Rename %s->%s in %s " | 				output(1, "CONFLICT (rename/delete): Rename %s->%s in %s " | ||||||
| 				       "and deleted in %s", | 				       "and deleted in %s", | ||||||
| 				       ren1_src, ren1_dst, branch1, | 				       ren1_src, ren1_dst, branch1, | ||||||
| 				       branch2); | 				       branch2); | ||||||
|  | @ -834,18 +842,18 @@ static int process_renames(struct path_list *a_renames, | ||||||
| 				const char *new_path; | 				const char *new_path; | ||||||
| 				clean_merge = 0; | 				clean_merge = 0; | ||||||
| 				try_merge = 1; | 				try_merge = 1; | ||||||
| 				output("CONFLICT (rename/add): Rename %s->%s in %s. " | 				output(1, "CONFLICT (rename/add): Rename %s->%s in %s. " | ||||||
| 				       "%s added in %s", | 				       "%s added in %s", | ||||||
| 				       ren1_src, ren1_dst, branch1, | 				       ren1_src, ren1_dst, branch1, | ||||||
| 				       ren1_dst, branch2); | 				       ren1_dst, branch2); | ||||||
| 				new_path = unique_path(ren1_dst, branch2); | 				new_path = unique_path(ren1_dst, branch2); | ||||||
| 				output("Adding as %s instead", new_path); | 				output(1, "Adding as %s instead", new_path); | ||||||
| 				update_file(0, dst_other.sha1, dst_other.mode, new_path); | 				update_file(0, dst_other.sha1, dst_other.mode, new_path); | ||||||
| 			} else if ((item = path_list_lookup(ren1_dst, renames2Dst))) { | 			} else if ((item = path_list_lookup(ren1_dst, renames2Dst))) { | ||||||
| 				ren2 = item->util; | 				ren2 = item->util; | ||||||
| 				clean_merge = 0; | 				clean_merge = 0; | ||||||
| 				ren2->processed = 1; | 				ren2->processed = 1; | ||||||
| 				output("CONFLICT (rename/rename): Rename %s->%s in %s. " | 				output(1, "CONFLICT (rename/rename): Rename %s->%s in %s. " | ||||||
| 				       "Rename %s->%s in %s", | 				       "Rename %s->%s in %s", | ||||||
| 				       ren1_src, ren1_dst, branch1, | 				       ren1_src, ren1_dst, branch1, | ||||||
| 				       ren2->pair->one->path, ren2->pair->two->path, branch2); | 				       ren2->pair->one->path, ren2->pair->two->path, branch2); | ||||||
|  | @ -870,11 +878,11 @@ static int process_renames(struct path_list *a_renames, | ||||||
| 						a_branch, b_branch); | 						a_branch, b_branch); | ||||||
|  |  | ||||||
| 				if (mfi.merge || !mfi.clean) | 				if (mfi.merge || !mfi.clean) | ||||||
| 					output("Renaming %s => %s", ren1_src, ren1_dst); | 					output(1, "Renaming %s => %s", ren1_src, ren1_dst); | ||||||
| 				if (mfi.merge) | 				if (mfi.merge) | ||||||
| 					output("Auto-merging %s", ren1_dst); | 					output(2, "Auto-merging %s", ren1_dst); | ||||||
| 				if (!mfi.clean) { | 				if (!mfi.clean) { | ||||||
| 					output("CONFLICT (rename/modify): Merge conflict in %s", | 					output(1, "CONFLICT (rename/modify): Merge conflict in %s", | ||||||
| 					       ren1_dst); | 					       ren1_dst); | ||||||
| 					clean_merge = 0; | 					clean_merge = 0; | ||||||
|  |  | ||||||
|  | @ -922,20 +930,20 @@ static int process_entry(const char *path, struct stage_data *entry, | ||||||
| 			/* Deleted in both or deleted in one and | 			/* Deleted in both or deleted in one and | ||||||
| 			 * unchanged in the other */ | 			 * unchanged in the other */ | ||||||
| 			if (a_sha) | 			if (a_sha) | ||||||
| 				output("Removing %s", path); | 				output(2, "Removing %s", path); | ||||||
| 			/* do not touch working file if it did not exist */ | 			/* do not touch working file if it did not exist */ | ||||||
| 			remove_file(1, path, !a_sha); | 			remove_file(1, path, !a_sha); | ||||||
| 		} else { | 		} else { | ||||||
| 			/* Deleted in one and changed in the other */ | 			/* Deleted in one and changed in the other */ | ||||||
| 			clean_merge = 0; | 			clean_merge = 0; | ||||||
| 			if (!a_sha) { | 			if (!a_sha) { | ||||||
| 				output("CONFLICT (delete/modify): %s deleted in %s " | 				output(1, "CONFLICT (delete/modify): %s deleted in %s " | ||||||
| 				       "and modified in %s. Version %s of %s left in tree.", | 				       "and modified in %s. Version %s of %s left in tree.", | ||||||
| 				       path, branch1, | 				       path, branch1, | ||||||
| 				       branch2, branch2, path); | 				       branch2, branch2, path); | ||||||
| 				update_file(0, b_sha, b_mode, path); | 				update_file(0, b_sha, b_mode, path); | ||||||
| 			} else { | 			} else { | ||||||
| 				output("CONFLICT (delete/modify): %s deleted in %s " | 				output(1, "CONFLICT (delete/modify): %s deleted in %s " | ||||||
| 				       "and modified in %s. Version %s of %s left in tree.", | 				       "and modified in %s. Version %s of %s left in tree.", | ||||||
| 				       path, branch2, | 				       path, branch2, | ||||||
| 				       branch1, branch1, path); | 				       branch1, branch1, path); | ||||||
|  | @ -968,13 +976,13 @@ static int process_entry(const char *path, struct stage_data *entry, | ||||||
| 		if (path_list_has_path(¤t_directory_set, path)) { | 		if (path_list_has_path(¤t_directory_set, path)) { | ||||||
| 			const char *new_path = unique_path(path, add_branch); | 			const char *new_path = unique_path(path, add_branch); | ||||||
| 			clean_merge = 0; | 			clean_merge = 0; | ||||||
| 			output("CONFLICT (%s): There is a directory with name %s in %s. " | 			output(1, "CONFLICT (%s): There is a directory with name %s in %s. " | ||||||
| 			       "Adding %s as %s", | 			       "Adding %s as %s", | ||||||
| 			       conf, path, other_branch, path, new_path); | 			       conf, path, other_branch, path, new_path); | ||||||
| 			remove_file(0, path, 0); | 			remove_file(0, path, 0); | ||||||
| 			update_file(0, sha, mode, new_path); | 			update_file(0, sha, mode, new_path); | ||||||
| 		} else { | 		} else { | ||||||
| 			output("Adding %s", path); | 			output(2, "Adding %s", path); | ||||||
| 			update_file(1, sha, mode, path); | 			update_file(1, sha, mode, path); | ||||||
| 		} | 		} | ||||||
| 	} else if (a_sha && b_sha) { | 	} else if (a_sha && b_sha) { | ||||||
|  | @ -988,7 +996,7 @@ static int process_entry(const char *path, struct stage_data *entry, | ||||||
| 			reason = "add/add"; | 			reason = "add/add"; | ||||||
| 			o_sha = (unsigned char *)null_sha1; | 			o_sha = (unsigned char *)null_sha1; | ||||||
| 		} | 		} | ||||||
| 		output("Auto-merging %s", path); | 		output(2, "Auto-merging %s", path); | ||||||
| 		o.path = a.path = b.path = (char *)path; | 		o.path = a.path = b.path = (char *)path; | ||||||
| 		hashcpy(o.sha1, o_sha); | 		hashcpy(o.sha1, o_sha); | ||||||
| 		o.mode = o_mode; | 		o.mode = o_mode; | ||||||
|  | @ -1004,7 +1012,7 @@ static int process_entry(const char *path, struct stage_data *entry, | ||||||
| 			update_file(1, mfi.sha, mfi.mode, path); | 			update_file(1, mfi.sha, mfi.mode, path); | ||||||
| 		else { | 		else { | ||||||
| 			clean_merge = 0; | 			clean_merge = 0; | ||||||
| 			output("CONFLICT (%s): Merge conflict in %s", | 			output(1, "CONFLICT (%s): Merge conflict in %s", | ||||||
| 					reason, path); | 					reason, path); | ||||||
|  |  | ||||||
| 			if (index_only) | 			if (index_only) | ||||||
|  | @ -1028,7 +1036,7 @@ static int merge_trees(struct tree *head, | ||||||
| { | { | ||||||
| 	int code, clean; | 	int code, clean; | ||||||
| 	if (sha_eq(common->object.sha1, merge->object.sha1)) { | 	if (sha_eq(common->object.sha1, merge->object.sha1)) { | ||||||
| 		output("Already uptodate!"); | 		output(0, "Already uptodate!"); | ||||||
| 		*result = head; | 		*result = head; | ||||||
| 		return 1; | 		return 1; | ||||||
| 	} | 	} | ||||||
|  | @ -1103,18 +1111,22 @@ static int merge(struct commit *h1, | ||||||
| 	struct tree *mrtree; | 	struct tree *mrtree; | ||||||
| 	int clean; | 	int clean; | ||||||
|  |  | ||||||
| 	output("Merging:"); | 	if (show(4)) { | ||||||
| 	output_commit_title(h1); | 		output(4, "Merging:"); | ||||||
| 	output_commit_title(h2); | 		output_commit_title(h1); | ||||||
|  | 		output_commit_title(h2); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if (!ca) { | 	if (!ca) { | ||||||
| 		ca = get_merge_bases(h1, h2, 1); | 		ca = get_merge_bases(h1, h2, 1); | ||||||
| 		ca = reverse_commit_list(ca); | 		ca = reverse_commit_list(ca); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	output("found %u common ancestor(s):", commit_list_count(ca)); | 	if (show(5)) { | ||||||
| 	for (iter = ca; iter; iter = iter->next) | 		output(5, "found %u common ancestor(s):", commit_list_count(ca)); | ||||||
| 		output_commit_title(iter->item); | 		for (iter = ca; iter; iter = iter->next) | ||||||
|  | 			output_commit_title(iter->item); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	merged_common_ancestors = pop_commit(&ca); | 	merged_common_ancestors = pop_commit(&ca); | ||||||
| 	if (merged_common_ancestors == NULL) { | 	if (merged_common_ancestors == NULL) { | ||||||
|  | @ -1196,6 +1208,15 @@ static struct commit *get_ref(const char *ref) | ||||||
| 	return (struct commit *)object; | 	return (struct commit *)object; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int merge_config(const char *var, const char *value) | ||||||
|  | { | ||||||
|  | 	if (!strcasecmp(var, "merge.verbosity")) { | ||||||
|  | 		verbosity = git_config_int(var, value); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	return git_default_config(var, value); | ||||||
|  | } | ||||||
|  |  | ||||||
| int main(int argc, char *argv[]) | int main(int argc, char *argv[]) | ||||||
| { | { | ||||||
| 	static const char *bases[20]; | 	static const char *bases[20]; | ||||||
|  | @ -1207,7 +1228,9 @@ int main(int argc, char *argv[]) | ||||||
| 	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); | 	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); | ||||||
| 	int index_fd; | 	int index_fd; | ||||||
|  |  | ||||||
| 	git_config(git_default_config); /* core.filemode */ | 	git_config(merge_config); | ||||||
|  | 	if (getenv("GIT_MERGE_VERBOSITY")) | ||||||
|  | 		verbosity = strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10); | ||||||
|  |  | ||||||
| 	if (argc < 4) | 	if (argc < 4) | ||||||
| 		die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]); | 		die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]); | ||||||
|  | @ -1229,7 +1252,8 @@ int main(int argc, char *argv[]) | ||||||
|  |  | ||||||
| 	branch1 = better_branch_name(branch1); | 	branch1 = better_branch_name(branch1); | ||||||
| 	branch2 = better_branch_name(branch2); | 	branch2 = better_branch_name(branch2); | ||||||
| 	printf("Merging %s with %s\n", branch1, branch2); | 	if (show(3)) | ||||||
|  | 		printf("Merging %s with %s\n", branch1, branch2); | ||||||
|  |  | ||||||
| 	index_fd = hold_lock_file_for_update(lock, get_index_file(), 1); | 	index_fd = hold_lock_file_for_update(lock, get_index_file(), 1); | ||||||
|  |  | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Shawn O. Pearce
						Shawn O. Pearce