Back to the future with Git's diff and apply commands

This is one of those “there's probably already a better way to do this” situations, but it worked.

I was having some issues this past weekend where, despite everything working fine locally, a server was showing a “500 Internal Server” after I pushed some changes to a site. In order to bring the site back online, I needed to revert the site files back to the previous version, but as part of a new commit.

The git reset commands removed the interim commits which meant that I couldn't push to the remote (force pushing, quite rightly, isn't allowed for the production branch), and using git revert was resulting in merge conflicts in composer.lock that I'd rather have avoided if possible.

This is what git log --oneline -n 4 was outputting:

14e40bc Change webflo/drupal-core-require-dev version
fc058bb Add services.yml
60bcf33 Update composer.json and re-generate lock file
722210c More styling

722210c is the commit SHA that I needed to go back to.

First Solution

My first solution was to use git diff to create a single patch file of all of the changes from the current point back to the original commit. In this case, I'm using head~3 (four commits before head) as the original reference, I could have alternatively used a commit ID, tag or branch name.

git diff head head~3 > temp.patch
git apply -v temp.patch

With the files are back in the former state, I can remove the patch, add the files as a new commit and push them to the remote.

rm temp.patch

git add .
git commit -m 'Back to the future'
git push

Although the files are back in their previous, working state, as this is a new commit with a new commit SHA reference, there is no issue with the remote rejecting the commit or needing to attempt to force push.

Second Solution

The second solution is just a shorter, cleaner version of the first!

Rather than creating a patch file and applying it, the output from git diff can be piped straight into git apply.

git diff head~3 head | git apply -v

This means that there's only one command to run and no leftover patch file, and I can go ahead and add and commit the changes straight away.