DEV Community

Discussion on: Terminal tricks from my dotfiles

Collapse
 
gredelston profile image
Greg Edelston • Edited
# Return the branch name if we're in a git repo, or nothing otherwise.
git_check () {
  local gitBranch=$(git branch 2> /dev/null | sed -e "/^[^*]/d" -e "s/* \(.*\)/\1/")
  if [[ $gitBranch ]]; then
    echo -en $gitBranch
    return
  fi
}
Enter fullscreen mode Exit fullscreen mode

To be honest I don't exactly know how this works. But if we're in a git repo it outputs the branch name, otherwise it does nothing.

Let's talk about it!

git_check () {
    ...
}
Enter fullscreen mode Exit fullscreen mode

This defines a function, git_check, which you can call later in the script.

  local gitBranch=$(...)
Enter fullscreen mode Exit fullscreen mode

This defines a local variable, gitBranch, which can only be used within the scope of git_check. The assigned value will be the output of the command within the $(...).

  git branch 2> /dev/null
Enter fullscreen mode Exit fullscreen mode

This runs the git branch command, and sends the stderr to /dev/null, effectively wiping the error message from existence. Stdout will still be printed, though. Thus, if you are inside a git repository, then you'll see the output of git branch; and if you're not, you won't see any error message.

As a reminder, here's what git branch output will look like if you're in a git repository...

  bar
* foo
  quux
Enter fullscreen mode Exit fullscreen mode

In the above example, we see all the branches in your local checkout, alphabetized; and the active branch is prefixed with *.

Moving on.

  | sed -e "/^[^*]/d" -e "s/* \(.*\)/\1/"
Enter fullscreen mode Exit fullscreen mode

We're piping the output of git branch into the sed command. sed is short for "stream editor"; we'll use it to transform the output of the git branch command, line-by-line. Each -e flag denotes a transformation script. Let's look at the two scripts.

  -e "/^[^*]/d"
Enter fullscreen mode Exit fullscreen mode

The /.../d command is documented as "Delete pattern space. Start next cycle." So, any line matching the regex pattern ^[^*] will be ignored. That regular expression has two components:

  1. ^: Start-of-line.
  2. [^*]: Match any character other than the literal *.

Thus, the regex will match any line which begins with a character other than the literal *. If you look at the example git branch output I pasted above, this would match the line for any branch except the active branch. Putting it all together, sed -e "/^[^*]/d" will ignore any line except for the active branch line.

Then, we have the other sed command:

  -e  "s/* \(.*\)/\1/"
Enter fullscreen mode Exit fullscreen mode

The s/regexp/replacement/ command will attempt to match the regexp against the input, and if successful, will replace that portion with the replacement. So let's look at the regexp and the replacement.

First, let's look at the regex: * \(.*\).

* at the beginning of the regex matches the literal *.
matches the literal space character, .
\(...\) creates a capture group. sed will replace the escaped characters \( and \) with parentheses. (If we wanted to capture literal parenthesis characters, we would use \\(foo\\)).
.* matches any quantity (including zero) of any character(s). It's a wildcard.

In short, this regex will match any line starting with the literal *, and everything thereafter will be remembered in a capture group.

Next, let's look at the replacement, \1. This syntax refers to the first capture group: which, recall, is everything after the *.

In summary, -e "s/* \(.*\)/\1/ would replace a line like * foo with just foo. It strips away the leading *. If the regex doesn't match, then there is no effect: the line bar would just become bar.

The sum total of our sed statement is that we have eliminated all lines from git branch except the active branch line, and we have removed the leading * from that active branch line. All we are left with is the name of the active branch. And, if we are not inside a git repository, then the sed command would have received no lines for input, and thus would yield no output.

Let's check in with our script. Where are we now? We are in a function called git_check, and we have just created a local variable gitBranch whose value is the name of the active branch. If we are not in a git repository, then the value would instead be the empty string.

Moving on.

  if [[ $gitBranch ]]; then
    ...
  fi
Enter fullscreen mode Exit fullscreen mode

This conditional block will execute if (and only if) the gitBranch variable has a non-empty value. Remember that gitBranch equals the name of the active git branch, but only if we are inside a git repository. Thus, the conditional block will execute if (and only if) we are inside a git repository.

For more info, you might look into the [[ built-in: mywiki.wooledge.org/BashFAQ/031

    echo -en $gitBranch
    return
Enter fullscreen mode Exit fullscreen mode

echo [FLAGS] MESSAGE will print out the message. In this case, we'll print the value of the gitBranch variable, and then return from (exit) our function.

The -e flag enables interpretation of backslash escapes: for example, echo "\t" will print the literal string \t, whereas echo -e "\t" will interpret \t as a special tab character, and will print a tab. (I don't think this flag is necessary here.)

The -nwill suppress the trailing newline. echo will normally print out a newline at the end of your output. For example:

$ echo "Hello!"
Hello!
$ echo -n "Hello!"
Hello!$ exit
Enter fullscreen mode Exit fullscreen mode

So, that's every part of the code-block! To review, we defined a function git_check, which runs git branch, discards any errors, and extracts the active git branch (if any) into the local variable gitBranch. Then, if that variable is not empty, we printed its contents without any surrounding whitespace.

Hope this helps! Thanks for your write-up; I stole several of your nifty tricks :)

Collapse
 
admhlt profile image
Adam Hollett

This is incredible! Thank you for taking the time to explain this part!