git rebase -i — why is it changing commit hashes?

前端 未结 5 1037
情书的邮戳
情书的邮戳 2021-02-05 17:09

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

相关标签:
5条回答
  • 2021-02-05 17:46

    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 to find_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'.

    0 讨论(0)
  • 2021-02-05 17:55

    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.

    0 讨论(0)
  • 2021-02-05 18:01

    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.

    0 讨论(0)
  • 2021-02-05 18:04

    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).

    0 讨论(0)
  • 2021-02-05 18:07

    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.

    0 讨论(0)
提交回复
热议问题