I have seen list of Bible contradictions, and for me, the hardest one to clarify is this one. Most of the rest are simple discrepancies and not contradictions at all. The difference is that with a contradiction, it is impossible for both statements to be true. With a discrepancy, the statements do not agree, but they could both be true. This turns out to be a discrepancy, because it is possible for both to be correct, but none of the ways they could be correct are obvious.
The best explanation is that Matthew is tracing Jesus’ genealogy through Joseph’s line because that would have been the strict Jewish tradition (patriarchal genealogy) at the time. It also would have been common to use the lineage of the father that raised a son, even when the true genetic father was unknown.
Luke on the other hand, appears to be using Mary’s genealogy. Luke was a careful and amazingly accurate historian and Jesus’ genealogy presented him with a dilemma. If he used Joseph’s line he would not have been as accurate as he possibly could be since Luke knew Jesus’ father was God. On the other hand, at that time it would have been a great insult to go straight to the mother of someone so saying, “Jesus, son of Mary” was not acceptable. Apparently Luke solved this problem by noting that “it was thought” Jesus was the son of Joseph. He then went to Mary’s genealogy by going to Joseph’s father-in-law, Heli. While this seems strange to us, it was not uncommon at that time to call a father-in-law someone’s “father”.
I will admit this is a difficult and unproveable explanation. It is however, certainly possible, so the differences in the Gospel text is not a contradiction, but a discrepancy. I will ask God about this when I get to heaven though, because I want to know exactly what is going on here.