Search code examples
gitgit-merge

Git: How to reintroduce former deletions or how to merge parent into child commit


Suppose I have a linear git history

A-B-C-...-X (HEAD)
          ↑
        master

Now I noticed, that I deleted some lines in C that were present in B and that I want to restore. It is possible to just copy everything manually or apply a patch. But I want the tree to look something like

    -------
   /       \
A-B-C-...-X-Y (HEAD)
            ↑
          master

to make the changes more apparent. I tried to merge using

git checkout master
git merge --no-commit --no-ff B

but git only tells me that everything is "already up to date".


Solution

  • There's no user-oriented command to do what you'd like. You can do exactly that, somewhat manually. For instance:

    git diff <hash-of-A> <hash-of-B> | git apply
    # make sure it looks right
    git add <files>    # as desired
    tree=$(git write-tree)
    vim /tmp/message   # or other editor of choice
    commit=$(git commit-tree -p HEAD -p <hash-of-B> -F /tmp/message $tree)
    git log --graph --decorate --oneline $commit  # make sure you like it
    git merge --ff-only $commit
    # then clean up etc
    

    You can shrink this down to just a few commands using nested $(...) sequences.

    Most people, however, just prefer to use git cherry-pick, perhaps with -x. Note that git cherry-pick -n lets you do the equivalent of the diff-and-apply sequence and may make the above more convenient: I chose the diff-and-apply sequence more for illustration.

    The git write-tree; git commit-tree; git merge --ff-only sequence is roughly how git commit itself actually works, except that git commit is much fancier, and doesn't let you specify the individual parents.

    Another approach is to use git checkout <hash-of-A>; git cherry-pick <hash-of-B> to make a new commit B', then use git merge to merge this new B' commit. (Save its hash for your git merge, or do this on a temporary branch that you delete after the merge, so that you don't need to fuss with raw hash IDs.) The result looks like this instead of what you've drawn:

      ------B'------
     /              \
    A--B--C--...--X--Y   <-- master (HEAD)
    

    This may be clearer to some future viewer than the weird manually-constructed merge.