Using Git Subtree for WordPress Projects
Strap in folks, this is a nerdy one. If you’re not already a little aware of the role of Git in your life, there’s a good chance that this article isn’t going to work for you. While I will make sure I anchor our conversation about Git Subtree’s use in my WordPress development with a little context of what Git is, it’ll be minimal. This is not a “Git” tutorial, it’s pretty explicitly a “git subtree” tutorial.
I don’t think
git subtree is right for everyone. I often don’t even think its right for me. But when certain realities our my work organizational structure and history demanded that I learn how to use it for WordPress development, I did. And so my goal here is to explain why Git Subtree was necessary for my (much loved) Git Push to WP Engine deployment system. And because that’s pretty closely a map off all the parts of
git subtree, I think this may help a few non-WordPress folks too. Lets get to it!
In short, Git is a version control software. What that means is that I can use Git to figure out the history of my code, what its status was on a particular day, who was responsible to which changes on a specific line of code, etc.
We’ll assume that if you understand that, you’ll also familiar with the basic patterns of Git. That you understand you
git pull to receive changes to a local copy of a project, and
git push them share them with others on Github, Bitbucket, Gitlab, etc.
Why use distributed version control?
Strictly and importantly Git isn’t just “version control software,” it is distributed version control software. That’s got a lot of advantages over its centralized alternatives, like Subversion. By being distributed people can work asynchronously, and generally not have too many complication to fix simultaneous-file-change problems, aka “merge conflicts 👻.” (Don’t worry, they still happen though. And most developers fear them at least a little. Even me.)
Also by being distributed (and, relatedly, having cheap branching) Git makes a pretty good deployment tool. So a simple
git push production master works very well to put all the files for your WordPress site right on hosts that support it. As you get into more complex setups, this can fray on the edges. But for many WordPress sites it is way more than adequate.
Why Git instead of SVN or Mercurial?
Why Git? I love touching on this just because
git annoys me. I feel like
git‘s CLI isn’t as well designed as others I’ve fiddled with from time-to-time, most relevantly Mercurial and Subversion.
But, for better worse, for complex (and simple) reasons, the world kind of all converged on Git as the distributed version control system of choice. The success of companies like Github and Gitlab owes everything to underlying success of Git vs its rivals. So partly we use Git because Git is good. Partly
git because to do anything else for version control these days is wacky and hard to justify. 🤪
Git Subtree vs Submodules
The primary problem we’re using Git subtrees for on this project is the same one that other people use “Git submodules” for. In both cases, the issue is “I have code I want to maintain in a separate Git repository. But I want to use that code in this project.” BTW: Composer is really the best-practice PHP answer for that problem, and one I continue to feel unsure quite how to mix into WordPress workflows best. (If you’ve got strong opinions on that and would like to write about them for WPShout, email “david” AT this domain. I’d love to publish an article or two on that. 🤓)
So to reuse code from a different project, we’ve got Subtrees and Submodules. If one of these is more popular, in my estimation it is surely Git Submodules. Here are the basic differences as I understand them:
- File control: A Git subtree pulls all files into the filesystem of your project’s Git repository. A Git submodule doesn’t add the “child project’s” files to your project’s history.
- History + Merging: with Git subtrees, you have commits explicitly merge in changes from the remote repository. In Submodules, those changes are tracked with a change to the
How I’m Using Git Subtree
So if Git submodules are more popular, why am I using subtrees? First: I’ve got a giant repo of all non-Core files in a WordPress site. Which means all the plugins, all the themes, and so effectively everything in
wp-content BUT the media files. And I’ve got a WordPress plugin that we’re maintaining independently because it’s also installed on a variety of other sites, other than this “monorepo.” So I’m using a Git subtree to track changes to the plugin in my “WordPress site” repository, and be able to
git push those changes to production.
WP Engine Allows Git Push Deploys with Subtree
In order to push my “sub-repository” to the WP Engine production server, I need its files to be reflected in the Git history. And to do that, I’ve been forced to use Git Subtree. My most recent use of Git Submodules was in the vague hope I could use them on this project. But then I realized that my valued Git push functionality (on WP Engine) required that I have a
git subtree instead. So that’s what I’ve got now.
To set this all up, there are really three basic steps:
1. Add Your “Sub-repository” as a Git Remote
First of all, we just want to save ourselves from needing to deal with a long Github/Gitlab/etc URL, so let’s define that remote as a branch to get a shorter name. In case it’s not obvious, in all the snippets that follow, you’ll be replacing
MyGitSubRepo with the actual “slug” you want to use for your project, capitalized and punctuated however you prefer. Similarly, you’ll customize my Github example URL. Anyway, command is:
git remote add -f MyGitSubRepo https://github.com/davidbhayes/MyGitSubRepo.git
2. Add that New Remote as a Subtree
Once we’ve got that remote, we need to tell Git where it should put our subtree’s files. Because my repository is essentially “a WordPress site” and my sub-repository is a WordPress plugin, that command looked like this for me:
git subtree add --prefix wp-content/plugins/MyGitSubRepo MyGitSubRepo master --squash
What’s happening is that Git will then pull (and squash) the
master branch of your repository and make a commit to the current branch you’re on of the current state of that branch on your sub-repository.
(Again, replace all
MyGitSubRepo with the real shorthand branch that matches how you think of your project.)
3. For future changes, fetch and pull your Git Subtree
The above will have initiated and pulled in all your files once. But chances are good that you’ve pursued this setup because you want to be able to pull changes from your Git sub-repository over time using the git subtree command. That’s basically done by (1) updating your local copy of the remote repository with a
git fetch and then (2)
git subtree pulling in the changes, with a command very similar to the first pull:
git fetch MyGitSubRepo git subtree pull --prefix wp-content/plugins/MyGitSubRepo MyGitSubRepo master --squash
Because I almost never need/want to run these command independent of each other, I have a little
update-MyGitSubRepo.sh script I can run to turn the two complex steps into a simple one-liner. But that’s by no means required, and a more extensive explanation of that isn’t really in the scope for this article.
A Gotcha That Gets Me A Lot
Doing all of the above will generally work fairly well for keeping your Git Subtree up to date for your WordPress plugin. There is a common issue that I have though:
git subtree requires that your local working copy be “clean.” By clean, I mean that it has no untracked-in-Git changes sitting around.
Making your Git repository “clean” before you update with Step 3 above isn’t too hard. It mostly means that your
git status should be blank. And 90% of the time, ensuring that before running works for me. If not, your command will fail with a
Working tree has modifications. Cannot add. response.
Sometimes I get that error because I forgot to clean my
git status. But I also sometimes get it even when I did. And that’s a frustrating and confusing error.
But, as I learned from a nice StackOverflow answer the solution here is a git command I didn’t know:
git diff-index HEAD
My quick summary of this command, having used it, is that it’s like a
git status with extra details. So often what I’ll find when running it is that an untracked file got updated, or something else that for various reasons
git status didn’t tell me about. Hopefully you won’t need it, but I also hope that having told you of it helps you out when you do.
Git Subtrees & WordPress: Branching Out in Git ;p
I don’t think most developers on most WordPress sites need to know about Git Subtrees. I think that most should use Git though 🤓
But I hope that by summarizing how I use Git Subtrees (and not Submodules) with WordPress I’ve given you a place to start when you finally have a situation that calls for you two maintain to independent WordPress code repositories for a single site. And if so, I hope you’ll have found this a bit less awkward to learn than I did. All the best!