WordPress with GIT, WP-Skeleton and Roots 101

Goals: – Keep a local WordPress  installation under version control (but ignore WordPress core files) – Use WP-Skeleton (wordpress core as submodule in separated folder, content and config files in root) – Use the starter theme Roots 101 – Deploy to a staging and production server (depending on the current Git branch?) – Keep uploads out of version control, sync them separately – Use a good DB migration strategy – If possible: automate repetitive tasks

Assumptions

– Windows 7 – WAMP – Cygwin (linux environment) – Git, (Ruby, Compass, Grunt, Rsync) – Bitbucket account – SSH access to server (shared host) – SSH-keys set up for your servers and Bitbucket

1. Create the WP-skeleton directory structure

If you set up this project recently, you can use an existing repo with WP-Skeleton and roots. See 1b.

  • Clone Jaquith’s WordPress-Skeleton in Bitbucket 
  • Note: for a clean start don’t fork the repo directly, but first clone the repository locally, remove the history and then push it to your new bitbucket repo.
  • mkdir temp
    cd temp
    git clone git@bitbucket.org:[USERNAME]/wordpress-skeleton-[version].git .
    rm -rf .git
    rm .gitmodules
    rm -rf wp
    git init
    git add .
    git submodule add https://github.com/WordPress/WordPress.git wp
    cd wp
    git tag
    git checkout [version]
    cd ..
    git add .
    git commit -m'setup skeleton'

Create a new repository with your project name on bitbucket. Then push:

    • git remote add origin git@bitbucket.org:[username]/[projectname].git
      git push -u origin --all
       
      #Of course there should be an easier way to remove history, but for now this works

Remove the temp folder created in the steps above
Now create a project folder and clone the repository into a subfolder (e.g. www). Recursively, because the wp submodule should also be cloned (don’t need to add –recursive if you added a localized WP installation as descrived above).

git clone --recursive git@bitbucket.org:USERNAME/PROJECTNAME.git www

1b. Headstart from existing repo

  • Clone the repo with WP-Skeleton and the roots theme in a temp folder
  • rename the theme folder to [project]-theme.
  • Change the style.css of the theme with your theme name and details
  • remove the contents of readme.md in the root (not roots!) folder
  • If using localized WP:
    • Remove WP submodule, and add WP localized as normal files to git repo (see also the post with Git snippets)
  • Create a new repository with your project name on bitbucket. Then push:
  • git remote add origin git@bitbucket.org:[username]/[projectname].git
    git push -u origin --all
  • Remove the temp folder created in the steps above
    Now create a project folder and clone the repository into a subfolder (e.g. www). Recursively, because the wp submodule should also be cloned.

    git clone --recursive git@bitbucket.org:USERNAME/PROJECTNAME.git www

2. Local Database and aliases

  • Create a local database. Doesn’t have to be the same name as on the server, since we are going to use a local config for wordpress
  • If the project directory is outside the WAMP directory, create an alias

3. Configure Wordpress files

    • Rename the local-config-sample.php to local-config.php
    • Fill in the local db name and credentials
    • While we’re at it, add these lines to enable debugging in the local environment
      // debug for local development
      define('WP_DEBUG', true);
      define('WP_DEBUG_LOG', true);
      define('SAVEQUERIES', true);
       
      ini_set('log_errors',TRUE);
      ini_set('error_reporting', E_ALL);
      ini_set('error_log', dirname(__FILE__) . '/error_log.txt');
    • Edit wp-config.php: Create salted phrases: https://api.wordpress.org/secret-key/1.1/salt/
    • Change the table prefix (if I remember well it is best to not use capitals, for some reason) . Use for instance this tool
      $table_prefix  = ‘wp09g3h4kg_’;
    • Because I want to use a staging and a production server, which will have different DB credentials, I added this code to wp-config.php:
===================================================
// Load database info and local development parameters
// ===================================================
if ( file_exists( dirname( __FILE__ ) . '/local-config.php' ) ) {
   define( 'WP_LOCAL_DEV', true );
   include( dirname( __FILE__ ) . '/local-config.php' );
} else {
   define( 'WP_LOCAL_DEV', false );
   if (file_exists( dirname( __FILE__ ) . '/.staging' ) ) { // STAGING SERVER
      define( 'DB_NAME', '%%STAGING_DB_NAME%%' );
      define( 'DB_USER', '%%STAGING_DB_USER%%' );
      define( 'DB_PASSWORD', '%%STAGING_DB_PASSWORD%%' );
      define( 'DB_HOST', '%%STAGING_DB_HOST%%' ); // Probably 'localhost'
   } else {
      define( 'DB_NAME', '%%DB_NAME%%' );
      define( 'DB_USER', '%%DB_USER%%' );
      define( 'DB_PASSWORD', '%%DB_PASSWORD%%' );
      define( 'DB_HOST', '%%DB_HOST%%' ); // Probably 'localhost'
   }
}
  • Don’t forget to add a .staging file to the repo root directory on the staging server
  • I also ended up adding this to wp-config.php, because the local url is different from the server url
    if ( WP_LOCAL_DEV ) {
       define( 'WP_CONTENT_URL', 'http://' . $_SERVER['HTTP_HOST'] . '/wptest7/content' );
    } else {
       define( 'WP_CONTENT_URL', 'http://' . $_SERVER['HTTP_HOST'] . '/content' );
    }
  • You may also add some other usefull stuff to the wp-config.php:
      • Increase the memory limit.
        define('WP_MEMORY_LIMIT', '64M')
      • FTP/SSH Constants:
    // forces the filesystem method: "direct", "ssh", "ftpext", or "ftpsockets"
    define('FS_METHOD', 'ftpext');
    // absolute path to root installation directory
    define('FTP_BASE', '/path/to/wordpress/');
    // absolute path to "wp-content" directory
    define('FTP_CONTENT_DIR', '/path/to/wordpress/wp-content/');
    // absolute path to "wp-plugins" directory
    define('FTP_PLUGIN_DIR ', '/path/to/wordpress/wp-content/plugins/');
    // absolute path to your SSH public key
    define('FTP_PUBKEY', '/home/username/.ssh/id_rsa.pub');
    // absolute path to your SSH private key
    define('FTP_PRIVKEY', '/home/username/.ssh/id_rsa');
    // either your FTP or SSH username
    define('FTP_USER', 'username');
    // password for FTP_USER username
    define('FTP_PASS', 'password');
    // hostname:port combo for your SSH/FTP server
    define('FTP_HOST', 'ftp.example.org:21');
  • By default, WordPress will save an unlimited number of revisions, however this is generally not needed. It is better to reduce this limit to something more practical such as two or three. You can do this by adding the following code to your wp-config.php file:
    define( 'WP_POST_REVISIONS', 3 );
  • Edit .gitignore to ignore . staging (and .production)
  • Remove the symlink to uploads and replace it with a directory uploads. Note: I guess it would be nice to symlink to ../shared/content/uploads and keep the uploads out of the git repo, but on my local install apache couldn’t follow the symlink (for whatever reason). 
  • It is now probably a good idea to ignore the uploads folder. so add this to your .gitignore:
    /content/uploads/
    • .GITIGNORE
      Consider adding the following files to git.ignore
.staging
.production
*.log
.htaccess
.DS_Store
*.bak
*.swp
Thumbs.db
*.log
/*DB backups (folders depend on the plugin you use)*/
wp-content/backup-db/
/*Cache files (folders depend on the cache plugin you use)*/
 
wp-content/advanced-cache.php
wp-content/wp-cache-config.php
wp-content/cache/*
wp-content/cache/supercache/*
wp-content/cache/
 
*/+ the files in .gitignore of roots theme*/
 
*/specific live-only OR development-only plugins*/
  • Note: Since roots 7.0 several assets are ignored. If you deploy with Git like I do, remove these files from .gitignore:
    /assets/vendor/*
    /assets/css/main.css.map
    /assets/css/*main*.css
    /assets/js/*scripts*.js
    /assets/js/vendor/modernizr.min.js
    /assets/manifest.json
  • TODO: come up with a strategy to sync the uploads
  • Add and commit the modified files

4. Install wordpress locally

– open up the url localhost/project/wp/wp-admin in your browser
– Do the install
– Log in
– Change the site address to the root folder (instead of the sub folder wp)
– Optional: change tagline, time format, timezone
– Change permalinks to postname (preset)
– If htaccess not writable: changer permissions to 666 (temporarily After changes are made, change it back to 644)
– Add and commit the changes (to .htaccess) with message: ‘changed permalinks and site url’
– Push to bitbucket

5. Install Roots 101 via a git branch

- git checkout -b roots
- git branch
- cd content/themes

Now clone a theme. We will use Roots 101. You can clone it into a folder named after your project. the default name of the folder is roots.

git clone https://github.com/roots/roots.git THEMENAME
  • Copy the entries in the .gitignore of the theme and paste it to the .gitignore in the root (adjust paths if necessary
  • Now remove the .git directory and all the other .git files:
    cd PROJECTNAME
    rm -rf .git
    rm .gitignore
  • Edit the style.css of the theme
    /*
    Theme Name: THEME NAME
    Theme URI: WEBSITE URL
    Description: Custom theme for ..
    Version: 1.0.0
    Author: YOUR NAME
    Author URI: YOUR URL
     
    License: MIT License
    License URI: <a class="linkification-ext" title="Linkification: http://opensource.org/licenses/MIT" href="http://opensource.org/licenses/MIT">http://opensource.org/licenses/MIT</a>
    */
  • Replace screenshot.png in the root directory of the theme with the logo of the project

Add and commit the changes

git status
git add .
git commit -m'Addition of the Roots 101 Theme'

6. Setup Roots 101

    • Also install GIT for windows, since the bower installer needs it. Make sure to choose the option where git is added to the windows path (enabling it from command prompt)
    • If Grunt isn’t installed yet, do it now (see also this post)
    • go to the roots directory and install the dependencies for Roots contained in package.json by running the following from the Roots theme directory.
      Note: this doesn’t work with cygwin for me. I need to run the command from a windows command prompt. Also: run command prompt as adminNOTE 2: if there is already a node_modules folder (from a previous installation), remove it first!
    • npm install
    • Try the available Grunt commands:
      grunt dev – Compile LESS to CSS, concatenate and validate JS
      (I had permission problems on my new windows 7 install. Setting the noacl option in fstab solved it. see this post)grunt watch – Compile assets when file changes are made
      grunt build – Create minified assets that are used on non-development environments
    • Some additional info regarding Roots v7.0. 
      Note: I opted for the old Roots approach concerning the building of minified assets. see next part (‘Adding *.min.css …’)
      You need to add define('WP_ENV', 'development'); to your wp-config.php, while developing, so your Roots won’t look for the minified version.

      grunt dev basically will do all the work but* minifying;
      grunt watch will run grunt dev when something is modified;
      grunt build will do all the work + minify, so you can send your project to your production server.

  • The process:
  1. Add define('WP_ENV', 'development'); to wp-config.php;
  2. Work on your project with grunt watch;
  3. Finishes the project -> runs grunt build;
  4. Send the project to production server (where the WP_ENV won’t be “development”).
  • Using less source maps sounds like a good idea to. It makes it possible to see the original source lines for preprocessed css in the web inspector. See this article for an explanation on source maps in roots.Note this rootpath will only work locally. Need to come up with a solution for this.
  •           sourceMap: true,
    sourceMapFilename: 'assets/css/main.min.css.map',
              sourceMapRootpath: '/PROJECTFOLDER/content/themes/THEME_NAME/'

    – I also enable sourcemaps for js as follows. Not tested if it works yet

              options: {
              // JS source map: to enable, uncomment the lines below and update sourceMappingURL based on your install
              sourceMap: 'assets/js/scripts.min.js.map',
              sourceMappingURL: '/PROJECTFOLDER/content/themes/THEME_NAME/',
             }

  • Now activate the theme. Leave all options to yes except the one that changes the uploads folder. I did this wrong and couldn’t reset it to the upload folder for skeleton.
  • The home page should look like this:Roots 101 Home page
  • Test if you can upload an image. It should appear in the content/uploads folder
  • If everything works, merge the roots branch with master and push to remote
    git checkout master
    git merge roots
    git push

Roots Plugins:

Note: Install order:
1. H5BP. After activation, refresh permalinks
2. Rewrites
3. Soil

– Since roots 7.0 some functionality are moved to plugins:

– Install Soil to clean up WordPress markup, use relative URLs, nicer search URLs, and disable trackbacks.

Make sure theme_support for soil is enabled in config.php

Note: this also adds a bodyclass demo-page to a page that is called Demo Page -> handy when referring to this page in main.js with demo_page

– Roots rewrites

Roots Rewrites enables clean URL rewrites for your WordPress plugins and theme assets folder.

/wp-content/themes/themename/assets/css/ to /assets/css/
/wp-content/themes/themename/assets/js/  to /assets/js/
/wp-content/themes/themename/assets/img/ to /assets/img/
/wp-content/plugins/                     to /plugins/

Note: refresh permalinks settings after activating this plugin

Example of how links change with these two plugins:

  • No Soil, no rewrites:
    http://localhost/rootsfiddle/content/themes/roots/assets/js/scripts.js
  • Soil:
    http://localhost/rootsfiddle/content/themes/roots/assets/js/scripts.js
  • Rewrites + Soil
    http://localhost/rootsfiddle/content/themes/roots/assets/js/scripts.js

HTML5 Boilerplate’s .htaccess for WordPress
WordPress plugin that adds HTML5 Boilerplate’s .htaccess

See also: https://github.com/h5bp/server-configs-apache

Note:  if htaccess does not change after activating the plugin, try deactivating the soil and or rewrite plugins temporarily.

Note 2: Anytime you update this file the .htaccess file in the root of your WordPress install is automatically updated with the changes whenever the permalinks are flushed or set  see lib/htaccess.php)

you need to ensure the you have the following Apache modules enabled:

Personal note: All the modules are installed on my webhost (if the mod_filter is not installed check if Apache < 2.3.7, since filter was in the Core module then -> in that case: remove <IfModule mod_filter.c> from .htaccess. WAMP local has a higher version of Apache ).

In WAMP I had to enable: autoindex, filter, headers.

7. Set up repos and hooks on your staging and production server

note: This might be a good moment to select the highest PHP version available on your shared server (webserver settings)

note 2: If Git is  not installed on a shared host, try to install it using this method

note 3: On a cPanel host, add this to your .bashrc file to prevent the cPanel ‘SoftException writable by group’ error:

umask 022

Steps:

  • See here (under ‘Server stuff’). don’t forget to create the .htacces file that blocks access to the .git repo
  • add the staging server to your remotes:

Create a bare repository outside the web directory, this is Hub.cd

cd
mkdir repos; cd repos 
mkdir site_hub.git; cd site_hub.git 
git --bare init

Go to the working directory of your website (the Prime):

cd ~/www
git init 
touch test.txt # extra step for empty dir: create a test.txt file (so there is something to commit)
git add . 
git commit -m"initial import of pre-existing web files"

from inside Prime’s  working directory, add Hub as a remote and push Prime’s master branch:

cd ~/www 
git remote add hub ~/site_hub.git 
git remote show hub
git push hub master

create post-commit in the Prime repository (in .git/hooks/):

#!/bin/sh 
 
echo 
echo "**** pushing changes to Hub [Prime's post-commit hook]" 
echo 
 
git push hub

create post-update in the Hub repository (in hooks/):

#!/bin/sh 
echo 
echo "**** Pulling changes into Prime [Hub's post-update hook]" 
echo 
 
cd $HOME/www || exit 
unset GIT_DIR 
git pull hub master 
 
exec git-update-server-info

Give the right permissions to both the hooks:

chmod +x post-commit

LOCAL:

git remote add test remoteuser@remoteserver:repos/myrepo_hub.git
git fetch test
git merge test/master
rm testfile.txt

Ignore .htaccess in gitignore

git commit -am 'message'
git push test master

On Server:

  • For our setup with a staging and production sevim .htarver there also need to be created a file named . staging in the root directory of the staging server:
    touch .staging
  • Deny acces to git repositories. important!
    Add the following to your top-level .htaccess file to forbid web access:

    # deny access to the top-level git repository: 
    RewriteEngine On 
    RewriteRule \.git - [F,L]
  • (Also copy the other roots related rewrite rules to your remote htaccess)
  • Update wordpress on the remote server
    /*You may first need to initialize the submodule*/
    cd www
    git submodule init
    git submodule update

Note: The new version of wp-skeleton aparently uses an https link for the submodule. For this to work you need to install curl-devel on the server. The easier option is to use the git url instead (.gitmodules)
(You may also need to update the .git/config if you already tried to initialize the submodule.)

[submodule "wp"]
path = wp
url = git://github.com/WordPress/WordPress.git
  • cd wordpress
    git fetch --tags
    git tag #list all tags
    git checkout 3.6.1

    Replace 3.6.1 with the correct version number. Now commit the changes to your subrepository version to your main project:

    cd ..
    git commit -m "Update Wordpress to version 3.6.1"
  • Repeat the above steps for your production server

7b. Install WordPress and theme on the server

  • Create a database, install WordPress
  • Change settings as before (tagline, Site address, timezone)
  • Change permalinks. You will probably need to temporarily change the persmissions for htaccess: chmod 664 .htaccess it. Change it back to 644 afterwards
  • Activate the theme on the server.
  • Activate the roots addons:
    – chmod 666 .htaccess
    – Activate H5BP (For me, nothing was written to .htaccess. Not sure why. But after the next step it was)
    – Activate Roots Rewrites
    – Activate Soil
  • Refresh the permalinks setting
    Actually the plugins behave rather weird. Try deactivating and activating until you have something like this:

    # BEGIN WordPress
     
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.php$ - [L]
    RewriteRule ^assets/(.*) /content/themes/roots/assets/$1 [QSA,L]
    RewriteRule ^plugins/(.*) /content/plugins/$1 [QSA,L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
     
     
    # END WordPress
     
    # BEGIN HTML5 Boilerplate 
    .....
  • Don’t forget to chmod 644 .htaccess after you are done (or 664 if WP keeps nagging about unwritable htaccess

8. Essential Plugins

see My WordPress Plugins

9. Updating WordPress

Change into the WordPress subrepository:

cd wordpress
git fetch --tags
git tag #list all tags
git checkout 3.6.1

Replace 3.6.1 with the correct version number. Now commit the changes to your subrepository version to your main project:

cd ..
git commit -m "Update Wordpress to version 3.6.1"

Important note: pushing to your server will not update the submodules, so you will have to copy the new wordpress directory to your servers manually. Or my current method:
– push to server
– update the wp submodule on the server just the same way as you did on the local machine

Note: after a wordpress update you may need to re-save the permalink settings

10. WordPress Security

source: http://www.woothemes.com/2013/09/improve-your-wordpress-security-with-these-10-tips/

Update regularly!

Admin name:
– Create a user name different from admin. Also use a nickname different from user name.
– Optional: Create a fake admin account with subscriber role and very strong pwd
– Also hide the username of all users that can publish by changing their user_nicename in the database. See this post

Limit login attempts

– with this plugin: http://wordpress.org/plugins/limit-login-attempts/
 Note: not needed if you already use better_wp_security 

Disable file editing via the dashboard
– if a hacker managed to gain access to your admin panel, they could also edit your files that way, and execute whatever code they wanted to. So it’s a good idea to disable this method of file editing, by adding the following to your wp-config.php file:

define( ‘DISALLOW_FILE_EDIT’, true );

Prevent base64 hacks

Free themes can often contain things like base64 encoding, which may be used to sneakily insert spam links into your site, or other malicious code that can cause all sorts of problems, as shown in this experiment, where 8 out of 10 sites reviewed offered free themes containing base64 code.

– Note: this plugin may help?
http://wordpress.org/plugins/bulletproof-security/

Keep a backup

Use security Plugins
Here are a handful of popular options:

More:
To learn more about hardening your website’s security, please check out these two resources:
http://codex.wordpress.org/Hardening_WordPress
http://wp.tutsplus.com/tutorials/11-quick-tips-securing-your-wordpress-site

11. Roots Theme features and important files

There are some important files if you want to play around with the starter theme. Check here on the roots 101 website for more information.  I left everything to the default settings for now

  • lib/config.php is used to enable/disable theme features and set configuration values.
  • functions.php is used to include files from the lib/ directory which contains all of the theme functionality. Don’t place any custom code in this file — use it only for includes. You can place custom code in lib/custom.php.  Since Roots is a starter theme, it’s okay for you to modify files within lib/ to meet the needs of the site you’re building.
  • lib/scripts.php is used to tell WordPress which CSS and JS files to enqueue. Any additional CSS or JS files should be added in here.
  • Root Relative URLs & Rewrites (These rules are added to your .htaccess file automatically).

    – With root relative URLs (lib/relative-urls.php) your URLs will be output as /assets/css/main.min.css instead of http://example.com/assets/css/main.min.css. Roots applies root relative URLs by hooking into WordPress filters on various functions, including navigation menu items and CSS/JavaScript enqueued assets. – The clean URL rewrites (lib/rewrites.php) for static theme and plugin assets make your paths cleaner: /wp-content/themes/themename/assets/css/ to /assets/css/

  • lib/sidebar.php contains the Roots_Sidebar class which determines whether or not to display the sidebar based on an array of conditional tags or page templates.
  • lib/widgets.php is used to register sidebars and widgets.
  • Roots includes an example vCard widget which can be removed or modified to create your own widget.
  • lib/nav.php contains functionality that cleans up the navigation menu markup.
  • lib/cleanup.php is mainly used to clean up the output of WordPress core markup.
  • Theme Templates. The markup in Roots is based off HTML5 Boilerplate with ARIA roles for accessibility and the hNews microformat for posts.- base.php — The theme wrapper which wraps the base markup around all template files the templates/ directory is where you’ll be making most of your customizations
  • Extending Templates:Even with the theme wrapper, the normal WordPress Template Hierarchy is still in tact. The Theme Wrapper Guide goes into depth about creating new base.php files
  • Theme Assets (css, images, js): – The assets directory contains all of your project’s LESS, CSS, images, and JavaScript. LESS and JavaScript are concatenated and minified with the Grunt build script- less/app.less contains all of your site styling and is compiled with less/bootstrap/bootstrap.less into css/main.min.css. – JavaScript is compiled into scripts.min.js
  • The CSS and JavaScript for your site are enqueued from lib/scripts.php. Every time you make changes to your LESS and JS, the files will be automatically cache busted as part of the Grunt build script.

12. Optional: Remove or replace Bootstrap

  • Note: I’m interested in using sass with roots, but for simplicity I’ll stay with LESS for now.
  • Remove Bootstrap (See Ben Word’s blog article)Want to get the benefits of using Roots (theme wrapper, clean code, URL rewrites) without having to use Bootstrap? Here’s how to get up and running in just a few minutes:
    – Download Roots
    – Edit lib/scripts.php and remove the CSS enqueues for Bootstrap CSS
    – Edit assets/js/plugins.js and remove the minified Bootstrap JS
    – Remove add_theme_support('bootstrap-top-navbar') from lib/config.php and the Bootstrap navbar specific modifications in the nav walker at lib/nav.php.Roots is delete key friendly, just like HTML5 Boilerplate.
  • Alternative: use a Bootstrap SASS versionI haven’t done this yet, but the instructions in this post
    will probably be helpful. You also may have to install  bootstrap for sass and install compass for grunt

    gem install bootstrap-sass
    npm install grunt-contrib-compass
  • Integrate Pure CSS with Roots
    See this post 

13. Block unecessary Plugins during development

There are certain plugins that you don’t need to have active during development. Think about: Spam Destroyer, Sucuri Security, W3 Cash, Database backup plugins

For this we can use Jaquith’s plugin that disables plugins when you are on a local environment. Note that the working of this plugin depends on the constant WP_LOCAL_DEV, which is defined in the wp-config.php of the wp-skeleton

To add this Gist (a static file), download it and put it in the mu-plugins folder

Add and submit

To test its working, log in to wordpress and install and activate a plugin to remove spam. Akismet and Spam destroyer for example.  Add and submit

Now add the main files of these plugins to the end of Jaquith’s plugin:

if ( defined( 'WP_LOCAL_DEV' ) &amp;&amp; WP_LOCAL_DEV ) {
    new CWS_Disable_Plugins_When_Local_Dev( array( 'vaultpress.php',        'spam-destroyer/index.php', 'akismet/akismet.php') );

Check if the plugins are indeed deactivated (no need to log out)

Because we switched to a different branch, we have to log out from WordPress first and then log in again to see the theme. Do this. Important: – If you switch back to the master branch, then you have to log out and in again – If your new theme is activated, switching to the master branch and then logging in into your wordpress will produce several errors. Therefore it is best to keep working on the testing branch until it is finished and then merge it back into the master branch.  After that you can go ahead and activate the theme. Now assume we have done a lot of work on the theme: – Log out of wordpress – Checkout the master branch – Merge the testing branch