DEV Community

Cover image for How to only show commits of current branch
Lucas Perez
Lucas Perez

Posted on • Updated on

How to only show commits of current branch

🇵🇹 Versão em português desse texto aqui!

Every now and then I make aliases that execute a command with a few flags that I use often. One of them being an alias for git log --oneline.

The problem is that when I executed this command, it would show all the commits untill my screen is filled, and the last line shows that it has more to come. If I press q, I will return to my prompt, but the most recent commit will be pushed out of my screen (because my prompt has two lines).
image of git commits filling up entirely a terminal screen

Other than this specific problem to my big prompt, I usually just want the commits on the branch I am right now. A simple way that crossed my mind was git log --oneline main..HEAD (or maybe use main^). This looked like a simple new alias for my collection, but then I remembered that I have projects with a main branch and also other projects with a master branch.

I decided, then, to practice my shell scripting and make a script that would decide between main or master automatically.

First things first

Before starting, let me plan what will be done beforehand.

If I would write some pseudo code of this script or summarize it in steps, how would that be?
Also, what should happen if I am not in a git repository?
Let's think about these things while writing down a plan:

We have to:

  1. Print some error message and exit non zero code if not in a git repository
  2. Find out if either main or master branch is available
  3. Execute git log main^/master^..HEAD if possible
  4. Print some error message and exit non zero code otherwise

1 - Check if in a git repository

A simple, and not very good, way would be to just execute ls .git. If it exits 0, we are in a git repo. If not, then we are not.

This is not a very good solution because it will not work if we are inside a subdirectory of a git repository. We have to think about something else, but hopefully git itself already has some quick way to check this.

And surely it has, since git is so nice.

A quick google search showed me that the command git rev-parse --git-dir can be used for this task. man git rev-parse shows more details, and the --git-dir option indeed looks like a good option. When we are not in a git repo, it prints an error message and exits "non zero", according to the manual.

I'm not really interested in the error message, just the exit code (zero or non zero).

So to check if we are in a git repository while also hiding possible error messages, I did this:

#!/bin/sh

if [ ! "$(git rev-parse --git-dir 2> /dev/null)" ]; then
  echo Not in a git repository
  exit 1
fi
Enter fullscreen mode Exit fullscreen mode

The [ ... ] syntax is a shorthand for the test command.
The ! will invert the check, if the command (in this case, git rev-parse --git-dir) returns 0, then this will be considered "false", or a "failure".
The 2> /dev/null is redirecting the error message to /dev/null. This is a simple way of not showing error messages.

So basically what this is doing is checking if the command failed. If it did fail, it means we are not in a git repo, so an error message is printed and the script returns 1. If the command succeeds, nothing happens, and we can proceed to our next steps.

2 - Find out if main or master exists

We can do this using the git branch command and some grepping, but git rev-parse can also do it.

The second option is very simple, git rev-parse --verify <branch-name> will do the trick (pass -q to hide error messages):

git rev-parse -q --verify main
Enter fullscreen mode Exit fullscreen mode

Just for a change, though, I'll use the simple (maybe naive) grep way. By doing so, we can learn more about regexes!

The command git branch shows all the branches you have avaiable in your local machine, with the current branch being preceded by a *.
image of git branch showing a main branch and another-branch
Basically we can grep for main or master.

There is a danger, though. Simply executing:

git branch | grep main
Enter fullscreen mode Exit fullscreen mode

will return successfully if any branch containing the word main exists.
image of git branch showing main branch but also not-main branch

To solve this problem, we can use a better regular expression than just main. The pattern we are looking for is:

  1. Start of the line
  2. Possibly a *, but could not have it
  3. Whitespaces (the amount depends if a * was there or not)
  4. The word main (or master) exactly
  5. End of the line

Let's break down how to do each part:

  1. The regex start of the line token is ^
  2. In regex, a * is a quantifier, meaning "the thing before me between 0 and unlimited times", so to match the * literal, we have to escape it: \*, and to make it optional, we use the ? quantifier (it means "the thing before me between 0 and 1 times"). So to match a possible *, the regex would be \*?
  3. Whitespaces can be simply a space or \s, the latter matching things like tabs and new lines also. The amount of whitespaces can vary, so we can use the * quantifier, leaving us with \s*
  4. The words main or master can be matched with the literal words
  5. The end of the line is the token $

So our regex would be ^\*?\s*main$ for the main branch. For the master, of course, is extremely similar.

An important note, to use some special regex characters, grep have to be invoked with the -E flag (the extended regex flag)!

The command for the main branch could then be:

git branch | grep -E '^\*?\s*main$'
Enter fullscreen mode Exit fullscreen mode

This should work for us right now.

3 - Execute git log for the correct branch

We can do this with a sequence of if/elif commands. If the main exists, use it. If not, but the master exists, use master instead.

We could store the result of the last step in a variable and check it, or we could just check it directly:

if [ "$(git branch | grep -E '^\*?\s*main$')" ]; then
  git log main^..HEAD
  exit 0
elif [ "$(git branch | grep -E '^\*?\s*master$')" ]; then
  git log master^..HEAD
  exit 0
fi
Enter fullscreen mode Exit fullscreen mode

This snippet is fairly simple. It checks if main exists using the regex we defined. If it does, it git logs the range from the main (inclusive, since we added ^) to HEAD. It then exits 0 to finish the script (this exit is probably not needed and also not very good, because if there is some bug in our logic/regex, it can fail silently, exiting 0!).

4 - Print error message and exit non zero if main/master not found

We can simply add an else statement to the snippet above:

if [ "$(git branch | grep -E '^\*?\s*main$')" ]; then
  git log main^..HEAD
  exit 0
elif [ "$(git branch | grep -E '^\*?\s*master$')" ]; then
  git log master^..HEAD
  exit 0
else
  echo 'Neither main nor master branch found'
  exit 2
fi
Enter fullscreen mode Exit fullscreen mode

Does it work?

We can test our script now! (:

I saved a file named git-log-main-master with our script and gave it execution permission. Let's try it out:
image showing only commits from main to HEAD

Now that's nice. But can we make it better?

What if we could pass the standard git log flags to it?

Flags!

This is fairly simple to do. In a shell script, the special variable $@ contains all the arguments passed to the script, so we can simply append it to our "git log" lines:

if [ "$(git branch | grep -E '^\*?\s*main$')" ]; then
  git log main^..HEAD "$@"
  exit 0
elif [ "$(git branch | grep -E '^\*?\s*master$')" ]; then
  git log master^..HEAD "$@"
  exit 0
else
  echo 'Neither main nor master branch found'
  exit 2
fi
Enter fullscreen mode Exit fullscreen mode

Now if we pass a bunch of flags, it is going to execute git log with those options:
image showing the script being run with --oneline and --graph flags correctly

Is there a way to make it even nicer to use? What if we passed a branch name to use instead of either main or master?

We could definitely do that. Maybe we could assume the first argument is either a branch name or a flag and use the positional special arguments $1, $2 etc.

Maybe the command could be git log $1^..HEAD $FLAGS, where $FLAGS would be all the arguments passed but the first.

This would involve some better treatment and checks to see if all the arguments are just flags (in this case we could keep main/master and $@) or if a branch name was passed, and is definitely doable, but I'll leave for possibly another post for now... 😅

Aliases...?

Like I said, I usually make aliases for simple commands with common flags. --oneline is definitely one of my most used options for git log, and sure enough, I made an alias:

alias gl='~/scripts/git-log-main-master --oneline'
Enter fullscreen mode Exit fullscreen mode

Summed up script

#!/bin/sh

##############################
# Non zero exit codes:
#   1 - If not in a git repository
#   2 - If neither git branch main nor master is found
##############################

if [ ! "$(git rev-parse --git-dir 2> /dev/null)" ]; then
  echo Not in a git repository

  exit 1
fi

if [ "$(git rev-parse -q --verify main)" ]; then
  git log main^..HEAD "$@"

  exit 0
elif [ "$(git rev-parse -q --verify master)" ]; then
  git log master^..HEAD "$@"

  exit 0
else
  echo 'Neither main nor master branch found'

  exit 2
fi
Enter fullscreen mode Exit fullscreen mode

That's it for today

I'm still learning the very basics of shell scripting, and there are probably better ways of doing this. Any comment and advice is more than welcome! 😊

Discussion (0)