检查Git是否有变更的程序化方法

5
我想写一个脚本,让我能够轻松地切换分支并保留当前的更改。
git stash
go checkout main
git stash pop

问题是如果当前存储库中没有更改。那么stash pop将弹出以前的存储。有什么最简单的方法来检查是否有更改可以弹出?
3个回答

11

一种简单的方法是检查工作区是否有更改的文件,建立在Chris Hayes的建议之上,提供了一个结合使用git status --porcelain --untracked-files=no (或者 -uno) 和 wc -l 的方式。

这将给出更改文件数量。

以下是一个脚本示例,应该能够满足您的需求:

changed_files=$(git status --porcelain --untracked-files=no | wc -l)
[ $changed_files -gt 0 ] && git stash

< do work > 

[ $changed_files -gt 0 ] && git stash pop

要了解更多关于git status的信息,请参考文档有关wc的信息请参阅相应的文档)。


5

git stash 实际上是一个 shell 脚本(纯 sh,不是 bash),你可以查看它的功能来决定是否制作一个存储:

no_changes () {
        git diff-index --quiet --cached HEAD --ignore-submodules -- &&
        git diff-files --quiet --ignore-submodules &&
        (test -z "$untracked" || test -z "$(untracked_files)")
}

untracked_files () {
        excl_opt=--exclude-standard
        test "$untracked" = "all" && excl_opt=
        git ls-files -o -z $excl_opt
}

因此,您可以重复这些测试来查看 git stash 是否会做任何事情,但实际上这并不是最好的选择:
- 如果 git stash 会做一些事情,首先它会重复这些测试(并且它们会说有什么可隐藏的内容),在这种情况下它们会发生两次。 - 如果“正确”的测试集合发生改变,您可能会错误地预测 git stash 的动作并陷入麻烦。
显然,您想要的是:
1. 运行 git stash ,并跟踪其是否有任何操作。 2. 在目标分支上运行 git checkout。 3. 如果 git stash 存储了一些内容,在运行 git stash pop (或者首先运行 --apply --keep-index ,以查看是否有效,并只在必要时不使用 --keep-index 应用等待解决方法等)。
棘手的部分是第一步。其实这很容易,您只需要了解 详细了解git stash 的工作方式,以及有关git的“plumbing命令”的一些其他信息。在 git stash save 步骤之前,只需获取以前的隐藏堆栈顶部(如果有),然后获取新的顶部(如果有),并进行比较即可。
old_stash=$(git rev-parse -q --verify refs/stash)
git stash save [additional arguments here if desired, e.g., -q]
new_stash=$(git rev-parse -q --verify refs/stash)

如果两个引用名称不同,或者旧的引用为空而新的引用不为空,则git stash会将新的存储推送到堆栈上。 如果两个引用都为空,或者两个引用名称指向相同的提交,则git stash不会执行任何操作:
if [ "$old_stash" = "$new_stash" ]; then ...; else ...; fi

或者在您的情况下,可能更像是这样的东西:
[ "$old_stash" != "$new_stash" ] && git stash pop

这些测试利用了两个空字符串相互等同的事实,在/bin/[和shell内置程序的结果中,它们与任何非空字符串都不相等。


4

使用git status

git status --porcelain是一种方法,虽然可能不是最好的(即最快的)。根据手册:

--porcelain

为脚本提供易于解析的输出格式。 这类似于短输出,但将在Git版本和用户配置不受影响的情况下保持稳定。有关详细信息,请参阅下文。

使用示例:

$ git status --porcelain

M tmp/myfile.c

$ git stash
$ git status --porcelain
$ 

如果没有更改,git status --porcelain 将不会有任何输出。但是有一个缺陷 - 新文件将被显示为 ?? newfile.txt,因此您可能需要对输出进行后处理才能获得所需的结果,因为新文件不会被存储:

$ git status --porcelain
?? newfile.txt

$ git status --porcelain | grep -v "^\?\?"
$

你可以使用git diff完成类似的操作,但是由于计算差异需要比枚举状态更长时间,所以我不建议这样做。

(mathspace)chris@cl:/Projects/mathspace$ git stash 没有本地更改需要保存 (mathspace)chris@cl:/Projects/mathspace$ echo $? 0 - Casebash
@Casebash 哎呀..我在测试时可能做了一些奇怪的事情,因为我无法重现那种行为。对此感到抱歉。 - Chris Hayes
1
@ChrisHayes 如果你想要在 git status 输出中隐藏未跟踪的文件,你可以使用 --untracked-files=no(或 -uno),请参考文档 - Sascha Wolf

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