* Before interpretting an upward path (../) in cd_to_toplevel, cd to a path without symlinks given by /bin/pwd * Add tests for cd_to_toplevel and "git pull" in a symlinked directory that failed before this fix, plus constrasting scenarios that already worked Signed-off-by: Marcel M. Cary <marcel@xxxxxxxxxxxxxxxx> --- I hope this patch will address concerns both about changes to existing APIs and speed of the new behavior. A few notes on implementation choices: I used /bin/pwd because of this precedent for choosing it over "cd -P" for compatibility. http://article.gmane.org/gmane.comp.version-control.git/46918 If cd_to_toplevel had concatenated $(/bin/pwd) with $cdup to avoid the separate "cd", it would require checking for $cdup being an absolute path. I wasn't sure how to check that in a way that is both portable and clearly faster than "cd", so cd_to_toplevel runs "cd" twice. I'm assuming that running an external command like expr or grep is slower than just doing the "cd". cd_to_toplevel doesn't check $PWD to see whether to do the first cd, because some shells allegedly don't update it reliably. Since cd_to_toplevel doesn't know whether it's at a symlinked PWD or not, I wrote it to treat the "cd $(/bin/pwd)" as mandatory, even when it might not actually be. So on systems without /bin/pwd, it will fail even when there are no symlinks. I thought that was better than inconsistent behavior depending on whether /bin/pwd is available. The extra "cd" will be skipped when the script is already at the top of the working tree. git-sh-setup.sh | 11 +++++++ t/t2300-cd-to-toplevel.sh | 37 +++++++++++++++++++++++++ t/t5521-pull-symlink.sh | 67 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 0 deletions(-) create mode 100755 t/t2300-cd-to-toplevel.sh create mode 100755 t/t5521-pull-symlink.sh diff --git a/git-sh-setup.sh b/git-sh-setup.sh index dbdf209..377700b 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -85,6 +85,17 @@ cd_to_toplevel () { cdup=$(git rev-parse --show-cdup) if test ! -z "$cdup" then + # Interpret $cdup relative to the physical, not logical, cwd. + # Probably /bin/pwd is more portable than passing -P to cd or pwd. + phys="$(/bin/pwd)" || { + echo >&2 "Cannot determine the physical path to the current dir" + exit 1 + } + cd "$phys" || { + echo >&2 "Cannot chdir to the physical path to current dir: $phys" + exit 1 + } + cd "$cdup" || { echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree" exit 1 diff --git a/t/t2300-cd-to-toplevel.sh b/t/t2300-cd-to-toplevel.sh new file mode 100755 index 0000000..293dc35 --- /dev/null +++ b/t/t2300-cd-to-toplevel.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +test_description='cd_to_toplevel' + +. ./test-lib.sh + +test_cd_to_toplevel () { + test_expect_success "$2" ' + ( + cd '"'$1'"' && + . git-sh-setup && + cd_to_toplevel && + [ "$(pwd -P)" = "$TOPLEVEL" ] + ) + ' +} + +TOPLEVEL="$(pwd -P)/repo" +mkdir -p repo/sub/dir +mv .git repo/ +SUBDIRECTORY_OK=1 + +test_cd_to_toplevel repo 'at physical root' + +test_cd_to_toplevel repo/sub/dir 'at physical subdir' + +ln -s repo symrepo +test_cd_to_toplevel symrepo 'at symbolic root' + +ln -s repo/sub/dir subdir-link +test_cd_to_toplevel subdir-link 'at symbolic subdir' + +cd repo +ln -s sub/dir internal-link +test_cd_to_toplevel internal-link 'at internal symbolic subdir' + +test_done diff --git a/t/t5521-pull-symlink.sh b/t/t5521-pull-symlink.sh new file mode 100755 index 0000000..f18fec7 --- /dev/null +++ b/t/t5521-pull-symlink.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +test_description='pulling from symlinked subdir' + +. ./test-lib.sh + +D=`pwd` + +# The scenario we are building: +# +# trash\ directory/ +# clone-repo/ +# subdir/ +# bar +# subdir-link -> clone-repo/subdir/ +# +# The working directory is subdir-link. +# +test_expect_success setup ' + + mkdir subdir && + touch subdir/bar && + git add subdir/bar && + git commit -m empty && + git clone . clone-repo && + # demonstrate that things work without the symlink + test_debug "cd clone-repo/subdir/ && git pull; cd ../.." && + ln -s clone-repo/subdir/ subdir-link && + cd subdir-link/ && + test_debug "set +x" +' + +# From subdir-link, pulling should work as it does from +# clone-repo/subdir/. +# +# Instead, the error pull gave was: +# +# fatal: 'origin': unable to chdir or not a git archive +# fatal: The remote end hung up unexpectedly +# +# because git would find the .git/config for the "trash directory" +# repo, not for the clone-repo repo. The "trash directory" repo +# had no entry for origin. Git found the wrong .git because +# git rev-parse --show-cdup printed a path relative to +# clone-repo/subdir/, not subdir-link/. Git rev-parse --show-cdup +# used the correct .git, but when the git pull shell script did +# "cd `git rev-parse --show-cdup`", it ended up in the wrong +# directory. Shell "cd" works a little different from chdir() in C. +# Bash's "cd -P" works like chdir() in C. +# +test_expect_success 'pulling from symlinked subdir' ' + + git pull +' + +# Prove that the remote end really is a repo, and other commands +# work fine in this context. +# +test_debug " + test_expect_success 'pushing from symlinked subdir' ' + + git push + ' +" +cd "$D" + +test_done -- 1.6.0.3 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html