BlackVegetable
BlackVegetable

Reputation: 13034

How to determine if a branch has no parent commits

I am writing some tooling that needs to know whether the current branch location/commit has no parents. (This could come about most likely by being a fresh repository with no commits, or a deliberately orphaned branch.) I need a way to check command line output/exit codes so this can be done within a program and automated.

Ideally, this could be accomplished only using git plumbing commands, but failing an easy way to do that, I can work with git porcelain commands.

How can I reliably and succinctly determine if a git branch is an orphan or not?

Upvotes: 1

Views: 989

Answers (3)

torek
torek

Reputation: 488013

The fundamental problem here is that orphan branches don't really exist.

Really, this is literally the case. In a new repository with no commits, there are no branches:

$ mkdir tt
$ cd tt
$ git init
Initialized empty Git repository in ...
$ git branch
$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)

(the No commits yet output is relatively new, in Git version 2.14).

A branch that has no commits on it literally does not exist. This is why the name master does not show up in the git branch output: branch master doesn't exist, even though we're on it.

What's happened is that Git has stored the desired branch name into HEAD:

$ git symbolic-ref HEAD
refs/heads/master

but the branch itself does not exist:

$ git rev-parse HEAD
HEAD
fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

The same thing happens immediately after any git checkout --orphan command: the symbolic reference HEAD does exist, but it points to no commit, so that git rev-parse HEAD fails.

In a script, use git rev-parse -q --verify HEAD to check for this state. Since HEAD always exists as a name,1 it will fail to parse if and only if it contains the name of a branch that does not exist. Edit: Or, as jthill notes in a comment, use git cat-file -e HEAD. The rev-parse command will print the hash ID of the current commit if HEAD is valid, so that one must be redirected somewhere. Both commands exit 0 if HEAD is valid, nonzero if not.

Now, there's another, different case, which Ôrel has already mentioned, which occurs when two branches do exist, but are not in any way related:

A--B--C   <-- master

D--E--F   <-- independent

This is an example of a repository with six total commits (labeled A through F here instead of using real hash IDs), and two branch names. Neither branch is related to the other, so master and indepedent have no commits in common, no matter how far back you traverse the history from either branch-tip.

The easiest way to test if two branches are unrelated is to use git merge-base. The merge base(s) of two branch tips is/are the commits that are "closest to" the tips but that are in the ancestry chain of each tip. In graph terms, they are the Lowest Common Ancestor nodes. Typically there is exactly one such node, though in the case of criss-cross merges there may be two, and in the case of unrelated branches, there are none.

If git merge-base says there are none, the two branches are unrelated.


1If you remove the HEAD file, Git refuses to believe that the repository is a repository. This can happen if you get a kernel panic / system crash, since the file HEAD tends to be pretty active and might get removed by the post-crash file system cleanup. If it does happen, writing ref: refs/heads/master into .git/HEAD can save everything for you. :-)

Upvotes: 2

r.avv85
r.avv85

Reputation: 77

As git chains commits with hashes, if there is no # associated with branch name - then it signifies it's orphaned branch & is not linked to others

Get name of current branch you are on :

git branch | grep "^*" | awk '{print $2}'

Run git branch in verbose mode & simply look for if there is hash!

git branch -v | grep `git branch | grep "^*" | awk '{print $2}'`

We can write more parsing on top of this as per need

Upvotes: 0

&#212;rel
&#212;rel

Reputation: 7622

Use --is-ancestor

git merge-base --is-ancestor <maybe-ancestor-commit> <descendant-commit>

From the doc

--is-ancestor

Check if the first is an ancestor of the second , and exit with status 0 if true, or with status 1 if not. Errors are signaled by a non-zero status that is not 1.

You can test from the initial commit

To get the initial commit

git rev-list --max-parents=0 HEAD

Your script can be

git merge-base --is-ancestor `git rev-list --max-parents=0 HEAD`  TheBranchToTest

Upvotes: 1

Related Questions