1311

OK, I thought this was a simple git scenario, what am I missing?

I have a master branch and a feature branch. I do some work on master, some on feature, and then some more on master. I end up with something like this (lexicographic order implies the order of commits):

A--B--C------F--G  (master)
       \    
        D--E  (feature)

I have no problem to git push origin master to keep the remote master updated, nor with git push origin feature (when on feature) to maintain a remote backup for my feature work. Up until now, we're good.

But now I want to rebase feature on top of the F--G commits on master, so I git checkout feature and git rebase master. Still good. Now we have:

A--B--C------F--G  (master)
                 \
                  D'--E'  (feature)

Problem: the moment I want to backup the new rebased feature branched with git push origin feature, the push is rejected since the tree has changed due to the rebasing. This can only be solved with git push --force origin feature.

I hate using --force without being sure I need it. So, do I need it? Does the rebasing necessarily imply that the next push should be --forceful?

This feature branch is not shared with any other devs, so I have no problem de facto with the force push, I'm not going to lose any data, the question is more conceptual.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
Yuval Adam
  • 161,610
  • 92
  • 305
  • 395

13 Answers13

989

The problem is that git push assumes that remote branch can be fast-forwarded to your local branch, that is that all the difference between local and remote branches is in local having some new commits at the end like that:

Z--X--R         <- origin/some-branch (can be fast-forwarded to Y commit)
       \        
        T--Y    <- some-branch

When you perform git rebase commits D and E are applied to new base and new commits are created. That means after rebase you have something like that:

A--B--C------F--G--D'--E'   <- feature-branch
       \  
        D--E                <- origin/feature-branch

In that situation remote branch can't be fast-forwarded to local. Though, theoretically local branch can be merged into remote (obviously you don't need it in that case), but as git push performs only fast-forward merges it throws and error.

And what --force option does is just ignoring state of remote branch and setting it to the commit you're pushing into it. So git push --force origin feature-branch simply overrides origin/feature-branch with local feature-branch.

In my opinion, rebasing feature branches on master and force-pushing them back to remote repository is OK as long as you're the only one who works on that branch.

slm
  • 15,396
  • 12
  • 109
  • 124
KL-7
  • 46,000
  • 9
  • 87
  • 74
  • 105
    To be honest, pulling and merging the original version of the feature branch into the rebased one kinda eliminates the whole idea of rebasing. – KL-7 Sep 20 '13 at 22:47
  • 34
    Maybe I didn't understand you correctly, but if you pull feature branch, rebase it onto fresh master branch, you can't push it back without force, because remote version of the feature branch can't be fast-forwarded to your new (rebased) version of feature branch. That's exactly what OP described in his question. If after rebasing, but before pushing, you do `git pull feature-branch`, this pull will generate a new merge commit (by merging remote and local versions of the feature branch). So either you get an unnecessary merge after rebasing, or you push with `--force`. – KL-7 Sep 21 '13 at 21:17
  • 6
    Ah, I think I got it. You're describing the same approach as in Mark Longair's answer. But it does generate a merge commit. It might be useful in some cases, but I use rebase mostly in my own feature branches (hence `push --force` is not a problem) to keep commits history linear without any merge commits at all. – KL-7 Sep 21 '13 at 21:21
  • 1
    This also goes to show, the entire idea with a DVCS is that you never push into the same branch. You pull changes other people have done, they should never be pushed to you. You only push to avoid firewalls (and for backup purposes), so the pushed branch should only mirror your work, not be the authoritative branch. IMHO, force push should be the default, but I guess too many people are pushing into the same branch for that to end happily. :) – falstro Sep 11 '14 at 12:37
  • 13
    The trouble with „force-push“ is, that you can indeed „loose stuff“ (prior commits), something that normally should NEVER be possible in any Version Control System ➪ For that reason at least one „master-ish“ branch should have the settings to [not accept force-pushes](http://stackoverflow.com/a/1754553/444255), to limit potential damage. (Name any of the following: grumpy/fired employees, own idiocy, tired&overworked ‘decisions’... ). – Frank N Nov 06 '15 at 09:55
  • I thought this would solve my problem, which seems similar, yet: `$ git push --force origin myBranch` returns "_Everything up to date_" and immediately followed by `git checkout myBranch` gives the same "_Switched to branch 'myBranch' Your branch is ahead of 'origin/feature/myBranch' by 34 commits._" – Eliyahu Skoczylas May 26 '16 at 15:14
  • 5
    Please note that `--force-with-lease` is much safer than `--force`, and it is the right thing to do in this scenario. –  Oct 05 '18 at 22:36
  • "git push origin +my_branch" will also help. – dgupta3091 Feb 01 '19 at 07:40
  • Thanks, @KL-7 for explaining this so beautifully. Also, you folks can check out https://youtu.be/gvAVjaC5UVE?t=1160 to understand the cause of this problem & how to avoid it in the future. – Ankit Jain Aug 14 '22 at 09:03
  • From your conversation, I realized that performing a `rebase master` on the feature branch disrupted the original commit order of the feature branch and generated new commit hashes, which is why the push was rejected. The term "force" can be confusing and make people fear that something chaotic has been executed. In any case, thank you both for the discussion, which helped me understand why the push was rejected. – taoliujun Apr 28 '23 at 05:55
917

Instead of using -f or --force developers should use

--force-with-lease

Why? Because it checks the remote branch for changes which is absolutely a good idea. Let's imagine that James and Lisa are working on the same feature branch and Lisa has pushed a commit. James now rebases his local branch and is rejected when trying to push. Of course James thinks this is due to rebase and uses --force and would rewrite all Lisa's changes. If James had used --force-with-lease he would have received a warning that there are commits done by someone else. I don't see why anyone would use --force instead of --force-with-lease when pushing after a rebase.

djvg
  • 11,722
  • 5
  • 72
  • 103
Hardev
  • 10,851
  • 2
  • 17
  • 17
  • 17
    I think both the accepted answer and this one address the question. The accepted answer explains why you need to force. And this one explains why `--force-with-lease` addresses the concern of using `--force` – Jeff Appareti Dec 13 '18 at 22:42
  • 1
    The problem with this answer is that the scenario where --force-with-lease comes in handy is rebasing a shared branch more than 1 person is working on. And no one should have rebased a shared/collaborative branch in the first place. Golden Rule of Rebasing: DON’T use rebase on a public/shared branch – Brian Ogden Feb 20 '20 at 02:16
  • I think it is quite useful anyway. Maybe someone doesn't know that somebody else pushed in that same branch. So with --force-with-lease one sees that and has the option to revert his rebase and for example do a merge instead. Or apply another solution. – finrod Mar 25 '20 at 08:08
  • Even better is to use the combination of these two flags: `--force-with-lease --force-if-includes`. It's quite a bit safer than just `----force-with-lease` e.g. when an IDE is doing background fetches. – Borek Bernard Oct 13 '21 at 08:34
  • It's worth to mention that: `git fetch && git push --force-with-lease;` `==` `git push --force;`! In other words, the local state should not be updated before "lease" pushing! – Artfaith Oct 11 '22 at 16:12
  • I don't believe this option existed when the question was originally answered, which is why the selected answer doesn't utilize it. – Jyosua Dec 15 '22 at 01:12
70

I would use instead "checkout -b" and it is easier to understand.

git checkout myFeature
git rebase master
git push origin --delete myFeature
git push origin myFeature

when you delete you prevent to push in an exiting branch that contains different SHA ID. I am deleting only the remote branch in this case.

Eddy Hernandez
  • 5,150
  • 1
  • 24
  • 31
  • 9
    This works great, especially if your team has a git hook that rejects all git push --force commands. – Ryan Thames Feb 10 '16 at 17:11
  • 1
    thank you for that it worked well. Here is more details on that I read to understand better. This is very useful when you don't want to or cannot do a force push. [Deleting Remote Branches](https://git-scm.com/book/en/v2/Git-Branching-Remote-Branches#Deleting-Remote-Branches) and [Rebasing](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) – RajKon Jul 25 '16 at 21:32
  • 11
    This has the same outcome as `push --force`, so is only a way to get around a git repo preventing `--force`. As such, I don't think this is ever a good idea - either the repo permits `push --force`, or for a good reason it disables it. Nabi's answer is more appropriate if `--force` is disabled on the remote repo, as it doesn't have the risk of losing commits from other developers or otherwise causing problems. – Logan Pickup Apr 20 '18 at 09:43
  • 1
    `git push --force` doesn't close Merge Request, unlike `git push origin --delete` does. – Grzegorz Głowacki Mar 01 '22 at 08:28
22

One solution to this is to do what msysGit's rebasing merge script does - after the rebase, merge in the old head of feature with -s ours. You end up with the commit graph:

A--B--C------F--G (master)
       \         \
        \         D'--E' (feature)
         \           /
          \       --
           \    /
            D--E (old-feature)

... and your push of feature will be a fast-forward.

In other words, you can do:

git checkout feature
git branch old-feature
git rebase master
git merge -s ours old-feature
git push origin feature

(Not tested, but I think that's right...)

Mark Longair
  • 446,582
  • 72
  • 411
  • 327
  • 37
    I believe the most common reason for using `git rebase` (instead of merging `master` back into your feature branch) is making clean linear commits history. With your approach commits history gets even worse. And as rebasing creates new commits without any reference to their previous versions I'm not even sure that result of this merge will be adequate. – KL-7 Jan 20 '12 at 13:25
  • 6
    @KL-7: The whole point of the `merge -s ours` is that it artificially adds a parent reference to the previous version. Sure, the history doesn't look clean, but the questioner seems to be particularly bothered by having to force the push of the `feature` branch, and this gets around that. If you want to rebase, it's more-or-less one or the other. :) More generally, I think it's interesting that the msysgit project does this.... – Mark Longair Jan 20 '12 at 16:37
  • @KL-7: Incidentally, I +1ed your answer, which is clearly the right one - I just thought this might be interesting too. – Mark Longair Jan 20 '12 at 16:39
  • It definitely is interesting, at least for me. Thank you. I've seen `ours` strategy before but I thought it applies only to conflict situation by automatically resolving them using changes in our branch. It turned out it works differently. And working that way it's very useful if you need rebased version (e.g., for repo maintainer to apply it cleanly to `master`) but want to avoid force pushing (if lots of other ppl for some reason are using your feature branch). – KL-7 Jan 20 '12 at 16:41
  • @KL-7 "I thought it applies only to conflict situation by automatically resolving them using changes in our branch." I know this is a _very_ old post but it bears some clarification. What you describe matches the "ours" _option_ to the "recursive" (default) _strategy_. The overloading of the word "ours" in the UI is unfortunate. (Strategy options are given with the `-X` arg.) – boweeb Jul 07 '20 at 13:10
18

Other's have answered your question. If you rebase a branch you will need to force to push that branch.

Rebase and a shared repository generally do not get along. This is rewriting history. If others are using that branch or have branched from that branch then rebase will be quite unpleasant.

In general, rebase works well for local branch management. Remote branch management works best with explicit merges (--no-ff).

We also avoid merging master into a feature branch. Instead we rebase to master but with a new branch name (e.g adding a version suffix). This avoids the problem of rebasing in the shared repository.

Bill Door
  • 18,272
  • 3
  • 32
  • 37
17

It may or may not be the case that there is only one developer on this branch, that is now (after the rebase) not inline with the origin/feature.

As such I would suggest to use the following sequence:

git rebase master
git checkout -b feature_branch_2
git push origin feature_branch_2

Yeah, new branch, this should solve this without a --force, which I think generally is a major git drawback.

JAR.JAR.beans
  • 9,668
  • 4
  • 45
  • 57
  • 5
    Sorry to say but: „Keep generating branches“ to avoid force-overriding existing ones doesn't help „lonely feature developers“ (who can override) nor multiple people working on a feature branch (need to communicate that branch “increment” and telling to move over, folks). — It's more like manual versioning (“thesis_00.doc, thesis_01.doc, ...”), within a versioning system... – Frank N Sep 25 '15 at 09:21
  • 2
    Plus, this doesnt help when you have a github PR opened on one branch name, you'd have to create a new PR for the new branch name that you pushed. – gprasant Sep 29 '15 at 22:10
  • 1
    @frankee Half true from my experience. for a lonely developer, yeah, just force pushing is easy enough, but it's the habit that might bite you later. + a new dev just joined? or maybe some CI system that is not using --hard reset? for a team collaborating, I think communicating the new branch name is easy enough, this can easily be scripted as well + for a team, I would suggest to rebase locally or when the branch is ready for merge, not during the day to day work, the extra commit is less of a trouble than dealing with rebase/merge conflicts as a result. – JAR.JAR.beans Oct 06 '15 at 10:11
  • @gprasant for PR, again, I think this would be wrong to rebase, I'd actually want to see the single commits with the PR fixes. A rebase (squash) should happen only later as part of the merge to master and when the PR is all done and ready (so no new PR needs to be opened). – JAR.JAR.beans Oct 06 '15 at 10:13
17

My way of avoiding the force push is to create a new branch and continuing work on that new branch and after some stability, remove the old branch that was rebased:

  • Rebasing the checked out branch locally
  • Branching from the rebased branch to a new branch
  • Pushing that branch as a new branch to remote. and deleting the old branch on remote
Nabi
  • 393
  • 1
  • 5
  • 13
  • 1
    Why no love for this option? It is definitely the cleanest, simplest, safest. – cdmo Jan 26 '18 at 13:35
  • 2
    Because I have about 200 systems tracking the branch name, and it has to be a specific name for the task, and if I start doing branch renames every push I'll loose my mind. – Tamir Daniely Sep 18 '18 at 10:04
  • @TamirDaniely I haven't tried, but does deleting the old branch (from remote) before pushing and pushing the new branch with the same old name solve your problem? – Nabi Nov 06 '18 at 09:46
  • 3
    @Nabi That's exactly what --force-with-lease does, except it also verifies that there are no new commits that are not yours. – Tamir Daniely Nov 08 '18 at 08:48
10

What is wrong with a git merge master on the feature branch? This will preserve the work you had, while keeping it separate from the mainline branch.

A--B--C------F--G
       \         \
        D--E------H

Edit: Ah sorry did not read your problem statement. You will need force as you performed a rebase. All commands that modify the history will need the --force argument. This is a failsafe to prevent you from losing work (the old D and E would be lost).

So you performed a git rebase which made the tree look like (although partially hidden as D and E are no longer in a named branch):

A--B--C------F--G
       \         \
        D--E      D'--E'

So, when trying to push your new feature branch (with D' and E' in it), you would lose D and E.

Bouke
  • 11,768
  • 7
  • 68
  • 102
  • 3
    There's nothing wrong with that, and I know it will work. It's just not what I need. Like I said, the question is more conceptual than practical. – Yuval Adam Jan 20 '12 at 10:37
6

For me following easy steps works:

1. git checkout myFeature
2. git rebase master
3. git push --force-with-lease
4. git branch -f master HEAD
5. git checkout master
6. git pull

After doing all above, we can delete myFeature branch as well by following command:

git push origin --delete myFeature
Neeraj Khede
  • 1,010
  • 9
  • 5
5

The following works for me:

git push -f origin branch_name

and it does not remove any of my code.

But, if you want to avoid this then you can do the following:

git checkout master
git pull --rebase
git checkout -b new_branch_name

then you can cherry-pick all your commits to the new branch. git cherry-pick COMMIT ID and then push your new branch.

Sohair Ahmad
  • 441
  • 8
  • 19
3

Fetch new changes of master and rebase feature branch on top of latest master

git checkout master
git pull
git checkout feature
git pull --rebase origin master
git push origin feature
algus
  • 31
  • 2
2

As the OP does understand the problem, just looks for a nicer solution...

How about this as a practice ?

  • Have on actual feature-develop branch (where you never rebase and force-push, so your fellow feature developers don't hate you). Here, regularly grab those changes from main with a merge. Messier history, yes, but life is easy and no one get's interupted in his work.

  • Have a second feature-develop branch, where one feature team member regulary pushes all feature commits to, indeed rebased, indeed forced. So almost cleanly based on a fairly recent master commit. Upon feature complete, push that branch on top of master.

There might be a pattern name for this method already.

Frank N
  • 9,625
  • 4
  • 80
  • 110
0

I would do as below

rebase feature
git checkout -b feature2 origin/feature
git push -u origin feature2:feature2
Delete the old remote branch feature
git push -u origin feature:feature

Now the remote will have feature(rebased on latest master) and feature2(with old master head). This would allow you to compare later if you have done mistakes in reolving conflicts.

Karthik
  • 1,383
  • 1
  • 10
  • 11