-2

This may be a completely obvious question but I have seen a number of articles that say that git rebase is a rewrite of history and the 'new' commits have new SHA's, a few tests of mine did not yield this result, the SHA seems to always be preserved, even when there are conflicts in a rebase the SHAs remain. Please help me understand what is indeed going on with the rebase? Do the SHA change? If so when?

Edit: to be clear when I write SHA I mean the commit ID I can see when I git log my commits or in git status. It is possible I have misunderstood the term.

Michael
  • 4,538
  • 5
  • 31
  • 58
  • 2
    Rebase doesn’t rewrite the history. It adds new history. Remember commits _are the history_. SHAs will be different because they’re new commits. – evolutionxbox Oct 13 '20 at 17:34
  • Fundamentally speaking, a rebase would absolutely rewrite history unless you're not rebasing anything (e.g. tip of your branch rebased against the tip of the same branch). – Makoto Oct 13 '20 at 17:35
  • Apparently you are mistaken the "SHA" with something else. What changes for sure is the commit hash. "SHA" is not its name and it is not even correct. Technically, SHA1 is the algorithm used now to generate the commit hash. It might change in the future (there is work in progress regarding this). The commit hash identifies the commit including the status of the code, the commit message, the author and committer, the creation date and last update (amend) date **and** the IDs of the parent commit(s). Since a rebase changes the parent commit, the commit ID of the rebased commit(s) change. – axiac Oct 13 '20 at 17:36
  • Does this answer your question? [Rebasing and what does one mean by rebasing pushed commits](https://stackoverflow.com/questions/2715085/rebasing-and-what-does-one-mean-by-rebasing-pushed-commits) – Makoto Oct 13 '20 at 17:37
  • 1
    "the SHA seems to always be preserved" Really? Prove it. – matt Oct 13 '20 at 17:40
  • @matt see my edit – Michael Oct 13 '20 at 17:45
  • That's just words. We need a reproducible example. Give actual commands to git and their output. I assure you that after every actual rebase _all_ the SHAs in the rebased portion of the branch are new. – matt Oct 13 '20 at 17:46
  • 2
    Your question has gotten downvoted all to heck, but remember: what `git rebase` does is to *copy* (some) commits. The new commits are new and different, and therefore have different hash IDs. The old commits still exist, but you can't *see* them any more, at least not by using the branch name; and eventually, if nobody has any way to see them, Git garbage-collects them. – torek Oct 13 '20 at 18:56
  • By default, `git rebase` tries to be smart. You give it one or two names and it figures out which commits really *have* to be copied, and which can just be left alone. Sometimes you may find you need to defeat this smart-ness, for which the `-f` / `--force` option exists. Alternatively, you can do a "rebase" manually using `git cherry-pick`. – torek Oct 13 '20 at 18:57

1 Answers1

9

The ground of being in Git is a commit, once created, is absolutely immutable. Nothing about it can ever be changed, including the hash (what you call the SHA) that uniquely identifies it.

However, during a rebase, all commits that change their parentage are copied, and the copies have the same commit message but (necessarily) a different SHA. Well, since we think of the commits "themselves" as being rebased (i.e. "moved") from one parentage to another, in that sense it is reasonable to speak loosely and say that the commits in which we are interested do change their SHA.

Here's a demonstration. We have this topology:

A -- B -- C -- D (master)
           \
            X -- Y (feature)

Now watch closely. I'll start on master:

$ git log --oneline
f23b8ca (HEAD -> master) D
2dfe0fb C
ede9af5 B
a2a98f2 A
$ git checkout feature
Switched to branch 'feature'
$ git log --oneline
d621b67 (HEAD -> feature) Y
0b7c870 X
2dfe0fb C
ede9af5 B
a2a98f2 A
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: X
Applying: Y
$ git log --oneline
8440343 (HEAD -> feature) Y
13c9a39 X
f23b8ca (master) D
2dfe0fb C
ede9af5 B
a2a98f2 A

As you can see, the SHA of both Y and X "changed" as a result of the rebase.

A, B, C, and D are unchanged because they are not what was rebased. However, if, from the situation at the end of the above, you were now to do an interactive rebase where (say) you squashed C onto B, then B, D, X, and Y would all "change" their SHAs (and C would eventually cease to exist):

$ git rebase -i a2a98f2
==============
pick ede9af5 B
squash 2dfe0fb C
pick f23b8ca D
pick 13c9a39 X
pick 8440343 Y
==============
$ git log --oneline
2ea8880 (HEAD -> feature) Y
4160563 X
39847f8 D
683b128 B
a2a98f2 A

Technically, it is important to understand that no commits were actually changed! The X and Y commits after the rebase, with the "changed" SHA values, are actually new commits (copies). The "original" X and Y listed in our first log do in fact still exist:

d621b67 Y
0b7c870 X

The difference is that we are not interested in these commits anymore. We are interested only in the new copies. And in fact, the originals no longer have any branch name preserving them, and will eventually be permitted to go quietly out of existence.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thanks for the example, in this example you can see that ONLY X,Y have changed. A,B,C,D have the same SHA they always had, meaning in plain english it's not a new history rather there are new commits which are added to an existing history that is unchanged! unless those ID's now contain different code than before. – Michael Oct 13 '20 at 17:57
  • No, there's no new "code". And yes, ABCD are unchanged, as they were not what was rebased. Rebasing does not change all the history in the universe. – matt Oct 13 '20 at 17:57
  • But I thought rebase was a command for branches not specific commits? Is it not true to say a "branch" has been rebased? – Michael Oct 13 '20 at 17:59
  • That's both irrelevant and a religious question. :) I would say: what's in the repo are commits, and there isn't really such a thing as a "branch" except as the (possibly temporary) name of one commit. Anyway you can see for yourself that this rebase applied X and Y. Those are commits. And their SHAs were changed. QED. Again I say: show me your counterexample. – matt Oct 13 '20 at 18:00
  • (1 of 2) @Michael - The issue here is that you have an incorrect understanding of what a branch *is* in git. A branch is merely a pointer to a commit. What rebase really does is (1) calculate patches represnting the changes made by a set of commits you've specified; (2) apply each of those patches in order onto a new base commit (usually the tip of another branch); (3) IF a branch was checked out (or you gave options that caused rebase to check a branch out before calculating patches), move the branch so that it points to the tip of the new commits. – Mark Adelsberger Oct 13 '20 at 19:27
  • 1
    (2 of 2) @Michael - When step 3 occurs, it *is* correct to say you've rebased the branch - because rebase did indeed change that branch's history (that's what "history rewrite" means, which is why the comments saying history isn't rewritten are 100% wrong). But that still doesn't mean that every commit reachable from the branch was rewritten. The branch is still just a pointer to the last commit. Some commits were removed from the branch's history and were replaced with new commits *which have different SHAs*. – Mark Adelsberger Oct 13 '20 at 19:29