Merge branch 'dc/stash-con-untracked'
authorJunio C Hamano <gitster@pobox.com>
Fri, 22 Jul 2011 21:46:28 +0000 (14:46 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 22 Jul 2011 21:46:28 +0000 (14:46 -0700)
* dc/stash-con-untracked:
stash: Add --include-untracked option to stash and remove all untracked files

Conflicts:
git-stash.sh

1  2 
git-stash.sh
diff --combined git-stash.sh
index 5619da5c10f968a7eaeeaafc497ed03e763c9439,7ffab6f26fff84c6ebbba7d185b4c69ee95588df..f4e6f05ee212f452a1b3e13c492960a9222029f7
@@@ -7,14 -7,14 +7,15 @@@ USAGE="list [<options>
     or: $dashless drop [-q|--quiet] [<stash>]
     or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
     or: $dashless branch <branchname> [<stash>]
-    or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] [<message>]]
+    or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
+                      [-u|--include-untracked] [-a|--all] [<message>]]
     or: $dashless clear"
  
  SUBDIRECTORY_OK=Yes
  OPTIONS_SPEC=
  START_DIR=`pwd`
  . git-sh-setup
 +. git-sh-i18n
  require_work_tree
  cd_to_toplevel
  
@@@ -34,13 -34,20 +35,20 @@@ f
  
  no_changes () {
        git diff-index --quiet --cached HEAD --ignore-submodules -- &&
-       git diff-files --quiet --ignore-submodules
+       git diff-files --quiet --ignore-submodules &&
+       (test -z "$untracked" || test -z "$(untracked_files)")
+ }
+ untracked_files () {
+       excl_opt=--exclude-standard
+       test "$untracked" = "all" && excl_opt=
+       git ls-files -o -z $excl_opt
  }
  
  clear_stash () {
        if test $# != 0
        then
 -              die "git stash clear with parameters is unimplemented"
 +              die "$(gettext "git stash clear with parameters is unimplemented")"
        fi
        if current=$(git rev-parse --verify $ref_stash 2>/dev/null)
        then
@@@ -50,6 -57,7 +58,7 @@@
  
  create_stash () {
        stash_msg="$1"
+       untracked="$2"
  
        git update-index -q --refresh
        if no_changes
@@@ -62,7 -70,7 +71,7 @@@
        then
                head=$(git rev-list --oneline -n 1 HEAD --)
        else
 -              die "You do not have the initial commit yet"
 +              die "$(gettext "You do not have the initial commit yet")"
        fi
  
        if branch=$(git symbolic-ref -q HEAD)
        i_tree=$(git write-tree) &&
        i_commit=$(printf 'index on %s\n' "$msg" |
                git commit-tree $i_tree -p $b_commit) ||
 -              die "Cannot save the current index state"
 +              die "$(gettext "Cannot save the current index state")"
  
+       if test -n "$untracked"
+       then
+               # Untracked files are stored by themselves in a parentless commit, for
+               # ease of unpacking later.
+               u_commit=$(
+                       untracked_files | (
+                               export GIT_INDEX_FILE="$TMPindex"
+                               rm -f "$TMPindex" &&
+                               git update-index -z --add --remove --stdin &&
+                               u_tree=$(git write-tree) &&
+                               printf 'untracked files on %s\n' "$msg" | git commit-tree $u_tree  &&
+                               rm -f "$TMPindex"
+               ) ) || die "Cannot save the untracked files"
+               untracked_commit_option="-p $u_commit";
+       else
+               untracked_commit_option=
+       fi
        if test -z "$patch_mode"
        then
  
                        git write-tree &&
                        rm -f "$TMPindex"
                ) ) ||
 -                      die "Cannot save the current worktree state"
 +                      die "$(gettext "Cannot save the current worktree state")"
  
        else
  
  
                # state of the working tree
                w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
 -              die "Cannot save the current worktree state"
 +              die "$(gettext "Cannot save the current worktree state")"
  
                git diff-tree -p HEAD $w_tree > "$TMP-patch" &&
                test -s "$TMP-patch" ||
 -              die "No changes selected"
 +              die "$(gettext "No changes selected")"
  
                rm -f "$TMP-index" ||
 -              die "Cannot remove temporary index (can't happen)"
 +              die "$(gettext "Cannot remove temporary index (can't happen)")"
  
        fi
  
                stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg")
        fi
        w_commit=$(printf '%s\n' "$stash_msg" |
-               git commit-tree $w_tree -p $b_commit -p $i_commit) ||
-               die "$(gettext "Cannot record working tree state")"
 -              git commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||
 -              die "Cannot record working tree state"
++      git commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||
++      die "$(gettext "Cannot record working tree state")"
  }
  
  save_stash () {
        keep_index=
        patch_mode=
+       untracked=
        while test $# != 0
        do
                case "$1" in
                -q|--quiet)
                        GIT_QUIET=t
                        ;;
+               -u|--include-untracked)
+                       untracked=untracked
+                       ;;
+               -a|--all)
+                       untracked=all
+                       ;;
                --)
                        shift
                        break
                        ;;
                -*)
 -                      echo "error: unknown option for 'stash save': $1"
 -                      echo "       To provide a message, use git stash save -- '$1'"
 +                      option="$1"
 +                      # TRANSLATORS: $option is an invalid option, like
 +                      # `--blah-blah'. The 7 spaces at the beginning of the
 +                      # second line correspond to "error: ". So you should line
 +                      # up the second line with however many characters the
 +                      # translation of "error: " takes in your language. E.g. in
 +                      # English this is:
 +                      #
 +                      #    $ git stash save --blah-blah 2>&1 | head -n 2
 +                      #    error: unknown option for 'stash save': --blah-blah
 +                      #           To provide a message, use git stash save -- '--blah-blah'
 +                      eval_gettext "$("error: unknown option for 'stash save': \$option
 +       To provide a message, use git stash save -- '\$option'")"; echo
                        usage
                        ;;
                *)
                shift
        done
  
+       if test -n "$patch_mode" && test -n "$untracked"
+       then
+           die "Can't use --patch and ---include-untracked or --all at the same time"
+       fi
        stash_msg="$*"
  
        git update-index -q --refresh
        if no_changes
        then
 -              say 'No local changes to save'
 +              say "$(gettext "No local changes to save")"
                exit 0
        fi
        test -f "$GIT_DIR/logs/$ref_stash" ||
 -              clear_stash || die "Cannot initialize stash"
 +              clear_stash || die "$(gettext "Cannot initialize stash")"
  
-       create_stash "$stash_msg"
+       create_stash "$stash_msg" $untracked
  
        # 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"
 +              die "$(gettext "Cannot save the current status")"
        say Saved working directory and index state "$stash_msg"
  
        if test -z "$patch_mode"
        then
                git reset --hard ${GIT_QUIET:+-q}
+               test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
+               if test -n "$untracked"
+               then
+                       git clean --force --quiet $CLEAN_X_OPTION
+               fi
  
                if test "$keep_index" = "t" && test -n $i_tree
                then
                fi
        else
                git apply -R < "$TMP-patch" ||
 -              die "Cannot remove worktree changes"
 +              die "$(gettext "Cannot remove worktree changes")"
  
                if test "$keep_index" != "t"
                then
@@@ -246,9 -279,11 +291,11 @@@ show_stash () 
  #   w_commit is set to the commit containing the working tree
  #   b_commit is set to the base commit
  #   i_commit is set to the commit containing the index tree
+ #   u_commit is set to the commit containing the untracked files tree
  #   w_tree is set to the working tree
  #   b_tree is set to the base tree
  #   i_tree is set to the index tree
+ #   u_tree is set to the untracked files tree
  #
  #   GIT_QUIET is set to t if -q is specified
  #   INDEX_OPTION is set to --index if --index is specified.
@@@ -273,9 -308,11 +320,11 @@@ parse_flags_and_rev(
        w_commit=
        b_commit=
        i_commit=
+       u_commit=
        w_tree=
        b_tree=
        i_tree=
+       u_tree=
  
        REV=$(git rev-parse --no-flags --symbolic "$@") || exit 1
  
  
        case $# in
                0)
 -                      have_stash || die "No stash found."
 +                      have_stash || die "$(gettext "No stash found.")"
                        set -- ${ref_stash}@{0}
                ;;
                1)
                        :
                ;;
                *)
 -                      die "Too many revisions specified: $REV"
 +                      die "$(eval_gettext "Too many revisions specified: \$REV")"
                ;;
        esac
  
 -      REV=$(git rev-parse --quiet --symbolic --verify $1 2>/dev/null) || die "$1 is not valid reference"
 +      REV=$(git rev-parse --quiet --symbolic --verify $1 2>/dev/null) || {
 +              reference="$1"
 +              die "$(eval_gettext "\$reference is not valid reference")"
 +      }
  
        i_commit=$(git rev-parse --quiet --verify $REV^2 2>/dev/null) &&
        set -- $(git rev-parse $REV $REV^1 $REV: $REV^1: $REV^2: 2>/dev/null) &&
        IS_STASH_LIKE=t &&
        test "$ref_stash" = "$(git rev-parse --symbolic-full-name "${REV%@*}")" &&
        IS_STASH_REF=t
+       u_commit=$(git rev-parse --quiet --verify $REV^3 2>/dev/null) &&
+       u_tree=$(git rev-parse $REV^3: 2>/dev/null)
  }
  
  is_stash_like()
  }
  
  assert_stash_like() {
 -      is_stash_like "$@" || die "'$*' is not a stash-like commit"
 +      is_stash_like "$@" || {
 +              args="$*"
 +              die "$(eval_gettext "'\$args' is not a stash-like commit")"
 +      }
  }
  
  is_stash_ref() {
  }
  
  assert_stash_ref() {
 -      is_stash_ref "$@" || die "'$*' is not a stash reference"
 +      is_stash_ref "$@" || {
 +              args="$*"
 +              die "$(eval_gettext "'\$args' is not a stash reference")"
 +      }
  }
  
  apply_stash () {
  
        assert_stash_like "$@"
  
 -      git update-index -q --refresh || die 'unable to refresh index'
 +      git update-index -q --refresh || die "$(gettext "unable to refresh index")"
  
        # current index state
        c_tree=$(git write-tree) ||
 -              die 'Cannot apply a stash in the middle of a merge'
 +              die "$(gettext "Cannot apply a stash in the middle of a merge")"
  
        unstashed_index_tree=
        if test -n "$INDEX_OPTION" && test "$b_tree" != "$i_tree" &&
        then
                git diff-tree --binary $s^2^..$s^2 | git apply --cached
                test $? -ne 0 &&
 -                      die 'Conflicts in index. Try without --index.'
 +                      die "$(gettext "Conflicts in index. Try without --index.")"
                unstashed_index_tree=$(git write-tree) ||
 -                      die 'Could not save index tree'
 +                      die "$(gettext "Could not save index tree")"
                git reset
        fi
  
+       if test -n "$u_tree"
+       then
+               GIT_INDEX_FILE="$TMPindex" git-read-tree "$u_tree" &&
+               GIT_INDEX_FILE="$TMPindex" git checkout-index --all &&
+               rm -f "$TMPindex" ||
+               die 'Could not restore untracked files from stash'
+       fi
        eval "
                GITHEAD_$w_tree='Stashed changes' &&
                GITHEAD_$c_tree='Updated upstream' &&
                        git diff-index --cached --name-only --diff-filter=A $c_tree >"$a" &&
                        git read-tree --reset $c_tree &&
                        git update-index --add --stdin <"$a" ||
 -                              die "Cannot unstage modified files"
 +                              die "$(gettext "Cannot unstage modified files")"
                        rm -f "$a"
                fi
                squelch=
                status=$?
                if test -n "$INDEX_OPTION"
                then
 -                      echo >&2 'Index was not unstashed.'
 +                      (
 +                              gettext "Index was not unstashed." &&
 +                              echo
 +                      ) >&2
                fi
                exit $status
        fi
@@@ -430,15 -466,14 +490,15 @@@ drop_stash () 
        assert_stash_ref "$@"
  
        git reflog delete --updateref --rewrite "${REV}" &&
 -              say "Dropped ${REV} ($s)" || die "${REV}: Could not drop stash entry"
 +              say "$(eval_gettext "Dropped \${REV} (\$s)")" ||
 +              die "$(eval_gettext "\${REV}: Could not drop stash entry")"
  
        # clear_stash if we just dropped the last stash entry
        git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
  }
  
  apply_to_branch () {
 -      test -n "$1" || die 'No branch name specified'
 +      test -n "$1" || die "$(gettext "No branch name specified")"
        branch=$1
        shift 1
  
@@@ -509,7 -544,7 +569,7 @@@ branch
        case $# in
        0)
                save_stash &&
 -              say '(To restore them type "git stash apply")'
 +              say "$(gettext "(To restore them type \"git stash apply\")")"
                ;;
        *)
                usage