Como nomear e recuperar um esconderijo pelo nome no git?

1421

Eu sempre tive a impressão de que você poderia dar um nome a um stash fazendo git stash save stashname, que você poderia aplicar mais tarde git stash apply stashname. Mas parece que, neste caso, tudo o que acontece é que stashnameserá usado como a descrição do stash.

Não há como realmente nomear um esconderijo? Caso contrário, o que você recomendaria para obter funcionalidade equivalente? Essencialmente, eu tenho um pequeno estoque que gostaria de aplicar periodicamente, mas não quero sempre ter que caçar git stash listqual é o seu número real.

Suan
fonte
68
git stash push -m stashnameé a sintaxe atual . git stash save stashnamefoi descontinuado.
SherylHohman
1
O git stash push -m stashname não funciona no 2.8.0.windows.1.
Jac
O Git para Windows 2.26.0 foi lançado há alguns dias. Talvez agora esteja consertado. github.com/git-for-windows/git/releases/tag/v2.26.0.windows.1
tom_mai78101

Respostas:

817

É assim que se faz:

git stash save "my_stash"

Onde "my_stash"está o nome do esconderijo.

Algumas coisas mais úteis a saber: Todos os stashes são armazenados em uma pilha. Tipo:

git stash list

Isso listará todos os seus esconderijos.

Para aplicar um stash e removê-lo da pilha stash, digite:

git stash pop stash@{n}

Para aplicar um stash e mantê-lo na pilha stash, digite:

git stash apply stash@{n}

Onde nestá o índice da alteração oculta.

Sri Murthy Upadhyayula
fonte
88
Isso não responde à pergunta. Por padrão, você acaba com vários números para o seu esconderijo, mas isso não responde como você pode colocar um nome para se identificar facilmente.
GoodSp33d
16
O OP está tentando explicitamente evitar os nomes stash @ {n} desajeitados para o nome personalizado. git stash apply <custom-name>
precisa saber é o seguinte
10
Não responde à pergunta sobre a recuperação de um esconderijo pelo nome.
nullsteph
47
git stash push -m my_stashé a sintaxe atual . git stash save my_stashfoi descontinuado.
SherylHohman
21
Não é irrelevante. É útil.
Gayan Weerakutti
444

git stash savefoi descontinuado a partir de 2.15.x / 2.16; em vez disso, você pode usargit stash push -m "message"

Você pode usá-lo assim:

git stash push -m "message"

onde "mensagem" é sua nota para esse esconderijo.

A fim de recuperar o estoque você pode usar: git stash list. Isso produzirá uma lista como esta, por exemplo:

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

Então você simplesmente usa applyo seguinte stash@{index}:

git stash apply stash@{1}

Referências página de manual git stash

EssaidiM
fonte
9
docs mostrando pushao invés de savesintaxe: empurrar git stash
SherylHohman
30
Esta é a verdadeira resposta. Infelizmente, existem muitas respostas antigas acima.
malan
1
Para git stash push
saber
fonte (no documento atual mais recente) do aviso de descontinuação: git-scm.com/docs/git-stash/2.24.0#Documentation/…
Gabriel Devillers
1
FWIW: Ao executar git stash apply stash@{1}no Powershell, você receberá uma error: unknown switch 'e'resposta. Em vez disso, use git stash apply --index 1ou git stash apply 'stash@{1}'ou escape }e {com um backtick `.
LosManos
104

Você pode transformar um esconderijo em um galho, se achar que é importante o suficiente:

git stash branch <branchname> [<stash>]

na página do manual:

Isso cria e efetua o check-out de uma nova ramificação denominada <branchname>a partir da confirmação na qual a <stash>criação original foi criada, aplica as alterações registradas na <stash>nova árvore e índice de trabalho e elimina a opção <stash>se for concluída com êxito. Quando não <stash>é dado, aplica o mais recente.

Isso é útil se a ramificação na qual você executou git stash savemudou o suficiente para que a aplicação do git stash falhe devido a conflitos. Como o stash é aplicado na parte superior da confirmação que era HEAD no momento em que o git stash foi executado, ele restaura o estado originalmente stashed sem conflitos.

Mais tarde, você pode refazer esse novo ramo para outro local que descende de onde estava quando se escondeu.

Adam Dymitruk
fonte
1
Como os galhos são muito baratos no git, essa sugestão é mais útil para mim.
Jayan 29/01
5
Claro, mas isso não ajuda se você quiser continuar reaplicando esse stash em diferentes ramos mais tarde, como o OP está pedindo. Você teria que escolher a cabeça dele.
precisa saber é o seguinte
@AdamDymitruk Existe alguma maneira de fazer isso mantendo o estoque sem aparecer. (like in git stash apply)
Kasun Siyambalapitiya
Estranhamente, quando tentei isso, recebi uma mensagem de erro informando que um dos meus arquivos seria substituído durante o check-out e que eu deveria confirmar ou esconder (!) Minhas alterações. git stash push -m 'name'trabalhou.
wortwart 31/03
@AdamDymmitruk amazing reply. explodiu minha mente.
Dan
77

Se você está apenas procurando uma maneira leve de salvar algumas ou todas as alterações atuais na sua cópia de trabalho e depois reaplicá-las à vontade, considere um arquivo de patch:

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

# re-apply it later
git apply some.patch

De vez em quando eu me pergunto se eu deveria usar esconderijos para isso e depois vejo coisas como a loucura acima e estou contente com o que estou fazendo :)

Pat Niemeyer
fonte
2
É isso! Obrigado. Também atualizei meu arquivo .gitignore para ignorar arquivos .patch e estou pronto para ter quantos patches desejar.
lings
Eu posso ver a intenção por trás da pergunta, que é aplicar algumas alterações locais toda vez que você retira uma ramificação do mestre e não as submete. Portanto, talvez a pergunta deva ter sido corrigida e essa resposta tenha sido aceita como a solução. Simples também.
ank 18/02
46

Os esconderijos não devem ser permanentes como você deseja. Você provavelmente seria melhor servido usando tags em confirmações. Construa o que você deseja esconder. Faça um commit com isso. Crie uma tag para esse commit. Em seguida, reverta sua ramificação paraHEAD^ . Agora, quando você quiser reaplicar esse esconderijo, poderá usar git cherry-pick -n tagname( -né --no-commit).

Lily Ballard
fonte
1
Definitivamente como essa abordagem, parece um pouco mais limpo named commitficar em algum lugar. Apenas um aborrecimento leve é ​​que ele não é confirmado na seleção de cereja e permanece no diff, o que significa que precisará ser manualmente não verificado durante a próxima confirmação.
Aditya MP
1
Este é o mais próximo. Eu acho que vou fazer alguns apelidos para isso. Não gosto de usar a descrição como um "nome".
stewSquared
Pena que isso aumenta o índice e você precisa redefinir, alguém deve corrigir uma --no-stageopção! Relacionado: stackoverflow.com/questions/32333383/…
Ciro Santilli escreveu
41

use git stash push -m aNameForYourStashpara salvá-lo. Em seguida, use git stash listpara aprender o índice do stash que você deseja aplicar. Em seguida, use git stash pop --index 0para abrir o esconderijo e aplicá-lo.

note: estou usando a versão 2.21.0.windows.1 do git

canbax
fonte
1
A sua resposta é, nominalmente, qual é a resposta mais votadas seria, tendo em conta este comentário sobre a sintaxe atual paragit stash {push,save}
Michael - Onde está Clay Shirky
32

Eu tenho essas duas funções no meu .zshrcarquivo:

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)
}

Utilizando-os desta maneira:

gitstash nice

gitstashapply nice
iWheelBuy
fonte
O que é "zsh_stash_name_"?
Sam Hasler
1
@ SamHasler apenas uma sequência única aleatória. No caso de você querer saber o esconderijo foi criado com git stash regular ou com estas funções
iWheelBuy
Solução elegante para fãs de pseudônimo
suarsenegger 21/03
22

Que tal isso?

git stash save stashname
git stash apply stash^{/stashname}
AdamB
fonte
1
Ele soa como algo como isso costumava ser a resposta aceita, mas desde então tem sido excluído.
Michael - de onde Clay Shirky
Hm, então por que foi excluído?
AdamB
Não sei, já que não postei a resposta e não tenho 10.000 reputação, mas presumo que isso tenha algo a ver com os comentários dizendo que não funciona: é lamentável que git stash apply stash^{/<regex>}não funcione (não funciona) pesquise na lista stash, veja os comentários na resposta aceita ).
Michael - de onde Clay Shirky
Esta é a resposta que você está procurando!
Kryysktos 24/10/19
1
para recuperar eu vou 1. git stash listque me mostra os stashes junto com o número de índice associado, depois vou 2. git stash apply 0- onde 0 é o número de índice que eu teria procurado desde o primeiro comando
ambidestro
8

Alias

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

Uso

git sapply "<regex>"

  • compatível com Git para Windows

Edit: Aderi à minha solução original, mas vejo por que a maioria prefere a versão de Etan Reisner (acima). Então, só para constar:

sapply = "!f() { git stash apply \"$(git stash list | grep -E \"$*\" | awk \"{ print $ 1; }\" | sed -n \"s/://;1p\")\"; }; f"
Vlastimil Ovčáčík
fonte
O uso awk -F: '{print $1}'eliminaria totalmente a necessidade do sed. Também por que envolver isso em uma função? E usar também awk -F: -vpat="$*" '$0 ~ pat {print $1}'deve permitir o cancelamento do grep. Embora possa exigir citações ligeiramente diferentes para o padrão.
Etan Reisner 27/11/2013
@EtanReisner: seu snippet gera mais de uma linha.
Vlastimil Ovčáčík
Execute a ação {print $1; exit}para sair após a primeira linha correspondente.
Etan Reisner 27/11
@EtanReisner: Após alguns testes, eu poderia me livrar do sed, mas o wrapper e o grep permanecem.
Vlastimil Ovčáčík
Você não precisa do grep, como eu disse, o padrão de citação pode diferir sem ele. Estou assumindo por invólucro você quer dizer a função shell? Você nunca explicou por que acha que precisa disso, então não posso comentar se realmente precisa, mas acredito que provavelmente não precisa. (Pode ser necessário chamar manualmente um shell em vez do git stash diretamente, mas possivelmente nem isso.)
Etan Reisner
8

É uma pena que git stash apply stash^{/<regex>}isso não funcione (na verdade, ele não pesquisa na lista stash, veja os comentários na resposta aceita ).

Aqui estão as substituições drop-in que pesquisam git stash listpor regex para encontrar o primeiro (mais recente) stash@{<n>}e depois o passam para 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")

Observe que os códigos de resultado adequados são retornados para que você possa usar esses comandos em outros scripts. Isso pode ser verificado após a execução de comandos com:

echo $?

Apenas tome cuidado com explorações de expansão variável, porque eu não tinha certeza sobre a --grep=$1parte. Talvez devesse ser, --grep="$1"mas não tenho certeza se isso interferiria nos delimitadores de expressões regulares (estou aberto a sugestões).

Zack Morris
fonte
6

Esta resposta deve muito a Klemen Slavič. Eu teria acabado de comentar a resposta aceita, mas ainda não tenho representante suficiente :(

Você também pode adicionar um alias do git para encontrar o ref do stash e usá-lo em outros aliases para mostrar, aplicar, soltar 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"

Observe que o motivo do ref=$( ... ); echo ${ref:-<no_match>};padrão é que uma string em branco não seja retornada, o que causaria ao sshow, sapply e sdrop direcionar o stash mais recente em vez de falhar, como seria de esperar.

Natanael
fonte
1
Isso funciona para mim enquanto a resposta aceita não parece trabalho (ver meu recomenda na resposta aceita)
Jan Rüegg
4

Alias Essa pode ser uma sintaxe mais direta para sistemas similares ao Unix, sem a necessidade de encapsular uma função. Adicione o seguinte a ~ / .gitconfig em [alias]

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

Uso: sapply regex

Exemplo: git sshow MySecretStash

O hífen no final diz receber informações da entrada padrão.

Rohanthewiz
fonte
4

Use um pequeno script bash para procurar o número do stash. Chame de "gitapply":

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

Uso:

gitapply foo

... onde foo é uma subcadeia do nome do esconderijo que você deseja.

Will Sheppard
fonte
3

Usar git stash save NAME para salvar.

Então ... você pode usar este script para escolher qual aplicar (ou 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

Eu gosto de poder ver os nomes dos esconderijos e escolher. Também uso o Zshell e, francamente, não sabia como usar alguns dos aliases do Bash acima;)

Nota: Como Kevin diz, você deve usar tags e cherry picks.

Dan Rosenstark
fonte
git stash savefoi descontinuado em favor de git stash push.
Wranvaud
2

Esta é uma maneira de fazer isso usando o 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''
    }
}

Mais detalhes aqui

Geoffrey Hudik
fonte
2

na minha concha de peixe

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

usar

gsap name_of_stash

Matsumoto Kazuya
fonte
Sim! Obrigado!!!
clozach
1

Tarde para a festa aqui, mas se você estiver usando o VSCode, uma maneira rápida de fazer isso é abrir a paleta de comandos (CTRL / CMD + SHIFT + P) e digitar "Pop Stash", você poderá recuperar o seu stash pelo nome sem a necessidade de usar git CLI

Alexandre Gomes
fonte
1

git stash applytambém funciona com outros árbitros que não stash@{0}. Portanto, você pode usar tags comuns para obter um nome persistente. Isso também tem a vantagem de você não poder acidentalmente git stash dropougit stash pop isso.

Então você pode definir um alias pstash(também conhecido como "stash persistente") como este:

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

Agora você pode criar um stash marcado:

git pstash x-important-stuff

e showe apply-lo novamente como de costume:

git stash show x-important-stuff
git stash apply x-important-stuff
AH
fonte
0

Eu não acho que haja uma maneira de colocar um esconderijo com seu nome.

Eu criei uma função bash que faz isso.

#!/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"
}

Exemplo de uso:

[~/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"

Espero que ajude!

franzisk
fonte
0

Para tudo, além da criação do stash, eu proporia outra solução introduzindo o fzf como uma dependência. Eu recomendo que você dedique 5 minutos do seu tempo e seja apresentado a ele, pois é um grande impulsionador da produtividade.

De qualquer forma, um trecho relacionado da página de exemplos oferece pesquisa de esconderijo. É muito fácil alterar o scriptlet para adicionar funcionalidades adicionais (como aplicação stash ou 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
}
laur
fonte
0

Portanto, não sei por que há tanta consternação nesse tópico. Posso nomear um stash com um push e o save obsoleto, e posso usar um regex para recuperá-lo com uma aplicação:

Método stash Git para usar um nome para aplicar

$ git stash push -m "john-hancock"

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

Como mencionado anteriormente, o comando save foi descontinuado, mas ainda funciona, portanto, você pode usá-lo em sistemas mais antigos, onde não pode atualizá-los com uma chamada por push. Diferente do comando push, a opção -m não é necessária com o save.

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

Problemas com um pop e drop

Não acredito que funcione com um pop, mas acho que é porque o pop executa uma queda após a aplicação e o regex não é amigável com a queda. Isso é apenas um palpite. Eu não testei.

Este é o Git 2.2 e o Windows 10.

Prova Visual

Aqui está um belo GIF animado demonstrando o processo.

GIF animado mostrando um stash git é aplicado usando um nome identificável.

Sequência de eventos

O GIF é executado rapidamente, mas se você olhar, o processo é o seguinte:

  1. O comando ls mostra 4 arquivos no diretório
  2. touch example.html adiciona um quinto arquivo
  3. git stash push -m "john-hancock" -a (O -a inclui arquivos não rastreados)
  4. O comando ls mostra 4 arquivos após o stash, o que significa que o stash e a reinicialização implícita do disco funcionaram
  5. git stash apply stash ^ {/ john-hancock} roda
  6. O comando ls lista 5 arquivos, mostrando que o arquivo example.html foi trazido de volta, o que significa que o comando git stash apply funcionou.

Isso faz algum sentido?

Para ser franco, não tenho certeza de qual é o benefício dessa abordagem. É importante dar um nome ao esconderijo, mas não a recuperação. Talvez para criar um script do processo, seria útil, mas ainda é muito mais fácil colocar um esconderijo por nome.

$ git stash pop 3
$ git stash apply 3

Isso me parece muito mais fácil do que o regex.

Cameron McKenzie
fonte