Git is a powerful version control system that allows for multiple branching versions of your project with automatic merging. That’s great, but sometimes it breaks when files are renamed or aren’t in the right place. Luckily, you can manually apply patches to different files.
When using Git branches, it’s often necessary to apply changes from one branch to another. A common use case of this is versioned branches—if you have an old LTS release, you may want to apply the occasional patch to an old version. Or perhaps you are providing two builds of software targetting different toolchains or environments, and need to keep them in sync.
If your branches have the same layout, you can use
git cherry-pick, which can copy individual commits from one branch onto another. For example, pulling a commit from a feature branch onto
master before the entire thing is merged:
However, software isn’t always clean, and in the process of refactoring, you may come across cases where files have been moved or renamed. Git works off of directory paths, so this completely breaks
git cherry-pick, as Git cannot apply changes to files that do not exist.
Git has tools to fix this though, so when normal merging breaks down, you can manually patch files with updates using
Using Git Diffs and Patches
To showcase this, we’ll create an empty repository with a single file:
Old.java. A new branch,
old-version, is made with this file, plus some changes. This file gets renamed with some later changes on
master branch to
New.java, breaking compatibility between the branches.
And this is what the branch history looks like, shown in the GUI Git program Fork:
In this example, we need to move the “Add more code” commit onto the
old-version branch. To do that, you’ll need to get the ID of that commit from the reference log:
format-patch with the ID and
-1 flag, which will create a patch file:
git format-patch 82176b5 -1
This will generate a patch file in the current directory. You may want to move this to a different folder that is in the
.gitignore config, so it’s not affected by changing branches.
Then, checkout the
old-version branch, and apply the patch changes using the Linux
patch utility. Git has their own tools,
git apply and
git am, which can handle it, but short of manually editing the patch file, they don’t have options for changing the target file in cases of renames/moves.
You can pass the filename with the
-p1 parameter, and pass the patch file with
patch -l -p1 old -i Patches/0001-Add-more-code.patch
This should edit the file and add in the changes, which you will need to commit. One downside of
patch compared to
git am is that is does not preserve commit info, though you can find that in the
.patch file if you want to keep the original message.
However, merge conflicts can still happen if the target branch gets edited in the meantime, and in the event one does, you’ll be stuck with manual editing, since
git merge cannot solve this for you.
patch will output files containing the diffs, which you will need to manually fix.
Alternatively, you could move the target file to the target directory Git expects, allowing Git to
cherry-pick and apply changes. Then, copy the file back to the original place, and delete the temporary file.
Patch can also have issues with line feed endings (LF) vs carriage return line feed (CRLF) that can result from Windows editing. You may need to swap to LF in your text editor for the patch to apply correctly.