git - 以编程方式确定 Git 分支状态的正确方法是什么?

标签 git parsing haskell

我阅读了各种其他 SO 帖子和 Google 搜索结果,如果您以编程方式解析当前的 git 分支状态,git status --porcelain 并不是您真正想要依赖的命令。我最终被指向用于执行此操作的 rev-parsediff-indexdiff-files 命令 - 然而,我的方法目前使用的是一个小错误,特别是在 master 以外的分支上。像 Bureau for oh-my-zsh 这样的主题似乎正在使用 git status --porcelain,我在上面提到的 Git 社区不推荐使用。那么读取这些分支状态的正确方法是什么?

来自 Bureau Oh-My-ZSH 主题的代码段,以便清楚我要重现的行为。

bureau_git_status () {
  _INDEX=$(command git status --porcelain -b 2> /dev/null)
  _STATUS=""
  if $(echo "$_INDEX" | grep '^[AMRD]. ' &> /dev/null); then
    _STATUS="$_STATUS$ZSH_THEME_GIT_PROMPT_STAGED"
  fi
  if $(echo "$_INDEX" | grep '^.[MTD] ' &> /dev/null); then
    _STATUS="$_STATUS$ZSH_THEME_GIT_PROMPT_UNSTAGED"
  fi
  if $(echo "$_INDEX" | command grep -E '^\?\? ' &> /dev/null); then
    _STATUS="$_STATUS$ZSH_THEME_GIT_PROMPT_UNTRACKED"
  fi
  if $(echo "$_INDEX" | grep '^UU ' &> /dev/null); then
    _STATUS="$_STATUS$ZSH_THEME_GIT_PROMPT_UNMERGED"
  fi
  if $(command git rev-parse --verify refs/stash >/dev/null 2>&1); then
    _STATUS="$_STATUS$ZSH_THEME_GIT_PROMPT_STASHED"
  fi
  if $(echo "$_INDEX" | grep '^## .*ahead' &> /dev/null); then
    _STATUS="$_STATUS$ZSH_THEME_GIT_PROMPT_AHEAD"
  fi
  if $(echo "$_INDEX" | grep '^## .*behind' &> /dev/null); then
    _STATUS="$_STATUS$ZSH_THEME_GIT_PROMPT_BEHIND"
  fi
  if $(echo "$_INDEX" | grep '^## .*diverged' &> /dev/null); then
    _STATUS="$_STATUS$ZSH_THEME_GIT_PROMPT_DIVERGED"
  fi

  echo $_STATUS
}

我最终将支持上述所有行为,这是我的开始以及我目前用来做事的基本命令(很抱歉,它是 Haskell,希望这不会阻止任何人从了解代码正在做什么的 Gist - 没有双关语意)。

hasCommitsToPush :: IO (Maybe Bool)
hasCommitsToPush = do
  latestCommits <- liftM (fmap $ deleteNulls . splitOnNewLine) $ parseProcessResponse gitRemoteRefDiff
  case latestCommits
    of Nothing                                      -> return Nothing
       Just []                                      -> return $ Just False
       Just [_]                                     -> return $ Just True -- This case is for a new repository with the first commit in local but not yet pushed.
       Just [latestRemoteCommit, latestLocalCommit] -> return . Just $ latestRemoteCommit /= latestLocalCommit
       _                                            -> return Nothing
  where gitRemoteRefDiff = readProcessWithExitCode "git" ["rev-parse", "@{u}", "HEAD"] []

hasStagedChanges :: IO (Maybe Bool)
hasStagedChanges = liftM (fmap isResponseNull) $ parseProcessResponse gitResponse
  where gitResponse = readProcessWithExitCode "git" ["diff-index","--cached","--ignore-submodules","HEAD"] []

hasUnstagedChanges :: IO (Maybe Bool)
hasUnstagedChanges = liftM (fmap isResponseNull) $ parseProcessResponse gitStatus
  where gitStatus = readProcessWithExitCode "git" ["diff-files","--ignore-submodules"] []

编辑 AndrewC 指出 --porcelain 在文档中被描述为用于脚本解析。这让我问了一个问题,我应该什么时候使用 rev-parse--porcelain??

最佳答案

官方给出的答案是这样的:

如评论中所述,文档确实表示带有 Git Status 的 --porcelain 标志可以为脚本提供解析。我感到困惑的是,一般来说,这不是 porcelain 标志的作用,传统上,在 Git 中通常会为此目的指定一个“管道”命令。因此,在这种情况下,使用 --porcelain 标志似乎是解析 Git 存储库状态的一种可接受的方式,但这与 --porcelain 通常的含义不同。

我在搜索更好的解释时发现的以下 SO 帖子中包含更多详细信息。 What does git rev-parse do? What does the term "porcelain" mean in Git?

关于git - 以编程方式确定 Git 分支状态的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28240388/

相关文章:

git - 如何判断git-hook是否有rebase?

bash - 如何忽略/恢复文件内容中大小写的更改?

java - 为什么我的 SimpleDateFormat 解析不起作用?

Python 请求使用 ast.literal_eval 错误语法无效?

java - 使用univocity解析两个不同的csv文件并写入新的csv文件

Haskell "show is applied to too many type arguments"

haskell - 了解 `5 1`的类型

haskell - 数据类型无效

Android repo init - 如何以非交互方式运行(或没有姓名/电子邮件提示)

git - 如何从 git repo 添加特定文件夹作为 git 子模块?