[git] How to name and retrieve a stash by name in git?

I was always under the impression that you could give a stash a name by doing git stash save stashname, which you could later on apply by doing git stash apply stashname. But it seems that in this case all that happens is that stashname will be used as the stash description.

Is there no way to actually name a stash? If not, what would you recommend to achieve equivalent functionality? Essentially I have a small stash which I would periodically like to apply, but don't want to always have to hunt in git stash list what its actual stash number is.

This question is related to git git-stash

The answer is


What about this?

git stash save stashname
git stash apply stash^{/stashname}

I don't think there is a way to git pop a stash by its name.

I have created a bash function that does it.

#!/bin/bash

function gstashpop {
  IFS="
"
  [ -z "$1" ] && { echo "provide a stash name"; return; }
  index=$(git stash list | grep -e ': '"$1"'$' | cut -f1 -d:)
  [ "" == "$index" ] && { echo "stash name $1 not found"; return; }
  git stash apply "$index"
}

Example of usage:

[~/code/site] on master*
$ git stash push -m"here the stash name"
Saved working directory and index state On master: here the stash name

[~/code/site] on master
$ git stash list
stash@{0}: On master: here the stash name

[~/code/site] on master
$ gstashpop "here the stash name"

I hope it helps!


Stashes are not meant to be permanent things like you want. You'd probably be better served using tags on commits. Construct the thing you want to stash. Make a commit out of it. Create a tag for that commit. Then roll back your branch to HEAD^. Now when you want to reapply that stash you can use git cherry-pick -n tagname (-n is --no-commit).


For everything besides the stash creation, I'd propose another solution by introducing fzf as a dependency. I recommend taking 5 minutes of your time and get introduced to it, as it is over-all great productivity booster.

Anyway, a related excerpt from their examples page offering stash searching. It's very easy to change the scriptlet to add additional functionality (like stash application or dropping):

fstash() {
    local out q k sha
    while out=$(
            git stash list --pretty="%C(yellow)%h %>(14)%Cgreen%cr %C(blue)%gs" |
            fzf --ansi --no-sort --query="$q" --print-query \
                --expect=ctrl-d,ctrl-b); do
        mapfile -t out <<< "$out"
        q="${out[0]}"
        k="${out[1]}"
        sha="${out[-1]}"
        sha="${sha%% *}"
        [[ -z "$sha" ]] && continue
        if [[ "$k" == 'ctrl-d' ]]; then
            git diff $sha
        elif [[ "$k" == 'ctrl-b' ]]; then
            git stash branch "stash-$sha" $sha
            break;
        else
            git stash show -p $sha
        fi
    done
}

in my fish shell

function gsap
  git stash list | grep ": $argv" | tr -dc '0-9' | xargs git stash apply
end

use

gsap name_of_stash


It's unfortunate that git stash apply stash^{/<regex>} doesn't work (it doesn't actually search the stash list, see the comments under the accepted answer).

Here are drop-in replacements that search git stash list by regex to find the first (most recent) stash@{<n>} and then pass that to git stash <command>:

# standalone (replace <stash_name> with your regex)
(n=$(git stash list --max-count=1 --grep=<stash_name> | cut -f1 -d":") ; if [[ -n "$n" ]] ; then git stash show "$n" ; else echo "Error: No stash matches" ; return 1 ; fi)
(n=$(git stash list --max-count=1 --grep=<stash_name> | cut -f1 -d":") ; if [[ -n "$n" ]] ; then git stash apply "$n" ; else echo "Error: No stash matches" ; return 1 ; fi)
# ~/.gitconfig
[alias]
  sshow = "!f() { n=$(git stash list --max-count=1 --grep=$1 | cut -f1 -d":") ; if [[ -n "$n" ]] ; then git stash show "$n" ; else echo "Error: No stash matches $1" ; return 1 ; fi }; f"
  sapply = "!f() { n=$(git stash list --max-count=1 --grep=$1 | cut -f1 -d":") ; if [[ -n "$n" ]] ; then git stash apply "$n" ; else echo "Error: No stash matches $1" ; return 1 ; fi }; f"

# usage:

$ git sshow my_stash
 myfile.txt | 1 +
 1 file changed, 1 insertion(+)

$ git sapply my_stash
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   myfile.txt

no changes added to commit (use "git add" and/or "git commit -a")

Note that proper result codes are returned so you can use these commands within other scripts. This can be verified after running commands with:

echo $?

Just be careful about variable expansion exploits because I wasn't sure about the --grep=$1 portion. It should maybe be --grep="$1" but I'm not sure if that would interfere with regex delimiters (I'm open to suggestions).


Use git stash save NAME to save.

Then... you can use this script to choose which to apply (or pop):

#!/usr/bin/env ruby
#git-stash-pick by Dan Rosenstark

# can take a command, default is apply
command = ARGV[0]
command = "apply" if !command
ARGV.clear

stashes = []
stashNames = []
`git stash list`.split("\n").each_with_index { |line, index|
    lineSplit = line.split(": ");
    puts "#{index+1}. #{lineSplit[2]}"
    stashes[index] = lineSplit[0]
    stashNames[index] = lineSplit[2]
}
print "Choose Stash or ENTER to exit: "
input = gets.chomp
if input.to_i.to_s == input
    realIndex = input.to_i - 1
    puts "\n\nDoing #{command} to #{stashNames[realIndex]}\n\n"
    puts `git stash #{command} #{stashes[realIndex]}`
end

I like being able to see the names of the stashes and choose. Also I use Zshell and frankly didn't know how to use some of the Bash aliases above ;)

Note: As Kevin says, you should use tags and cherry-picks instead.


This answer owes much to Klemen Slavic. I would have just commented on the accepted answer but I don't have enough rep yet :(

You could also add a git alias to find the stash ref and use it in other aliases for show, apply, drop, etc.

[alias]
    sgrep = "!f() { ref=$(git --no-pager stash list | grep "$1" | cut -d: -f1 | head -n1); echo ${ref:-<no_match>}; }; f"
    sshow = "!f() { git stash show $(git sgrep "$1") -p; }; f"
    sapply = "!f() { git stash apply $(git sgrep "$1"); }; f"
    sdrop = "!f() { git stash drop $(git sgrep "$1"); }; f"

Note that the reason for the ref=$( ... ); echo ${ref:-<no_match>}; pattern is so a blank string is not returned which would cause sshow, sapply and sdrop to target the latest stash instead of fail as one would expect.


git stash save is deprecated as of 2.15.x/2.16, instead you can use git stash push -m "message"

You can use it like this:

git stash push -m "message"

where "message" is your note for that stash.

In order to retrieve the stash you can use: git stash list. This will output a list like this, for example:

stash@{0}: On develop: perf-spike
stash@{1}: On develop: node v10

Then you simply use apply giving it the stash@{index}:

git stash apply stash@{1}

References git stash man page


Alias This might be a more direct syntax for Unix-like systems without needing to encapsulate in a function. Add the following to ~/.gitconfig under [alias]

sshow = !sh -c 'git stash show stash^{/$*} -p' -
sapply = !sh -c 'git stash apply stash^{/$*}' -
ssave = !sh -c 'git stash save "${1}"' -

Usage: sapply regex

Example: git sshow MySecretStash

The hyphen at the end says take input from standard input.


You can turn a stash into a branch if you feel it's important enough:

git stash branch <branchname> [<stash>]

from the man page:

This creates and checks out a new branch named <branchname> starting from the commit at which the <stash> was originally created, applies the changes recorded in <stash> to the new working tree and index, then drops the <stash> if that completes successfully. When no <stash> is given, applies the latest one.

This is useful if the branch on which you ran git stash save has changed enough that git stash apply fails due to conflicts. Since the stash is applied on top of the commit that was HEAD at the time git stash was run, it restores the originally stashed state with no conflicts.

You can later rebase this new branch to some other place that's a descendent of where you were when you stashed.


So, I'm not sure why there's so much consternation on this topic. I can name a git stash with both a push and the deprecated save, and I can use a regex to pull it back with an apply:

Git stash method to use a name to apply

$ git stash push -m "john-hancock"

$ git stash apply stash^{/john-hancock}

As it has been mentioned before, the save command is deprecated, but it still works, so you can used this on older systems where you can't update them with a push call. Unlike the push command, the -m switch isn't required with save.

// save is deprecated but still functional  
$ git stash save john-hancock

This is Git 2.2 and Windows 10.

Visual Proof

Here's a beautiful animated GIF demonstrating the process.

Animated GIF showing a git stash apply using an identifiable name.

Sequence of events

The GIF runs quickly, but if you look, the process is this:

  1. The ls command shows 4 files in the directory
  2. touch example.html adds a 5th file
  3. git stash push -m "john-hancock" -a (The -a includes untracked files)
  4. The ls command shows 4 files after the stash, meaning the stash and the implicit hard reset worked
  5. git stash apply stash^{/john-hancock} runs
  6. The ls command lists 5 files, showing the example.html file was brought back, meaning the git stash apply command worked.

Does this even make sense?

To be frank, I'm not sure what the benefit of this approach is though. There's value in giving the stash a name, but not the retrieval. Maybe to script the shelve and unshelve process it'd be helpful, but it's still way easier to just pop a stash by name.

$ git stash pop 3
$ git stash apply 3

That looks way easier to me than the regex.


Alias

sapply = "!f() { git stash apply \"$(git stash list | awk -F: --posix -vpat=\"$*\" \"$ 0 ~ pat {print $ 1; exit}\")\"; }; f"

Usage

git sapply "<regex>"

  • compatible with Git for Windows

Edit: I sticked to my original solution, but I see why majority would prefer Etan Reisner's version (above). So just for the record:

sapply = "!f() { git stash apply \"$(git stash list | grep -E \"$*\" | awk \"{ print $ 1; }\" | sed -n \"s/://;1p\")\"; }; f"

use git stash push -m aNameForYourStash to save it. Then use git stash list to learn the index of the stash that you want to apply. Then use git stash pop --index 0 to pop the stash and apply it.

note: I'm using git version 2.21.0.windows.1


Late to the party here, but if using VSCode, a quick way to do so is to open the command palette (CTRL / CMD + SHIFT + P) and type "Pop Stash", you'll be able to retrieve your stash by name without the need to use git CLI


If you are just looking for a lightweight way to save some or all of your current working copy changes and then reapply them later at will, consider a patch file:

# save your working copy changes
git diff > some.patch

# re-apply it later
git apply some.patch

Every now and then I wonder if I should be using stashes for this and then I see things like the insanity above and I'm content with what I'm doing :)


This is one way to accomplish this using PowerShell:

<#
.SYNOPSIS
Restores (applies) a previously saved stash based on full or partial stash name.

.DESCRIPTION
Restores (applies) a previously saved stash based on full or partial stash name and then optionally drops the stash. Can be used regardless of whether "git stash save" was done or just "git stash". If no stash matches a message is given. If multiple stashes match a message is given along with matching stash info.

.PARAMETER message
A full or partial stash message name (see right side output of "git stash list"). Can also be "@stash{N}" where N is 0 based stash index.

.PARAMETER drop
If -drop is specified, the matching stash is dropped after being applied.

.EXAMPLE
Restore-Stash "Readme change"
Apply-Stash MyStashName
Apply-Stash MyStashName -drop
Apply-Stash "stash@{0}"
#>
function Restore-Stash  {
    [CmdletBinding()]
    [Alias("Apply-Stash")]
    PARAM (
        [Parameter(Mandatory=$true)] $message,         
        [switch]$drop
    )

    $stashId = $null

    if ($message -match "stash@{") {
        $stashId = $message
    }

    if (!$stashId) {
        $matches = git stash list | Where-Object { $_ -match $message }

        if (!$matches) {
            Write-Warning "No stashes found with message matching '$message' - check git stash list"
            return
        }

        if ($matches.Count -gt 1) {
            Write-Warning "Found $($matches.Count) matches for '$message'. Refine message or pass 'stash{@N}' to this function or git stash apply"
            return $matches
        }

        $parts = $matches -split ':'
        $stashId = $parts[0]
    }

    git stash apply ''$stashId''

    if ($drop) {
        git stash drop ''$stashId''
    }
}

More details here


This is how you do it:

git stash push -m "my_stash"

Where "my_stash" is the stash name.

Some more useful things to know: All the stashes are stored in a stack. Type:

git stash list

This will list down all your stashes.

To apply a stash and remove it from the stash stack, type:

git stash pop stash@{n}

To apply a stash and keep it in the stash stack, type:

git stash apply stash@{n}

Where n is the index of the stashed change.


Use a small bash script to look up the number of the stash. Call it "gitapply":

NAME="$1"
if [[ -z "$NAME" ]]; then echo "usage: gitapply [name]"; exit; fi
git stash apply $(git stash list | grep "$NAME" | cut -d: -f1)

Usage:

gitapply foo

...where foo is a substring of the name of the stash you want.


git stash apply also works with other refs than stash@{0}. So you can use ordinary tags to get a persistent name. This also has the advantage that you cannot accidentaly git stash drop or git stash pop it.

So you can define an alias pstash (aka "persistent stash") like this:

git config --global alias.pstash '!f(){ git stash && git tag "$1" stash && git stash drop; }; f'

Now you can create a tagged stash:

git pstash x-important-stuff

and show and apply it again as usual:

git stash show x-important-stuff
git stash apply x-important-stuff

I have these two functions in my .zshrc file:

function gitstash() {
    git stash push -m "zsh_stash_name_$1"
}

function gitstashapply() {
    git stash apply $(git stash list | grep "zsh_stash_name_$1" | cut -d: -f1)
}

Using them this way:

gitstash nice

gitstashapply nice