diff options
-rw-r--r-- | Documentation/config.txt | 7 | ||||
-rw-r--r-- | Documentation/git-update-index.txt | 5 | ||||
-rw-r--r-- | builtin-apply.c | 2 | ||||
-rw-r--r-- | builtin-update-index.c | 6 | ||||
-rw-r--r-- | cache.h | 6 | ||||
-rw-r--r-- | combine-diff.c | 10 | ||||
-rw-r--r-- | config.c | 5 | ||||
-rw-r--r-- | diff-lib.c | 3 | ||||
-rw-r--r-- | entry.c | 9 | ||||
-rw-r--r-- | environment.c | 1 | ||||
-rw-r--r-- | merge-recursive.c | 3 | ||||
-rw-r--r-- | read-cache.c | 10 | ||||
-rwxr-xr-x | t/t2005-checkout-index-symlinks.sh | 28 | ||||
-rwxr-xr-x | t/t2102-update-index-symlinks.sh | 31 | ||||
-rw-r--r-- | t/t6025-merge-symlinks.sh | 62 |
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); @@ -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) { @@ -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); @@ -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 |