我一直认为你可以通过做来给一个存储命名git stash save stashname
,你以后可以通过做来申请git stash apply stashname
。但似乎在这种情况下,所发生的一切stashname
都将用作存储描述。
有没有办法真正命名一个藏匿处?如果没有,您会推荐什么来实现等效功能?本质上,我有一个小储藏室,我想定期申请,但不想总是寻找git stash list
它的实际储藏室编号。
这就是你的做法:
git stash push -m "my_stash"
"my_stash"
存储名称在哪里。
一些更有用的事情要知道:所有的存储都存储在一个堆栈中。类型:
git stash list
这将列出你所有的藏匿处。
要应用存储并将其从存储堆栈中删除,请键入:
git stash pop stash@{n}
要应用存储并将其保存在存储堆栈中,请键入:
git stash apply stash@{n}
n
隐藏更改的索引在哪里。
请注意,您可以使用存储名称应用存储并将其保存在堆栈中:
git stash apply my_stash_name
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}
如果您只是在寻找一种轻量级的方法来保存当前工作副本的部分或全部更改,然后在以后随意重新应用它们,请考虑使用补丁文件:
# save your working copy changes
git diff > some.patch
# re-apply it later
git apply some.patch
时不时地我想知道我是否应该为此使用藏匿处,然后我看到上面的疯狂之类的事情,我对我正在做的事情感到满意:)
如果你觉得它足够重要,你可以把一个 stash 变成一个分支:
git stash branch <branchname> [<stash>]
从手册页:
这将创建并签出一个名为<branchname>
从<stash>
最初创建的提交开始的新分支,将记录的更改应用<stash>
到新的工作树和索引,然后删除<stash>
成功完成的 if。如果没有<stash>
给出,则应用最新的。
git stash save
如果您运行的分支发生了足够的变化,以至于 git stash apply 由于冲突而失败,这将很有用。由于 stash 应用在 git stash 运行时的 HEAD 提交之上,因此它恢复了最初的隐藏状态,没有冲突。
您可以稍后将这个新分支重新定位到其他地方,该地方是您藏匿时所在位置的后代。
藏匿处并不意味着像你想要的那样永久存在。在提交时使用标签可能会更好。构建你想要隐藏的东西。做出承诺。为该提交创建一个标签。然后将您的分支回滚到HEAD^
. 现在,当您想重新应用该存储时,您可以使用git cherry-pick -n tagname
( -n
is --no-commit
)。
所以,我不确定为什么这个话题会引起如此多的恐慌。我可以用 a和 deprecated命名 agit stash
,并且可以使用正则表达式将其拉回 a :push
save
apply
$ git stash push -m "john-hancock"
$ git stash apply stash^{/john-hancock}
正如之前提到的,该save
命令已被弃用,但它仍然有效,因此您可以在无法通过push
调用更新它们的旧系统上使用它。与push
命令不同,-m
开关不需要save
.
// save is deprecated but still functional
$ git stash save john-hancock
这是 Git 2.2 和 Windows 10。
这是一个漂亮的动画 GIF,演示了这个过程。
GIF 运行很快,但如果你看,过程是这样的:
ls
命令显示目录中的 4 个文件touch example.html
添加第 5 个文件git stash push -m "john-hancock" -a
(-a
包括未跟踪的文件)ls
命令在存储后显示 4 个文件,这意味着存储和隐式硬重置有效git stash apply stash^{/john-hancock}
运行ls
命令列出了 5 个文件,显示example.html文件被带回,这意味着该git stash apply
命令有效。坦率地说,我不确定这种方法的好处是什么。给存储命名是有价值的,但不是检索。也许编写搁置和取消搁置过程的脚本会有所帮助,但仅按名称弹出一个存储仍然更容易。
$ git stash pop 3
$ git stash apply 3
这对我来说看起来比正则表达式容易得多。
用来git stash push -m aNameForYourStash
保存它。然后用于git stash list
了解您要应用的存储的索引。然后使用git stash pop --index 0
弹出存储并应用它。
注意:我使用的是git 版本 2.21.0.windows.1
我的.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
那这个呢?
git stash save stashname
git stash apply stash^{/stashname}
$ git stash push -m "say-my-name"
$ 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 stash
stash@{1}: On master: second stash
我们可以选择任何存储,我们必须通过 stash@{x},下面我选择第二个存储,即 1。
PS D:\git-example> git stash pop 1
sapply = "!f() { git stash apply \"$(git stash list | awk -F: --posix -vpat=\"$*\" \"$ 0 ~ pat {print $ 1; exit}\")\"; }; f"
git sapply "<regex>"
编辑:我坚持我原来的解决方案,但我明白为什么大多数人更喜欢 Etan Reisner 的版本(上图)。所以只是为了记录:
sapply = "!f() { git stash apply \"$(git stash list | grep -E \"$*\" | awk \"{ print $ 1; }\" | sed -n \"s/://;1p\")\"; }; f"
不幸的是,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"
,但我不确定这是否会干扰正则表达式分隔符(我愿意接受建议)。
别名 对于类 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}"' -
用法:sapply正则表达式
示例: git sshow MySecretStash
最后的连字符表示从标准输入中获取输入。
这个答案很大程度上归功于 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、sapply 和 sdrop 以最新的存储为目标,而不是像预期的那样失败。
使用一个小的 bash 脚本来查找存储的数量。称之为“gitapply”:
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 是您想要的存储名称的子字符串。
用来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 别名;)
注意:正如凯文所说,你应该使用标签和樱桃挑选。
这是我的社区别名:wip
和wip-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"
这里有很多答案,但我相信 OP 所追求的所需等效功能并没有被任何一个答案或评论完全封装。
通过将git add
、git diff
、git rm
和组合git reset
到自定义 git 命令中,我们可以快速将更改聚合到补丁文件中,稍后我们可以通过名称轻松引用该文件:
以下是上述自定义 git 命令中使用的命令(也可作为 gist使用) -请注意使用--hard
标志将重置您当前的分支以及删除对本地文件的所有更改:
#!/usr/bin/env bash
if [ $# -eq 1 ] ; then
NAME=$1
else
echo "Please pass exactly one argument, which is the name of the patch file"
exit 1
fi
git add .
# if previous patch file with the same name exists untrack it
if [ -f "$NAME.patch" ] ; then
git rm --cached $NAME.patch
fi
# 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.patch
git reset --hard $HEAD
git bottle hello
地创建一个hello.patch
文件。git apply hello.patch
诀窍是首先跟踪所有文件,以便我们可以利用 diff 命令的暂存(或缓存)选项。通过一些调整,您可以扩展自定义命令以在工作目录之外的某个位置输出补丁文件,即可能在硬盘驱动器上的某个补丁文件夹中,或者您可以更新.gitignore
文件以忽略它。
应得的信用:这个答案激发了我自己的灵感,它描述了补丁方法,但忽略了新文件中的更改将被排除在差异显示之外。
警告:由于此命令依赖于它,git add
它不会限制 git 已经忽略的任何文件的更改。
这是使用 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''
}
}
在我的鱼壳里
function gsap
git stash list | grep ": $argv" | tr -dc '0-9' | xargs git stash apply
end
利用
gsap name_of_stash
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
迟到了,但如果使用 VSCode,一个快速的方法是打开命令面板(CTRL / CMD + SHIFT + P)并输入“Pop Stash”,您将能够按名称检索您的存储无需使用 git CLI
我不认为有办法 git pop 一个 stash 的名字。
我创建了一个 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"
我希望它有帮助!
如果您使用的是 ZSH,则此别名组合非常致命:
zstyle ':completion:*' completer _expand_alias _complete _ignored
alias gs="git stash push -u -m "
alias gsp='git stash pop'
基本上你可以使用 tab 来自动完成你的别名,然后你可以很容易地按名称命名和搜索你的 git stashes。此推送别名还将包括任何未跟踪的文件,我发现默认情况下这些文件很有用。
对于除了存储创建之外的所有内容,我会通过引入fzf作为依赖项来提出另一种解决方案。我建议您花 5 分钟的时间来了解它,因为它总体上是极大的生产力助推器。
无论如何,他们的示例页面的相关摘录提供了存储搜索。更改 scriptlet 以添加其他功能(如存储应用程序或删除)非常容易:
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
}
这是我做的一个快速设置,对我有用,希望它也对你有用:
假设我的 package.json 项目文件中有一个自定义/本地脚本,我不想推送到远程仓库
{
// ... package.json stuff
"scripts": {
"custom": "ts-node a_ts_test_file.ts"
}
}
因此,当我想推送我的分支或类似的东西时,我决定存储这个更改,然后弹出存储直到我的下一个“git push”。
所以 ...
# dev is the "stash tag"
# To stash current working directory
git config --global alias.sh "stash -m 'dev'"
~.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 step
alias gitsh="git stash pop $(git stash list | grep 'dev' | cut -d ':' -f 1) || echo 'nope'"
使用标签“dev”推送您的工作目录:git sh
从标签为“dev”的存储中提取您的隐藏更改:sitsh
(这是我在五分钟内制作的一个小脚本,对我有用,如果它失败了......修复它!)