In order to understand what a HMAC does for you, you need to know what a HMAC is. A HMAC is basically a hash function, that works a secret key "into" the hash. So instead of
Digest = Hash(Data)
you have
Digest = HMAC(Data, SecretKey)
So how does this guarantee integrity? Similarly to a "regular" hash, data and the HMAC are transmitted together. The recipient, who also knows the secret key, can then calculate the HMAC again, and if the result is identical, it is guaranteed that the file has not been tampered with.
How is this any different from a regular hash?
The difference is that an attacker can't modify the data and simply calculate a new hash. For example, imagine you transmit the message Security.SE is the best Stack Exchange site
, with the SHA-256 hash 71d90574502edd87ea8ef969dc5a28dc13a0c4ce1e9e9664600ea275f1b364e4
. Now an evil hacker from Crypto.SE intercepted that message, changed it to Crypto.SE is the best Stack Exchange site
and changes the hash to ed53d3d9f49ba1fe00c7591236ab72d6f27913bdd40fa7f2c6c0b177f76157c2
.
The recipient wouldn't know that the data has been modified. If I were to use a HMAC, however, the attacker would not have access to the secret key, and thus is not able to generate their own HMAC.
But isn't a hash enough to guarantee integrity?
Yes, because a HMAC can do everything a regular hash function can do. However, a HMAC can, aside from integrity, also guarantee authenticity, assuming that the key has not been compromised.