Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git log: show second parent on the left

Tags:

git

Consider this example git repository.

#!/bin/bash -e
rm -rf example
git init example
cd example
echo 0 > file.txt
git add file.txt
git commit -am "initial commit"
git branch branch1
echo 1 > file.txt
git commit -am "1 (on master)"
git branch branch2
git checkout branch1
echo 2 > file2.txt
git add file2.txt
git commit -m "2 (on branch1)"
git merge --no-edit master
echo 3 > file2.txt
git commit -am "3 (on branch1)"
git checkout master
echo 4 > file.txt
git commit -am "4 (on master)"
git checkout branch1
git merge --no-edit master
echo 5 > file2.txt
git commit -am "5 (on branch1)"
git checkout master
git merge --no-edit --no-ff branch1
git log --graph --oneline

We create a branch branch1, merge up to it from the master branch a couple of times, and finally merge back to master.

git log --oneline --graph looks weirdly twisted.

*   2c3b97a Merge branch 'branch1'
|\
| * 1d722df 5 (on branch1)
| *   54039b6 Merge branch 'master' into branch1
| |\
| |/
|/|
* | d80d82c 4 (on master)
| * 2399286 3 (on branch1)
| *   9a0cb38 Merge branch 'master' into branch1
| |\
| |/
|/|
* | 31df841 1 (on master)
| * 5809072 2 (on branch1)
|/
* 08fc7a6 initial commit

The graph would be simpler if it looked like this:

*   2c3b97a Merge branch 'branch1'
|\
| * 1d722df 5 (on branch1)
| *   54039b6 Merge branch 'master' into branch1
|/|
* | d80d82c 4 (on master)
| * 2399286 3 (on branch1)
| *   9a0cb38 Merge branch 'master' into branch1
|/|
* | 31df841 1 (on master)
| * 5809072 2 (on branch1)
|/
* 08fc7a6 initial commit

The reason it doesn't look like that, as I understand it, is that git log always prefers to show the "second parent" of each merge commit on the right-hand side, so for each merge commit it has to snake around to the right. (The "second parent" is the branch merging in; the "first parent" is the branch we're merging into.)

  1. Is there a way to convince git log to generate the simpler graph I'd prefer? There's an old gist from 2013 proposing fixing this ("Let me specify which branches sink to the left"; there are other interesting ideas there, too), but I see no progress on that.
  2. Assuming git log can't do it naturally, is there another git commit graphing tool/library I can use to make a graph like this?
like image 558
Dan Fabulich Avatar asked Mar 15 '17 20:03

Dan Fabulich


People also ask

How can a commit have 2 parents?

A git commit can have an arbitrary number of parents. This can be the case, for example, when you use git merge to merge more than one commit into your current branch. Hope you found this post useful. It was published 10 months ago .

How do you see parents of a commit?

To get the parent, just add a ^ after the commit SHA of the commit you want to see.

Why merge commit has 2 parents?

A commit can have 2 parents, which is the result of merging one branch to another in the way of "true merge". A commit can have more than 2 parents, which is the result of merging 2 or more branches to one branch at the same time, in the way of "octopus merge". etc.

Does git log show all branches?

Many times it's useful to know which branch or tag each commit is associated with. The --decorate flag makes git log display all of the references (e.g., branches, tags, etc) that point to each commit.


1 Answers

Thanks to torek's suggestion to try dot (aka Graphviz) I wrote this Python script to generate output in the dot file format. It generates a graph like this, which looks pretty good to me:

enter image description here

#!/usr/bin/env python3
import subprocess, sys, re

args = ['git', 'log', '--pretty=format:%x00%H%x01%h%x01%P%x00'] + sys.argv[1:]

rows = subprocess.check_output(args).split(b'\x00')[1::2]

seen_commits = set([row.split(b'\x01')[0].decode() for row in rows])

print("digraph G {")
for row in rows:
    columns = row.split(b'\x01')
    commit = columns[0].decode()
    label = columns[1].decode()
    parents = columns[2].decode().split(' ')
    print('"{}" [label="{}"];'.format(commit, label))
    for parent in parents:
        if parent == "":
            continue
        if not parent in seen_commits:
            continue
        print('"{}" -> "{}"'.format(commit, parent))
print("}")

If you put that file on your $PATH as git-log-dot and chmod +x git-log-dot, git will automatically run it if you type git log-dot. (It's a legacy holdover from when git was distributed as separate scripts, git-checkout, git-reset, etc.)

You'll also need Graphviz. On macOS I installed it with brew install graphviz; on Ubuntu I believe you can install it with sudo apt-get graphviz.

You can generate a PNG file using git log-dot like so:

git log-dot | dot -Tpng -o graph.png

(In the sample above, I also used -Grankdir=LR to make the graph horizontal, but the default vertical orientation is pretty good, too.)

like image 117
Dan Fabulich Avatar answered Oct 04 '22 13:10

Dan Fabulich