Droidum
Droidum

Reputation: 434

git rebase re-hashes untouched commit

Situation

These are the commit hashes in my branch. The PUSHED commits are part of the public master which was the base of my branch. The other commits are not yet pushed to public.

PUSHED_A - PUSHED_B - mywork_A - mywork_B - correction_A

Now I used git rebase -i to reorder and squash correction_A into mywork_A.
I understand why mywork_A and mywork_B have a new commit hash after rebasing.

But now even PUSHED_B has got a new hash. The hashes now look like this

PUSHED_A - PUSHED_B2 - mywork_A2 - mywork_B2

I am afraid this will make it hard to correctly merge my branch into the public master branch because PUSHED_B2 is not recognized as the alread existing PUSHED_B and will cause problems.

I corrected that (new branch, cherry picking...) but I don't understand the backgrounds.

Why did that happen?

I looked at the commit content of both PUSHED_B and PUSHED_B2. There is no difference.

Perhaps I took PUSHED_B into the list of commits when doing the rebase, chosing HEAD~x too large. But I did not modify this commit and didn't reorder something before it. So even if it was part of the commits in the list, shouldn't it be left as it was?

Can I trace back what happened and when this happened?

Upvotes: 1

Views: 568

Answers (2)

torek
torek

Reputation: 487705

Note that some forms of git rebase attempt to notice that a particular existing commit can be reused, and will then re-use that existing commit (by "fast forwarding"). If you don't want that to happen, git rebase offers options to disable it. Other forms of git rebase are less careful about this: in effect, those options are always on. It seems likely you used the less-careful form of rebase.

The BEHAVIORAL DIFFERENCES section of the git rebase documentation alludes to the three "back end" internal implementations: am (which uses git format-patch and git am to accomplish the copying, rather than cherry-picking commits), merge (which uses cherry-pick), and interactive (which uses cherry-pick but first generates an instruction sheet that you can edit, and gets pretty fancy):

  • The am back-end is the less-careful form. It is also the default.
  • The front end git rebase command switches to the merge back-end if you supply -m, -s, or -X options.
  • The front end git rebase command switches to the interactive back-end if you use -i.

In Git a year or so ago, rebase was mainly written as shell scripts and it was easier to tell which rebase did what. The new one is in C and much more opaque. The -p option used to us the interactive back end, but not interactively, and maybe still does; the -r or --rebase-merge might also. It's clear that neither uses the old am back end, and, from the description in the documentation, that -k cannot use the am back end either.

As kan said, you can re-rebase to put the two have-to-be-different commits back atop the original sequence of commits. Or, as you did, you can use git cherry-pick manually, which gives you full control over every step, at the price of requiring full control over every step. :-)

Upvotes: 2

kan
kan

Reputation: 28951

When you rebase, it changes 'committer' and date of rebased commits, hence there is a new hash.

Simplest way is to rebase again against upstream using correct commit range. In this case git will detect that PUSHED_B and PUSHED_B2 has the same content, and will skip the latter.

Upvotes: 2

Related Questions