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:
Junio C Hamano 2007-03-04 17:31:09 -08:00
commit e6f9511343
15 changed files with 175 additions and 13 deletions

View 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

View 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.

View 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.
*/

View 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);

View File

@ -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;

View 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) {

View File

@ -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;

View 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);

View File

@ -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 "

View 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 */

View 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);

View 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);

View 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

View 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
View 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