environment: move has_symlinks into repo_config_values
Move the global 'has_symlinks' configuration into the repository-specific 'repo_config_values' struct. Introduce 'repo_has_symlinks()' getter for readability. Callers access this configuration by passing in 'repo' when possible, and explicitly fall back to 'the_repository' the rest of the time. Introduce 'platform_has_symlinks()' macro to allow platform specific-customization, primarily to help MinGW. Platforms can override this in their respective headers. Mentored-by: Christian Couder <christian.couder@gmail.com> Mentored-by: Ayush Chandekar <ayu.chandekar@gmail.com> Mentored-by: Olamide Caleb Bello <belkid98@gmail.com> Signed-off-by: Tian Yuchen <cat@malon.dev> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Tian Yuchen committed
Jul 20, 2026 at 18:53 UTC
df2bc04e6937ba35b9a905a65dd0137627b46c8b
10 files changed
+42
-15
apply.c
+1
-1
@@ -4511,7 +4511,7 @@ static int try_create_file(struct apply_state *state, const char *path,
4511
return !!mkdir(path, 0777);
4512
}
4513
4514
- if (has_symlinks && S_ISLNK(mode))
4514
+ if (repo_has_symlinks(state->repo) && S_ISLNK(mode))
4515
/* Although buf:size is counted string, it also is NUL
4516
* terminated.
4517
*/
combine-diff.c
+1
-1
@@ -1078,7 +1078,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
1078
/* if symlinks don't work, assume symlink if all parents
1079
* are symlinks
1080
*/
1081
- is_file = has_symlinks;
1081
+ is_file = repo_has_symlinks(rev->repo);
1082
for (i = 0; !is_file && i < num_parent; i++)
1083
is_file = !S_ISLNK(elem->parent[i].mode);
1084
if (!is_file)
compat/mingw.c
+13
-4
@@ -7,6 +7,7 @@
7
#include "config.h"
8
#include "dir.h"
9
#include "environment.h"
10
+#include "repository.h"
11
#include "gettext.h"
12
#include "run-command.h"
13
#include "strbuf.h"
@@ -1043,7 +1044,7 @@ int mingw_chdir(const char *dirname)
1044
if (xutftowcs_path(wdirname, dirname) < 0)
1045
return -1;
1046
1046
- if (has_symlinks) {
1047
+ if (repo_has_symlinks(the_repository)) {
1048
HANDLE hnd = CreateFileW(wdirname, 0,
1049
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
1050
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
@@ -2903,7 +2904,7 @@ int symlink(const char *target, const char *link)
2904
int len;
2905
2906
/* fail if symlinks are disabled or API is not supported (WinXP) */
2906
- if (!has_symlinks) {
2907
+ if (!repo_has_symlinks(the_repository)) {
2908
errno = ENOSYS;
2909
return -1;
2910
}
@@ -3173,15 +3174,23 @@ static void setup_windows_environment(void)
3174
if (!tmp && (tmp = getenv("USERPROFILE")))
3175
setenv("HOME", tmp, 1);
3176
}
3177
+}
3178
3179
+int mingw_platform_has_symlinks(void)
3180
+{
3181
+ static int has_symlinks = -1;
3182
/*
3183
* Change 'core.symlinks' default to false, unless native symlinks are
3184
* enabled in MSys2 (via 'MSYS=winsymlinks:nativestrict'). Thus we can
3185
* run the test suite (which doesn't obey config files) with or without
3186
* symlink support.
3187
*/
3183
- if (!(tmp = getenv("MSYS")) || !strstr(tmp, "winsymlinks:nativestrict"))
3184
- has_symlinks = 0;
3188
+ if (has_symlinks < 0) {
3189
+ const char *tmp = getenv("MSYS");
3190
+ has_symlinks = (tmp && strstr(tmp, "winsymlinks:nativestrict")) ? 1 : 0;
3191
+ }
3192
+
3193
+ return has_symlinks;
3194
}
3195
3196
static void get_current_user_sid(PSID *sid, HANDLE *linked_token)
compat/mingw.h
+3
@@ -208,6 +208,9 @@ void open_in_gdb(void);
208
*/
209
int err_win_to_posix(DWORD winerr);
210
211
+int mingw_platform_has_symlinks(void);
212
+#define platform_has_symlinks() mingw_platform_has_symlinks()
213
+
214
#ifndef NO_UNIX_SOCKETS
215
int mingw_have_unix_sockets(void);
216
#undef have_unix_sockets
entry.c
+2
-1
@@ -321,7 +321,8 @@ static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca
321
* We can't make a real symlink; write out a regular file entry
322
* with the symlink destination as its contents.
323
*/
324
- if (!has_symlinks || to_tempfile)
324
+ if (!repo_has_symlinks(state->istate && state->istate->repo ?
325
+ state->istate->repo : the_repository) || to_tempfile)
326
goto write_file_entry;
327
328
ret = symlink(new_blob, path);
environment.c
+10
-2
@@ -43,7 +43,6 @@ static int zlib_compression_seen;
43
44
int trust_ctime = 1;
45
int check_stat = 1;
46
-int has_symlinks = 1;
46
int minimum_abbrev = 4, default_abbrev = -1;
47
int ignore_case;
48
int assume_unchanged;
@@ -148,6 +147,13 @@ int repo_trust_executable_bit(struct repository *repo)
147
: 1;
148
}
149
150
+int repo_has_symlinks(struct repository *repo)
151
+{
152
+ return repo->initialized
153
+ ? repo_config_values(repo)->has_symlinks
154
+ : platform_has_symlinks();
155
+}
156
+
157
int have_git_dir(void)
158
{
159
return startup_info->have_repository
@@ -336,7 +342,8 @@ int git_default_core_config(const char *var, const char *value,
342
}
343
344
if (!strcmp(var, "core.symlinks")) {
339
- has_symlinks = git_config_bool(var, value);
345
+ struct repo_config_values *cfg = repo_config_values(the_repository);
346
+ cfg->has_symlinks = git_config_bool(var, value);
347
return 0;
348
}
349
@@ -727,5 +734,6 @@ void repo_config_values_init(struct repo_config_values *cfg)
734
cfg->attributes_file = NULL;
735
cfg->apply_sparse_checkout = 0;
736
cfg->trust_executable_bit = 1;
737
+ cfg->has_symlinks = platform_has_symlinks();
738
cfg->branch_track = BRANCH_TRACK_REMOTE;
739
}
environment.h
+3
-1
@@ -92,6 +92,7 @@ struct repo_config_values {
92
char *attributes_file;
93
int apply_sparse_checkout;
94
int trust_executable_bit;
95
+ int has_symlinks;
96
97
/* section "branch" config values */
98
enum branch_track branch_track;
@@ -126,6 +127,8 @@ int git_default_core_config(const char *var, const char *value,
127
128
int repo_trust_executable_bit(struct repository *repo);
129
130
+int repo_has_symlinks(struct repository *repo);
131
+
132
void repo_config_values_init(struct repo_config_values *cfg);
133
134
/*
@@ -163,7 +166,6 @@ extern char *git_work_tree_cfg;
166
/* Environment bits from configuration mechanism */
167
extern int trust_ctime;
168
extern int check_stat;
166
-extern int has_symlinks;
169
extern int minimum_abbrev, default_abbrev;
170
extern int ignore_case;
171
extern int assume_unchanged;
git-compat-util.h
+4
@@ -245,6 +245,10 @@ static inline int git_is_dir_sep(int c)
245
#define is_dir_sep git_is_dir_sep
246
#endif
247
248
+#ifndef platform_has_symlinks
249
+#define platform_has_symlinks() 1
250
+#endif
251
+
252
#ifndef offset_1st_component
253
static inline int git_offset_1st_component(const char *path)
254
{
read-cache.c
+4
-3
@@ -207,7 +207,7 @@ static unsigned int st_mode_from_ce(const struct cache_entry *ce)
207
{
208
switch (ce->ce_mode & S_IFMT) {
209
case S_IFLNK:
210
- return has_symlinks ? S_IFLNK : (S_IFREG | 0644);
210
+ return repo_has_symlinks(the_repository) ? S_IFLNK : (S_IFREG | 0644);
211
case S_IFREG:
212
return (ce->ce_mode & (repo_trust_executable_bit(the_repository) ? 0755 : 0644)) | S_IFREG;
213
case S_IFGITLINK:
@@ -325,7 +325,7 @@ static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
325
break;
326
case S_IFLNK:
327
if (!S_ISLNK(st->st_mode) &&
328
- (has_symlinks || !S_ISREG(st->st_mode)))
328
+ (repo_has_symlinks(the_repository) || !S_ISREG(st->st_mode)))
329
changed |= TYPE_CHANGED;
330
break;
331
case S_IFGITLINK:
@@ -740,7 +740,8 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
740
ce->ce_flags |= CE_INTENT_TO_ADD;
741
742
743
- if (repo_trust_executable_bit(istate->repo) && has_symlinks) {
743
+ if (repo_trust_executable_bit(istate->repo) &&
744
+ repo_has_symlinks(istate->repo)) {
745
ce->ce_mode = create_ce_mode(st_mode);
746
} else {
747
/* If there is an existing entry, pick the mode bits and type
read-cache.h
+1
-2
@@ -17,8 +17,7 @@ static inline unsigned int ce_mode_from_stat(struct repository *repo,
17
const struct cache_entry *ce,
18
unsigned int mode)
19
{
20
- extern int has_symlinks;
21
- if (S_ISREG(mode) && !has_symlinks &&
20
+ if (S_ISREG(mode) && !repo_has_symlinks(repo) &&
21
ce && S_ISLNK(ce->ce_mode))
22
return ce->ce_mode;
23
if (S_ISREG(mode) && !repo_trust_executable_bit(repo)) {