如何以编程方式确定是否存在未提交的更改?

Dan*_*ach 205 git

在Makefile中,如果存在未提交的更改(在工作树或索引中),我想执行某些操作.什么是最干净,最有效的方法?退出的命令在一种情况下返回值为零而在另一种情况下为非零符合我的目的.

我可以运行git status并输出输出grep,但我觉得必须有更好的方法.

Von*_*onC 271

UPDATE:在OP 丹尼尔Stutzbach指出,在评论这个简单的命令git diff-index为他工作:

git diff-index --quiet HEAD --
Run Code Online (Sandbox Code Playgroud)

然后,如果您在bash脚本中使用它,则可以看到" 如何检查命令是否成功? ":

git diff-index --quiet HEAD -- || echo "untracked"; // do something about it
Run Code Online (Sandbox Code Playgroud)

注:由Anthony Sottile 评论

git diff-index HEAD ...将在没有提交的分支上失败(例如新初始化的存储库).
我找到的一个解决方法是git diff-index $(git write-tree) ...


"以编程方式"意味着永远不要依赖瓷器命令.
始终依赖管道命令.

另请参阅" 使用Git检查脏索引或未跟踪文件 "(如haridsv)

您可以从我们发言时写的新" git diff-files功能 "中获取灵感;)(2010年10月初)

git add ${file_args} && \
git diff-index --cached --quiet HEAD || git commit -m '${commit_msg}'
Run Code Online (Sandbox Code Playgroud)

  • 点击你建议的一些链接后,我找到了我要找的东西:`git diff-index --quiet HEAD`. (18认同)
  • @DanielStutzbach:如果在工作目录中有一个名为`HEAD`的文件,那可能会失败.更好地使用`git diff-index --quiet HEAD --`. (11认同)
  • "管道与瓷器为脚本"的原则是[JakubNarębski](http://stackoverflow.com/users/46058/jakub-narebski)向我反复提到的一个教训:"[如何列出当前的所有日志项目在git?](http://stackoverflow.com/questions/2978947/how-to-list-all-the-log-for-current-project-in-git/2979124#2979124)","[git:每天更换日志](http://stackoverflow.com/questions/2976665/git-changelog-day-by-day/2976776#2976776)",... (10认同)
  • 然而,`git status --help`中的手册指出:** - 瓷器**_以易于解析的格式为脚本提供输出.这类似于短输出,但在Git版本中保持稳定,无论用户配置如何.详见下文._ (7认同)
  • @VonC真的没有意义.这样你就可以扭转一切. - 瓷器给你的印象很快就会破裂.如果不是,它应该被称为管道,而不是瓷器.使用--porcelain会导致您的脚本不会中断,这使得它不是瓷器脚本;-).如果你想让你的脚本破解,你不应该使用--porcelain !!.所以这完全不可理解,并把所有人都抛弃了. (7认同)
  • 提议的函数require_clean_work_tree未检测到新的(未跟踪的)文件。我需要检测任何更改(甚至忘记了新文件),所以我选择了Nepthar的答案:`[[-z $(git status -s)]]` (2认同)
  • @theDmi 其实这是有道理的:“这里的--porcelain 的意思是“生产适合瓷器脚本消费的输出”。请参阅我在 http://stackoverflow.com/a/6978402/6309 上回答的结尾。所以是的, `git status --porcelain` 非常适合编写脚本。 (2认同)

Nep*_*har 88

虽然其他解决方案非常彻底,但如果您想要一些非常快速和肮脏的东西,请尝试以下方法:

[[ -z $(git status -s) ]]
Run Code Online (Sandbox Code Playgroud)

它只检查状态摘要中是否有任何输出.

  • 适合我.使用-n作为逆(你有变化),例如`if [[-n $(git status -s)]]; 然后...... fi` (6认同)
  • 这几乎是正确的答案,但对于脚本,最好使用 [`--porcelain`](/sf/answers/488488171/) 参数,如图 [here](https://stackoverflow.com/ a/5737794/350384) (3认同)
  • @EM在这个测试中实际上忽略了`git status`的返回码.它只关注输出.查看[此bash相关页面](http://wiki.bash-hackers.org/commands/classictest#string_tests)以获取有关`[`,`[[`以及如何在bash中进行测试的更多信息). (2认同)
  • 您可能需要使用`git status -s -uall`来包含未跟踪的文件。 (2认同)
  • @barfuin 看起来“git status”默认为“all”。来自 `git status -h`:`-u, --untracked-files[=<mode>]` `显示未跟踪的文件,可选模式:全部、正常、否。(默认:全部)` (2认同)

Jos*_*Lee 53

git diff --exit-code如果有任何变化,将返回非零值; git diff --quiet没有输出是一样的.由于您要检查工作树和索引,请使用

git diff --quiet && git diff --cached --quiet
Run Code Online (Sandbox Code Playgroud)

要么

git diff --quiet HEAD
Run Code Online (Sandbox Code Playgroud)

任何一个都会告诉您是否存在未提交的未提交的更改.

  • `git diff --quiet && git diff --cached --quiet`未检测到新的(未跟踪的)文件. (23认同)
  • 那些不等同.单个命令`git diff --quite HEAD`只会告诉你工作树是否干净,而不是索引是否干净.例如,如果在HEAD~和HEAD之间改变了`file`,那么在`git reset HEAD~ - file`之后,即使索引中存在分阶段的变化,它仍然会退出0(wt == HEAD,但是索引) != HEAD). (6认同)
  • 警告,这不会使用 git rm、AFAICS 捕获从暂存区删除的文件。 (2认同)

Tra*_*der 14

扩展@Nepthar的答案:

if [[ -z $(git status -s) ]]
then
  echo "tree is clean"
else
  echo "tree is dirty, please commit changes before running this"
  exit
fi
Run Code Online (Sandbox Code Playgroud)

  • 如果你将 `git status -s` 设置为 `git status --porcelain ;git clean -nd` 相反,垃圾目录也会在这里出现,这些目录对 `git status` 是不可见的。 (2认同)

Myr*_*ium 9

一些答案既使问题过于复杂,又没有达到预期的结果。例如,接受的答案会遗漏未跟踪的文件。

尽管有些人(错误地)在评论中另有说法,但仍使用git status --porcelain设计为机器可解析的提供。如果git status. 所以我用 test 测试清洁度,[ -z "$(git status --porcelain=v1 2>/dev/null)" ]如果在 git 目录之外运行,它也会通过。

最低工作示例:

[ -z "$(git status --porcelain=v1 2>/dev/null)" ] && echo "git undirty"
Run Code Online (Sandbox Code Playgroud)

出现在git status(截至目前)中的任何内容都将正确触发此测试。该=v1位确保跨 git 版本的输出格式一致。


额外:计算脏文件

受到这个答案的启发。你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
Run Code Online (Sandbox Code Playgroud)


Cer*_*vEd 9

工作树是“干净的”如果

git ls-files \
  --deleted \
  --modified \
  --others \
  --exclude-standard \
  -- :/
Run Code Online (Sandbox Code Playgroud)

什么也不返回。

解释

  • --deleted检查工作树中删除的文件
  • --modified检查工作树中修改的文件
  • --others检查工作树中添加的文件
  • --exclude-standard根据通常的.gitignore.git/info/exclude...规则忽略
  • -- :/所有内容的路径规范,如果不在存储库的根目录中运行则需要

如果工作树是干净的,则输出为空


stk*_*stk 6

我创建了一些方便的 git 别名来列出未暂存和暂存的文件:

git config --global alias.unstaged 'diff --name-only'
git config --global alias.staged 'diff --name-only --cached'
Run Code Online (Sandbox Code Playgroud)

然后,您可以轻松执行以下操作:

[[ -n "$(git unstaged)" ]] && echo unstaged files || echo NO unstaged files
[[ -n "$(git staged)" ]] && echo staged files || echo NO staged files
Run Code Online (Sandbox Code Playgroud)

您可以通过在PATH被调用的某处创建一个脚本来使其更具可读性git-has

#!/bin/bash
[[ $(git "$@" | wc -c) -ne 0 ]]
Run Code Online (Sandbox Code Playgroud)

现在上面的例子可以简化为:

git has unstaged && echo unstaged files || echo NO unstaged files
git has staged && echo staged files || echo NO staged files
Run Code Online (Sandbox Code Playgroud)

为了完整起见,这里是未跟踪和忽略文件的类似别名:

git config --global alias.untracked 'ls-files --exclude-standard --others'
git config --global alias.ignored 'ls-files --exclude-standard --others --ignored'
Run Code Online (Sandbox Code Playgroud)


小智 5

使用 python 和 GitPython 包:

import git
git.Repo(path).is_dirty(untracked_files=True)
Run Code Online (Sandbox Code Playgroud)

True如果存储库不干净,则返回

  • 请注意,GitPython 也只是使用 git CLI。如果设置“LOGLEVEL=DEBUG”,您将看到它用来运行“git diff”的所有 Popen 命令 (2认同)