我如何检查我的git存储库中是否有任何未提交的更改:

添加到索引但未提交的更改 无路径的文件

从一个脚本?

git-status在git 1.6.4.2版本中似乎总是返回0。


当前回答

可靠地“脚本化”Git的关键是使用“管道”命令。

The developers take care when changing the plumbing commands to make sure they provide very stable interfaces (i.e. a given combination of repository state, stdin, command line options, arguments, etc. will produce the same output in all versions of Git where the command/option exists). New output variations in plumbing commands can be introduced via new options, but that can not introduce any problems for programs that have already been written against older versions (they would not be using the new options, since they did not exist (or at least were not used) at the time the script was written).

不幸的是,“日常”Git命令是“瓷器”命令,所以大多数Git用户可能不熟悉管道命令。瓷器和管道命令之间的区别是在git的主要manpage中做出的(参见高级命令(瓷器)和低级命令(管道)的子章节。


要找出未提交的更改,你可能需要git diff-index(比较索引(可能是工作树的跟踪位)和一些其他树(例如HEAD)),可能是git diff-files(比较工作树和索引),可能是git ls-files(列表文件;例如,列出未跟踪、未忽略的文件)。

(注意,在下面的命令中,使用HEAD——而不是HEAD,否则如果有一个名为HEAD的文件,命令就会失败。)

要检查存储库是否有阶段性更改(尚未提交),请使用以下命令:

git diff-index --quiet --cached HEAD --

如果它以0退出,则没有差异(1表示有差异)。

要检查工作树是否有可以阶段性的更改:

git diff-files --quiet

退出代码与git的diff-index相同(0 ==没有区别;1 ==差异)。

检查工作树中索引和被跟踪文件的组合是否对HEAD有变化:

git diff-index --quiet HEAD --

这就像是前两者的结合。一个主要的区别是,如果您在工作树中“撤消”了一个阶段性的更改(回到HEAD中的内容),它仍然会报告“无差异”。在同样的情况下,两个单独的命令都将返回“存在差异”的报告。

您还提到了未跟踪文件。您可能指的是“未跟踪和未忽略”,也可能只是简单的“未跟踪”(包括被忽略的文件)。不管怎样,git ls-files都是完成这项工作的工具:

对于“untracked”(将包括被忽略的文件,如果存在):

git ls-files --others

对于“untracked and unignored”:

git ls-files --exclude-standard --others

我的第一个想法是检查这些命令是否有输出:

test -z "$(git ls-files --others)"

如果它以0退出,则没有未跟踪的文件。如果它以1退出,则存在未跟踪的文件。

这有很小的可能会将git ls-files中的异常退出转换为“no untracked files”报告(两者都会导致上述命令的非零退出)。更健壮的版本可能是这样的:

u="$(git ls-files --others)" && test -z "$u"

这个想法与前面的命令相同,但是它允许来自git ls-files的意外错误传播出去。在这种情况下,非零退出可能意味着“有未跟踪的文件”,也可能意味着发生了错误。如果你想要“error”结果与“no untracked files”结果相结合,使用test -n "$u"(其中exit为0表示“一些未跟踪的文件”,non- 0表示错误或“no untracked files”)。

另一种想法是使用——error-unmatch在没有未跟踪文件时导致非零退出。这也存在将“没有未跟踪文件”(退出1)与“发生错误”(退出非零,但可能是128)混为一谈的风险。但是检查0、1和非0退出码可能相当健壮:

git ls-files --others --error-unmatch . >/dev/null 2>&1; ec=$?
if test "$ec" = 0; then
    echo some untracked files
elif test "$ec" = 1; then
    echo no untracked files
else
    echo error from ls-files
fi

如果您想只考虑未跟踪和未忽略的文件,以上任何git ls-files示例都可以采用——exclude-standard。

其他回答

一个DIY的可能性,更新遵循0xfe的建议

#!/bin/sh
exit $(git status --porcelain | wc -l) 

正如Chris Johnsen所指出的,这只适用于Git 1.7.0或更新版本。

假设你使用的是git 1.7.0或更高版本…

在阅读了本页上的所有答案并进行了一些实验后,我认为将正确性和简洁性正确结合的方法是:

test -n "$(git status --porcelain)"

虽然git允许在跟踪、忽略、未跟踪但未忽略等内容之间有很多细微差别,但我相信典型的用例是用于自动化构建脚本,如果签出不干净,您希望停止所有内容。

在这种情况下,模拟程序员将要做的事情是有意义的:键入git status并查看输出。但我们不想依赖于特定的单词显示,所以我们使用了1.7.0中引入的——porcelain模式;启用干净目录时,不会产生任何输出。

然后使用test -n查看是否有输出。

如果工作目录是干净的,该命令将返回1,如果有要提交的更改,则返回0。你可以把-n改成-z。这对于将其链接到脚本中的命令非常有用。例如:

test -z "$(git status --porcelain)" || red-alert "UNCLEAN UNCLEAN"

这实际上是在说“要么不需要做出改变,要么就会拉响警报”;这个一行语句可能比if语句更可取,这取决于您正在编写的脚本。

这个帖子可能会有更好的答案组合。但这对我有用……对于你的.gitconfig的[alias]部分…

          # git untracked && echo "There are untracked files!"
untracked = ! git status --porcelain 2>/dev/null | grep -q "^??"
          # git unclean && echo "There are uncommited changes!"
  unclean = ! ! git diff --quiet --ignore-submodules HEAD > /dev/null 2>&1
          # git dirty && echo "There are uncommitted changes OR untracked files!"
    dirty = ! git untracked || git unclean

你也可以

git describe --dirty

. 如果它检测到一个肮脏的工作树,它将在结尾附加单词“-dirty”。根据git-describe(1):

   --dirty[=<mark>]
       Describe the working tree. It means describe HEAD and appends <mark> (-dirty by default) if
       the working tree is dirty.

. 注意:未跟踪的文件不被认为是“脏文件”,因为,正如manpage声明的那样,它只关心工作树。

VonC答案的实现:

if [[ -n $(git status --porcelain) ]]; then echo "repo is dirty"; fi