如何检查git存储库中是否有未提交的更改:
- 更改已添加到索引但尚未提交
- 未跟踪的文件
从脚本?
git-status
git版本1.6.4.2似乎总是返回零。
diff
指示是否存在差异而不是成功运行命令,则需要使用--exit-code
或--quiet
。git命令通常与返回零或非零退出代码以指示命令成功非常一致。
如何检查git存储库中是否有未提交的更改:
从脚本?
git-status
git版本1.6.4.2似乎总是返回零。
diff
指示是否存在差异而不是成功运行命令,则需要使用--exit-code
或--quiet
。git命令通常与返回零或非零退出代码以指示命令成功非常一致。
Answers:
很好的时机!几天前,当我弄清楚如何在提示符下添加git status信息时,我写了一篇关于此的博客文章。
这是我的工作:
对于脏状态:
# Returns "*" if the current git branch is dirty.
function evil_git_dirty {
[[ $(git diff --shortstat 2> /dev/null | tail -n1) != "" ]] && echo "*"
}
对于未跟踪的文件(请注意提供可解析输出的--porcelain
标记git status
):
# 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)
注意:2>/dev/null
筛选出错误消息,以便您可以在非git目录上使用这些命令。(他们只会返回0
文件计数。)
编辑:
这是帖子:
__git_ps1
。它显示了分支名称,如果您正在进行重新设置,am-apply,merge或bisect的处理,则包括特殊处理。您可以将环境变量设置GIT_PS1_SHOWDIRTYSTATE
为对未暂存的更改使用星号,对于暂存的更改使用加号。(我认为您也可以获取它来指示未跟踪的文件,并提供一些git-describe
输出信息)
git diff --shortstat
如果索引中已有更改,将给出错误的否定。
git status --porcelain
首选,因为git diff --shortstat
它将不会捕获新创建的空文件。您可以在任何干净的工作树中进行尝试:touch foo && git diff --shortstat
可靠地“脚本化” 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
==差异)。要检查索引和工作树中的跟踪文件的组合是否相对于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
)混淆的风险。但是检查0
vs. 1
vs.非零退出代码可能相当健壮:
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 ls-files --others
提供了本地未跟踪的文件,而git status --porcelain
可接受答案的给出了git存储库下的所有未跟踪的文件。我不是原始海报想要的,但是两者之间的区别很有趣。
--error-unmatch
。尝试(例如)git ls-files --other --error-unmatch --exclude-standard .
(注意尾随时间,它指的是cwd;在工作树的顶级目录中运行它)。
git update-index -q --refresh
在进行操作之前diff-index
避免由于stat(2)信息不匹配而导致的某些“误报”。
git update-index
需要咬了!如果某些东西未经修改便触碰文件,则这一点很重要。
--porcelain
解决方案列出了整个git存储库中找到的所有未跟踪文件(减去git忽略的文件),即使您位于其子目录之一内。
假设您使用的是git 1.7.0或更高版本...
阅读完本页上的所有答案并进行了一些实验后,我认为达到正确性和简洁性的正确方法是:
test -n "$(git status --porcelain)"
虽然git在跟踪,忽略,未跟踪但未被忽略等之间允许有很多细微差别,但我相信典型的用例是使构建脚本自动化,如果结帐不干净,您希望在其中停止所有操作。
在那种情况下,模拟程序员将要做的事情是有意义的:键入git status
并查看输出。但是我们不想依赖显示的特定单词,因此我们使用--porcelain
1.7.0中引入的模式。启用后,干净目录不会产生任何输出。
然后我们用test -n
看是否有任何输出。
如果工作目录是干净的,那么此命令将返回1,如果要提交的更改将返回0。如果需要相反的选项,可以将更-n
改为-z
。这对于将其链接到脚本中的命令很有用。例如:
test -z "$(git status --porcelain)" || red-alert "UNCLEAN UNCLEAN"
这实际上表示“要么不进行任何更改,要么发出警报”;根据您正在编写的脚本,单行代码可能比if语句更可取。
test -n "$(git diff origin/$branch)"
,以帮助防止在部署中允许本地提交
仔细查看了其中的一些答案...(并且在* nix和windows上遇到了各种问题,这是我的要求)...发现以下各项效果很好...
git diff --no-ext-diff --quiet --exit-code
在* nix中检查退出代码
echo $?
#returns 1 if the repo has changes (0 if clean)
在window $中检查退出代码
echo %errorlevel%
#returns 1 if the repos has changes (0 if clean)
来源于https://github.com/sindresorhus/pure/issues/115 感谢@paulirish在该帖子上的分享
一种DIY可能性,更新为遵循0xfe的建议
#!/bin/sh
exit $(git status --porcelain | wc -l)
如Chris Johnsen所述,这仅适用于Git 1.7.0或更高版本。
--porcelain
添加标志吗?不适用于1.6.4.2。
git status --short
然后尝试。
git status --porcelain
并git status --short
在1.7.0中引入。--porcelain
专门引入它是为了允许git status --short
将来更改其格式。因此,git status --short
将遭受与git status
(将输出随时更改,因为它不是“管道”命令)相同的问题。
如果在执行结束时有任何被修改的跟踪文件或任何未被忽略的未跟踪文件,我通常需要一种简单的方法来使构建失败。
这对于避免构建产生剩余的情况非常重要。
到目前为止,我最终使用的最佳命令如下所示:
test -z "$(git status --porcelain | tee /dev/fd/2)" || \
{{ echo "ERROR: git unclean at the end, failing build." && return 1 }}
它可能看起来有点复杂,如果有人发现可以保持所需行为的简短变体,我将不胜感激:
@ 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。
之后,我们可以检查两个属性,并设置一个变量(或其他变量)。
这个线程可能会有更好的答案组合。.但这对我有用...在您.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 --all
diff-index
则不会注意到未跟踪的文件。git reset
在测试错误代码后运行,以使一切恢复原状。这是最好,最干净的方法。选定的答案由于某种原因对我不起作用,它没有获取已上演的未提交新文件的更改。
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
}