How to keep your sanity while working with Git

marcinwosinek

Marcin Wosinek

Posted on November 23, 2022

How to keep your sanity while working with Git

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:

  • how to use CLI
  • how to use Git
  • and last but not least: programming

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
Enter fullscreen mode Exit fullscreen mode

for when you are on clean branch and up to date

$ git status
HEAD detached at abc01e7
nothing to commit, working tree clean
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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 <marcin.wosinek@gmail.com>
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
Enter fullscreen mode Exit fullscreen mode

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…
Enter fullscreen mode Exit fullscreen mode

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 <marcin.wosinek@gmail.com>
Enter fullscreen mode Exit fullscreen mode

git tree—a custom alias I recommend to everybody

For seeing the graph of all the branches, I recommend defining a tree alias—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
Enter fullscreen mode Exit fullscreen mode

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 the one that contains EDITOR. On my MacOS, it’s vi:

$ git var -l | grep EDITOR
GIT_EDITOR=vi
Enter fullscreen mode Exit fullscreen mode

Similarly, on Ubuntu:

$ git var -l | grep EDITOR
GIT_EDITOR=editor
Enter fullscreen mode Exit fullscreen mode

On Ubuntu, editor is a command that starts what is configured as your default text editor. On 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
…
Enter fullscreen mode Exit fullscreen mode

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

  • edit the file—which can be challenging for beginners in Vim, due to various modes of it’s interface
  • save the changes
  • exit the editor

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 to not type them whole. In most shells, when you press tab key, the shell either:

  • shows you all available command auto completes
  • picks command if there is only one available

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
Enter fullscreen mode Exit fullscreen mode

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

$ git commit
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

  • doing fetch all the time
  • paying attention to where in the history tree you are

Short-lived branches

Having branches that are merged quickly has a few benefits:

  • new features are integrated sooner into the codebase. If they are not completely ready, they can always be hidden by a feature flag.
  • fewer changes in both main and another branch means there is less risk of conflicts,
  • smaller branches means less code to review.

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:

  • git fetch, and
  • git merge or git rebase, depending on your configuration

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

  • git fetch—to get updates from remote,
  • git tree—the alias I mentioned above, to make sure the repo is in the state I expect it to be,
  • git rebase origin/<branch-name>—when there are changes on remote and locally, or
  • `git merge origin/—when there are changes on remote but not locally. That is, it can be done as a fast forward merge.

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 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 problem that is better addressed with:

  • using some package manager (like npm) for external dependencies
  • merging multiple repositories into one for internal dependencies

What’s next

Git has many confusing aspects when you start using it, 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.

💖 💪 🙅 🚩
marcinwosinek
Marcin Wosinek

Posted on November 23, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related