Search code examples
gitversion-controlgit-rebasegit-merge

Git rebase loses history, then why rebase?


I've been looking into rebasing with Git over the past couple days. Most of the arguments for rebasing say that it cleans up the history and makes it more linear. If you do plain merges (for example), you get a history that shows when the history diverged and when it was brought back together. As far as I can tell, rebasing removes all that history. Question is this: why wouldn't you want the repo history to reflect all the ways the code developed, including where and how it diverged?


Solution

  • As far as I can tell, rebasing removes all that history.

    That's not correct. Rebasing, as the name suggests, changes the base of commits. Usually no commit is lost in that process (except that you don't get a merge commit). While your argument about keeping really everything of the development process inside the history the way it was done is correct, very often this leads to confusing histories.

    Especially when working with others that each work on their own branches while requiring certain changes from others to continue (for example A asks B to implement something so that A can use that feature in his own development), this leads to many merges. For example like this:

         #--#--#--#--*-----*-----------------*---#---\         Branch B
        /           /     /                 /         \
    ---#-----#-----#-----#-----#-----#-----#-----#-----*       Branch A
    

    In this example we have a branch that works separately for the time but constantly pulls in changes from the original branch (# are original commits, * are merges).

    Now if we do a rebase on Branch B before merging in back in, we could get the following:

                                 #--#--#--#--#---\         Branch B
                                /                 \
    ---#---#---#---#---#---#---#---#---------------*       Branch A
    

    This represents the same actual changes, but B was rebased to some older commit on A, so all merges on B that were done before are no longer needed (because those changes are already there in that older commit). And all commits that are missing now, are the merges, which usually do not contain any information about the development process. (Note that in this example you could also rebase that last commit on A later on, to get a straight line, effectively removing any hints to the second branch)