Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git rebase and git push: non-fast forward, why use?

Tags:

git

merge

rebase

I have a branch that should be available to other contributors and that should constantly stay up to date with the master.

Unfortunately, every time I do 'git rebase' and then try to push, it results in 'non-fast forward' message and abortion of pushing. The only way to push here is to use --force. Does that mean I should use 'git merge' instead of rebasing if my branch went public and others are working on it?

like image 211
snitko Avatar asked Feb 18 '09 05:02

snitko


People also ask

What does non-Fast-forward mean in git?

Git push rejected non-fast-forward means, this error is faced when git cannot commit your changes to the remote repository. This may happen because your commit was lost or if someone else is trying to push to the same branch as you. This is the error you face.

Why you should git rebase?

The Rebase Option But, instead of using a merge commit, rebasing re-writes the project history by creating brand new commits for each commit in the original branch. The major benefit of rebasing is that you get a much cleaner project history. First, it eliminates the unnecessary merge commits required by git merge .

What does rebase and fast forward mean?

Rebase, fast-forward ( rebase + merge --ff-only) : Commits from the source branch onto the target branch, creating a new non-merge commit for each incoming commit. Fast-forwards the target branch with the resulting commits. The PR branch is not modified by this operation.

Is rebase the same as fast forward?

The git rebase command has no “fast-forward” or “no-fast-forward” option (git -no-ff rebase) like the git merge command, because it is not merging anything. A rebase is only integrating the (local feature) branch with another branch (e.g., master) at a certain position.


1 Answers

A few notes on how git works (non technical):

When you rebase, git takes the commits in question, and "recommits" them on top of a clean history. This is to prevent the history from showing:

Description: tree -> mywork -> merge -> mywork -> merge -> mywork -> merge Commit SHA1: aaaa -> bbbb   -> cccc  -> dddd   -> eeee  -> ffff   -> gggg 

After a rebase, it may look like this (or similar):

Description: tree -> rebase Commit SHA1: aaaa -> hhhh 

The issue is that the new commit you are attempting to push out there is NOT a descendant of the commit on the tip of the branch you are pushing to.

Now, you know that the same info is in the commits, but git is being responsible by not just overwriting those commits out there (bbbb-gggg in the example above).


Shared Repo Model

If you are using a shared repository, then things like this can get mighty confusing. Let me explain why. Say another developer pulled down the branch, and they have commits aaaa -> gggg in their branch. Then they make a commit iiii

In the meanwhile, you rebased and forced a push, causing the tree to look like this:

Description: tree -> rebase Commit SHA1: aaaa -> hhhh 

When the other developer tries to push, he gets a "non-fast forward" message. When he does a merge, then both histories are RELINKED together, and you end up with a mess

Something like this (messy):

Description: tree -> rebase -> mywork -> merge -> mywork -> merge -> mywork -> merge -> devwork -> merge  Commit SHA1: aaaa -> hhhh   -> bbbb   -> cccc  -> dddd   -> eeee  -> ffff   -> gggg -> iiii    -> jjjj 

IN other words, if others are pulling AND pushing, it's better that you stick with git merge, or AVOID PUSHING until after the rebase (and only rebase your work).


Publicly Visible Repository Model

Perhaps you are using a different (more gittish) model where you just want people to be able to pull from your repo. In this case, git push --force isn't too bad, because then they can deal with keeping up with it. They can rebase their changes to be on top of your changes before giving their patches to you. It prevents your repo from getting all messed up.

However, there may be a better way for you. git push --mirror

From http://www.kernel.org/pub/software/scm/git/docs/git-push.html

Instead of naming each ref to push, specifies that all refs under $GIT_DIR/refs/ (which includes but is not limited to refs/heads/, refs/remotes/, and refs/tags/) be mirrored to the remote repository. Newly created local refs will be pushed to the remote end, locally updated refs will be force updated on the remote end, and deleted refs will be removed from the remote end. This is the default if the configuration option remote..mirror is set.


One of the great things about git is that it is very flexible and allows for many different kind of workflows. But it's real strength lies in the fact that it is a distributed model, so I believe that the most ROI can be reaped by using it that way.

like image 108
gahooa Avatar answered Sep 29 '22 10:09

gahooa