Add core.symlinks to mark filesystems that do not support symbolic links.
authorJohannes Sixt <johannes.sixt@telecom.at>
Fri, 2 Mar 2007 21:11:30 +0000 (22:11 +0100)
committerJunio C Hamano <junkio@cox.net>
Sat, 3 Mar 2007 00:58:05 +0000 (16:58 -0800)
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>
12 files changed:
Documentation/config.txt
Documentation/git-update-index.txt
builtin-apply.c
builtin-update-index.c
cache.h
config.c
diff-lib.c
entry.c
environment.c
read-cache.c
t/t2005-checkout-index-symlinks.sh [new file with mode: 0755]
t/t2102-update-index-symlinks.sh [new file with mode: 0755]

index d2b4a05ca5849c59514c6f0e67c8b31ad444be9e..b809772b866c74ed28b170a1284b6bc59646ecda 100644 (file)
@@ -117,6 +117,13 @@ core.fileMode::
        the working copy are ignored; useful on broken filesystems like FAT.
        See gitlink:git-update-index[1]. True by default.
 
+core.symlinks::
+       If false, symbolic links are checked out as small plain files that
+       contain the link text. gitlink:git-update-index[1] and
+       gitlink:git-add[1] will not change the recorded type to regular
+       file. Useful on filesystems like FAT that do not support
+       symbolic links. True by default.
+
 core.gitProxy::
        A "proxy command" to execute (as 'command host port') instead
        of establishing direct connection to the remote server when
index b161c8b32b196b2e251ee65ee43e5aa0349b96b4..cd5e014d4899954b204173f6e1d84e011a307ac8 100644 (file)
@@ -295,6 +295,11 @@ in the index and the file mode on the filesystem if they differ only on
 executable bit.   On such an unfortunate filesystem, you may
 need to use `git-update-index --chmod=`.
 
+Quite similarly, if `core.symlinks` configuration variable is set
+to 'false' (see gitlink:git-config[1]), symbolic links are checked out
+as plain files, and this command does not modify a recorded file mode
+from symbolic link to regular file.
+
 The command looks at `core.ignorestat` configuration variable.  See
 'Using "assume unchanged" bit' section above.
 
index 38f647510a13f51e63a35aaf442aeb154e34915c..53935109a3e320eda6177b74be76c45ee2a6e4d2 100644 (file)
@@ -2359,7 +2359,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
        char *nbuf;
        unsigned long nsize;
 
-       if (S_ISLNK(mode))
+       if (has_symlinks && S_ISLNK(mode))
                /* Although buf:size is counted string, it also is NUL
                 * terminated.
                 */
index 65246dad8d075de3f77454494c419000288c9ed2..71cef633c0e36b3a33fa29eccb8579a9304b0777 100644 (file)
@@ -109,11 +109,11 @@ static int add_file_to_cache(const char *path)
        ce->ce_flags = htons(namelen);
        fill_stat_cache_info(ce, &st);
 
-       if (trust_executable_bit)
+       if (trust_executable_bit && has_symlinks)
                ce->ce_mode = create_ce_mode(st.st_mode);
        else {
-               /* If there is an existing entry, pick the mode bits
-                * from it, otherwise assume unexecutable.
+               /* If there is an existing entry, pick the mode bits and type
+                * from it, otherwise assume unexecutable regular file.
                 */
                struct cache_entry *ent;
                int pos = cache_name_pos(path, namelen);
diff --git a/cache.h b/cache.h
index 8018b2cd3b20533ea03ce503bc88b8bd0155432e..c291163e6d2096181ddf89954d2d65953d1ba687 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -108,7 +108,10 @@ static inline unsigned int create_ce_mode(unsigned int mode)
 }
 static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
 {
-       extern int trust_executable_bit;
+       extern int trust_executable_bit, has_symlinks;
+       if (!has_symlinks && S_ISREG(mode) &&
+           ce && S_ISLNK(ntohl(ce->ce_mode)))
+               return ce->ce_mode;
        if (!trust_executable_bit && S_ISREG(mode)) {
                if (ce && S_ISREG(ntohl(ce->ce_mode)))
                        return ce->ce_mode;
@@ -215,6 +218,7 @@ extern int delete_ref(const char *, unsigned char *sha1);
 /* Environment bits from configuration mechanism */
 extern int use_legacy_headers;
 extern int trust_executable_bit;
+extern int has_symlinks;
 extern int assume_unchanged;
 extern int prefer_symlink_refs;
 extern int log_all_ref_updates;
index 0ff413b804afc1ec042ed5190ae4f9055a17ea87..7ac3947858b8d2d44a7877d65579f16353b8d4ba 100644 (file)
--- a/config.c
+++ b/config.c
@@ -269,6 +269,11 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.symlinks")) {
+               has_symlinks = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
                return 0;
index 2e916199066db068d80ff5168b3feee6cfba7056..f70888f8af57b3b7a695ecdbc19a3b8bc04c13b2 100644 (file)
@@ -346,6 +346,9 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
                    S_ISREG(newmode) && S_ISREG(oldmode) &&
                    ((newmode ^ oldmode) == 0111))
                        newmode = oldmode;
+               else if (!has_symlinks &&
+                   S_ISREG(newmode) && S_ISLNK(oldmode))
+                       newmode = oldmode;
                diff_change(&revs->diffopt, oldmode, newmode,
                            ce->sha1, (changed ? null_sha1 : ce->sha1),
                            ce->name, NULL);
diff --git a/entry.c b/entry.c
index 21b5f2e26d77a2e7b10a6336d9e14db72b1f7e45..d72f811580ad10e792e38b40fe79bf4af3868846 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -111,9 +111,12 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
                        return error("git-checkout-index: unable to write file %s", path);
                break;
        case S_IFLNK:
-               if (to_tempfile) {
-                       strcpy(path, ".merge_link_XXXXXX");
-                       fd = mkstemp(path);
+               if (to_tempfile || !has_symlinks) {
+                       if (to_tempfile) {
+                               strcpy(path, ".merge_link_XXXXXX");
+                               fd = mkstemp(path);
+                       } else
+                               fd = create_file(path, 0666);
                        if (fd < 0) {
                                free(new);
                                return error("git-checkout-index: unable to create "
index 570e32ac3cae2c1502a49a6ca1aacebb52f42ce0..49486dd9f161bd8e140c5a1b3346180ded51c069 100644 (file)
@@ -13,6 +13,7 @@ char git_default_email[MAX_GITNAME];
 char git_default_name[MAX_GITNAME];
 int use_legacy_headers = 1;
 int trust_executable_bit = 1;
+int has_symlinks = 1;
 int assume_unchanged;
 int prefer_symlink_refs;
 int is_bare_repository_cfg = -1; /* unspecified */
index 7a104e351263ee47dc51bcca2ce2761fbd7f5092..4a972b4ab76edfa608704fae7f70097a0570d5ac 100644 (file)
@@ -125,7 +125,9 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
                        changed |= MODE_CHANGED;
                break;
        case S_IFLNK:
-               changed |= !S_ISLNK(st->st_mode) ? TYPE_CHANGED : 0;
+               if (!S_ISLNK(st->st_mode) &&
+                   (has_symlinks || !S_ISREG(st->st_mode)))
+                       changed |= TYPE_CHANGED;
                break;
        default:
                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)
        ce->ce_flags = htons(namelen);
        fill_stat_cache_info(ce, &st);
 
-       if (trust_executable_bit)
+       if (trust_executable_bit && has_symlinks)
                ce->ce_mode = create_ce_mode(st.st_mode);
        else {
-               /* If there is an existing entry, pick the mode bits
-                * from it, otherwise assume unexecutable.
+               /* If there is an existing entry, pick the mode bits and type
+                * from it, otherwise assume unexecutable regular file.
                 */
                struct cache_entry *ent;
                int pos = cache_name_pos(path, namelen);
diff --git a/t/t2005-checkout-index-symlinks.sh b/t/t2005-checkout-index-symlinks.sh
new file mode 100755 (executable)
index 0000000..e34a515
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes Sixt
+#
+
+test_description='git-checkout-index on filesystem w/o symlinks test.
+
+This tests that git-checkout-index creates a symbolic link as a plain
+file if core.symlinks is false.'
+
+. ./test-lib.sh
+
+test_expect_success \
+'preparation' '
+git-config core.symlinks false &&
+l=$(echo -n file | git-hash-object -t blob -w --stdin) &&
+echo "120000 $l        symlink" | git-update-index --index-info'
+
+test_expect_success \
+'the checked-out symlink must be a file' '
+git-checkout-index symlink &&
+test -f symlink'
+
+test_expect_success \
+'the file must be the blob we added during the setup' '
+test "$(git-hash-object -t blob symlink)" = $l'
+
+test_done
diff --git a/t/t2102-update-index-symlinks.sh b/t/t2102-update-index-symlinks.sh
new file mode 100755 (executable)
index 0000000..969ef89
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes Sixt
+#
+
+test_description='git-update-index on filesystem w/o symlinks test.
+
+This tests that git-update-index keeps the symbolic link property
+even if a plain file is in the working tree if core.symlinks is false.'
+
+. ./test-lib.sh
+
+test_expect_success \
+'preparation' '
+git-config core.symlinks false &&
+l=$(echo -n file | git-hash-object -t blob -w --stdin) &&
+echo "120000 $l        symlink" | git-update-index --index-info'
+
+test_expect_success \
+'modify the symbolic link' '
+echo -n new-file > symlink &&
+git-update-index symlink'
+
+test_expect_success \
+'the index entry must still be a symbolic link' '
+case "`git-ls-files --stage --cached symlink`" in
+120000" "*symlink) echo ok;;
+*) echo fail; git-ls-files --stage --cached symlink; (exit 1);;
+esac'
+
+test_done