^ is an XOR - the result of each bit XORed with another bits is one if and only if the bits are different.
Expand it all out into the generations and it's pretty obvious:
a1 = a0 xor b0
b1 = a1 xor b0
a2 = a1 xor b1
Substituting for a1, then b1:
a1 = a0 xor b0
b1 = (a0 xor b0) xor b0
a2 = (a0 xor b0) xor ((a0 xor b0) xor b0)
Because XOR is fully associative and commutative we can move the brackets round
b1 = a0 xor (b0 xor b0)
a2 = ((a0 xor a0) xor (b0 xor b0)) xor b0
Since
x xor x
is always zero for any x,
b1 = a0 xor 0
a2 = (0 xor 0) xor b0
And since
x xor 0
is always x for any value of x:
b1 = a0
a2 = b0
And the swap is done.