• Skip to primary navigation
  • Skip to main content

Deeper Scenery

Further up and further in

  • Home
  • Blog
  • Code
  • Tutorials
  • Privacy Policy
You are here: Home / Tutorial / WordPress Plugin Development with Git/GitHub

Tutorial · Published: March 23, 2014 · Modified: September 4, 2014

WordPress Plugin Development with Git/GitHub

Major props to Kaspars Dambis and the original tutorial that he wrote. I do things a bit differently, but that tutorial was absolutely instrumental in getting me up and running.

Illustration: Using Git with Subversion Mirroring for WordPress Plugin Development

Before We Begin

Note: This tutorial assumes that you’re setting up a new Plugin in the WordPress.org repository, and that you are starting with a GitHub repository. If you have an existing Plugin in the WordPress.org repository and merely want to switch to managing it with Git/GitHub, then please see this awesome tutorial instead.

When you first request that your Plugin be added to the WordPress.org repository, one of the required fields is a URL to your plugin. As described by the form:

must be a link to a ZIP file of the working plugin that includes a completed readme.txt

For this reason (and the fact that I prefer working with Git), I like to start my Plugin development on GitHub. However, this leads to a few extra steps in getting the Plugin set up on the WordPress.org repository. The purpose of this turorial is to document these steps as much for myself as for anyone else who wants to do something similar.

Here’s an overview of the steps we’re going to take:

  1. Set up the Plugin on GitHub
  2. Submit the Plugin to the WordPress.org repository
  3. Wait. Probably for days.
  4. Sync up the Plugin from GitHub to the WordPress.org repository

Set up the Plugin on GitHub

If you already have a local Git Repository to push, skip to the “Existing Repository” section.

New Repository

  1. Create a new repository on GitHub.
  2. Use the “README” link to create a new README file (there’s a reason not to do this when you initially create the repo… see the next step)
    Screenshot: Showing the location of the README link
  3. Change the name of the README.md file to README.txt. This ensures that WordPress will recognize it in the Plugin Repository. If you don’t know where to start, use the sample readme.txt file
    Screenshot: Changing the name of the README file
  4. Click the “New File” link to create the main file of your plugin. It is good practice to give it the same name as the overall repository name (e.g. sample-plugin/sample-plugin.php).
    Screenshot: The location of the "New File" link

Existing Repository

This is very easy ๐Ÿ™‚

  1. Create a new repository on GitHub.
  2. Add the new repository as a remote to your local repository (make sure to use your repository name, not the sample below)
    git remote add origin https://github.com/JPry/sample-plugin.git
    
  3. Push your code up to GitHub (this is assuming you’re working with the branch “master”).
    git push --set-upstream origin master
    

Submit the Plugin to the WordPress.org repository

This part is fairly simple.

  1. In GitHub, copy the link to the “Download ZIP” button for your repository. I usually just right-click on the button and choose “Copy Link”
    Screenshot: The "Download Zip" button on GitHub
  2. Fill out the Add Your Plugin form on WordPress.org. Since you already have a README.txt file, this should be a matter of copy and paste. For the “Plugin URL” field of the form, use the link that you just copied from GitHub.
  3. Note: I’m not sure if this step is necessary. Delete your local repository. We’ll set it up again later.

Wait. Probably for Days

Seriously, just be patient. It could take a long time (a week or more) before the Plugin Review team is able to review and accept your plugin. It all depends on how many other plugins need to be reviewed ahead of you.

Sync up the Plugin from GitHub to the WordPress.org repository

Once your Plugin is reviewed and accepted, you will receive an email from WordPress.org with the link to your plugin. From here on out, we’ll use the link to the last plugin I set up this way: http://plugins.svn.wordpress.org/remove-wp-engine-404-for-bots/

There are a few sub-steps to this process, which I will flesh out even further:

  1. Connect to SVN, and link your local machine to the SVN repository
  2. Connect your local repository to GitHub
  3. Rebase your Git history onto SVN
  4. Push to SVN and Git
  5. Tag everything!

Connect to SVN

  1. First, we need to get the revision number of the first commit to the WordPress Subversion repository, because otherwise Git will try to go through all the 100000+ commits in order to find it:
    svn log http://plugins.svn.wordpress.org/remove-wp-engine-404-for-bots/
    

    When I did this, I received the following output:

    ------------------------------------------------------------------------
    r876816 | plugin-master | 2014-03-17 18:08:41 -0400 (Mon, 17 Mar 2014) | 1 line
    
    adding remove-wp-engine-404-for-bots by JPry
    ------------------------------------------------------------------------
    
  2. Use the revision ID (in my example, r876816) to create a local Git Repository based on the SVN repository:
    git svn clone --no-minimize-url -s -r876816 http://plugins.svn.wordpress.org/remove-wp-engine-404-for-bots/
    

    This generated the following output:

    WARNING: --prefix is not given, defaulting to empty prefix.
    This is probably not what you want! In order to stay compatible
    with regular remote-tracking refs, provide a prefix like
    --prefix=origin/ (remember the trailing slash), which will cause
    the SVN-tracking refs to be placed at refs/remotes/origin/*.
    NOTE: In Git v2.0, the default prefix will change from empty to 'origin/'.
    
    Initialized empty Git repository in /Users/jeremypry/plugins/remove-wp-engine-404-for-bots/.git/
    r876816 = 368bc048d6213a636054431e45bbc34ebadc6e20 (refs/remotes/trunk)
    Checked out HEAD:
    http://plugins.svn.wordpress.org/remove-wp-engine-404-for-bots/trunk r876816
    

    The --no-minimize-url flag is important, as it stops git svn clone from moving into the base folder of the remote SVN repository and going through all plugins (see git-svn docs). We also use the -s flag which is a shorthand way of setting trunk, tags, branches as the relative paths, which is the Subversion default (from git-svn docs).

    In regards to the big WARNING message, it looks like it would be a good idea to include the --prefix flag in the future. I would probably recommend doing something like --prefix=svn/, so that we can still use the origin remote for GitHub later.

  3. Move into the new directory

    cd remove-wp-engine-404-for-bots/
    

  4. Fetch any and all SVN revisions. Because this is for a new plugin, this shouldn’t take very long
    git svn fetch --all
    
  5. Probably not necessary (for a new plugin) but we should ensure we’re up-to-date anyway:
    git svn rebase
    

Connect your local repository to GitHub

  1. Add the GitHub repository as a remote.
    git remote add origin https://github.com/JCPry/remove-wpengine-404.git
    
  2. Retrieve your commits from GitHub
    git fetch origin
    
  3. Take a look at your branches, as we’ll need them in the next major step
    git branch --all
    

    I received this output:

    * master
    remotes/origin/master
    remotes/trunk
    

Rebase your Git history onto SVN

Rebasing is something that seems pretty scary, but here we have a valid use for it: We want to set up the very first SVN revision as the beginning of our plugin history. The reason rebasing is required is because the Plugin creation in SVN took place after we created the GitHub repository. With that said, we’re ready to jump in! ๐Ÿ™‚

  1. Run the rebase command:
    git rebase --onto remotes/trunk --root remotes/origin/master
    

    Here, you can see that I’m using the branch information from the previous section. The --onto remotes/trunk flag is telling Git that I’m using the SVN trunk “branch” as the base, or beginning, of my history. The --root remotes/origin/master section is specifying the exact branch we want to rebase. This is the history from GitHub.

    I had 3 commits, so my output looked like this:

    First, rewinding head to replay your work on top of it...
    Applying: Set up the Readme file
    Applying: Create the main plugin file
    Applying: Update minimum version
    
  2. See where we stand in the Git history:
    git log
    

    Here’s my output:

    Wed Mar 12 15:26:26 2014 -0400 382f7de (HEAD) Update minimum version [Jeremy Pry]
    Wed Mar 12 15:25:06 2014 -0400 9fa6aad Create the main plugin file [Jeremy Pry]
    Wed Mar 12 15:24:07 2014 -0400 88c72ca Set up the Readme file [Jeremy Pry]
    Mon Mar 17 22:08:41 2014 +0000 368bc04 (trunk, master) adding remove-wp-engine-404-for-bots by JPry [plugin-master]
    

    What are we looking at? Well, basically you can see that trunk (our SVN “branch”) and master are still back at the commit with ID 368bc04 (the last one in the output above). We need to bring those up-to-date.

  3. Create a new branch with the up-to-date history. I’m using the branch name git-merge:

    git checkout -b git-merge
    

  4. Switch mack to the master branch, and merge in the new git-merge branch:
    git checkout master
    git merge git-merge
    

    We’re almost there! Checking the status again with git log, we can see that the master branch is up-to-date, but trunk (the SVN branch) is still behind:

    Wed Mar 12 15:26:26 2014 -0400 d2bdb48 (HEAD, master, git-merge) Update minimum version [Jeremy Pry]
    Wed Mar 12 15:25:06 2014 -0400 36811a7 Create the main plugin file [Jeremy Pry]
    Wed Mar 12 15:24:07 2014 -0400 d85ad1f Set up the Readme file [Jeremy Pry]
    Mon Mar 17 22:08:41 2014 +0000 368bc04 (trunk) adding remove-wp-engine-404-for-bots by JPry [plugin-master]
    
  5. Before we go any further, we should delete the git-merge branch, since we’re done with it:
    git branch -d git-merge
    

Push to SVN and Git

This section is easy, but the first step could take a bit of time (I’m not sure why…)

  1. Push the changes up to SVN
    git svn dcommit
    

    For me, when all was said and done the output looked like this:

    Committing to http://plugins.svn.wordpress.org/remove-wp-engine-404-for-bots/trunk ...
    A README.txt
    Committed r877638
    A README.txt
    r877638 = 94682a77c937231cedd37fc11cfad5f588df1465 (refs/remotes/trunk)
    A remove-wpengine-404.php
    Committed r877639
    A remove-wpengine-404.php
    r877639 = 30eddd8196c70af8a3bb5aa3f391fe2994be1d3c (refs/remotes/trunk)
    M README.txt
    Committed r877640
    M README.txt
    r877640 = 0452d0393666971d6e3546a559535f775b05863f (refs/remotes/trunk)
    No changes between d2bdb48c8798c69b727b5c03a711b85f7f7396fb and refs/remotes/trunk
    Resetting to the latest refs/remotes/trunk
    
  2. Do a forced push to GitHub:
    git push -f origin master
    

    The -f flag is necessary to force the push to go through. The reason is because of the git rebase command that we used earlier. We have existing history on GitHub, and one of the side effects of rebasing is that all of the old history is removed, and replaced with new history. The forced push stops Git from complaining that the history no longer matches.

Tag everything!

You already made this Plugin, so you should be ready to release it to the world! I’m tagging with version 1.0, but feel free to use a different version if applicable.

  1. Tag in Git
    git tag 1.0
    
  2. Tag in SVN
    git svn tag 1.0
    

    This takes a lot longer… again, I’m not sure why, but I’m assuming it has to do with the processing that needs to be done by the SVN server.

    The output should look something like this:

    Copying http://plugins.svn.wordpress.org/remove-wp-engine-404-for-bots/trunk at r877640 to http://plugins.svn.wordpress.org/remove-wp-engine-404-for-bots/tags/1.0...
    Found possible branch point: http://plugins.svn.wordpress.org/remove-wp-engine-404-for-bots/trunk => http://plugins.svn.wordpress.org/remove-wp-engine-404-for-bots/tags/1.0, 877640
    Found branch parent: (refs/remotes/tags/1.0) 0452d0393666971d6e3546a559535f775b05863f
    Following parent with do_switch
    Successfully followed parent
    r877641 = df9c0f98857fbbf5e7f0f138a5bccdd67ff9d8b1 (refs/remotes/tags/1.0)
    

Working with the Plugin

Now let’s assume that we want to create a new release of the Plugin in order to invite the Plugin users to report bugs and get involved on GitHub. For that we need to edit the copy of readme.txt and bump the version number of the plugin.

Once we have made the changes, we can check the status of our local Git repository:

git status

Let’s commit the changes to our local repository:

git commit -am "Adding message about moving to GitHub, version bump"

Pushing Changes to GitHub

To publish our local commits to GitHub, we use

git push

Pushing Changes to WordPress Subversion Repository

Once you have switched to using Git as your version control system, you should always use git svn dcommit to publish changes to the SVN repository. Otherwise, you must run git svn rebase every time before pushing changes to SVN in order to merge changes from the SVN repository into your local Git repo.

If you are not using trunk as a release branch for your plugin, you may safely push all your Git changes to the WordPress repository without worrying about users getting update notices:

git svn dcommit

Tagging Releases

Tagging a release in Git is very simple:

git tag 1.0.1

To create an SVN tag, simply:

git svn tag 1.0.1

This will create /tags/1.0.1 in the remote SVN repository and copy all the files from the remote /trunk into that tag, so be sure to push all the latest code to /trunk before creating an SVN tag.

Updating and Trakcing Plugin Assets

Assets folder in the WordPress SVN repository can be used for storing Plugin banners and screenshots. Make sure that the /assets folder actually exists in your SVN repository before trying to fetch it. Open this in your web browser:

http://plugins.svn.wordpress.org/easy-digital-downloads/

where easy-digital-downloads is the slug of your plugin, and you should see the root folder of your Plugin SVN repository:

Revision 851441: /easy-digital-downloads

..
1.0.4/
assets/
branches/
tags/
trunk/

Notice that the /assets folder is present.

Let’s track that folder as a seperate Git branch called assets. Every time you need to update the cover image or screenshots you, simply checkout that branch, do the changes and publish it to SVN using the same old git svn dcommit.

Add a new SVN remote called assets:

git config --add svn-remote.assets.url http://plugins.svn.wordpress.org/easy-digital-downloads/assets

and set it to use the assets branch on our remote SVN repository:

git config --add svn-remote.assets.fetch :refs/remotes/assets

This will have appended the following to our .git/config:

[svn-remote "assets"]
url = http://plugins.svn.wordpress.org/easy-digital-downloads/assets
fetch = :refs/remotes/assets

which means that we can now fetch the assets remote at its current HEAD:

git svn fetch -r HEAD assets

and switch to it using:

git checkout remotes/assets

Note: checking out 'remotes/assets'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

git checkout -b new_branch_name

HEAD is now at 7e5b727... Updated banner image

Let’s create a local branch called assets to track this remote:

git checkout -b assets

Now you can add and modify Plugin assets, commit the changes and then publish the changes to the SVN, using:

git svn dcommit

Converting SVN Folders into Git Branches and Tags

In the list of newly created branches we see that all SVN tags have been turned into regular Git branches. Here is a simple script to convert those tag branches into proper Git tags:

git for-each-ref refs/remotes/tags | cut -d / -f 4- | grep -v @ | while read tagname; do git tag "$tagname" "tags/$tagname"; git branch -r -d "tags/$tagname"; done

And we can do the same for all other references under refs/remotes — turn them into Git branches like so:

git for-each-ref refs/remotes | cut -d / -f 3- | grep -v @ | while read branchname; do git branch "$branchname" "refs/remotes/$branchname"; git branch -r -d "$branchname"; done

Suggested Reading

  • http://teleogistic.net/2011/05/revisiting-git-github-and-the-wordpress-org-plugin-repository/
  • http://eamann.com/tech/how-to-publish-a-wordpress-plugin-git/
  • http://ben.lobaugh.net/blog/90633/creating-a-synchronized-github-fork-of-a-wordpress-org-subversion-plugin-repository
  • http://en.hetarena.com/archives/232

Filed Under: Tutorial

Previous Post: « Perspectives
Next Post: Mother’s Day โ€“ My Wife »

Reader Interactions

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Copyright © 2025 ยท JPry Showcase Pro on Genesis Framework ยท WordPress ยท Log in