Comment nommer et récupérer une cachette par son nom dans git?


1423

J'ai toujours eu l'impression que vous pouviez donner un nom à une cachette en faisant git stash save stashname, que vous pourriez ensuite appliquer en faisant git stash apply stashname. Mais il semble que dans ce cas, tout ce qui se passe est celui stashnamequi sera utilisé comme description de la cachette.

N'y a-t-il aucun moyen de nommer une cachette? Sinon, que recommanderiez-vous pour obtenir des fonctionnalités équivalentes? Essentiellement, j'ai une petite cachette que j'aimerais périodiquement appliquer, mais je ne veux pas toujours avoir à chercher git stash listquel est son numéro de cachette réel.


68
git stash push -m stashnameest la syntaxe actuelle . git stash save stashnameest obsolète.
SherylHohman

1
git stash push -m stashname ne fonctionne pas dans 2.8.0.windows.1.
Jac

Git pour Windows 2.26.0 est sorti il ​​y a quelques jours. Peut-être que c'est maintenant corrigé. github.com/git-for-windows/git/releases/tag/v2.26.0.windows.1
tom_mai78101

Réponses:


818

Voici comment procéder:

git stash save "my_stash"

"my_stash"est le nom de la cachette.

Quelques choses plus utiles à savoir: Toutes les cachettes sont stockées dans une pile. Type:

git stash list

Cela répertoriera toutes vos réserves.

Pour appliquer une stash et la retirer de la pile de stash, tapez:

git stash pop stash@{n}

Pour appliquer une stash et la conserver dans la pile de stash, tapez:

git stash apply stash@{n}

nest l'indice du changement caché.


88
Cela ne répond pas à la question. Par défaut, vous vous retrouvez avec un tas de chiffres pour votre cachette, mais cela ne répond pas à la façon dont vous pouvez mettre un nom pour l'identifier facilement.
GoodSp33d

16
OP essaie explicitement d'éviter les noms de cachette mal nommés @ {n} pour les noms personnalisés. git stash apply <custom-name>
stewSquared

10
Ne répond pas à la question sur la récupération d'une cachette par son nom.
nullsteph

47
git stash push -m my_stashest la syntaxe actuelle . git stash save my_stashest obsolète.
SherylHohman

21
Ce n'est pas sans importance. C'est utile.
Gayan Weerakutti

444

git stash saveest obsolète à partir de 2.15.x / 2.16, à la place, vous pouvez utilisergit stash push -m "message"

Vous pouvez l'utiliser comme ceci:

git stash push -m "message"

où "message" est votre note pour cette cachette.

Afin de récupérer la planque , vous pouvez utiliser: git stash list. Cela produira une liste comme celle-ci, par exemple:

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

Ensuite, vous utilisez simplement applyen lui donnant stash@{index}:

git stash apply stash@{1}

Références page de manuel de git stash


9
documents montrant pushplutôt que la savesyntaxe: git stash push
SherylHohman

30
Telle est la vraie réponse. Malheureusement, il y a une tonne d'anciennes réponses au-dessus.
malan

1
Pour en savoir plus sur le plus récent git stash push: stackoverflow.com/a/47231547/6309
VonC

source (sur le dernier document actuel) pour l'avis de dépréciation: git-scm.com/docs/git-stash/2.24.0#Documentation/…
Gabriel Devillers

1
FWIW: Lorsque vous exécutez git stash apply stash@{1}dans Powershell, vous obtiendrez un error: unknown switch 'e'dos. Utilisez plutôt git stash apply --index 1ou git stash apply 'stash@{1}'ou escape }et {avec un backtick `.
LosManos

105

Vous pouvez transformer une cachette en branche si vous pensez qu'elle est suffisamment importante:

git stash branch <branchname> [<stash>]

à partir de la page de manuel:

Cela crée et extrait une nouvelle branche nommée à <branchname>partir de la validation à laquelle le <stash>a été créé à l'origine, applique les modifications enregistrées dans <stash>la nouvelle arborescence de travail et l'index, puis supprime le <stash>si cela se termine avec succès. Si non <stash>est donné, applique le dernier.

Cela est utile si la branche sur laquelle vous avez exécuté git stash savea suffisamment changé pour que git stash apply échoue en raison de conflits. Étant donné que le stash est appliqué au-dessus du commit qui était HEAD au moment où git stash a été exécuté, il restaure l'état initialement stashé sans conflits.

Vous pouvez ultérieurement rebaser cette nouvelle branche vers un autre endroit qui est un descendant de l'endroit où vous vous trouviez lorsque vous vous êtes caché.


1
Étant donné que les branches sont assez bon marché en git, cette suggestion est très utile pour moi.
Jayan

5
Bien sûr, mais cela n'aide pas si vous souhaitez continuer à réappliquer cette cachette dans différentes branches plus tard, comme le demande l'OP. Vous auriez à choisir sa tête.
stewSquared

@AdamDymitruk Existe-t-il un moyen d'effectuer cela tout en conservant la cachette sans éclater. (comme dans git stash apply)
Kasun Siyambalapitiya

Étrangement, lorsque j'ai essayé cela, j'ai reçu un message d'erreur indiquant qu'un de mes fichiers serait écrasé lors de la vérification et que je devrais valider ou cacher (!) Mes modifications. git stash push -m 'name'travaillé.
wortwart

@AdamDymmitruk réponse incroyable. a soufflé mon esprit.
Dan

77

Si vous cherchez simplement un moyen léger d'enregistrer tout ou partie de vos modifications de copie de travail actuelles, puis de les réappliquer plus tard à volonté, envisagez un fichier de correctif:

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

# re-apply it later
git apply some.patch

De temps en temps, je me demande si je devrais utiliser des cachettes pour cela, puis je vois des choses comme la folie ci-dessus et je me contente de ce que je fais :)


2
Ça y est ...! Je vous remercie. J'ai également mis à jour mon .gitignore pour ignorer les fichiers .patch et je suis prêt à avoir autant de correctifs que je le souhaite.
LINGS

Je peux voir l'intention derrière la question, qui est d'avoir appliqué des changements locaux chaque fois que vous supprimez une branche de master et ne les validez pas. Donc, peut-être que la question aurait dû être corrigée et cette réponse aurait dû être acceptée comme la solution. Simple aussi.
ank

46

Les cachettes ne sont pas censées être des choses permanentes comme vous le souhaitez. Vous seriez probablement mieux servi en utilisant des balises lors des validations. Construisez la chose que vous voulez cacher. Faites-en un commit. Créez une balise pour ce commit. Faites ensuite reculer votre succursale vers HEAD^. Maintenant, lorsque vous souhaitez réappliquer cette réserve, vous pouvez utiliser git cherry-pick -n tagname( -nest --no-commit).


1
Certainement, comme cette approche, se sent un peu plus propre d'avoir juste un named commitendroit quelque part. Le seul inconvénient léger est qu'il n'est pas engagé lors de la sélection et reste dans le diff, ce qui signifie qu'il ne devra pas être enregistré manuellement lors du prochain commit.
Aditya MP

1
C'est le plus proche. Je pense que je vais faire quelques alias pour cela. Je n'aime pas utiliser la description comme "nom".
stewSquared

Dommage que cela ajoute à l'index et que vous devez réinitialiser, quelqu'un devrait patcher une --no-stageoption! Connexe: stackoverflow.com/questions/32333383/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

41

utiliser git stash push -m aNameForYourStashpour l'enregistrer. Utilisez ensuite git stash listpour connaître l' index de la cachette que vous souhaitez appliquer. Ensuite, utilisez git stash pop --index 0pour éclater la cachette et l'appliquer.

remarque: j'utilise git version 2.21.0.windows.1


1
Votre réponse est nominalement la réponse la mieux notée, en tenant compte de ce commentaire sur la syntaxe actuelle degit stash {push,save}
Michael - Où est Clay Shirky

32

J'ai ces deux fonctions dans mon .zshrcdossier:

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

En les utilisant de cette façon:

gitstash nice

gitstashapply nice

Qu'est-ce que "zsh_stash_name_"?
Sam Hasler

1
@SamHasler juste une chaîne unique aléatoire. Dans le cas où vous voulez savoir que la cachette a été créée avec la cachette git régulière ou avec ces fonctions
iWheelBuy

Solution élégante pour les fans d'alias
suarsenegger

22

Et ça?

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

1
Cela ressemble à quelque chose comme ça qui était la réponse acceptée, mais a depuis été supprimé.
Michael - Où est Clay Shirky

Hm, alors pourquoi il a été supprimé?
AdamB

Je ne sais pas, car je n'ai pas posté la réponse et je n'ai pas 10 000 points de réputation, mais je suppose que cela a quelque chose à voir avec les commentaires disant que ça ne marche pas: c'est malheureux que git stash apply stash^{/<regex>}ça ne marche pas (ça ne marche pas effectuez une recherche dans la liste cachée, voir les commentaires sous la réponse acceptée ).
Michael - Où est Clay Shirky

c'est LA réponse que vous cherchez!
kiedysktos

1
pour récupérer je vais 1. git stash listqui me montre les cachettes avec leur numéro d'index associé, je vais ensuite 2. git stash apply 0- où 0 est le numéro d'index que j'aurais recherché à partir de la première commande
ambidextre le

8

Alias

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

Usage

git sapply "<regex>"

  • compatible avec Git pour Windows

Edit: je suis resté sur ma solution d'origine, mais je vois pourquoi la majorité préférerait la version d'Etan Reisner (ci-dessus). Donc, pour mémoire:

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

L'utilisation awk -F: '{print $1}'éliminerait complètement le besoin de sed. Aussi pourquoi envelopper cela dans une fonction? Et l'utilisation awk -F: -vpat="$*" '$0 ~ pat {print $1}'devrait également permettre de supprimer le grep. Bien que cela puisse nécessiter des devis légèrement différents pour le modèle.
Etan Reisner

@EtanReisner: votre extrait de code génère plusieurs lignes.
Vlastimil Ovčáčík

Effectuez l'action {print $1; exit}pour quitter après la première ligne correspondante.
Etan Reisner

@EtanReisner: Après quelques tests, j'ai pu me débarrasser du sed, mais le wrapper et le grep restent.
Vlastimil Ovčáčík

Vous n'avez pas besoin de la grep bien que, comme je l'ai dit, la citation de modèle puisse différer sans elle. Je suppose que par wrapper vous voulez dire la fonction shell? Vous n'avez jamais expliqué pourquoi vous pensez que vous en avez besoin, je ne peux donc pas dire si vous en avez réellement besoin, mais je pense que vous n'en avez probablement pas. (Vous devrez peut-être appeler manuellement un shell au lieu de git stash directement, mais peut-être même pas.)
Etan Reisner

8

Il est regrettable que git stash apply stash^{/<regex>}cela ne fonctionne pas (il ne recherche pas réellement la liste cachée, voir les commentaires sous la réponse acceptée ).

Voici des remplaçants sans git stash listrendez-vous qui recherchent par expression régulière pour trouver le premier (le plus récent) stash@{<n>}, puis le transmettent à 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")

Notez que les codes de résultat appropriés sont renvoyés afin que vous puissiez utiliser ces commandes dans d'autres scripts. Cela peut être vérifié après l'exécution de commandes avec:

echo $?

Faites juste attention aux exploits d'expansion variable car je n'étais pas sûr de la --grep=$1portion. Cela devrait peut-être l'être, --grep="$1"mais je ne suis pas sûr que cela interfère avec les délimiteurs d'expressions régulières (je suis ouvert aux suggestions).


6

Cette réponse doit beaucoup à Klemen Slavič. J'aurais juste commenté la réponse acceptée mais je n'ai pas encore assez de représentants :(

Vous pouvez également ajouter un alias git pour trouver la référence cachée et l'utiliser dans d'autres alias pour afficher, appliquer, supprimer, 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"

Notez que la raison du ref=$( ... ); echo ${ref:-<no_match>};motif est qu'aucune chaîne vierge n'est renvoyée, ce qui entraînerait sshow, sapply et sdrop à cibler la dernière cachette au lieu d'échouer comme on pourrait s'y attendre.


1
Cela fonctionne pour moi alors que la réponse acceptée ne semble pas fonctionner (voir mon éloge pour la réponse acceptée)
Jan Rüegg

4

Alias Cela pourrait être une syntaxe plus directe pour les systèmes de type Unix sans avoir besoin d'encapsuler dans une fonction. Ajoutez ce qui suit à ~ / .gitconfig sous [alias]

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

Utilisation: regex sapply

Exemple: git sshow MySecretStash

Le tiret à la fin indique prendre l'entrée de l'entrée standard.


4

Utilisez un petit script bash pour rechercher le numéro de la cachette. Appelez ça "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

... où foo est une sous-chaîne du nom de la cachette que vous souhaitez.


3

Utilisez git stash save NAMEpour enregistrer.

Ensuite ... vous pouvez utiliser ce script pour choisir lequel appliquer (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

J'aime pouvoir voir les noms des cachettes et choisir. J'utilise également Zshell et je ne savais franchement pas comment utiliser certains des alias Bash ci-dessus;)

Remarque: Comme Kevin le dit, vous devriez plutôt utiliser des balises et des choix de cerise.


git stash saveest déconseillé en faveur de git stash push.
wranvaud

2

C'est une façon d'y parvenir à l'aide de 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''
    }
}

Plus de détails ici


2

dans ma coquille de poisson

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

utilisation

gsap name_of_stash


Oui! Je vous remercie!!!
clozach

1

Tard dans la soirée ici, mais si vous utilisez VSCode, un moyen rapide de le faire est d'ouvrir la palette de commandes (CTRL / CMD + SHIFT + P) et de taper "Pop Stash", vous pourrez récupérer votre stash par nom sans avoir besoin d'utiliser git CLI


1

git stash applyfonctionne également avec d'autres références que stash@{0}. Vous pouvez donc utiliser des balises ordinaires pour obtenir un nom persistant. Cela a également l'avantage que vous ne pouvez pas accidentellement git stash dropou git stash popcela.

Vous pouvez donc définir un alias pstash(alias "stash persistant") comme ceci:

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

Vous pouvez maintenant créer une cachette balisée:

git pstash x-important-stuff

et showet applyencore , comme d' habitude:

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

0

Je ne pense pas qu'il y ait un moyen de faire sauter une cachette par son nom.

J'ai créé une fonction bash qui le fait.

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

Exemple d'utilisation:

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

J'espère que ça aide!


0

Pour tout autre que la création de stash, je proposerais une autre solution en introduisant fzf comme dépendance. Je recommande de prendre 5 minutes de votre temps et de vous y familiariser, car c'est un excellent booster de productivité.

Quoi qu'il en soit, un extrait connexe de leur page d'exemples proposant une recherche cachée. Il est très facile de changer le scriptlet pour ajouter des fonctionnalités supplémentaires (comme l'application stash ou la suppression):

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
}

0

Donc, je ne sais pas pourquoi il y a tant de consternation sur ce sujet. Je peux nommer une cachette git avec à la fois une poussée et la sauvegarde obsolète, et je peux utiliser une expression régulière pour la retirer avec une application:

Méthode Git Stash pour utiliser un nom à appliquer

$ git stash push -m "john-hancock"

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

Comme cela a été mentionné précédemment, la commande save est déconseillée, mais elle fonctionne toujours, vous pouvez donc l'utiliser sur des systèmes plus anciens où vous ne pouvez pas les mettre à jour avec un appel push. Contrairement à la commande push, le commutateur -m n'est pas requis avec save.

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

Il s'agit de Git 2.2 et de Windows 10.

Preuve visuelle

Voici un beau GIF animé illustrant le processus.

Un GIF animé montrant une cachette Git s'applique en utilisant un nom identifiable.

Séquence d'événements

Le GIF s'exécute rapidement, mais si vous regardez, le processus est le suivant:

  1. La commande ls affiche 4 fichiers dans le répertoire
  2. touch example.html ajoute un 5ème fichier
  3. git stash push -m "john-hancock" -a (Le -a inclut les fichiers non suivis)
  4. La commande ls affiche 4 fichiers après la cachette, ce qui signifie que la cachette et la réinitialisation matérielle implicite ont fonctionné
  5. git stash applique stash ^ {/ john-hancock} s'exécute
  6. La commande ls répertorie 5 fichiers, montrant que le fichier example.html a été ramené, ce qui signifie que la commande git stash apply a fonctionné.

Est-ce même logique?

Pour être franc, je ne sais pas vraiment quel est l'avantage de cette approche. Il est utile de donner un nom à la cachette, mais pas la récupération. Peut-être pour le script du shelve et unshelve processus , il serait utile, mais il est encore plus facile de pop juste une planque par son nom.

$ git stash pop 3
$ git stash apply 3

Cela me semble beaucoup plus facile que l'expression régulière.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.