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
Note that before Git 2.29 (Q4 2020), Git could also change the hash of the commit onto which you are rebasing in the todo
message, commit which should not change (since you are replaying old commits onto this one).
See commit 5da69c0 (13 Aug 2020) by Antti Keränen (Detegr).
(Merged by Junio C Hamano -- gitster -- in commit 4499a42, 19 Aug 2020)
rebase -i: fix possibly wrong onto hash in todo
Found-by: Jussi Keränen
Signed-off-by: Antti Keränen
Acked-by: Alban Gruin
'
todo_list_write_to_file
' may overwrite the static buffer, originating from 'find_unique_abbrev
', that was used to store the short commit hash 'c
' for "# Rebase a..b onto c
" message in the todo editor.This is because the buffer that is returned from '
find_unique_abbrev
' is valid until 4 more calls tofind_unique_abbrev
are made.As '
todo_list_write_to_file
' calls 'find_unique_abbrev
' for each rebased commit, the hash for 'c
' is overwritten if there are 4 or more commits in the rebase.
This behavior has been broken since its introduction.Fix by storing the short onto commit hash in a different buffer that remains valid, before calling '
todo_list_write_to_file
'.
this is because when you rebase the node in HEAD that's 20 commits prior to the current one interactively, you are starting at the node in HEAD 20 commits ago and reapplying the subsequent 20 commits as if they're new, with the option to make changes. Even if you only change (squash) commit -20 and -19, you are still reapplying the subsequent 18 commits as if they're new commits. You are not re-using the same node for those 18 commits, you are copying their contents but appending new ones to your rebased HEAD.
From the git book on rebasing:
Remember again that this is a rebasing command – every commit included in the range HEAD~3..HEAD will be rewritten, whether you change the message or not.
this will modify the hashes of all 20 commits, even if the only action I take is to squash the last two.
If by "the last two" you mean the most recent two commits in the history, then no, it won't.
Please be concrete, show the actual evidence you're looking at, the todo list you got and the one you executed. Characterizations are far too unreliablevulnerable to unshared context.
Here for instance is what happens when I squash the last two commits, as I understand it:
$ git log --oneline --reverse @{u}..
00f53a2 echo >master6
afcef3e echo >master7
1f55c48 echo >master8
c3197a0 echo >master9
d30bb35 (HEAD -> master) echo >master10
$ GIT_SEQUENCE_EDITOR='sed -i 5s/pick/squash/' git rebase -i
[detached HEAD 16dc80d] echo >master9
Date: Mon Feb 5 09:25:55 2018 -0800
2 files changed, 2 insertions(+)
create mode 100644 master10
create mode 100644 master9
Successfully rebased and updated refs/heads/master.
$ git log --oneline --reverse @{u}..
00f53a2 echo >master6
afcef3e echo >master7
1f55c48 echo >master8
16dc80d (HEAD -> master) echo >master9
$
you can see that the last two commits have been squashed together, the id's of all the commits whose history hasn't changed are left untouched.
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).
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.