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.
This commit is contained in:
commit
e6f9511343
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
6
cache.h
6
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;
|
||||
|
@ -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) {
|
||||
|
5
config.c
5
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;
|
||||
|
@ -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);
|
||||
|
9
entry.c
9
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 "
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
10
read-cache.c
10
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);
|
||||
|
28
t/t2005-checkout-index-symlinks.sh
Executable file
28
t/t2005-checkout-index-symlinks.sh
Executable file
@ -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
|
31
t/t2102-update-index-symlinks.sh
Executable file
31
t/t2102-update-index-symlinks.sh
Executable file
@ -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
|
62
t/t6025-merge-symlinks.sh
Normal file
62
t/t6025-merge-symlinks.sh
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user