1

I've come back to some code I was working on a few weeks ago (before the holidays). I've made changes based on some comments left in a merge request.

I went to push my changes and I get the error:

 ! [rejected]        my_branch -> my_branch (non-fast-forward)
error: failed to push some refs to '<remote>:my_repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

I recognize this as the message I get when I try to push after a rebase (needing to force push); however, I don't remember rebasing (I might have, I just don't remember).

I'm hesitant to force push when I don't really know what I'm pushing.

So my questions: Is there a way to check if my branch (specifically my local) has been rebased? Is there something other than a rebase that could likely cause this error (if not then I can probably assume that I did just rebase a few weeks ago and that it's probably safe to force push)? Any other suggestions on how to safely deal with this situation?

ginny
  • 241
  • 4
  • 13
  • *"I recognize this as the message I get when I try to push after a rebase (needing to force push)"* - you should *never* need to force push under normal circumstances. – 0x5453 Jan 09 '23 at 16:27
  • 2
    I guess what you need to do is take a look at the history of the local and the remote branches to see what is going on. – eftshift0 Jan 09 '23 at 16:29
  • 2
    Non-fast-forward error could be a result of you trying to push new commits when another contributor just pushed different new commits. The way to check for it is `git fetch`, `git diff` and `git log`. The way to remedy is `git pull --rebase` or `git fetch && git rebase`, then `git push`. – phd Jan 09 '23 at 16:33
  • @0x5453, thanks -- could you elaborate? The highest rated answer [here](https://stackoverflow.com/a/8940299/3729598) appears to suggest force pushing after a rebase is fine. – ginny Jan 09 '23 at 16:46
  • @eftshift0, thanks. I took a look at the histories and there were discrepancies but given that I was last working on these changes at the start of December I still can't see whether these are necessarily due to a rebase or something else. – ginny Jan 09 '23 at 16:47
  • 1
    @phd -- this worked, thanks! I followed your steps and was able to push as normal. Although I still think there could be a case where one wishes to know if they rebased (my main question), feel free to add this as an answer since it did resolve my issue (and might resolve others' in future too). – ginny Jan 09 '23 at 16:50
  • 1
    @0x5453 "never" completely depends on your workflow. In a rebase workflow where you make perfect commits, you might force push your personal feature branches multiple times per day in response to PR comments. (Whereas in a squash workflow where you don't save your commits, you would rarely need to force push.) However, when you change the target branch of a PR, you'll likely have to force push more times than not. – TTT Jan 09 '23 at 17:26
  • You should not be seeing messages about being behind upstream after a successful rebase over the latest objects from `git fetch`, unless someone published new commits in that very moment. It is not expected behavior. Rebased means that all your commits are new relative to the upstream branch. – Kaz Jan 09 '23 at 18:21
  • @Kaz but in a feature or topic branch workflow you aren't rebasing onto the upstream, you'd be rebasing onto `origin/master` or similar. You would see this message if after rebasing, you were, say, 50 commits ahead and 1 behind. – TTT Jan 09 '23 at 19:49
  • @ginny You may want to look closely at your commits before you complete your MR. If you rebased onto your old version of your branch, although doing phd's suggestion would enable you to push, you might now have ended up with duplicate copies of your commits. Depending on *why* you were diverged, you might have actually been better of force pushing. – TTT Jan 09 '23 at 21:23

2 Answers2

3

Non-fast-forward error could be a result of you trying to push new commits when another contributor just pushed different new commits. The way to check for it is git fetch, git diff origin/my_branch and git log origin/my_branch..my_branch. The way to remedy is git pull --rebase origin my_branch or

git fetch origin
git rebase origin/my_branch my_branch
git push origin my_branch
phd
  • 82,685
  • 13
  • 120
  • 165
  • I think I disagree with this. Your remedy works well if the reason you diverged is due to someone else pushing to the branch. But if the reason you diverged is because of your own rebase, you probably don't want to rebase onto the old commits that you intended to get rid of. – TTT Jan 09 '23 at 17:58
  • @TTT "*…diverged is due to someone else pushing to the branch.*" Which was exactly the problem of the topicstarter. – phd Jan 09 '23 at 18:00
  • 1
    Ah... I see it now. The error says "is behind" so we know this is what happened. I just addressed both scenarios in my answer, but linked to this one for the correct answer in *this* scenario. – TTT Jan 09 '23 at 18:14
  • Wait... I think my initial impression was right. I just tested this and you can't tell from the `push` message whether you are only behind or diverged- the message is the same either way. It's `status` that differentiates it. So, if OP did what was stated, this might actually have rebased a shared branch onto the remote, which would we bad. (If you were truly behind you'd have nothing to push.) – TTT Jan 09 '23 at 19:46
  • 2
    an alternative git log command to view the differences between the active branch and its remote counterpart: `git log --oneline --graph --boundary HEAD...@{u}` (thanks @TTT :) ). An additional option that can be useful in symmetric comparison (3 dots) is `--cherry-mark` : when you see a `=` on both sides, it indicates some commits were probably cherry-picked or rebased between the two branches. – LeGEC Jan 09 '23 at 20:12
1

The message you received usually1 means your branch is diverged with its remote counterpart. The most common scenarios for this are:

  1. After previously pushing your branch, you integrated the latest changes from a remote base branch, perhaps origin/main, by rebasing your branch onto the newer version of it.
  2. After previously pushing your branch, you interactive rebased your own branch or amended your tip commit.
  3. Someone else pushed commits to your branch.

It's important to know which it is because the action you should take for #1 and #2 is a force push, whereas the action you should take for #3 is to pull (with merge or rebase), or rebase your branch onto the remote version- see phd's answer for details on resolving Scenario #3.

You definitely don't want to get it wrong though in either case, because:

  • If you're in scenario #1 or #2, pulling with merge will bring back in your old commits. (This is bad if you don't notice.)
  • Rebasing onto the remote in scenario #1 will rewrite all of the new commits from the shared branch! (This is really bad if you don't notice.)
  • Force pushing in scenario #3 will blow away someone else's commits. (This ranges from bad to really bad if you don't notice depending on how nice that person is.)

So it's important to get it right. Based on your first sentence:

I've come back to some code I was working on a few weeks ago (before the holidays). I've made changes based on some comments left in a merge request.

I think it's safe to assume two things:

  1. You're using GitLab. (I only know this because you said MR instead of PR, and this is of course irrelevant...)
  2. You changed something about your code, based on a suggestion. If you amended your tip commit this leads to scenario #2. If you created a new commit with the changes, then any of the 3 scenarios is possible (if you did #1 or #2 weeks ago without pushing) and/or someone added a new commit to your branch.

Now let's see what happened:

This happens to me quite frequently. I switch to one of my personal branches I wish to work on, I type git status and see that my branch has diverged from the remote, and for example it tells me I'm 300 commits ahead and 2 behind. In this case I know I rebased, but I might still look anyway:

git log @{u} -n3 # show me the top 3 commits of my remote branch
git log @ -n3 # show me the top 3 commits of my local branch

I expect to see the same 2 commits with the same author (me) and author dates, but with different commit IDs. The 3rd commit should be newer on my local branch meaning I simply rebased onto a newer version of the base branch. (Scenario #1)

Note if my divergence numbers are 2 and 2, and the 3rd commits are the same ID, then this means instead of rebasing onto a target, I probably did an interactive rebase or amend (Scenario #2), and this makes it easy to view the diff:

git diff @{u} @

In general, I tend to always use git push --force-with-lease so I'm confident after looking that I can push. If the --force-with-lease still fails, I simply git fetch, then git status, and repeat the above steps to see what I'm missing.

If when comparing the logs you see someone else's commit on the remote branch, then you know someone else pushed a commit and you need to bring it into your branch so you don't blow it away. If you're in Scenario #1 and/or #2, and also Scenario #3, meaning someone pushed a commit to your old version of the branch, then you might need to do a little surgery on your branch using the rebase --onto option.


1 Another possible way to get this message is if you aren't diverged and instead are only behind the remote branch. In that case though you don't have any new commits to push and instead you could simply do git merge or git pull or git reset --hard @{u}, or even git rebase @{u} all of which would have the same effect.

TTT
  • 22,611
  • 8
  • 63
  • 69