In a nutshell its about using y (and the "carries/x&y" it becomes) to modify x until it becomes the sum of both ints. For example,
y=1 (....0001), x=anything (either .....0 or .....1)
if x ends with 0, x&y=0
//x^y = x becomes ....001 (thereby adding 1)
//since x&y=0 the loop stops
if x ends with 1, x&y=1
//x^y = x
//since y= x&y<<1, new y=(.....000010)
if x ends with 01, x&y=0
//x^y = x becomes ....010 (thereby adding 1)
//since x&y=0 the loop stops
if x ends with 11, x&y=1
//x^y = .....01
//since y= x&y<<1, new y=(......000100)
if x ends with 011
//stuff happens and x becomes ....100 (thereby adding 1)
//loop stops
if x ends with 111
//...
//if x ends with 111111, x becomes ....1000000 (thereby adding 1)
//if x ends with 1111111, x becomes ....10000000 (thereby adding 1)
//if x ends with 11111111, x becomes ....100000000 (thereby adding 1)
//well you get the idea
The same logic is applicable to all values of y, and is not that much different from normal addition only that there are now 2 possible digits (0 and 1) instead of the usual 10 (0 to 9).