Antti Kantola's... personal blog by a developer covering Git internals, Go JSON handling, and other programming topics.
HN commenters repeatedly recommend 'Git for Computer Scientists' as a clear explanation of Git's simple underlying data structure.
I can attempt to answer your questions briefly
Aren't the arrows all facing the wrong way?
>> No, the arrows are in the right direction. Git maintains a Directed Acyclic Graph (DAG) of commits. Each new commit created points to 0..n parents. So let's say you were on the first commit, and then you created a new commit. The newly created commit is the "child" of the first commit, and has a "parent" pointer that is the SHA of the parent. The parent cannot have pointers to the children since they don't know that one will be created!
Now, if you were to do a merge between branch a and b, assuming it's not a fast-forward merge, a new child commit is created that will have 2 "parent" pointers - the SHA of a and b.
A commit will have 0 parents if it the first commit in a repo.
What happens to a commit after 'git reset' abandons it; can the commit be accessed ever again?
>> That commit is "lost". I put that in quotes because git dosen't immediately throw away the commit (rather there is a garbage collection process that will eventually remove that commit). For the same reason if you happen to know the SHA of the commit you just reset, then you can do a "git checkout <new_branch> <sha>" and create a branch off that commit.
One way to get to that "lost" commit is to use "git reflog" - the reflog (that is ref-log) is a lot that updates each time the "HEAD" moves. Since when you do a "git reset" the HEAD moves from the current commit to another one, the reflog will record…
> Rebase workflows are awful and unintuitive. Leave the rebasing to the git wizards who actually know what they're doing, in no circumstances should this be part of your day-to-day work.
Hard disagree. I hardly consider myself a "rebase wizard," but I've been a near-exclusive practitioner of rebase workflows since I can remember. I find rebasing much more intuitive than workflows with merge commits. Squash merges are fine, but with proper intuition, they appear like a special case of rebase.
In my experience, the resistance to rebasing comes down to fears about "rewriting history" and false intuitions about how git works. I usually allay the former by pointing out that squash merges - which almost everyone approves of - also rewrite history. The latter issue seems to arise from arrows in popular git visualizations pointing in the wrong direction, e.g. in Gitflow.[0] In git, the child commit points to its parent, because each node is immutable. The git data structures are extremely simple (hence why git is so named), consisting of blobs, trees, commits, tags and references. Once you understand how these work in practice, rebase becomes intuitive.[1][2]
IMO, the only thing unintuitive about git is the CLI. Translating the graph operation I want into the commands is sometimes a challenge. Maybe that makes me a (frustrated) wizard after all?
[0] https://nvie.com/img/git-model@2x.png
[1] https://speakerdeck.com/pbhogan/power-your-workflow-with-git?slide=72
[2]…
Git isn't designed for "noobs trying to pick up coding just to experiment". It also isn't really designed for solo developers as the OP is about.
Git is designed for collaboration. Git's UI is absolutely unintuitive, especially coming from other version control software. But that's irrelevant. Everyone wants to use it because of the network effect.
In terms of _why_ the UI is unintuitive, I believe it's because it intentionally exposes its internal representation. Unlike its predecessors like subversion, Git has infinite flexibility in terms of the workflow that teams of people can adopt. But the flexibility comes at the cost of learning how to use a distributed graph of commits.
Personally, I never learned git by reading recipes of commands, like the OP. I only really grokked it by understanding how it works internally. This article helped me:
Git for Computer Scientists
https://eagain.net/articles/git-for-computer-scientists/
With all these recipies - one thing I do whenever I attempt some stunt in git: I assign temporary tags to every changeset that's important.
git tag tmp
git perform-stunt
This eases undoing the stunt without needing to find the "before" state from reflog. And if you use a graphical log viewer (I like SourceTree on Mac) you'll see the tagged state in the history view - which makes things a lot clearer.
And to be aware what happens, there one single explanation of git that helps a lot: http://eagain.net/articles/git-for-computer-scientists/
As soon as you start viewing git as a graph of nodes with branches/tags just being "marked" nodes a lot of things make sense, and whatever "git perform-stunt" you attempt it's easy to explain within that mental model.
I agree that git can be characterized as having some usability issues; I initially found the documentation obtuse in many places.
My favorite git joke, by far: "git gets easier once you get the basic idea that branches are homeomorphic endofunctors mapping submanifolds of a Hilbert space."
If you're having trouble understanding git but you find yourself comfortable with basic CS terms, you might want to check out Git for Computer Scientists [0]. This really helped put it in perspective for me.
[0] http://eagain.net/articles/git-for-computer-scientists/
I found this very helpful: http://eagain.net/articles/git-for-computer-scientists/. Git's data structures are well designed. Once you have internalized them, you will be better equipped to navigate through the jungle of command-line options.
To understand the data structures interactively, use "git cat-file -p HEAD" and continue drilling down to an individual file in a subdirectory with "git cat-file -p OBJECTHASH"