First of all look at how XOR works:
a | b | a^b
- - - - - -
0 | 0 | 0
1 | 0 | 1
0 | 1 | 1
1 | 1 | 0
So the result of an xor operation is 1 or true if the inputs are different, and 0 if the inputs are equal. Another way to look at it is to think of it as addition without carry, i'll denote it as (+) :
x = x ^ y <=> x = x (+) y;
y = y ^ x <=> y = y (+) x;
x = x ^ y <=> x = x (+) y;
First step : set all the bits that are 0 in x but 1 in y to 1. Now some bits are wrong in x because if a bit is 1 in x and also y it'll be set to 0. The other bits are correct.
Second step : Now set the same bit positions we set to 1 in x to 0 in y (we are done with y now). This works because x already has all bits that are different set to 1, so XORing y with x now basically means: toggle the bits in y that are set to 1 in x. We are done with y now :)
Third step : now we still need to set those bits in x to 1 that already were set to 1 originally and were reset to 0 after the first step back to 1. How ? We just XOR x with y one last time because what does xor do ? it sets a bit to 1 if the 2 inputs differ, which is exactly what we need.
If you are still confused about it you should just draw it on paper and play it through to see how it works and/or refer to the tables Jason did above.