There are some common Git tasks that developers do daily - commiting code, pushing to a central repository, switching branches and more. Many do this in the IDE or code editor and some prefer to do this via the command line.

I fall into the latter category. I guess I like the control and the daily exercise of working with Git, so if I need to do something complex with Git itself, I'm well used to using it directly.

However, the common tasks of committing, switching branches, pushing code and more can feel like a bit of a chore and typos can get a little annoying, so I thought I'd level up by automating some parts for efficiency and to avoid typing errors.

I'll show what I came up with shortly, but first let's look at FZF.

What is FZF?

FZF is a command line fuzzy finder. It's an incredibly powerful tool that you can use (at its simplest) to filter lists as you type.

We're going to use this to display a list of branches available, to switch from (when switching branches) and also display a list of files that have changed, to select from (when choosing files to commit).

Okay, let's get to it!

Changing branches using FZF

You can add something like this to your "RC file" in your Home directory (whether that's ~/.bashrc, ~/.zshrc or something else):

# Checkout git branches interactively with fzf (fuzzy finder)
function gcb() {
    if [ "$1" = "all" ]; then
        local BRANCHFOCUS="-a"
    elif [ -n "$1" ]; then
        git checkout $1
        return
    else
        local BRANCHFOCUS=""
    fi
    git branch $BRANCHFOCUS | sort -r | fzf --header Checkout | sed 's/remotes\/origin\///' | xargs git checkout
}

You'll need to open a new terminal window after adding this for it to take effect.

Okay, let's look at the code here to explain what's going on. We've a new function, which I've called gcb (for "git change branch", but it can be called whatever you like).

If the 1st argument you provide is all (that is, you executed gcb all), then the variable BRANCHFOCUS will contain the value -a (meaning "all"), we'll get to use of that shortly.

If the 1st argument wasn't that, but is provided - eg gcb main, we provided the branch name and should switch to that and return straight away.

Otherwise, we didn't provide an argument, we only executed gcb so set BRANCHFOCUS to an empty string and let's continue. So BRANCHFOCUS is either -a or an empty string as we move forward.

The last line to consider is the most complex one, we'll run git branch (to get branches) with either the -a flag or nothing extra, to either get all branches (including remote branches), or just the local ones on your machine. We'll sort that list in reverse order and pass to FZF with the title "Checkout", that'll display our list of branches for us to pick from. From the choice picked, we'll use sed to remove any remotes/origin/ prefix and finally, pass the name chosen to git checkout using xargs.

Wow, that was a lot to explain, but the result is pretty awesome:

gcb         # Shows a list of local branches to pick from
gcb all     # Shows a list of local + remote branches to pick from
gcb <name>  # Will instantly switch to that branch

Here's an example of running gcb all:

It initially shows all 682 available branches and as we type, shows branches that match. Use your arrow keys (or mouse) to select a choice and hit Enter to switch to it. No more trying to find branch names, copy & paste them or even type and get typo errors.

Much simpler!

Committing code using FZF

This next example is just as useful and contains even less code. Here we'll use FZF to display a list of changed files available for committing and then also ask for a commit message:

# Commit file(s) to Git interactively with fzf (fuzzy finder)
function gc() {
    GC_ARGS=$(echo $(git diff --name-only | fzf -m))
    print -P "Enter commit message:"
    /bin/bash -c 'read -e COMMIT_MESSAGE && git commit $1 -m "$COMMIT_MESSAGE"' _ $GC_ARGS
}

Hopefully the code is mostly understandable, but let's look at it. Again, we start a new function, called gc (for "git commit") but call it what you'd like.

It starts another shell inside a shell, which runs git diff and asks for the names only of files that have changed. This list is passed along to FZF using -m ("multi select") so from this list we can pick not just one but many (using the Tab key and Enter to confirm selections).

The choices you make are passed to it's parent shell which echo's them out onto a single line. That's then our list of space separated files we'd like to commit, as the value of GC_ARGS, eg file-abc.js some-other-file.json etc.

Finally, we display some text, which asks to for a commit message, which we'll get keyboard input for using read -e. (We need to do using inside bash, as other shells such as ZShell don't work as you'd probably like with arrow keys if you'd like to traverse your text while editing your message). So we execute the /bin/bash shell with -c to state we're providing a command, and the first part of what we provide is read -e ...so Bash is executing this for us.

The result of the keyboard input is stored in variable COMMIT_MESSAGE and finally we use git commit with that message, on the files within GC_ARGS - which are passed along to the Bash shell as $1 as the first argument. (Note that _ is just a placeholder for the name of the shell being used, not important to us).

Here's an example of usage, after I amended 3 files, so they're ready to commit and running gc:

Fantastic - it shows the 3 files I changed, can use Tab to select files (here I selected tsconfig.json and .typos.toml) and when I hit the Enter key it asks for a commit message, which will then commit those couple of files, with that commit message. Sweet!

Wrapping up

Shell code can feel a little complex, but in a relatively small amount of code (and with the help of FZF) we can have a more efficient, dynamic dev experience that helps in everyday tasks such as changing branches and commiting files that we select.

This really only scratches the surface of what FZF can do, but hopefully this shows a couple of uses that can save you time (and frustration) during your dev day!

Let me know your thoughts and if you've any other great use cases.

Author Of article : Matt Pass Read full article