Skip to content

Low Hanging Fruit

Here are some tips that you can immediately start incorporating into your daily work for some quick wins.

Version everything

Even if you just work on something alone, put it in a Git repository. If you don't require it to be hosted online, you can still get all the benefits of versioning and safe rollbacks when using just a local repository. Many developers think of a local repository as something you get from cloning a remote repository, but creating a local repository using git init is an underrated habit.

Quickly switch between branches using git switch -

Normally you would switch to another branch using git switch my-other-branch. If you need to temporarily switch to another branch, or checkout some commit (git checkout a1b2c3d), then at the point when you need to switch back you might need to look up the branch name again (for example using git reflog) and then type the possibly long branch name. Instead, you can switch to the last branch using git switch -. You can also use it to quickly switch back and forth between two branches.

Visualize the commit graph in the terminal

Although Git GUIs have their place, there's no need to use them just to get a nice visualization of the commit graph.

You're probably familiar with git log. You can use git log --graph to render a nice and clear graph in the terminal. Most UNIX-like terminals use less for paging by default, so you can page up and down using the "u" and "d" keys, scroll (in smaller steps) with the up and down arrow keys, and close with the "q" key.

If you use Git via the CLI but don't frequently use git log --graph or git log --graph --oneline, you might want to make it a habit, especially before and after operations like merge, rebase, force push, etc. It halps you quickly see where you are and what's happening around you.

Avoid unnecessary branch switching

Many developers, when rebasing my-feature onto main (or merging main into my-feature), would start by switching to main, then git pull, then switch back to my-feature, and finally git rebase main (or git merge main).

Instead, make it a habit to simply do git fetch and then git rebase origin/main (or git merge origin/main). This way you not only avoid unnecessary extra steps, but it's also more semantically correct:

  • The intent is to rebase on origin/main, not main, and updating the local main ref is something many developers do because they misunderstand remote tracking branches.
  • Commit messages that Git automatically creates for merge commits will say Merge remote-tracking branch 'origin/main' into my-feature, which correctly communicates the intent.
  • A common mistake is to accidentally merge an outdated local main branch. The less complicated the workflow and the fewer extra steps, the less likely you forget an importent step.
  • You're also less likely to accidentally commit directly to main (assuming a workflow where that is not your goal). You probably don't need a local branch that you never intend to commit to.

Whenever you need to checkout the current state of origin/main, you can always do git checkout origin/main, then create a new branch from there or switch back to your feature branch afterwards. There's no need to keep a redundant local branch just to "track" your remote tracking branch (you probably see the irony in manually tracking a tracking branch).

If git checkout origin/main looks weird or dangerous to you because you think it lets you actually work in a remote repository, make sure to (re-)read the fundamental chapters on refs and remotes.

Enable rerere

rerere stands for "reuse recorded resolution". Have you ever had to resolve the same merge conflict multiple times? With rerere, Git records your merge conflict resolutions and then compares them to future conflicts. If a merge conflict exactly matches a conflict resolution recorded earlier, Git then resolves it automatically and informs you that it applied a recorded conflict resolution.

You might ask: If rerere can be so useful, why is it not enabled by default? That's because Git is very conservative when it comes to enabling new features that change existing workflows. There's no good reason to not enable it, it just doesn't want to suprise you with changed default behavior when you upgrade from an older version of Git.

Go ahead, you can enable it by running git config --global rerere.enabled true.

When force pushing, always --force-with-lease

Assuming that force pushing is actually what you want, if you would so far have used git push --force, make it a habit to use the slightly longer git push --force-with-lease by default. With tab completion in your CLI, you just type "--force-w *tab*" anyway and let tab completion do the rest.

Chances are you'll never need git push --force and you should think of it as deprecated. Git is very conservative when it comes to changing default behavior. New features that are meant to replace existing ones are often added as an alternative command or an option for an existing command.

Here's the difference between both options:

  • --force, as you probably know, updates a remote branch even if it results in commits being dropped.
  • --force-with-lease, the lesser known option, does the same, except it ensures that commits that don't also exist in your local repository cannot be dropped. Thus, if you realize you accidentally force pushed the wrong branch or otherwise dropped the wrong commits, you can always recover from your mistake by simply pushing the same commits again. If commits would be dropped that don't exist locally, Git[^1] will simply throw an error and not execute the force push.

Good habits not only reduce mistakes, but also make it easier to recover from the ones that do happen.

You should also make sure to check the commit history of the affected branches (local and remote) before force pushing, so that you're actually aware that you're dropping commits. Remember that only commits that don't exist locally will be protected.

Further reading:

[^1]: It's actually the remote repository rejecting the force push, because your local Git will notify the remote which commits you allow to be dropped.

Further reading

If you want more details and an example, read the chapter on rerere in the Git Book.
Once you've enabled rerere you can simply benefit passively from less hassle with merge conflicts, but if want to know how you can reset recorded resolutions (e.g., if you realize you accidentally resolved a conflict incorrectly), you can look up the docs of the rerere command.