Git merge vs. git rebase

Currently I am working on a 64-bit API for a storage server at work. The plan is to port a limited number of APIs in order to be able to call them directly from other applications / services without having to marshal such calls. I am trying to convert the data structures from 32 to 64 bits while keeping backwards compatibility. At some point in time we should be able to port with little additional effort the current storage server to 64-bits and still support client applications / servers running on 32-bits.

Following is a screen dump of a utility we use to count the lines of code (LOC). The results for this port at this time follow:

[30] >>> 30
>>> specify the path [c:\icas64\]:
TreeCountSourceLines <<< count: 55 line: 12955
TreeCountSourceLines <<< path ==>c:\icas64\<== fileCount: 27 functionCount: 113 lineCount: 66987 line: 44157

A week or so ago I was chatting with a colleague regarding the advantages / disadvantages of using git merge versus git rebase. Most developers typically use git merge in order to merge a feature branch back into master. In case you are working on a private project, or with a reduced number of developers, probably you would prefer to use git rebase. The reason for this comes from the Git documentation which I will paraphrase here:

“The Perils of Rebasing

Ahh, but the bliss of rebasing isn’t without its drawbacks, which can be summed up in a single line:

Do not rebase commits that exist outside your repository and people may have based work on them!!!

If you follow that guideline, you’ll be fine.

If you don’t, people will hate you, and you’ll be scorned by friends and family.”

In order to go through an example, let’s use the following diagram:

The idea is to start with a master branch. We have a first commit labeled m1 followed by a second one labeled m2. At that point we create the feature branch. Once we are in the feature branch we make a commit for feature one labeled f1 and later we commit feature two labeled f2. In parallel the team makes a commit on the main branch labeled m3.

What we want to do now is consolidate the changes of the feature branch back into master. This is quite typical on most projects. The question is how to proceed. Do we use git merge of git rebase?

Let’s start by creating the scenario illustrated in the Git Merge vs. Git rebase diagram. Please keep in mind that there are many ways to get to a similar scenario. I will attempt to illustrate each step that we will use.

# **** create master branch with first commit (m1) ****

C:\Users\John>cd c:\temp\gitplay

c:\Temp\GitPlay>code .

c:\Temp\GitPlay>dir
05/20/2019  11:31 AM                 2 index.html   <==== c:\Temp\GitPlay>type index.html
m1                                                  <==== c:\Temp\GitPlay>git init
Initialized empty Git repository in c:/Temp/GitPlay/.git/

c:\Temp\GitPlay>git add index.html

c:\Temp\GitPlay>git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   index.html

c:\Temp\GitPlay>git commit -m "m1"
[master (root-commit) 6ef1917] m1
 1 file changed, 1 insertion(+)
 create mode 100644 index.html

c:\Temp\GitPlay>git status
On branch master
nothing to commit, working tree clean

c:\Temp\GitPlay>git log
commit 6ef1917475da8b93cc7f8257e5f051ab9a2e8047 (HEAD -> master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:35:28 2019 -0500

    m1

We will start by creating a folder and use Visual Studio Code (the IDE does not matter, we could have used any editor) to create a file named index.html (once again, the name does not matter). We will write in the file the name of the first commit (in this case m1). After initializing git, we add and commit our code to the master branch.

# **** modify and commit to master ****

c:\Temp\GitPlay>type index.html
m1 m2                                   <==== c:\Temp\GitPlay>git add index.html

c:\Temp\GitPlay>git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   index.html


c:\Temp\GitPlay>git commit -m "m2"
[master 0962747] m2
 1 file changed, 1 insertion(+), 1 deletion(-)

c:\Temp\GitPlay>git status
On branch master
nothing to commit, working tree clean

We add “m2” to the contents of the index.html file and commit it with the label “m2”. AT this point we have the top left two commits on master done.

# **** create feature branch, new file and commit f1 ****

c:\Temp\GitPlay>git branch feature      <==== c:\Temp\GitPlay>git status
On branch master
nothing to commit, working tree clean

c:\Temp\GitPlay>git branch -a
  feature                               <==== * master c:\Temp\GitPlay>git checkout feature
Switched to branch 'feature'

c:\Temp\GitPlay>git status
On branch feature
nothing to commit, working tree clean

c:\Temp\GitPlay>mkdir feature

c:\Temp\GitPlay>cd feature

c:\Temp\GitPlay>code .

c:\Temp\GitPlay\feature>dir
05/20/2019  11:52 AM                 4 index.html

c:\Temp\GitPlay\feature>type index.html
f1

c:\Temp\GitPlay\feature>git status
On branch feature
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        ./

nothing added to commit but untracked files present (use "git add" to track)

c:\Temp\GitPlay\feature>git add index.html

c:\Temp\GitPlay\feature>git status
On branch feature
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   index.html

c:\Temp\GitPlay\feature>git commit -m "f1"
[feature 24ded46] f1
 1 file changed, 1 insertion(+)
 create mode 100644 feature/index.html

We now create a new branch which we label it as feature. In our workspace we create a folder name feature and in it we create a index.html file with our IDE. We just ass the string “f1” and commit it using the comment “f1”. The naming convention will help us follow that things are being done correctly and that the results are as expected.

On our diagram we now have the feature branch and a single commit labeled “f1”.

# **** switch to master to commit m3 ****

c:\Temp\GitPlay\feature>git branch -a
* feature
  master

c:\Temp\GitPlay\feature>git checkout master
Deletion of directory 'feature' failed. Should I try again? (y/n) n
Switched to branch 'master'

c:\Temp\GitPlay\feature>git status
On branch master
nothing to commit, working tree clean

c:\Temp\GitPlay\feature>git branch -a
  feature
* master

c:\Temp\GitPlay\feature>git status
On branch master
nothing to commit, working tree clean

c:\Temp\GitPlay\feature>git log
commit 09627479545123f87ad603bfc6a0249d2b601b35 (HEAD -> master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:43:37 2019 -0500

    m2

commit 6ef1917475da8b93cc7f8257e5f051ab9a2e8047
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:35:28 2019 -0500

    m1

c:\Temp\GitPlay\feature>git checkout feature
Switched to branch 'feature'

c:\Temp\GitPlay\feature>git log
commit 24ded46eaec4b8c5b467fb549028e5108cf6a19d (HEAD -> feature)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:58:33 2019 -0500

    f1

commit 09627479545123f87ad603bfc6a0249d2b601b35 (master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:43:37 2019 -0500

    m2

commit 6ef1917475da8b93cc7f8257e5f051ab9a2e8047
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:35:28 2019 -0500

    m1

c:\Temp\GitPlay\feature>cd ..

c:\Temp\GitPlay>git checkout master
Switched to branch 'master'

c:\Temp\GitPlay>git status
On branch master
nothing to commit, working tree clean

c:\Temp\GitPlay>type index.html
m1 m2 m3

c:\Temp\GitPlay>git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   index.html

no changes added to commit (use "git add" and/or "git commit -a")

c:\Temp\GitPlay>git add index.html

c:\Temp\GitPlay>git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   index.html

c:\Temp\GitPlay>git commit -m "m3"
[master bbf5ed7] m3
 1 file changed, 1 insertion(+), 1 deletion(-)

c:\Temp\GitPlay>git status
On branch master
nothing to commit, working tree clean

c:\Temp\GitPlay>git log
commit bbf5ed72d70b43808f92baf80772064183d8dd60 (HEAD -> master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 14:14:45 2019 -0500

    m3

commit 09627479545123f87ad603bfc6a0249d2b601b35
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:43:37 2019 -0500

    m2

commit 6ef1917475da8b93cc7f8257e5f051ab9a2e8047
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:35:28 2019 -0500

    m1

The idea is now to complete the set of commits on the master branch. We are missing m3.

We first check the state on the master and then the feature branch. All seems to be as expected.

The index.html file associated with the master branch is edited by adding the string “m3”. We then commit the change using the label “m3”. We display the status of the main branch and verify that it matches our diagram.

# **** switch to feature to commit f2 ****
c:\Temp\GitPlay>git checkout feature
Switched to branch 'feature'

c:\Temp\GitPlay>git log
commit 24ded46eaec4b8c5b467fb549028e5108cf6a19d (HEAD -> feature)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:58:33 2019 -0500

    f1

commit 09627479545123f87ad603bfc6a0249d2b601b35
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:43:37 2019 -0500

    m2

commit 6ef1917475da8b93cc7f8257e5f051ab9a2e8047
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:35:28 2019 -0500

    m1

c:\Temp\GitPlay\feature>git status
On branch feature
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   index.html

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   ../index.html


c:\Temp\GitPlay\feature>git commit -m "f2"
[feature da4f148] f2
 1 file changed, 1 insertion(+), 1 deletion(-)

c:\Temp\GitPlay\feature>git log
commit da4f148191a363f6bc2add5d766b4b7fdf99f832 (HEAD -> feature)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 14:25:32 2019 -0500

    f2

commit 24ded46eaec4b8c5b467fb549028e5108cf6a19d
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:58:33 2019 -0500

    f1

commit 09627479545123f87ad603bfc6a0249d2b601b35
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:43:37 2019 -0500

    m2

commit 6ef1917475da8b93cc7f8257e5f051ab9a2e8047
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:35:28 2019 -0500

    m1

Now it is time to add the last commit labeled f2 to the feature branch. This represents some additional work which concludes the second and last feature.

The process is similar to what we have been doing. We first check things, edit the file, commit and check if all is well.

Like I have stated multiple times, the best way to learn is by experimenting. I made some changes that fall outside of the main scope of this post. You will probably experiment as you go. The following is a set of commands that I use to drop changes to a file before committing.

# **** drop a change ****
c:\Temp\GitPlay>git status
On branch feature
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   index.html

no changes added to commit (use "git add" and/or "git commit -a")

c:\Temp\GitPlay>git stash
Saved working directory and index state WIP on feature: da4f148 f2

c:\Temp\GitPlay>git status
On branch feature
nothing to commit, working tree clean

c:\Temp\GitPlay>git stash drop
Dropped refs/stash@{0} (db07f07007ec4ed495f001d9d1b7d18026a07020)

c:\Temp\GitPlay>git status
On branch feature
nothing to commit, working tree clean

c:\Temp\GitPlay>git checkout master
Switched to branch 'master'

c:\Temp\GitPlay>git status
On branch master
nothing to commit, working tree clean

In this case I have edited on the feature branch the index.html file. I do not wish to commit such changes. The simplest was I have found to do this is with git stash followed by git stash drop.

# **** this is how we need things to start with (before git merge) ****

c:\Temp\GitPlay>git log
commit bbf5ed72d70b43808f92baf80772064183d8dd60 (HEAD -> master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 14:14:45 2019 -0500

    m3

commit 09627479545123f87ad603bfc6a0249d2b601b35
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:43:37 2019 -0500

    m2

commit 6ef1917475da8b93cc7f8257e5f051ab9a2e8047
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:35:28 2019 -0500

    m1

c:\Temp\GitPlay>git checkout feature
Switched to branch 'feature'

c:\Temp\GitPlay>git log
commit da4f148191a363f6bc2add5d766b4b7fdf99f832 (HEAD -> feature)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 14:25:32 2019 -0500

    f2

commit 24ded46eaec4b8c5b467fb549028e5108cf6a19d
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:58:33 2019 -0500

    f1

commit 09627479545123f87ad603bfc6a0249d2b601b35
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:43:37 2019 -0500

    m2

commit 6ef1917475da8b93cc7f8257e5f051ab9a2e8047
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:35:28 2019 -0500

    m1

We are ready to use git merge. The status of the master and feature branches is as expected. They match the diagram for this post.

# **** git merge ****

c:\Temp\GitPlay>git status
On branch master
nothing to commit, working tree clean

//$git merge feature
c:\Temp\GitPlay>git merge --squash feature
Automatic merge went well; stopped before committing as requested
Squash commit -- not updating HEAD

c:\Temp\GitPlay>git commit -m "feature and master merged"
[master a400679] feature and master merged
 1 file changed, 1 insertion(+)
 create mode 100644 feature/index.html

c:\Temp\GitPlay>git log
commit a4006799874ae0676f3b077084d7eeda8134477f (HEAD -> master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 14:48:11 2019 -0500

    feature and master merged   <====

commit bbf5ed72d70b43808f92baf80772064183d8dd60
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 14:14:45 2019 -0500

    m3

commit 09627479545123f87ad603bfc6a0249d2b601b35
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:43:37 2019 -0500

    m2

commit 6ef1917475da8b93cc7f8257e5f051ab9a2e8047
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 11:35:28 2019 -0500

    m1

We now merge the feature branch into master. When done we check and the results match the diagram as illustrated in the master combined portion.

Now let’s use git rebase and see how we use it and what the results are.

# **** this is how we need things to start with (before git rebase) ****

c:\Temp\GitPlay>git checkout master
Switched to branch 'master'

c:\Temp\GitPlay>git log
commit b83e2677b2e26d6e303cb3171a4c23e01134febc (HEAD -> master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:05:21 2019 -0500

    m2

commit 2b43b486f478a483ac3f8897d0323cacbc23b8cf
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:04:32 2019 -0500

    m1

c:\Temp\GitPlay>git checkout feature
Switched to branch 'feature'

c:\Temp\GitPlay>git log
commit 23d547047b37b652206ce601b3ebb79a50204184 (HEAD -> feature)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:11:42 2019 -0500

    f1

commit b83e2677b2e26d6e303cb3171a4c23e01134febc (master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:05:21 2019 -0500

    m2

commit 2b43b486f478a483ac3f8897d0323cacbc23b8cf
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:04:32 2019 -0500

    m1

At this point we have on master commits m1 and m2 and on feature commit f1.

We first edit and commit m3 on the master branch. We verify that all is well. We then edit and commit f2 to the feature branch. The feature branch is displayed and all is well so far. The master branch also seems to be as expected.

# **** rebase on main branch ****

c:\Temp\GitPlay>git checkout feature

c:\Temp\GitPlay>git log
commit 23d547047b37b652206ce601b3ebb79a50204184 (HEAD -> feature)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:11:42 2019 -0500

    f1

commit b83e2677b2e26d6e303cb3171a4c23e01134febc (master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:05:21 2019 -0500

    m2

commit 2b43b486f478a483ac3f8897d0323cacbc23b8cf
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:04:32 2019 -0500

    m1

c:\Temp\GitPlay>git checkout master
Already on 'master'

c:\Temp\GitPlay>git status
On branch master
nothing to commit, working tree clean

c:\Temp\GitPlay>git log
commit b83e2677b2e26d6e303cb3171a4c23e01134febc (HEAD -> master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:05:21 2019 -0500

    m2

commit 2b43b486f478a483ac3f8897d0323cacbc23b8cf
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:04:32 2019 -0500

    m1

c:\Temp\GitPlay>git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   index.html

no changes added to commit (use "git add" and/or "git commit -a")

c:\Temp\GitPlay>type index.html
m1 m2 m3

c:\Temp\GitPlay>git add index.html

c:\Temp\GitPlay>git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   index.html

c:\Temp\GitPlay>git commit -m "m3"
[master a128b66] m3
 1 file changed, 1 insertion(+), 1 deletion(-)

c:\Temp\GitPlay>git log
commit a128b66558351f4cef6c5fa3d749240b38154625 (HEAD -> master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:30:58 2019 -0500

    m3

commit b83e2677b2e26d6e303cb3171a4c23e01134febc
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:05:21 2019 -0500

    m2

commit 2b43b486f478a483ac3f8897d0323cacbc23b8cf
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:04:32 2019 -0500

    m1

c:\Temp\GitPlay>git checkout feature
Switched to branch 'feature'

c:\Temp\GitPlay>git rebase master
First, rewinding head to replay your work on top of it...
Applying: f1

c:\Temp\GitPlay>git log
commit 97d37d187fd6c0cb53be7a606fe84e73dfa4cd5c (HEAD -> feature)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:11:42 2019 -0500

    f1

commit a128b66558351f4cef6c5fa3d749240b38154625 (master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:30:58 2019 -0500

    m3

commit b83e2677b2e26d6e303cb3171a4c23e01134febc
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:05:21 2019 -0500

    m2

commit 2b43b486f478a483ac3f8897d0323cacbc23b8cf
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:04:32 2019 -0500

    m1

c:\Temp\GitPlay>git checkout feature
Already on 'feature'
M       feature/index.html

c:\Temp\GitPlay>git status
On branch feature
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   feature/index.html

no changes added to commit (use "git add" and/or "git commit -a")

c:\Temp\GitPlay>git add feature/index.html

c:\Temp\GitPlay>git status
On branch feature
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   feature/index.html

c:\Temp\GitPlay>git commit -m "f2"
[feature 35baf08] f2
 1 file changed, 1 insertion(+), 1 deletion(-)

c:\Temp\GitPlay>git log
commit 35baf0890778f5af007bd916b3259f227b6f89d1 (HEAD -> feature)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:40:57 2019 -0500

    f2

commit 97d37d187fd6c0cb53be7a606fe84e73dfa4cd5c
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:11:42 2019 -0500

    f1

commit a128b66558351f4cef6c5fa3d749240b38154625 (master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:30:58 2019 -0500

    m3

commit b83e2677b2e26d6e303cb3171a4c23e01134febc
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:05:21 2019 -0500

    m2

commit 2b43b486f478a483ac3f8897d0323cacbc23b8cf
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:04:32 2019 -0500

    m1


c:\Temp\GitPlay>git checkout master
Switched to branch 'master'

c:\Temp\GitPlay>git log
commit a128b66558351f4cef6c5fa3d749240b38154625 (HEAD -> master)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:30:58 2019 -0500

    m3

commit b83e2677b2e26d6e303cb3171a4c23e01134febc
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:05:21 2019 -0500

    m2

commit 2b43b486f478a483ac3f8897d0323cacbc23b8cf
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:04:32 2019 -0500

    m1

We now rebase on master using the feature branch. The idea is to get an updated master branch with all the changes from the feature branch.

# **** rebase on feature branch ****
c:\Temp\GitPlay>git rebase feature
First, rewinding head to replay your work on top of it...
Fast-forwarded master to feature.

c:\Temp\GitPlay>git log
commit 35baf0890778f5af007bd916b3259f227b6f89d1 (HEAD -> master, feature)
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:40:57 2019 -0500

    f2

commit 97d37d187fd6c0cb53be7a606fe84e73dfa4cd5c
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:11:42 2019 -0500

    f1

commit a128b66558351f4cef6c5fa3d749240b38154625
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:30:58 2019 -0500

    m3

commit b83e2677b2e26d6e303cb3171a4c23e01134febc
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:05:21 2019 -0500

    m2

commit 2b43b486f478a483ac3f8897d0323cacbc23b8cf
Author: John Canessa <john.canessa@gmail.com>
Date:   Mon May 20 15:04:32 2019 -0500

    m1

Hope you enjoyed this post. If you have comments or questions regarding this or any other post in this blog, or if you would like me to help with any phase in the SDLC (Software Development Life Cycle) of a product or service, please do not hesitate and leave me a note below. Requests for help will remain private.

Keep on reading and experimenting. It is the best way to learn!

John

Follow me on Twitter:  @john_canessa

Leave a Reply

Your email address will not be published.

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