From 4f6339b0c353b5c03b566b605e50a743d97fc08e Mon Sep 17 00:00:00 2001 From: "Pickens, James E" Date: Wed, 29 Jul 2009 14:02:39 -0700 Subject: Demonstrate bugs when a directory is replaced with a symlink This test creates two directories, a/b and a/b-2, then replaces a/b with a symlink to a/b-2, then merges that change into the 'baseline' commit, which contains an unrelated change. There are two bugs: 1. 'git checkout' incorrectly deletes work tree file a/b-2/d. 2. 'git merge' incorrectly deletes work tree file a/b-2/d. The test goes on to create another branch in which a/b-2 is replaced with a symlink to a/b (i.e., the reverse of what was done the first time), and merge it into the 'baseline' commit. There is a different bug: 3. The merge should be clean, but git reports a conflict. Signed-off-by: James Pickens Signed-off-by: Junio C Hamano --- t/t6035-merge-dir-to-symlink.sh | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100755 t/t6035-merge-dir-to-symlink.sh diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh new file mode 100755 index 0000000000..18d5f9454b --- /dev/null +++ b/t/t6035-merge-dir-to-symlink.sh @@ -0,0 +1,87 @@ +#!/bin/sh + +test_description='merging when a directory was replaced with a symlink' +. ./test-lib.sh + +test_expect_success 'create a commit where dir a/b changed to symlink' ' + mkdir -p a/b/c a/b-2/c && + > a/b/c/d && + > a/b-2/c/d && + > a/x && + git add -A && + git commit -m base && + git tag start && + rm -rf a/b && + ln -s b-2 a/b && + git add -A && + git commit -m "dir to symlink" +' + +test_expect_failure 'keep a/b-2/c/d across checkout' ' + git checkout HEAD^0 && + git reset --hard master && + git rm --cached a/b && + git commit -m "untracked symlink remains" && + git checkout start^0 && + test -f a/b-2/c/d +' + +test_expect_failure 'checkout should not have deleted a/b-2/c/d' ' + git checkout HEAD^0 && + git reset --hard master && + git checkout start^0 && + test -f a/b-2/c/d +' + +test_expect_success 'setup for merge test' ' + git reset --hard && + test -f a/b-2/c/d && + echo x > a/x && + git add a/x && + git commit -m x && + git tag baseline +' + +test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' ' + git reset --hard && + git checkout baseline^0 && + git merge -s resolve master && + test -h a/b && + test -f a/b-2/c/d +' + +test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' ' + git reset --hard && + git checkout baseline^0 && + git merge -s recursive master && + test -h a/b && + test -f a/b-2/c/d +' + +test_expect_success 'setup a merge where dir a/b-2 changed to symlink' ' + git reset --hard && + git checkout start^0 && + rm -rf a/b-2 && + ln -s b a/b-2 && + git add -A && + git commit -m "dir a/b-2 to symlink" && + git tag test2 +' + +test_expect_failure 'merge should not have conflicts (resolve)' ' + git reset --hard && + git checkout baseline^0 && + git merge -s resolve test2 && + test -h a/b-2 && + test -f a/b/c/d +' + +test_expect_failure 'merge should not have conflicts (recursive)' ' + git reset --hard && + git checkout baseline^0 && + git merge -s recursive test2 && + test -h a/b-2 && + test -f a/b/c/d +' + +test_done -- cgit v1.2.3 From 77716755cbdf970fa0814a5f77c884b1f17693de Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Sun, 14 Jun 2009 15:08:28 +0200 Subject: lstat_cache: guard against full match of length of 'name' parameter longest_path_match() in symlinks.c does exactly what it's name says, but in some cases that match can be too long, since the has_*_leading_path() functions assumes that the match will newer be as long as the name string given to the function. fix this by adding an extra if test which checks if the match length is equal to the 'len' parameter. Signed-off-by: Junio C Hamano --- symlinks.c | 4 ++++ t/t6035-merge-dir-to-symlink.sh | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/symlinks.c b/symlinks.c index 4bdded39c5..7b0a86d357 100644 --- a/symlinks.c +++ b/symlinks.c @@ -91,6 +91,10 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len, longest_path_match(name, len, cache->path, cache->len, &previous_slash); match_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK); + + if (!(track_flags & FL_FULLPATH) && match_len == len) + match_len = last_slash = previous_slash; + if (match_flags && match_len == cache->len) return match_flags; /* diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh index 18d5f9454b..ba90fc53e0 100755 --- a/t/t6035-merge-dir-to-symlink.sh +++ b/t/t6035-merge-dir-to-symlink.sh @@ -26,7 +26,7 @@ test_expect_failure 'keep a/b-2/c/d across checkout' ' test -f a/b-2/c/d ' -test_expect_failure 'checkout should not have deleted a/b-2/c/d' ' +test_expect_success 'checkout should not have deleted a/b-2/c/d' ' git checkout HEAD^0 && git reset --hard master && git checkout start^0 && -- cgit v1.2.3 From b6986d8a75812a003a1623e0f0dff93c4a026b44 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 29 Jul 2009 20:22:25 -0700 Subject: git-checkout: be careful about untracked symlinks This fixes the case where an untracked symlink that points at a directory with tracked paths confuses the checkout logic, demostrated in t6035. Signed-off-by: Junio C Hamano --- cache.h | 3 +++ entry.c | 15 ++++++++++++++- t/t6035-merge-dir-to-symlink.sh | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/cache.h b/cache.h index e6c7f3307d..9222774e6c 100644 --- a/cache.h +++ b/cache.h @@ -468,6 +468,9 @@ extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_obje extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); +/* "careful lstat()" */ +extern int check_path(const char *path, int len, struct stat *st); + #define REFRESH_REALLY 0x0001 /* ignore_valid */ #define REFRESH_UNMERGED 0x0002 /* allow unmerged */ #define REFRESH_QUIET 0x0004 /* be quiet about it */ diff --git a/entry.c b/entry.c index d3e86c722a..f276cf3b88 100644 --- a/entry.c +++ b/entry.c @@ -175,6 +175,19 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout return 0; } +/* + * This is like 'lstat()', except it refuses to follow symlinks + * in the path. + */ +int check_path(const char *path, int len, struct stat *st) +{ + if (has_symlink_leading_path(path, len)) { + errno = ENOENT; + return -1; + } + return lstat(path, st); +} + int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath) { static char path[PATH_MAX + 1]; @@ -188,7 +201,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t strcpy(path + len, ce->name); len += ce_namelen(ce); - if (!lstat(path, &st)) { + if (!check_path(path, len, &st)) { unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); if (!changed) return 0; diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh index ba90fc53e0..a0ddf1e0f4 100755 --- a/t/t6035-merge-dir-to-symlink.sh +++ b/t/t6035-merge-dir-to-symlink.sh @@ -17,7 +17,7 @@ test_expect_success 'create a commit where dir a/b changed to symlink' ' git commit -m "dir to symlink" ' -test_expect_failure 'keep a/b-2/c/d across checkout' ' +test_expect_success 'keep a/b-2/c/d across checkout' ' git checkout HEAD^0 && git reset --hard master && git rm --cached a/b && -- cgit v1.2.3 From b6b0737d02e3bb9b3015ec66f5b6454d5b83d57b Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sun, 9 Aug 2009 17:35:02 +0200 Subject: t6035-merge-dir-to-symlink depends on SYMLINKS prerequisite Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- t/t6035-merge-dir-to-symlink.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh index a0ddf1e0f4..5b96fb0b37 100755 --- a/t/t6035-merge-dir-to-symlink.sh +++ b/t/t6035-merge-dir-to-symlink.sh @@ -3,6 +3,12 @@ test_description='merging when a directory was replaced with a symlink' . ./test-lib.sh +if ! test_have_prereq SYMLINKS +then + say 'Symbolic links not supported, skipping tests.' + test_done +fi + test_expect_success 'create a commit where dir a/b changed to symlink' ' mkdir -p a/b/c a/b-2/c && > a/b/c/d && -- cgit v1.2.3