wt-status.*: better advices for git status added
This patch provides new informative help messages in the display of 'git status' (at the top) during conflicts, rebase, am, bisect or cherry-pick process. The new messages are not shown when using options such as -s or --porcelain. The messages about the current situation of the user are always displayed but the advices on what the user needs to do in order to resume a rebase/bisect/am/commit after resolving conflicts can be hidden by setting advice.statushints to 'false' in the config file. Thus, information about the updated advice.statushints key are added in Documentation/config.txt. Also, the test t7060-wt-status.sh is now working with the new help messages. Tests about suggestions of "git rm" are also added. Signed-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr> Signed-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr> Signed-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr> Signed-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr> Signed-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr> Signed-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									3fe4498197
								
							
						
					
					
						commit
						83c750acde
					
				|  | @ -159,9 +159,10 @@ advice.*:: | ||||||
| 		specified a refspec that isn't your current branch) and | 		specified a refspec that isn't your current branch) and | ||||||
| 		it resulted in a non-fast-forward error. | 		it resulted in a non-fast-forward error. | ||||||
| 	statusHints:: | 	statusHints:: | ||||||
| 		Directions on how to stage/unstage/add shown in the | 		Show directions on how to proceed from the current | ||||||
| 		output of linkgit:git-status[1] and the template shown | 		state in the output of linkgit:git-status[1] and in | ||||||
| 		when writing commit messages. | 		the template shown when writing commit messages in | ||||||
|  | 		linkgit:git-commit[1]. | ||||||
| 	commitBeforeMerge:: | 	commitBeforeMerge:: | ||||||
| 		Advice shown when linkgit:git-merge[1] refuses to | 		Advice shown when linkgit:git-merge[1] refuses to | ||||||
| 		merge to avoid overwriting local changes. | 		merge to avoid overwriting local changes. | ||||||
|  |  | ||||||
|  | @ -30,6 +30,9 @@ test_expect_success 'Report new path with conflict' ' | ||||||
|  |  | ||||||
| cat >expect <<EOF | cat >expect <<EOF | ||||||
| # On branch side | # On branch side | ||||||
|  | # You have unmerged paths. | ||||||
|  | #   (fix conflicts and run "git commit") | ||||||
|  | # | ||||||
| # Unmerged paths: | # Unmerged paths: | ||||||
| #   (use "git add/rm <file>..." as appropriate to mark resolution) | #   (use "git add/rm <file>..." as appropriate to mark resolution) | ||||||
| # | # | ||||||
|  | @ -118,4 +121,65 @@ test_expect_success 'git diff-index --cached -C shows 2 copies + 1 unmerged' ' | ||||||
| 	test_cmp expected actual | 	test_cmp expected actual | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | test_expect_success 'status when conflicts with add and rm advice (deleted by them)' ' | ||||||
|  | 	git reset --hard && | ||||||
|  | 	git checkout master && | ||||||
|  | 	test_commit init main.txt init && | ||||||
|  | 	git checkout -b second_branch && | ||||||
|  | 	git rm main.txt && | ||||||
|  | 	git commit -m "main.txt deleted on second_branch" && | ||||||
|  | 	test_commit second conflict.txt second && | ||||||
|  | 	git checkout master && | ||||||
|  | 	test_commit on_second main.txt on_second && | ||||||
|  | 	test_commit master conflict.txt master && | ||||||
|  | 	test_must_fail git merge second_branch && | ||||||
|  | 	cat >expected <<-\EOF && | ||||||
|  | 	# On branch master | ||||||
|  | 	# You have unmerged paths. | ||||||
|  | 	#   (fix conflicts and run "git commit") | ||||||
|  | 	# | ||||||
|  | 	# Unmerged paths: | ||||||
|  | 	#   (use "git add/rm <file>..." as appropriate to mark resolution) | ||||||
|  | 	# | ||||||
|  | 	#	both added:         conflict.txt | ||||||
|  | 	#	deleted by them:    main.txt | ||||||
|  | 	# | ||||||
|  | 	no changes added to commit (use "git add" and/or "git commit -a") | ||||||
|  | 	EOF | ||||||
|  | 	git status --untracked-files=no >actual && | ||||||
|  | 	test_i18ncmp expected actual | ||||||
|  | ' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | test_expect_success 'status when conflicts with add and rm advice (both deleted)' ' | ||||||
|  | 	git reset --hard && | ||||||
|  | 	git checkout -b conflict && | ||||||
|  | 	test_commit one main.txt one && | ||||||
|  | 	git branch conflict_second && | ||||||
|  | 	git mv main.txt sub_master.txt && | ||||||
|  | 	git commit -m "main.txt renamed in sub_master.txt" && | ||||||
|  | 	git checkout conflict_second && | ||||||
|  | 	git mv main.txt sub_second.txt && | ||||||
|  | 	git commit -m "main.txt renamed in sub_second.txt" && | ||||||
|  | 	test_must_fail git merge conflict && | ||||||
|  | 	cat >expected <<-\EOF && | ||||||
|  | 	# On branch conflict_second | ||||||
|  | 	# You have unmerged paths. | ||||||
|  | 	#   (fix conflicts and run "git commit") | ||||||
|  | 	# | ||||||
|  | 	# Unmerged paths: | ||||||
|  | 	#   (use "git add/rm <file>..." as appropriate to mark resolution) | ||||||
|  | 	# | ||||||
|  | 	#	both deleted:       main.txt | ||||||
|  | 	#	added by them:      sub_master.txt | ||||||
|  | 	#	added by us:        sub_second.txt | ||||||
|  | 	# | ||||||
|  | 	no changes added to commit (use "git add" and/or "git commit -a") | ||||||
|  | 	EOF | ||||||
|  | 	git status --untracked-files=no >actual && | ||||||
|  | 	test_i18ncmp expected actual | ||||||
|  | ' | ||||||
|  |  | ||||||
|  |  | ||||||
| test_done | test_done | ||||||
|  |  | ||||||
							
								
								
									
										156
									
								
								wt-status.c
								
								
								
								
							
							
						
						
									
										156
									
								
								wt-status.c
								
								
								
								
							|  | @ -23,6 +23,7 @@ static char default_wt_status_colors[][COLOR_MAXLEN] = { | ||||||
| 	GIT_COLOR_GREEN,  /* WT_STATUS_LOCAL_BRANCH */ | 	GIT_COLOR_GREEN,  /* WT_STATUS_LOCAL_BRANCH */ | ||||||
| 	GIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */ | 	GIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */ | ||||||
| 	GIT_COLOR_NIL,    /* WT_STATUS_ONBRANCH */ | 	GIT_COLOR_NIL,    /* WT_STATUS_ONBRANCH */ | ||||||
|  | 	GIT_COLOR_NORMAL, /* WT_STATUS_IN_PROGRESS */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static const char *color(int slot, struct wt_status *s) | static const char *color(int slot, struct wt_status *s) | ||||||
|  | @ -728,6 +729,160 @@ static void wt_status_print_tracking(struct wt_status *s) | ||||||
| 	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); | 	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int has_unmerged(struct wt_status *s) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < s->change.nr; i++) { | ||||||
|  | 		struct wt_status_change_data *d; | ||||||
|  | 		d = s->change.items[i].util; | ||||||
|  | 		if (d->stagemask) | ||||||
|  | 			return 1; | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void show_merge_in_progress(struct wt_status *s, | ||||||
|  | 				struct wt_status_state *state, | ||||||
|  | 				const char *color) | ||||||
|  | { | ||||||
|  | 	if (has_unmerged(s)) { | ||||||
|  | 		status_printf_ln(s, color, _("You have unmerged paths.")); | ||||||
|  | 		if (advice_status_hints) | ||||||
|  | 			status_printf_ln(s, color, | ||||||
|  | 				_("  (fix conflicts and run \"git commit\")")); | ||||||
|  | 	} else { | ||||||
|  | 		status_printf_ln(s, color, | ||||||
|  | 			_("All conflicts fixed but you are still merging.")); | ||||||
|  | 		if (advice_status_hints) | ||||||
|  | 			status_printf_ln(s, color, | ||||||
|  | 				_("  (use \"git commit\" to conclude merge)")); | ||||||
|  | 	} | ||||||
|  | 	wt_status_print_trailer(s); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void show_am_in_progress(struct wt_status *s, | ||||||
|  | 				struct wt_status_state *state, | ||||||
|  | 				const char *color) | ||||||
|  | { | ||||||
|  | 	status_printf_ln(s, color, | ||||||
|  | 		_("You are in the middle of an am session.")); | ||||||
|  | 	if (state->am_empty_patch) | ||||||
|  | 		status_printf_ln(s, color, | ||||||
|  | 			_("The current patch is empty.")); | ||||||
|  | 	if (advice_status_hints) { | ||||||
|  | 		if (!state->am_empty_patch) | ||||||
|  | 			status_printf_ln(s, color, | ||||||
|  | 				_("  (fix conflicts and then run \"git am --resolved\")")); | ||||||
|  | 		status_printf_ln(s, color, | ||||||
|  | 			_("  (use \"git am --skip\" to skip this patch)")); | ||||||
|  | 		status_printf_ln(s, color, | ||||||
|  | 			_("  (use \"git am --abort\" to restore the original branch)")); | ||||||
|  | 	} | ||||||
|  | 	wt_status_print_trailer(s); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void show_rebase_in_progress(struct wt_status *s, | ||||||
|  | 				struct wt_status_state *state, | ||||||
|  | 				const char *color) | ||||||
|  | { | ||||||
|  | 	struct stat st; | ||||||
|  |  | ||||||
|  | 	if (has_unmerged(s)) { | ||||||
|  | 		status_printf_ln(s, color, _("You are currently rebasing.")); | ||||||
|  | 		if (advice_status_hints) { | ||||||
|  | 			status_printf_ln(s, color, | ||||||
|  | 				_("  (fix conflicts and then run \"git rebase --continue\")")); | ||||||
|  | 			status_printf_ln(s, color, | ||||||
|  | 				_("  (use \"git rebase --skip\" to skip this patch)")); | ||||||
|  | 			status_printf_ln(s, color, | ||||||
|  | 				_("  (use \"git rebase --abort\" to check out the original branch)")); | ||||||
|  | 		} | ||||||
|  | 	} else if (state->rebase_in_progress || !stat(git_path("MERGE_MSG"), &st)) { | ||||||
|  | 		status_printf_ln(s, color, _("You are currently rebasing.")); | ||||||
|  | 		if (advice_status_hints) | ||||||
|  | 			status_printf_ln(s, color, | ||||||
|  | 				_("  (all conflicts fixed: run \"git rebase --continue\")")); | ||||||
|  | 	} else { | ||||||
|  | 		status_printf_ln(s, color, _("You are currently editing a commit during a rebase.")); | ||||||
|  | 		if (advice_status_hints && !s->amend) { | ||||||
|  | 			status_printf_ln(s, color, | ||||||
|  | 				_("  (use \"git commit --amend\" to amend the current commit)")); | ||||||
|  | 			status_printf_ln(s, color, | ||||||
|  | 				_("  (use \"git rebase --continue\" once you are satisfied with your changes)")); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	wt_status_print_trailer(s); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void show_cherry_pick_in_progress(struct wt_status *s, | ||||||
|  | 					struct wt_status_state *state, | ||||||
|  | 					const char *color) | ||||||
|  | { | ||||||
|  | 	status_printf_ln(s, color, _("You are currently cherry-picking.")); | ||||||
|  | 	if (advice_status_hints) { | ||||||
|  | 		if (has_unmerged(s)) | ||||||
|  | 			status_printf_ln(s, color, | ||||||
|  | 				_("  (fix conflicts and run \"git commit\")")); | ||||||
|  | 		else | ||||||
|  | 			status_printf_ln(s, color, | ||||||
|  | 				_("  (all conflicts fixed: run \"git commit\")")); | ||||||
|  | 	} | ||||||
|  | 	wt_status_print_trailer(s); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void show_bisect_in_progress(struct wt_status *s, | ||||||
|  | 				struct wt_status_state *state, | ||||||
|  | 				const char *color) | ||||||
|  | { | ||||||
|  | 	status_printf_ln(s, color, _("You are currently bisecting.")); | ||||||
|  | 	if (advice_status_hints) | ||||||
|  | 		status_printf_ln(s, color, | ||||||
|  | 			_("  (use \"git bisect reset\" to get back to the original branch)")); | ||||||
|  | 	wt_status_print_trailer(s); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void wt_status_print_state(struct wt_status *s) | ||||||
|  | { | ||||||
|  | 	const char *state_color = color(WT_STATUS_IN_PROGRESS, s); | ||||||
|  | 	struct wt_status_state state; | ||||||
|  | 	struct stat st; | ||||||
|  |  | ||||||
|  | 	memset(&state, 0, sizeof(state)); | ||||||
|  |  | ||||||
|  | 	if (!stat(git_path("MERGE_HEAD"), &st)) { | ||||||
|  | 		state.merge_in_progress = 1; | ||||||
|  | 	} else if (!stat(git_path("rebase-apply"), &st)) { | ||||||
|  | 		if (!stat(git_path("rebase-apply/applying"), &st)) { | ||||||
|  | 			state.am_in_progress = 1; | ||||||
|  | 			if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size) | ||||||
|  | 				state.am_empty_patch = 1; | ||||||
|  | 		} else { | ||||||
|  | 			state.rebase_in_progress = 1; | ||||||
|  | 		} | ||||||
|  | 	} else if (!stat(git_path("rebase-merge"), &st)) { | ||||||
|  | 		if (!stat(git_path("rebase-merge/interactive"), &st)) | ||||||
|  | 			state.rebase_interactive_in_progress = 1; | ||||||
|  | 		else | ||||||
|  | 			state.rebase_in_progress = 1; | ||||||
|  | 	} else if (!stat(git_path("CHERRY_PICK_HEAD"), &st)) { | ||||||
|  | 		state.cherry_pick_in_progress = 1; | ||||||
|  | 	} | ||||||
|  | 	if (!stat(git_path("BISECT_LOG"), &st)) | ||||||
|  | 		state.bisect_in_progress = 1; | ||||||
|  |  | ||||||
|  | 	if (state.merge_in_progress) | ||||||
|  | 		show_merge_in_progress(s, &state, state_color); | ||||||
|  | 	else if (state.am_in_progress) | ||||||
|  | 		show_am_in_progress(s, &state, state_color); | ||||||
|  | 	else if (state.rebase_in_progress || state.rebase_interactive_in_progress) | ||||||
|  | 		show_rebase_in_progress(s, &state, state_color); | ||||||
|  | 	else if (state.cherry_pick_in_progress) | ||||||
|  | 		show_cherry_pick_in_progress(s, &state, state_color); | ||||||
|  | 	if (state.bisect_in_progress) | ||||||
|  | 		show_bisect_in_progress(s, &state, state_color); | ||||||
|  | } | ||||||
|  |  | ||||||
| void wt_status_print(struct wt_status *s) | void wt_status_print(struct wt_status *s) | ||||||
| { | { | ||||||
| 	const char *branch_color = color(WT_STATUS_ONBRANCH, s); | 	const char *branch_color = color(WT_STATUS_ONBRANCH, s); | ||||||
|  | @ -750,6 +905,7 @@ void wt_status_print(struct wt_status *s) | ||||||
| 			wt_status_print_tracking(s); | 			wt_status_print_tracking(s); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	wt_status_print_state(s); | ||||||
| 	if (s->is_initial) { | 	if (s->is_initial) { | ||||||
| 		status_printf_ln(s, color(WT_STATUS_HEADER, s), ""); | 		status_printf_ln(s, color(WT_STATUS_HEADER, s), ""); | ||||||
| 		status_printf_ln(s, color(WT_STATUS_HEADER, s), _("Initial commit")); | 		status_printf_ln(s, color(WT_STATUS_HEADER, s), _("Initial commit")); | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								wt-status.h
								
								
								
								
							
							
						
						
									
										11
									
								
								wt-status.h
								
								
								
								
							|  | @ -15,6 +15,7 @@ enum color_wt_status { | ||||||
| 	WT_STATUS_LOCAL_BRANCH, | 	WT_STATUS_LOCAL_BRANCH, | ||||||
| 	WT_STATUS_REMOTE_BRANCH, | 	WT_STATUS_REMOTE_BRANCH, | ||||||
| 	WT_STATUS_ONBRANCH, | 	WT_STATUS_ONBRANCH, | ||||||
|  | 	WT_STATUS_IN_PROGRESS, | ||||||
| 	WT_STATUS_MAXSLOT | 	WT_STATUS_MAXSLOT | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -71,6 +72,16 @@ struct wt_status { | ||||||
| 	struct string_list ignored; | 	struct string_list ignored; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | struct wt_status_state { | ||||||
|  | 	int merge_in_progress; | ||||||
|  | 	int am_in_progress; | ||||||
|  | 	int am_empty_patch; | ||||||
|  | 	int rebase_in_progress; | ||||||
|  | 	int rebase_interactive_in_progress; | ||||||
|  | 	int cherry_pick_in_progress; | ||||||
|  | 	int bisect_in_progress; | ||||||
|  | }; | ||||||
|  |  | ||||||
| void wt_status_prepare(struct wt_status *s); | 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); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Lucien Kong
						Lucien Kong