5

Title, basically. This seems to be the go-to resource for what a commit is built from. I couldn't find an article describing this subject in the official book, so do point me that way if I missed it.

My question is about commits without parents, i.e. initial commits. Specifically, what gets hashed in the case of no parent commit existing? And, when are parent commits required? Is it possible to have multiple commits without parents?

In my experimentation, I see that before the first commit, no branches exist; which makes sense considering branches are refs to commits, and none exist yet.

Eden Landau
  • 553
  • 4
  • 12
  • 1
    *Is it possible to have multiple commits without parents?* [Yes](https://stackoverflow.com/questions/25928921/how-can-i-splice-two-or-more-completely-unrelated-linear-branch-ancestries-i). Commits form a directed acyclic graph, and such graphs need not have a single root node. However, having multiple root commits is quite rare with a "normal" use of Git. – jub0bs Nov 03 '21 at 16:35
  • 1
    To find all commits without parents in a repo : `git rev-list --max-parents=0`. To create a new orphan branch in an existing repo, use `--orphan`, see [here](https://git-scm.com/docs/git-checkout#Documentation/git-checkout.txt---orphanltnewbranchgt). – Romain Valeri Nov 03 '21 at 16:39
  • Note that the hash is computed by running the function (SHA1 or, now, sometimes SHA256) on the commit data, which is entirely metadata and is printed with `git cat-file -p `. The saved source is in the (singular) `tree`, while the parents are listed one per line in each `parent` line. The list of parents can be empty, be one line, or be N > 1 lines. – torek Nov 04 '21 at 01:30
  • Note that since both hash functions are order-sensitive, changing the *order* of the parents results in two different hash IDs (desirable, since merges in the "other order" produce the same tree, but different first-parent links, and the `--first-parent` option traverses first-parent links only). – torek Nov 04 '21 at 01:31

1 Answers1

5

Buckle up, because today we're talking about commits!

How many parents can a commit have?

As many or as few as you want!

A commit doesn't have to have just one parent. It can have any number of parents!

  • Initial commits have 0 parents
  • Normal commits have 1 parent
  • Merge commits usually have 2 parents...
  • But Octopus Merges can result in commits with any number of parents (the linux kernel has a commit with 66 parents, that was the result of a 66 way merge! The technical term for this is a cthlulhu merge.)

Doing an octopus merge looks just like a regular merge, just with more arguments.

Regular merge:

# merge branch1 into current branch
git merge branch1 

Octopus merge:

# Merge branches 1, 2, and 3 into the current branch
git merge branch1 branch2 branch3

Why go through the horror of an octopus merge?

It can be useful if you want to merge a bunch of separate feature branches in all at once.

git switch main
git merge feature-1 feature-2 feature-3 feature-4

This looks cleaner in the history, because you only have one merge commit, as opposed to a whole bunch of merge commits in a row.

It's best used when there's a very low probability of a merge conflict.

In the case of the 66-way merge in the linux kernel, they were merging in updates to a bunch of separate drivers, so there were no conflicts between the branches.

What gets hashed in an initial commit

Basically, everything.

  • The author and the author's email
  • any files or directories of stuff added to the initial commit
  • the commit message

This means that if you change a commit message (e.g., with git commit --amend), you're changing the commit hash and therefore the commit appears as a different commit

Is it possible to have multiple commits without parents?

Yes! Git can do anything! There are two ways to have multiple commits without parents:

  • Merge in an unrelated repository with it's own history (this has happened 3 times in the history of the linux kernel)
  • Create an orphan branch (this is a branch that doesn't share history with any other branch)

You can create an orphan branch via git switch --orphan <new branch name>, which will create an empty orphan branch.

Note: to merge it a repository with an unrelated history, just do:

# Add a separate repo as a remote
git remote add other-repo <other-repo-url>
# Fetch the contents of that repo
git fetch other-repo
# Merge the main branch of the other repo into your repo
git merge --allow-unrelated-histories other-repo/main
sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Alecto Irene Perez
  • 10,321
  • 23
  • 46