Reputation: 11561
Sometimes I could use an equivalent of git stash && git checkout $branch && git stash pop
that would remember what was the state of the tree for a given branch and only restore the one that was stashed for this particular branch. How could easily achieve that?
Upvotes: 0
Views: 224
Reputation: 4346
You can also use stash-and-checkout
command of git-whistles. Git-whistles is a Ruby gem, so if you have Ruby installed, you can install git-whistles with gem install git-whistles
. Then you can simply do git stash-and-checkout [branch]
to achieve what you're wanting to do.
For brevity I've defined a shell alias g
for git, and git alias co
for stash-and-checkout
, so to stash any uncommitted work on the current branch, switch to the new branch, and pop that branch's stash (if any), I can simply type g co [branch]
. For git alias setup example see my .gitconfig at github.com/vwal/my-git-extras.
Upvotes: 1
Reputation: 487875
By design, git stash
makes a temporary commit1 that is not on any branch. This is so that you can apply it anywhere else.
If you want to save the state of the index and/or work-tree and associate that state with a particular branch, what Git provides that does this is git commit
. Really, that's all you need: a regular old commit.2 You can "uncommit" later, once you come back to that branch, with git reset --soft HEAD^
.
If you want to wrap this up into one package, you could choose a short commit message that you promise never to use in a "real" commit, and write a little wrapper for git checkout
(note, this is quite untested):
#! /bin/sh
# git-swapto: commit any temporary work, swap to another branch;
# automatically de-commits temporary work.
# for git-sh-setup: can work in subdirectory
SUBDIRECTORY_OK=true
. $(git --exec-path)/git-sh-setup
case $# in
1) ;;
*) die "usage: git-swapto <branch>";;
esac
require_work_tree
# From require_clean_work_tree, converted to status;
# assumes work tree exists.
work_tree_is_clean() {
git update-index -q --ignore-submodules --refresh
# if diff-files says different, fail (work tree not clean)
git diff-files --quiet --ignore-submodules || return 1
# if diff-index says index is different, fail (index not clean)
git diff-index --cached --quiet --ignore-submodules HEAD || return 1
# both work tree and index are clean => nothing to commit
return 0
}
# Force branch name as argument, since we want to do a soft
# reset if there is a temporary commit on it. Also, require
# that current HEAD name a branch.
case "$(git rev-parse --symbolic-full-name "$1")" in
refs/heads/*) ;;
*) die "`$1': not a branch name";;
esac
git symbolic-ref -q HEAD >/dev/null || die "not currently on a branch"
temp_commit_string="!! temporary commit, do not push"
# test whether HEAD refers to our temporary commit
current_commit_is_temp() {
local head_text="$(git log --no-walk --pretty=format:%B)"
test "$head_text" = "$temp_commit_string"
}
# Before leaving this branch, make a temporary commit if
# necessary, amending existing temporary commit if there is one.
# (We should never need --amend, this is just paranoia.)
if ! work_tree_is_clean; then
echo "note: leaving temp commit behind" 1>&2
if current_commit_is_temp; then
git commit -a --amend --no-edit ||
die "cannot update current temp commit"
else
git commit -a -m "$temp_commit_string" --no-edit ||
die "cannot make temp commit"
fi
fi
# work tree is now clean, switch to target
git checkout "$1" || die "cannot switch to $1"
# finally, if it is our special temp commit, reset it away
if current_commit_is_temp; then
git reset --soft HEAD^ || die "failed to unmake temp commit"
fi
# all done, for good or ill...
1Actually it makes at least two, and sometimes three, temporary commits. This is more "mechanism" than "policy" though, while "not on a branch" is policy/design.
2Or, as with git stash
, use two commits if you want to preserve "index" and "work-tree" separately. Otherwise just add stuff to the index as usual, then make a single commit.
Upvotes: 0