Git development
 help / color / mirror / Atom feed
From: Tian Yuchen <cat@malon.dev>
To: git@vger.kernel.org
Cc: ps@pks.im, cirnovskyv@gmail.com, Tian Yuchen <cat@malon.dev>,
	Christian Couder <christian.couder@gmail.com>,
	Ayush Chandekar <ayu.chandekar@gmail.com>,
	Olamide Caleb Bello <belkid98@gmail.com>
Subject: [PATCH v5 4/4] environment: move has_symlinks into repo_config_values
Date: Wed, 15 Jul 2026 11:55:01 +0800	[thread overview]
Message-ID: <20260715035501.48271-5-cat@malon.dev> (raw)
In-Reply-To: <20260715035501.48271-1-cat@malon.dev>

Move the global 'has_symlinks' configuration into the
repository-specific 'repo_config_values' struct.

To ensure code readability, the getter function
'repo_has_symlinks()' has been introduced. Callers access
this configuration by passing in 'repo' when possible,
and explicitly fall back to 'the_repository' the rest
of the time.

Note:
To support early platform-specific (MinGW) overrides
before repository initialization, a global variable
'default_has_symlinks' fallback is introduced as a fallback
in environment.h. The *writer* in compat/mingw.c can only
access this variable.

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>
---
 apply.c        |  2 +-
 combine-diff.c |  2 +-
 compat/mingw.c |  7 ++++---
 entry.c        |  2 +-
 environment.c  | 17 +++++++++++++++--
 environment.h  |  5 ++++-
 read-cache.c   |  9 +++++----
 7 files changed, 31 insertions(+), 13 deletions(-)

diff --git a/apply.c b/apply.c
index 47b6ae5904..4ce4160b48 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.
 		 */
diff --git a/combine-diff.c b/combine-diff.c
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)
diff --git a/compat/mingw.c b/compat/mingw.c
index aa7525f419..b38cd9a1ec 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;
 	}
@@ -3181,7 +3182,7 @@ static void setup_windows_environment(void)
 	 * symlink support.
 	 */
 	if (!(tmp = getenv("MSYS")) || !strstr(tmp, "winsymlinks:nativestrict"))
-		has_symlinks = 0;
+		default_has_symlinks = 0;
 }
 
 static void get_current_user_sid(PSID *sid, HANDLE *linked_token)
diff --git a/entry.c b/entry.c
index 7817aee362..f2854b4cd8 100644
--- a/entry.c
+++ b/entry.c
@@ -321,7 +321,7 @@ 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 : NULL) || to_tempfile)
 			goto write_file_entry;
 
 		ret = symlink(new_blob, path);
diff --git a/environment.c b/environment.c
index 75069a884d..c7cebe18fd 100644
--- a/environment.c
+++ b/environment.c
@@ -43,7 +43,7 @@ static int zlib_compression_seen;
 
 int trust_ctime = 1;
 int check_stat = 1;
-int has_symlinks = 1;
+int default_has_symlinks = 1;
 int minimum_abbrev = 4, default_abbrev = -1;
 int ignore_case;
 int assume_unchanged;
@@ -148,6 +148,17 @@ int repo_trust_executable_bit(struct repository *repo)
 		1;
 }
 
+int repo_has_symlinks(struct repository *repo)
+{
+	if (!repo)
+		repo = the_repository;
+
+	if (!repo->gitdir)
+		return default_has_symlinks;
+
+	return repo_config_values(repo)->has_symlinks;
+}
+
 int have_git_dir(void)
 {
 	return startup_info->have_repository
@@ -336,7 +347,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 +739,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 = default_has_symlinks;
 	cfg->branch_track = BRANCH_TRACK_REMOTE;
 }
diff --git a/environment.h b/environment.h
index 72b59fd89c..e590225c86 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);
 
 /*
@@ -165,7 +168,7 @@ 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 default_has_symlinks;
 extern int minimum_abbrev, default_abbrev;
 extern int ignore_case;
 extern int assume_unchanged;
diff --git a/read-cache.c b/read-cache.c
index a9c11a3346..5a40ffa061 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -211,7 +211,7 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
  */
 unsigned int ce_mode_from_stat(const struct cache_entry *ce, unsigned int mode)
 {
-	if (!has_symlinks && S_ISREG(mode) &&
+	if (!repo_has_symlinks(the_repository) && S_ISREG(mode) &&
 	    ce && S_ISLNK(ce->ce_mode))
 		return ce->ce_mode;
 	if (!repo_trust_executable_bit(the_repository) && S_ISREG(mode)) {
@@ -226,7 +226,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:
@@ -344,7 +344,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:
@@ -759,7 +759,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
-- 
2.43.0


  parent reply	other threads:[~2026-07-15  3:55 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-30 16:05 [PATCH v1 0/4] environment.c: migrate 'trust_executable_bit' into 'repo_config_values' Tian Yuchen
2026-05-30 16:05 ` [PATCH v1 1/4] read-cache: remove redundant extern declarations Tian Yuchen
2026-05-30 16:05 ` [PATCH v1 2/4] read-cache: move 'ce_mode_from_stat()' to 'read-cache.c' Tian Yuchen
2026-06-04  6:47   ` Patrick Steinhardt
2026-06-10  8:30     ` Tian Yuchen
2026-05-30 16:05 ` [PATCH v1 3/4] environment: move 'trust_executable_bit' into repo_config_values Tian Yuchen
2026-05-30 18:02   ` Christian Couder
2026-05-30 23:17   ` Junio C Hamano
2026-06-01 10:10     ` Tian Yuchen
2026-06-01 18:03       ` Tian Yuchen
2026-06-09 13:45         ` Junio C Hamano
2026-05-30 16:05 ` [PATCH v1 4/4] read-cache: pass 'istate' to stat/mode helper functions Tian Yuchen
2026-05-30 18:14   ` Christian Couder
2026-06-10  9:36 ` [PATCH v2 0/3] environment: migrate 'trust_executable_bit' into 'repo_config_values' Tian Yuchen
2026-06-10  9:36   ` [PATCH v2 1/3] read-cache: remove redundant extern declarations Tian Yuchen
2026-06-10  9:36   ` [PATCH v2 2/3] read-cache: move 'ce_mode_from_stat()' to 'read-cache.c' Tian Yuchen
2026-06-12  7:43     ` Christian Couder
2026-06-10  9:36   ` [PATCH v2 3/3] environment: move trust_executable_bit into repo_config_values Tian Yuchen
2026-06-11  2:58     ` Tian Yuchen
2026-06-12  7:48     ` Christian Couder
2026-06-12 15:21       ` Junio C Hamano
2026-06-12 16:05   ` [PATCH v3 0/3] environment: migrate 'trust_executable_bit' into 'repo_config_values' Tian Yuchen
2026-06-12 16:05     ` [PATCH v3 1/3] read-cache: remove redundant extern declarations Tian Yuchen
2026-06-12 16:05     ` [PATCH v3 2/3] read-cache: move 'ce_mode_from_stat()' to 'read-cache.c' Tian Yuchen
2026-06-12 16:05     ` [PATCH v3 3/3] environment: move trust_executable_bit into repo_config_values Tian Yuchen
2026-06-19 16:21     ` [PATCH v4 0/3] environment: migrate 'trust_executable_bit' into 'repo_config_values' Tian Yuchen
2026-06-19 16:21       ` [PATCH v4 1/3] read-cache: remove redundant extern declarations Tian Yuchen
2026-06-19 16:21       ` [PATCH v4 2/3] read-cache: move 'ce_mode_from_stat()' to 'read-cache.c' Tian Yuchen
2026-06-19 16:21       ` [PATCH v4 3/3] environment: move trust_executable_bit into repo_config_values Tian Yuchen
2026-06-29 20:55       ` [PATCH v4 0/3] environment: migrate 'trust_executable_bit' into 'repo_config_values' Junio C Hamano
2026-07-15  3:28         ` Tian Yuchen
2026-07-15  3:54       ` [PATCH v5 0/4] environment: migrate 'trust_executable_bit' and 'has_symlinks' " Tian Yuchen
2026-07-15  3:54         ` [PATCH v5 1/4] read-cache: remove redundant extern declarations Tian Yuchen
2026-07-15  3:54         ` [PATCH v5 2/4] read-cache: move 'ce_mode_from_stat()' to 'read-cache.c' Tian Yuchen
2026-07-15  3:55         ` [PATCH v5 3/4] environment: move trust_executable_bit into repo_config_values Tian Yuchen
2026-07-15  3:55         ` Tian Yuchen [this message]
2026-07-15  6:21           ` [PATCH v5 4/4] environment: move has_symlinks " Christian Couder

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260715035501.48271-5-cat@malon.dev \
    --to=cat@malon.dev \
    --cc=ayu.chandekar@gmail.com \
    --cc=belkid98@gmail.com \
    --cc=christian.couder@gmail.com \
    --cc=cirnovskyv@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=ps@pks.im \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox