0

I read a lot of articles about force push after a rebase but I still don't understand everything, I though.

What it's not clear to me is in which way a rebase is rewriting git history.

Suppose to be in this situation:

enter image description here

So there si a main branch, at certain time I create a new branch (feat1) from commit d (on main branch). Then I 4 commits (e to h). In the same time some one else continues working on main and pushes commit i and l.

Now I'm ready to rebase feat1 branch on main so before doing it I pull main:

git checkout main
git pull --rebase
git checkout feat1
git rebase main

Now the situation is this:

enter image description here

I solve (if the are) the conflicts, and test that everything works as I expected. Cool, it's perfect.

Now I want to push my branch but I can't. Since I did a rebase, I need to do force push. Why?

Why did I rewritten the git history?

whitecircle
  • 237
  • 9
  • 34
  • 1
    Normally you rebase only **before** doing any pushes, so a force-push should never be necessary. – Dai Sep 02 '23 at 17:16
  • _"Now I want to push my branch but I can't."_ - what is the **exact** error message you get? – Dai Sep 02 '23 at 17:17
  • @ikegami yes, you're right. I updated the image – whitecircle Sep 02 '23 at 17:27
  • [This answer](https://stackoverflow.com/a/8940299/7976758) gives good, short explanation with diagrams. The keyword for understanding why forcing is required is **non fast-forward**. – phd Sep 02 '23 at 17:30
  • @Dai thank you for the reply. Mmm not only, sometimes make sense to rebase branches also after a push. For example, during the development of `feat1`, I can crete a PR – whitecircle Sep 02 '23 at 17:32
  • 1
    `git push` only normally works if the new ref is an ancestor of the commit at the head of the remote branch. That isn't the case here, since `e-h` in the second diagram are totally different commits to the `e-h` commits in tne first diagram. – Lee Sep 02 '23 at 17:32
  • @Lee what do you mean with `ref`? – whitecircle Sep 02 '23 at 17:38
  • 1
    @whitecircle - a `ref` is just a name for an object (commit, tag etc.), so I meant the commit at the head of your local branch needs to be an ancestor of the commit at the head of the remote branch. – Lee Sep 02 '23 at 18:18
  • https://stackoverflow.com/questions/73646342/git-commits-are-snapshots-not-diffs-then-why-is-rebase-necessary-to-remove-old – matt Sep 02 '23 at 20:09

3 Answers3

3

e in your second diagram isn't the same commit as e in your first diagram. It may make the same change (although it doesn't necessarily), and it may have the same message (although it doesn't necessarily), but its ancestor and the contents of the tree are both different, so it's a different commit. Likewise your second f can't possibly be the same commit as your first f, and the same applies to g and h. Normally people explaining rebasing would denote the yellow commits in your second diagram as e', f', g', and h' to show the difference.

When you go to push, the upstream has h and you're pushing h'. h' isn't the same commit as h, and it also isn't a descendent of h (we can't reach h' just by tacking some more commits onto h), so this is called a "non-fast-forward" push.

In the time while h was published as feat1, someone else could have made some more commits i and j, beginning with h. After you push h', they're left with commits that are still descendants of h, which is no longer a part of feat1 (and probably no longer a part of any branch). It's not actually technically hard for them to fix that situation (it's just another rebase, with --onto) but it's an annoyance and it forces people to stop and figure out what's going on, so git puts a roadblock in your way to make sure it's something that you really intended to do.

hobbs
  • 223,387
  • 19
  • 210
  • 288
2

If you pushed your changes upstream to your feature branch before the rebase, then your local copy will be in a different order to the upstream - hence needing a force push.

However, if you merge your now rebased local copy onto the main branch, it will be a fast forward only merge so can be pushed upstream to main without needing a force.

Bob Vale
  • 18,094
  • 1
  • 42
  • 49
0

See good explanation in @hobbs answer on new created commits when you rebase them on top of main branch.

You should now understand that in your feature branch new commits i, l', e', f', g', and h' will replace e, f, g, and h old commits when your push will succeed.

A normal push can only add new commits on top of the existing one (what we call fast-forward) to ensure that you won't loose data by mistake.

So git oblige you to force push to be sure you are full aware that you are rewriting history and you will loose some commits (the ones 'replaced')... which is intended in your case because you have created similar ones.

Philippe
  • 28,207
  • 6
  • 54
  • 78