Skip to content

Commit 7425dcc

Browse files
committed
Merge branch 'ns/stash'
* ns/stash: Documentation: quote {non-attributes} for asciidoc git-stash: don't complain when listing in a repo with no stash git-stash: fix "can't shift that many" with no arguments git-stash: fix "no arguments" case in documentation git-stash: require "save" to be explicit and update documentation Document git-stash Add git-stash script
2 parents f36db54 + 9a5391c commit 7425dcc

File tree

5 files changed

+330
-1
lines changed

5 files changed

+330
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ git-ssh-fetch
124124
git-ssh-pull
125125
git-ssh-push
126126
git-ssh-upload
127+
git-stash
127128
git-status
128129
git-stripspace
129130
git-submodule

Documentation/cmd-list.perl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ sub format_one {
178178
git-sh-setup purehelpers
179179
git-ssh-fetch synchingrepositories
180180
git-ssh-upload synchingrepositories
181+
git-stash mainporcelain
181182
git-status mainporcelain
182183
git-stripspace purehelpers
183184
git-submodule mainporcelain

Documentation/git-stash.txt

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
git-stash(1)
2+
============
3+
4+
NAME
5+
----
6+
git-stash - Stash the changes in a dirty working directory away
7+
8+
SYNOPSIS
9+
--------
10+
[verse]
11+
'git-stash' (save | list | show [<stash>] | apply [<stash>] | clear)
12+
13+
DESCRIPTION
14+
-----------
15+
16+
Use 'git-stash save' when you want to record the current state of the
17+
working directory and the index, but want to go back to a clean
18+
working directory. The command saves your local modifications away
19+
and reverts the working directory to match the `HEAD` commit.
20+
21+
The modifications stashed away by this command can be listed with
22+
`git-stash list`, inspected with `git-stash show`, and restored
23+
(potentially on top of a different commit) with `git-stash apply`.
24+
Calling git-stash without any arguments is equivalent to `git-stash
25+
list`.
26+
27+
The latest stash you created is stored in `$GIT_DIR/refs/stash`; older
28+
stashes are found in the reflog of this reference and can be named using
29+
the usual reflog syntax (e.g. `stash@\{1}` is the most recently
30+
created stash, `stash@\{2}` is the one before it, `stash@\{2.hours.ago}`
31+
is also possible).
32+
33+
OPTIONS
34+
-------
35+
36+
save::
37+
38+
Save your local modifications to a new 'stash', and run `git-reset
39+
--hard` to revert them.
40+
41+
list::
42+
43+
List the stashes that you currently have. Each 'stash' is listed
44+
with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1} is
45+
the one before, etc.), the name of the branch that was current when the
46+
stash was made, and a short description of the commit the stash was
47+
based on.
48+
+
49+
----------------------------------------------------------------
50+
stash@{0}: submit: 6ebd0e2... Add git-stash
51+
stash@{1}: master: 9cc0589... Merge branch 'master' of gfi
52+
----------------------------------------------------------------
53+
54+
show [<stash>]::
55+
56+
Show the changes recorded in the stash as a diff between the the
57+
stashed state and its original parent. When no `<stash>` is given,
58+
shows the latest one. By default, the command shows the diffstat, but
59+
it will accept any format known to `git-diff` (e.g., `git-stash show
60+
-p stash@\{2}` to view the second most recent stash in patch form).
61+
62+
apply [<stash>]::
63+
64+
Restore the changes recorded in the stash on top of the current
65+
working tree state. When no `<stash>` is given, applies the latest
66+
one. The working directory must match the index.
67+
+
68+
This operation can fail with conflicts; you need to resolve them
69+
by hand in the working tree.
70+
71+
clear::
72+
Remove all the stashed states. Note that those states will then
73+
be subject to pruning, and may be difficult or impossible to recover.
74+
75+
76+
DISCUSSION
77+
----------
78+
79+
A stash is represented as a commit whose tree records the state of the
80+
working directory, and its first parent is the commit at `HEAD` when
81+
the stash was created. The tree of the second parent records the
82+
state of the index when the stash is made, and it is made a child of
83+
the `HEAD` commit. The ancestry graph looks like this:
84+
85+
.----W
86+
/ /
87+
...--H----I
88+
89+
where `H` is the `HEAD` commit, `I` is a commit that records the state
90+
of the index, and `W` is a commit that records the state of the working
91+
tree.
92+
93+
94+
EXAMPLES
95+
--------
96+
97+
Pulling into a dirty tree::
98+
99+
When you are in the middle of something, you learn that there are
100+
upstream changes that are possibly relevant to what you are
101+
doing. When your local changes do not conflict with the changes in
102+
the upstream, a simple `git pull` will let you move forward.
103+
+
104+
However, there are cases in which your local changes do conflict with
105+
the upstream changes, and `git pull` refuses to overwrite your
106+
changes. In such a case, you can stash your changes away,
107+
perform a pull, and then unstash, like this:
108+
+
109+
----------------------------------------------------------------
110+
$ git pull
111+
...
112+
file foobar not up to date, cannot merge.
113+
$ git stash
114+
$ git pull
115+
$ git stash apply
116+
----------------------------------------------------------------
117+
118+
Interrupted workflow::
119+
120+
When you are in the middle of something, your boss comes in and
121+
demands that you fix something immediately. Traditionally, you would
122+
make a commit to a temporary branch to store your changes away, and
123+
return to your original branch to make the emergency fix, like this:
124+
+
125+
----------------------------------------------------------------
126+
... hack hack hack ...
127+
$ git checkout -b my_wip
128+
$ git commit -a -m "WIP"
129+
$ git checkout master
130+
$ edit emergency fix
131+
$ git commit -a -m "Fix in a hurry"
132+
$ git checkout my_wip
133+
$ git reset --soft HEAD^
134+
... continue hacking ...
135+
----------------------------------------------------------------
136+
+
137+
You can use `git-stash` to simplify the above, like this:
138+
+
139+
----------------------------------------------------------------
140+
... hack hack hack ...
141+
$ git stash
142+
$ edit emergency fix
143+
$ git commit -a -m "Fix in a hurry"
144+
$ git stash apply
145+
... continue hacking ...
146+
----------------------------------------------------------------
147+
148+
SEE ALSO
149+
--------
150+
gitlink:git-checkout[1],
151+
gitlink:git-commit[1],
152+
gitlink:git-reflog[1],
153+
gitlink:git-reset[1]
154+
155+
AUTHOR
156+
------
157+
Written by Nanako Shiraishi <[email protected]>
158+
159+
GIT
160+
---
161+
Part of the gitlink:git[7] suite

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ SCRIPT_SH = \
212212
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
213213
git-merge-resolve.sh git-merge-ours.sh \
214214
git-lost-found.sh git-quiltimport.sh git-submodule.sh \
215-
git-filter-branch.sh
215+
git-filter-branch.sh \
216+
git-stash.sh
216217

217218
SCRIPT_PERL = \
218219
git-add--interactive.perl \

git-stash.sh

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#!/bin/sh
2+
# Copyright (c) 2007, Nanako Shiraishi
3+
4+
USAGE='[ | list | show | apply | clear]'
5+
6+
. git-sh-setup
7+
require_work_tree
8+
9+
TMP="$GIT_DIR/.git-stash.$$"
10+
trap 'rm -f "$TMP-*"' 0
11+
12+
ref_stash=refs/stash
13+
14+
no_changes () {
15+
git-diff-index --quiet --cached HEAD &&
16+
git-diff-files --quiet
17+
}
18+
19+
clear_stash () {
20+
logfile="$GIT_DIR/logs/$ref_stash" &&
21+
mkdir -p "$(dirname "$logfile")" &&
22+
: >"$logfile"
23+
}
24+
25+
save_stash () {
26+
if no_changes
27+
then
28+
echo >&2 'No local changes to save'
29+
exit 0
30+
fi
31+
test -f "$GIT_DIR/logs/$ref_stash" ||
32+
clear_stash || die "Cannot initialize stash"
33+
34+
# state of the base commit
35+
if b_commit=$(git-rev-parse --verify HEAD)
36+
then
37+
head=$(git-log --abbrev-commit --pretty=oneline -n 1 HEAD)
38+
else
39+
die "You do not have the initial commit yet"
40+
fi
41+
42+
if branch=$(git-symbolic-ref -q HEAD)
43+
then
44+
branch=${branch#refs/heads/}
45+
else
46+
branch='(no branch)'
47+
fi
48+
msg=$(printf '%s: %s' "$branch" "$head")
49+
50+
# state of the index
51+
i_tree=$(git-write-tree) &&
52+
i_commit=$(printf 'index on %s' "$msg" |
53+
git-commit-tree $i_tree -p $b_commit) ||
54+
die "Cannot save the current index state"
55+
56+
# state of the working tree
57+
w_tree=$( (
58+
GIT_INDEX_FILE="$TMP-index" &&
59+
export GIT_INDEX_FILE &&
60+
61+
rm -f "$TMP-index" &&
62+
git-read-tree $i_tree &&
63+
git-add -u &&
64+
git-write-tree &&
65+
rm -f "$TMP-index"
66+
) ) ||
67+
die "Cannot save the current worktree state"
68+
69+
# create the stash
70+
w_commit=$(printf 'WIP on %s' "$msg" |
71+
git-commit-tree $w_tree -p $b_commit -p $i_commit) ||
72+
die "Cannot record working tree state"
73+
74+
git-update-ref -m "$msg" $ref_stash $w_commit ||
75+
die "Cannot save the current status"
76+
printf >&2 'Saved WIP on %s\n' "$msg"
77+
}
78+
79+
have_stash () {
80+
git-rev-parse --verify $ref_stash >/dev/null 2>&1
81+
}
82+
83+
list_stash () {
84+
have_stash || return 0
85+
git-log --pretty=oneline -g "$@" $ref_stash |
86+
sed -n -e 's/^[.0-9a-f]* refs\///p'
87+
}
88+
89+
show_stash () {
90+
flags=$(git-rev-parse --no-revs --flags "$@")
91+
if test -z "$flags"
92+
then
93+
flags=--stat
94+
fi
95+
s=$(git-rev-parse --revs-only --no-flags --default $ref_stash "$@")
96+
97+
w_commit=$(git-rev-parse --verify "$s") &&
98+
b_commit=$(git-rev-parse --verify "$s^") &&
99+
git-diff $flags $b_commit $w_commit
100+
}
101+
102+
apply_stash () {
103+
git-diff-files --quiet ||
104+
die 'Cannot restore on top of a dirty state'
105+
106+
# current index state
107+
c_tree=$(git-write-tree) ||
108+
die 'Cannot apply a stash in the middle of a merge'
109+
110+
s=$(git-rev-parse --revs-only --no-flags --default $ref_stash "$@") &&
111+
w_tree=$(git-rev-parse --verify "$s:") &&
112+
b_tree=$(git-rev-parse --verify "$s^:") ||
113+
die "$*: no valid stashed state found"
114+
115+
eval "
116+
GITHEAD_$w_tree='Stashed changes' &&
117+
GITHEAD_$c_tree='Updated upstream' &&
118+
GITHEAD_$b_tree='Version stash was based on' &&
119+
export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree
120+
"
121+
122+
if git-merge-recursive $b_tree -- $c_tree $w_tree
123+
then
124+
# No conflict
125+
a="$TMP-added" &&
126+
git-diff --cached --name-only --diff-filter=A $c_tree >"$a" &&
127+
git-read-tree --reset $c_tree &&
128+
git-update-index --add --stdin <"$a" ||
129+
die "Cannot unstage modified files"
130+
git-status
131+
rm -f "$a"
132+
else
133+
# Merge conflict; keep the exit status from merge-recursive
134+
exit
135+
fi
136+
}
137+
138+
# Main command set
139+
case "$1" in
140+
list | '')
141+
test $# -gt 0 && shift
142+
if test $# = 0
143+
then
144+
set x -n 10
145+
shift
146+
fi
147+
list_stash "$@"
148+
;;
149+
show)
150+
shift
151+
show_stash "$@"
152+
;;
153+
apply)
154+
shift
155+
apply_stash "$@"
156+
;;
157+
clear)
158+
clear_stash
159+
;;
160+
save)
161+
save_stash && git-reset --hard
162+
;;
163+
*)
164+
usage
165+
esac

0 commit comments

Comments
 (0)