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

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

从一个脚本?

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


当前回答

VonC答案的实现:

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

其他回答

这是最好、最干净的方法。由于某些原因,所选的答案对我不起作用,它没有拾取未提交的新文件所进行的更改。

function git_dirty {
    text=$(git status)
    changed_text="Changes to be committed"
    untracked_files="Untracked files"

    dirty=false

    if [[ ${text} = *"$changed_text"* ]];then
        dirty=true
    fi

    if [[ ${text} = *"$untracked_files"* ]];then
        dirty=true
    fi

    echo $dirty
}

这是一个更加shell友好的变体,用于查找存储库中是否存在任何未跟踪的文件:

# Works in bash and zsh
if [[ "$(git status --porcelain 2>/dev/null)" = *\?\?* ]]; then
  echo untracked files
fi

这不会派生第二个进程grep,并且不需要检查您是否在git存储库中。这对于shell提示等很方便。

@eduard- wch的回答很完整,但因为我想同时检查两者,这里是我的最后一个变体。

        set -eu

        u="$(git ls-files --others)"
        if ! git diff-index --name-only --quiet HEAD -- || [ -z "${u:-}" ]; then
            dirty="-dirty"
        fi

当不使用set -e或类似的方法执行时,我们可以执行u="$(git ls-files——others)"|| exit 1(如果对已使用的函数有效,则返回)

因此,untracked_files仅在命令成功时设置。

之后,我们可以检查这两个属性,并设置一个变量(或任何东西)。

可靠地“脚本化”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。

假设你使用的是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语句更可取,这取决于您正在编写的脚本。