问题
I encountered an interesting problem after rebasing a branch on another branch; git rebase -i HEAD~n
shows not only the wrong commit history (the old branch) but also the incorrect amount of commits.
What I want to do
I want to be able to do a git rebase -i HEAD~n
on the correct commit history in order to squash a leftover commit from the old branch I based my branch on.
What I did to cause the issue
# On another Feature branch
git branch -b NewFeautureBranch
# Develop my commit
git add editedfile.extension
git commit -m "Commit message"
# I squashed and merged the feature branch into the development branch, as well as some other feature branches
git fetch --all
git checkout DevelopmentBranch
git pull
git checkout NewFeatureBranch
git rebase DevelopmentBranch
git push -f
Running git log
and gitk
will show the history as it should be, which is my current commit and an older commit from the feature branch it was originally based on, as expected. However, if I then run git rebase -i HEAD~4
I get the old branch's history as well as 11 commits instead of the 4 I asked for:
# Git rebase -i HEAD~4 opens up in VIM like this:
pick f79316dc Commit on old Branch base
pick ba742f2f Commit on old Branch base
pick 7577ea7a Commit on old Branch base
pick 91c4088c Commit on old Branch base
pick 98feed6d Commit on old Branch base
pick e8a73d78 Commit on old Branch base
pick 198f79e7 Commit on old Branch base
pick 10bb699c Commit on old Branch base
pick 1d15a926 Commit on old Branch base
pick 0cf569bb Previous commit I want to squash with
pick 470de8d9 Current FeatureBranch commit
But git log
prints this:
# As it should be when seeing git log and gitk
commit 470de8d92bb490dd14c31d5741b7edec82ca7597 (HEAD -> FeatureBranch, origin/FeatureBranch)
Author: evFox <evFox@fake-email.com>
Date: Wed Jan 3 16:08:24 2018 +0100
Feature commit message
commit 0cf569bba43b5747831a28d6cb42209dab1c2ffa
Author: evFox <evFox@fake-email.com>
Date: Wed Jan 3 12:09:48 2018 +0100
Feature from old branch still relevant to this; what I want to merge with the later commit.
commit 982c30d9c3b46539340fe48c241192e377d3e136 (origin/Development, Development)
Merge: ab6e7c9d 1d15a926
Author: evFox <evFox@fake-email.com>
Date: Tue Jan 9 10:51:06 2018 +0000
Merged PR XX: Merge OldFeatureBranch to Development
What I've tried
I've tried to delete the branch locally and pull a fresh one off of the remote repository:
git checkout DevelopmentBranch
git branch -D NewFeatureBranch
git checkout -b NewFeatureBranch origin/NewFeatureBranch
I have tried to get more history by increasing the commits in the git rebase -i
and I can clearly see that the commits are old, belonging to the OldFeatureBranch as it was before it was updated and merged:
# Git rebase -i HEAD~20 opens up in VIM like this:
pick bt67f432 Commit on old version DevelopmentBranch
pick 5g67f33s Commit on old version DevelopmentBranch
pick rt53d563 Commit on old version DevelopmentBranch
pick ew5r45fg Commit on old version DevelopmentBranch
pick 9gy3f74f Commit on old version DevelopmentBranch
pick 58u5hh63 Commit on old version DevelopmentBranch
pick 34fdg5d5 Commit on old version DevelopmentBranch
pick n678hcn7 Commit on old version DevelopmentBranch
pick mh7y88dr PR merge of DevelopmentBranch where old Branch base were branched out originally, but not where DevelopmentBranch was when old Branch Base was squashed and merged.
pick f79316dc Commit on old Branch base
pick ba742f2f Commit on old Branch base
pick 7577ea7a Commit on old Branch base
pick 91c4088c Commit on old Branch base
pick 98feed6d Commit on old Branch base
pick e8a73d78 Commit on old Branch base
pick 198f79e7 Commit on old Branch base
pick 10bb699c Commit on old Branch base
pick 1d15a926 Commit on old Branch base
pick 0cf569bb Previous commit I want to squash with
pick 470de8d9 Current FeatureBranch commit
But the issue remains.
Possible work-around
I suppose I could try to do a soft reset and amend to the commit I want to squash into:
git reset --soft HEAD~1
But that doesn't fix my current issue of having the git rebase -i HEAD~n
history messed up.
回答1:
However, if I then run git rebase -i HEAD~4 I get the old branch's history as well as 11 commits instead of the 4 I asked for:
One of your commits is a merge. Whenever you try to squash
[interactive rebase] you will get all the commits. If there is a merge in the middle you will get all the commits form this merge.
回答2:
I just encountered this issue and it turned out I had left my editor open so whenever I ran rebase it was opening it up to the buffer I still had running from the previous rebase. To fix it just close the editor window or kill the process.
回答3:
I just encountered this issue and it turned out I had left my editor open so whenever I ran rebase it was opening it up to the buffer I still had running from the previous rebase.
This has been fixed with Git 2.24 (Q4 2019).
Before, "git rebase -i
" showed a wrong HEAD while "reword
" open the editor, as discussed here.
See commit b0a3186, commit a47ba3c, commit 450efe2 (19 Aug 2019) by Phillip Wood (phillipwood).
(Merged by Junio C Hamano -- gitster -- in commit 4608a02, 11 Oct 2019)
rebase -i: always update HEAD before rewording
If the user runs
git log
while rewording a commit, it is confusing if sometimes we're amending the commit that's being reworded and at other times we're creating a new commit depending on whether we could fast-forward or not.
Fix this inconsistency by always committing the picked commit and then running 'git commit --amend
' to do the reword.The first commit is performed by the sequencer without forking
git commit
and does not impact on the speed of rebase.
In a test rewording 100 commits withGIT_EDITOR=true GIT_SEQUENCE_EDITOR='sed -i s/pick/reword/' \ ../bin-wrappers/git rebase -i --root
and taking the best of three runs the current master took 957ms and with this patch it took 961ms.
This change fixes rewording the new root commit when rearranging commits with
--root
.Note that the new code no longer updates
CHERRY_PICK_HEAD
after creating a root commit - I'm not sure why the old code was that creating that ref after a successful commit, everywhere else it is removed after a successful commit.
It also improves the C version of git rebase:
rebase -i
: check for updatedtodo
aftersquash
andreword
While a rebase is stopped for the user to edit a commit message it can be convenient for them to also edit the
todo
list.
The scripted version of rebase supported this but the C version does not.We already check to see if the
todo
list has been updated by anexec
command so extend this to rewords and squashes.
It only costs a single stat call to do this so it should not affect the speed of the rebase (especially as it has just stopped for the user to edit a message)Note that for squashes the editor may be opened on a different pick to the squash itself as we edit the message at the end of a chain fixups and squashes.
But that recent fix also included a regression: While running "revert
" or "cherry-pick --edit
" for multiple commits, a recent regression incorrectly detected "nothing to commit, working tree clean", instead of replaying the commits, which has been corrected with Git 2.25 (Q1 2020).
See commit befd4f6 (23 Nov 2019) by SZEDER Gábor (szeder).
(Merged by Junio C Hamano -- gitster -- in commit f233c9f, 06 Dec 2019)
sequencer: don't re-read
todo
forrevert
andcherry-pick
Reported-by: Brian Norris
Signed-off-by: SZEDER GáborWhen '
git revert
' or 'git cherry-pick --edit
' is invoked with multiple commits, then after editing the first commit message is finished both these commands should continue with processing the second commit and launch another editor for its commit message, assuming there are no conflicts, of course.Alas, this inadvertently changed with commit a47ba3c777 ("
rebase -i
: check for updatedtodo
aftersquash
andreword
", 2019-08-19, Git v2.24.0-rc0 -- merge listed in batch #8): after editing the first commit message is finished, both 'git revert
' and 'git cherry-pick --edit
' exit with error, claiming that "nothing to commit, working tree clean".The reason for the changed behaviour is twofold:
- Prior to commit a47ba3c777, the up-to-dateness of the
todo
list file was only checked after 'exec
' instructions, and that commit moved those checks to the common code path.
The intention was that this check should be performed after instructions spawning an editor ('squash
' and 'reword
') as well, so the ongoing 'rebase -i
' notices when the user runs a 'git rebase --edit-todo
' while squashing/rewording a commit message.However, as it happened that check is now performed even after '
revert
' and 'pick
' instructions when they involved editing the commit message.
And 'revert
' by default while 'pick
' optionally (with 'git cherry-pick --edit
') involves editing the commit message.
- When invoking '
git revert
' or 'git cherry-pick --edit
' with multiple commits they don't read atodo
list file but assemble thetodo
list in memory, thus the associatedstat
data used to check whether the file has been updated is all zeroed out initially.Then the sequencer writes all instructions (including the very first) to the todo file, executes the first '
revert/pick
' instruction, and after the user finished editing the commit message the changes of a47ba3c777 kick in, and it checks whether thetodo
file has been modified.
The initial all-zero stat data obviously differs from thetodo
file's currentstat
data, so the sequencer concludes that the file has been modified.
Technically it is not wrong, of course, because the file just has been written indeed by the sequencer itself, though the file's contents still match what the sequencer was invoked with in the beginning.Consequently, after re-reading the
todo
file the sequencer executes the same first instruction again, thus ending up in that "nothing to commit" situation.The
todo
list was never meant to be edited during multi-commit 'git revert
' or 'cherry-pick
' operations, so perform that "has thetodo
file been modified" check only when the sequencer was invoked as part of an interactive rebase.
来源:https://stackoverflow.com/questions/48183264/git-rebase-i-shows-wrong-commit-history-after-a-rebase-and-force-push