So I'm more or less familiar with how rebasing works, but up until recently I usually just did a git rebase -i HEAD~20
, and modified whatever needed to be modified.
I was surprised to learn that this will modify the hashes of all 20 commits, even if the only action I take is to squash the last two.
I'm not sure what causes the hash change for the other 18 commits though, since neither their parents, neither their contents change... Or does it? Maybe a timestamp?
Also is there a way to prevent that?
A Rebase Changes Hashes, a Merge Does Not.
To modify older or multiple commits, you can use git rebase to combine a sequence of commits into a new base commit. In standard mode, git rebase allows you to literally rewrite history — automatically applying commits in your current working branch to the passed branch head.
That is, git-rebase does not give you an option to preserve the committer date. Unless you give the --ignore-date (or its alias, --reset-author-date ) option, it will always preserve the author date. However, there is no way to make git-rebase preserve the committer date, unless some manual script is crafted.
Rebasing can be dangerous! Rewriting history of shared branches is prone to team work breakage. This can be mitigated by doing the rebase/squash on a copy of the feature branch, but rebase carries the implication that competence and carefulness must be employed.
From the rebase doc:
The commits that were previously saved into the temporary area are then reapplied to the current branch, one by one, in order.
When the commits are "reapplied" it's really creating a brand new commit...it's content may be the same, but it will have a different timestamp which is part of how it's SHA is generated...thus the new commits will have a new SHA's.
You can read more about how SHA's are calculated here.
You may feel that you didn't change some commits, but you actually changed them in several ways:
Each commit contains the sha1 of its parents. So, changing the sha1 of a parent modifies the commit, hence its sha1. The hashes are chained, changing anything in the past changes the future. Technically, this is called a Merkle tree. This is an important property of Git, because given a sha1 it guarantees not only the integrity of the current commit but also of the whole history leading to it (assuming you can't find a collision in sha1, which is no longer really the case today, but collisions are still very hard to find).
Each commit contains a snapshot of the current state of your project. Hence, even if a commit seems identical because it introduces the same diff, it may not correspond to the same state of the project (to the same tree object).
As already noted, commits contain timestamps (one timestamp for the author, one for the commiter, the second one being changed when you rebase).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With