Skip to content

Commit f2c66ed

Browse files
しらいしななこgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 06f59e9 commit f2c66ed

File tree

3 files changed

+163
-1
lines changed

3 files changed

+163
-1
lines changed

.gitignore

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

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: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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+
list_stash () {
80+
git-log --pretty=oneline -g "$@" $ref_stash |
81+
sed -n -e 's/^[.0-9a-f]* refs\///p'
82+
}
83+
84+
show_stash () {
85+
flags=$(git-rev-parse --no-revs --flags "$@")
86+
if test -z "$flags"
87+
then
88+
flags=--stat
89+
fi
90+
s=$(git-rev-parse --revs-only --no-flags --default $ref_stash "$@")
91+
92+
w_commit=$(git-rev-parse --verify "$s") &&
93+
b_commit=$(git-rev-parse --verify "$s^") &&
94+
git-diff $flags $b_commit $w_commit
95+
}
96+
97+
apply_stash () {
98+
git-diff-files --quiet ||
99+
die 'Cannot restore on top of a dirty state'
100+
101+
# current index state
102+
c_tree=$(git-write-tree) ||
103+
die 'Cannot apply a stash in the middle of a merge'
104+
105+
s=$(git-rev-parse --revs-only --no-flags --default $ref_stash "$@") &&
106+
w_tree=$(git-rev-parse --verify "$s:") &&
107+
b_tree=$(git-rev-parse --verify "$s^:") ||
108+
die "$*: no valid stashed state found"
109+
110+
eval "
111+
GITHEAD_$w_tree='Stashed changes' &&
112+
GITHEAD_$c_tree='Updated upstream' &&
113+
GITHEAD_$b_tree='Version stash was based on' &&
114+
export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree
115+
"
116+
117+
if git-merge-recursive $b_tree -- $c_tree $w_tree
118+
then
119+
# No conflict
120+
a="$TMP-added" &&
121+
git-diff --cached --name-only --diff-filter=A $c_tree >"$a" &&
122+
git-read-tree --reset $c_tree &&
123+
git-update-index --add --stdin <"$a" ||
124+
die "Cannot unstage modified files"
125+
git-status
126+
rm -f "$a"
127+
else
128+
# Merge conflict; keep the exit status from merge-recursive
129+
exit
130+
fi
131+
}
132+
133+
# Main command set
134+
case "$1" in
135+
list)
136+
shift
137+
if test $# = 0
138+
then
139+
set x -n 10
140+
shift
141+
fi
142+
list_stash "$@"
143+
;;
144+
show)
145+
shift
146+
show_stash "$@"
147+
;;
148+
apply)
149+
shift
150+
apply_stash "$@"
151+
;;
152+
clear)
153+
clear_stash
154+
;;
155+
'')
156+
save_stash && git-reset --hard
157+
;;
158+
*)
159+
usage
160+
esac

0 commit comments

Comments
 (0)