Have you heard about Envirotechnical? Let's fight climate change one byte at a time :) Learn More →

Published on

Git forward and backward in tree history. It's not Klingon, it's a nice Bash oneliner

Table of Contents

Bash oneliner to move forward and backward in Git tree

A bash terminal with Ubuntu showing

This is not going to be one of the usual articles where I'm going to drop off some jokes while we all learn something new.

Pinky swear

Who am I kidding.

So. Long story short, the other day I was developing a little CLI that should be helpful for beginners to learn the ways of git while also embracing a very opinionated way of dealing with version control both locally and with origin.

The tool is called flig but this is not yet the time to unveil the badass tool so :bear: with me while we discover a really nice oneliner I developed to: * drum rolls *

Moving back and forth in the git commit tree without prior knowledge of the commit hashes.

Yeah, I did tell you it was cool. You just didn't expect this cool. Deal with it.

Deal with it
git log --all --oneline | grep -B 1 $(git rev-parse --short HEAD) | awk '{print $1}' | head -1 | xargs -I {} git checkout {}

This is the little magical oneliner that can help us move forward while, to move backward, we need to do a little change:

git log --all --oneline | grep -A 1 $(git rev-parse --short HEAD) | awk '{print $1}' | tail -1 | xargs -I {} git checkout {}
Can you see the change, Waldo?

Can you see the change?

I'll leave it as a very boring exercise to the reader to find the only thing that changed between those two. Or you can even go further and diff to find the differences. That'd be an overkill tho.

So, anyway. Explanation time.

The little | character is called pipe and it's a very useful terminal tool that pretty much takes whatever the output of the command is and forwards it to the next command. Very badassy.

This first part of the command is the entry point. It pretty much asks git to show the logs of all commits in a oneline fashion. Afterwards, it pipes everything that the command outputted to the next one.

git log --all --oneline |

The output would look like this:

c4cb3f1 (HEAD -> main, origin/main, origin/HEAD) A commit message
453a23b A commit message
e99539d A commit message
f073dd5 A commit message
973354d A commit message
90ff07c A commit message

With this command we are using the utility grep which helps us find patterns, characters, words and so much more. We use grep to look for what that other (command) is going to output. Big spoilers, it's going to return the hash of the current commit we are on, with a short hash instead of the entire row.

Now, take a close look at that piece of magnificency that is -B 1. That bad boi is actually allowing us to also return the previous row of the output, effetively returning not only the results of the git rev-parse command, but also the previous line.

grep -B 1 $(git rev-parse --short HEAD) |

The output would look like this:

e99539d A commit message of the previous commit
f073dd5 A commit message of the current commit

That output is then passed on (or better yet, piped into) the awk command.

awk '{print $1}' |

The output would look like this:


We are printing/returning just the necessary text we need, which is to say, the current and previous commit short hash :)

That output is then piped into the head command which takes care of selecting just the previous commit, leaving the current one alone.

head -1 |

It will look like this:


And finally the xargs magic comes into play. The head -1 command piped the resulting commit short hash into the xargs utility which will, in this particular case, use that value to replace the {} in the git checkout {} command.

xargs -I {} git checkout {}

By wrapping it all together in a single line command we just created a command to move back (and with the other) forth the git commit tree without having prior knowledge of the hashes it contains.

The goodbye

I hope you found this article useful and to your liking and if you have any requests, drop a message on one of my social media accounts or open an issue/start a discussion on github, on this repository!

As always you can find me on Twitter, listen to my Podcast on Spotify and add me on LinkedIn to talk professionally (yeah, right)