如何按名称命名和检索Git存储?

如何保存/应用有名称的存储?我不想在git stash list中查找它的索引号。我尝试了git stash save "my_stash_name",但这只会更改存储描述,相应的git apply "my_stash_name"不起作用。

953563 次浏览

隐藏并不意味着像你想要的那样是永久性的。你可能会更好地使用提交上的标签。构造你想要隐藏的东西。用它做一个提交。为该提交创建一个标签。然后将你的分支回滚到HEAD^。现在当你想重新应用那个存储时,你可以使用git cherry-pick -n tagname-n--no-commit)。

你可以把一个stash变成一个分支,如果你觉得它足够重要:

git stash branch <branchname> [<stash>]

从手册页:

这将从最初创建<stash>的提交开始创建并签出一个名为<branchname>的新分支,将<stash>中记录的更改应用于新的工作树和索引,然后如果成功完成则删除<stash>。当没有给出<stash>时,应用最新的一个。

如果您运行git stash save的分支发生了足够大的变化,以至于git stash应用由于冲突而失败,这很有用。由于stash应用于运行git stash时HEAD的提交之上,因此它会恢复最初的隐藏状态而没有冲突。

您可以稍后将此新分支重新定位到其他位置,这是您藏匿时所在位置的后代。

保存一个包含消息的stash:

git stash push -m "my_stash_name"

列表藏匿:

git stash list

所有的存储都存储在一个堆栈中。


应用和删除n藏匿:

git stash pop stash@{n}

应用和删除按名称存储:

git stash pop stash^{/my_stash_name}

适用n藏匿:

git stash apply stash@{n}

适用按名称存储:

git stash apply stash^{/my_stash_name}

别名

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

用法

git sapply "<regex>"

  • 与Git for Windows兼容

编辑:我坚持我最初的解决方案,但我明白为什么大多数人更喜欢Etan Reisner的版本(上图)。所以只是为了记录:

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

别名对于类Unix系统来说,这可能是一种更直接的语法,而无需封装在函数中。将以下内容添加到[alias]

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

用法:应用regex

示例:git sshow我的秘密存储

最后的连字符表示从标准输入中获取输入。

对于除了存储创建之外的所有内容,我提出了另一个解决方案,将fzf作为依赖项引入。我建议花5分钟的时间去了解它,因为它总体上是一个很好的生产力助推器。

无论如何,他们提供的示例页面藏匿搜索的相关摘录。更改脚本以添加附加功能(如藏匿应用程序或删除)非常容易:

fstash() {local out q k shawhile 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); domapfile -t out <<< "$out"q="${out[0]}"k="${out[1]}"sha="${out[-1]}"sha="${sha%% *}"[[ -z "$sha" ]] && continueif [[ "$k" == 'ctrl-d' ]]; thengit diff $shaelif [[ "$k" == 'ctrl-b' ]]; thengit stash branch "stash-$sha" $shabreak;elsegit stash show -p $shafidone}

使用git stash save NAME保存。

然后……您可以使用此脚本选择要应用(或弹出)的脚本:

#!/usr/bin/env ruby#git-stash-pick by Dan Rosenstark
# can take a command, default is applycommand = ARGV[0]command = "apply" if !commandARGV.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.chompif input.to_i.to_s == inputrealIndex = input.to_i - 1puts "\n\nDoing #{command} to #{stashNames[realIndex]}\n\n"puts `git stash #{command} #{stashes[realIndex]}`end

我喜欢能够看到藏匿的名称并进行选择。我也使用Zshell,坦率地说,我不知道如何使用上面的一些Bash别名;)

注:正如凯文所说,你应该使用标签和樱桃选择代替。

这是使用PowerShell完成此操作的一种方法:

<#.SYNOPSISRestores (applies) a previously saved stash based on full or partial stash name.
.DESCRIPTIONRestores (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 messageA 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 dropIf -drop is specified, the matching stash is dropped after being applied.
.EXAMPLERestore-Stash "Readme change"Apply-Stash MyStashNameApply-Stash MyStashName -dropApply-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''}}

更多的信息在这里

如果您只是在寻找一种轻量级的方法来保存部分或全部当前工作副本更改,然后随意重新应用它们,请考虑使用补丁文件:

# save your working copy changesgit diff > some.patch
# re-apply it latergit apply some.patch

时不时地,我想知道我是否应该为此使用藏匿,然后我看到上面的疯狂,我对我正在做的事情感到满意:)

这个答案很大程度上归功于Klemen Slavič。我本来会对接受的答案发表评论,但我还没有足够的代表:(

您还可以添加一个git别名来查找stash ref并在其他别名中使用它来显示、应用、删除等。

[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、sApplication和sdrop以最新的存储为目标,而不是像预期的那样失败。

使用一个小bash脚本来查找藏匿的编号。称之为“git应用”:

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

用法:

gitapply foo

…其中foo是您想要的藏匿名称的子字符串。

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-spikestash@{1}: On develop: node v10

然后你只需使用apply给它stash@{index}

git stash apply 1

参考资料git stash man page

我的.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 nicegitstashapply nice

不幸的是,git stash apply stash^{/<regex>}不起作用(它实际上并没有搜索藏匿列表,请参阅接受的答案下的注释)。

以下是通过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_stashmyfile.txt | 1 +1 file changed, 1 insertion(+)
$ git sapply my_stashOn branch masterYour 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",但我不确定这是否会干扰正则表达式分隔符(我愿意接受建议)。

迟到了,但是如果使用VSCode,一个快速的方法是打开命令面板(CTRL/CMD+SHIFT+P)并键入“Pop Stash”,您将能够按名称检索您的存储,而无需使用git CLI

在我的鱼壳里

function gsapgit stash list | grep ": $argv" | tr -dc '0-9' | xargs git stash applyend

使用

GSAPname_of_stash

git stash apply也适用于stash@{0}以外的其他引用。所以你可以使用普通的标签来获得一个持久的名称。这也有一个优点,你不能意外地git stash dropgit stash pop它。

所以你可以像这样定义一个别名pstash(又名“持久存储”):

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

现在你可以创建一个标记的藏匿:

git pstash x-important-stuff

showapply又像往常一样:

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

这个怎么样?

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

使用git stash push -m aNameForYourStash保存它。然后使用git stash list了解您要应用的藏匿索引。然后使用git stash pop --index 0弹出存储并应用它。

注意:我正在使用git版本2.21.0.windows.1

我不认为有一种方法可以通过它的名字来git pop一个藏匿点。

我创建了一个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 liststash@{0}: On master: here the stash name
[~/code/site] on master$ gstashpop "here the stash name"

希望有帮助!

所以,我不知道为什么在这个话题上有这么多惊愕。我可以名称a#0同时使用push和已弃用的save,我可以使用正则表达式用apply将其拉回来:

使用名称应用的Git stash方法

$ git stash push -m "john-hancock"
$ git stash apply stash^{/john-hancock}

如前所述,save命令已被弃用,但它仍然有效,因此您可以在无法使用push调用更新它们的旧系统上使用它。与push命令不同,save不需要-m开关。

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

这是Git 2.2和Windows 10。

视觉证明

这是一个美丽的动画GIF演示过程。

使用可识别名称显示git stash应用程序的动画GIF。

事件顺序

GIF运行得很快,但如果你看一下,过程是这样的:

  1. ls命令显示目录中的4个文件
  2. touch example.html添加第5个文件
  3. git stash push -m "john-hancock" -a-a包括未跟踪的文件)
  4. ls命令在存储后显示4个文件,这意味着存储和隐式硬重置有效
  5. git stash apply stash^{/john-hancock}运行
  6. ls命令列出了5个文件,显示example.html文件被带回,这意味着git stash apply命令有效。

这有道理吗?

坦率地说,我不确定这种方法的好处是什么。为藏匿点命名是有价值的,但不是检索。也许编写搁置和取消搁置进程的脚本会有所帮助,但按名称弹出藏匿点仍然要容易得多。

$ git stash pop 3$ git stash apply 3

对我来说,这看起来比regex容易得多。

这是我做的一个快速设置,为我工作,希望它也能为你工作:

假设我在我的package.json项目文件中有一个自定义/本地脚本,我不想推送到远程存储库

{// ... package.json stuff"scripts": {"custom": "ts-node a_ts_test_file.ts"}}

因此,当我想推送我的分支或类似的东西时,我决定将此更改隐藏起来,并弹出存储直到我的下一次“git推送”。

那么…

  1. 您需要创建一个新的git别名:
# dev is the "stash tag"# To stash current working directorygit config --global alias.sh "stash -m 'dev'"
  1. 您需要编辑您的~.bashrz~.zshrc如果您使用的是zsh或oh-my-zsh,请添加以下别名:
# Apply stash "tagged" $(X) where X is substring of "git stash list" output filtered by output that contains "dev".# I didn't use git stash apply because "dev" tag isn't unique, so it's a need to pop the stash and ensure to create a new one alias set on first stepalias gitsh="git stash pop $(git stash list | grep 'dev' | cut -d ':' -f 1) || echo 'nope'"
  1. 享受🙂

使用标签“dev”推送您的工作目录:git sh从隐藏标记为“dev”的隐藏中提取您的隐藏更改:sitsh

(这是我在五分钟内制作的一个小脚本,对我有用,如果它失败了……修复它!😉)

这里有很多答案,但我相信OP所追求的所需等效功能并没有被任何一个答案或评论完全封装。

通过将git addgit diffgit rmgit reset组合到自定义git命令中,我们可以快速将更改聚合到一个补丁文件中,稍后我们可以轻松地按名称引用该文件:

git-part-demo

以下是上述自定义git命令(也可用于作为要点)-请注意#0标志的使用中使用的命令,它将重置您当前的分支并删除对本地文件的所有更改:

#!/usr/bin/env bash
if [ $# -eq 1 ] ; thenNAME=$1elseecho "Please pass exactly one argument, which is the name of the patch file"exit 1fi
git add .
# if previous patch file with the same name exists untrack itif [ -f "$NAME.patch" ] ; thengit rm --cached $NAME.patchfi
# warning: this will diff all changes into a file called NAME.patch and do a hard reset of the current branch
git diff --staged > $NAME.patchgit reset --hard $HEAD
  • 您现在可以简单地执行git bottle hello来创建hello.patch文件。
  • git apply hello.patch应用它

诀窍是首先跟踪所有文件,以便我们可以利用diff命令的分阶段(或缓存)选项。通过一些调整,您可以扩展自定义命令以将补丁文件输出到工作目录之外的某个地方,即可能在硬盘驱动器上的某个Patches文件夹中,或者您可以更新.gitignore文件以忽略它。

归功于:这个答案启发了我自己的描述补丁方法,但忽略了提及新文件的更改将被排除在diff显示之外。

警告:由于此命令依赖于git add,因此不会限制git已经忽略的任何文件的更改。

保存一个带有名称的git stash

$ git stash push -m "say-my-name"

按名称执行git stash应用程序

$ git stash apply stash^{/say-my-name}

Stash可以使用以下命令自定义评论

PS D:\git-example> git stash -m "your comment"

列表藏匿

PS D:\git-exapmle> git stash list
stash@{0}: On master: first stashstash@{1}: On master: second stash

我们可以选择任何藏匿点,我们必须通过藏匿点@{x},我选择第二个藏匿点,即1。

PS D:\git-example> git stash pop 1

这里是我的社区别名:wipwip-apply。当您git wip时,您还会隐藏未跟踪的文件并返回到之前的提交状态。

git config --global alias.wip '!f() { git stash save $1 -u ; }; f'
git config --global alias.wip-apply '!f() { temp=$(git stash list | cut -d ':' -f 3 | grep -n -w $1 | cut -d ':' -f 1) ; stashnum=$((temp-1)) ; stashname=$(echo stash@{$stashnum}) ; git stash apply $stashname ; }; f'

用法:

git wip "featureA"git wip-apply "featureA"

如果你使用的是ZSH,这个别名组合是相当致命的:

zstyle ':completion:*' completer _expand_alias _complete _ignoredalias gs="git stash push -u -m "alias gsp='git stash pop'

基本上,您可以使用选项卡自动完成您的别名,然后您可以轻松地按名称命名和搜索您的git藏匿。此推送别名还将包括任何未跟踪的文件,我发现这作为默认值很有帮助。

我怀疑如果你使用了太多的藏匿(比如超过三个),那么你就做错了:存储通常用于工作中断,而不是用于实现功能(您将使用功能分支来实现)。

假设您正在处理某个功能A,然后您发现一些必须修复的问题B(以实现功能A)。你可以这样做:

  1. git add --interactivepatch功能A的部分,忽略问题B的修复。
  2. git commit交互式选择到当前分支。
  3. git stash未提交的更改(修复问题B)
  4. 返回主分支或主分支,可能检查出一个新分支来修复问题B。
  5. git stash pop修复当前分支中的问题B并提交它们。如果存储需要手动合并,可能会git stash drop
  6. 回到功能A分支并将其重新建立在具有问题B修复的分支上。然后你就没有更多的藏匿了,但在不同的分支上仍然有功能A和问题B的修复。

您也可以先提交问题B的修复,然后隐藏功能A的更改,但您明白了。