Website Version Control

This entire site (the non-media parts, anyway) is now version controlled. I think this is how I did it.

Recreating This Post

I'm probably going to miss some steps along the way here, because I am recreating this post after losing it due to a gnome-terminal memory leak that brought my laptop to a grinding halt and I had to forcefully power down.

That meant losing everything I was working on. When I'm creating a Web page I usually have a previously created page in another tab in my text editor (I'm using Geany on Debian) and I just copy and paste the header and footer into the page I'm working on, change the variables (name, description, creation date, etc.) and then save the file per the directory and name I have just entered in those variables.

The problem with that, however, is that it means when I start working on something, it isn't typically saved until I am ready to start checking that it is formatted properly as a Web page. I have therefore installed geany-plugins (the whole lot) and enabled the Save Actions plugin.

I now have auto-save enabled (including with the "Enable when losing focus" option checked), as well as Instant Save (default filetype: none) and Backup Copy (save location: /var/tmp/thejc-geany/, since /var/tmp should never be a RAM disk if best practises are followed).

All 3 options combined is great. A file is created in /var/tmp/thejc-geany/ as soon as I open a new tab (random filename) and it is automatically saved every 300 seconds or when the edit window loses focus—switch to another tab or the Scribble pad and it is automatically saved. If I switch to a Web browser to copy something, Backup Copy means that even if what is being auto-saved is wrong, every save is stuck in a new file in my Backup Copy (/var/tmp/thejc-geany/) folder.

Since undo works even after saving in Geany, as long as I haven't overwritten something and then closed Geany everything should be OK. Since I now use version control for my Web site (which will be the focus of this article) the liklihood of me overwriting something I've written and it not being reverseable is about as likely as me having to forcefully power cycle my laptop—unlikely, but probable.

On the plus side, all of the commands I typed are stored somewhere.

scp thejc@home.thejc.me.uk:/home/thejc/.bash_history bash-history-after-laptop-crash

All my Web browser tabs (and history) should be safe, I now just need to recombine everything I wrote and did.

Clean tree

The way I installed Foundation 5 meant it came with a git repository. I must have cloned it from GitHub.

That means there is already a commit history even though I have never made a commit in this folder. Because git log produces output rather than an error message, I will be skipping the initalisation step.

What I do need, however, is to sort through all of my files before adding them to version control. I want to skip binary files where possible.

git add --dry-run ./ > ~/gitblahblah
sed "s,^add ',/," ~/gitblahblah | sed "s,'$,,;s,.php$,," > ~/gitblahblah2
cat ~/gitblahblah2 ~/Scripts/sitemap_links_web_watfordjc_uk.tmp2 | sort | uniq -u | egrep -v .JPG$ | egrep -v .jpg$ | egrep -v .png$
  1. The first line here adds all files that don't match in .gitignore in dry-run mode (it just prints the output) which I then redirect to a file called gitblahblah.
  2. On line 2 I then parse the gitblahblah file so I am just left with all the filenames sans .php extensions. That output is redirected to file gitblahblah2.
  3. On line 3 I then merge the contents of gitblahblah2 with the parsed list of links in the sitemap on WatfordJC.UK (includes the potentially NSFW pages not listed on JohnCook.UK), sort the lines, remove lines that appear more than once (leaving the unique lines), and then remove any lines which are for images.

This list of files that are output are those .php files which would be added to git but which do not appear in the sitemap. Odds are these are either orphaned files, files that are linked from elswhere in the site and should be in the sitemap, or files that were either temporary or are drafts.

After moving files around, creating a /drafts folder, and updating the .gitignore file, I ran git add --dry-run ./ to double-check all the files I want excluded are covered by .gitignore, and eventually git add ./ adding all the files not ignored to the staging area.

Configuring Git

I stupidly committed before configuring git. That resulted in the wrong e-mail address being listed for the commit.

Although this section came after my First commit, I have moved it higher in this page so it is where it should be if done correctly (as well as what to do if you forget).

In order to configure git I am going to do the following:

  1. See what the current settings are.
  2. Set my user.name globally (for all repositories on my system where a local user.name hasn't been set).
  3. Set my user.email locally (so it isn't the default everywhere).
  4. Set my text editor for things like commit messages to nano.
git config --list
git config --global user.name "John Cook"
git config --local user.email "webmaster@johncook.co..uk"
git config --list
git config --global core.editor nano

Next, I need to change my e-mail address in all existing commit messages. To do this I am going to search by e-mail address and then update the commiter and author name and e-mail address.

While I could search by name, my name isn't unique enough for it to be suitable to reuse this code again later on a private copy of a large multi-user repository.

git filter-branch --commit-filter '
        if [ "$GIT_COMMITTER_EMAIL" = "thejc@home.thejc.me.uk" ];
        then
                GIT_COMMITTER_NAME="John Cook";
                GIT_AUTHOR_NAME="John Cook";
                GIT_COMMITTER_EMAIL="webmaster@johncook.co..uk";
                GIT_AUTHOR_EMAIL="webmaster@johncook.co..uk";
                git commit-tree "$@";
        else
                git commit-tree "$@";
        fi' HEAD

Creating branches and Initial Commit

Branch master already exists from when Foundation 5 initialised it. I'm going to use a further two branches: develop for my development work, and release for the live site.

git branch release
git branch develop
git checkout release

As I am doing all this within the live Web site's folder structure, I want to be on the release branch.

git commit -m "First commit"

I now want to check for orphaned .php files again. These are files that are now in the git repository but which do not appear in the sitemap.

git ls-tree -r master --name-only | grep -v "^js/" | grep -v "^inc/" | grep -v "^gpg_keys/" | grep -v "^scss/" | grep -v "^downloads/" | grep -v "^api/" | grep ".php$" | sed 's,^,/,;s,.php$,,;s,/index,/,' > ~/gitblahblah2
cat ~/gitblahblah2 ~/Scripts/sitemap_links_web_watfordjc_uk.tmp2 | sort | uniq -u

I am not going to walk through that one liner, but with the second line it produced the following output:

/404
/410
/about/accessibility
/links/
/links/sitemap-rss.xml
/links/sitemap.xml
/robots.txt

The only files that stick out there are /about/accessibility and /links/.

The accessibility page is an orphaned page, with zero links to it from anywhere on the site. While it might be a draft page, I am going to come back to it another time and see if it needs updating before being unorphaned (adopted?).

As for /links/, it sticks out because other than / there are no index pages on the site. Upon inspection /links/index.php is a 307 redirect to /links.

I think this is a remnant of the past when I was going to actually have the category pages act like directory listings, so /articles/ rather than /articles, but I changed my mind.

In fact the 404 page acts like a soft redirect for /articles/ and all the other directory pages:

Your browser requested a directory, attempting to find a file with a similar name... Found.

/articles

Creating a Development Site

At present I only have the live site, which I have created a git repository from.

In order to improve my workflow, it would perhaps be best if I have a development site I can test things on before pushing them to the production site—exactly like what I tell banks to do after they break their Web sites.

cd ..
sudo mkdir dev_johncook_co_uk
sudo chown thejc:thejc dev_johncook_co_uk/
git clone -b develop thejc@home.thejc.me.uk:/home/www/var/www/johncook_co_uk dev_johncook_co_uk

I have now cloned the live repository's develop branch into dev_johncook_co_uk. After a bit of NGINX configuration and NSD DNS updating it is now my live dev site.

I mustn't forget to configure Git this time…

git config --list
git config --local user.email "webmaster@johncook.co..uk"
git config --list
git fetch
git pull

With everything up to date, it is now time to install all the NodeJS modules and Bower components that my site needs for the CSS and JavaScript compilation and minification.

npm install grunt --save-dev
npm install --save-dev
grunt
npm install --save-dev grunt-contrib-copy grunt-contrib-cssmin grunt-contrib-uglify

With that done, I can commit and push the changes so if I make another clone I can just tell bower and npm to install the (dev‑)dependencies.

git status
git add package.json 
git status
git commit -m "package.json dependencies update"
git log
git status
git push

Shell Generated Pages

One potential problem is with pages generated by dash/bash being updated during checkouts and merges. These files, such as my sitemaps, should not be under version control.

I'm going to remove them from the repository, and then add the files to .gitignore.

cd ../johncook_co_uk/
git log
git status
git diff links/sitemap.xml.php 
git rm --cached --dry-run links/sitemap.xml.php 
git rm --cached links/sitemap.xml.php 
git status
git add .gitignore 
git status
git commit -m "Removed script-generated links/sitemap.xml.php"

I have made some modifications to the commands entered, to strip out the extraneous commands (except the git status commands).

Reporting that I had tried to rebase release whilst on the release branch in the command list is sort of pointless.

Propagating Modifications

With the release branch updated, and now one commit ahead of the develop branch, it was time to work out how best to propagate the changes.

I want my site to always be on the release branch. You can't merge changes into a branch unless you first checkout that branch. That has the potential to create 404 errors.

As I was still working out Git I thought I shouldn't have the release and master branches hanging around in my dev site as it might forget what branch I'm on. I have since modified my .bashrc to show what branch I'm currently on.

Anyway…

cd ../dev_johncook_co_uk/
git checkout -b release-ff --track origin/release
git checkout develop
git merge --ff-only release-ff
git status
git push
git branch -d release-ff
cd ../johncook_co_uk/
git merge --ff-only develop
git log
cd ../dev_johncook_co_uk/

This is how I used to make changes, things are different now.

At this point were commits 794d261c01769a3a3966b0801ce335d092a066a5 (Removed copies of source JavaScript files.) and 18cb078ca66092133edf435a497453181d094257 (Removed copies of source JavaScript files.), which pretty much followed the same process.

Make Code Compatible With Dev Site

My site's code at this point was not compatible with the new dev domain.

Basically a PHP switch statement looks at the domain, and if no match is found it falls through to a default which isn't suitable for the dev site.

cd /home/www/var/www/dev_johncook_co_uk/
git branch dev-sites
git checkout dev-sites

After creating a new branch and switching to it, I then modified the PHP files so that the dev domain was an option in the aforementioned switch statement and to add it to the list of domains that my alerts "API" permit cross-origin requests from.

git status
git add inc/htmlheader.php
git status
git commit -m "Add dev domains to switch statements and CORS"
git log -1
git checkout develop
git merge --ff-only dev-sites
git log -1
git push
cd ../johncook_co_uk/
git merge --ff-only develop
git log -1
touch api/alerts.php
cd ../dev_johncook_co_uk/
git branch -d dev-sites

Another change I needed to make was to stop using absolute URLs pointing to Web.JohnCook.UK for the CSS and JavaScript.

As the CSS and JS, including the minified and compressed versions and the symlinks, are going to be under version control, I don't want to have to make lots of commits just to test some modifications.

To do this I just added an if/else around the lines referencing the CSS and PHP files. If on the dev site, use the relative URL /x/combined.min.x, otherwise use the absolute URL https://web.johncook.uk/x/combined.2015-11-23R001.min.x (with x being js or css).

git add inc/header.php inc/htmlfooter.php
git commit -m "Use relative URLs for style and script on dev sites"
git push
cd ../johncook_co_uk/
git merge --ff-only develop
cd ../dev_johncook_co_uk

I should have created a branch here, then merged the branch into develop. Oh well.

Update the Site

While testing the site, I came across a bit of a display bug. In one of the pages there was horizontal scrolling because for some strange reason a paragraph was being randomly placed next to preformatted test.

I say randomly because rather than appearing where it was in source code, it was appearing next to preformatted text further up the page.

Thanks to the major browsers now having Inspect Element and similar tools, it is now a lot easier to test CSS changes and fixes without having to modify the file, save the file, (upload the changes and purge intermediary and browser caches) and refresh the page.

Anyway, the simple fix was to give <pre> tags a style of clear:both.

git branch css-fix-pre
git checkout css-fix-pre

I created a new branch and made the modifications.

grunt
cd css
gzip -kf combined.min.css

After compiling the CSS, I then tested the modifications.

Since the fix worked, I could now create the necessary symlinks, and add the files to the staging area.

ln -s combined.min.css combined.2015-11-13R001.min.css && ln -s combined.min.css.gz combined.2015-11-13R001.min.css.gz
git status
cd ../scss/
git add app.scss
cd ../css/
git add -f combined.min.css combined.min.css.gz combined.2015-11-13R001.min.css combined.min.css.gz combined.2015-11-13R001.min.css.gz
cd ..
nano inc/header.php

After modifying the .css reference to point at the new symlink file, I could commit the changes.

git status
git diff HEAD inc/header.php
git add inc/header.php
git status
git commit -m "Stop </pre><p> causing horizontal scrolling"
git checkout develop
git merge css-fix-pre
git push
cd ../johncook_co_uk/
git merge develop
cd ../dev_johncook_co_uk
git branch -d css-fix-pre

And that is how CSS modifications will be made.

Because I'm no longer creating a new set of symlinks every time I test a modification, I will have a lot less symlinks littering my css folder.

Push Behaviour

After a while, I started to get annoyed with the following message whenever I pushed changes:

warning: push.default is unset; its implicit value has changed in
Git 2.0 from 'matching' to 'simple'. To squelch this message
and maintain the traditional behavior, use:

  git config --global push.default matching

To squelch this message and adopt the new behavior now, use:

  git config --global push.default simple

When push.default is set to 'matching', git will push local branches
to the remote branches that already exist with the same name.

Since Git 2.0, Git defaults to the more conservative 'simple'
behavior, which only pushes the current branch to the corresponding
remote branch that 'git pull' uses to update the current branch.

See 'git help config' and search for 'push.default' for further information.
(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode
'current' instead of 'simple' if you sometimes use older versions of Git)
git remote -v
origin	thejc@home.thejc.me.uk:/home/www/var/www/johncook_co_uk (fetch)
origin	thejc@home.thejc.me.uk:/home/www/var/www/johncook_co_uk (push)

Basically, matching mode pushes all local branches to the matching branch names on the remote, whereas simple mode pushes only the current branch to the remote if the name matches. simple mode is the default now.

In simple mode, git push only pushes to the remote repository if the local repository has an upstream branch set. For example, if I were to create a test123 repository using the branch command, git branch -vv would show the following:

* develop 0ff3d55 [origin/develop] Remove CSS from <h2><span>s unless heading class
  test123 0ff3d55 Remove CSS from <h2><span>s unless heading class

We're currently on the develop branch (*) and it has an upstream branch of origin/develop. The test123 branch, however, has no upstream branch and therefore produces the following error message on a push attempt:

fatal: The current branch test123 has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin test123

To set an upstream branch on a branch, you can either use git branch --track or git branch --set-upstream. 

I am completely fine with the default push behaviour, so I will squelch the warning message.

cd ../dev_johncook_co_uk/
git config --local push.default simple

Updating the Master Branch

Ever since I created branches and my live site was moved to the release branch, I haven't updated the master branch at all. This is evident when using the --decorate option with git log.

git fetch
git log --graph --decorate
* commit d83929386ee98c28a05b2c836b6ab4f8ab2b362f (HEAD, origin/release, origin/develop, origin/HEAD, develop)
| Author: John Cook <webmaster@johncook.co..uk>
| Date:   Fri Nov 13 12:30:46 2015 +0000
| 
|     Set canonical URLs on dev pages to non-dev URLs
|
...
* commit 6357c81f43af0691b1194eb9afc4041f469f342c (origin/master)
| Author: John Cook <webmaster@johncook.co..uk>
| Date:   Thu Nov 12 08:51:38 2015 +0000
| 
|     First commit
|
...

While HEAD (currently the develop branch) is at the same commit as origin/develop and origin/HEAD (currently origin/release), origin/master is at the commit all the way back at "First commit".

There are two ways I could update origin/master, from within that copy of the repository or from within the dev_johncook_co_uk (or another) copy of the repository which has the live site repository as an upstream.

The simplest way to do things would be to do it all within the dev repository, since that would avoid having to switch the live site to a different branch.

If the live site were to move back in time that would mean my last modification time script and Redis database would have incorrect data; new features, bugfixes, and pages would disappear; and 304 responses would not be logical (If-Modified-Since 1 hour ago? Yes, 200 OK, Last-Modified 24 hours ago).

The quickest way to do things would be to checkout (and switch to) a "detached HEAD"—another way of saying the current branch is a temporary branch that will be thrown away when you checkout another branch (HEAD points directly at a commit instead of at a commit via a branch).

I have used the fetch command already, so I have the latest state of origin/HEAD, origin/release, origin/develop, and origin/master available. I can now checkout origin/master, merge into it origin/release, and then push the changes to origin/master.

git checkout origin/master
git merge origin/release
git push origin HEAD:master
git checkout develop

As can be seen from log --decorate, all the branches on the live site are now at the same commit.

git log --graph --decorate
* commit d83929386ee98c28a05b2c836b6ab4f8ab2b362f (HEAD, origin/release, origin/master, origin/develop, origin/HEAD, develop)
| Author: John Cook <webmaster@johncook.co..uk>
| Date:   Fri Nov 13 12:30:46 2015 +0000
| 
|     Set canonical URLs on dev pages to non-dev URLs
|

This method would probably be the way I'd go when I need to merge the develop branch with origin/release. I'd likely just use these commands:

git checkout develop
git fetch
git push
git push origin HEAD:release

What I'm doing is pushing to origin the HEAD commit, and I'm pushing it to the develop branch (no need to specify as it is the upstream branch) plus the release branch.

Naming is one of those things that I could probably improve. How about if I rename origin to web (the sub-domain the live site lives at?)

git remote rename origin web
git remote -v
web	thejc@home.thejc.me.uk:/home/www/var/www/johncook_co_uk (fetch)
web	thejc@home.thejc.me.uk:/home/www/var/www/johncook_co_uk (push)
git branch -a
* develop
  remotes/web/HEAD -> web/release
  remotes/web/develop
  remotes/web/master
  remotes/web/release

I was wrong, you can't push if that branch is currently checked out by the remote repository.

git fetch
git checkout -b release web/release
git merge develop
git push
Total 0 (delta 0), reused 0 (delta 0)
remote: error: refusing to update checked out branch: refs/heads/release
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require 'git reset --hard' to match
remote: error: the work tree to HEAD.
remote: error: 
remote: error: You can set 'receive.denyCurrentBranch' configuration variable to
remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error: 
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To thejc@home.thejc.me.uk:/home/www/var/www/johncook_co_uk
 ! [remote rejected] release -> release (branch is currently checked out)
error: failed to push some refs to 'thejc@home.thejc.me.uk:/home/www/var/www/johncook_co_uk'
git checkout develop
git branch -D release

Since web will always have release checked out, there is no point in me having a release branch in repository dev_johncook_co_uk.

What I can do, however, is switch to repository johncook_co_uk and either merge with develop or master.

Merging with develop is a bit risky, since there is always the chance I have pushed changes from develop that aren't ready yet. It will, instead, be better to merge with master.

That way, when something in develop is ready for release, I can merge develop into master, push, and then merge master into release.

git checkout -b master web/master
git fetch
git merge develop
git push
cd ../johncook_co_uk
git merge master
cd ../dev_johncook_co_uk

That can, of course, be simplified in future to:

git checkout master
git fetch
git merge develop
git push
git -C ../johncook_co_uk merge master

Changing Every Page on the Site

I have started some work on the backend of the site, to make the PHP files easier to read and to standardise the headers and footers.

Because the header is slightly different depending on the type of page, the easiest way to do things will be to create a different branch for each section of the site, and work on each page type progressively.

I am starting off with the category pages. These are the pages a level below /articles, /blogs, and /music. I have finished the article category pages:

git fetch
git log --all --decorate
commit 4ea5bdf3117721733c9ae59feeec1d70e86b48cc (dev-new-headfoot-articles)
Author: John Cook <webmaster@johncook.co..uk>
Date:   Sat Nov 14 14:08:23 2015 +0000

    Switch article categories to new header/footer

commit 7cebe54fde52e2975aa5f2fe6451090e7598913e (HEAD, web/release, web/master, web/develop, web/HEAD, master, develop, dev-nsfw-exit, dev-new-headfoot-status, dev-new-headfoot-music, dev-new-headfoot-links, dev-new-headfoot-home, dev-new-headfoot-gallery, dev-new-headfoot-downloads, dev-new-headfoot-blogs, dev-new-headfoot-archives, dev-new-headfoot-about, dev-new-headfoot)
Author: John Cook <webmaster@johncook.co..uk>
Date:   Sat Nov 14 13:30:28 2015 +0000

    Add function was_requested

I'll next switch to the dev-new-headfoot-blogs branch and work on those category pages. After that I'll start working on the section pages (/articles, /blogs, /gallery, etc.). I'll then work on the final level pages (/archives/johncook-2014, /articles/vps/vps-hosting-2015, /links/complete-sitemap, etc.), and finally the home (/) page.

This way I'll have a lot of branches that have all diverged from develop, which I'll then merge into dev-new-headfoot one by one like so:

git rebase dev-new-headfoot dev-new-headfoot-articles
git checkout dev-new-headfoot
git merge --ff-only dev-new-headfoot-articles

Actually, I decided in the end I didn't want fast-foward merges in this case, so I did the following instead:

git checkout dev-new-headfoot
git merge --no-ff dev-new-headfoot-article-pages

dev-new-headfoot-article-pages was a branch off dev-new-headfoot-article-page which was a branch off dev-new-headfoot-article-cats.

I renamed the branch dev-new-headfoot-articles to dev-new-headfoot-article-cats (as well as the branches for the other category pages) because it just made keeping track of things easier.

Version Control Lowers Risks

Without git I probably wouldn't even be attempting this because changing every single page on my site has the potential of breaking something. In fact the reason I ran git init in the first place was because I had used sed to add some Microdata site-wide (to all instances of <pre><code) without any way of reverting what I did if it went wrong other than by using sed again.

The entire site (the non-media parts, anyway) is now version controlled and such a worry is no more. As long as I don't mess things up, that is.

Consistent Line Endings

I forgot to add newlines at the end of my new files I have now added them and I want to merge my staged changes with my previous commit on the dev-new-headfoot-articles branch. How do I do that?

git checkout dev-new-headfoot-articles
git add -u articles/
git commit --amend

I noticed ^M in one of the diffs and it is because that file uses Windows (CRLF) line endings. I would much rather have all my files with the same line endings, although Geany doesn't really care - open a file with Windows line endings, it saves it with Windows line endings.

Converting all files with Windows line endings to Unix (LF) line endings will mean a lot of lines will be changed, so I need to carefully think about how I'm going to do this.

https://help.github.com/articles/dealing-with-line-endings/
git config --global core.autocrlf input
git checkout master
git branch temp-save
git checkout temp-save
git add -u .
git commit  -m "Saving files before refreshing line endings"
git status
git branch line-endings
git checkout line-endings
git rm --cached -r .
git reset --hard

OK... according to grep -R "<ctrl+v,ctrl+m>" that did absolutely nothing.

cd ../johncook_co_uk
git rm --cached -r .
git diff --cached --name-only -z | xargs -0 git add -f
git diff --name-only HEAD

OK, that list of files looks promising, and there aren't any binary files in the list.

git commit -m "Convert CRLF to LF in all files"
git checkout line-endings
git rm --cached -r .
git diff --cached --name-only -z | xargs -0 git add
git log --graph --all --decorate

Release is ahead of master and develop.

grep "<ctrl+v,ctrl+m>" articles.php

As was warned, the files weren't updated in my working copy.

git checkout master
git merge release
grep "<ctrl+v,ctrl+m>" articles.php

The files in my working copy have been updated.

git checkout release
grep "<ctrl+v,ctrl+m>" articles.php

The files in my working copy no longer have Windows line endings.

Now to update my other branches.

cd ../dev_johncook_co_uk
git checkout develop
git fetch
git log --graph --all --decorate
git merge --ff-only web/release
git push
git checkout master
git pull
git log --graph --all --decorate
git checkout dev-new-headfoot
git merge --ff-only develop
git rebase dev-new-headfoot dev-new-headfoot-articles
git rebase dev-new-headfoot dev-new-headfoot-blogs
git rebase dev-new-headfoot dev-new-headfoot-music
git rebase dev-new-headfoot dev-new-headfoot-status
git rebase dev-new-headfoot dev-new-headfoot-links
git rebase dev-new-headfoot dev-new-headfoot-home
git rebase dev-new-headfoot dev-new-headfoot-gallery
git rebase dev-new-headfoot dev-new-headfoot-downloads
git rebase dev-new-headfoot dev-new-headfoot-archives
git rebase dev-new-headfoot dev-new-headfoot-about
git log --graph --all --decorate

Now to restore articles.php's changes.

git checkout temp-save
git diff HEAD^
git reset --soft HEAD~
git stash
git rebase develop
git stash pop
nano articles.php

Open articles.php, and resolve the merge conflict - copy the changed lines from the bottom to the top, remove all the remaining old lines, and remove the merge conflict lines.

Save the file.

git diff HEAD
diff --git a/articles.php b/articles.php
index f33eaa2..7d1e8bb 100644
--- a/articles.php
+++ b/articles.php
@@ -27,8 +27,7 @@ EOF;
 <?php panelheader($page_name,$article_description,TRUE);?>
 <?php echo $breadcrumbs_content;?>
 <?php panelfooter();?>
-</div>
-</div>
+<?php end_lead_section();?>
 <?php include $_SERVER['DOCUMENT_ROOT']."/articles/computing.php";?>
 <?php include $_SERVER['DOCUMENT_ROOT']."/articles/personal.php";?>
 <?php include $_SERVER['DOCUMENT_ROOT']."/articles/save-money.php";?>

That looks fine.

git checkout dev-new-headfoot-articles
git diff HEAD
git branch -D temp-save
git stash drop stash@{0}
git branch -d line-endings
git branch -d dev-nsfw-exit
git log --graph --all --decorate

All line endings are now LF rather than CRLF (at least as far as tracked files are concerned), and all my branches are either at the "Convert CRLF to LF in all files" commit (adcb6dd...) or are ahead of that commit.

Also, that is the only new commit that has been made. My untracked files already have Unix line endings as Geany is configured to save new files with LF line endings, and my unstaged changes are the same as what I was working on before this.

Time to get back to working on articles.php.

A New Offshoot

What I ended up having at the time I merged dev-new-headfoot into develop was what can only be described as a side-tree.

Off of develop was dev-new-functions, which added new functions needed for the changes made in branches coming off of it but which would, if merged with develop, mess up the site.

Then off of dev-new-headfoot was a branch for each level 2 page of the site and each level 1 page where no level 2 page exists. Then off of each level 2 branch was a level 1 branch (because /articles includes content from the /articles/ categories). Then off of those level 1 branches were level 3 branches for the articles/posts.

This is best described with an image rather than text:

A visual representation of dev-new-headfoot branching from develop.

What you can see here is the branching off of the main branch, with the first 4 commits being from dev-new-functions (which dev-new-headfoot was branched from) and from dev-new-headfoot all the branches as described above.

For example, the level 1 about page isn't a category, so it doesn't have any other commits on that branch.

None of the branches with level 3 pages are seen in this image because they appear closer to the point where dev-new-headfoot was merged back into develop.

You can see from the next image where all those branches got merged back into dev-new-headfoot (using no fast forwarding) and where dev-new-headfoot was eventually merged back into develop.

A visual representation of dev-new-headfoot being merged back into develop.

You'll also see from the commit before dev-new-headfoot branched off of develop (previous image) and the commit where it was merged back into develop (this image) there are yellow markers.

They are git tags, and I am simply using them in this repository to mark points at which the site is considered by me to be stable (i.e. where multiple previous commits have resulted in something that I consider a point where a snapshot could be taken).

These images are from gitg, and with a lot of merged branches it looks a bit of a mess. Topological order condenses the branches so you see the commits as if they were made one after the other, whereas anti-chronological order (the default) shows them in date order.

With these images showing the branches, commits, and merge commits in topological order, it is a lot easier to see what is going on. I have omitted the middle section as it much of the same.

I have also disabled the option in gitg that collapses inactive lines (this is how I usually look at gitg). You can see from the tag before the branch started until the tag where the branch was merged back that there is an orange line on the left.

That line is the develop (and master and release branches) moving along without a single commit until dev-new-headfoot is merged back in. This is the reason why I didn't do rebasing and made every merge a non-fast-foward-merge—it makes it easier to see exactly what happened, with some commits having two parent commits.

The second line from the left was the dev-new-headfoot-site branch. You can see after the first 4 commits dev-new-headfoot (the green line) becomes the third line, with the commit where dev-new-headfoot-music-pages being the point where dev-new-headfoot-site starts being merged into.

The dev-new-headfoot-site branch was a branch I created to merge all the branches into once I had finished a section of the site. The last section to merge in was the home page (because that contains content from a lot of pages on the site), and with a few final checks it was merged back into the develop branch.

During the entire process my dev site was the checked out branch. That meant that when I loaded /music, for example, changes I'd made in other branches weren't there if I loaded those pages. It allowed me to focus on only working on one part of the site at a time.

Current Version Control Usage

Although I mostly use the command line when using git, when I need to find a commit or I want to have a quick look at where all my branches are I use gitg as it is a lot easier to visually see everything.

There are currently two commits ahead of HEAD—one that adds a Christmas CSS theme to the site and one that removes it. Whenever I update develop I have to rebase those two branches (christmas-css‑patch with develop, regular-css‑patch with christmas-css-patch) so that they can easily be fast-forward merged into develop later.

Sometimes, however, there is a merge conflict with develop. When that happens I either abort the process, or I resolve the conflict.

If it is a big conflict, such as after I integrated Foundation 6, I just create a patch git diff file between two commits (e.g. on christmas-css‑patch between HEAD~1 and HEAD) and save it to a file.

I then recreated those changes in a new branch off of develop, and once done and commited I'd then create another diff patch between HEAD and HEAD~1. I'd then create a branch to revert the changes made, apply the patch, and make a commit.

As long as the christmas-css-patch branch is ahead of develop, I can quickly replace the theme on this site with the Christmas one. Likewise, as long as regular-css-patch is always ahead of that, I should be able to revert back to the regular theme.

I am probably going to be sticking with Git this time around now that I have gotten used to it. In the past I would have put off doing something like upgrading my CMS, or migrating from Foundation 5 to Foundation 6.

With the ability to go back as long as the file is source controlled, and what I want to revert to was committed, since I started writing the previous version of this page I have converted every page to Unix line endings, standardised the top and bottom of every .php page on the site (as far those that are Web pages are concerned), and migrated to Foundation 6.

Now that I also have a development version of the site, I have even started making more CSS tweaks than in the past—since I don't have to finish before being able to work on something else—and have even created a Christmas theme for the site.

Speaking of which, both JohnCook.UK and WatfordJC.UK will be one year old on Christmas Day 2015, as they were registered on . Unless I am mistaken it will be the first time a site of mine has had a seasonal theme.

Although I don't know exactly when this site was first created, I can tell from an old git repository where the site was born:

commit ad36889243220354344bfeab8da9c2fd33d40262
Author: John Cook <webmaster@johncook.co..uk>
Date:   Wed Mar 26 02:34:03 2014 +0000

    Installed Zurb Foundation, changed where app.css is output to

commit cba0d0bc02888790b7ed2a08a644b1eb523361b5
Author: John Cook <thejc@PC1-JC.thejc.local>
Date:   Wed Mar 26 01:37:28 2014 +0000

    Initial Commit

While it would be nice to have all those old commits in the current repository, the folder structure is completely different.

In fact this following commit would look rather amusing in the commit history, given what I ended up basing my site on:

commit 4d5b36ee65a99a37b63fd2a1287ce5487bf0832b
Author: John Cook <webmaster@johncook.co..uk>
Date:   Sun Apr 6 23:18:12 2014 +0100

    Use HTML5 tags instead of divs in index.php
    
    Decided to move away from Zurb Foundation to reduce unnecessary CSS and JavaScript bloat. Reducing t
he use of divs and classes in favour of HTML5 elements should reduce the size of CSS and HTML.
    
     * Replace <div> with <section> and <header>
    
     * Replace <div> with <fieldset> and <legend>
    
     * Remove unneeded classes.

While I presumably abondoned that development for a reason, it probably wasn't because of Git. Just looking at /includes/header.php it is more comment than code to tell me how the whole models/views/controllers thing is supposed to be done.

I'm sure if I YouTube php I will find the video where I decided to try going down that route. Here it is:

Thumbnail of YouTube video: Lecture 3 - MVC, XML - Building Dynamic Websites - Harvard OpenCourseWare (Latest, Summer 2012)

Video: Lecture 3 "MVC, XML" - Building Dynamic Websites - Harvard OpenCourseWare (Latest, Summer 2012)

Play Video Embedded Watch Video on YouTube Google Privacy Policy Google Cookies

It also talks about source control, which is probably where I decided to use git back then. I think I'll go and watch that video now, and perhaps the whole series of them.

The final thing to do before publishing this page is munging all the e-mail addresses.

echo "@johncook.co..uk" | hexdump -C --
00000000  40 6a 6f 68 6e 63 6f 6f  6b 2e 63 6f 2e 75 6b 0a  |@johncook.co..uk.|
00000010

With that string of hexadecimal numbers I can now create the replacement string.

  • @ = 40 = &#x40;
  • @johncook.co.uk = &#x40;&#x6a;&#x6f;&#x68;&#x6e;&#x63;&#x6f;&#x6f;&#x6b;&#x2e;&#x63;&#x6f;&#x2e;&#x75;&#x6b;