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>

Lucien Kong committed Jun 5, 2012 at 22:21 UTC 83c750acde6ac5847845f9c2eeb3261cf9cc54bc
4 files changed +235 -3
Documentation/config.txt
+4 -3
@@ -159,9 +159,10 @@ advice.*::
159 specified a refspec that isn't your current branch) and
160 it resulted in a non-fast-forward error.
161 statusHints::
162 - Directions on how to stage/unstage/add shown in the
163 - output of linkgit:git-status[1] and the template shown
164 - when writing commit messages.
162 + Show directions on how to proceed from the current
163 + state in the output of linkgit:git-status[1] and in
164 + the template shown when writing commit messages in
165 + linkgit:git-commit[1].
166 commitBeforeMerge::
167 Advice shown when linkgit:git-merge[1] refuses to
168 merge to avoid overwriting local changes.
t/t7060-wtstatus.sh
+64
@@ -30,6 +30,9 @@ test_expect_success 'Report new path with conflict' '
30
31 cat >expect <<EOF
32 # On branch side
33 +# You have unmerged paths.
34 +# (fix conflicts and run "git commit")
35 +#
36 # Unmerged paths:
37 # (use "git add/rm <file>..." as appropriate to mark resolution)
38 #
@@ -118,4 +121,65 @@ test_expect_success 'git diff-index --cached -C shows 2 copies + 1 unmerged' '
121 test_cmp expected actual
122 '
123
124 +
125 +test_expect_success 'status when conflicts with add and rm advice (deleted by them)' '
126 + git reset --hard &&
127 + git checkout master &&
128 + test_commit init main.txt init &&
129 + git checkout -b second_branch &&
130 + git rm main.txt &&
131 + git commit -m "main.txt deleted on second_branch" &&
132 + test_commit second conflict.txt second &&
133 + git checkout master &&
134 + test_commit on_second main.txt on_second &&
135 + test_commit master conflict.txt master &&
136 + test_must_fail git merge second_branch &&
137 + cat >expected <<-\EOF &&
138 + # On branch master
139 + # You have unmerged paths.
140 + # (fix conflicts and run "git commit")
141 + #
142 + # Unmerged paths:
143 + # (use "git add/rm <file>..." as appropriate to mark resolution)
144 + #
145 + # both added: conflict.txt
146 + # deleted by them: main.txt
147 + #
148 + no changes added to commit (use "git add" and/or "git commit -a")
149 + EOF
150 + git status --untracked-files=no >actual &&
151 + test_i18ncmp expected actual
152 +'
153 +
154 +
155 +test_expect_success 'status when conflicts with add and rm advice (both deleted)' '
156 + git reset --hard &&
157 + git checkout -b conflict &&
158 + test_commit one main.txt one &&
159 + git branch conflict_second &&
160 + git mv main.txt sub_master.txt &&
161 + git commit -m "main.txt renamed in sub_master.txt" &&
162 + git checkout conflict_second &&
163 + git mv main.txt sub_second.txt &&
164 + git commit -m "main.txt renamed in sub_second.txt" &&
165 + test_must_fail git merge conflict &&
166 + cat >expected <<-\EOF &&
167 + # On branch conflict_second
168 + # You have unmerged paths.
169 + # (fix conflicts and run "git commit")
170 + #
171 + # Unmerged paths:
172 + # (use "git add/rm <file>..." as appropriate to mark resolution)
173 + #
174 + # both deleted: main.txt
175 + # added by them: sub_master.txt
176 + # added by us: sub_second.txt
177 + #
178 + no changes added to commit (use "git add" and/or "git commit -a")
179 + EOF
180 + git status --untracked-files=no >actual &&
181 + test_i18ncmp expected actual
182 +'
183 +
184 +
185 test_done
wt-status.c
+156
@@ -23,6 +23,7 @@ static char default_wt_status_colors[][COLOR_MAXLEN] = {
23 GIT_COLOR_GREEN, /* WT_STATUS_LOCAL_BRANCH */
24 GIT_COLOR_RED, /* WT_STATUS_REMOTE_BRANCH */
25 GIT_COLOR_NIL, /* WT_STATUS_ONBRANCH */
26 + GIT_COLOR_NORMAL, /* WT_STATUS_IN_PROGRESS */
27 };
28
29 static const char *color(int slot, struct wt_status *s)
@@ -728,6 +729,160 @@ static void wt_status_print_tracking(struct wt_status *s)
729 color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
730 }
731
732 +static int has_unmerged(struct wt_status *s)
733 +{
734 + int i;
735 +
736 + for (i = 0; i < s->change.nr; i++) {
737 + struct wt_status_change_data *d;
738 + d = s->change.items[i].util;
739 + if (d->stagemask)
740 + return 1;
741 + }
742 + return 0;
743 +}
744 +
745 +static void show_merge_in_progress(struct wt_status *s,
746 + struct wt_status_state *state,
747 + const char *color)
748 +{
749 + if (has_unmerged(s)) {
750 + status_printf_ln(s, color, _("You have unmerged paths."));
751 + if (advice_status_hints)
752 + status_printf_ln(s, color,
753 + _(" (fix conflicts and run \"git commit\")"));
754 + } else {
755 + status_printf_ln(s, color,
756 + _("All conflicts fixed but you are still merging."));
757 + if (advice_status_hints)
758 + status_printf_ln(s, color,
759 + _(" (use \"git commit\" to conclude merge)"));
760 + }
761 + wt_status_print_trailer(s);
762 +}
763 +
764 +static void show_am_in_progress(struct wt_status *s,
765 + struct wt_status_state *state,
766 + const char *color)
767 +{
768 + status_printf_ln(s, color,
769 + _("You are in the middle of an am session."));
770 + if (state->am_empty_patch)
771 + status_printf_ln(s, color,
772 + _("The current patch is empty."));
773 + if (advice_status_hints) {
774 + if (!state->am_empty_patch)
775 + status_printf_ln(s, color,
776 + _(" (fix conflicts and then run \"git am --resolved\")"));
777 + status_printf_ln(s, color,
778 + _(" (use \"git am --skip\" to skip this patch)"));
779 + status_printf_ln(s, color,
780 + _(" (use \"git am --abort\" to restore the original branch)"));
781 + }
782 + wt_status_print_trailer(s);
783 +}
784 +
785 +static void show_rebase_in_progress(struct wt_status *s,
786 + struct wt_status_state *state,
787 + const char *color)
788 +{
789 + struct stat st;
790 +
791 + if (has_unmerged(s)) {
792 + status_printf_ln(s, color, _("You are currently rebasing."));
793 + if (advice_status_hints) {
794 + status_printf_ln(s, color,
795 + _(" (fix conflicts and then run \"git rebase --continue\")"));
796 + status_printf_ln(s, color,
797 + _(" (use \"git rebase --skip\" to skip this patch)"));
798 + status_printf_ln(s, color,
799 + _(" (use \"git rebase --abort\" to check out the original branch)"));
800 + }
801 + } else if (state->rebase_in_progress || !stat(git_path("MERGE_MSG"), &st)) {
802 + status_printf_ln(s, color, _("You are currently rebasing."));
803 + if (advice_status_hints)
804 + status_printf_ln(s, color,
805 + _(" (all conflicts fixed: run \"git rebase --continue\")"));
806 + } else {
807 + status_printf_ln(s, color, _("You are currently editing a commit during a rebase."));
808 + if (advice_status_hints && !s->amend) {
809 + status_printf_ln(s, color,
810 + _(" (use \"git commit --amend\" to amend the current commit)"));
811 + status_printf_ln(s, color,
812 + _(" (use \"git rebase --continue\" once you are satisfied with your changes)"));
813 + }
814 + }
815 + wt_status_print_trailer(s);
816 +}
817 +
818 +static void show_cherry_pick_in_progress(struct wt_status *s,
819 + struct wt_status_state *state,
820 + const char *color)
821 +{
822 + status_printf_ln(s, color, _("You are currently cherry-picking."));
823 + if (advice_status_hints) {
824 + if (has_unmerged(s))
825 + status_printf_ln(s, color,
826 + _(" (fix conflicts and run \"git commit\")"));
827 + else
828 + status_printf_ln(s, color,
829 + _(" (all conflicts fixed: run \"git commit\")"));
830 + }
831 + wt_status_print_trailer(s);
832 +}
833 +
834 +static void show_bisect_in_progress(struct wt_status *s,
835 + struct wt_status_state *state,
836 + const char *color)
837 +{
838 + status_printf_ln(s, color, _("You are currently bisecting."));
839 + if (advice_status_hints)
840 + status_printf_ln(s, color,
841 + _(" (use \"git bisect reset\" to get back to the original branch)"));
842 + wt_status_print_trailer(s);
843 +}
844 +
845 +static void wt_status_print_state(struct wt_status *s)
846 +{
847 + const char *state_color = color(WT_STATUS_IN_PROGRESS, s);
848 + struct wt_status_state state;
849 + struct stat st;
850 +
851 + memset(&state, 0, sizeof(state));
852 +
853 + if (!stat(git_path("MERGE_HEAD"), &st)) {
854 + state.merge_in_progress = 1;
855 + } else if (!stat(git_path("rebase-apply"), &st)) {
856 + if (!stat(git_path("rebase-apply/applying"), &st)) {
857 + state.am_in_progress = 1;
858 + if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
859 + state.am_empty_patch = 1;
860 + } else {
861 + state.rebase_in_progress = 1;
862 + }
863 + } else if (!stat(git_path("rebase-merge"), &st)) {
864 + if (!stat(git_path("rebase-merge/interactive"), &st))
865 + state.rebase_interactive_in_progress = 1;
866 + else
867 + state.rebase_in_progress = 1;
868 + } else if (!stat(git_path("CHERRY_PICK_HEAD"), &st)) {
869 + state.cherry_pick_in_progress = 1;
870 + }
871 + if (!stat(git_path("BISECT_LOG"), &st))
872 + state.bisect_in_progress = 1;
873 +
874 + if (state.merge_in_progress)
875 + show_merge_in_progress(s, &state, state_color);
876 + else if (state.am_in_progress)
877 + show_am_in_progress(s, &state, state_color);
878 + else if (state.rebase_in_progress || state.rebase_interactive_in_progress)
879 + show_rebase_in_progress(s, &state, state_color);
880 + else if (state.cherry_pick_in_progress)
881 + show_cherry_pick_in_progress(s, &state, state_color);
882 + if (state.bisect_in_progress)
883 + show_bisect_in_progress(s, &state, state_color);
884 +}
885 +
886 void wt_status_print(struct wt_status *s)
887 {
888 const char *branch_color = color(WT_STATUS_ONBRANCH, s);
@@ -750,6 +905,7 @@ void wt_status_print(struct wt_status *s)
905 wt_status_print_tracking(s);
906 }
907
908 + wt_status_print_state(s);
909 if (s->is_initial) {
910 status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
911 status_printf_ln(s, color(WT_STATUS_HEADER, s), _("Initial commit"));
wt-status.h
+11
@@ -15,6 +15,7 @@ enum color_wt_status {
15 WT_STATUS_LOCAL_BRANCH,
16 WT_STATUS_REMOTE_BRANCH,
17 WT_STATUS_ONBRANCH,
18 + WT_STATUS_IN_PROGRESS,
19 WT_STATUS_MAXSLOT
20 };
21
@@ -71,6 +72,16 @@ struct wt_status {
72 struct string_list ignored;
73 };
74
75 +struct wt_status_state {
76 + int merge_in_progress;
77 + int am_in_progress;
78 + int am_empty_patch;
79 + int rebase_in_progress;
80 + int rebase_interactive_in_progress;
81 + int cherry_pick_in_progress;
82 + int bisect_in_progress;
83 +};
84 +
85 void wt_status_prepare(struct wt_status *s);
86 void wt_status_print(struct wt_status *s);
87 void wt_status_collect(struct wt_status *s);