summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/config.txt7
-rw-r--r--Documentation/git-update-index.txt5
-rw-r--r--builtin-apply.c2
-rw-r--r--builtin-update-index.c6
-rw-r--r--cache.h6
-rw-r--r--combine-diff.c10
-rw-r--r--config.c5
-rw-r--r--diff-lib.c3
-rw-r--r--entry.c9
-rw-r--r--environment.c1
-rw-r--r--merge-recursive.c3
-rw-r--r--read-cache.c10
-rwxr-xr-xt/t2005-checkout-index-symlinks.sh28
-rwxr-xr-xt/t2102-update-index-symlinks.sh31
-rw-r--r--t/t6025-merge-symlinks.sh62
15 files changed, 175 insertions, 13 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt
index d20902bc33..5408dd67d3 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -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
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index b161c8b32b..cd5e014d48 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -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.
diff --git a/builtin-apply.c b/builtin-apply.c
index 38f647510a..53935109a3 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -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.
*/
diff --git a/builtin-update-index.c b/builtin-update-index.c
index 65246dad8d..71cef633c0 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -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 8018b2cd3b..c291163e6d 100644
--- 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;
diff --git a/combine-diff.c b/combine-diff.c
index 9daa0cb9a9..6d928f282a 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -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) {
diff --git a/config.c b/config.c
index 0ff413b804..7ac3947858 100644
--- 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;
diff --git a/diff-lib.c b/diff-lib.c
index 88e59b5794..778cf58244 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -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 21b5f2e26d..d72f811580 100644
--- 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 "
diff --git a/environment.c b/environment.c
index 570e32ac3c..49486dd9f1 100644
--- a/environment.c
+++ b/environment.c
@@ -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 */
diff --git a/merge-recursive.c b/merge-recursive.c
index 87cd8709bb..c96e1a734c 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -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);
diff --git a/read-cache.c b/read-cache.c
index 7a104e3512..4a972b4ab7 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -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
index 0000000000..e34a515333
--- /dev/null
+++ b/t/t2005-checkout-index-symlinks.sh
@@ -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
index 0000000000..969ef891d3
--- /dev/null
+++ b/t/t2102-update-index-symlinks.sh
@@ -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
index 0000000000..3c1a6972bd
--- /dev/null
+++ b/t/t6025-merge-symlinks.sh
@@ -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