Как назвать и получить тайник по имени в git?

1423

У меня всегда было впечатление, что вы можете дать тайнику имя, сделав git stash save stashname, что позже вы могли бы применить git stash apply stashname. Но, похоже, что в этом случае все, что происходит, stashnameбудет использоваться в качестве описания тайника.

Нет ли способа на самом деле назвать тайник? Если нет, что бы вы порекомендовали для достижения эквивалентной функциональности? По сути, у меня есть небольшой тайник, который я хотел бы периодически применять, но не хочу всегда охотиться за git stash listтем, каков его фактический номер тайника.

Суан
источник
68
git stash push -m stashnameявляется текущим синтаксисом . git stash save stashnameбыл объявлен устаревшим
ШерилХоман
1
git stash push -m stashname не работает в 2.8.0.windows.1.
Jac
Git для Windows 2.26.0 выпущен несколько дней назад. Может быть, это сейчас исправлено. github.com/git-for-windows/git/releases/tag/v2.26.0.windows.1
tom_mai78101

Ответы:

818

Вот как вы это делаете:

git stash save "my_stash"

Где "my_stash"имя тайника.

Еще несколько полезных вещей: все тайники хранятся в стеке. Тип:

git stash list

Это перечислит все ваши тайники.

Чтобы применить тайник и удалить его из стека, введите:

git stash pop stash@{n}

Чтобы применить тайник и сохранить его в стеке, введите:

git stash apply stash@{n}

Где nнаходится индекс скрытого изменения.

Шри Мурти Упадхьяюла
источник
88
Это не отвечает на вопрос. По умолчанию у вас есть куча цифр для вашего тайника, но это не дает ответа на вопрос, как можно легко идентифицировать имя.
GoodSp33d
16
OP явно пытается избежать неуклюжих имен stash @ {n} для пользовательских имен. git stash apply <custom-name>
stewSquared
10
Не отвечает на вопрос о получении тайника по имени.
nullsteph
47
git stash push -m my_stashявляется текущим синтаксисом . git stash save my_stashбыл объявлен устаревшим
ШерилХоман
21
Это не имеет значения. Это полезно.
Гаян Виракутти
444

git stash saveявляется устаревшим , как из 2.15.x / 2.16, вместо этого вы можете использоватьgit stash push -m "message"

Вы можете использовать это так:

git stash push -m "message"

где «сообщение» - это ваша заметка для этого тайника.

Для того , чтобы восстановить тайник , вы можете использовать: git stash list. Это выведет такой список, например:

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

Тогда вы просто используете, applyдавая ему stash@{index}:

git stash apply stash@{1}

Справочная страница по git stash

EssaidiM
источник
9
документы, показывающие, pushа не saveсинтаксис: git stash push
SherylHohman
30
Это настоящий ответ. К сожалению, есть тонна старых ответов над ним.
Малан
1
Подробнее о новой версии git stash push: stackoverflow.com/a/47231547/6309
VonC
источник (в последнем текущем документе) для уведомления об устаревании: git-scm.com/docs/git-stash/2.24.0#Documentation/…
Габриэль Devillers
1
FWIW: Работая git stash apply stash@{1}в Powershell, вы получите error: unknown switch 'e'ответ. Вместо этого используйте git stash apply --index 1or git stash apply 'stash@{1}'или escape }и {с обратной чертой `.
LosManos
105

Вы можете превратить тайник в ветку, если считаете, что это достаточно важно:

git stash branch <branchname> [<stash>]

со страницы руководства:

Это создает и проверяет новую ветвь с именем, <branchname>начиная с коммита, в котором он <stash>был изначально создан, применяет записанные изменения <stash>к новому рабочему дереву и индексу, а затем удаляет <stash>if, если оно успешно завершается. Если нет <stash>, применяется последний.

Это полезно, если ветвь, в которой вы работали git stash save, изменилась настолько, что git stash не работает из-за конфликтов. Поскольку хранилище применяется поверх коммита, который был HEAD во время запуска git stash, он восстанавливает исходное сохраненное состояние без конфликтов.

Позже вы можете переместить эту новую ветку в другое место, которое является потомком того места, где вы находились, когда прятались.

Адам Димитрук
источник
1
Поскольку ветки в git довольно дешевы, это предложение наиболее полезно для меня.
Джаян
5
Конечно, но это не поможет, если вы захотите снова применить этот тайник в разных ветках позже, как просит ОП. Тебе бы пришлось вишнёвать его голову.
stewSquared
@AdamDymitruk Есть ли способ сделать это, сохраняя при этом тайник без всплывающих окон. (как в git stash apply)
Касун Сиямбалапития
Как ни странно, когда я попробовал это, я получил сообщение об ошибке, что один из моих файлов будет перезаписан при проверке, и я должен зафиксировать или сохранить (!) Свои изменения. git stash push -m 'name'работал.
Вортварт
@AdamDymmitruk потрясающий ответ. взорвал мой разум.
Дан
77

Если вы просто ищете легкий способ сохранить некоторые или все текущие изменения рабочей копии, а затем повторно применить их по желанию, рассмотрите файл исправления:

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

# re-apply it later
git apply some.patch

Время от времени я задаюсь вопросом, должен ли я использовать тайники для этого, и тогда я вижу такие вещи, как безумие выше, и я доволен тем, что я делаю :)

Пэт Нимейер
источник
2
Это оно! Спасибо. Я также обновил свой .gitignore, чтобы он игнорировал файлы .patch, и у меня все настроено так много патчей, сколько я хочу.
LINGS
Я вижу смысл вопроса, который заключается в том, чтобы применить некоторые локальные изменения каждый раз, когда вы извлекаете ветку из master, а не фиксируете их. Таким образом, возможно, вопрос должен быть исправлен, и этот ответ должен был быть принят в качестве решения. Просто так же.
апк
46

Тайники не предназначены для того, чтобы быть постоянными вещами, как вы хотите. Возможно, вам лучше использовать теги коммитов. Создайте вещь, которую вы хотите спрятать. Сделайте коммит из этого. Создайте тег для этого коммита. Затем вернитесь к своей ветке HEAD^. Теперь, когда вы захотите повторно применить этот тайник, вы можете использовать его git cherry-pick -n tagname( -nесть --no-commit).

Лили Баллард
источник
1
Определенно, как этот подход, чувствует себя немного чище, просто где-то named commitтусоваться. Единственное легкое раздражение заключается в том, что он не фиксируется при выборе вишни и остается в diff, что означает, что его нужно будет не регистрировать вручную во время следующего коммита.
Адитья MP
1
Это самый близкий. Я думаю, что я сделаю несколько псевдонимов для этого. Мне не нравится использовать описание в качестве «имени».
stewSquared
Позор, это добавляет к индексу, и вы должны сбросить, кто-то должен исправить --no-stageвариант! Связанный: stackoverflow.com/questions/32333383/…
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
41

использовать, git stash push -m aNameForYourStashчтобы сохранить его. Затем используйте, git stash listчтобы узнать индекс тайника, который вы хотите применить. Затем используйте, git stash pop --index 0чтобы открыть тайник и применить его.

примечание: я использую git версии 2.21.0.windows.1

canbax
источник
1
Ваш ответ номинально будет тем, что будет лучшим ответом, принимая во внимание этот комментарий о текущем синтаксисе дляgit stash {push,save}
Майкл - Где Клэй Ширки
32

У меня есть две функции в моем .zshrcфайле:

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

Используя их таким образом:

gitstash nice

gitstashapply nice
iWheelBuy
источник
Что такое "zsh_stash_name_"?
Сэм Хаслер
1
@SamHasler просто какая-то случайная уникальная строка. Если вы хотите знать, что хранилище было создано с помощью обычного git stash или с этими функциями
iWheelBuy
Элегантное решение для поклонников псевдонимов
suarsenegger
22

Что насчет этого?

git stash save stashname
git stash apply stash^{/stashname}
AdamB
источник
1
Похоже, что- то вроде этого был принятый ответ, но с тех пор был удален.
Майкл - Где Клэй Ширки
Хм, тогда почему он был удален?
AdamB
Я не знаю, так как я не опубликовал ответ и у меня нет 10000 репутации, но я предполагаю, что это как-то связано с комментариями о том, что это не работает: к сожалению, git stash apply stash^{/<regex>}это не работает (это не так на самом деле поиск в тайнике, см. комментарии под принятым ответом ).
Майкл - Где Клей Ширки
это ответ, который вы ищете!
kiedysktos
1
для извлечения я иду 1., git stash listкоторый показывает мне тайники вместе с их связанным индексным номером, я тогда иду 2. git stash apply 0- где 0 - индексный номер, который я бы посмотрел по первой команде
ambidexterous
8

кличка

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

Применение

git sapply "<regex>"

  • совместим с Git для Windows

Редактировать: я придерживался своего первоначального решения, но я понимаю, почему большинство предпочло бы версию Итана Рейснера (выше). Так что просто для записи:

sapply = "!f() { git stash apply \"$(git stash list | grep -E \"$*\" | awk \"{ print $ 1; }\" | sed -n \"s/://;1p\")\"; }; f"
Властимил Овчачик
источник
Использование awk -F: '{print $1}'устранит необходимость в sed полностью. И зачем оборачивать это в функцию? И использование awk -F: -vpat="$*" '$0 ~ pat {print $1}'должно позволить сбросить grep также. Хотя может потребоваться немного другое цитирование для шаблона.
Этан Рейснер
@EtanReisner: ваш фрагмент выводит более одной строки.
Властимил Овчачик
Выполните действие, {print $1; exit}чтобы выйти после первой соответствующей строки.
Этан Рейснер
@EtanReisner: После некоторого тестирования я мог избавиться от sed, но оболочка и grep остались.
Властимил Овчачик
Вам не нужен grep, хотя, как я уже сказал, цитирование шаблона может отличаться без него. Я предполагаю, что под оболочкой вы имеете в виду функцию оболочки? Вы никогда не объясняли, почему вы думаете, что вам это нужно, поэтому я не могу комментировать, действительно ли вы это делаете, но я полагаю, что вы, скорее всего, этого не сделаете. (Возможно, вам придется вручную вызывать оболочку вместо git stash напрямую, но, возможно, даже не это.)
Etan Reisner
8

К сожалению, git stash apply stash^{/<regex>}это не работает (на самом деле он не ищет в тайнике, см. Комментарии под принятым ответом ).

Вот вставные замены, которые ищут git stash listпо регулярному выражению, чтобы найти первое (самое последнее), stash@{<n>}а затем передать это 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")

Обратите внимание, что возвращаются правильные коды результатов, поэтому вы можете использовать эти команды в других сценариях. Это можно проверить после запуска команд с:

echo $?

Просто будьте осторожны с эксплойтами расширения переменных, потому что я не был уверен в --grep=$1части. Возможно, так и должно быть, --grep="$1"но я не уверен, что это помешает разделителям регулярных выражений (я открыт для предложений).

Зак Моррис
источник
6

Этот ответ многим обязан Клемену Славичу. Я бы только прокомментировал принятый ответ, но мне пока не хватает представителя :(

Вы также можете добавить псевдоним git, чтобы найти ссылку на stash и использовать ее в других псевдонимах для show, apply, drop и т. Д.

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

Обратите внимание, что причина для ref=$( ... ); echo ${ref:-<no_match>};шаблона в том, что пустая строка не возвращается, что приведет к тому, что sshow, sapply и sdrop нацелятся на последний тайник вместо сбоя, как и следовало ожидать.

Нафанаил
источник
1
Это работает для меня, в то время как принятый ответ, кажется, не работает (см. Мой комментарий по принятому ответу)
Jan Rüegg
4

Псевдоним. Это может быть более прямой синтаксис для Unix-подобных систем без необходимости инкапсуляции в функцию. Добавьте следующее в ~ / .gitconfig под [alias]

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

Использование: Саппи регулярное выражение

Пример: git sshow MySecretStash

Дефис в конце говорит, что берут данные из стандартного ввода.

Rohanthewiz
источник
4

Используйте маленький скрипт bash, чтобы найти номер тайника. Назовите это "бесполезным"

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

Применение:

gitapply foo

... где foo является подстрокой имени тайника, который вы хотите.

Уилл Шеппард
источник
3

Используйте, git stash save NAMEчтобы сохранить.

Затем ... вы можете использовать этот скрипт, чтобы выбрать, какой из них применить (или всплыть):

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

Мне нравится видеть имена тайников и выбирать. Также я использую Zshell и, честно говоря, не знал, как использовать некоторые из псевдонимов Bash выше;)

Примечание: как говорит Кевин, вы должны использовать вместо этого теги и черри.

Дэн Розенстарк
источник
git stash saveне рекомендуется в пользу git stash push.
wranvaud
2

Это один из способов сделать это с помощью 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''
    }
}

Подробнее здесь

Джеффри Худик
источник
2

в моей рыбной раковине

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

использование

gsap name_of_stash

Мацумото Казуя
источник
Да! Спасибо!!!
клозах
1

Здесь поздно вечером, но если вы используете VSCode, быстрый способ сделать это - открыть палитру команд (CTRL / CMD + SHIFT + P) и набрать «Pop Stash», вы сможете получить свой тайник по имени без необходимости использовать git CLI

Александр Гомес
источник
1

git stash applyтакже работает с другими ссылками, чем stash@{0}. Таким образом, вы можете использовать обычные теги, чтобы получить постоянное имя. Это также имеет то преимущество, что вы не можете случайно git stash dropили git stash popэто.

Таким образом, вы можете определить псевдоним pstash(так называемый «постоянный тайник») следующим образом:

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

Теперь вы можете создать помеченный тайник:

git pstash x-important-stuff

и showи applyэто снова , как обычно:

git stash show x-important-stuff
git stash apply x-important-stuff
AH
источник
0

Я не думаю, что есть способ сделать сундук с его именем.

Я создал функцию Bash, которая делает это.

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

Пример использования:

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

Я надеюсь, что это помогает!

Франциска
источник
0

Для всего, кроме создания тайника , я бы предложил другое решение, введя fzf в качестве зависимости. Я рекомендую потратить 5 минут вашего времени и познакомиться с ним, так как это в целом повышает производительность.

Во всяком случае, соответствующая выдержка из их страницы с примерами, предлагающей поиск тайника. Очень легко изменить скриптлет, чтобы добавить дополнительную функциональность (например, приложение stash или удаление):

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
источник
0

Так что я не уверен, почему по этой теме так много потрясений. Я могу назвать git stash как push, так и устаревшим сохранением, и я могу использовать регулярное выражение, чтобы вернуть его с помощью apply:

Git stash метод, чтобы использовать имя для применения

$ git stash push -m "john-hancock"

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

Как уже упоминалось ранее, команда сохранения устарела, но она все еще работает, поэтому вы можете использовать ее на старых системах, где вы не можете обновить их с помощью push-вызова. В отличие от команды push, ключ -m не требуется при сохранении.

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

Это Git 2.2 и Windows 10.

Визуальное доказательство

Вот красивый анимированный GIF, демонстрирующий процесс.

Анимированный GIF, показывающий git stash, применяется с использованием идентифицируемого имени.

Цепочка событий

GIF работает быстро, но если вы посмотрите, процесс выглядит так:

  1. Команда ls показывает 4 файла в каталоге
  2. touch example.html добавляет 5-й файл
  3. git stash push -m "Джон-Хэнкок" -a (-a включает неотслеживаемые файлы)
  4. Команда ls показывает 4 файла после тайника, что означает, что тайник и неявный hard reset сработали
  5. git stash применить stash ^ {/ john-hancock} работает
  6. Команда ls перечисляет 5 файлов, показывая, что файл example.html был возвращен, что означает, что команда git stash apply сработала.

Имеет ли это смысл?

Честно говоря, я не уверен, какая польза от этого подхода. Есть смысл дать тайнику имя, но не поиск. Может быть, было бы полезно написать сценарий для полок и небрежных процессов, но все равно проще просто скопировать тайник по имени.

$ git stash pop 3
$ git stash apply 3

Это выглядит намного проще для меня, чем регулярное выражение.

Кэмерон МакКензи
источник