Reputation: 13034
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
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
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
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