Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the proper way to determine the status of a Git branch programmatically?

I read over various other SO posts and Google search results that git status --porcelain is not really the command you want to be relying on if you are parsing out the current git branch status programmatically. I was eventually pointed to rev-parse, diff-index, and diff-files commands for doing this - however, the method I'm currently using is a little buggy, particularly on branches other than master. Themes like Bureau for oh-my-zsh seem to be using git status --porcelain, which I stated above was not recommended by the Git community. So what's the proper way to read in such branch statuses as these?

Code segment from the Bureau Oh-My-ZSH theme so that it's clear what behaviour I'm trying to reproduce.

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
}

I'm eventually going to support all of the behavior above, by here is my start on that and the basic commands I'm currently using to do things (sorry about the fact it's Haskell, hopefully that doesn't prevent anyone from getting the gist of what the code is doing - no pun intended).

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"] []

Edit AndrewC pointed out that --porcelain is described in the docs as being purposed for parsing by scripts. This causes me to ask the question, when should I use rev-parse vs. --porcelain??

like image 483
josiah Avatar asked Jan 30 '15 16:01

josiah


People also ask

How do you check if a git branch is up to date?

To check if you're up-to-date with GitHub run git fetch origin before git status and you'll know you're up-to-date.

How do you check the status of your local git?

To check the status, open the git bash, and run the status command on your desired directory. It will run as follows: $ git status.

What information can you find with git status?

The git status command displays the state of the working directory and the staging area. It lets you see which changes have been staged, which haven't, and which files aren't being tracked by Git. Status output does not show you any information regarding the committed project history.


1 Answers

Just so there is an official answer:

As in the comments, the docs DO say that the --porcelain flag with Git Status is there to provide parsing for scripts. My source of confusion is that, in general, this is not the role of the porcelain flag, and traditionally, a 'plumbing' command would normally be specified for such a purpose in Git. Thus, in this case, using the --porcelain flag seems to be an accepted way of parsing out the status of Git repositories, but this is an exception to what --porcelain normally means.

More details are covered in the below SO posts that I uncovered while searching around for better explanations. What does git rev-parse do? What does the term "porcelain" mean in Git?

like image 180
josiah Avatar answered Oct 03 '22 19:10

josiah