From 75b364dfe28cf1700ac4f519ff7d3e42505e17a6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 9 Jan 2007 17:37:50 -0800 Subject: [PATCH 1/2] git-checkout: safety check for detached HEAD checks existing refs Checking for reachability from refs does not help much if the state we are currently on is somewhere in the middle. We will lose where we were. So this makes sureh that HEAD is something directly pointed at by one of the existing refs (most likely a tag for a user who has been "sightseeing"). Signed-off-by: Junio C Hamano --- git-checkout.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/git-checkout.sh b/git-checkout.sh index a309bf0a1a..dcf6ddbdf1 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -171,14 +171,14 @@ then # Coming back... if test -z "$force" then - mb=$(git merge-base --all $old $new) && - case "$LF$mb$LF" in - *"$LF$old$LF"*) : ;; - *) false ;; - esac || { + git show-ref -d -s | grep "$old" >/dev/null || { echo >&2 \ -"You are not on a branch and switching to $new_name branch may lose -your changes. Use 'git checkout -f $new_name' if you want to." +"You are not on any branch and switching to branch '$new_name' +may lose your changes. At this point, you can do one of two things: + (1) Decide it is Ok and say 'git checkout -f $new_name'; + (2) Start a new branch from the current commit, by saying + 'git checkout -b '. +Leaving your HEAD detached; not switching to branch '$new_name'." exit 1; } fi From bfbbb8f8cf8250718ffd028efe179557d29ae72d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 9 Jan 2007 20:39:09 -0800 Subject: [PATCH 2/2] git-checkout: handle local changes sanely when detaching HEAD When switching branches, we usually first try read-tree to make sure that we do not lose the local changes and then updated the HEAD using update-ref. However, we detached and updated HEAD before these checks, which was quite bad in a repository with local changes. Signed-off-by: Junio C Hamano --- git-checkout.sh | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/git-checkout.sh b/git-checkout.sh index dcf6ddbdf1..8f4356d49a 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -150,21 +150,18 @@ fi # correct, it feels somewhat funny. More importantly, we do not # want "git checkout" nor "git checkout -f" to detach HEAD. +detached= +detach_warn= + if test -z "$branch$newbranch" && test "$new" != "$old" then - # NEEDSWORK: we would want to have a command here - # that allows us to detach the HEAD atomically. Perhaps - # something like "git update-ref --detach HEAD $new" - echo "$new" >"$GIT_DIR/HEAD.new" && - mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" || die "Cannot detach HEAD" - + detached="$new" if test -n "$oldbranch" then - echo >&2 "warning: you are not on ANY branch anymore. + detach_warn="warning: you are not on ANY branch anymore. If you meant to create a new branch from the commit, you need -b to associate a new branch with the wanted checkout. Example: - git checkout -b $arg -" + git checkout -b $arg" fi elif test -z "$oldbranch" && test -n "$branch" then @@ -258,8 +255,25 @@ if [ "$?" -eq 0 ]; then git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit branch="$newbranch" fi - [ "$branch" ] && - GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch" + if test -n "$branch" + then + GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch" + elif test -n "$detached" + then + # NEEDSWORK: we would want a command to detach the HEAD + # atomically, instead of this handcrafted command sequence. + # Perhaps: + # git update-ref --detach HEAD $new + # or something like that... + # + echo "$detached" >"$GIT_DIR/HEAD.new" && + mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" || + die "Cannot detach HEAD" + if test -n "$detach_warn" + then + echo >&2 "$detach_warn" + fi + fi rm -f "$GIT_DIR/MERGE_HEAD" else exit 1