提交 e6f95113 编写于 作者: J Junio C Hamano

Merge branch 'js/symlink'

* 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.
...@@ -117,6 +117,13 @@ core.fileMode:: ...@@ -117,6 +117,13 @@ core.fileMode::
the working copy are ignored; useful on broken filesystems like FAT. the working copy are ignored; useful on broken filesystems like FAT.
See gitlink:git-update-index[1]. True by default. 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:: core.gitProxy::
A "proxy command" to execute (as 'command host port') instead A "proxy command" to execute (as 'command host port') instead
of establishing direct connection to the remote server when of establishing direct connection to the remote server when
......
...@@ -295,6 +295,11 @@ in the index and the file mode on the filesystem if they differ only on ...@@ -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 executable bit. On such an unfortunate filesystem, you may
need to use `git-update-index --chmod=`. 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 The command looks at `core.ignorestat` configuration variable. See
'Using "assume unchanged" bit' section above. 'Using "assume unchanged" bit' section above.
......
...@@ -2359,7 +2359,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, ...@@ -2359,7 +2359,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
char *nbuf; char *nbuf;
unsigned long nsize; unsigned long nsize;
if (S_ISLNK(mode)) if (has_symlinks && S_ISLNK(mode))
/* Although buf:size is counted string, it also is NUL /* Although buf:size is counted string, it also is NUL
* terminated. * terminated.
*/ */
......
...@@ -109,11 +109,11 @@ static int add_file_to_cache(const char *path) ...@@ -109,11 +109,11 @@ static int add_file_to_cache(const char *path)
ce->ce_flags = htons(namelen); ce->ce_flags = htons(namelen);
fill_stat_cache_info(ce, &st); 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); ce->ce_mode = create_ce_mode(st.st_mode);
else { else {
/* If there is an existing entry, pick the mode bits /* If there is an existing entry, pick the mode bits and type
* from it, otherwise assume unexecutable. * from it, otherwise assume unexecutable regular file.
*/ */
struct cache_entry *ent; struct cache_entry *ent;
int pos = cache_name_pos(path, namelen); int pos = cache_name_pos(path, namelen);
......
...@@ -108,7 +108,10 @@ static inline unsigned int create_ce_mode(unsigned int mode) ...@@ -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) 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 (!trust_executable_bit && S_ISREG(mode)) {
if (ce && S_ISREG(ntohl(ce->ce_mode))) if (ce && S_ISREG(ntohl(ce->ce_mode)))
return ce->ce_mode; return ce->ce_mode;
...@@ -215,6 +218,7 @@ extern int delete_ref(const char *, unsigned char *sha1); ...@@ -215,6 +218,7 @@ extern int delete_ref(const char *, unsigned char *sha1);
/* Environment bits from configuration mechanism */ /* Environment bits from configuration mechanism */
extern int use_legacy_headers; extern int use_legacy_headers;
extern int trust_executable_bit; extern int trust_executable_bit;
extern int has_symlinks;
extern int assume_unchanged; extern int assume_unchanged;
extern int prefer_symlink_refs; extern int prefer_symlink_refs;
extern int log_all_ref_updates; extern int log_all_ref_updates;
......
...@@ -699,8 +699,18 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, ...@@ -699,8 +699,18 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
!fstat(fd, &st)) { !fstat(fd, &st)) {
size_t len = st.st_size; size_t len = st.st_size;
size_t sz = 0; size_t sz = 0;
int is_file, i;
elem->mode = canon_mode(st.st_mode); 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_size = len;
result = xmalloc(len + 1); result = xmalloc(len + 1);
while (sz < len) { while (sz < len) {
......
...@@ -269,6 +269,11 @@ int git_default_config(const char *var, const char *value) ...@@ -269,6 +269,11 @@ int git_default_config(const char *var, const char *value)
return 0; return 0;
} }
if (!strcmp(var, "core.symlinks")) {
has_symlinks = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "core.bare")) { if (!strcmp(var, "core.bare")) {
is_bare_repository_cfg = git_config_bool(var, value); is_bare_repository_cfg = git_config_bool(var, value);
return 0; return 0;
......
...@@ -412,6 +412,9 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed) ...@@ -412,6 +412,9 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
S_ISREG(newmode) && S_ISREG(oldmode) && S_ISREG(newmode) && S_ISREG(oldmode) &&
((newmode ^ oldmode) == 0111)) ((newmode ^ oldmode) == 0111))
newmode = oldmode; newmode = oldmode;
else if (!has_symlinks &&
S_ISREG(newmode) && S_ISLNK(oldmode))
newmode = oldmode;
diff_change(&revs->diffopt, oldmode, newmode, diff_change(&revs->diffopt, oldmode, newmode,
ce->sha1, (changed ? null_sha1 : ce->sha1), ce->sha1, (changed ? null_sha1 : ce->sha1),
ce->name, NULL); ce->name, NULL);
......
...@@ -111,9 +111,12 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat ...@@ -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); return error("git-checkout-index: unable to write file %s", path);
break; break;
case S_IFLNK: case S_IFLNK:
if (to_tempfile) { if (to_tempfile || !has_symlinks) {
strcpy(path, ".merge_link_XXXXXX"); if (to_tempfile) {
fd = mkstemp(path); strcpy(path, ".merge_link_XXXXXX");
fd = mkstemp(path);
} else
fd = create_file(path, 0666);
if (fd < 0) { if (fd < 0) {
free(new); free(new);
return error("git-checkout-index: unable to create " return error("git-checkout-index: unable to create "
......
...@@ -13,6 +13,7 @@ char git_default_email[MAX_GITNAME]; ...@@ -13,6 +13,7 @@ char git_default_email[MAX_GITNAME];
char git_default_name[MAX_GITNAME]; char git_default_name[MAX_GITNAME];
int use_legacy_headers = 1; int use_legacy_headers = 1;
int trust_executable_bit = 1; int trust_executable_bit = 1;
int has_symlinks = 1;
int assume_unchanged; int assume_unchanged;
int prefer_symlink_refs; int prefer_symlink_refs;
int is_bare_repository_cfg = -1; /* unspecified */ int is_bare_repository_cfg = -1; /* unspecified */
......
...@@ -570,7 +570,7 @@ static void update_file_flags(const unsigned char *sha, ...@@ -570,7 +570,7 @@ static void update_file_flags(const unsigned char *sha,
if (type != OBJ_BLOB) if (type != OBJ_BLOB)
die("blob expected for %s '%s'", sha1_to_hex(sha), path); 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; int fd;
if (mkdir_p(path, 0777)) if (mkdir_p(path, 0777))
die("failed to create path %s: %s", path, strerror(errno)); die("failed to create path %s: %s", path, strerror(errno));
...@@ -591,6 +591,7 @@ static void update_file_flags(const unsigned char *sha, ...@@ -591,6 +591,7 @@ static void update_file_flags(const unsigned char *sha,
mkdir_p(path, 0777); mkdir_p(path, 0777);
unlink(path); unlink(path);
symlink(lnk, path); symlink(lnk, path);
free(lnk);
} else } else
die("do not know what to do with %06o %s '%s'", die("do not know what to do with %06o %s '%s'",
mode, sha1_to_hex(sha), path); mode, sha1_to_hex(sha), path);
......
...@@ -125,7 +125,9 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) ...@@ -125,7 +125,9 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
changed |= MODE_CHANGED; changed |= MODE_CHANGED;
break; break;
case S_IFLNK: 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; break;
default: default:
die("internal error: ce_mode is %o", ntohl(ce->ce_mode)); 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) ...@@ -344,11 +346,11 @@ int add_file_to_index(const char *path, int verbose)
ce->ce_flags = htons(namelen); ce->ce_flags = htons(namelen);
fill_stat_cache_info(ce, &st); 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); ce->ce_mode = create_ce_mode(st.st_mode);
else { else {
/* If there is an existing entry, pick the mode bits /* If there is an existing entry, pick the mode bits and type
* from it, otherwise assume unexecutable. * from it, otherwise assume unexecutable regular file.
*/ */
struct cache_entry *ent; struct cache_entry *ent;
int pos = cache_name_pos(path, namelen); int pos = cache_name_pos(path, namelen);
......
#!/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
#!/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
#!/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
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册