Git stash只有在成功隐藏之前才会弹出

bob*_*sox 8 git bash

我的工作流程的一部分涉及做很多这样的事情:

  • git stash的变化
  • git pull
  • 流行隐藏的变化
  • 启动mergetool以解决冲突

我正在尝试编写一个脚本来同时执行所有这些操作,所以我可以从终端调用它.

#!/bin/bash

# First stash our local changes
git stash

# Then git pull to update our repo
git pull

# Pop the stash
git stash pop

# Launch mergetool if necessary
git mergetool
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是,如果我意外地运行,并且没有对藏匿的更改,则git stash pop应用一些(通常超级旧的)存储.我想要做的git stash pop只是在我之前实际存在的东西之前运行.有没有办法做到这一点?

tor*_*rek 8

正如XavierÁlvarez 所说,codeWizard写道,git stash完全避免这里可能更明智.例如,我会考虑使用单独的git fetchgit rebase步骤(参见Xavier的答案),并注意到rebase现在--autostash基本上只是你想要的东西,它只是不能通过git pull便利脚本直接获得.1

这就是说,有一种方法可以做到你问什么.这有点棘手.如果git stash save有一个类似的"强制"选项会更容易git commit --allow-empty,但它没有这样的选择.2 相反,你可以做的是检测是否git stash save推了一个新的藏匿处.如果git stash save退出状态指示是否推送存储,这也会更容易,但同样不会.这意味着我们必须完全依赖不同的技巧.我们从两个事实开始:git rev-parse从"引用"中查找SHA-1,并git stash使用一个特定的引用.

git rev-parse命令会将任何引用转换为SHA-1:

$ git rev-parse refs/remotes/origin/master
2635c2b8bfc9aec07b7f023d8e3b3d02df715344
Run Code Online (Sandbox Code Playgroud)

引用只是一个名称,通常以refs命名某些SHA-1 ID开头.最常见的是分支:.你可能也使用过标签:,你可能已经使用了远程跟踪分支,比如全名的缩写.refs/heads/branchrefs/tags/tagorigin/masterrefs/remotes/origin/master

stash脚本使用refs/stash,所以我们可以简单地运行git rev-parse refs/stash.3 我们希望之前运行它git stash save,然后再运行它git stash save.如果输出发生更改,则该git stash save步骤必须将新存储推送到存储堆栈.

我们必须要小心一点,因为如果存储堆栈是空的(因为最后一个存储被先前弹出或删除,或者还没有创建过存储器),git rev-parse则会给出错误消息并且不生成SHA-1:

$ git rev-parse refs/stash
fatal: ambiguous argument 'refs/stash': unknown revision or path not in
the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
Run Code Online (Sandbox Code Playgroud)

因此我们实际上需要git rev-parse -q --verify refs/stash,如果引用不存在则默默地产生任何内容,然后我们只需要在任何使用结果的shell脚本中小心一点:

oldsha=$(git rev-parse -q --verify refs/stash)
git stash -q save  # add options as desired here
newsha=$(git rev-parse -q --verify refs/stash)
if [ "$oldsha" = "$newsha" ]; then
    made_stash_entry=false
else
    made_stash_entry=true
fi
... all of your other code goes here ...
if $made_stash_entry; then git stash pop; fi
Run Code Online (Sandbox Code Playgroud)

1git pull命令基本上是一个简写,git fetch后面跟着git merge,或者,如果你告诉它,运行git fetch后跟通常更合适的命令git rebase.但是,如果将其分解为两个单独的步骤,则可以获得更多控制权,以及在合并或重新定位之前检查传入更改的能力.

2您可以使用较新的有效的强制藏匿创作createstore子命令:创建一个存储,然后存储所产生的SHA-1,你已经被迫即使没有什么藏匿藏匿保存.但并不是每个人都对最新的git有所了解,所以对于脚本来说,依旧的方式可能更明智(或者如前所述,根本不使用藏匿,特别是因为它有各种各样的小但烦人的bug,各种版本git).

3拼出全名是明智的,因为它git rev-parse stash会先找一个名为的分支stash.在编写别名或脚本时,所有引用都是正确的:拼写全名(并--根据需要使用语法)以确保git不会在奇怪的角落情况下执行它认为您的意思.


jgr*_*aup 5

使用时git stash save <messsage>,您传递的消息将在成功保存后显示。

因此,一个技巧是生成一个时间戳,将其用作消息,如果在结果消息中找到时间戳,则删除最新的存储。

一条线:

t=timestamp-$(date +%s); r=$(git stash save $t); v=$(echo $r|grep $t); if [ "$v" ]; then git stash list; echo "SAVED! NOW REMOVING..."; git stash drop stash@{0}; else echo "Nothing to Stash!"; fi; echo "Stashes: "; git stash list; echo "Done!"
Run Code Online (Sandbox Code Playgroud)

扩展:

# unique timestamp
t=timestamp-$(date +%s) 

# stash with message
r=$(git stash save $t)

# check if the value exists
v=$(echo $r|grep $t)

# if the message is found...
if [ "$v" ] then 

  # DEBUG: Before
  git stash list
  echo "SAVED! NOW REMOVING..."

  # remove last stash
  git stash drop stash@{0}
else 
  echo "Nothing to Stash!"
fi

# DEBUG: after
echo "Stash List: "
git stash list
echo "Done!"
Run Code Online (Sandbox Code Playgroud)


Xav*_*rez 4

阅读你对为什么要做你所做的事情的解释,我可能会采取完全不同的方法。首先,我会获取您要使用的遥控器:

git fetch <remote> (e.g. git fetch origin)
Run Code Online (Sandbox Code Playgroud)

然后,我将针对该远程的特定分支执行变基:

git rebase <remote>/<branch> (e.g. git rebase origin/master)
Run Code Online (Sandbox Code Playgroud)

这将合并您的更改,并且您仍然能够解决任何冲突。

如果您不喜欢这种方法,您可能需要使用带有--no-commit标志的git pull来代替:

git pull --no-commit
Run Code Online (Sandbox Code Playgroud)

这样合并后就不会执行自动提交。