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

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


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

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

评论

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

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

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

您可以假设它不干净,然后进行git reset --hard origin / branch(如果您要这样做的话)...就像您在编译某些内容后尝试进行清理等。
@SnakeDoc您可以,但是我认为反情况会更常见,即如果工作目录很脏,请退出以免处理本地更改。考虑这两种情况将使问题对将来的读者更有用。

#1 楼

解析git status的输出是一个坏主意,因为该输出旨在供人类读取,而不是机器可读。无法保证在将来的Git版本或不同配置的环境中输出将保持不变。是未提交的更改。但是,它确实提供了git status选项,该选项可使--porcelain的输出以易于解析的脚本格式进行格式化,并且在Git版本和任何用户配置下都将保持稳定。

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

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


如果我们不关心工作目录中未跟踪的文件,我们可以使用git status --porcelain选项来忽略那些:到:

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


还值得注意的是,尽管当工作目录不整洁时--untracked-files=no并没有给出有意义的退出代码,但是git status提供了stdout选项,这使其工作与diff实用程序类似,即当存在差异和q4312时以状态git status退出079q找不到时。

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

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


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

评论


ughhh git status --porcelain将以代码0退出,即使未针对提交和未跟踪的文件进行更改。

–亚历山大·米尔斯(Alexander Mills)
18年8月31日在21:19

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

–德文·莱恩(Devin Lane)
18-10-27在21:03

@AlexanderMills:我观察到了同样的情况。但是然后检查[-z在做什么。 -z表示如果以下字符串为空,则if的值为true。换句话说,如果此git status --porcelain不产生任何字符串,则该存储库是干净的。如果不是,它将列出已修改/添加/删除的文件,并且不再是空字符串。如果为if,则结果为false。

– Adeynack
19年5月17日在12:36

如果您只想要退出代码,请使用git diff --quiet --exit-code

– kigawas
20年7月2日,3:09

#2 楼

使用:

git diff-index --quiet HEAD


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

评论


当前目录中有未跟踪的文件时,返回0。

–亚当·帕金(Adam Parkin)
18年4月12日在1:48

如果文件已被触摸/覆盖,但在其他方面与索引相同,则需要先在git diff-index HEAD之前运行git update-index --refresh。更多信息:stackoverflow.com/q/34807971/1407170

–sffc
18年11月1日在2:28

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

–ceztko
18/12/7在13:27

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

– Orion elenzil
19年3月29日在16:00

#3 楼

我做了这种测试

TEST=$(git status --porcelain|wc -l)
if [ 0 -eq $TEST ]; then
   echo "No changes"
else
   echo "Changes"
fi 


评论


不必为了确定是否产生了任何行而对行数进行计数,因此,传递给另一个程序很浪费,并且不能很好地表达意图。请参阅Thomas Nyman的答案以获得更好的解决方案。

– ErikE
20年7月13日在4:11

#4 楼

对André的出色回答进行了小扩展。
未跟踪的文件将被忽略。
if git diff-index --quiet HEAD
then
  GIT_MODS="clean"
else
  GIT_MODS="dirty"
fi


评论


访问$?不如直接在命令上使用if:if! git diff-index-安静的HEAD;然后。但是,此方法不是100%可靠的,如果您不先运行git update-index --really-refresh,则可能会出现假阳性的“脏”结果。要查看此操作,请触摸现有的提交文件以仅更新其日期,然后即使实际上没有任何更改,您也会从diff-index中看到非零返回码。

– ErikE
20 Jul 13'4:19



#5 楼

如何使用git describe --broken --dirty --all并检查结果以查看是否以-dirty(或-broken)结尾;然后,在具有git submodule foreach ...的所有子模块上使用相同的东西吗?
据我所知,这需要现代的(版本> = 3.x)bash,它也不会调查未跟踪的文件-我认为这是不合理的(至少如果检出了差异分支,则不应破坏它们!)-请注意,如果一切都干净,它只会返回零退出状态:
#!/bin/bash

RESULT=$(git describe --broken --dirty --all)
STATUS="clean"
if [[ "${RESULT}" =~ broken$ ]]; then
    # Bail out now, in case it is not safe to look for sub-modules
    echo "Main repository is broken."
    exit -3
elif [[ "${RESULT}" =~ dirty$ ]]; then
    echo "Main repository is dirty - not checking any submodules."
    STATUS="dirty"
    exit -1
else
    git submodule foreach --recursive --quiet 'RESULT=$(git describe --broken --dirty --all) 
        if [[ "${RESULT}" =~ broken$ ]]; then
            echo "Submodule ${name} is broken."
            exit -3
        elif [[ "${RESULT}" =~ dirty$ ]]; then
            echo "Submodule ${name} is dirty."
            STATUS="dirty"
        else
            echo "Submodule ${name} is clean."
        fi' 
    echo -n "Main repository is clean "
    if [ "${STATUS}" = "dirty" ]; then
        echo "- but at least one submodule is dirty."
        exit -2
    else
        echo "- and so are any submodules."
    fi
fi
exit 0