git-stash.sh 15.4 KB
Newer Older
1 2 3
#!/bin/sh
# Copyright (c) 2007, Nanako Shiraishi

4 5
dashless=$(basename "$0" | sed -e 's/-/ /')
USAGE="list [<options>]
Stephen Boyd's avatar
Stephen Boyd committed
6 7 8
   or: $dashless show [<stash>]
   or: $dashless drop [-q|--quiet] [<stash>]
   or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
9
   or: $dashless branch <branchname> [<stash>]
10 11 12 13 14
   or: $dashless save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
		      [-u|--include-untracked] [-a|--all] [<message>]
   or: $dashless [push [--patch] [-k|--[no-]keep-index] [-q|--quiet]
		       [-u|--include-untracked] [-a|--all] [-m <message>]
		       [-- <pathspec>...]]
15
   or: $dashless clear"
16

17
SUBDIRECTORY_OK=Yes
18
OPTIONS_SPEC=
19
START_DIR=$(pwd)
20 21
. git-sh-setup
require_work_tree
22
prefix=$(git rev-parse --show-prefix) || exit 1
23
cd_to_toplevel
24 25

TMP="$GIT_DIR/.git-stash.$$"
26
TMPindex=${GIT_INDEX_FILE-"$(git rev-parse --git-path index)"}.stash.$$
27
trap 'rm -f "$TMP-"* "$TMPindex"' 0
28 29 30

ref_stash=refs/stash

31 32 33 34 35 36 37 38
if git config --get-colorbool color.interactive; then
       help_color="$(git config --get-color color.interactive.help 'red bold')"
       reset_color="$(git config --get-color '' reset)"
else
       help_color=
       reset_color=
fi

39
no_changes () {
40 41
	git diff-index --quiet --cached HEAD --ignore-submodules -- "$@" &&
	git diff-files --quiet --ignore-submodules -- "$@" &&
42 43 44 45
	(test -z "$untracked" || test -z "$(untracked_files)")
}

untracked_files () {
46 47 48 49 50 51 52
	if test "$1" = "-z"
	then
		shift
		z=-z
	else
		z=
	fi
53 54
	excl_opt=--exclude-standard
	test "$untracked" = "all" && excl_opt=
55
	git ls-files -o $z $excl_opt -- "$@"
56 57 58
}

clear_stash () {
59 60
	if test $# != 0
	then
61
		die "$(gettext "git stash clear with parameters is unimplemented")"
62
	fi
63
	if current=$(git rev-parse --verify --quiet $ref_stash)
64
	then
65
		git update-ref -d $ref_stash $current
66
	fi
67 68
}

69
create_stash () {
70 71 72 73 74 75 76 77 78 79 80 81 82
	stash_msg=
	untracked=
	while test $# != 0
	do
		case "$1" in
		-m|--message)
			shift
			stash_msg=${1?"BUG: create_stash () -m requires an argument"}
			;;
		-u|--include-untracked)
			shift
			untracked=${1?"BUG: create_stash () -u requires an argument"}
			;;
83 84 85 86
		--)
			shift
			break
			;;
87 88 89
		esac
		shift
	done
90

91
	git update-index -q --refresh
92
	if no_changes "$@"
93 94 95
	then
		exit 0
	fi
96

97
	# state of the base commit
98
	if b_commit=$(git rev-parse --verify HEAD)
99
	then
100
		head=$(git rev-list --oneline -n 1 HEAD --)
101
	else
102
		die "$(gettext "You do not have the initial commit yet")"
103 104
	fi

105
	if branch=$(git symbolic-ref -q HEAD)
106 107 108 109 110 111 112 113
	then
		branch=${branch#refs/heads/}
	else
		branch='(no branch)'
	fi
	msg=$(printf '%s: %s' "$branch" "$head")

	# state of the index
114
	i_tree=$(git write-tree) &&
115
	i_commit=$(printf 'index on %s\n' "$msg" |
116
		git commit-tree $i_tree -p $b_commit) ||
117
		die "$(gettext "Cannot save the current index state")"
118

119 120 121 122 123
	if test -n "$untracked"
	then
		# Untracked files are stored by themselves in a parentless commit, for
		# ease of unpacking later.
		u_commit=$(
124
			untracked_files -z "$@" | (
125 126
				GIT_INDEX_FILE="$TMPindex" &&
				export GIT_INDEX_FILE &&
127 128 129 130 131
				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"
132
		) ) || die "$(gettext "Cannot save the untracked files")"
133 134 135 136 137 138

		untracked_commit_option="-p $u_commit";
	else
		untracked_commit_option=
	fi

139 140 141 142 143
	if test -z "$patch_mode"
	then

		# state of the working tree
		w_tree=$( (
144 145
			git read-tree --index-output="$TMPindex" -m $i_tree &&
			GIT_INDEX_FILE="$TMPindex" &&
146
			export GIT_INDEX_FILE &&
147
			git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" &&
148
			git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
149
			git write-tree &&
150
			rm -f "$TMPindex"
151
		) ) ||
152
			die "$(gettext "Cannot save the current worktree state")"
153 154 155

	else

156
		rm -f "$TMP-index" &&
157 158 159 160
		GIT_INDEX_FILE="$TMP-index" git read-tree HEAD &&

		# find out what the user wants
		GIT_INDEX_FILE="$TMP-index" \
161
			git add--interactive --patch=stash -- "$@" &&
162 163 164

		# state of the working tree
		w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
165
		die "$(gettext "Cannot save the current worktree state")"
166

167
		git diff-tree -p HEAD $w_tree -- >"$TMP-patch" &&
168
		test -s "$TMP-patch" ||
169
		die "$(gettext "No changes selected")"
170 171

		rm -f "$TMP-index" ||
172
		die "$(gettext "Cannot remove temporary index (can't happen)")"
173 174 175

	fi

176
	# create the stash
177 178 179 180 181 182 183
	if test -z "$stash_msg"
	then
		stash_msg=$(printf 'WIP on %s' "$msg")
	else
		stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg")
	fi
	w_commit=$(printf '%s\n' "$stash_msg" |
184 185
	git commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||
	die "$(gettext "Cannot record working tree state")"
186
}
187

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
store_stash () {
	while test $# != 0
	do
		case "$1" in
		-m|--message)
			shift
			stash_msg="$1"
			;;
		-q|--quiet)
			quiet=t
			;;
		*)
			break
			;;
		esac
		shift
	done
	test $# = 1 ||
	die "$(eval_gettext "\"$dashless store\" requires one <commit> argument")"

	w_commit="$1"
	if test -z "$stash_msg"
	then
		stash_msg="Created via \"git stash store\"."
	fi

214
	git update-ref --create-reflog -m "$stash_msg" $ref_stash $w_commit
215
	ret=$?
216
	test $ret != 0 && test -z "$quiet" &&
217 218 219 220
	die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")"
	return $ret
}

221
push_stash () {
222
	keep_index=
223
	patch_mode=
224
	untracked=
225
	stash_msg=
Stephen Boyd's avatar
Stephen Boyd committed
226 227 228
	while test $# != 0
	do
		case "$1" in
229
		-k|--keep-index)
Stephen Boyd's avatar
Stephen Boyd committed
230 231
			keep_index=t
			;;
232
		--no-keep-index)
233
			keep_index=n
234 235 236
			;;
		-p|--patch)
			patch_mode=t
237 238
			# only default to keep if we don't already have an override
			test -z "$keep_index" && keep_index=t
Stephen Boyd's avatar
Stephen Boyd committed
239 240 241 242
			;;
		-q|--quiet)
			GIT_QUIET=t
			;;
243 244 245 246 247 248
		-u|--include-untracked)
			untracked=untracked
			;;
		-a|--all)
			untracked=all
			;;
249 250 251 252 253
		-m|--message)
			shift
			test -z ${1+x} && usage
			stash_msg=$1
			;;
254 255 256
		--help)
			show_help
			;;
257 258 259 260 261
		--)
			shift
			break
			;;
		-*)
262 263 264 265 266 267 268 269 270 271 272
			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'
273 274
			eval_gettextln "error: unknown option for 'stash save': \$option
       To provide a message, use git stash save -- '\$option'"
275 276
			usage
			;;
Stephen Boyd's avatar
Stephen Boyd committed
277 278 279 280
		*)
			break
			;;
		esac
281
		shift
Stephen Boyd's avatar
Stephen Boyd committed
282
	done
283

284 285
	eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")"

286 287
	if test -n "$patch_mode" && test -n "$untracked"
	then
288
		die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")"
289 290
	fi

291 292
	test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1

293
	git update-index -q --refresh
294
	if no_changes "$@"
295
	then
296
		say "$(gettext "No local changes to save")"
297 298
		exit 0
	fi
299

300
	git reflog exists $ref_stash ||
301
		clear_stash || die "$(gettext "Cannot initialize stash")"
302

303
	create_stash -m "$stash_msg" -u "$untracked" -- "$@"
304 305
	store_stash -m "$stash_msg" -q $w_commit ||
	die "$(gettext "Cannot save the current status")"
306
	say "$(eval_gettext "Saved working directory and index state \$stash_msg")"
307

308
	if test -z "$patch_mode"
309
	then
310 311 312 313 314 315
		test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
		if test -n "$untracked"
		then
			git clean --force --quiet -d $CLEAN_X_OPTION -- "$@"
		fi

316 317
		if test $# != 0
		then
318
			git reset -q -- "$@"
319 320
			git ls-files -z --modified -- "$@" |
			git checkout-index -z --force --stdin
321
			git clean --force -q -d -- "$@"
322
		else
323
			git reset --hard -q
324
		fi
325

326
		if test "$keep_index" = "t" && test -n "$i_tree"
327
		then
328 329 330
			git read-tree --reset $i_tree
			git ls-files -z --modified -- "$@" |
			git checkout-index -z --force --stdin
331 332 333
		fi
	else
		git apply -R < "$TMP-patch" ||
334
		die "$(gettext "Cannot remove worktree changes")"
335

336
		if test "$keep_index" != "t"
337
		then
338
			git reset -q -- "$@"
339
		fi
340
	fi
341 342
}

343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
save_stash () {
	push_options=
	while test $# != 0
	do
		case "$1" in
		--)
			shift
			break
			;;
		-*)
			# pass all options through to push_stash
			push_options="$push_options $1"
			;;
		*)
			break
			;;
		esac
		shift
	done

	stash_msg="$*"

	if test -z "$stash_msg"
	then
		push_stash $push_options
	else
		push_stash $push_options -m "$stash_msg"
	fi
}

373
have_stash () {
374
	git rev-parse --verify --quiet $ref_stash >/dev/null
375 376
}

377
list_stash () {
378
	have_stash || return 0
379
	git log --format="%gd: %gs" -g --first-parent -m "$@" $ref_stash --
380 381 382
}

show_stash () {
383
	ALLOW_UNKNOWN_FLAGS=t
384
	assert_stash_like "$@"
385

386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
	if test -z "$FLAGS"
	then
		if test "$(git config --bool stash.showStat || echo true)" = "true"
		then
			FLAGS=--stat
		fi

		if test "$(git config --bool stash.showPatch || echo false)" = "true"
		then
			FLAGS=${FLAGS}${FLAGS:+ }-p
		fi

		if test -z "$FLAGS"
		then
			return 0
		fi
	fi

	git diff ${FLAGS} $b_commit $w_commit
405 406
}

407 408 409 410 411
show_help () {
	exec git help stash
	exit 1
}

412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
#
# Parses the remaining options looking for flags and
# at most one revision defaulting to ${ref_stash}@{0}
# if none found.
#
# Derives related tree and commit objects from the
# revision, if one is found.
#
# stash records the work tree, and is a merge between the
# base commit (first parent) and the index tree (second parent).
#
#   REV is set to the symbolic version of the specified stash-like commit
#   IS_STASH_LIKE is non-blank if ${REV} looks like a stash
#   IS_STASH_REF is non-blank if the ${REV} looks like a stash ref
#   s is set to the SHA1 of the stash commit
#   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
430
#   u_commit is set to the commit containing the untracked files tree
431 432 433
#   w_tree is set to the working tree
#   b_tree is set to the base tree
#   i_tree is set to the index tree
434
#   u_tree is set to the untracked files tree
435 436 437
#
#   GIT_QUIET is set to t if -q is specified
#   INDEX_OPTION is set to --index if --index is specified.
438
#   FLAGS is set to the remaining flags (if allowed)
439 440 441 442 443 444
#
# dies if:
#   * too many revisions specified
#   * no revision is specified and there is no stash stack
#   * a revision is specified which cannot be resolve to a SHA1
#   * a non-existent stash reference is specified
445
#   * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not "t"
446 447 448 449 450 451 452 453 454 455 456 457 458 459
#

parse_flags_and_rev()
{
	test "$PARSE_CACHE" = "$*" && return 0 # optimisation
	PARSE_CACHE="$*"

	IS_STASH_LIKE=
	IS_STASH_REF=
	INDEX_OPTION=
	s=
	w_commit=
	b_commit=
	i_commit=
460
	u_commit=
461 462 463
	w_tree=
	b_tree=
	i_tree=
464
	u_tree=
465 466

	FLAGS=
467
	REV=
468
	for opt
469
	do
470 471 472 473
		case "$opt" in
			-q|--quiet)
				GIT_QUIET=-t
			;;
474 475 476
			--index)
				INDEX_OPTION=--index
			;;
477 478 479
			--help)
				show_help
			;;
480
			-*)
481 482
				test "$ALLOW_UNKNOWN_FLAGS" = t ||
					die "$(eval_gettext "unknown option: \$opt")"
483
				FLAGS="${FLAGS}${FLAGS:+ }$opt"
484
			;;
485 486 487
			*)
				REV="${REV}${REV:+ }'$opt'"
			;;
488 489 490
		esac
	done

491
	eval set -- $REV
492 493 494

	case $# in
		0)
495
			have_stash || die "$(gettext "No stash entries found.")"
496 497 498 499 500 501
			set -- ${ref_stash}@{0}
		;;
		1)
			:
		;;
		*)
502
			die "$(eval_gettext "Too many revisions specified: \$REV")"
503 504 505
		;;
	esac

506 507 508 509 510 511 512 513 514
	case "$1" in
		*[!0-9]*)
			:
		;;
		*)
			set -- "${ref_stash}@{$1}"
		;;
	esac

515
	REV=$(git rev-parse --symbolic --verify --quiet "$1") || {
516
		reference="$1"
517
		die "$(eval_gettext "\$reference is not a valid reference")"
518
	}
519

520
	i_commit=$(git rev-parse --verify --quiet "$REV^2") &&
521
	set -- $(git rev-parse "$REV" "$REV^1" "$REV:" "$REV^1:" "$REV^2:" 2>/dev/null) &&
522 523 524 525 526 527 528 529 530
	s=$1 &&
	w_commit=$1 &&
	b_commit=$2 &&
	w_tree=$3 &&
	b_tree=$4 &&
	i_tree=$5 &&
	IS_STASH_LIKE=t &&
	test "$ref_stash" = "$(git rev-parse --symbolic-full-name "${REV%@*}")" &&
	IS_STASH_REF=t
531

532
	u_commit=$(git rev-parse --verify --quiet "$REV^3") &&
533
	u_tree=$(git rev-parse "$REV^3:" 2>/dev/null)
534 535 536 537 538 539 540 541 542
}

is_stash_like()
{
	parse_flags_and_rev "$@"
	test -n "$IS_STASH_LIKE"
}

assert_stash_like() {
543 544 545 546
	is_stash_like "$@" || {
		args="$*"
		die "$(eval_gettext "'\$args' is not a stash-like commit")"
	}
547 548 549 550 551 552 553
}

is_stash_ref() {
	is_stash_like "$@" && test -n "$IS_STASH_REF"
}

assert_stash_ref() {
554 555 556 557
	is_stash_ref "$@" || {
		args="$*"
		die "$(eval_gettext "'\$args' is not a stash reference")"
	}
558 559
}

560
apply_stash () {
Stephen Boyd's avatar
Stephen Boyd committed
561

562
	assert_stash_like "$@"
563

564
	git update-index -q --refresh || die "$(gettext "unable to refresh index")"
565 566 567

	# current index state
	c_tree=$(git write-tree) ||
568
		die "$(gettext "Cannot apply a stash in the middle of a merge")"
569

570
	unstashed_index_tree=
571
	if test -n "$INDEX_OPTION" && test "$b_tree" != "$i_tree" &&
572
			test "$c_tree" != "$i_tree"
573
	then
574
		git diff-tree --binary $s^2^..$s^2 | git apply --cached
575
		test $? -ne 0 &&
576
			die "$(gettext "Conflicts in index. Try without --index.")"
577
		unstashed_index_tree=$(git write-tree) ||
578
			die "$(gettext "Could not save index tree")"
579
		git reset
580
	fi
581

582 583
	if test -n "$u_tree"
	then
584
		GIT_INDEX_FILE="$TMPindex" git read-tree "$u_tree" &&
585 586
		GIT_INDEX_FILE="$TMPindex" git checkout-index --all &&
		rm -f "$TMPindex" ||
587
		die "$(gettext "Could not restore untracked files from stash entry")"
588 589
	fi

590 591 592 593 594 595 596
	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
	"

Stephen Boyd's avatar
Stephen Boyd committed
597 598
	if test -n "$GIT_QUIET"
	then
599
		GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
Stephen Boyd's avatar
Stephen Boyd committed
600
	fi
601
	if git merge-recursive $b_tree -- $c_tree $w_tree
602 603
	then
		# No conflict
604 605 606
		if test -n "$unstashed_index_tree"
		then
			git read-tree "$unstashed_index_tree"
607 608
		else
			a="$TMP-added" &&
609
			git diff-index --cached --name-only --diff-filter=A $c_tree >"$a" &&
610 611
			git read-tree --reset $c_tree &&
			git update-index --add --stdin <"$a" ||
612
				die "$(gettext "Cannot unstage modified files")"
613
			rm -f "$a"
614
		fi
Stephen Boyd's avatar
Stephen Boyd committed
615 616 617 618 619
		squelch=
		if test -n "$GIT_QUIET"
		then
			squelch='>/dev/null 2>&1'
		fi
620
		(cd "$START_DIR" && eval "git status $squelch") || :
621 622
	else
		# Merge conflict; keep the exit status from merge-recursive
623
		status=$?
624
		git rerere
625
		if test -n "$INDEX_OPTION"
626
		then
627
			gettextln "Index was not unstashed." >&2
628
		fi
629
		exit $status
630 631 632
	fi
}

633 634 635
pop_stash() {
	assert_stash_ref "$@"

636 637 638 639 640
	if apply_stash "$@"
	then
		drop_stash "$@"
	else
		status=$?
641
		say "$(gettext "The stash entry is kept in case you need it again.")"
642 643
		exit $status
	fi
644 645
}

646
drop_stash () {
647
	assert_stash_ref "$@"
648

649
	git reflog delete --updateref --rewrite "${REV}" &&
650 651
		say "$(eval_gettext "Dropped \${REV} (\$s)")" ||
		die "$(eval_gettext "\${REV}: Could not drop stash entry")"
652 653

	# clear_stash if we just dropped the last stash entry
654 655
	git rev-parse --verify --quiet "$ref_stash@{0}" >/dev/null ||
	clear_stash
656 657
}

658
apply_to_branch () {
659
	test -n "$1" || die "$(gettext "No branch name specified")"
660
	branch=$1
661
	shift 1
662

663 664 665 666
	set -- --index "$@"
	assert_stash_like "$@"

	git checkout -b $branch $REV^ &&
667 668 669
	apply_stash "$@" && {
		test -z "$IS_STASH_REF" || drop_stash "$@"
	}
670 671
}

672 673
test "$1" = "-p" && set "push" "$@"

674
PARSE_CACHE='--not-parsed'
675
# The default command is "push" if nothing but options are given
676 677 678 679
seen_non_option=
for opt
do
	case "$opt" in
680
	--) break ;;
681 682 683 684 685
	-*) ;;
	*) seen_non_option=t; break ;;
	esac
done

686
test -n "$seen_non_option" || set "push" "$@"
687

688 689
# Main command set
case "$1" in
690 691
list)
	shift
692 693 694 695 696 697
	list_stash "$@"
	;;
show)
	shift
	show_stash "$@"
	;;
698 699
save)
	shift
700
	save_stash "$@"
701
	;;
702 703 704 705
push)
	shift
	push_stash "$@"
	;;
706 707 708 709 710
apply)
	shift
	apply_stash "$@"
	;;
clear)
711 712
	shift
	clear_stash "$@"
713
	;;
714
create)
715
	shift
716
	create_stash -m "$*" && echo "$w_commit"
717
	;;
718 719 720 721
store)
	shift
	store_stash "$@"
	;;
722 723 724 725
drop)
	shift
	drop_stash "$@"
	;;
726 727
pop)
	shift
728
	pop_stash "$@"
729
	;;
730 731 732 733
branch)
	shift
	apply_to_branch "$@"
	;;
734
*)
735 736
	case $# in
	0)
737
		push_stash &&
738
		say "$(gettext "(To restore them type \"git stash apply\")")"
739 740
		;;
	*)
741
		usage
742
	esac
743
	;;
744
esac