Search code examples
gitformat-patch

Git: Apply a patch as if the changes were manually


I have changes in a feature branch, that I'd like to transfer over to the develop branch commit-by-commit as if I initially created them on develop in the first place, by hand.

Constraints:

  • If necessary, I want to manually fix merge conflicts per commit.
  • The commits on develop should be completely independent of the feature branch. I.e. they should be new commits, not sharing any ID (or else(?)) with the feature branch.

What I tried

I tried to use format-patch and git am / git apply for that, but it fails at merging.

I created a patch file containing several patches using
git format-patch [COMMIT ID] --stdout > ~/my.patch

Applying the patch results in errors
$ git am --3way --ignore-whitespace ~/my.patch

Applying: commit message 1
Applying: commit message 2
Applying: commit message 3
Applying: commit message 4
Applying: commit message 5
Using index info to reconstruct a base tree...
M   app/myfile1.py
M   app/myfile2.py
M   tests/test_myfile1.py
error: patch failed: app/myfile1.py:61
error: app/myfile1.py: patch does not apply
error: patch failed: app/myfile3.py:58
error: app/myfile3.py: patch does not apply
error: Did you hand edit your patch?
It does not apply to blobs recorded in its index.
Patch failed at 0005 Review
hint: Use 'git am --show-current-patch=diff' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

(I did not touch the patch file after creation)

Trying to manually resolve conflicts says there is no conflict:
git mergetool

No files need merging

Notes

The following works fine in regard of merging/conflict solving, but it crushes all commits into one single commit:

git checkout feature
git diff [COMMIT ID] > ~/my.patch
git checkout develop
git apply --3way ~/my.patch```

Solution

  • If you want to reapply a series of commits that are already merged (and reverted) into the current branch, you need to trick git into thinking that the commits have never been merged in the first place... it's not that hard, really, just requires a little bit more work than usual. Let's say that it is a series of 4 commits one after the other, the last one being X.... then you could do this:

    git checkout X~4 # go back to the commit _before_ the 4 commits we want to trick git into believing are brand new
    git cherry-pick HEAD..X # reapply the 4 commits, no changes in them... but these are new commits to git
    # let's bring them over to develop
    git rebase --onto develop HEAD~4 HEAD
    

    And you got your commits on top of develop