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

时间:2010-04-17 09:41:37

标签: git shell

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

  1. 添加到索引但未提交的更改
  2. 未跟踪文件
  3. 来自脚本?

    git-status似乎总是使用git版本1.6.4.2返回零。

14 个答案:

答案 0 :(得分:383)

可靠地“编写脚本”Git的关键是使用'plumbing'命令。

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

不幸的是''每天'的Git命令是'瓷器'命令,因此大多数Git用户可能不熟悉管道命令。瓷器和管道命令之间的区别在主git manpage中进行(参见标题为High-level commands (porcelain)Low-level commands (plumbing)的小节。


要了解未经修改的更改,您可能需要git diff-index(比较索引(可能跟踪工作树的位)与其他树木(例如HEAD)),也许{{3} (比较工作树与索引),可能git diff-files(列表文件;例如列表未跟踪,未标记文件)。

(请注意,在以下命令中,使用HEAD --而不是HEAD,否则命令git ls-files如果存在名为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都是工作的工具:

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

git ls-files --others

对于“未跟踪和不受欢迎”:

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

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

test -z "$(git ls-files --others)"
  • 如果它以0退出,则没有未跟踪的文件。如果它以1退出,则会有未跟踪的文件。

这很可能会将异常退出从git ls-files转换为“无跟踪文件”报告(两者都会导致上述命令的非零退出)。更健壮的版本可能如下所示:

u="$(git ls-files --others)" && test -z "$u"
  • 这个想法与上一个命令相同,但它允许来自git ls-files的意外错误传播出去。在这种情况下,非零退出可能意味着“有未跟踪的文件”或者它可能意味着发生了错误。如果您希望将“错误”结果与“无跟踪文件”结果相结合,请使用test -n "$u"(其中0的退出表示“某些未跟踪文件”,非零表示错误或“否”未跟踪文件“)。

另一个想法是在没有未跟踪文件时使用--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

答案 1 :(得分:163)

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

这就是我的所作所为:

  1. 对于脏状态:

    # Returns "*" if the current git branch is dirty.
    function evil_git_dirty {
      [[ $(git diff --shortstat 2> /dev/null | tail -n1) != "" ]] && echo "*"
    }
    
  2. 对于未跟踪的文件(注意--porcelain的{​​{1}}标志,它为您提供了可用的可解析输出):

    git status
  3. 虽然# Returns the number of untracked files function evil_git_num_untracked_files { expr `git status --porcelain 2>/dev/null| grep "^??" | wc -l` } 更方便,但您也可以使用git diff --shortstat来获取脏文件:

    git status --porcelain

    注意:# 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) 会过滤掉错误消息,因此您可以在非git目录上使用这些命令。 (他们只需返回2>/dev/null以获取文件计数。)

    修改

    以下是帖子:

    Adding Git Status Information to your Terminal Prompt

    Improved Git-enabled Shell Prompt

答案 2 :(得分:130)

假设您使用的是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语句更好。

答案 3 :(得分:13)

VonC答案的实施:

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

答案 4 :(得分:6)

看了几个答案......(并且在* 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在该帖子上分享

答案 5 :(得分:4)

为什么不使用以下脚本封装'git status

  • 将分析该命令的输出
  • 将根据您的需要返回相应的错误代码

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


0xfe中提及excellent answer时, git status --porcelain 对任何基于脚本的解决方案都有帮助

--porcelain
  

以稳定,易于解析的格式为脚本提供输出   目前这与--short output相同,但保证将来不会改变,使脚本安全。

答案 6 :(得分:4)

一种DIY可能性,已更新以遵循0xfe的建议

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

Chris Johnsen所述,这仅适用于Git 1.7.0或更新版本。

答案 7 :(得分:2)

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

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

到目前为止,我最终使用的最佳命令如下:

 test -z "$(git status --porcelain | tee /dev/fd/2)" || \
     {{ echo "ERROR: git unclean at the end, failing build." && return 1 }}

它可能看起来有点复杂,如果有人发现一个简短的变体,我将不胜感激 保持所需的行为:

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

答案 8 :(得分:2)

@ eduard-wirch的答案很完整,但是由于我想同时检查两者,这是我的最终变体。

        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。

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

答案 9 :(得分:1)

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

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

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

答案 10 :(得分:1)

您也可以

git describe --dirty

。如果检测到脏工作树,它会在末尾附加单词“-dirty”。根据{{​​1}}:

git-describe(1)

。警告:未跟踪的文件不被视为“脏”,因为正如联机帮助页所述,它只关心工作树。

答案 11 :(得分:0)

这个帖子的答案可能会有更好的组合..但这适用于我... .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

答案 12 :(得分:0)

我用来检测脏状态 = 包括未跟踪文件在内的任何更改的最简单的自动测试

git add --all
git diff-index --exit-code HEAD

注:

  • 没有add --all diff-index没有注意到未跟踪的文件。
  • 通常情况下,我在测试错误代码后运行git reset以取消所有内容。

答案 13 :(得分:-2)

这是最好,最干净的方式。由于某种原因,所选答案对我不起作用,它没有收到那些未提交的新文件。

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
}