How to Apply a GitHub Commit to Magento Core Code

How to Apply a GitHub Commit to Magento Core Code

Read about how the process of applying a specific commit from the GitHub version control system to the core of Magento code.

You have a bug with Magento, which does happens from time to time. Then, you stumble onto a PR on GitHub that looks like there’s already a fixed merged in or one pending. You could wait until the next release to get this update in, but that can take many weeks or months. And this is an important fix that you would really like to push out sometime in the next couple days, or even today.

There’s a way to apply the related commit by using the cweageans/composer-patches module with Git & composer.

Get the diff

Let’s say I have a bug with Magento where it isn’t translating salutations, such as prefixes or suffixes in a name. Then, I head to GitHub to see if there isn’t already a fix for it. Low & behold, here’s a closed issue that describes my exact situation. And it’s closed, because there’s already a merged PR about how to fix this.

Let’s go to the PR, and since it’s already merged into the current release branch, magento:2.4-develop, we know that Magento has already code-reviewed this PR, and it’s already merged into the core.

Merged GitHub commit

When we go to the files changed, we can see that it’s not a huge update but more than a couple lines of code than we want to type. There’s a really easy way to grab the diff or patch file for this PR. Head back to he conversation tab, head up to the URL, and add in a .diff extension, and the diff file will load. This is pretty neat, because we can now take this diff patch file and apply it to our Magento store.

Git diff

Fix module & filesystem references

Now that we have the diff file, we need to update the file structure of the patched file. This isn’t an official patch file, which makes it a bit more difficult to apply. Note how the file is referencing the locations of Magento modules in app/code/Magento — we need this location to be the composer location instead in the vendor directory.

We will download this file, but we’ll first create a directory at patches/composer to place it in. This is the recommended directory by the cweagans module, and is the place to put all of these diff files.

Now lets download it. We can do this right from a browser, but lets use curl for now by typing curl and the URL of the diff, followed by an arrow, then followed by what I want to name it, which will be PR-30413.diff.

curl https://patch-diff.githubusercontent.com/raw/magento/magento2/pull/30413.diff > PR-30413.diff

Then we will need to rename all of these file locations. Let’s do a global search for app/code/Magento/Customer, and replace it with vendor/magento/module-customer.

We’ll need to also do the same for app/code/Magento/Sales, renaming the references to vendor/magento/module-sales.

We’ll also need to actually separate this file out into two different patch files, one for each module. This is because later on when we use the automated patch tool, it expects these diffs to relate 1:1 to a specific module.

Let's move the Sales-module-specific code to PR-30413-sales.diff, and keep the Customer-module-specific code in the file and rename it to PR-30413-customer.diff. Then, each file will only contain references to the related module, one for the customer module, and one for the sales module.

patches/composer/PR-30413-customer.diff
# Contains code related to vendor/magento/module-customer
 
patches/composer/PR-30413-sales.diff
# Contains code related to vendor/magento/module-sales

Test the patches

Now we are going test out applying the patch, but with the --dry-run argument. We can type:

patch --dry-run < m2-hotfixes/PR-30413-customer.diff

And, we'll see there's actually an error when applying one of the hunks on the patch:

Patch hunk error

When we look at this diff, every of these groups of changes is called a hunk. So if we look at the one applying to vendor/magento/module-customer/Helper/View.php, it has two hunks, and they are each separated by one of these @ symbols in the changeset.

We need to find out exactly how and where this error is occuring. Patches only don’t apply when the diff changesets don’t exactly match the original file.

First we will check out the getCustomerName function in the vendor/magento/module-customer/Helper/View.php file. Let's start with the first line of the patch, which looks like it replaces the brackets in the @inheritdoc docblock with text that doesn't have the brackets. Checking this out in the View class, we’ll see that it already doesn't have brackets.

Inheritdoc diff View file

Usually we’d need to re-generate the diff or patch file, but this is overly complex. What we can do instead is just change the removal line to the same line as the addition line. This essentially makes it so that this update doesn’t happen at all.

Updated diff

Now we re-run the patch command with --dry-run, and we can see that all patches will now succeed. This is why you should always use --dry-run rather than assuming a patch will succeed, because once you get this all set up in your automated deployments, a patch that bombs out can kill that deployment and prevent it from being pushed out in your pipeline.

We’ll do the same with the sales module, running patch --dry-run < m2-hotfixes/PR-30413-sales.diff, and since everything is all good there as well, we are ready to automate this process.

Path dry-run succeeds

Automate applying patches when deploying code

Now we could just run the patch line again without the —dry-run argument, and this will patch our files. But we want our patches to be automatically applied when we deploy our code. Luckily, there’s a really handle module called cweagans/composer-patches that is extremely popular, and made for just this scenario.

First, we will need to install it with Composer. We can do that be executing:

composer require cweagans/composer-patches

This module uses a combination of being a Composer plugin along with git apply behind the scenes to automatically apply the patch anytime the composer install or composer update command is executed. It looks for specific properties of the composer.json file to determine what patches to apply.

Lets open up the composer.json and go all the way to the bottom. Magento usually has an extra property already containing the magento-force override property value, but the cweagans module looks for a property at extra.patches, so let’s create that:

...
"extra": {
    "magento-force": "override",
    "patches": {

    }
}
...

Next comes the module we wish to apply the patch to, which is relative to the vendor directory. Since we want to apply our first patch to the customer module, this will be magento/module-customer. Next, within this object is where we will define a key:value pair. The key will be a description of the patch we want to install. I like to prefix this with the patch filename which is the PR and number, and then the description of the PR or what it actually does. This will help us in the future if this starts failling out during an upgrade or something else. Our second value will be the location of the patch file, so this will be patches/composer/PR-30413-customer.diff. We’ll then do the same thing with the sales module.

Note that each of the keys must be different for the cweagans module to act on them. If we had the same key for both of these modules, even though they are for two different modules, it will only execute the first one it sees.

...
"extra": {
    "magento-force": "override",
    "patches": {
        "magento/module-customer": {
            "PR-30413-customer: Prefix and Suffix Translation": "patches/composer/PR-30413-customer.diff"
        },
        "magento/module-sales": {
            "PR-30413-sales: Prefix and Suffix Translation": "patches/composer/PR-30413-sales.diff"
        }
    }
}
...

Now that these patch definitions have been setup, the cweagans Composer plugin will detect these patches and execute it on any composer install or composer update command. Let’s run composer install, and we’ll see that it detected a patch exists for the customer and slaes modules.

What it did is interesting; it actually completely removes the modules, installs them again, and then applies our diff patches on top of the modules. It does this just to make sure that it starts from a fresh clean base. This ensures that we get the same result every time to account for a situation where we may have actually modified one of these files in development.

Composer install

Now that it has applied these updates, we can confirm it did be opening up one of the files that had changes in this diff.

For example, the vendor/magento/module-sales/Model/Order/Address.php file. If we go to the getName() module, we’ll see that the translation function is now wrapping the getPrefix() and getSuffix() function calls of the name:

View file updated

Now you know how to apply Magento code changes from GitHub, and have them be automatically applied to an automated deployment pipeline.

Curious to explore the world of Magento further? I'm here to help with these 3 resources:

  1. Explore Magento 2 fundamentals & best practices course (1,000+ students)
  2. Grow your Magento expertise with all courses & lessons (700+ students)
  3. Learn visually with blocks of code & inline comments (3,000+ students)