git-clone: do not forget to create origin branch.
[gitweb.git] / git-checkout.sh
index 2c053a33c3c78f27150cdd397ad28641787abb3a..cb33fdc7e20272a77d97d1b68c31d9b93d3cf4d4 100755 (executable)
@@ -1,6 +1,10 @@
 #!/bin/sh
 . git-sh-setup || die "Not a git archive"
 
+usage () {
+    die "usage: git checkout [-f] [-b <new_branch>] [<branch>] [<paths>...]"
+}
+
 old=$(git-rev-parse HEAD)
 new=
 force=
@@ -23,32 +27,84 @@ while [ "$#" != "0" ]; do
        "-f")
                force=1
                ;;
+       --)
+               break
+               ;;
+       -*)
+               usage
+               ;;
        *)
-               rev=$(git-rev-parse --verify "$arg^0" 2>/dev/null) ||
-                       die "I don't know any '$arg'."
-               if [ -z "$rev" ]; then
-                       echo "unknown flag $arg"
-                       exit 1
-               fi
-               if [ "$new" ]; then
-                       echo "Multiple revisions?"
-                       exit 1
-               fi
-               new="$rev"
-               if [ -f "$GIT_DIR/refs/heads/$arg" ]; then
-                       branch="$arg"
+               if rev=$(git-rev-parse --verify "$arg^0" 2>/dev/null)
+               then
+                       if [ -z "$rev" ]; then
+                               echo "unknown flag $arg"
+                               exit 1
+                       fi
+                       new="$rev"
+                       if [ -f "$GIT_DIR/refs/heads/$arg" ]; then
+                               branch="$arg"
+                       fi
+               elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null)
+               then
+                       # checking out selected paths from a tree-ish.
+                       new="$rev"
+                       branch=
+               else
+                       new=
+                       branch=
+                       set x "$arg" "$@"
+                       shift
                fi
+               break
                ;;
     esac
 done
-[ -z "$new" ] && new=$old
 
+# The behaviour of the command with and without explicit path
+# parameters is quite different.
+#
+# Without paths, we are checking out everything in the work tree,
+# possibly switching branches.  This is the traditional behaviour.
 #
+# With paths, we are _never_ switching branch, but checking out
+# the named paths from either index (when no rev is given),
+# or the named tree-ish (when rev is given).
+
+if test "$#" -ge 1
+then
+       if test '' != "$newbranch$force"
+       then
+               die "updating paths and switching branches or forcing are incompatible."
+       fi
+       if test '' != "$new"
+       then
+               # from a specific tree-ish; note that this is for
+               # rescuing paths and is never meant to remove what
+               # is not in the named tree-ish.
+               git-ls-tree -r "$new" "$@" |
+               sed -ne 's/^\([0-7]*\) blob \(.*\)$/\1 \2/p' |
+               git-update-index --index-info || exit $?
+       fi
+       git-checkout-index -f -u -- "$@"
+       exit $?
+else
+       # Make sure we did not fall back on $arg^{tree} codepath
+       # since we are not checking out from an arbitrary tree-ish,
+       # but switching branches.
+       if test '' != "$new"
+       then
+               git-rev-parse --verify "$new^{commit}" >/dev/null 2>&1 ||
+               die "Cannot switch branch to a non-commit."
+       fi
+fi
+
+[ -z "$new" ] && new=$old
+
 # If we don't have an old 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
-#
+
 [ -z "$branch$newbranch" ] &&
        [ "$new" != "$old" ] &&
        die "git checkout: you need to specify a new branch name"