advice: sort the advice related lists
[alt-git.git] / advice.c
blob03322caba011cd1f1956d38008f6c37ff902cfdc
1 #include "git-compat-util.h"
2 #include "advice.h"
3 #include "config.h"
4 #include "color.h"
5 #include "gettext.h"
6 #include "help.h"
7 #include "string-list.h"
9 static int advice_use_color = -1;
10 static char advice_colors[][COLOR_MAXLEN] = {
11 GIT_COLOR_RESET,
12 GIT_COLOR_YELLOW, /* HINT */
15 enum color_advice {
16 ADVICE_COLOR_RESET = 0,
17 ADVICE_COLOR_HINT = 1,
20 static int parse_advise_color_slot(const char *slot)
22 if (!strcasecmp(slot, "reset"))
23 return ADVICE_COLOR_RESET;
24 if (!strcasecmp(slot, "hint"))
25 return ADVICE_COLOR_HINT;
26 return -1;
29 static const char *advise_get_color(enum color_advice ix)
31 if (want_color_stderr(advice_use_color))
32 return advice_colors[ix];
33 return "";
36 static struct {
37 const char *key;
38 int enabled;
39 } advice_setting[] = {
40 [ADVICE_ADD_EMBEDDED_REPO] = { "addEmbeddedRepo", 1 },
41 [ADVICE_ADD_EMPTY_PATHSPEC] = { "addEmptyPathspec", 1 },
42 [ADVICE_ADD_IGNORED_FILE] = { "addIgnoredFile", 1 },
43 [ADVICE_AMBIGUOUS_FETCH_REFSPEC] = { "ambiguousFetchRefspec", 1 },
44 [ADVICE_AM_WORK_DIR] = { "amWorkDir", 1 },
45 [ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME] = { "checkoutAmbiguousRemoteBranchName", 1 },
46 [ADVICE_COMMIT_BEFORE_MERGE] = { "commitBeforeMerge", 1 },
47 [ADVICE_DETACHED_HEAD] = { "detachedHead", 1 },
48 [ADVICE_DIVERGING] = { "diverging", 1 },
49 [ADVICE_FETCH_SHOW_FORCED_UPDATES] = { "fetchShowForcedUpdates", 1 },
50 [ADVICE_GRAFT_FILE_DEPRECATED] = { "graftFileDeprecated", 1 },
51 [ADVICE_IGNORED_HOOK] = { "ignoredHook", 1 },
52 [ADVICE_IMPLICIT_IDENTITY] = { "implicitIdentity", 1 },
53 [ADVICE_NESTED_TAG] = { "nestedTag", 1 },
54 [ADVICE_OBJECT_NAME_WARNING] = { "objectNameWarning", 1 },
55 [ADVICE_PUSH_ALREADY_EXISTS] = { "pushAlreadyExists", 1 },
56 [ADVICE_PUSH_FETCH_FIRST] = { "pushFetchFirst", 1 },
57 [ADVICE_PUSH_NEEDS_FORCE] = { "pushNeedsForce", 1 },
58 [ADVICE_PUSH_NON_FF_CURRENT] = { "pushNonFFCurrent", 1 },
59 [ADVICE_PUSH_NON_FF_MATCHING] = { "pushNonFFMatching", 1 },
60 [ADVICE_PUSH_REF_NEEDS_UPDATE] = { "pushRefNeedsUpdate", 1 },
61 [ADVICE_PUSH_UNQUALIFIED_REF_NAME] = { "pushUnqualifiedRefName", 1 },
62 [ADVICE_PUSH_UPDATE_REJECTED] = { "pushUpdateRejected", 1 },
63 [ADVICE_PUSH_UPDATE_REJECTED_ALIAS] = { "pushNonFastForward", 1 }, /* backwards compatibility */
64 [ADVICE_RESET_NO_REFRESH_WARNING] = { "resetNoRefresh", 1 },
65 [ADVICE_RESOLVE_CONFLICT] = { "resolveConflict", 1 },
66 [ADVICE_RM_HINTS] = { "rmHints", 1 },
67 [ADVICE_SEQUENCER_IN_USE] = { "sequencerInUse", 1 },
68 [ADVICE_SET_UPSTREAM_FAILURE] = { "setUpstreamFailure", 1 },
69 [ADVICE_SKIPPED_CHERRY_PICKS] = { "skippedCherryPicks", 1 },
70 [ADVICE_STATUS_AHEAD_BEHIND_WARNING] = { "statusAheadBehindWarning", 1 },
71 [ADVICE_STATUS_HINTS] = { "statusHints", 1 },
72 [ADVICE_STATUS_U_OPTION] = { "statusUoption", 1 },
73 [ADVICE_SUBMODULES_NOT_UPDATED] = { "submodulesNotUpdated", 1 },
74 [ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE] = { "submoduleAlternateErrorStrategyDie", 1 },
75 [ADVICE_SUGGEST_DETACHING_HEAD] = { "suggestDetachingHead", 1 },
76 [ADVICE_UPDATE_SPARSE_PATH] = { "updateSparsePath", 1 },
77 [ADVICE_WAITING_FOR_EDITOR] = { "waitingForEditor", 1 },
78 [ADVICE_WORKTREE_ADD_ORPHAN] = { "worktreeAddOrphan", 1 },
81 static const char turn_off_instructions[] =
82 N_("\n"
83 "Disable this message with \"git config advice.%s false\"");
85 static void vadvise(const char *advice, int display_instructions,
86 const char *key, va_list params)
88 struct strbuf buf = STRBUF_INIT;
89 const char *cp, *np;
91 strbuf_vaddf(&buf, advice, params);
93 if (display_instructions)
94 strbuf_addf(&buf, turn_off_instructions, key);
96 for (cp = buf.buf; *cp; cp = np) {
97 np = strchrnul(cp, '\n');
98 fprintf(stderr, _("%shint: %.*s%s\n"),
99 advise_get_color(ADVICE_COLOR_HINT),
100 (int)(np - cp), cp,
101 advise_get_color(ADVICE_COLOR_RESET));
102 if (*np)
103 np++;
105 strbuf_release(&buf);
108 void advise(const char *advice, ...)
110 va_list params;
111 va_start(params, advice);
112 vadvise(advice, 0, "", params);
113 va_end(params);
116 int advice_enabled(enum advice_type type)
118 switch(type) {
119 case ADVICE_PUSH_UPDATE_REJECTED:
120 return advice_setting[ADVICE_PUSH_UPDATE_REJECTED].enabled &&
121 advice_setting[ADVICE_PUSH_UPDATE_REJECTED_ALIAS].enabled;
122 default:
123 return advice_setting[type].enabled;
127 void advise_if_enabled(enum advice_type type, const char *advice, ...)
129 va_list params;
131 if (!advice_enabled(type))
132 return;
134 va_start(params, advice);
135 vadvise(advice, 1, advice_setting[type].key, params);
136 va_end(params);
139 int git_default_advice_config(const char *var, const char *value)
141 const char *k, *slot_name;
142 int i;
144 if (!strcmp(var, "color.advice")) {
145 advice_use_color = git_config_colorbool(var, value);
146 return 0;
149 if (skip_prefix(var, "color.advice.", &slot_name)) {
150 int slot = parse_advise_color_slot(slot_name);
151 if (slot < 0)
152 return 0;
153 if (!value)
154 return config_error_nonbool(var);
155 return color_parse(value, advice_colors[slot]);
158 if (!skip_prefix(var, "advice.", &k))
159 return 0;
161 for (i = 0; i < ARRAY_SIZE(advice_setting); i++) {
162 if (strcasecmp(k, advice_setting[i].key))
163 continue;
164 advice_setting[i].enabled = git_config_bool(var, value);
165 return 0;
168 return 0;
171 void list_config_advices(struct string_list *list, const char *prefix)
173 int i;
175 for (i = 0; i < ARRAY_SIZE(advice_setting); i++)
176 list_config_item(list, prefix, advice_setting[i].key);
179 int error_resolve_conflict(const char *me)
181 if (!strcmp(me, "cherry-pick"))
182 error(_("Cherry-picking is not possible because you have unmerged files."));
183 else if (!strcmp(me, "commit"))
184 error(_("Committing is not possible because you have unmerged files."));
185 else if (!strcmp(me, "merge"))
186 error(_("Merging is not possible because you have unmerged files."));
187 else if (!strcmp(me, "pull"))
188 error(_("Pulling is not possible because you have unmerged files."));
189 else if (!strcmp(me, "revert"))
190 error(_("Reverting is not possible because you have unmerged files."));
191 else if (!strcmp(me, "rebase"))
192 error(_("Rebasing is not possible because you have unmerged files."));
193 else
194 BUG("Unhandled conflict reason '%s'", me);
196 if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
198 * Message used both when 'git commit' fails and when
199 * other commands doing a merge do.
201 advise(_("Fix them up in the work tree, and then use 'git add/rm <file>'\n"
202 "as appropriate to mark resolution and make a commit."));
203 return -1;
206 void NORETURN die_resolve_conflict(const char *me)
208 error_resolve_conflict(me);
209 die(_("Exiting because of an unresolved conflict."));
212 void NORETURN die_conclude_merge(void)
214 error(_("You have not concluded your merge (MERGE_HEAD exists)."));
215 if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
216 advise(_("Please, commit your changes before merging."));
217 die(_("Exiting because of unfinished merge."));
220 void NORETURN die_ff_impossible(void)
222 advise_if_enabled(ADVICE_DIVERGING,
223 _("Diverging branches can't be fast-forwarded, you need to either:\n"
224 "\n"
225 "\tgit merge --no-ff\n"
226 "\n"
227 "or:\n"
228 "\n"
229 "\tgit rebase\n"));
230 die(_("Not possible to fast-forward, aborting."));
233 void advise_on_updating_sparse_paths(struct string_list *pathspec_list)
235 struct string_list_item *item;
237 if (!pathspec_list->nr)
238 return;
240 fprintf(stderr, _("The following paths and/or pathspecs matched paths that exist\n"
241 "outside of your sparse-checkout definition, so will not be\n"
242 "updated in the index:\n"));
243 for_each_string_list_item(item, pathspec_list)
244 fprintf(stderr, "%s\n", item->string);
246 advise_if_enabled(ADVICE_UPDATE_SPARSE_PATH,
247 _("If you intend to update such entries, try one of the following:\n"
248 "* Use the --sparse option.\n"
249 "* Disable or modify the sparsity rules."));
252 void detach_advice(const char *new_name)
254 const char *fmt =
255 _("Note: switching to '%s'.\n"
256 "\n"
257 "You are in 'detached HEAD' state. You can look around, make experimental\n"
258 "changes and commit them, and you can discard any commits you make in this\n"
259 "state without impacting any branches by switching back to a branch.\n"
260 "\n"
261 "If you want to create a new branch to retain commits you create, you may\n"
262 "do so (now or later) by using -c with the switch command. Example:\n"
263 "\n"
264 " git switch -c <new-branch-name>\n"
265 "\n"
266 "Or undo this operation with:\n"
267 "\n"
268 " git switch -\n"
269 "\n"
270 "Turn off this advice by setting config variable advice.detachedHead to false\n\n");
272 fprintf(stderr, fmt, new_name);
275 void advise_on_moving_dirty_path(struct string_list *pathspec_list)
277 struct string_list_item *item;
279 if (!pathspec_list->nr)
280 return;
282 fprintf(stderr, _("The following paths have been moved outside the\n"
283 "sparse-checkout definition but are not sparse due to local\n"
284 "modifications.\n"));
285 for_each_string_list_item(item, pathspec_list)
286 fprintf(stderr, "%s\n", item->string);
288 advise_if_enabled(ADVICE_UPDATE_SPARSE_PATH,
289 _("To correct the sparsity of these paths, do the following:\n"
290 "* Use \"git add --sparse <paths>\" to update the index\n"
291 "* Use \"git sparse-checkout reapply\" to apply the sparsity rules"));