Git is not that hard (really)

I've seen some criticism of Git recently, on Twitter, with a ton of likes, suggesting a large amount of people have a difficult time using Git.

Git is not that hard.

It really isn't.

I think the problem stems from two main approaches.

Problem the first: Shying away from the command line

The command line reveals all but can be daunting for the beginner. I'd come from TortoiseSVN and then Kiln's Mercurial equivalent before starting down the git-path. I wanted a visual too. Gitk was dire, but gitg is excellent. You can run gitg from the command line from a repo directory and it'll launch straight away and give you the nice familiar graph so you can see what's happening. Despite using the command line most of the time I still use gitg alongside it constantly. There's no substitute for the clear visual. I have an alias for git log that paints a pretty OK to follow graph into the terminal but you can't beat that full UI.

The git command line is excellent. Its tools near enough always tell you exactly what to do and what is going on. Push was rejected? It'll say why and what to do. Can't commit? Unstaged changes?

Whoa, wait a minute! What the smeg is an unstaged change? Well there you go. Run git status, and it'll tell you if you have unstaged changes. That gives you a clear set of keywords to search for. It's the same for most things that you'll encounter when using git. There's usually also a heavily upvoted StackOverflow post that are often far more accessible and specific to the use case a beginner needs than the manual. However, on this occasion the manual page is pretty damn good (and includes a flow diagram!).

Problem the second: Layering of additional tools such as git-flow

These tools only serve to hide what is actually happening and what you actually are doing or need to do. If something has gone wrong and you don't know what happened then you'll be hard pushed to fix it. The article that prompted the git-flow toolkit's creation is enough - once you know the git basics creating a feature branch is simple. You don't need it. It'll harm your learning.

Solution! git status, git status, git status

Always be running git status. I run it so often I have it aliased* to git st because I sure as hell can't be arsed typing all the way to git stat before the tab-autocompletion can kick in and finish the job. (If you run git st and hit tab you'll see the other potential commands it's found.)

Git status is the single most important git command.

Most if not all problems people have stem from not knowing the current state of their repository.

Most of the time when your repo is clean (nothing happening yet) it'll return this:

$ git st
On branch master
Your branch is up-to-date with 'origin/master'.

nothing to commit, working tree clean

Let's make a new file.

$ touch filename

Now what do we have?

$ git st
On branch master
Your branch is up-to-date with 'origin/master'.

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

	filename

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

It's revealed to you what is going on in the repo right now. It's told you what you need to do if you want your changes (the creation of our new file) to be committed to version control.

Because our working tree was clean we can add everything:

$ git add .

Now we have:

 $ git st
On branch master
Your branch is up-to-date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   filename

If you have terminal highlighting the new file will be green, which is a nice visual aid.

So now we know our file will be included when we run git commit. It's also helpfully told us how to unstage the file. And we can deduce from what's just happened what unstaging will do. But let's run that now and see.

$ git reset HEAD filename

Our changes are still present in the file but it's no longer included if we were to commit. This is good. We know exactly what's happening.

$ git st
On branch master
Your branch is up-to-date with 'origin/master'.

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

	filename

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

In this way we know now that we can pick and choose which of our changes are to be included in a commit. Very useful! Now we can make small, specific changes with a really nice clear and specific git commit message to tell future us and our colleagues what the change was for and why. No more surprises.

More importantly though is the principle. This post isn't intended as a full blown git tutorial. There are plenty out there. Tutorials won't cover every single use case for you though. The intention of this post is to teach you how to learn git not to teach git itself. Git makes this process easy for us with contextual help in the CLI output every step of the way.

TL;DR

Command line interfaces can be daunting to the uninitiated. Git's though, provides guidance every step of the way. I've talked through just one such example, there are many more.

When you run into a problem, look for the keywords Git has output. Read what it's told you, and apply that to a StackOverflow search.

If you always buy ready meals and takeaways you won't be able to learn how to cook.


  1. Aliases

Git is powerful, and as such there are lot of ways to acheive the same result. Aliases can help when you find yourself repeating the same command often, for example.

Here are some of mine that I find most useful, extracted from my gitconfig:

[alias]
    # laziness and typos
    st = status
    co = checkout
    rb = branch
    pop = stash pop
    amend = commit --amend

    # nice graph in the terminal
    lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative
    
    # show files ignored by git
    ign = ls-files -o -i --exclude-standard

Shortcuts, typos, avoiding having to autocomplete too much, and in the last two cases, a quick and easy visual representation in the terminal and a way to show the files that have been set to be ignored. (The most common use case here is for dependencies such as node_modules to be excluded from the repository. Debugging why the changes you've made to a file can't be committed might lead you down this path!)