【问题标题】:Fastest way to get git status in bash在 bash 中获取 git 状态的最快方法
【发布时间】:2012-06-20 15:02:38
【问题描述】:

一段时间以来,我一直在 bash 的 PS1 提示符中使用 __git_ps1 函数 (PS1='\w$(__git_ps1)')。现在我想根据分支给它上色 地位。

我写了一个 bash 函数来检查当前分支是否被修改,并且 根据状态颜色为红色或白色。问题是它使用git status 来检查状态(这是我知道的唯一方法),这是几个 比__git_ps1 慢几倍,这足以引起恼人的延迟 我正在使用提示(我使用的是非常弱的上网本)。

所以我问:有没有更快的方法来检查当前 git 文件夹的状态? __git_ps1 比手动解析 git branch 快很多,所以我在想 可能还有其他一些隐藏的 git 功能。

【问题讨论】:

  • 澄清一下:你想知道分支是否有未跟踪的文件吗?
  • @Petervander 是的。 git status 将报告的任何内容。
  • 注意:Git 2.6+(2015 年第三季度)应该会改善这一点。见my answer below

标签: git bash


【解决方案1】:

不完全是你的答案,但 bash-completion 有这个内置的。

如果您将 bash ENV GIT_PS1_SHOWDIRTYSTATE 设置为非空值,则未暂存 (*) 和暂存 (+) 更改将显示在分支名称旁边。您可以使用 bash.showDirtyState 变量配置此每个存储库,一旦启用 GIT_PS1_SHOWDIRTYSTATE,该变量默认为 true。

您还可以通过将 GIT_PS1_SHOWSTASHSTATE 设置为非空值来查看当前是否隐藏了某些内容。如果某些东西被隐藏了,那么分支名称旁边会显示一个“$”。

如果您想查看是否有未跟踪的文件,则可以将 GIT_PS1_SHOWUNTRACKEDFILES 设置为非空值。如果有未跟踪的文件,则分支名称旁边会显示一个“%”。

不确定启用此功能时速度会降低。 如果你想做着色:

暂存文件:

if git rev-parse --quiet --verify HEAD >/dev/null; then
 git diff-index --cached --quiet HEAD -- || color for staged changes
else
  color unstaged changes
fi

隐藏文件

git rev-parse --verify refs/stash >/dev/null 2>&1 && color for stashed files

未跟踪的文件

if [ -n "$(git ls-files --others --exclude-standard)" ]; then
  Color untrack files
fi

上面的 sn-ps 来自 bash-completion 脚本。

【讨论】:

  • 不错的答案,但第一个脚本对我不起作用。无论暂存/未暂存状态如何,git rev-parse --quiet --verify HEAD >/dev/null 似乎总是返回 0
  • 唯一对我不起作用的方法是当我还没有分支时。 git rev-parse --verify HEAD的输出是什么
  • 一个大的十六进制数。我试着玩弄一下分支,但它总是返回 0。不过这不是什么大问题,这是我决定使用 GIT_PS1 变量的其他东西。您是从哪里得知他们的?
  • 数字是个好东西,命令本身有效。不知道为什么它在你的情况下返回 0。 sn-ps 来自 bash 完成脚本。它是在安装 git 时安装的。 __git_ps1 是该脚本中的一个命令。你可以在目录contrib/completion的git源码中找到它
【解决方案2】:

注意:Git 2.6+(2015 年第三季度)应该会加速 __git_ps1 状态:

参见commit dd160d7commit 6bfab99(2015 年 7 月 19 日)SZEDER Gábor (szeder)
(由 Junio C Hamano -- gitster -- 合并到 commit 461c119,2015 年 8 月 3 日)

bash 提示:更快的未跟踪状态指示器与未跟踪目录

如果启用了未跟踪状态指示器,__git_ps1() 通过运行“git ls-files”来查找未跟踪文件。
如果未跟踪目录包含大量文件,这可能会明显变慢,因为它列出了在未跟踪目录中找到的所有文件,只会立即重定向到 /dev/null
这是__git_ps1()实际运行的命令:

$ ls untracked-dir/ |wc -l
100000
$ time git ls-files --others --exclude-standard --error-unmatch \
  -- ':/*' >/dev/null 2>/dev/null

real    0m0.955s
user    0m0.936s
sys 0m0.016s

通过另外将“--directory --no-empty-directory”选项传递给“git ls-files”以仅显示非空未跟踪目录的名称而不是其所有内容来消除此延迟

$ time git ls-files --others --exclude-standard --directory \
  --no-empty-directory --error-unmatch -- ':/*' >/dev/null 2>/dev/null

real    0m0.010s
user    0m0.008s
sys 0m0.000s

这个效仿ea95c7b(完成:改进未跟踪目录 过滤文件名完成,2013-09-18,git 1.8.5)。


确保使用 Git 2.29(2020 年第四季度),因为在影响 git ls-files 的部分代码中提供了泄漏修复。

参见Elijah Newren (newren)commit eceba53commit dad4f23(2020 年 8 月 18 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit ad00f44,2020 年 8 月 24 日)

dir:修复有问题的 API 以避免内存泄漏

签字人:Elijah Newren

dir 结构似乎存在许多漏洞和问题。

首先我注意到 parent_hashmaprecursive_hashmap 被泄露(尽管 Peff 在我之前注意到并提交了修复)。

然后我注意到在之前的提交中clear_directory() 只负责dir_struct, 中的一部分字段,尽管我们在内部分配了entry[] 和ignore[] 给dir.c
当然,这导致许多调用者要么泄漏,要么随意尝试释放这些数组及其内容。

进一步挖掘,我发现尽管dir.h 顶部附近有非常清晰的文档,当用户不再需要dir_struct, 时,人们应该调用clear_directory(),但有四个调用者没有打扰完全没有。
然而,他们中的两个显然考虑到了泄漏,因为他们有一个 UNLEAK(dir) 指令,在我看来,这表明释放数据的方法太不清楚了。
我怀疑 API 的不明显性及其漏洞导致人们避免使用它,然后滚雪球般地引发entries[]ignored[]parent_hashmap,recursive_hashmap 问题的进一步问题。

clear_directory() 重命名为dir_clear() 以更符合git 中的其他数据结构,并引入dir_init() 来处理建议的dir_struct 的memsetting 为全零。
我希望像 "dir_clear()" 这样的名字更清楚,dir_init() 的存在将为那些查看代码的人提供一个提示,他们需要寻找 dir_clear()dir_free() 并引导他们找到dir_clear()

影响:

  • git add
  • git check-ignore
  • git clean
  • git grep
  • git ls-files
  • git stash
  • git merge

【讨论】:

    【解决方案3】:

    git diff --quiet 如果工作目录发生更改,则返回 1,git diff --quiet --cached 对索引执行相同操作。

    你可以用它来做:

    git diff --quiet
        || echo "There be changes!"
    

    【讨论】:

      【解决方案4】:
      ! git diff-index --cached --quiet HEAD      # fastest staged-changes test
      ! git diff-files --quiet                    # fastest unstaged-changes test
      ! git diff-index --quiet          HEAD      # fastest any-changes test
      stdbuf -oL git ls-files -o | grep -qs .     # fastest untracked-files test
      git rev-parse -q --verify refs/stash >&-    # fastest any-stash test
      

      如果忽略状态对未跟踪文件很重要,请使用 --exclude-standard -oi 表示未跟踪-忽略,--exclude-standard -o 表示未跟踪和未忽略。

      我知道获得 HEAD 绰号(分支名称或 sha)的最快方法是

      { git symbolic-ref -q --short HEAD || git rev-parse -q --short --verify HEAD; } 2>&-
      

      如果你不在回购中,它会留下一个返回码,所以你可以例如

      if HEAD=`{ git symbolic-ref -q --short HEAD || git rev-parse -q --verify HEAD;  } 2>&-`
      then
              : in a git repo, $HEAD is the branch name or sha
      fi
      

      请注意,未跟踪文件测试取决于使用 shopt -s lastpipe 运行 bashzshksh 默认以这种方式运行。

      【讨论】:

      • 非常有趣的反馈。赞成。我会尝试在我自己的 bash 函数中使用它。
      【解决方案5】:

      我正在使用 ksh 并且遇到了同样的问题。最重要的是,我的 Git 存储库位于 NFS 挂载上,这使得响应时间更慢。

      要直接回答有关速度的问题,请尝试使用:

      git ls-files -m
      

      在第一次进入不同的存储库后,似乎比 'git status -s' 快得多。一旦进入存储库,操作系统和 NFS 缓存似乎会大大加快此命令的速度。当深入到 git 工作目录树的底部时,这个命令也显得非常快速。

      【讨论】:

        【解决方案6】:

        所有带有git 的解决方案最终都会受到git 运行速度的限制。你可以让git 本身运行得比通过将一些 fs-monitoring hooks 输入到 git 中更快:

        或者你可以做更少的工作,比如--directory --no-empty-directory,但更进一步:

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-07-28
          • 2020-08-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多