There are many reasons for wanting to visualize the git graph. I think it’s one of the most helpful tools for understanding git, so try doing it before and after common git operations (see “suggested operations for learning” at the end). It’s particularly handy for those times you can’t avoid branching off another existing branch, like when multiple team members are working on the same feature. It’s still useful any time you’re collaborating with other developers in the same repository. It helps verify that a given branch is in compliance with the team’s branching standards, and can save a lot of headaches by revealing rookie git mistakes much sooner.

Existing tools for visualizing git graphs

  • git plus some alias you found on StackOverflow- Works well, but not with large number of branches (my workaround below).

  • Github network graph- Generally my go-to choice when I’m on someone else’s computer. Not everyone has a license though. It also won’t be helpful for visualizing before you push (like if you want to verify you’ve performed a merge/rebase correctly).

  • IntelliJ Version Control pane- Worth looking into. It’s cumbersome to grab commit hashes, and launching it will take a moment if you’re jumping between repos, so I rarely use it for this.

  • GitUp- Looks great if you have a Mac; I don’t.

  • gitk- Included in your git installation. UI comes straight from 1999, but the info is there if you can digest it.

Problems with existing tools

Whatever your taste, one of the tools mentioned above should work well. Try them out and use each when it makes sense. But I can almost guarantee that you’ll become overwhelmed trying to visualize a large number of branches.

Visualizing a few branches via CLI

To do this, set up a concise git log format of your choice. My favorite is slightly adapted from https://coderwall.com/p/euwpig/a-better-git-log:

git config --global format.pretty format:"%C(bold cyan)%h%Creset %C(cyan)<%an>%Creset %Cgreen(%cr)%Creset%C(yellow)%d%Creset %<(80,trunc)%s"

Then the command for viewing the graph:

git log --graph --all

Nobody wants to press that many buttons, so set up an alias in your .gitconfig file:

[alias]
  lg = log --graph --all

so then you can just run git lg. This is my go-to command; I use it just as often as git status.

Here’s an example running this on a repo with many branches (https://github.com/vuejs/vue):

Visualizing branches using git lg

It’s helpful, but notice what happens when multiple branches have concurrent development. What branch does the yellow line belong to? How far will I need to scroll to see what branch it was based off of, and off which commit from that parent branch?

Visualizing concurrent branches using git lg

Visualizing many branches via CLI (Advanced)

When visualizing a few branches, we have the luxury of viewing every commit. This becomes overwhelming when the repo has a large number of branches. Granted, we could explicitly specify a few branches like git log --graph master mybranch anotherbranch instead of using --all (which is certainly a technique I’d recommend when the following one is overkill). However, sometimes you want to see an overview of them all, even if each branch has numerous commits. Here’s my solution.

Setup

  1. Install ruby for running my script.
  2. Copy this script into a convenient location, like C:\DEV\graph_helper.rb
# Makes "git log --graph --all --simplify-by-decoration" much more useful.
# Creates a handful of branches named "merge-base-tmp1" (etc) at the merge-base for existing branches.
# This ensures that the git log command (above) shows the full branch structure between all existing branches
# This is helpful to run after "git remote prune origin".


require 'set'

branches = `git branch --all`

# Convert from one big string into lines
branches = branches.split("\n")

# Remove the "* " and "  remotes/" from the beginning of branch names.
# Cleans them into a format we can insert into "git log"
branches = branches.map {|e| e.gsub(/[* ]+(?:remotes\/)?/, '') }

# Remove the branch "origin/HEAD->..."
branches = branches.reject {|e| e.start_with?('origin/HEAD') }


# Remove all the old "merge-base-tmpX" branches
branches_to_delete = branches.select {|e| e.start_with?('merge-base-tmp') }
branches_to_delete.each do |branch_to_delete|
	puts `git branch -D #{branch_to_delete}`
end

branches = branches - branches_to_delete



# Gather all combinations of branches
branch_combs = branches.combination(2).to_a

# Gather the merge-bases of the combinations
mergebases = Set.new
branch_combs.each do |branch_pair|
	command = "git merge-base #{branch_pair[0]} #{branch_pair[1]}"
	puts command
	
	mergebase = `#{command}`
	mergebase = mergebase.strip
	mergebases.add(mergebase)
end


# Don't create a "merge-base-tmpX" branch if another branch already exists at that commit

# Find all the commit hashes for existing branches
existing_branch_hashes = `git show-ref -s`
existing_branch_hashes = existing_branch_hashes.split("\n")

# Removing existing branches from "mergebases"
mergebases = mergebases - existing_branch_hashes



# Generate branches at all the merge-bases
i = 1
mergebases.to_a.each do |merge_base|
	command = "git branch merge-base-tmp#{i.to_s} #{merge_base}"
	puts command
	`#{command}`
	i = i + 1
end

Word of warning- the script runs in O(n^2) time, based on the number of branches. There’s probably a more efficient algorithm, but I haven’t investigated this yet.

  1. Setup an alias to run the script in the current repo:
    # .gitconfig snippet
    [alias]
      ...
      logfix = !ruby /c/DEV/graph_helper.rb
    
  2. Setup an alias for the actual graph command:
    # .gitconfig snippet
    [alias]
      ...
      lgs = log --graph --all --simplify-by-decoration
    

Usage

  1. Run git logfix to prepare the temporary branches necessary for displaying the graph.
  2. Run git lgs to show the simplified/condenses git graph.

Here’s an example running this on a repo with many branches (https://github.com/vuejs/vue):

Visualizing branches using git lgs

This is showing almost every branch currently in existence, all inside a single terminal window. Nice, right?

How it works

Git has a built in feature to simplify history. However, it discards all commits that don’t have a branch attached to them. This would cause many of the graph edges to disappear. By creating temporary branches at all of the merge-bases, we ensure that --simplify-by-decoration shows the relationships between all the branches, while still showing the most concise version of the graph possible.

Suggested operations for learning

I think visualizing the git graph is one of the most helpful tools for understanding git, so try it before and after these common git operations. The basic git lg (alias for git log --graph --all) will work fine to visualize these:

  • git commit
  • git pull and git fetch (and for learning the difference between them)
  • git push, particularly right before your workflow calls for a force push
  • git checkout
  • git merge and git rebase
  • git reset