When I learned about Git branching, all of the explanations involved graphics like this one from Git Branching – Basic Branching and Merging
Unfortunately, this is not how my brain visualizes Git.
Under the hood, Git uses a Directed Acyclic Graph (DAG) to relate commits to one another and the branches they belong to. These diagrams are a faithful representation of a DAG. Unfortunately, this is not how my brain visualizes Git.
Fortunately, I found that I could instead visualize Git commits and branches as stacks of building blocks.
Each Commit is a Block
Instead of visualizing each commit as a node in a DAG, I can visual each commit as a building block.
A Stack of Blocks Represents a Branch
When you are on a branch, you can type
git log --oneline to see the commits in that branch (see Improve Git Log to setup the alias
git lg to save yourself some typing). These commits are stacked one of top of another, like a stack of blocks. When a new commit is added, it appears at the top of the stack of blocks.
Examples of Branches as Stacks of Blocks
Here are two branches
feat/c. Notice in both branches the first two commits are the same (with
feat/c having one additional commit,
(main) (feat/c) C B B A A
If we switch to the
feat/c branch and run
git log --oneline
We can see that our branch
feat/c contains all of the commits of the branch
main. Git even labels where the
main branch ends within our branch.
git merge we can merge the commits from one branch into our current branch.
Let’s assume we are on the
main branch (we’ll indicate this with a
(main*) (feat/c) C B B A A
and we run
git merge feat/c. This will copy the commit(s) from
feat/c into our current branch (
main), in this case only
C needs to be copied over.
(main*) (feat/c) C C B B A A
Since all the commits from our
main branch already appeared inside our
feat/c branch, it was easy for Git to add the new commit(s) from
feat/c to the top of
main. This is the simplest type of merge, know as a fast-forward merge.
When Merging Gets Complicated
Unfortunately, things are seldom this simple. Perhaps while you were busy adding feature
C, someone else went ahead and introduced feature
(main*) (feat/c) D C B B A A
main now contains a commit that does NOT appear in
feat/c we say the branches have diverged and we can NOT perform a fast-forward merge.
In this situation, if we switch to branch
feat/c and run
git log --oneline, we’ll see that our branch no longer contains all of the commits from
main (the branch label
main no longer appears in our output).
Dealing with Diverged Branches
When faced with two branches that have diverged (preventing you from performing a fast-forward merge) you have two options: