X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=git-checkout.sh;h=14835a4aa982af88d758ea014afacf7b2fb1bb1e;hb=d58c6184e345b9c8c8bfe8cc3eb1bbfe2f5ee4f9;hp=dd477245fb3703402b950cbc1378b35ce379d631;hpb=dd0c367e5e1358539bdd11c3a474bb6792504d30;p=git.git diff --git a/git-checkout.sh b/git-checkout.sh index dd477245f..14835a4aa 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -1,11 +1,13 @@ #!/bin/sh -USAGE='[-f] [-b ] [-m] [] [...]' +USAGE='[-q] [-f] [-b ] [-m] [] [...]' SUBDIRECTORY_OK=Sometimes . git-sh-setup +require_work_tree old_name=HEAD old=$(git-rev-parse --verify $old_name 2>/dev/null) +oldbranch=$(git-symbolic-ref $old_name 2>/dev/null) new= new_name= force= @@ -13,6 +15,9 @@ branch= newbranch= newbranch_log= merge= +quiet= +LF=' +' while [ "$#" != "0" ]; do arg="$1" shift @@ -22,7 +27,7 @@ while [ "$#" != "0" ]; do shift [ -z "$newbranch" ] && die "git checkout: -b needs a branch name" - [ -e "$GIT_DIR/refs/heads/$newbranch" ] && + git-show-ref --verify --quiet -- "refs/heads/$newbranch" && die "git checkout: branch $newbranch already exists" git-check-ref-format "heads/$newbranch" || die "git checkout: we do not like '$newbranch' as a branch name." @@ -36,6 +41,9 @@ while [ "$#" != "0" ]; do -m) merge=1 ;; + "-q") + quiet=1 + ;; --) break ;; @@ -50,8 +58,9 @@ while [ "$#" != "0" ]; do exit 1 fi new="$rev" - new_name="$arg^0" - if [ -f "$GIT_DIR/refs/heads/$arg" ]; then + new_name="$arg" + if git-show-ref --verify --quiet -- "refs/heads/$arg" + then branch="$arg" fi elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null) @@ -76,6 +85,11 @@ while [ "$#" != "0" ]; do esac done +case "$force$merge" in +11) + die "git checkout: -f and -m are incompatible" +esac + # The behaviour of the command with and without explicit path # parameters is quite different. # @@ -106,7 +120,11 @@ Did you intend to checkout '$@' which can not be resolved as commit?" git-ls-tree --full-name -r "$new" "$@" | git-update-index --index-info || exit $? fi - git-checkout-index -f -u -- "$@" + + # Make sure the request is about existing paths. + git-ls-files --error-unmatch -- "$@" >/dev/null || exit + git-ls-files -- "$@" | + git-checkout-index -f -u --stdin exit $? else # Make sure we did not fall back on $arg^{tree} codepath @@ -121,28 +139,43 @@ fi # We are switching branches and checking out trees, so # we *NEED* to be at the toplevel. -cdup=$(git-rev-parse --show-cdup) -if test ! -z "$cdup" -then - cd "$cdup" -fi +cd_to_toplevel [ -z "$new" ] && new=$old && new_name="$old_name" -# If we don't have an old branch that we're switching to, +# If we don't have an existing branch that we're switching to, # and we don't have a new branch name for the target we -# are switching to, then we'd better just be checking out -# what we already had +# are switching to, then we are detaching our HEAD from any +# branch. However, if "git checkout HEAD" detaches the HEAD +# from the current branch, even though that may be logically +# correct, it feels somewhat funny. More importantly, we do not +# want "git checkout" nor "git checkout -f" to detach HEAD. + +detached= +detach_warn= -[ -z "$branch$newbranch" ] && - [ "$new" != "$old" ] && - die "git checkout: to checkout the requested commit you need to specify - a name for a new branch which is created and switched to" +if test -z "$branch$newbranch" && test "$new" != "$old" +then + detached="$new" + if test -n "$oldbranch" && test -z "$quiet" + then + detach_warn="Note: moving to \"$new_name\" which isn't a local branch +If you want to create a new branch from this checkout, you may do so +(now or later) by using -b with the checkout command again. Example: + git checkout -b " + fi +elif test -z "$oldbranch" && test -z "$quiet" +then + echo >&2 "Previous HEAD position was $old" +fi if [ "X$old" = X ] then - echo "warning: You do not appear to currently be on a branch." >&2 - echo "warning: Forcing checkout of $new_name." >&2 + if test -z "$quiet" + then + echo >&2 "warning: You appear to be on a branch yet to be born." + echo >&2 "warning: Forcing checkout of $new_name." + fi force=1 fi @@ -151,7 +184,7 @@ then git-read-tree --reset -u $new else git-update-index --refresh >/dev/null - merge_error=$(git-read-tree -m -u $old $new 2>&1) || ( + merge_error=$(git-read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || ( case "$merge" in '') echo >&2 "$merge_error" @@ -161,15 +194,12 @@ else # Match the index to the working tree, and do a three-way. git diff-files --name-only | git update-index --remove --stdin && work=`git write-tree` && - git read-tree --reset -u $new && - git read-tree -m -u --aggressive $old $new $work || exit + git read-tree --reset -u $new || exit - if result=`git write-tree 2>/dev/null` - then - echo >&2 "Trivially automerged." - else - git merge-index -o git-merge-one-file -a - fi + eval GITHEAD_$new=${new_name:-${branch:-$new}} && + eval GITHEAD_$work=local && + export GITHEAD_$new GITHEAD_$work && + git merge-recursive $old -- $new $work # Do not register the cleanly merged paths in the index yet. # this is not a real merge before committing, but just carrying @@ -190,9 +220,9 @@ else exit 0 ) saved_err=$? - if test "$saved_err" = 0 + if test "$saved_err" = 0 && test -z "$quiet" then - test "$new" = "$old" || git diff-index --name-status "$new" + git diff-index --name-status "$new" fi (exit $saved_err) fi @@ -212,8 +242,30 @@ 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 -m "checkout: moving to $branch" HEAD "refs/heads/$branch" + if test -z "$quiet" + then + echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\"" + fi + 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... + # + git-rev-parse HEAD >"$GIT_DIR/HEAD.new" && + mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" && + git-update-ref -m "checkout: moving to $arg" HEAD "$detached" || + 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