使用Git检查脏索引或未跟踪的文件

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

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

从一个脚本?

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

91793 次浏览

为什么不封装` git status与一个脚本:

  • 将分析该命令的输出
  • 是否会根据需要返回适当的错误代码

这样,您就可以在脚本中使用“增强”状态。


正如以0 xfe在他的优秀的答案中提到的,git status --porcelain在任何基于脚本的解决方案中都是有用的

--porcelain

为脚本提供稳定、易于解析的输出格式 目前它与--short output相同,但保证将来不会改变,因此对脚本是安全的

好时机!几天前我写了一篇关于这方面的博客文章,当时我想出了如何在提示符中添加git状态信息。

我是这么做的:

  1. 对于脏状态:

    # Returns "*" if the current git branch is dirty.
    function evil_git_dirty {
    [[ $(git diff --shortstat 2> /dev/null | tail -n1) != "" ]] && echo "*"
    }
    
  2. For untracked files (Notice the --porcelain flag to git status which gives you nice parse-able output):

    # Returns the number of untracked files
    
    
    function evil_git_num_untracked_files {
    expr `git status --porcelain 2>/dev/null| grep "^??" | wc -l`
    }
    

Although git diff --shortstat is more convenient, you can also use git status --porcelain for getting dirty files:

# Get number of files added to the index (but uncommitted)
expr $(git status --porcelain 2>/dev/null| grep "^M" | wc -l)


# Get number of files that are uncommitted and not added
expr $(git status --porcelain 2>/dev/null| grep "^ M" | wc -l)


# Get number of total uncommited files
expr $(git status --porcelain 2>/dev/null| egrep "^(M| M)" | wc -l)

注意:2>/dev/null会过滤掉错误消息,所以你可以在非git目录上使用这些命令。(它们将简单地返回0文件计数。)

编辑:

以下是这些帖子:

在终端提示中添加Git状态信息

改进的Git-enabled Shell Prompt . Shell提示

一个DIY的可能性,更新到以0 xfe的建议

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

正如克里斯·约翰森所指出的,这只适用于Git 1.7.0或更新版本。

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

开发人员在更改管道命令时要小心,以确保它们提供非常稳定的接口(例如,存储库状态、stdin、命令行选项、参数等的给定组合将在所有存在命令/选项的Git版本中产生相同的输出)。管道命令中的新输出变体可以通过新选项引入,但这不会给已经针对旧版本编写的程序带来任何问题(它们不会使用新选项,因为在编写脚本时它们不存在(或至少没有使用))。

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


要找出未提交的更改,你可能需要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 == differences)。

检查工作树中索引和被跟踪文件的组合相对于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"(其中0的退出表示“一些未跟踪的文件”,非零表示错误或“no untracked files”)。

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

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

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

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

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

VonC的答案的实现:

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

这个帖子可能会有更好的答案组合。但这对我有用……为你的.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 add --all
git diff-index --exit-code HEAD

备注:

  • 如果没有add --alldiff-index将不会注意到未跟踪的文件。
  • 通常情况下,我在测试错误代码后运行git reset来取消所有内容。
  • 考虑用--quiet代替--exit-code来避免输出。

你也可以

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声明的那样,它只关心工作树。

看了一下这些答案…(在*nix和windows上有各种问题,这是我的要求)…发现以下方法效果很好…

git diff --no-ext-diff --quiet --exit-code

检查*nix中的退出代码

echo $?
#returns 1 if the repo has changes (0 if clean)

查看窗口$中的退出代码

echo %errorlevel%
#returns 1 if the repos has changes (0 if clean)

来源https://github.com/sindresorhus/pure/issues/115 感谢@paulirish在那篇文章中分享

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

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
}

我经常需要一种简单的方法让构建失败,如果在执行结束时有任何被修改的跟踪文件或任何未被忽略的未跟踪文件。

这对于避免构建产生剩余的情况非常重要。

到目前为止,我使用的最好的命令是这样的:

 test -z "$(git status --porcelain | tee /dev/fd/2)" || \
\{\{ echo "ERROR: git unclean at the end, failing build." && return 1 }}
它可能看起来有点复杂,如果有人能找到一个短的变体,我会很感激

  • 如果一切正常,则没有输出和成功退出代码
  • 如果失败,退出代码1
  • stderr上的错误消息解释了它失败的原因
  • 显示导致失败的文件列表,再次stderr。

@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仅在命令成功时设置。

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