git rev-list --all --max-parents=0
will find all the roots.
git log --all --ancestry-path ^$root --pretty=%D --simplify-by-decoration
will find all the refs that trace back to any particular root (to get machine-readable you'll need some scraping, most compact with GNU would be awk 1 RS='\n+| -> |, ').
So this will dump the sets of tips that trace back to each root into tips-$root:
roots=`git rev-list --all --max-parents=0`
for root in $roots; do
git log --all --ancestry-path ^$root --pretty=%D \
| awk 1 RS='\n+|, | -> ' >tips-$root
done
And that's the git-specific part of the problem, what's left is to identify the disjoint graphs from these sets. Common elements in any two tip sets join the graphs,
A--\ tools
*---B master
C--/ doc-html
has three tips, tools and doc-html share no roots but each of their graphs is joined to the master tip, there's just one connected DAG here if you look at the whole thing. sort tips-*|uniq -c|sort -n|tail -1 will tell you the largest number of roots joined to any one tip, echo $roots|wc -w will tell you how many roots there are, if they match, you've got one joined history, if they don't you've got work left to do.
On any kind of industrial scale I'd hit the books for graph theory on identifying disjoint sets, but incrementally merging a list of provisionally-disjoint sets looks efficient enough for light duty, which in this context means "probably everything", and it's in range for basic unix tools. For a real one-off you don't even need awk for this, just a plain old shell script with shell arrays tracking the sets. isdisjoint() { return $(( 0 == `sort $*|uniq -d|wc -l` )); } and the like.