Skip to content

Commit 78a8d64

Browse files
Johannes SixtJunio C Hamano
authored andcommitted
Add core.symlinks to mark filesystems that do not support symbolic links.
Some file systems that can host git repositories and their working copies do not support symbolic links. But then if the repository contains a symbolic link, it is impossible to check out the working copy. This patch enables partial support of symbolic links so that it is possible to check out a working copy on such a file system. A new flag core.symlinks (which is true by default) can be set to false to indicate that the filesystem does not support symbolic links. In this case, symbolic links that exist in the trees are checked out as small plain files, and checking in modifications of these files preserve the symlink property in the database (as long as an entry exists in the index). Of course, this does not magically make symbolic links work on such defective file systems; hence, this solution does not help if the working copy relies on that an entry is a real symbolic link. Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at> Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent 4808bec commit 78a8d64

File tree

12 files changed

+101
-12
lines changed

12 files changed

+101
-12
lines changed

Documentation/config.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ core.fileMode::
117117
the working copy are ignored; useful on broken filesystems like FAT.
118118
See gitlink:git-update-index[1]. True by default.
119119

120+
core.symlinks::
121+
If false, symbolic links are checked out as small plain files that
122+
contain the link text. gitlink:git-update-index[1] and
123+
gitlink:git-add[1] will not change the recorded type to regular
124+
file. Useful on filesystems like FAT that do not support
125+
symbolic links. True by default.
126+
120127
core.gitProxy::
121128
A "proxy command" to execute (as 'command host port') instead
122129
of establishing direct connection to the remote server when

Documentation/git-update-index.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ in the index and the file mode on the filesystem if they differ only on
295295
executable bit. On such an unfortunate filesystem, you may
296296
need to use `git-update-index --chmod=`.
297297

298+
Quite similarly, if `core.symlinks` configuration variable is set
299+
to 'false' (see gitlink:git-config[1]), symbolic links are checked out
300+
as plain files, and this command does not modify a recorded file mode
301+
from symbolic link to regular file.
302+
298303
The command looks at `core.ignorestat` configuration variable. See
299304
'Using "assume unchanged" bit' section above.
300305

builtin-apply.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2359,7 +2359,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
23592359
char *nbuf;
23602360
unsigned long nsize;
23612361

2362-
if (S_ISLNK(mode))
2362+
if (has_symlinks && S_ISLNK(mode))
23632363
/* Although buf:size is counted string, it also is NUL
23642364
* terminated.
23652365
*/

builtin-update-index.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,11 @@ static int add_file_to_cache(const char *path)
109109
ce->ce_flags = htons(namelen);
110110
fill_stat_cache_info(ce, &st);
111111

112-
if (trust_executable_bit)
112+
if (trust_executable_bit && has_symlinks)
113113
ce->ce_mode = create_ce_mode(st.st_mode);
114114
else {
115-
/* If there is an existing entry, pick the mode bits
116-
* from it, otherwise assume unexecutable.
115+
/* If there is an existing entry, pick the mode bits and type
116+
* from it, otherwise assume unexecutable regular file.
117117
*/
118118
struct cache_entry *ent;
119119
int pos = cache_name_pos(path, namelen);

cache.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,10 @@ static inline unsigned int create_ce_mode(unsigned int mode)
108108
}
109109
static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
110110
{
111-
extern int trust_executable_bit;
111+
extern int trust_executable_bit, has_symlinks;
112+
if (!has_symlinks && S_ISREG(mode) &&
113+
ce && S_ISLNK(ntohl(ce->ce_mode)))
114+
return ce->ce_mode;
112115
if (!trust_executable_bit && S_ISREG(mode)) {
113116
if (ce && S_ISREG(ntohl(ce->ce_mode)))
114117
return ce->ce_mode;
@@ -215,6 +218,7 @@ extern int delete_ref(const char *, unsigned char *sha1);
215218
/* Environment bits from configuration mechanism */
216219
extern int use_legacy_headers;
217220
extern int trust_executable_bit;
221+
extern int has_symlinks;
218222
extern int assume_unchanged;
219223
extern int prefer_symlink_refs;
220224
extern int log_all_ref_updates;

config.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,11 @@ int git_default_config(const char *var, const char *value)
269269
return 0;
270270
}
271271

272+
if (!strcmp(var, "core.symlinks")) {
273+
has_symlinks = git_config_bool(var, value);
274+
return 0;
275+
}
276+
272277
if (!strcmp(var, "core.bare")) {
273278
is_bare_repository_cfg = git_config_bool(var, value);
274279
return 0;

diff-lib.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,9 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
346346
S_ISREG(newmode) && S_ISREG(oldmode) &&
347347
((newmode ^ oldmode) == 0111))
348348
newmode = oldmode;
349+
else if (!has_symlinks &&
350+
S_ISREG(newmode) && S_ISLNK(oldmode))
351+
newmode = oldmode;
349352
diff_change(&revs->diffopt, oldmode, newmode,
350353
ce->sha1, (changed ? null_sha1 : ce->sha1),
351354
ce->name, NULL);

entry.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,12 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
111111
return error("git-checkout-index: unable to write file %s", path);
112112
break;
113113
case S_IFLNK:
114-
if (to_tempfile) {
115-
strcpy(path, ".merge_link_XXXXXX");
116-
fd = mkstemp(path);
114+
if (to_tempfile || !has_symlinks) {
115+
if (to_tempfile) {
116+
strcpy(path, ".merge_link_XXXXXX");
117+
fd = mkstemp(path);
118+
} else
119+
fd = create_file(path, 0666);
117120
if (fd < 0) {
118121
free(new);
119122
return error("git-checkout-index: unable to create "

environment.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ char git_default_email[MAX_GITNAME];
1313
char git_default_name[MAX_GITNAME];
1414
int use_legacy_headers = 1;
1515
int trust_executable_bit = 1;
16+
int has_symlinks = 1;
1617
int assume_unchanged;
1718
int prefer_symlink_refs;
1819
int is_bare_repository_cfg = -1; /* unspecified */

read-cache.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
125125
changed |= MODE_CHANGED;
126126
break;
127127
case S_IFLNK:
128-
changed |= !S_ISLNK(st->st_mode) ? TYPE_CHANGED : 0;
128+
if (!S_ISLNK(st->st_mode) &&
129+
(has_symlinks || !S_ISREG(st->st_mode)))
130+
changed |= TYPE_CHANGED;
129131
break;
130132
default:
131133
die("internal error: ce_mode is %o", ntohl(ce->ce_mode));
@@ -344,11 +346,11 @@ int add_file_to_index(const char *path, int verbose)
344346
ce->ce_flags = htons(namelen);
345347
fill_stat_cache_info(ce, &st);
346348

347-
if (trust_executable_bit)
349+
if (trust_executable_bit && has_symlinks)
348350
ce->ce_mode = create_ce_mode(st.st_mode);
349351
else {
350-
/* If there is an existing entry, pick the mode bits
351-
* from it, otherwise assume unexecutable.
352+
/* If there is an existing entry, pick the mode bits and type
353+
* from it, otherwise assume unexecutable regular file.
352354
*/
353355
struct cache_entry *ent;
354356
int pos = cache_name_pos(path, namelen);

0 commit comments

Comments
 (0)