Dave

I've been thinking for a little while about how to make git more usable. Part of the problem is that I think most approaches to git suffer from xkcd syndrome: "Just wait through a few minutes of 'It's really pretty simple, just think of branches as...' and eventually you'll learn the commands that will fix everything."

Git has a simple model, but it's not an intuitive model. Unfortunately, git tries to help you by hiding the details of that model behind user-friendly "porcelain" commands that ultimately stop working in weird ways you don't expect. The reason why your local neckbeard can jump in and fix things is because they're not thinking in terms of the Fisher Price My First Version Control metaphors you've been given, they're thinking in terms of the actual model, which is harder to understand at first but is how the system actually works.

So I think it would be worth, effectively, reskinning git. Instead of starting with commands that seem like the kinds of things people might understand, start with the fundamental concepts of the system and build commands from those. If this system takes a little longer to get up to speed with, fine. Everyone needs a tutorial for git anyway, and they still don't understand it when they're finished. This can't be worse.


Here are the concepts I would use to build my new git frontend, which I have named Dave after a git expert I know:

Objects are the core building block: an object is something that has been added to git and stored in its internal database. An object can be a file, or a directory of files, or even a commit. Objects are stored based on their contents, so if you add the same file a hundred times with different names, it will still only be stored once. For that reason, they have funny names like d3486ae9136e7856bc42212385ea797094475802 Adding a file as an object is not very useful on its own, you usually want it to be part of a commit.

Commands for operating on objects include dave object add file/directory (adds a new object to the database), dave object list (shows all objects), and dave object show (shows you the contents of an object).

Commits are objects that represent some files at a certain point in time. Normally we measure time with numbers, but this gets tricky when multiple people are doing things at the same time. Imagine you start with version 1, and two people separately make new versions, call them 2a and 2b. Which is the real version 2? And how do you get to version 3? To resolve this, we get rid of version numbers, and instead each commit just says which other commits it comes after. Commit WASH_HAIR and SCRUB_ARMPITS can both come after commit TURN_ON_SHOWER, no problem. Then we can add a new commit TURN_OFF_SHOWER which comes after WASH_HAIR and SCRUB_ARMPITS. It turns out that this comes after idea is enough to give us a workable history even without numbers.

Commands for operating on commits include dave commit add (saves your current checkout into a commit), dave commit list (shows the history going back in time from the given commit), and dave commit show (shows information about a commit).

Refs are how we deal with the fact that d3486ae9136e7856bc42212385ea797094475802 is not a very nice name to look at. It's too long and doesn't mean anything unless you're a robot. We want nice short names we can use to look particular commits up, or describe things like "whichever commit is the latest one in production" or "whichever commit Dave worked on last". For these, we use refs, which are just labels for a particular commit that change in predictable ways. There are also special refs like HEAD, which points to whatever commit you currently have checked out.

Commands for operating on refs include dave ref add name [commit] (makes a ref that points to the given commit), dave ref update name [commit] (updates the ref so it points to the given commit, but only if that commit comes after the current one), dave ref set name [commit] (sets the ref to point to the given commit), dave ref list (shows all current refs)

A Checkout is the connection between the files in your working directory and the files in a commit. When you want to work on a commit, you check it out, which updates your directory so it has the files from that commit. After you make changes, you add them to the checkout so git knows about them. Then you commit that checkout to add it to the history. After that, you will probably want to update some refs so they point to your new changes.

Commands for operating on checkouts include dave checkout update commit (make your directory look like the given commit, unless that would lose changes), dave checkout set commit, (make your directory look like the given commit, even if it would lose changes) dave checkout add file/directory (adds changes from your directory to the current checkout), and dave checkout remove file/directory (removes changes from the checkout)


All the commands take the form dave concept [verb], where the verb is inferred if not provided. So you can say dave object commit or dave object file instead of using object add and object show. Similarly, you can use dave commit instead of dave commit add, dave ref instead of dave ref add or dave ref update, and dave checkout instead of dave checkout update.

This may seem fairly similar to vanilla git, and in a sense it is, but consider something like the awful git reset. If you want to undo staging a file, you git reset file. If you want to undo a commit, you git reset --soft HEAD^ or git reset --mixed HEAD^. If you want to update your current branch to point to a different commit, you git reset --hard commit. The metaphors are hopelessly mixed: "reset" says something about user intention, but "soft/mixed/hard" are about git's internal model, whether you update the index, work tree, or both.

By contrast, you unstage a file (remove it from your checkout) with dave checkout remove file. You go back to the previous commit with dave checkout update HEAD^. You update the current branch (ref) with dave ref set branch commit. The metaphor is consistently about the internal data model of git, with very little affordance for the user's mental model. That may be quite confronting at first, but over time I think it would be far easier and lead to less memorising of arbitrary commands.