From f2c66ed196d1d1410d014e4ee3e2b585936101f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=97=E3=82=89=E3=81=84=E3=81=97=E3=81=AA=E3=81=AA?= =?UTF-8?q?=E3=81=93?= Date: Sat, 30 Jun 2007 14:37:09 +0900 Subject: [PATCH 1/7] Add git-stash script When my boss has something to show me and I have to update, for some reason I am always in the middle of doing something else, and git pull command refuses to work in such a case. I wrote this little script to save the changes I made, perform the update, and then come back to where I was, but on top of the updated commit. This is how you would use the script: $ git stash $ git pull $ git stash apply [jc: with a few fixlets from the list] Signed-off-by: Nanako Shiraishi Signed-off-by: Junio C Hamano --- .gitignore | 1 + Makefile | 3 +- git-stash.sh | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 1 deletion(-) create mode 100755 git-stash.sh diff --git a/.gitignore b/.gitignore index e8b060cbe4..02d9b04da0 100644 --- a/.gitignore +++ b/.gitignore @@ -123,6 +123,7 @@ git-ssh-fetch git-ssh-pull git-ssh-push git-ssh-upload +git-stash git-status git-stripspace git-submodule diff --git a/Makefile b/Makefile index 5d60dc8e12..b3b66b7991 100644 --- a/Makefile +++ b/Makefile @@ -212,7 +212,8 @@ SCRIPT_SH = \ git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \ git-merge-resolve.sh git-merge-ours.sh \ git-lost-found.sh git-quiltimport.sh git-submodule.sh \ - git-filter-branch.sh + git-filter-branch.sh \ + git-stash.sh SCRIPT_PERL = \ git-add--interactive.perl \ diff --git a/git-stash.sh b/git-stash.sh new file mode 100755 index 0000000000..c8c5c5648e --- /dev/null +++ b/git-stash.sh @@ -0,0 +1,160 @@ +#!/bin/sh +# Copyright (c) 2007, Nanako Shiraishi + +USAGE='[ | list | show | apply | clear]' + +. git-sh-setup +require_work_tree + +TMP="$GIT_DIR/.git-stash.$$" +trap 'rm -f "$TMP-*"' 0 + +ref_stash=refs/stash + +no_changes () { + git-diff-index --quiet --cached HEAD && + git-diff-files --quiet +} + +clear_stash () { + logfile="$GIT_DIR/logs/$ref_stash" && + mkdir -p "$(dirname "$logfile")" && + : >"$logfile" +} + +save_stash () { + 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" + + # state of the base commit + if b_commit=$(git-rev-parse --verify HEAD) + then + head=$(git-log --abbrev-commit --pretty=oneline -n 1 HEAD) + else + die "You do not have the initial commit yet" + fi + + if branch=$(git-symbolic-ref -q HEAD) + then + branch=${branch#refs/heads/} + else + branch='(no branch)' + fi + msg=$(printf '%s: %s' "$branch" "$head") + + # state of the index + i_tree=$(git-write-tree) && + i_commit=$(printf 'index on %s' "$msg" | + git-commit-tree $i_tree -p $b_commit) || + die "Cannot save the current index state" + + # state of the working tree + w_tree=$( ( + GIT_INDEX_FILE="$TMP-index" && + export GIT_INDEX_FILE && + + rm -f "$TMP-index" && + git-read-tree $i_tree && + git-add -u && + git-write-tree && + rm -f "$TMP-index" + ) ) || + die "Cannot save the current worktree state" + + # create the stash + w_commit=$(printf 'WIP on %s' "$msg" | + git-commit-tree $w_tree -p $b_commit -p $i_commit) || + die "Cannot record working tree state" + + git-update-ref -m "$msg" $ref_stash $w_commit || + die "Cannot save the current status" + printf >&2 'Saved WIP on %s\n' "$msg" +} + +list_stash () { + git-log --pretty=oneline -g "$@" $ref_stash | + sed -n -e 's/^[.0-9a-f]* refs\///p' +} + +show_stash () { + flags=$(git-rev-parse --no-revs --flags "$@") + if test -z "$flags" + then + flags=--stat + fi + s=$(git-rev-parse --revs-only --no-flags --default $ref_stash "$@") + + w_commit=$(git-rev-parse --verify "$s") && + b_commit=$(git-rev-parse --verify "$s^") && + git-diff $flags $b_commit $w_commit +} + +apply_stash () { + git-diff-files --quiet || + die 'Cannot restore on top of a dirty state' + + # current index state + c_tree=$(git-write-tree) || + die 'Cannot apply a stash in the middle of a merge' + + s=$(git-rev-parse --revs-only --no-flags --default $ref_stash "$@") && + w_tree=$(git-rev-parse --verify "$s:") && + b_tree=$(git-rev-parse --verify "$s^:") || + die "$*: no valid stashed state found" + + eval " + GITHEAD_$w_tree='Stashed changes' && + GITHEAD_$c_tree='Updated upstream' && + GITHEAD_$b_tree='Version stash was based on' && + export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree + " + + if git-merge-recursive $b_tree -- $c_tree $w_tree + then + # No conflict + a="$TMP-added" && + git-diff --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" + git-status + rm -f "$a" + else + # Merge conflict; keep the exit status from merge-recursive + exit + fi +} + +# Main command set +case "$1" in +list) + shift + if test $# = 0 + then + set x -n 10 + shift + fi + list_stash "$@" + ;; +show) + shift + show_stash "$@" + ;; +apply) + shift + apply_stash "$@" + ;; +clear) + clear_stash + ;; +'') + save_stash && git-reset --hard + ;; +*) + usage +esac From 09ccdb630517842e6ad3d9354fbd856174c70d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=97=E3=82=89=E3=81=84=E3=81=97=E3=81=AA=E3=81=AA?= =?UTF-8?q?=E3=81=93?= Date: Sun, 1 Jul 2007 14:26:08 +0900 Subject: [PATCH 2/7] Document git-stash This describes the git-stash command. I borrowed a few paragraphs from Johannes's version, and added a few examples. Signed-off-by: Nanako Shiraishi Signed-off-by: Junio C Hamano --- Documentation/cmd-list.perl | 1 + Documentation/git-stash.txt | 162 ++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 Documentation/git-stash.txt diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl index fcea1d74d5..f50f613879 100755 --- a/Documentation/cmd-list.perl +++ b/Documentation/cmd-list.perl @@ -178,6 +178,7 @@ git-show-ref plumbinginterrogators git-sh-setup purehelpers git-ssh-fetch synchingrepositories git-ssh-upload synchingrepositories +git-stash mainporcelain git-status mainporcelain git-stripspace purehelpers git-submodule mainporcelain diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt new file mode 100644 index 0000000000..17ebc83196 --- /dev/null +++ b/Documentation/git-stash.txt @@ -0,0 +1,162 @@ +git-stash(1) +============ + +NAME +---- +git-stash - Stash the changes in a dirty working directory away + +SYNOPSIS +-------- +[verse] +'git-stash' +'git-stash' [list | show [] | apply [] | clear] + +DESCRIPTION +----------- + +Use 'git-stash' when you want to record the current state of the +working directory and the index, but want to go back to a clean +working directory. The command saves your local modifications away +and reverts the working directory to match the `HEAD` commit. + +The modifications stashed away by this command can be listed with +`git-stash list`, inspected with `git-stash show`, and restored +(potentially on top of a different commit) with `git-stash apply` +commands. The default operation when called without options is to +save the changes away. + +The latest stash you created is stored in `$GIT_DIR/refs/stash`; older +stashes are found in the reflog of this refererence and can be named using +the usual reflog syntax (e.g. `stash@{1}` is the stash one previously made, +`stash@{2}` is the one before it, `stash@{2.hours.ago}` is also possible). + +OPTIONS +------- + +(no subcommand):: + + Save your local modifications to a new 'stash', and run `git-reset + --hard` to revert them. + +list:: + + List the stashes that you currently have. Each 'stash' is listed + with its name (e.g. `stash@{0}` is the latest stash, `stash@{1} is + the one before), the name of the branch that was current when the + stash was made, and a short description of the commit the stash was + based on. ++ +---------------------------------------------------------------- +stash@{0}: submit: 6ebd0e2... Add git-stash +stash@{1}: master: 9cc0589... Merge branch 'master' of gfi +---------------------------------------------------------------- + +show []:: + + Show the changes recorded in the stash. When no `` is given, + shows the latest one. By default, the command shows diffstat, but + you can add `-p` option (i.e. `git stash show -p stash@{2}`) to view + it in patch form. + +apply []:: + + Restores the changes recorded in the stash on top of the current + working tree state. When no `` is given, applies the latest + one. The working directory must match the index. When the changes + conflict, you need to resolve them by hand and mark the result with + `git add` as usual. When the changes are cleanly merged, your + earlier local changes stored in the stash becomes the differences + between the index and the working tree (i.e. `git diff`), except + that newly created files are registered in the index (i.e. `git diff + --cached` is necessary to review the newly added files). + +clear:: + Removes all the stashed states. + + +DISCUSSION +---------- + +A stash is represented as a commit whose tree records the state of the +working directory, and its first parent is the commit at `HEAD` when +the stash was created. The tree of the second parent records the +state of the index when the stash is made, and it is made a child of +the `HEAD` commit. The ancestry graph looks like this: + + .----W + / / + ...--H----I + +where `H` is the `HEAD` commit, `I` is a commit that records the state +of the index, and `W` is a commit that records the state of the working +tree. + + +EXAMPLES +-------- + +Pulling into a dirty tree:: + +When you are in the middle of something, you learn that there are +changes that possibly are relevant to what you are doing in the +upstream. When your local changes do not conflict with the changes in +the upstream, a simple `git pull` will let you move forward. ++ +However, there are cases in which your local changes do conflict with +the upstream changes, and `git pull` refuses to overwrite your +changes. In such a case, you can first stash your changes away, +perform a pull, and then unstash, like this: ++ +---------------------------------------------------------------- +$ git pull +... +file foobar not up to date, cannot merge. +$ git stash +$ git pull +$ git stash apply +---------------------------------------------------------------- + +Interrupted workflow:: + +When you are in the middle of something, your boss comes in and +demands you to fix something immediately. Traditionally, you would +make a commit to a temporary branch to store your changes away, and +come back to make the emergency fix, like this: ++ +---------------------------------------------------------------- +... hack hack hack ... +$ git checkout -b my_wip +$ git commit -a -m "WIP" +$ git checkout master +$ edit emergency fix +$ git commit -a -m "Fix in a hurry" +$ git checkout my_wip +$ git reset --soft HEAD^ +... continue hacking ... +---------------------------------------------------------------- ++ +You can use `git-stash` to simplify the above, like this: ++ +---------------------------------------------------------------- +... hack hack hack ... +$ git stash +$ edit emergency fix +$ git commit -a -m "Fix in a hurry" +$ git stash apply +... continue hacking ... +---------------------------------------------------------------- + +SEE ALSO +-------- +gitlink:git-checkout[1], +gitlink:git-commit[1], +gitlink:git-reflog[1], +gitlink:git-reset[1] + +AUTHOR +------ +Written by Nanako Shiraishi + +GIT +--- +Part of the gitlink:git[7] suite From 9488e875862de4e3c9b022ecd9d0845bc28d1996 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 1 Jul 2007 15:29:01 -0700 Subject: [PATCH 3/7] git-stash: require "save" to be explicit and update documentation Signed-off-by: Junio C Hamano --- Documentation/git-stash.txt | 57 ++++++++++++++++++------------------- git-stash.sh | 4 +-- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 17ebc83196..4815965efd 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -8,32 +8,32 @@ git-stash - Stash the changes in a dirty working directory away SYNOPSIS -------- [verse] -'git-stash' -'git-stash' [list | show [] | apply [] | clear] +'git-stash' (save | list | show [] | apply [] | clear) DESCRIPTION ----------- -Use 'git-stash' when you want to record the current state of the +Use 'git-stash save' when you want to record the current state of the working directory and the index, but want to go back to a clean working directory. The command saves your local modifications away and reverts the working directory to match the `HEAD` commit. The modifications stashed away by this command can be listed with `git-stash list`, inspected with `git-stash show`, and restored -(potentially on top of a different commit) with `git-stash apply` -commands. The default operation when called without options is to -save the changes away. +(potentially on top of a different commit) with `git-stash apply`. +The default operation when called without options is to save the +changes away. The latest stash you created is stored in `$GIT_DIR/refs/stash`; older -stashes are found in the reflog of this refererence and can be named using -the usual reflog syntax (e.g. `stash@{1}` is the stash one previously made, -`stash@{2}` is the one before it, `stash@{2.hours.ago}` is also possible). +stashes are found in the reflog of this reference and can be named using +the usual reflog syntax (e.g. `stash@{1}` is the most recently +created stash, `stash@{2}` is the one before it, `stash@{2.hours.ago}` +is also possible). OPTIONS ------- -(no subcommand):: +save:: Save your local modifications to a new 'stash', and run `git-reset --hard` to revert them. @@ -42,7 +42,7 @@ list:: List the stashes that you currently have. Each 'stash' is listed with its name (e.g. `stash@{0}` is the latest stash, `stash@{1} is - the one before), the name of the branch that was current when the + the one before, etc.), the name of the branch that was current when the stash was made, and a short description of the commit the stash was based on. + @@ -53,25 +53,24 @@ stash@{1}: master: 9cc0589... Merge branch 'master' of gfi show []:: - Show the changes recorded in the stash. When no `` is given, - shows the latest one. By default, the command shows diffstat, but - you can add `-p` option (i.e. `git stash show -p stash@{2}`) to view - it in patch form. + Show the changes recorded in the stash as a diff between the the + stashed state and its original parent. When no `` is given, + shows the latest one. By default, the command shows the diffstat, but + it will accept any format known to `git-diff` (e.g., `git-stash show + -p stash@{2}` to view the second most recent stash in patch form). apply []:: - Restores the changes recorded in the stash on top of the current + Restore the changes recorded in the stash on top of the current working tree state. When no `` is given, applies the latest - one. The working directory must match the index. When the changes - conflict, you need to resolve them by hand and mark the result with - `git add` as usual. When the changes are cleanly merged, your - earlier local changes stored in the stash becomes the differences - between the index and the working tree (i.e. `git diff`), except - that newly created files are registered in the index (i.e. `git diff - --cached` is necessary to review the newly added files). + one. The working directory must match the index. ++ +This operation can fail with conflicts; you need to resolve them +by hand in the working tree. clear:: - Removes all the stashed states. + Remove all the stashed states. Note that those states will then + be subject to pruning, and may be difficult or impossible to recover. DISCUSSION @@ -98,13 +97,13 @@ EXAMPLES Pulling into a dirty tree:: When you are in the middle of something, you learn that there are -changes that possibly are relevant to what you are doing in the -upstream. When your local changes do not conflict with the changes in +upstream changes that are possibly relevant to what you are +doing. When your local changes do not conflict with the changes in the upstream, a simple `git pull` will let you move forward. + However, there are cases in which your local changes do conflict with the upstream changes, and `git pull` refuses to overwrite your -changes. In such a case, you can first stash your changes away, +changes. In such a case, you can stash your changes away, perform a pull, and then unstash, like this: + ---------------------------------------------------------------- @@ -119,9 +118,9 @@ $ git stash apply Interrupted workflow:: When you are in the middle of something, your boss comes in and -demands you to fix something immediately. Traditionally, you would +demands that you fix something immediately. Traditionally, you would make a commit to a temporary branch to store your changes away, and -come back to make the emergency fix, like this: +return to your original branch to make the emergency fix, like this: + ---------------------------------------------------------------- ... hack hack hack ... diff --git a/git-stash.sh b/git-stash.sh index c8c5c5648e..ec18ef6d46 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -132,7 +132,7 @@ apply_stash () { # Main command set case "$1" in -list) +list | '') shift if test $# = 0 then @@ -152,7 +152,7 @@ apply) clear) clear_stash ;; -'') +save) save_stash && git-reset --hard ;; *) From aaca4914e9074f5f5b51c8966688a75e7fe132d7 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 2 Jul 2007 00:20:06 -0400 Subject: [PATCH 4/7] git-stash: fix "no arguments" case in documentation Commit 9488e875 changed this from 'save' to 'list', but missed this spot in the documentation. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-stash.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 4815965efd..4dc344dfe7 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -21,8 +21,8 @@ and reverts the working directory to match the `HEAD` commit. The modifications stashed away by this command can be listed with `git-stash list`, inspected with `git-stash show`, and restored (potentially on top of a different commit) with `git-stash apply`. -The default operation when called without options is to save the -changes away. +Calling git-stash without any arguments is equivalent to `git-stash +list`. The latest stash you created is stored in `$GIT_DIR/refs/stash`; older stashes are found in the reflog of this reference and can be named using From 006a86646434e5212defdea59092059fe41387b0 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 2 Jul 2007 00:20:34 -0400 Subject: [PATCH 5/7] git-stash: fix "can't shift that many" with no arguments Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-stash.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-stash.sh b/git-stash.sh index ec18ef6d46..7644bd5a23 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -133,7 +133,7 @@ apply_stash () { # Main command set case "$1" in list | '') - shift + test $# -gt 0 && shift if test $# = 0 then set x -n 10 From 401de4057ac83d473f8bead9b11d00b6371bd6a0 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 2 Jul 2007 00:21:24 -0400 Subject: [PATCH 6/7] git-stash: don't complain when listing in a repo with no stash Previously, the git-log invocation would complain if a repo had not had any stashes created in it yet: $ git-init $ git-stash fatal: ambiguous argument 'refs/stash': unknown revision or path not in the working tree. Use '--' to separate paths from revisions Instead, we only call git-log if we actually have a refs/stash. We could alternatively create the ref when any stash command is called, but it's better for the 'list' command to not require write access to the repo. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-stash.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/git-stash.sh b/git-stash.sh index 7644bd5a23..18d3322ab5 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -76,7 +76,12 @@ save_stash () { printf >&2 'Saved WIP on %s\n' "$msg" } +have_stash () { + git-rev-parse --verify $ref_stash >/dev/null 2>&1 +} + list_stash () { + have_stash || return 0 git-log --pretty=oneline -g "$@" $ref_stash | sed -n -e 's/^[.0-9a-f]* refs\///p' } From 9a5391cf1836fa1b66f3c89ce9b38f8249bb0521 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 2 Jul 2007 01:24:59 -0400 Subject: [PATCH 7/7] Documentation: quote {non-attributes} for asciidoc Asciidoc treats {foo} as an attribute to be substituted; if 'foo' doesn't exist as an attribute, then the entire line gets dropped. When the literal {foo} is desired, \{foo} is required. The exceptions to this rule are: - inside literal blocks - if the 'foo' contains non-alphanumeric characters (e.g., {foo|bar} is assumed not to be an attribute) Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-stash.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 4dc344dfe7..b7d263d650 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -26,8 +26,8 @@ list`. The latest stash you created is stored in `$GIT_DIR/refs/stash`; older stashes are found in the reflog of this reference and can be named using -the usual reflog syntax (e.g. `stash@{1}` is the most recently -created stash, `stash@{2}` is the one before it, `stash@{2.hours.ago}` +the usual reflog syntax (e.g. `stash@\{1}` is the most recently +created stash, `stash@\{2}` is the one before it, `stash@\{2.hours.ago}` is also possible). OPTIONS @@ -41,7 +41,7 @@ save:: list:: List the stashes that you currently have. Each 'stash' is listed - with its name (e.g. `stash@{0}` is the latest stash, `stash@{1} is + with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1} is the one before, etc.), the name of the branch that was current when the stash was made, and a short description of the commit the stash was based on. @@ -57,7 +57,7 @@ show []:: stashed state and its original parent. When no `` is given, shows the latest one. By default, the command shows the diffstat, but it will accept any format known to `git-diff` (e.g., `git-stash show - -p stash@{2}` to view the second most recent stash in patch form). + -p stash@\{2}` to view the second most recent stash in patch form). apply []::