71

In this documentation it is mentioned

A commit object may have any number of parents.

But from my understanding, the only case where a commit will have more than 1 parent is when a merge has happened, and in that case there will only be two parents. So my question is, can a commit have more than 2 parents? If so, when?

Can't Tell
  • 1,161
  • 15
    https://github.com/torvalds/linux/commit/9b25d604182169a08b206306b312d2df26b5f502 - I don't think Github knows how to display a 27-way diff, but feel free to clone the repository and view it yourself. – user253751 Mar 30 '16 at 06:51

3 Answers3

65

You can use git merge to merge more than one commit into your current branch. From man git-merge (or git help merge):

git-merge - Join two or more development histories together

The result will be a commit with more than two parents when you do that.

David Hammen
  • 8,301
  • 28
  • 37
  • 51
    A merge of more than one branch (i.e. a commit with more than two parents) is colloquially known as an "octopus merge". That's also the source of inspiration for GitHub's logo and mascot, the eight-legged cat: it was originally called the Octopuss, but was renamed the more corporate-friendly Octocat. – Jörg W Mittag Mar 30 '16 at 03:05
  • "Couldn’t load network graph. Too many forks to display." sob I want my sob forks I sob want my forks sob – yo' Mar 30 '16 at 07:07
  • 6
    What are the advantages of merging three or more branches in a single commit, instead of a series of commits? – Tor Klingberg Mar 30 '16 at 09:09
  • 2
    @TorKlingberg Cleaner history, but make sure to test the final product before pushing to the remote repsitory. – Ferrybig Mar 30 '16 at 09:23
  • @DavidHammen can you please provide a sample link in github where there are more than 2 parent commits for a certain commit – Kasun Siyambalapitiya Apr 17 '17 at 12:29
  • 12
    @KasunSiyambalapitiya - There are multiple examples in one particular github repo. One of the many octopus merges in that repo which involves 27 parents. – David Hammen Apr 17 '17 at 13:58
  • 1
    @JörgWMittag Literally none of that is true.

    Source: worked at GitHub for five years.

    – gjtorikian Nov 13 '17 at 19:50
  • @GJTorikian: the manual page for git merge says [fat emphasis mine] "Specifying more than one commit will create a merge with more than two parents (affectionately called an Octopus merge)." The command line option to select a merge strategy for more than two parents is called octopus. The executable that implements said merge strategy is called git-merge-octopus. – Jörg W Mittag Nov 13 '17 at 20:21
  • @JörgWMittagmate mate I was one hundred percent referring to the GitHub's logo and mascot and the "more corporate-friendly Octocat" part. – gjtorikian Nov 14 '17 at 22:05
  • 3
    Insert "why would you do that?" meme here... – Mason Wheeler Sep 04 '18 at 12:59
  • How can i revert such a commit – Alexander Mladzhov Mar 23 '20 at 16:30
  • 1
    @AlexanderMladzhov - That's a new question, so ask it as a new question. A brief answer: git revert takes an optional -m <parent-number> argument. Quoting from the git revert man page, emphasis mine, "As a result, later merges will only bring in tree changes introduced by commits that are not ancestors of the previously reverted merge. This may or may not be what you want." – David Hammen Mar 24 '20 at 07:03
26

Yes, how about 100k parents?

Here is a live GitHub example with a merge of 100k commits: https://github.com/cirosantilli/test-octopus-100k Generated with this script.

Trivia

Linus does not like commits with more than 60 parents: https://www.destroyallsoftware.com/blog/2017/the-biggest-and-weirdest-commits-in-linux-kernel-git-history

It's pulled, and it's fine, but there's clearly a balance between "octopus merges are fine" and "Christ, that's not an octopus, that's a Cthulhu merge".

Have a look at the format for the Git commit object

https://stackoverflow.com/questions/22968856/what-is-the-file-format-of-a-git-commit-object/37438460#37438460

From that analysis, we can see that the list of parents list is an arbitrary newline separated list of type:

parent {parent_1_sha}
parent {parent_2_sha}
...
parent {parent_N_sha}

and so an arbitrary number of parents is allowed.

Minimal example

Script:

#!/usr/bin/env bash
set -eu

mkdir tmp cd tmp git init

touch root git add . git commit -m root sha_root="$(git log -1 --format="%H")"

touch 1 git add . git commit -m 1 sha1="$(git log -1 --format="%H")"

git reset --hard "$sha_root" touch 2 git add . git commit -m 2 sha2="$(git log -1 --format="%H")"

git reset --hard "$sha_root" touch 3 git add . git commit -m 3 sha3="$(git log -1 --format="%H")"

git merge -m merge "$sha1" "$sha2"

View it:

git log --abbrev-commit --decorate --graph --pretty=oneline --all

Output:

*-.   70b4d8c (HEAD -> master) merge
|\ \  
| | * e54f235 2
| * | c7ab8ef 1
| |/  
* / ae5f932 3
|/  
* ef2f89e root

How it shows on git log:

commit 70b4d8c93b8ba47d3a2b2d4d45619ad5e0f34289 (HEAD -> master)
Merge: ae5f932 c7ab8ef e54f235
Author: Ciro Santilli <[email protected]>
Date:   Thu Dec 3 23:45:07 2020 +0000
merge

commit ae5f932c1de34255400806f85f7f58ff52b7ae86 Author: Ciro Santilli <[email protected]> Date: Thu Dec 3 23:45:07 2020 +0000

3

commit c7ab8efcb25e49ffc653ce5308cd5151d6a3fb94 Author: Ciro Santilli <[email protected]> Date: Thu Dec 3 23:45:07 2020 +0000

1

commit e54f235a54da0900cec99301099673e3bc1cd01e Author: Ciro Santilli <[email protected]> Date: Thu Dec 3 23:45:07 2020 +0000

2

commit ef2f89eeb8fa83779b58b604f9814ec6fdfcc20c Author: Ciro Santilli <[email protected]> Date: Thu Dec 3 23:45:07 2020 +0000

root

so note how a commit with multiple parents gets the:

Merge: ae5f932 c7ab8ef e54f235

line with the parents. The parent of commits with a single parent is not as clearly shown in that format however, but we could get it with:

git log --pretty=raw

which gives:

commit 4210da20504fc08136421a351983c179baf0dccb
tree 2d509da01229d1c0f2f0efa0951fd623f6e26f62
parent 5b1eba2de78f303d5a3a8b0ca18fcf1219d5431a
parent 802c349017b0f911ccb8e59b1a730b51113d198d
parent 228b5883376dce7874c1c2269666077a9db285b6
author Ciro Santilli <[email protected]> 1642065317 +0000
committer Ciro Santilli <[email protected]> 1642065317 +0000
merge

commit 5b1eba2de78f303d5a3a8b0ca18fcf1219d5431a tree 235125b9eb17ab8889306536ca28a192cbb5499b parent 1a257992f676efa966ffaf826e1bd1b6ece9325b author Ciro Santilli <[email protected]> 1642065317 +0000 committer Ciro Santilli <[email protected]> 1642065317 +0000

3

commit 802c349017b0f911ccb8e59b1a730b51113d198d tree 64b425efe9e0a742c6a085eb4c0771641be57582 parent 1a257992f676efa966ffaf826e1bd1b6ece9325b author Ciro Santilli <[email protected]> 1642065317 +0000 committer Ciro Santilli <[email protected]> 1642065317 +0000

1

commit 228b5883376dce7874c1c2269666077a9db285b6 tree 40693988bf998a8acbc7cdec9982cdfd358915c9 parent 1a257992f676efa966ffaf826e1bd1b6ece9325b author Ciro Santilli <[email protected]> 1642065317 +0000 committer Ciro Santilli <[email protected]> 1642065317 +0000

2

commit 1a257992f676efa966ffaf826e1bd1b6ece9325b tree f5ba07a8f892d043d1b8f3429d3bec69a2331d4b author Ciro Santilli <[email protected]> 1642065317 +0000 committer Ciro Santilli <[email protected]> 1642065317 +0000

root

related: https://stackoverflow.com/questions/9059335/how-can-i-get-the-parents-of-a-merge-commit-in-git

Another related git log option is: --min-parents which filters commits by the number of parents.

Tested on git version 2.25.1.

  • I'm not sure if GitHub just can't handle a merge of that magnitude or if you've left it as a private repository. Or if GitHub is just having some unrelated issue. But regardless, the 100k merge link is a 500 error for me. – 8bittree Sep 04 '18 at 16:01
  • @8bittree it can't handle, private repo would be 400 :-) https://github.com/isaacs/github/issues/1344 – Ciro Santilli OurBigBook.com Sep 04 '18 at 16:03
  • 2
    What is this connection between bearded sandal wearing FOSS advocates and Lovecraft that I repeatedly enounter? – Neutrino Sep 23 '19 at 15:29
8

You can specify more than one branch when merging.

For example:

git merge branch_A branch_B branch_C [...]

Then commit has more parents.