从脚本确定Git工作目录是否干净


82

我有一个rsync以Git工作目录作为目标运行的脚本。我希望脚本具有不同的行为,具体取决于工作目录是否干净(没有要提交的更改)。例如,如果输出git status如下,我希望脚本退出:

git status
Already up-to-date.
# On branch master
nothing to commit (working directory clean)
Everything up-to-date

如果目录不干净,那么我希望它执行更多命令。

如何在shell脚本中检查上述输出?


在这里检查上一条命令的状态会有所帮助吗?($?)
UVV 2014年

您能提供更多细节吗?您的脚本的主要思想是什么?
tachomi 2014年

@tachomi我在编辑中添加了上下文
brentwpeterson

您可以假设它不是干净的,然后执行git reset --hard origin/branchif if that is
wanting

1
@SnakeDoc您可以,但是我认为反写情况会更常见,即,如果工作目录很脏,请退出以免破坏本地更改。考虑这两种情况将使问题对将来的读者更有用。
Thomas Nyman 2014年

Answers:


133

解析输出git status不是一个好主意,因为该输出是人类可读的,而不是机器可读的。不能保证在将来的Git版本或不同配置的环境中输出将保持不变。

UVV的注释处于正确的轨道上,但是不幸的是git status,当存在未提交的更改时,的返回代码不会更改。它,然而,提供的--porcelain选项,这将导致输出git status --porcelain在一个易于解析格式的脚本进行格式化,并且将整个版本的Git保持稳定,而不管用户配置的。

我们可以将的空输出git status --porcelain用作没有更改要提交的指示符:

if [ -z "$(git status --porcelain)" ]; then 
  # Working directory clean
else 
  # Uncommitted changes
fi

如果我们不在乎工作目录中的未跟踪文件,则可以使用该--untracked-files=no选项来忽略这些文件:

if [ -z "$(git status --untracked-files=no --porcelain)" ]; then 
  # Working directory clean excluding untracked files
else 
  # Uncommitted changes in tracked files
fi

为了使这种方法在没有输出到的情况下实际上会导致git status失败的情况下更加可靠stdout,我们可以将检查优化为:

if output=$(git status --porcelain) && [ -z "$output" ]; then
  # Working directory clean
else 
  # Uncommitted changes
fi

还值得注意的是,尽管git status在工作目录不干净时不会给出有意义的退出代码,但git diff提供了--exit-code选项,使其行为与diff实用程序相似,即1在存在差异0且未找到差异时以状态退出。

使用此方法,我们可以使用以下方法检查未进行的变更:

git diff --exit-code

并通过以下方式进行但未提交的更改:

git diff --cached --exit-code

尽管git diff可以通过适当的参数来报告子模块中未跟踪的文件--ignore-submodules,但是不幸的是,似乎没有办法在实际工作目录中报告未跟踪的文件。如果工作目录中的未跟踪文件相关,git status --porcelain则可能是最好的选择。


4
ughhh git status --porcelain将以代码0退出,即使没有为提交和未跟踪的文件暂存更改。
亚历山大·米尔斯

我有兴趣提前确定是否git stash会做任何事情(它不会输出有用的返回码)。我必须添加--ignore-submodules,否则git status将指示git stash忽略的子模块更改。
Devin Lane

1
@AlexanderMills:我观察到了同样的情况。但是,然后检查if [ -z正在做什么。的-z手段,如果以下字符串是空的,如果计算结果为true。换句话说,如果git status --porcelain没有任何结果,则仓库是干净的。如果不是,它将列出已修改/添加/删除的文件,并且不再是空字符串。在if随后的计算结果为false
阿德纳克(Adeynack)

19

采用:

git diff-index --quiet HEAD

返回代码反映了工作目录的状态(0 =干净,1 =脏)。未跟踪的文件将被忽略。


6
当前目录中有未跟踪的文件时,返回0。
亚当·帕金

2
如果文件已经触及/覆盖,但在其它方面相同的索引,你需要先运行git update-index --refresh之前git diff-index HEAD。更多信息:stackoverflow.com/q/34807971/1407170
sffc

@AdamParkin我只是git add .在发布前添加所有文件。通常,这是在脚本中使用它的方式
ceztko

这很棒。请注意,返回/退出代码非零也将被解释为“错误”,如果您使用带-e的脚本,则脚本将在“脏”的情况下退出。为避免这种情况,可以set +e在调用之前进行git,然后set -e在评估之后再次添加$?
Orion elenzil

1

André的出色回答进行了次要扩展。

如果您使用的是先前发出set -e的脚本,这是评估结果并避免陷阱的一种方法。

未跟踪的文件将被忽略。

set +e
git diff-index --quiet HEAD

if [ $? == 1 ] ; then
  set -e
  GIT_MODS="dirty"
else
  set -e
  GIT_MODS="clean"
fi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.