Steve Bennett blogs

…about maps, open data, Git, and other tech.

Git: what they didn’t tell you


Credit:Tim Strater from Rotterdam, Nederland CC-BY-SA

Of all the well-documented difficulties I’ve had working with Git over the years, a few conceptual difficulties really stand out. They’re quirks in the Git architecture that took me far too long to realise, far too long to believe, or far too long to really grasp. And maybe you have the same problem without realising it.

Branch names are completely arbitrary


git branch -d master
git checkout develop -b master

There, I did it. I’m now calling the develop branch master. What you call this branch, and what I call it, and what your Github repo calls it, and what my Github repo calls it just don’t matter. Four different names? No problem. There are some flimsy conventions that Git half-heartedly follows to link two branches with the same name, but it gives up pretty easily.

Remote branches are local

I have very frequently fallen into this trap:

$ git diff origin/master
$

No differences, so my branch must be in sync with Origin, right? Wrong. What is true is my branch is in sync with the local copy of Origin. If you don’t run git fetch, then Git will never even update its local copies.

Technically, Git has always been upfront about this. The Git book opens the section on remote branches:

Remote branches are references to the state of branches on your remote repositories.

But it’s counterintuitive, and so I keep messing it up. I keep hoping (and assuming) that Git will one day include an auto-fetch option, where it constantly synchronises remote branches

‘Detached HEAD’ mode is fine

Here’s the message that we have seen many times:

Note: checking out ‘origin/develop’.

You are in ‘detached HEAD’ state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

git checkout -b new_branch_name

This scary looking message threw me off for a long time, despite the fact that it’s actually one of Git’s most helpful messages – it tells you everything you need to know.

It boils down to this: you can’t make a commit if you’re not at the top of a branch. The two most common situations that cause this are:

    1. Checking out a remote branch. You should do this:

      git checkout origin/develop -b mydevelop
      or even, if you want to abandon your branch completely:
      git branch -d develop
      git checkout origin/develop -b develop
  1. Checking out a commit in the middle of a branch, like:

    git checkout a8e6b18

    Usually in this case you just want to look at it, so you can just ignore the message.

There’s nothing special about ‘git clone’

For a long time, I thought that ‘git init’, ‘git pull’ and ‘git clone’ somehow created repositories that were different, even if they ended up with the same commits in them. It’s hard to recreate my state of mind, but I spent a long time trying to salvage certain directories on disk when I should have just abandoned them.

Similarly, there is no difference between:

  1. git clone http://github.com/stevage/myrepo
  2. git init
    git remote add origin http://github.com/stevage/myrepo
    git pull origin master

Well, in the second case Git’s a bit confused about which local branches map onto which remote branches, so you have to be more explicit or fix it with some configuration option.

Don’t call any remote ‘origin’

Credit: Chevassu (GFDL)

For some reason, Git encourages you to call the source of the first clone “origin”. I have found this very confusing and ultimately very unhelpful. Let’s say you’re working on a project called widget, and you fork it in Github so you can work on it. You will want both remotes accessible locally, so you will probably do one of these:

  1. git clone http://github.com/stevage/widget
    git remote add widget http://github.com/widget/widget
  2. git clone http://github.com/widget/widget
    git remote add mine http://github.com/stevage/widget

So you either have remotes called “origin” and “widget”, or remotes called “origin” and “mine”. But on the next project, you might make the opposite choice, and soon you really don’t remember what “origin” means.

My tip: never name any remote “origin” ever. Name them all after their Github username.

  1. git init
    git remote add widget http://github.com/widget/widget
    git remote add stevage http://github.com/stevage/widget
    git fetch --all
    get checkout stevage -b master

You will never understand “git reset”‘s options

The difference between “git reset”, “git reset –soft” and “git reset –hard” is beyond your comprehension. And you probably wanted “git checkout” anyway.

Rule of thumb:

  • If your directory is FUBAR: git reset –hard
  • If you just want to throw away changes to one file: git checkout file
  • In all other cases, google it.

7 responses to “Git: what they didn’t tell you

  1. Paul S May 9, 2015 at 4:10 am

    Actually the different options of `git reset` are quite helpful. It does take some time to get them down though. :-)

    For example:
    `git reset HEAD –hard` : Revert your index / working copy to the state of the last commit.
    `git reset HEAD~1 –soft` : Undo the last commit, but keep the changes in your index.
    `git reset` : Remove all files from the index (i.e. “Un-add” all files that have been added for the next commit.)

    • steveko May 9, 2015 at 9:00 am

      >Revert your index / working copy to the state of the last commit.

      This can be more safely achieved by “git stash”

      >Undo the last commit, but keep the changes in your index.

      Sounds like you really want “git commit –amend” – you are just about to commit again, aren’t you?

      > Remove all files from the index

      Right. Would be much better named “unstage”.

      • Paul S May 9, 2015 at 9:05 am

        > This can be more safely achieved by “git stash”

        Yeah, that would mostly do the same thing. I use `git reset –hard` when I just want to dump my changes, without needing to keep them.

        > Sounds like you really want “git commit –amend” – you are just about to commit again, aren’t you?

        Yeah, if I’m intending to just change the commit that’s what I do as well. I guess I only use `git reset HEAD~1 –soft` when doing some history rewriting, which isn’t recommended if you’re not certain you’re the only one working on a branch.

  2. Jan Stoker July 20, 2015 at 3:18 pm

    > My tip: never name any remote “origin” ever. Name them all after their Github username.

    Heh, i’ve also learned not to do that.’origin’ is completely confusing. As git is a distributed SCM, the notion of ‘origin’ is shaky at most. It’s not quite clear why it is encouraged. On the other hand, that branch names aren’t treated specially is a good thing, it allows the user to decide on a naming scheme.

  3. Pingback: Critics of Git | Git Advanced

  4. Michael Bailey June 16, 2017 at 5:13 am

    git reset –hard: throw away everything I have done
    git reset [–soft]: move me back, but keep my changes in local filesystem

  5. ankostis August 15, 2018 at 2:23 am

    Actually they are called “remote tracking branches”, and according the the manual[1] <>

    So “Remote-branched” are remote, the “tracking” ones, not.

Leave a comment