How can git lose history when pushing a non-fast-forward commit?

I know that sometimes when I run git push, I get this error:

error: failed to push some refs
To prevent you from losing history, non-fast-forward updates were rejected

Can someone give an example of a situation where history would be lost if I pushed a non-fast-forward update? From my understanding, when I do git push, git will simply push my commits to the remote repository and not delete any of the existing commits, so how will it lose the history?

  • Remove a merge commit, keeping current changes
  • git change all history for committers names and email for specific committer
  • How to configure Git post commit hook
  • How do you merge two Git repositories when one is a subdirectory of the other without losing commit history?
  • Git: Move to previous and next commit
  • Is it better to use a separate commit message for a git merge?
  • Thanks!

  • Git shallow submodules
  • Git: Check out a branch keeping uncommitted changes
  • Continue an interrupted git-push (resume upload)
  • How do I get the list of branches not merged into master, ordered by most recent commit?
  • Cannot push to GitHub - keeps saying need merge
  • Where are the pull/push commands in GitHub for Windows client?
  • 2 Solutions collect form web for “How can git lose history when pushing a non-fast-forward commit?”

    When you push, you send your commit, but you also update the remote branch.

    Say a remote repo is:

    A <- B <- C (master)
    

    Now you clone it, make some dev starting with commit A, and end up with:

    A <- B <- C (origin/master)
      <- D <- E (master)
    

    If you force push, the remote repo will look like:

    A <- D <- E (master)
      <- B <- C
    

    So commits B and C still exist, but they aren’t reachable with a branch.
    And if someone clones the repo now, he will locally have:

    A <- D <- E (master; origin/master)
    

    ie: from his point of view, commits B and C are lost


    Edit:

    • “the commits still exist” means:

    Say my commit B has the sha1 abcdef. Then, there’s a file objects/ab/cdef (at the root of the remote repo, and in the .git directory on my local clone) which represents this commit.

    If you push -f, it won’t delete this remote file, hence, commit B still exists on the remote repo. (At least, as long as it is not garbage collected)

    • “but they aren’t reachable with a branch” implies:

    When someone else clones this remote after I pushed force, he’ll get this .git/objects/ab/cdef file. However, unless he is looking for it, he won’t be aware this commit once existed. For example it will not be displayed by gitk --all.

    Suppose you push your local master to the remote master.

    If your local branch and the remote branch have diverged (both have had commits since last you pulled), how would that be handled?

    One way would be to declare the current commit for your local master as the new remote master. But that would loose the history of commits that are currently on the remote master, since they would never appear in the ancestry of your commit.

    (I suppose that the “lost” commits would still exist remotely, but they would not be on the master branch.)

    Git Baby is a git and github fan, let's start git clone.