Git is difficult. On top of that, for many new developers, it’s the first tool with a command line interface (CLI) that they use. It can be a bit too much if you’re learning all the following at the same time:

In this article, I’ll show you a few tricks to make your Git experience less painful and more fun!

Check your state

Git is generous enough to offer many commands to check the state of your repository, yet prudent enough not to overwhelm the beginner with verbosity. In short—you should ask Git to give you the details you need. You have following command for it:

git status

A key command to get a glimpse of where you are in Git. Example outputs:

$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

for when you are on clean branch and up to date

$ git status
HEAD detached at abc01e7
nothing to commit, working tree clean

for when you are in detached HEAD state.

$ git status
On branch main
Your branch is up to date with 'origin/main'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        lorem-ipsum.txt

nothing added to commit but untracked files present (use "git add" to track)

When you have some new files in your working copy.

git show

This is a command that allows you to see changes that happened in a commit. When run without an argument, it shows you the current commit:

$ git show
commit abc01e761cb9cc14c4d5aecae2488810c834c0f9 (HEAD -> main, origin/main, origin/HEAD)
Author: Marcin Wosinek <[email protected]>
Date:   Wed Nov 2 11:57:33 2022 +0100

    Add lorem ipsum to readme

diff --git a/README.md b/README.md
index 8ae0569..9dca8b4 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
 # Test
+Lorem ipsum

You get all the details about the commit: its author, time, message and diff of each file that was changed.

You can specify any commit you want to see:

$ git show edd3504
commit edd3504f6edc722482fa4383443fa1729acc9a87
…rest of the output…

Note! This command works on commits. Thus, if you use branch name, it will show the most recent commit from that branch:

$ git show main
commit abc01e761cb9cc14c4d5aecae2488810c834c0f9 (HEAD -> main, origin/main, origin/HEAD)
Author: Marcin Wosinek <[email protected]>

git tree—a custom alias I recommend to everybody

I recommend defining a tree alias to see the graph of all the branches—you can learn more about it in this article. With it in place, you can run:

$ git tree
* 11f7f3c (test) add test.txt file
| * 2dbd30f (origin/test) add test.txt file
| * e7be203 (test-2) add new file
|/
* abc01e7 (HEAD -> main, origin/main, origin/HEAD) Add lorem ipsum to readme
* edd3504 Add readme

And get a nice overview of the entire repository.

Check what your default editor is

In some cases, Git wants you to provide input to it by editing a temporary file it created. This workflow can be confusing at first, and even more so if you don’t know your default editor. Here’s how to check it by listing Git’s logical variables and filtering them to the one that contains EDITOR. On my MacOS, it’s vi:

$ git var -l | grep EDITOR
GIT_EDITOR=vi

Similarly, on Ubuntu:

$ git var -l | grep EDITOR
GIT_EDITOR=editor

On Ubuntu, editor is a command that starts what is configured as your default text editor. One machine I have access to, it’s nano:

$ update-alternatives --display editor
editor - auto mode
  link best version is /bin/nano
  link currently points to /bin/nano
…

No matter your editor, make sure you know how to do the following things:

Or, change the editor to something you know how to use.

Use tab while typing commands

Git commands, all the attributes, and branch names are long; and the interface accepts no mistakes. The key productivity trick is not to type them whole. In most shells, when you press tab key, the shell either:

So, for example, with the zsh shell I use, let’s see these options after typing git co<tab>:

$ git co
Completing main porcelain command
commit        -- record changes to repository
Completing ancillary manipulator command
config        -- get and set repository or global options
Completing ancillary interrogator command
count-objects -- count unpacked objects and display their disk consumption
Completing plumbing manipulator command
commit-graph  -- write and verify Git commit-graph files
commit-tree   -- create new commit object
Completing plumbing internal helper command
column        -- display data in columns

And just complete the word when I type git com<tab>:

$ git commit

Similarly, it provides autocomplete for parameters, branch names, etc. It makes a big difference while typing.

Use arrows

Watching someone retype the whole command they used a few moments ago is a painful experience. Most shells allow you to reuse the last command by just using arrow keys. Arrow up brings the most recent command, you type it again to get the previous one, and so on. After finding the one you need, you can edit it to match the operation you’re performing right now.

Get remote changes all the time

I sync my local repository with remote all the time—even in my private repos, where I know, I’m working alone. It’s too easy to mess up stuff by not paying attention to what others are changing. And it takes only one command to make sure everything is up to date:

$ git fetch

After running it, you know that every origin/<branch-name> reference you see locally is in the same place as it is on remote.

Start your new branches from the updated main

Conflicts are sometimes painful to fix, but nothing hurts more than conflicts that could have been easily avoided. One of the common scenarios when people introduce an unnecessary conflict, is when they start a new branch behind the most recent commit. This can be easily avoided by:

Short-lived branches

Having branches that are merged quickly has a few benefits:

Short-lived branches are a way to progress via small interactions—something I recommend in another article.

Things to avoid

I often see beginners coming with problems I mostly don’t have to deal with because I avoid the following things in Git:

git pull

In Git, pull combines two operations into one:

I always want to double-check my remote state before doing either merge or rebase. So my typical flow would be:

Git submodules

Finally, submodules are a way of embedding a Git repository or repositories inside another. The internal repository maintains its separate history, whereas the containing one keeps only the reference to the origin and the current commit that the internal repo should be at.

My main issue with Git submodules is that they complicate an already difficult problem—version control—and add more layers of complexity.

Git submodules provide an alternative solution for the problem that is better addressed with:

What’s next

Git has many confusing aspects, but it gets way simpler once you understand it. If you are interested in learning more about Git, sign up here to get updates about my Git-focused content.

Originally published here.