Merge branch 'ph/parseopt-sh'
authorJunio C Hamano <gitster@pobox.com>
Sun, 18 Nov 2007 05:39:37 +0000 (21:39 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 18 Nov 2007 05:39:37 +0000 (21:39 -0800)
* ph/parseopt-sh:
git-quiltimport.sh fix --patches handling
git-am: -i does not take a string parameter.
sh-setup: don't let eval output to be shell-expanded.
git-sh-setup: fix parseopt `eval` string underquoting
Give git-am back the ability to add Signed-off-by lines.
git-rev-parse --parseopt
scripts: Add placeholders for OPTIONS_SPEC
Migrate git-repack.sh to use git-rev-parse --parseopt
Migrate git-quiltimport.sh to use git-rev-parse --parseopt
Migrate git-checkout.sh to use git-rev-parse --parseopt --keep-dashdash
Migrate git-instaweb.sh to use git-rev-parse --parseopt
Migrate git-merge.sh to use git-rev-parse --parseopt
Migrate git-am.sh to use git-rev-parse --parseopt
Migrate git-clone to use git-rev-parse --parseopt
Migrate git-clean.sh to use git-rev-parse --parseopt.
Update git-sh-setup(1) to allow transparent use of git-rev-parse --parseopt
Add a parseopt mode to git-rev-parse to bring parse-options to shell scripts.

12 files changed:
1  2 
git-bisect.sh
git-clean.sh
git-commit.sh
git-instaweb.sh
git-lost-found.sh
git-merge.sh
git-rebase--interactive.sh
git-rebase.sh
git-request-pull.sh
git-stash.sh
git-submodule.sh
git.c
diff --combined git-bisect.sh
index 1ed44e56ad5021a74b2a0ec3e91849e55d4a67ce,c18bd32bf430c4cde07777e160cc3d4c81ee8999..3aac8164c62c1f4af8df76832322df9f25a05b38
@@@ -22,6 -22,7 +22,7 @@@ git bisect lo
  git bisect run <cmd>...
          use <cmd>... to automatically bisect.'
  
+ OPTIONS_SPEC=
  . git-sh-setup
  require_work_tree
  
@@@ -275,8 -276,7 +276,8 @@@ exit_if_skipped_commits () 
        if expr "$_tried" : ".*[|].*" > /dev/null ; then
                echo "There are only 'skip'ped commit left to test."
                echo "The first bad commit could be any of:"
 -              echo "$_tried" | sed -e 's/[|]/\n/g'
 +              echo "$_tried" | sed -e 's/[|]/\
 +/g'
                echo "We cannot bisect more!"
                exit 2
        fi
diff --combined git-clean.sh
index ad68595fbf313c1cab7de565e6d44f2c87aff384,35a5142c56f8d6f9007fcde840bf0d90b1d02a3a..01c95e9fe8a19afcf331ed5ffd47eea478886213
@@@ -3,16 -3,22 +3,22 @@@
  # Copyright (c) 2005-2006 Pavel Roskin
  #
  
- USAGE="[-d] [-f] [-n] [-q] [-x | -X] [--] <paths>..."
- LONG_USAGE='Clean untracked files from the working directory
-       -d      remove directories as well
-       -f      override clean.requireForce and clean anyway
-       -n      don'\''t remove anything, just show what would be done
-       -q      be quiet, only report errors
-       -x      remove ignored files as well
-       -X      remove only ignored files
+ OPTIONS_KEEPDASHDASH=
+ OPTIONS_SPEC="\
+ git-clean [options] <paths>...
+ Clean untracked files from the working directory
  When optional <paths>... arguments are given, the paths
- affected are further limited to those that match them.'
+ affected are further limited to those that match them.
+ --
+ d remove directories as well
+ f override clean.requireForce and clean anyway
+ n don't remove anything, just show what would be done
+ q be quiet, only report errors
+ x remove ignored files as well
+ X remove only ignored files"
  SUBDIRECTORY_OK=Yes
  . git-sh-setup
  require_work_tree
  ignored=
  ignoredonly=
  cleandir=
 -disabled="`git config --bool clean.requireForce`"
  rmf="rm -f --"
  rmrf="rm -rf --"
  rm_refuse="echo Not removing"
  echo1="echo"
  
 +disabled=$(git config --bool clean.requireForce)
 +
  while test $# != 0
  do
        case "$1" in
                cleandir=1
                ;;
        -f)
 -              disabled=
 +              disabled=false
                ;;
        -n)
 -              disabled=
 +              disabled=false
                rmf="echo Would remove"
                rmrf="echo Would remove"
                rm_refuse="echo Would not remove"
                shift
                break
                ;;
-       -*)
-               usage
-               ;;
        *)
-               break
+               usage # should not happen
+               ;;
        esac
        shift
  done
  
 -if [ "$disabled" = true ]; then
 +# requireForce used to default to false but now it defaults to true.
 +# IOW, lack of explicit "clean.requireForce = false" is taken as
 +# "clean.requireForce = true".
 +case "$disabled" in
 +"")
 +      die "clean.requireForce not set and -n or -f not given; refusing to clean"
 +      ;;
 +"true")
        die "clean.requireForce set and -n or -f not given; refusing to clean"
 -fi
 +      ;;
 +esac
  
case "$ignored,$ignoredonly" in
-       1,1) usage;;
- esac
if [ "$ignored,$ignoredonly" = "1,1" ]; then
+       die "-x and -X cannot be set together"
+ fi
  
  if [ -z "$ignored" ]; then
        excl="--exclude-per-directory=.gitignore"
 +      excl_info= excludes_file=
        if [ -f "$GIT_DIR/info/exclude" ]; then
                excl_info="--exclude-from=$GIT_DIR/info/exclude"
        fi
 +      if cfg_excl=$(git config core.excludesfile) && test -f "$cfg_excl"
 +      then
 +              excludes_file="--exclude-from=$cfg_excl"
 +      fi
        if [ "$ignoredonly" ]; then
                excl="$excl --ignored"
        fi
  fi
  
 -git ls-files --others --directory $excl ${excl_info:+"$excl_info"} -- "$@" |
 +git ls-files --others --directory \
 +      $excl ${excl_info:+"$excl_info"} ${excludes_file:+"$excludes_file"} \
 +      -- "$@" |
  while read -r file; do
        if [ -d "$file" -a ! -L "$file" ]; then
                if [ -z "$cleandir" ]; then
diff --combined git-commit.sh
index 959c4d68e7bbb501dee1778a33d45eaf09b4bf2d,9a636e55ccb41cb1eba6cde7a54fd33d9d735d94..485339754ca3567c86b824af656700654c68e173
@@@ -5,6 -5,7 +5,7 @@@
  
  USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]'
  SUBDIRECTORY_OK=Yes
+ OPTIONS_SPEC=
  . git-sh-setup
  require_work_tree
  
@@@ -26,7 -27,7 +27,7 @@@ refuse_partial () 
  }
  
  TMP_INDEX=
 -THIS_INDEX="$GIT_DIR/index"
 +THIS_INDEX="${GIT_INDEX_FILE:-$GIT_DIR/index}"
  NEXT_INDEX="$GIT_DIR/next-index$$"
  rm -f "$NEXT_INDEX"
  save_index () {
@@@ -282,9 -283,9 +283,9 @@@ unset onl
  case "$all,$interactive,$also,$#" in
  *t,*t,*)
        die "Cannot use -a, --interactive or -i at the same time." ;;
 -t,,[1-9]*)
 +t,,,[1-9]*)
        die "Paths with -a does not make sense." ;;
 -,t,[1-9]*)
 +,t,,[1-9]*)
        die "Paths with --interactive does not make sense." ;;
  ,,t,0)
        die "No paths with -i does not make sense." ;;
diff --combined git-instaweb.sh
index ada1180528b08607858efbcce9b2afc6d1b0e061,f05884c1fab547ab7db5dddc7c399ef90916f3ed..8503ae40309e6c8b1547289c9950d99f4bf736b5
@@@ -2,9 -2,21 +2,21 @@@
  #
  # Copyright (c) 2006 Eric Wong
  #
- USAGE='[--start] [--stop] [--restart]
-   [--local] [--httpd=<httpd>] [--port=<port>] [--browser=<browser>]
-   [--module-path=<path> (for Apache2 only)]'
+ OPTIONS_KEEPDASHDASH=
+ OPTIONS_SPEC="\
+ git-instaweb [options] (--start | --stop | --restart)
+ --
+ l,local        only bind on 127.0.0.1
+ p,port=        the port to bind to
+ d,httpd=       the command to launch
+ b,browser=     the browser to launch
+ m,module-path= the module path (only needed for apache2)
+  Action
+ stop           stop the web server
+ start          start the web server
+ restart        restart the web server
+ "
  
  . git-sh-setup
  
@@@ -15,7 -27,7 +27,7 @@@ browser="`git config --get instaweb.bro
  port=`git config --get instaweb.port`
  module_path="`git config --get instaweb.modulepath`"
  
 -conf=$GIT_DIR/gitweb/httpd.conf
 +conf="$GIT_DIR/gitweb/httpd.conf"
  
  # Defaults:
  
@@@ -32,7 -44,7 +44,7 @@@ start_httpd () 
        httpd_only="`echo $httpd | cut -f1 -d' '`"
        if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac
        then
 -              $httpd $fqgitdir/gitweb/httpd.conf
 +              $httpd "$fqgitdir/gitweb/httpd.conf"
        else
                # many httpds are installed in /usr/sbin or /usr/local/sbin
                # these days and those are not in most users $PATHs
@@@ -78,52 -90,26 +90,26 @@@ d
                start_httpd
                exit 0
                ;;
-       --local|-l)
+       -l|--local)
                local=true
                ;;
-       -d|--httpd|--httpd=*)
-               case "$#,$1" in
-               *,*=*)
-                       httpd=`expr "$1" : '-[^=]*=\(.*\)'` ;;
-               1,*)
-                       usage ;;
-               *)
-                       httpd="$2"
-                       shift ;;
-               esac
+       -d|--httpd)
+               shift
+               httpd="$1"
+               ;;
+       -b|--browser)
+               shift
+               browser="$1"
                ;;
-       -b|--browser|--browser=*)
-               case "$#,$1" in
-               *,*=*)
-                       browser=`expr "$1" : '-[^=]*=\(.*\)'` ;;
-               1,*)
-                       usage ;;
-               *)
-                       browser="$2"
-                       shift ;;
-               esac
+       -p|--port)
+               shift
+               port="$1"
                ;;
-       -p|--port|--port=*)
-               case "$#,$1" in
-               *,*=*)
-                       port=`expr "$1" : '-[^=]*=\(.*\)'` ;;
-               1,*)
-                       usage ;;
-               *)
-                       port="$2"
-                       shift ;;
-               esac
+       -m|--module-path)
+               shift
+               module_path="$1"
                ;;
-       -m|--module-path=*|--module-path)
-               case "$#,$1" in
-               *,*=*)
-                       module_path=`expr "$1" : '-[^=]*=\(.*\)'` ;;
-               1,*)
-                       usage ;;
-               *)
-                       module_path="$2"
-                       shift ;;
-               esac
+       --)
                ;;
        *)
                usage
@@@ -185,14 -171,14 +171,14 @@@ server.pid-file = "$fqgitdir/pid
  cgi.assign = ( ".cgi" => "" )
  mimetype.assign = ( ".css" => "text/css" )
  EOF
 -      test "$local" = true && echo 'server.bind = "127.0.0.1"' >> "$conf"
 +      test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf"
  }
  
  apache2_conf () {
        test -z "$module_path" && module_path=/usr/lib/apache2/modules
        mkdir -p "$GIT_DIR/gitweb/logs"
        bind=
 -      test "$local" = true && bind='127.0.0.1:'
 +      test x"$local" = xtrue && bind='127.0.0.1:'
        echo 'text/css css' > $fqgitdir/mime.types
        cat > "$conf" <<EOF
  ServerName "git-instaweb"
@@@ -245,7 -231,7 +231,7 @@@ EO
  }
  
  script='
 -s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "'`dirname $fqgitdir`'";#
 +s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "'$(dirname "$fqgitdir")'";#
  s#\(my\|our\) $gitbin =.*#\1 $gitbin = "'$GIT_EXEC_PATH'";#
  s#\(my\|our\) $projects_list =.*#\1 $projects_list = $projectroot;#
  s#\(my\|our\) $git_temp =.*#\1 $git_temp = "'$fqgitdir/gitweb/tmp'";#'
@@@ -265,8 -251,8 +251,8 @@@ gitweb_css () 
  EOFGITWEB
  }
  
 -gitweb_cgi $GIT_DIR/gitweb/gitweb.cgi
 -gitweb_css $GIT_DIR/gitweb/gitweb.css
 +gitweb_cgi "$GIT_DIR/gitweb/gitweb.cgi"
 +gitweb_css "$GIT_DIR/gitweb/gitweb.css"
  
  case "$httpd" in
  *lighttpd*)
@@@ -285,5 -271,6 +271,5 @@@ webrick
  esac
  
  start_httpd
 -test -z "$browser" && browser=echo
  url=http://127.0.0.1:$port
 -$browser $url || echo $url
 +"$browser" $url || echo $url
diff --combined git-lost-found.sh
index f2ec5d147a56136b8c3aff8b693912a7c792da63,a5a32e7c70f33369a016dd18ebb072c90b3a40f7..9cedaf80ceac1d4100adf3cfb152c76c7f945e4d
@@@ -2,10 -2,9 +2,11 @@@
  
  USAGE=''
  SUBDIRECTORY_OK='Yes'
+ OPTIONS_SPEC=
  . git-sh-setup
  
 +echo "WARNING: '$0' is deprecated in favor of 'git fsck --lost-found'" >&2
 +
  if [ "$#" != "0" ]
  then
      usage
diff --combined git-merge.sh
index b9f05192d1122df13f134b361887060ea042180c,173d2325ed3fd8cea6ee47f6510e377d6cd4e3e2..1c123a37e6941b6727c6101bb29cbc485a380c20
@@@ -3,7 -3,19 +3,19 @@@
  # Copyright (c) 2005 Junio C Hamano
  #
  
- USAGE='[-n] [--summary] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s <strategy>] [-m=<merge-message>] <commit>+'
+ OPTIONS_KEEPDASHDASH=
+ OPTIONS_SPEC="\
+ git-merge [options] <remote>...
+ git-merge [options] <msg> HEAD <remote>
+ --
+ summary              show a diffstat at the end of the merge
+ n,no-summary         don't show a diffstat at the end of the merge
+ squash               create a single commit instead of doing a merge
+ commit               perform a commit if the merge sucesses (default)
+ ff                   allow fast forward (default)
+ s,strategy=          merge strategy to use
+ m,message=           message to be used for the merge commit (if any)
+ "
  
  SUBDIRECTORY_OK=Yes
  . git-sh-setup
@@@ -28,19 -40,20 +40,19 @@@ allow_trivial_merge=
  
  dropsave() {
        rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
 -               "$GIT_DIR/MERGE_SAVE" || exit 1
 +               "$GIT_DIR/MERGE_STASH" || exit 1
  }
  
  savestate() {
        # Stash away any local modifications.
 -      git diff-index -z --name-only $head |
 -      cpio -0 -o >"$GIT_DIR/MERGE_SAVE"
 +      git stash create >"$GIT_DIR/MERGE_STASH"
  }
  
  restorestate() {
 -        if test -f "$GIT_DIR/MERGE_SAVE"
 +        if test -f "$GIT_DIR/MERGE_STASH"
        then
                git reset --hard $head >/dev/null
 -              cpio -iuv <"$GIT_DIR/MERGE_SAVE"
 +              git stash apply $(cat "$GIT_DIR/MERGE_STASH")
                git update-index --refresh >/dev/null
        fi
  }
@@@ -132,72 -145,47 +144,47 @@@ merge_name () 
        fi
  }
  
- parse_option () {
-       case "$1" in
-       -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
-               --no-summa|--no-summar|--no-summary)
-               show_diffstat=false ;;
-       --summary)
-               show_diffstat=t ;;
-       --sq|--squ|--squa|--squas|--squash)
-               allow_fast_forward=t squash=t no_commit=t ;;
-       --no-sq|--no-squ|--no-squa|--no-squas|--no-squash)
-               allow_fast_forward=t squash= no_commit= ;;
-       --c|--co|--com|--comm|--commi|--commit)
-               allow_fast_forward=t squash= no_commit= ;;
-       --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
-               allow_fast_forward=t squash= no_commit=t ;;
-       --ff)
-               allow_fast_forward=t squash= no_commit= ;;
-       --no-ff)
-               allow_fast_forward=false squash= no_commit= ;;
-       -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
-               --strateg=*|--strategy=*|\
-       -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
-               case "$#,$1" in
-               *,*=*)
-                       strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;;
-               1,*)
-                       usage ;;
-               *)
-                       strategy="$2"
-                       shift ;;
-               esac
-               case " $all_strategies " in
-               *" $strategy "*)
-                       use_strategies="$use_strategies$strategy " ;;
-               *)
-                       die "available strategies are: $all_strategies" ;;
-               esac
-               ;;
-       -m=*|--m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
-               merge_msg=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-               have_message=t
-               ;;
-       -m|--m|--me|--mes|--mess|--messa|--messag|--message)
-               shift
-               case "$#" in
-               1)      usage ;;
-               esac
-               merge_msg="$1"
-               have_message=t
-               ;;
-       -*)     usage ;;
-       *)      return 1 ;;
-       esac
-       shift
-       args_left=$#
- }
  parse_config () {
-       while test $# -gt 0
-       do
-               parse_option "$@" || usage
-               while test $args_left -lt $#
-               do
+       while test $# != 0; do
+               case "$1" in
+               -n|--no-summary)
+                       show_diffstat=false ;;
+               --summary)
+                       show_diffstat=t ;;
+               --squash)
+                       allow_fast_forward=t squash=t no_commit=t ;;
+               --no-squash)
+                       allow_fast_forward=t squash= no_commit= ;;
+               --commit)
+                       allow_fast_forward=t squash= no_commit= ;;
+               --no-commit)
+                       allow_fast_forward=t squash= no_commit=t ;;
+               --ff)
+                       allow_fast_forward=t squash= no_commit= ;;
+               --no-ff)
+                       allow_fast_forward=false squash= no_commit= ;;
+               -s|--strategy)
+                       shift
+                       case " $all_strategies " in
+                       *" $1 "*)
+                               use_strategies="$use_strategies$1 " ;;
+                       *)
+                               die "available strategies are: $all_strategies" ;;
+                       esac
+                       ;;
+               -m|--message)
+                       shift
+                       merge_msg="$1"
+                       have_message=t
+                       ;;
+               --)
                        shift
-               done
+                       break ;;
+               *)      usage ;;
+               esac
+               shift
        done
+       args_left=$#
  }
  
  test $# != 0 || usage
@@@ -209,17 -197,12 +196,12 @@@ the
        mergeopts=$(git config "branch.${branch#refs/heads/}.mergeoptions")
        if test -n "$mergeopts"
        then
-               parse_config $mergeopts
+               parse_config $mergeopts --
        fi
  fi
  
- while parse_option "$@"
- do
-       while test $args_left -lt $#
-       do
-               shift
-       done
- done
+ parse_config "$@"
+ while test $args_left -lt $#; do shift; done
  
  if test -z "$show_diffstat"; then
      test "$(git config --bool merge.diffstat)" = false && show_diffstat=false
@@@ -436,7 -419,7 +418,7 @@@ case "$use_strategies" i
      single_strategy=no
      ;;
  *)
 -    rm -f "$GIT_DIR/MERGE_SAVE"
 +    rm -f "$GIT_DIR/MERGE_STASH"
      single_strategy=yes
      ;;
  esac
index 51063776d2540ed4ad6537e3578916b40d46c81a,6d140928890fccc0b2ac8fd640a236ffbad21793..66c80d4e16e3217b93b9de12a4b02d107a2eeb71
@@@ -13,6 -13,7 +13,7 @@@
  USAGE='(--continue | --abort | --skip | [--preserve-merges] [--verbose]
        [--onto <branch>] <upstream> [<branch>])'
  
+ OPTIONS_SPEC=
  . git-sh-setup
  require_work_tree
  
@@@ -391,7 -392,7 +392,7 @@@ d
        -s|--strategy)
                case "$#,$1" in
                *,*=*)
 -                      STRATEGY="-s `expr "z$1" : 'z-[^=]*=\(.*\)'`" ;;
 +                      STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
                1,*)
                        usage ;;
                *)
diff --combined git-rebase.sh
index c059749bbd5bc1e5bf4103c09b4935c46c965431,e3ad7dedc4c27c505ae1278ed9e108d43bdca32d..df5fd65d56bde3ed046c9ed40e62e8a17ede2e8e
@@@ -29,6 -29,7 +29,7 @@@ Example:       git-rebase master~1 topi
  '
  
  SUBDIRECTORY_OK=Yes
+ OPTIONS_SPEC=
  . git-sh-setup
  set_reflog_action rebase
  require_work_tree
@@@ -87,7 -88,7 +88,7 @@@ call_merge () 
        cmt="$(cat "$dotest/cmt.$1")"
        echo "$cmt" > "$dotest/current"
        hd=$(git rev-parse --verify HEAD)
 -      cmt_name=$(git symbolic-ref HEAD)
 +      cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
        msgnum=$(cat "$dotest/msgnum")
        end=$(cat "$dotest/end")
        eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
        esac
  }
  
 +move_to_original_branch () {
 +      test -z "$head_name" &&
 +              head_name="$(cat "$dotest"/head-name)" &&
 +              onto="$(cat "$dotest"/onto)" &&
 +              orig_head="$(cat "$dotest"/orig-head)"
 +      case "$head_name" in
 +      refs/*)
 +              message="rebase finished: $head_name onto $onto"
 +              git update-ref -m "$message" \
 +                      $head_name $(git rev-parse HEAD) $orig_head &&
 +              git symbolic-ref HEAD $head_name ||
 +              die "Could not move back to $head_name"
 +              ;;
 +      esac
 +}
 +
  finish_rb_merge () {
 +      move_to_original_branch
        rm -r "$dotest"
        echo "All done."
  }
                        finish_rb_merge
                        exit
                fi
 -              git am --resolved --3way --resolvemsg="$RESOLVEMSG"
 +              head_name=$(cat .dotest/head-name) &&
 +              onto=$(cat .dotest/onto) &&
 +              orig_head=$(cat .dotest/orig-head) &&
 +              git am --resolved --3way --resolvemsg="$RESOLVEMSG" &&
 +              move_to_original_branch
                exit
                ;;
        --skip)
                        finish_rb_merge
                        exit
                fi
 -              git am -3 --skip --resolvemsg="$RESOLVEMSG"
 +              head_name=$(cat .dotest/head-name) &&
 +              onto=$(cat .dotest/onto) &&
 +              orig_head=$(cat .dotest/orig-head) &&
 +              git am -3 --skip --resolvemsg="$RESOLVEMSG" &&
 +              move_to_original_branch
                exit
                ;;
        --abort)
                git rerere clear
                if test -d "$dotest"
                then
 +                      move_to_original_branch
                        rm -r "$dotest"
                elif test -d .dotest
                then
 +                      dotest=.dotest
 +                      move_to_original_branch
                        rm -r .dotest
                else
                        die "No rebase in progress?"
@@@ -346,19 -319,6 +347,19 @@@ the
        GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
  fi
  
 +# move to a detached HEAD
 +orig_head=$(git rev-parse HEAD^0)
 +head_name=$(git symbolic-ref HEAD 2> /dev/null)
 +case "$head_name" in
 +'')
 +      head_name="detached HEAD"
 +      ;;
 +*)
 +      git checkout "$orig_head" > /dev/null 2>&1 ||
 +              die "could not detach HEAD"
 +      ;;
 +esac
 +
  # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
  echo "First, rewinding head to replay your work on top of it..."
  git-reset --hard "$onto"
  if test "$mb" = "$branch"
  then
        echo >&2 "Fast-forwarded $branch_name to $onto_name."
 +      move_to_original_branch
        exit 0
  fi
  
  if test -z "$do_merge"
  then
        git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
 -      git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG"
 -      exit $?
 +      git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" &&
 +      move_to_original_branch
 +      ret=$?
 +      test 0 != $ret -a -d .dotest &&
 +              echo $head_name > .dotest/head-name &&
 +              echo $onto > .dotest/onto &&
 +              echo $orig_head > .dotest/orig-head
 +      exit $ret
  fi
  
  # start doing a rebase with git-merge
  mkdir -p "$dotest"
  echo "$onto" > "$dotest/onto"
  echo "$onto_name" > "$dotest/onto_name"
 -prev_head=`git rev-parse HEAD^0`
 +prev_head=$orig_head
  echo "$prev_head" > "$dotest/prev_head"
 +echo "$orig_head" > "$dotest/orig-head"
 +echo "$head_name" > "$dotest/head-name"
  
  msgnum=0
  for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD`
diff --combined git-request-pull.sh
index 95ad66630f39effead53598a650dec0ab7b90289,90d969c625f83860538f4cf17a2a2abde81fc945..068f5e0fc7308db601141bc3e70475ec2145ef67
@@@ -8,6 -8,7 +8,7 @@@ USAGE='<commit> <url> [<head>]
  LONG_USAGE='Summarizes the changes since <commit> to the standard output,
  and includes <url> in the message generated.'
  SUBDIRECTORY_OK='Yes'
+ OPTIONS_SPEC=
  . git-sh-setup
  . git-parse-remote
  
@@@ -24,13 -25,13 +25,13 @@@ headrev=`git rev-parse --verify "$head"
  merge_base=`git merge-base $baserev $headrev` ||
  die "fatal: No commits in common between $base and $head"
  
 -url="`get_remote_url "$url"`"
 -branch=`git peek-remote "$url" \
 +url=$(get_remote_url "$url")
 +branch=$(git peek-remote "$url" \
        | sed -n -e "/^$headrev refs.heads./{
                s/^.*   refs.heads.//
                p
                q
 -      }"`
 +      }")
  if [ -z "$branch" ]; then
        echo "warn: No branch of $url is at:" >&2
        git log --max-count=1 --pretty='format:warn:   %h: %s' $headrev >&2
diff --combined git-stash.sh
index 1c8b7f92591f87b2d32b51cca6173921c5e041b2,9cabd5dc2cdbdd5de98acb3a0c839dfb07a3c9f3..534eb168abf8b0b8ae5c2195bf85b66d9cb6b21e
@@@ -4,6 -4,7 +4,7 @@@
  USAGE='[ | list | show | apply | clear]'
  
  SUBDIRECTORY_OK=Yes
+ OPTIONS_SPEC=
  . git-sh-setup
  require_work_tree
  cd_to_toplevel
@@@ -21,17 -22,23 +22,17 @@@ no_changes () 
  clear_stash () {
        if current=$(git rev-parse --verify $ref_stash 2>/dev/null)
        then
 -              git update-ref -d refs/stash $current
 +              git update-ref -d $ref_stash $current
        fi
  }
  
 -save_stash () {
 +create_stash () {
        stash_msg="$1"
  
        if no_changes
        then
 -              echo >&2 'No local changes to save'
                exit 0
        fi
 -      test -f "$GIT_DIR/logs/$ref_stash" ||
 -              clear_stash || die "Cannot initialize stash"
 -
 -      # Make sure the reflog for stash is kept.
 -      : >>"$GIT_DIR/logs/$ref_stash"
  
        # state of the base commit
        if b_commit=$(git rev-parse --verify HEAD)
        w_commit=$(printf '%s\n' "$stash_msg" |
                git commit-tree $w_tree -p $b_commit -p $i_commit) ||
                die "Cannot record working tree state"
 +}
 +
 +save_stash () {
 +      stash_msg="$1"
 +
 +      if no_changes
 +      then
 +              echo >&2 'No local changes to save'
 +              exit 0
 +      fi
 +      test -f "$GIT_DIR/logs/$ref_stash" ||
 +              clear_stash || die "Cannot initialize stash"
 +
 +      create_stash "$stash_msg"
 +
 +      # Make sure the reflog for stash is kept.
 +      : >>"$GIT_DIR/logs/$ref_stash"
  
        git update-ref -m "$stash_msg" $ref_stash $w_commit ||
                die "Cannot save the current status"
@@@ -213,13 -203,6 +214,13 @@@ apply
  clear)
        clear_stash
        ;;
 +create)
 +      if test $# -gt 0 && test "$1" = create
 +      then
 +              shift
 +      fi
 +      create_stash "$*" && echo "$w_commit"
 +      ;;
  help | usage)
        usage
        ;;
diff --combined git-submodule.sh
index 5af28ecd58de8c46f194280887ef9c27c784e606,1c656be1cfb6eb7d63e6a1f3cdda959cf90aca89..82ac28fa27dc41b821905f1a83155994d56c4f3b
@@@ -5,6 -5,7 +5,7 @@@
  # Copyright (c) 2007 Lars Hjemli
  
  USAGE='[--quiet] [--cached] [add <repo> [-b branch]|status|init|update] [--] [<path>...]'
+ OPTIONS_SPEC=
  . git-sh-setup
  require_work_tree
  
@@@ -73,7 -74,7 +74,7 @@@ resolve_relative_url (
  module_name()
  {
        # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
 -      re=$(printf '%s' "$1" | sed -e 's/\([^a-zA-Z0-9_]\)/\\\1/g')
 +      re=$(printf '%s' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
        name=$( GIT_CONFIG=.gitmodules \
                git config --get-regexp '^submodule\..*\.path$' |
                sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
diff --combined git.c
index 4a250f7e8b84f2334c84daaf93baa0fe1f0ca344,b843da4d424d23d2855d84f55eef52e8a3fa1bb7..7604319b5a9e8c12b2f00b4d5e6480445e02daa6
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -249,9 -249,14 +249,9 @@@ static int run_command(struct cmd_struc
                prefix = setup_git_directory();
        if (p->option & USE_PAGER)
                setup_pager();
 -      if (p->option & NEED_WORK_TREE) {
 -              const char *work_tree = get_git_work_tree();
 -              const char *git_dir = get_git_dir();
 -              if (!is_absolute_path(git_dir))
 -                      set_git_dir(make_absolute_path(git_dir));
 -              if (!work_tree || chdir(work_tree))
 -                      die("%s must be run in a work tree", p->cmd);
 -      }
 +      if (p->option & NEED_WORK_TREE)
 +              setup_work_tree();
 +
        trace_argv_printf(argv, argc, "trace: built-in: git");
  
        status = p->fn(argc, argv, prefix);
@@@ -338,9 -343,9 +338,9 @@@ static void handle_internal_command(in
                { "rerere", cmd_rerere, RUN_SETUP },
                { "reset", cmd_reset, RUN_SETUP },
                { "rev-list", cmd_rev_list, RUN_SETUP },
-               { "rev-parse", cmd_rev_parse, RUN_SETUP },
+               { "rev-parse", cmd_rev_parse },
                { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
 -              { "rm", cmd_rm, RUN_SETUP | NEED_WORK_TREE },
 +              { "rm", cmd_rm, RUN_SETUP },
                { "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },
                { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
                { "show-branch", cmd_show_branch, RUN_SETUP },