Merge branch 'js/symlink'
authorJunio C Hamano <junkio@cox.net>
Mon, 5 Mar 2007 01:31:09 +0000 (17:31 -0800)
committerJunio C Hamano <junkio@cox.net>
Mon, 5 Mar 2007 01:31:09 +0000 (17:31 -0800)
* js/symlink:
Tell multi-parent diff about core.symlinks.
Handle core.symlinks=false case in merge-recursive.
Add core.symlinks to mark filesystems that do not support symbolic links.

15 files changed:
Documentation/config.txt
Documentation/git-update-index.txt
builtin-apply.c
builtin-update-index.c
cache.h
combine-diff.c
config.c
diff-lib.c
entry.c
environment.c
merge-recursive.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]
t/t6025-merge-symlinks.sh [new file with mode: 0644]
index d20902bc33226fbb7395fe4d003e9c8408eefdb2..5408dd67d316ce3334f2843267a604f5a4ea044b 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 9daa0cb9a95fe6c2176a927f093d8788d0195240..6d928f282a490a1295b11e31756e20b32cbc3822 100644 (file)
@@ -699,8 +699,18 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                         !fstat(fd, &st)) {
                        size_t len = st.st_size;
                        size_t sz = 0;
+                       int is_file, i;
 
                        elem->mode = canon_mode(st.st_mode);
+                       /* if symlinks don't work, assume symlink if all parents
+                        * are symlinks
+                        */
+                       is_file = has_symlinks;
+                       for (i = 0; !is_file && i < num_parent; i++)
+                               is_file = !S_ISLNK(elem->parent[i].mode);
+                       if (!is_file)
+                               elem->mode = canon_mode(S_IFLNK);
+
                        result_size = len;
                        result = xmalloc(len + 1);
                        while (sz < len) {
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 88e59b5794236e8ca3162c8dd823ea1a64b4683b..778cf58244d64b1e1b14ead43c2fdf5735bd717f 100644 (file)
@@ -412,6 +412,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 87cd8709bbc99db559f3892c048ec111673a2566..c96e1a734cb0c258cd4adb4f00e247abdf804640 100644 (file)
@@ -570,7 +570,7 @@ static void update_file_flags(const unsigned char *sha,
                if (type != OBJ_BLOB)
                        die("blob expected for %s '%s'", sha1_to_hex(sha), path);
 
-               if (S_ISREG(mode)) {
+               if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {
                        int fd;
                        if (mkdir_p(path, 0777))
                                die("failed to create path %s: %s", path, strerror(errno));
@@ -591,6 +591,7 @@ static void update_file_flags(const unsigned char *sha,
                        mkdir_p(path, 0777);
                        unlink(path);
                        symlink(lnk, path);
+                       free(lnk);
                } else
                        die("do not know what to do with %06o %s '%s'",
                            mode, sha1_to_hex(sha), path);
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
diff --git a/t/t6025-merge-symlinks.sh b/t/t6025-merge-symlinks.sh
new file mode 100644 (file)
index 0000000..3c1a697
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes Sixt
+#
+
+test_description='merging symlinks on filesystem w/o symlink support.
+
+This tests that git-merge-recursive writes merge results as plain files
+if core.symlinks is false.'
+
+. ./test-lib.sh
+
+test_expect_success \
+'setup' '
+git-config core.symlinks false &&
+> file &&
+git-add file &&
+git-commit -m initial &&
+git-branch b-symlink &&
+git-branch b-file &&
+l=$(echo -n file | git-hash-object -t blob -w --stdin) &&
+echo "120000 $l        symlink" | git-update-index --index-info &&
+git-commit -m master &&
+git-checkout b-symlink &&
+l=$(echo -n file-different | git-hash-object -t blob -w --stdin) &&
+echo "120000 $l        symlink" | git-update-index --index-info &&
+git-commit -m b-symlink &&
+git-checkout b-file &&
+echo plain-file > symlink &&
+git-add symlink &&
+git-commit -m b-file'
+
+test_expect_failure \
+'merge master into b-symlink, which has a different symbolic link' '
+! git-checkout b-symlink ||
+git-merge master'
+
+test_expect_success \
+'the merge result must be a file' '
+test -f symlink'
+
+test_expect_failure \
+'merge master into b-file, which has a file instead of a symbolic link' '
+! (git-reset --hard &&
+git-checkout b-file) ||
+git-merge master'
+
+test_expect_success \
+'the merge result must be a file' '
+test -f symlink'
+
+test_expect_failure \
+'merge b-file, which has a file instead of a symbolic link, into master' '
+! (git-reset --hard &&
+git-checkout master) ||
+git-merge b-file'
+
+test_expect_success \
+'the merge result must be a file' '
+test -f symlink'
+
+test_done