【发布时间】:2010-10-07 04:16:13
【问题描述】:
在 Makefile 中,如果有未提交的更改(在工作树或索引中),我想执行某些操作。最干净、最有效的方法是什么?在一种情况下返回值为零而在另一种情况下返回值非零的命令将适合我的目的。
我可以运行git status 并通过grep 管道输出,但我觉得必须有更好的方法。
【问题讨论】:
标签: git
在 Makefile 中,如果有未提交的更改(在工作树或索引中),我想执行某些操作。最干净、最有效的方法是什么?在一种情况下返回值为零而在另一种情况下返回值非零的命令将适合我的目的。
我可以运行git status 并通过grep 管道输出,但我觉得必须有更好的方法。
【问题讨论】:
标签: git
更新:OP Daniel Stutzbach 指出in the comments 这个简单的命令git diff-index 对他有用:
git update-index --refresh
git diff-index --quiet HEAD --
更精确的选择是使用porcelain option 测试git status --porcelain=v1 2>/dev/null | wc -l。
见Myridium的answer。
(nornagon提到in the comments,如果有文件被触及,但内容与索引相同,则需要在git diff-index之前运行git update-index --refresh,否则@987654349 @ 会错误地报告树是脏的)
如果您在 bash 脚本中使用它,则可以看到“How to check if a command succeeded?”:
git diff-index --quiet HEAD -- || echo "untracked"; // do something about it
git diff-index HEAD ...将在没有提交的分支上失败(例如新初始化的存储库)。
我发现的一种解决方法是git diff-index $(git write-tree) ...
并且haridsv 指出in the comments 在新 文件上的git diff-files 不会将其检测为差异。
更安全的方法似乎是先在文件规范上运行git add,然后使用git diff-index 在运行git commit 之前查看是否有任何内容添加到索引中。
git add ${file_args} && \
git diff-index --cached --quiet HEAD || git commit -m '${commit_msg}'
6502 在 cmets 中报告:
我遇到的一个问题是,
git diff-index会告诉我存在差异,而实际上除了文件的时间戳之外没有其他差异。
运行git diff一次即可解决问题(令人惊讶的是,git diff确实改变了沙箱的内容,这里的意思是.git/index)
如果 git 是 running in docker,也会出现这些时间戳问题。
原答案:
“以编程方式”表示永远不要依赖porcelain commands。
始终依赖plumbing commands。
另请参阅“Checking for a dirty index or untracked files with Git”了解替代方案(如git status --porcelain)
您可以从中汲取灵感from the new "require_clean_work_tree function" which is written as we speak ;)(2010 年 10 月上旬)
require_clean_work_tree () {
# Update the index
git update-index -q --ignore-submodules --refresh
err=0
# Disallow unstaged changes in the working tree
if ! git diff-files --quiet --ignore-submodules --
then
echo >&2 "cannot $1: you have unstaged changes."
git diff-files --name-status -r --ignore-submodules -- >&2
err=1
fi
# Disallow uncommitted changes in the index
if ! git diff-index --cached --quiet HEAD --ignore-submodules --
then
echo >&2 "cannot $1: your index contains uncommitted changes."
git diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2
err=1
fi
if [ $err = 1 ]
then
echo >&2 "Please commit or stash them."
exit 1
fi
}
【讨论】:
git diff-index --quiet HEAD。
HEAD 的文件,这可能会失败。最好使用git diff-index --quiet HEAD --。
git status --help 的手册指出:--porcelain 以易于解析的脚本格式提供输出。这与短输出类似,但在 Git 版本之间以及无论用户配置如何都将保持稳定。详情见下文。
虽然其他解决方案非常彻底,但如果您想要快速而肮脏的东西,请尝试以下方法:
[[ -z $(git status -s) ]]
它只是检查状态摘要中是否有任何输出。
【讨论】:
git status 的返回码在这个测试中实际上被忽略了。它只查看输出。查看this bash related page 了解更多关于[、[[ 以及如何在bash 中进行测试的信息。
--porcelain 参数,如图here
git status -s -uall 来包含未跟踪的文件。
git status 默认为 all。来自git status -h:-u, --untracked-files[=<mode>]show untracked files, optional modes: all, normal, no. (Default: all)
git diff --exit-code 如果有任何更改,将返回非零值; git diff --quiet 相同,没有输出。由于您要检查工作树和索引,请使用
git diff --quiet && git diff --cached --quiet
或者
git diff --quiet HEAD
任何一个都会告诉您是否有未提交的更改已暂存。
【讨论】:
git diff --quite HEAD 只会告诉您工作树是否干净,而不是索引是否干净。例如,如果file 在 HEAD~ 和 HEAD 之间更改,那么在git reset HEAD~ -- file 之后,即使索引中存在阶段性更改(wt == HEAD,但索引!= HEAD),它仍将退出 0。
git diff --quiet && git diff --cached --quiet 未检测到新的(未跟踪的)文件。
扩展@Nepthar 的回答:
if [[ -z $(git status -s) ]]
then
echo "tree is clean"
else
echo "tree is dirty, please commit changes before running this"
exit
fi
【讨论】:
$(git status -s "$file") 然后在else 子句git add "$file"; git commit -m "your autocommit process" "$file" 中使用它来自动提交单个文件
git status -s 改为git status --porcelain ; git clean -nd,垃圾目录也会出现在这里,git status 看不到这些目录。
有些答案既使事情变得过于复杂,也没有达到预期的结果。例如。接受的答案错过了未跟踪的文件。
使用提供的git status --porcelain,它设计为可机器解析,尽管有些人(错误地)在 cmets 中另有说明。如果git status 中出现了某些内容,那么我就认为工作目录很脏。所以我使用测试[ -z "$(git status --porcelain=v1 2>/dev/null)" ] 测试清洁度,如果在 git 目录之外运行,它也会通过。
最小工作示例:
[ -z "$(git status --porcelain=v1 2>/dev/null)" ] && echo "git undirty"
git status 中出现的任何内容(截至目前)都将正确触发此测试。 =v1 位确保跨 git 版本的输出格式一致。
灵感来自this answer。你grepgit status --porcelain=v1 的行输出。每行的前两个字符表示特定文件的状态。在 grepping 之后,您可以通过将输出传送到计算行数的 wc -l 来计算有多少具有该状态。
例如如果在 git 存储库中运行,此脚本将打印一些信息。
#!/bin/sh
GS=$(git status --porcelain=v1 2>/dev/null) # Exit code 128 if not in git directory. Unfortunately this exit code is a bit generic but it should work for most purposes.
if [ $? -ne 128 ]; then
function _count_git_pattern() {
echo "$(grep "^$1" <<< $GS | wc -l)"
}
echo "There are $(_count_git_pattern "??") untracked files."
echo "There are $(_count_git_pattern " M") unstaged, modified files."
echo "There are $(_count_git_pattern "M ") staged, modified files."
fi
【讨论】:
--porcelain 选项:“以易于解析的格式为脚本提供输出。这类似于短输出,但无论用户配置如何,都会在 Git 版本中保持稳定。”
我创建了一些方便的 git 别名来列出未暂存和暂存的文件:
git config --global alias.unstaged 'diff --name-only'
git config --global alias.staged 'diff --name-only --cached'
然后您可以轻松地执行以下操作:
[[ -n "$(git unstaged)" ]] && echo unstaged files || echo NO unstaged files
[[ -n "$(git staged)" ]] && echo staged files || echo NO staged files
您可以通过在 PATH 的某处创建一个名为 git-has 的脚本来使其更具可读性:
#!/bin/bash
[[ $(git "$@" | wc -c) -ne 0 ]]
现在上面的例子可以简化为:
git has unstaged && echo unstaged files || echo NO unstaged files
git has staged && echo staged files || echo NO staged files
为了完整起见,这里是未跟踪和忽略文件的类似别名:
git config --global alias.untracked 'ls-files --exclude-standard --others'
git config --global alias.ignored 'ls-files --exclude-standard --others --ignored'
【讨论】:
使用 python 和 GitPython 包:
import git
git.Repo(path).is_dirty(untracked_files=True)
如果存储库不干净,则返回 True
【讨论】:
LOGLEVEL=DEBUG,你会看到它用来运行git diff的所有Popen命令
如果工作树是“干净的”
git ls-files --deleted --modified --others --exclude-standard
什么都不返回。
--deleted 检查工作树中删除的文件--modified 检查工作树中修改的文件--others 检查添加到工作树中的文件--exclude-standard 按照通常的.gitignore, .git/info/exclude ... 规则忽略如果工作树是干净的,则输出为空
【讨论】:
正如其他答案所指出的那样,这样的命令就足够了:
git diff-index --quiet HEAD --
如果您省略最后两个破折号,如果您有一个名为 HEAD 的文件,该命令将失败。
例子:
#!/bin/bash
set -e
echo -n "Checking if there are uncommited changes... "
trap 'echo -e "\033[0;31mFAILED\033[0m"' ERR
git diff-index --quiet HEAD --
trap - ERR
echo -e "\033[0;32mAll set!\033[0m"
# continue as planned...
注意事项:此命令会忽略未跟踪的文件。
【讨论】:
git add 和 git clean 救援
这是最好、最干净的方法。
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
}
【讨论】:
git status 是一个“瓷器”命令。不要在脚本中使用瓷器命令,因为它们可以在 git 版本之间更改。而是使用“管道”命令。
git status --porcelain (这是为此目的——一种你可以在脚本中解析的稳定格式),也可能使用 -z (空分隔而不是换行符?)你可以用这个想法做一些有用的事情。 @codyc4321 详见stackoverflow.com/questions/6976473/…