Git is...complicated. This series focuses on breaking git down into commit-sized pieces.
The Graph
The foundation of git is a directed acyclic graph. You can dive into the Wikipedia article for more information than you probably want, but the basics as they relate to Git are this:
- Each commit in a Git repo is a node on the graph.
- Each commit "knows about" (references) the one or more nodes that came before it.
- We can trace the history of commits through the graph.
- We can't go backward, so there are no loops, but it is possible to have multiple paths between two nodes.
Notes about Graphs
This graph was created with the absolutely amazing Mermaid.js. It represents commits with a number and a hash by default, like 0-2b99de5
. The number is the order they appear in the graph definition. The hash is a randomly generated hexadecimal to simulate what actual hashes in git look like.
Nodes
There are mostly two types of "nodes" on the graph in git: commits and merges.
You can see that 2-a0f289f
on the develop
branch has a line that leads back to 1-10ed042
, which in turn has a line that leads back to 0-2b99de5
, the start of this graph. You can walk the graph and see the history of each commit in this way.
Merges
Looking at the graph, you can see a node where the changes from the feature
branch are merged into develop
. That node is connected to two parents: 5-ca2ed61
on develop
and 6-fdcd8eb
on feature
.
It is possible to perform a merge of more than two sources – an "octopus merge" – but it is very uncommon.
Branches
Git's branching system is sometimes referred to as lightweight branching. Compared to some version control systems which make whole copies for new branches, git branches are essentially just a pointer to a commit on the graph, so making a new branch is basically instantaneous. Let's see that in action.
I've created an empty array and committed a simple README file. If I run git log
, it gives me the commit.
commit 1df473a77206a3208ef8c9213dbd298e0ad9b369 (HEAD -> main)
Author: Me <me@example.com>
Date: Fri Sep 19 12:10:39 2025 -0400
Initial commit
If I make a new branch with git branch develop
and run git log
again, I see that this same commit is now represented on main
and develop
.
commit 1df473a77206a3208ef8c9213dbd298e0ad9b369 (HEAD -> main, develop)
Author: Me <me@example.com>
Date: Fri Sep 19 12:10:39 2025 -0400
Initial commit
Even though I've created a branch, I haven't changed the contents of the repo. The branch is metadata – information about the commits.
Divergence
Interestingly, the branch doesn't keep track of every commit on it. The branch only has to know about the latest commit, because that one knows about the commit that came before it, and so on.
Going back to our branch diagram from earlier, the develop
branch can build the list of commits in its history, and discover where it has one in common with main
– 0-2b99de5
, so we can say develop
diverged from main
at 1-10ed042
.
Timeless Representation
One important distinction is that time is not actually a necessary part of the graph. We think of them in time order because it's helpful to us, but the graph is really only concerned with direction.
The HEAD
While there are timestamps on commits, there's really only one time git is interested in: right now. Git represents "wherever you are right now" as a value called HEAD
.
If we look at that git log
command again, we can see that HEAD
is pointing to the main
branch, even though we also created a develop
branch.
commit 1df473a77206a3208ef8c9213dbd298e0ad9b369 (HEAD -> main, develop)
If we git checkout develop
we'll still be on the same commit, but HEAD
will be different.
commit 1df473a77206a3208ef8c9213dbd298e0ad9b369 (HEAD -> develop, main)
You can use commands to move back from HEAD
, like git checkout HEAD~1
which will check out one commit before the current HEAD
.
Re-writing History
The Graph keeps track of the relationship between things, but it is not written in stone. There are several ways you can change the past, but, just as in the movies, there are risks of doing so.
Most of the concerns with re-writing history are around shared branches. Because git is decentralized, there isn't something that says which history is correct if histories diverge. You create a different timeline that didn't happen for everyone. It's a science-fiction nightmare, but for code.
Still, when you are on your personal branch, re-writing history can be really useful.
git commit --amend
let's you change the last commit you created. If you misspelled a word or forgot an update, --amend
can let you create a history where that didn't happen. For simple changes that exist only for you, this is an easy way to avoid having a commit with an obvious error.
Rebase
Rebasing has a bad reputation. Rebasing is when change history by "moving the start of your branch". This usually happens during parallel development when two or more people are submitting changes to a branch or repository. When one developer's work is accepted and merged, another developer may need to incorporate that work.
Now that feature1
is merged, we have a couple options. We could create a merge from main
to feature2
with git merge main
while we are on the feature2
branch.
If feature2
is a branch that multiple people are working on, that's usually the correct solution. But if it's just one developer, use git rebase main
creates a "cleaner" history, because there is no longer a point where history diverged.
We can say, "feature2
was rebased onto main
." History has changed, and there is no record that it was ever attached to the earlier commit.
We'll talk more about rebasing another time. But from the simple diagrams we can see that rebase picks up part of the graph and connects it somewhere else.
Resources
Some information in this article comes from the unassailable git-scm.com site and the Pro Git Book available to read for free, there.
The section Branches in a Nutshell provides some really good detail and diagrams on the graph and branching.
Summary
Let's bring it back together. Git is a directed graph of commits, each representing a snapshot of the repo. Branches and tags are metadata that point to specific commits. You can add to, change, or even move parts of the graph.
git commit -am 'The Graph'
git push
Top comments (0)