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

时间:2015-12-06 06:23:10

标签: git bash

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

  • git stash changes
  • git pull
  • pop stashed changes
  • 启动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

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

6 个答案:

答案 0 :(得分:7)

作为Xavier Álvarez notedcodeWizard wrote,在这里完全避免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

引用只是一个名称,通常以refs开头,它命名了一些SHA-1 ID。最常见的是分支:refs/heads/branch。您可能还使用了代码:refs/tags/tag,并且您可能使用了origin/master等远程跟踪分支,它是全名refs/remotes/origin/master的缩写。

stash脚本使用refs/stash,因此我们只需运行git rev-parse refs/stash 3 我们希望在git stash save之前运行它,然后再运行在git stash save之后。如果输出发生变化,git stash save步骤必须将新存储推送到存储堆栈。

我们必须要小心一点,因为如果存储堆栈是空的(因为最后一个存储被先前弹出或删除,或者还没有创建过stashes),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>...]'

因此我们实际上需要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

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

2 您可以使用相对较新的git rebasecreate子命令有效地强制存储创建:创建一个存储,然后存储生成的SHA-1,然后你就可以了即使没有任何东西可以藏匿,也会被迫藏匿。但并不是每个人都对最新的git有所了解,所以对于脚本来说,依旧的方式可能更明智(或者如前所述,根本不使用藏匿,特别是因为它有各种各样的小但烦人的bug,各种版本git)。

3 拼出全名是明智的,因为store将首先查找名为git rev-parse stash分支。在编写别名或脚本时,所有引用都是如此:拼出全名(并根据需要使用stash语法)以确保git不会执行认为的意思,在奇怪的角落里。

答案 1 :(得分:3)

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

git fetch <remote> (e.g. git fetch origin)

然后,我会针对该遥控器的特定分支执行 rebase

git rebase <remote>/<branch> (e.g. git rebase origin/master)

这将合并您的更改,您仍然可以解决任何冲突。

如果您不喜欢这种方法,您可能希望将 git pull - no-commit 标志一起使用:

git pull --no-commit

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

答案 2 :(得分:3)

使用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!"

展开:

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

答案 3 :(得分:1)

首先运行git状态怎么样?如果存在本地更改,请运行stash命令。如果没有,请跳过它。将此结果保存在bool中,如果没有新的藏匿,则不要运行pop。

答案 4 :(得分:0)

如果你在脚本中做很多事情,你应该尽量避免藏匿,而只需提交你的代码,然后提取你的更改。

您也可以编写别名而不是脚本:

git config --global alias.<your alias> "git add. && git commit &1 && git pull"

上面的命令将接受提交消息作为参数,并将执行git add,commit&amp;拉

答案 5 :(得分:0)

我正在寻找类似的东西来自动合并母版。我最终只是创建一个具有唯一名称的空文件。下一步是在存储(stash -u)时包括未跟踪的文件。现在我知道我可以一直弹出,因为我正在创建要隐藏的东西。最后,一旦完成所有其他操作,就删除我创建的新文件。

然后我创建了以下别名:

 up  - pull with rebase and sub-modules*
 mm  - merge master
 tm  - create file with novel name
 rtm - remove said file

...以及实际别名:

[alias]
up           = !git pull --rebase --prune --recurse-submodules $@ && git submodule update --init --recursive && git submodule foreach git up && echo 'git on up'
mm           = "!f() { git tm; git stash -u; git co ${1-master}; git up; git co -; git merge ${1-master}; git stash pop; git rtm; }; f"
tm           = "!f() { touch __nothing_to_see_here__; }; f"
rtm          = "!f() { rm __nothing_to_see_here__; }; f"

* haacked起被盗