GIT STRATEGY, A PRACTICAL SAMPLE – PART I

GIT STRATEGY, A PRACTICAL SAMPLE – PART I

Git Tutorial Part I

After a quiet month, you finally get a client call! His name is Steve and he wants to open up an online store. Nothing big, just a web page to show a catalog. He’ll send you an email with the details.

Because it’s supposed to be a very simple project, we set up a git repository and begin working directly in the master branch. Currently, the only information we have from the client is that he needs a web page, so let’s get started!

CREATE A REPO

To create a workspace repository, you need to use the git init command. Navigate to the folder where the repository will be stored and execute:

git init

However, we usually do this in a different manner. First, create a bare repository:

git init --bare

After creating the repository, clone it with the git clone command. A bare repository is where you can store the data in a centralized and remote location, but you shouldn’t work directly on it. You should work in a workspace repository and fetch or push the changes to the bare repo.

Because I want the focus of this post to be repository strategies, I’m going to omit the remote repository part and make the examples only in the local workspace repo.

Let’s create our very simple “hello world” file, labeled index.html, which will look like this:

<!DOCTYPE HTML>
<html>
 <head></head>
 <body></body>
</html>

SAVE CHANGES

First, we are going to add the file to the index (git add), and then we will actually record the changes to the repository (git commit):

git add index.html
git commit -m "First commit web store"

MAKE CHANGES

When the expected email arrives, it indicates that Steve wants to open up a bike store in Canada. He wants to display each bike and their specs, very much like an online catalog.

Let’s update the file with the new info:

<!DOCTYPE HTML>
<html lang=”en-CA”>
<head><title>Steve store</title></head>
<body><p>This is a fantastic bike shop!!</p></body>
</html>

Now we need to record the changes so our repository is up to date:

git commit -a -m "Bike shop in en-CA"

The above command does git-add and git-commit in just one single command.


At this point, we have a linear history in the master branch with just 2 commits.
git commit example


CREATE A BRANCH

Steve call us again, informing us that perhaps he’ll expand the business to sell scooters, but he is not completely sure that is a good idea.

Because it is unclear that this feature will remain in the final product, we create a new branch for it.

Create the scooters branch:

git branch scooters

Navigate to the scooters branch:

git checkout scooters

And modify our HTML file in the scooters branch:

<!DOCTYPE HTML>
<html lang="en-CA">
<head><tittle>Steve store</tittle></head>
<body><p>This is a fantastic bike store!!</p>
<p>This is also a fantastic scooter store!!</p></body>
</html>

And commit:

git add index.html
git commit -m "Bike and scooters demo"

We now have 2 commits in the master branch, and one additional in the scooters branch.

git create branch example


MAINTAIN DEPENDANT BRANCHES

We get another call from Steve, he really is a nervous and talkative client. He said that it does not matter whether the store will only be for bikes or bikes and scooters, he needs to move from Canada to the USA. So now we need to change the language clause from lang=”en-CA” to lang=”en-US” in both branches.

We currently have a very small project, and we can modify both branches manually, but let’s err on the side of caution and pretend this is a big project. How should we apply the change? And in which branch?

In our case, since the scooters branch is a particularization of the master branch, and the desired change is a general change we are going to apply, we will implement it in the master branch and merge it over to the scooters branch.

Navigate to the branch that we want to modify, which is the master branch, by making it the active branch:

git checkout master

Edit the file:

<!DOCTYPE HTML>
<html lang=”en-US”>
<head><tittle>Steve store</tittle></head>
<body><p>This is a fantastic bike shop!!</p></body>
</html>

And commit to master:

git commit -a -m "Moved from Canada to USA"

Now go over to the scooters branch (git checkout) and merge in master (git merge):

git checkout scooters
git merge master

We have a conflict! There are changes in the same file in the master branch and in the scooters branch. We will resolve the conflict by editing index.html, and by committing it.

Edit the file:

<!DOCTYPE HTML>
 <html lang=”en-US”>
 <head><tittle>Steve store</tittle></head>
 <body><p>This is a fantastic bike store!!</p>
 <p>Also this is a fantastic scooter store!!</p></body>
 </html>

And now to finish the conflict resolution by committing the file:

 git commit -a -m "Merge from master, changing to USA"

This was a normal commit, so the content from (f945be) and the content from (bb9273) was mixed and the result was written in a new commit (0343f0).


git merge example


CHANGING REPO DESIGN

The phone rings. Hello…, Oh Steve, It has been a long time since we talked! Steve tell us that he has finally decided that he wants to sell bikes and scooters, but there is this tiny new thing that he wants: His nephew Brian, has just finished his computer science studies, and he needs to make some money, so he will be developing the bike part of the site. Since Brian will be part of the team, his uncle wants us to create a common architecture and 2 modules, one for bikes and one for scooters.

This creates a huge problem, we did not anticipate this change, in the code nor in the repository design. Really, this is a huge challenge in a big project, the main architecture decisions need to be thought of carefully at the beginning of the project so it can nicely evolve and not work against the developers. It’s hard to overemphasize how important this is, as it is one of the strongest points of the Sileria team, think before touching the keyboard!

Really, please do.

Where are the changes to the architecture going to go? In the master branch or in the scooters branch?

One point favoring the master branch is that the change is general and scooters is a particularization, but at the same time, the scooters branch has the code pertinent to both stores, which is a point in its favor.

We can test the architecture and the two modules that are already there. The master branch does not know anything about the scooters store. After the architecture separation, we will have the same code in both branches.

In this case, and this is very specific for this case, I’m going to select the second option, and separate the architecture in the scooters branch. So index.html will be common, with two links, one for each module:

<!DOCTYPE HTML>
 <html lang=”en-US”>
 <head><tittle>Steve store</tittle></head>
 <body>
 <a href="bikes.html">Bike store</a>
 <a href="scooters.html">Scooter store</a>
 </body>
 </html>

Create bikes.html

 <html lang=”en-US”>
 <head><tittle>Bike store</tittle></head>
 <body></body>
 </html>

And create scooters.html

 <html lang=”en-US”>
 <head><tittle>Scooter store</tittle></head>
 <body></body>
 </html>

We are already in the scooters branch, so let’s commit our lovely new architecture:

git add index.html scooters.html bikes.html
git commit -m “New architecture, module separation”

And now we will merge this into master:

 git checkout master
 git merge scooters

This is the actual state.

git fast forward example

What happened here? Well, this merger is called a “fast forward”.

What we call branches, are really pointers to one specific commit. When the commit that we want to merge (a899de), has a linear path that can be traced back until the target commit (f945be), the merge just needs to move the pointer of the target branch, but it does not need to create a new commit with the mixture of both because that is not necessary.

In a “no fast-forward” merge, a new commit is created with a mixture of the code and the target branch pointer is moved to it (like the 0343f0 merge).

We can avoid the fast-forward mechanism and force git to create the commit anyway if we use the –no-ff option. This results in a more human-friendly sequence of commits. Human-friendly sounds like something we humans like, let’s go back and do it that way!

To go back, we will just move the master branch pointer from (a899de) to (f945be). The –hard option does not only move the pointer but also updates our workspace:

git reset --hard f945be

And now we do the merge while avoiding fast-forward:

git merge --no-ff -m “New architecture merge” scooters

git merge avoiding fast forward

Now we are happy! We did the costly architectural change in the middle of our project!

CREATE FEATURE BRANCHES

However, we still need to resolve how Brian and I will be working together without interfering with each other’s projects.

What we can do is that we can create another branch for him where he can do the bike module development. The following creates a new branch which also points to (eb9a4), although I would not recommend creating a commit by itself:

git branch bikes

After some work in both modules the state will be like this:

git repository with feature branches

Our repository strategy is the following:

Brian will now work on the bikes branch without interfering with our work in the scooters branch. When one of the modules is in a stable state, we will then merge it into the master branch, test it, and generate a new release.

What we have explained here is one of the simplest but most useful repository strategies.

RELEASE A VERSION

Last but not least, we will explain how to tag master with a new version name and propagate the changes to the other branches every time we merge from the modules branch into the master.

Navigate to the master branch:

git checkout master

Merge the stable advance from bikes:

git merge –no-ff -m “Merge bikes feature” bikes

And tag it for version 1.1 release:

git tag -a v1.1 -m “Release version with bikes new feature”

And finally, update the other branch (scooters) from the master branch:

git checkout scooters
git merge --no-ff -m “Updated from v1.1” master

git merge to master for release

From this point, work can continue independently in both modules, after we have released the v1.1 from the master branch.

Stay tuned for part 2, where Steve & Brian challenge us by making the project increasingly complex. We will discuss where bug-fixing should be done or where to maintain a common architecture!

Leave a Reply

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

%d bloggers like this: