8
9
#include "git-compat-util.h"
10
#include "abspath.h"
11
+#include "advice.h"
12
#include "date.h"
13
#include "branch.h"
14
#include "config.h"
1956
struct comment_char_config {
1957
unsigned last_key_id;
1958
bool auto_set;
1959
+ bool auto_set_in_file;
1960
+ struct strintmap key_flags;
1961
+ size_t alloc, nr;
1962
+ struct comment_char_config_item {
1963
+ unsigned key_id;
1964
+ char *path;
1965
+ enum config_scope scope;
1966
+ } *item;
1967
};
1968
1960
-#define COMMENT_CHAR_CFG_INIT { 0 }
1969
+#define COMMENT_CHAR_CFG_INIT { \
1970
+ .key_flags = STRINTMAP_INIT, \
1971
+ }
1972
+
1973
+static void comment_char_config_release(struct comment_char_config *config)
1974
+{
1975
+ strintmap_clear(&config->key_flags);
1976
+ for (size_t i = 0; i < config->nr; i++)
1977
+ free(config->item[i].path);
1978
+ free(config->item);
1979
+}
1980
+
1981
+/* Used to track whether the key occurs more than once in a given file */
1982
+#define KEY_SEEN_ONCE 1u
1983
+#define KEY_SEEN_TWICE 2u
1984
+#define COMMENT_KEY_SHIFT(id) (2 * (id))
1985
+#define COMMENT_KEY_MASK(id) (3u << COMMENT_KEY_SHIFT(id))
1986
+
1987
+static void set_comment_key_flags(struct comment_char_config *config,
1988
+ const char *path, unsigned id, unsigned value)
1989
+{
1990
+ unsigned old = strintmap_get(&config->key_flags, path);
1991
+ unsigned new = (old & ~COMMENT_KEY_MASK(id)) |
1992
+ value << COMMENT_KEY_SHIFT(id);
1993
+
1994
+ strintmap_set(&config->key_flags, path, new);
1995
+}
1996
+
1997
+static unsigned get_comment_key_flags(struct comment_char_config *config,
1998
+ const char *path, unsigned id)
1999
+{
2000
+ unsigned value = strintmap_get(&config->key_flags, path);
2001
+
2002
+ return (value & COMMENT_KEY_MASK(id)) >> COMMENT_KEY_SHIFT(id);
2003
+}
2004
2005
static const char *comment_key_name(unsigned id)
2006
{
2016
}
2017
2018
static void comment_char_callback(const char *key, const char *value,
1976
- const struct config_context *ctx UNUSED,
1977
- void *data)
2019
+ const struct config_context *ctx, void *data)
2020
{
2021
struct comment_char_config *config = data;
2022
+ const struct key_value_info *kvi = ctx->kvi;
2023
unsigned key_id;
2024
2025
if (!strcmp(key, "core.commentchar"))
2031
2032
config->last_key_id = key_id;
2033
config->auto_set = value && !strcmp(value, "auto");
2034
+ if (kvi->origin_type != CONFIG_ORIGIN_FILE) {
2035
+ return;
2036
+ } else if (get_comment_key_flags(config, kvi->filename, key_id)) {
2037
+ set_comment_key_flags(config, kvi->filename, key_id,
2038
+ KEY_SEEN_TWICE);
2039
+ } else {
2040
+ struct comment_char_config_item *item;
2041
+
2042
+ ALLOC_GROW_BY(config->item, config->nr, 1, config->alloc);
2043
+ item = &config->item[config->nr - 1];
2044
+ item->key_id = key_id;
2045
+ item->scope = kvi->scope;
2046
+ item->path = xstrdup(kvi->filename);
2047
+ set_comment_key_flags(config, kvi->filename, key_id,
2048
+ KEY_SEEN_ONCE);
2049
+ }
2050
+ config->auto_set_in_file = config->auto_set;
2051
}
2052
2053
+static void add_config_scope_arg(struct repository *repo, struct strbuf *buf,
2054
+ struct comment_char_config_item *item)
2055
+{
2056
+ char *global_config = git_global_config();
2057
+ char *system_config = git_system_config();
2058
+
2059
+ if (item->scope == CONFIG_SCOPE_SYSTEM && access(item->path, W_OK)) {
2060
+ /*
2061
+ * If the user cannot write to the system config recommend
2062
+ * setting the global config instead.
2063
+ */
2064
+ strbuf_addstr(buf, "--global ");
2065
+ } else if (fspatheq(item->path, system_config)) {
2066
+ strbuf_addstr(buf, "--system ");
2067
+ } else if (fspatheq(item->path, global_config)) {
2068
+ strbuf_addstr(buf, "--global ");
2069
+ } else if (fspatheq(item->path,
2070
+ mkpath("%s/config",
2071
+ repo_get_git_dir(repo)))) {
2072
+ ; /* --local is the default */
2073
+ } else if (fspatheq(item->path,
2074
+ mkpath("%s/config.worktree",
2075
+ repo_get_common_dir(repo)))) {
2076
+ strbuf_addstr(buf, "--worktree ");
2077
+ } else {
2078
+ const char *path = item->path;
2079
+ const char *home = getenv("HOME");
2080
+
2081
+ strbuf_addstr(buf, "--file ");
2082
+ if (home && !fspathncmp(path, home, strlen(home))) {
2083
+ path += strlen(home);
2084
+ if (!fspathncmp(path, "/", 1))
2085
+ path++;
2086
+ strbuf_addstr(buf, "~/");
2087
+ }
2088
+ sq_quote_buf_pretty(buf, path);
2089
+ strbuf_addch(buf, ' ');
2090
+ }
2091
+
2092
+ free(global_config);
2093
+ free(system_config);
2094
+}
2095
+
2096
+static bool can_unset_comment_char_config(struct comment_char_config *config)
2097
+{
2098
+ for (size_t i = 0; i < config->nr; i++) {
2099
+ struct comment_char_config_item *item = &config->item[i];
2100
+
2101
+ if (item->scope == CONFIG_SCOPE_SYSTEM &&
2102
+ access(item->path, W_OK))
2103
+ return false;
2104
+ }
2105
+
2106
+ return true;
2107
+}
2108
+
2109
+static void add_unset_auto_comment_char_advice(struct repository *repo,
2110
+ struct comment_char_config *config)
2111
+{
2112
+ struct strbuf buf = STRBUF_INIT;
2113
+
2114
+ if (!can_unset_comment_char_config(config))
2115
+ return;
2116
+
2117
+ for (size_t i = 0; i < config->nr; i++) {
2118
+ struct comment_char_config_item *item = &config->item[i];
2119
+
2120
+ strbuf_addstr(&buf, " git config unset ");
2121
+ add_config_scope_arg(repo, &buf, item);
2122
+ if (get_comment_key_flags(config, item->path, item->key_id) == KEY_SEEN_TWICE)
2123
+ strbuf_addstr(&buf, "--all ");
2124
+ strbuf_addf(&buf, "%s\n", comment_key_name(item->key_id));
2125
+ }
2126
+ advise(_("\nTo use the default comment string (#) please run\n\n%s"),
2127
+ buf.buf);
2128
+ strbuf_release(&buf);
2129
+}
2130
+
2131
+static void add_comment_char_advice(struct repository *repo,
2132
+ struct comment_char_config *config)
2133
+{
2134
+ struct strbuf buf = STRBUF_INIT;
2135
+ struct comment_char_config_item *item;
2136
+ /* TRANSLATORS this is a place holder for the value of core.commentString */
2137
+ const char *placeholder = _("<comment string>");
2138
+
2139
+ /*
2140
+ * If auto is set in the last file that we saw advise the user how to
2141
+ * update their config.
2142
+ */
2143
+ if (!config->auto_set_in_file)
2144
+ return;
2145
+
2146
+ add_unset_auto_comment_char_advice(repo, config);
2147
+ item = &config->item[config->nr - 1];
2148
+ strbuf_reset(&buf);
2149
+ strbuf_addstr(&buf, " git config set ");
2150
+ add_config_scope_arg(repo, &buf, item);
2151
+ strbuf_addf(&buf, "%s %s\n", comment_key_name(item->key_id),
2152
+ placeholder);
2153
+ advise(_("\nTo set a custom comment string please run\n\n"
2154
+ "%s\nwhere '%s' is the string you wish to use.\n"),
2155
+ buf.buf, placeholder);
2156
+ strbuf_release(&buf);
2157
+}
2158
+
2159
+#undef KEY_SEEN_ONCE
2160
+#undef KEY_SEEN_TWICE
2161
+#undef COMMENT_KEY_SHIFT
2162
+#undef COMMENT_KEY_MASK
2163
+
2164
struct repo_config {
2165
struct repository *repo;
2166
struct comment_char_config comment_char_config;
2171
.repo = repo_, \
2172
};
2173
2174
+static void repo_config_release(struct repo_config *config)
2175
+{
2176
+ comment_char_config_release(&config->comment_char_config);
2177
+}
2178
+
2179
#ifdef WITH_BREAKING_CHANGES
2004
-static void check_auto_comment_char_config(struct comment_char_config *config)
2180
+static void check_auto_comment_char_config(struct repository *repo,
2181
+ struct comment_char_config *config)
2182
{
2183
if (!config->auto_set)
2184
return;
2185
2186
die_message(_("Support for '%s=auto' has been removed in Git 3.0"),
2187
comment_key_name(config->last_key_id));
2188
+ add_comment_char_advice(repo, config);
2189
die(NULL);
2190
}
2191
#else
2014
-static void check_auto_comment_char_config(struct comment_char_config *config)
2192
+static void check_auto_comment_char_config(struct repository *repo,
2193
+ struct comment_char_config *config)
2194
{
2195
extern bool warn_on_auto_comment_char;
2196
const char *DEPRECATED_CONFIG_ENV =
2210
2211
warning(_("Support for '%s=auto' is deprecated and will be removed in "
2212
"Git 3.0"), comment_key_name(config->last_key_id));
2213
+ add_comment_char_advice(repo, config);
2214
}
2215
#endif /* WITH_BREAKING_CHANGES */
2216
2219
if (!config->repo->check_deprecated_config)
2220
return;
2221
2042
- check_auto_comment_char_config(&config->comment_char_config);
2222
+ check_auto_comment_char_config(config->repo,
2223
+ &config->comment_char_config);
2224
}
2225
2226
static int repo_config_callback(const char *key, const char *value,
2263
*/
2264
die(_("unknown error occurred while reading the configuration files"));
2265
check_deprecated_config(&config);
2266
+ repo_config_release(&config);
2267
}
2268
2269
static void git_config_check_init(struct repository *repo)