I thought of one possible solution, but I’m still hoping to find something more elegant:
Mark all second-parents of all merges into the main branch as good
Marking all remote parents of each merge as good will consider all the commits preceding them as good (and as such skipped by bisect). This solution should also be generic enough to handle multiple merges from multiple branches, leaving only the commits on the main branch.
git rev-list --first-parent --merges --parents GOOD..BAD \
| sed 's/^[^ ][^ ]* [^ ][^ ]* //' \
| xargs git bisect good
(replace GOOD and BAD with the relevant commits)
The regex in sed removes the first two commits of each line; the merge commit itself, and the first parent, leaving the rest of the parents (usually just the second one).
Given the history stated in the question, running the one-liner would give you:
G---o---o---o---o---o---o---B main project branch / / / G---x---G---x---G dependency \ / x' dependency project taskbranch
This would make bisect traverse only the commits on the main branch:
o---o---o---o---o---o
If any of the merged branches are indirectly the cause of the problem, it will be discovered when you test the merge commit via bisect, which could be reason to investigate further on that branch.