Search code examples
gitgit-rebase

git rebase implementation details


I am trying to figure out the working mechanism of git-rebase. Documentation provides information about what git-rebase does, but doesn't comment on how it does?

I have looked into the source code, worked out some test cases and so far understand following:
1. Git maintains the state of rebase in .git/rebase-apply (with files like patch, final-commit, head-name etc)
2. Git uses git-format-patch to create all necessary patch files (which are inside rebase-apply)
3. Git uses git-am to apply those patches one by one

I think I am missing quite a lot of details. Where can I find the implementation details? Is it simply dumping the patch and naively applying it?


Solution

  • Your summary is basically complete. Rebase is actually relatively simple.

    1. First, the work to do is calculated. This is basically git rev-list <upstream>..<branch> to identify all the commits that need to be moved over.
    2. The needed information to perform the rebase is created in a state folder in the .git directory. There are two primary rebase backends, which each use a different folder:
      • For an apply rebase (patch-based, currently the default), patches for each of the commits will be generated and saved in .git/rebase-apply
      • For a merge rebase (used for e.g. interactive rebases), a todo list containing a list of commits and how to apply them will be generated in .git/rebase-merge
      • For both backends, other files containing information such as references to the target and original commits are generated and stored in the state folder.
    3. Next, the HEAD is detached and set to the onto commit (the new base branch, where you will be applying those changes).
    4. Application takes place until a commit cannot be applied.
      • For a patch-based rebase, then patches are applied in order. If a patch fails to apply, then Git will try to instead cherry-pick the commit in question. This is because cherry-pick is able to write merge conflicts to the index and working directory.
      • For a merge-based rebase, then the commit is cherry-picked.
    5. If a cherry-pick fails with conflicts, rebase stops and you (the user) must resolve any conflicts and git add them to the index. When you have resolved all conflicts, you can git rebase --continue.
    6. Once all conflicts have been applied, the original branch is updated to point to the final, rebased commit.

    Some closing details on backends: The merge backend was originally just the interactive rebase backend. It can be invoked manually with the --merge option, and is also used as the default backend for --onto among others. The apply backend is older, and may eventually be replaced entirely by the merge backend.