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
index edb1502414..b748192ee2 100644
--- a/apply.c
+++ b/apply.c
@@ -4511,7 +4511,7 @@ static int try_create_file(struct apply_state *state, const char *path,
return !!mkdir(path, 0777);
}
- if (has_symlinks && S_ISLNK(mode))
+ if (repo_has_symlinks(state->repo) && S_ISLNK(mode))
/* Although buf:size is counted string, it also is NUL
* terminated.
*/
combine-diff.c
+1
-1
index b799862068..80e5c46e9b 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -1078,7 +1078,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
/* if symlinks don't work, assume symlink if all parents
* are symlinks
*/
- is_file = has_symlinks;
+ is_file = repo_has_symlinks(rev->repo);
for (i = 0; !is_file && i < num_parent; i++)
is_file = !S_ISLNK(elem->parent[i].mode);
if (!is_file)
compat/mingw.c
+13
-4
index aa7525f419..4781911929 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -7,6 +7,7 @@
#include "config.h"
#include "dir.h"
#include "environment.h"
+#include "repository.h"
#include "gettext.h"
#include "run-command.h"
#include "strbuf.h"
@@ -1043,7 +1044,7 @@ int mingw_chdir(const char *dirname)
if (xutftowcs_path(wdirname, dirname) < 0)
return -1;
- if (has_symlinks) {
+ if (repo_has_symlinks(the_repository)) {
HANDLE hnd = CreateFileW(wdirname, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
@@ -2903,7 +2904,7 @@ int symlink(const char *target, const char *link)
int len;
/* fail if symlinks are disabled or API is not supported (WinXP) */
- if (!has_symlinks) {
+ if (!repo_has_symlinks(the_repository)) {
errno = ENOSYS;
return -1;
}
@@ -3173,15 +3174,23 @@ static void setup_windows_environment(void)
if (!tmp && (tmp = getenv("USERPROFILE")))
setenv("HOME", tmp, 1);
}
+}
+int mingw_platform_has_symlinks(void)
+{
+ static int has_symlinks = -1;
/*
* Change 'core.symlinks' default to false, unless native symlinks are
* enabled in MSys2 (via 'MSYS=winsymlinks:nativestrict'). Thus we can
* run the test suite (which doesn't obey config files) with or without
* symlink support.
*/
- if (!(tmp = getenv("MSYS")) || !strstr(tmp, "winsymlinks:nativestrict"))
- has_symlinks = 0;
+ if (has_symlinks < 0) {
+ const char *tmp = getenv("MSYS");
+ has_symlinks = (tmp && strstr(tmp, "winsymlinks:nativestrict")) ? 1 : 0;
+ }
+
+ return has_symlinks;
}
static void get_current_user_sid(PSID *sid, HANDLE *linked_token)
compat/mingw.h
+3
index 444daedfa5..df02aeb632 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -208,6 +208,9 @@ void open_in_gdb(void);
*/
int err_win_to_posix(DWORD winerr);
+int mingw_platform_has_symlinks(void);
+#define platform_has_symlinks() mingw_platform_has_symlinks()
+
#ifndef NO_UNIX_SOCKETS
int mingw_have_unix_sockets(void);
#undef have_unix_sockets
entry.c
+2
-1
index 7817aee362..5913a8b51f 100644
--- a/entry.c
+++ b/entry.c
@@ -321,7 +321,8 @@ static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca
* We can't make a real symlink; write out a regular file entry
* with the symlink destination as its contents.
*/
- if (!has_symlinks || to_tempfile)
+ if (!repo_has_symlinks(state->istate && state->istate->repo ?
+ state->istate->repo : the_repository) || to_tempfile)
goto write_file_entry;
ret = symlink(new_blob, path);
environment.c
+10
-2
index 32b110c405..e351043446 100644
--- a/environment.c
+++ b/environment.c
@@ -43,7 +43,6 @@ static int zlib_compression_seen;
int trust_ctime = 1;
int check_stat = 1;
-int has_symlinks = 1;
int minimum_abbrev = 4, default_abbrev = -1;
int ignore_case;
int assume_unchanged;
@@ -148,6 +147,13 @@ int repo_trust_executable_bit(struct repository *repo)
: 1;
}
+int repo_has_symlinks(struct repository *repo)
+{
+ return repo->initialized
+ ? repo_config_values(repo)->has_symlinks
+ : platform_has_symlinks();
+}
+
int have_git_dir(void)
{
return startup_info->have_repository
@@ -336,7 +342,8 @@ int git_default_core_config(const char *var, const char *value,
}
if (!strcmp(var, "core.symlinks")) {
- has_symlinks = git_config_bool(var, value);
+ struct repo_config_values *cfg = repo_config_values(the_repository);
+ cfg->has_symlinks = git_config_bool(var, value);
return 0;
}
@@ -727,5 +734,6 @@ void repo_config_values_init(struct repo_config_values *cfg)
cfg->attributes_file = NULL;
cfg->apply_sparse_checkout = 0;
cfg->trust_executable_bit = 1;
+ cfg->has_symlinks = platform_has_symlinks();
cfg->branch_track = BRANCH_TRACK_REMOTE;
}
environment.h
+3
-1
index c15456fc0d..8f54c481e9 100644
--- a/environment.h
+++ b/environment.h
@@ -92,6 +92,7 @@ struct repo_config_values {
char *attributes_file;
int apply_sparse_checkout;
int trust_executable_bit;
+ int has_symlinks;
/* section "branch" config values */
enum branch_track branch_track;
@@ -126,6 +127,8 @@ int git_default_core_config(const char *var, const char *value,
int repo_trust_executable_bit(struct repository *repo);
+int repo_has_symlinks(struct repository *repo);
+
void repo_config_values_init(struct repo_config_values *cfg);
/*
@@ -163,7 +166,6 @@ extern char *git_work_tree_cfg;
/* Environment bits from configuration mechanism */
extern int trust_ctime;
extern int check_stat;
-extern int has_symlinks;
extern int minimum_abbrev, default_abbrev;
extern int ignore_case;
extern int assume_unchanged;
git-compat-util.h
+4
index 8809776407..a0f901ce79 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -245,6 +245,10 @@ static inline int git_is_dir_sep(int c)
#define is_dir_sep git_is_dir_sep
#endif
+#ifndef platform_has_symlinks
+#define platform_has_symlinks() 1
+#endif
+
#ifndef offset_1st_component
static inline int git_offset_1st_component(const char *path)
{
read-cache.c
+4
-3
index 90789ff049..046b1e649e 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -207,7 +207,7 @@ static unsigned int st_mode_from_ce(const struct cache_entry *ce)
{
switch (ce->ce_mode & S_IFMT) {
case S_IFLNK:
- return has_symlinks ? S_IFLNK : (S_IFREG | 0644);
+ return repo_has_symlinks(the_repository) ? S_IFLNK : (S_IFREG | 0644);
case S_IFREG:
return (ce->ce_mode & (repo_trust_executable_bit(the_repository) ? 0755 : 0644)) | S_IFREG;
case S_IFGITLINK:
@@ -325,7 +325,7 @@ static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
break;
case S_IFLNK:
if (!S_ISLNK(st->st_mode) &&
- (has_symlinks || !S_ISREG(st->st_mode)))
+ (repo_has_symlinks(the_repository) || !S_ISREG(st->st_mode)))
changed |= TYPE_CHANGED;
break;
case S_IFGITLINK:
@@ -740,7 +740,8 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
ce->ce_flags |= CE_INTENT_TO_ADD;
- if (repo_trust_executable_bit(istate->repo) && has_symlinks) {
+ if (repo_trust_executable_bit(istate->repo) &&
+ repo_has_symlinks(istate->repo)) {
ce->ce_mode = create_ce_mode(st_mode);
} else {
/* If there is an existing entry, pick the mode bits and type
read-cache.h
+1
-2
index 4b54cfc57c..ab9d40aa81 100644
--- a/read-cache.h
+++ b/read-cache.h
@@ -17,8 +17,7 @@ static inline unsigned int ce_mode_from_stat(struct repository *repo,
const struct cache_entry *ce,
unsigned int mode)
{
- extern int has_symlinks;
- if (S_ISREG(mode) && !has_symlinks &&
+ if (S_ISREG(mode) && !repo_has_symlinks(repo) &&
ce && S_ISLNK(ce->ce_mode))
return ce->ce_mode;
if (S_ISREG(mode) && !repo_trust_executable_bit(repo)) {