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

2360

我该如何保存/应用一个有名字的git储藏(stash)? 我不想在 git stash list 中查找它的索引号。我尝试了 git stash save "my_stash_name",但这只会更改储藏的描述,对应的 git apply "my_stash_name" 是无效的。


195
git stash push -m stashname当前语法git stash save stashname 已被弃用。 - SherylHohman
3
简单的自定义 git 命令(链接到 gist)。用法:git bottle name,然后稍后使用 git apply name.patch,如果您想要更详细的解释和使用示例,请参见我的答案 - GrayedFox
3
有趣的是,大多数答案似乎与问题无关。Cameron的答案最接近,因为它提供了一个解决方法,并详细解释了为什么它有效。然而,显然问题的答案是否定的。你不能做到OP所问的,但有一个解决方法可以通过其描述性评论应用stash。这可能就是为什么没有答案被接受为答案,或者OP只是忘记选择一个。这是一个好问题,我很欣赏,因为当我找到它时,我正好在寻找答案。 - shawn1874
28个回答

12

使用以下命令可以自定义注释 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

1
这基本上与早期的答案相同。 - Michael
我认为你的意思是 -> git stash push -m "你的注释" - Craig
@Michael,但这个有点不同,我从来没有推送过stash,它总是留在本地git中。这就是我以前的做法,而且它有效! - Safry
1
@Craig 不要使用 "push",只需像这样存储提交; git stash -m "你的注释" - Safry
“push”在这里的意思与“git push”不同。Stashes始终是本地的。您正在将存储堆栈顶部的存储推入。稍后,您将其从顶部“弹出”。 - Michael
@Michael,这个使用 git stash pop 1git stash apply stash@{1} 有微小的不同,https://www.theserverside.com/video/Compare-git-stash-pop-and-git-stash-apply-for-file-restores#:~:text=The%20key%20difference%20between%20git,then%20deletes%20the%20applied%20stash。 - Safry

9
很遗憾,git stash apply stash^{/<regex>} 不起作用(它实际上并没有搜索stash列表,参见接受的答案下的评论)。
以下是可替代方案,它通过正则表达式搜索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",但我不确定这是否会干扰正则表达式定界符(欢迎提出建议)。


9

别名

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

用法

git sapply "<正则表达式>"

  • 兼容Git for Windows

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

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。虽然模式可能需要稍微不同的引用方式。 - Etan Reisner
@EtanReisner:你的代码片段输出了不止一行。 - Vlastimil Ovčáčík
将动作 {print $1; exit} 修改为在匹配到第一行后退出。 - Etan Reisner
@EtanReisner:经过一些测试,我可以摆脱sed,但是wrapper和grep仍然存在。 - Vlastimil Ovčáčík
你不需要使用grep,尽管如我所说,没有它可能会导致模式引用的差异。我猜你所谓的包装器是指shell函数?你从未解释过为什么认为你需要它,因此我无法评论你是否真的需要它,但我相信你可能并不需要。(你可能需要手动调用一个shell而不是直接使用git stash,但甚至可能都不需要这样做。) - Etan Reisner
显示剩余3条评论

6

这篇答案受到Klemen Slavič的很大启发。我原本只是想在被接受的答案中发表评论,但我还没有足够的声望 :(

您还可以添加一个git别名来查找stash引用,并将其用于其他别名的显示、应用、删除等操作。

[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

6

别名 这可能是 Unix-like 系统中更直接的语法,不需要封装在函数中。 将以下内容添加到 ~/.gitconfig 文件的 [alias] 节点下。

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

末尾的连字符表示从标准输入获取输入。


4
使用一个小的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是您想要的stash名称的子字符串。


4
使用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别名;)
注意:正如Kevin所说,您应该使用标签和挑选(cherry-picks)

1
git stash save已被弃用,推荐使用git stash push - wranvaud
@wranvaud:在2022年仍然可用(但是man命令显示它已被弃用)。当它变得不可用时,我会更新答案。谢谢! - Dan Rosenstark

4

这里是我为社区设置的别名: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"

git wip-apply在使用时抛出错误:git wip-apply“25903” 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:2 3:表达式中的语法错误(错误令牌为“3”) - SwissLoop
脚本是UNIX的,你在Windows上使用它吗? - Alessandro Argentieri
嗨!谢谢您的回答。我正在使用Mac OS v12.0.1。 - SwissLoop

3
这是使用PowerShell实现的一种方法:

使用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''
    }
}

更多详情请点击这里


3

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

然后像往常一样 显示应用 它:

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

1
这是迄今为止对我来说最方便的解决方案,因为我不介意在任何一个代码库中有一些额外的标签。 - Vikas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接