Git Quick Start Guide

Ditch USBs and start using real version control, and if you follow this guide, you can start using git in 30 minutes! By Patrick Whelan

If you have any experience with programming or just altering config files, I'm sure you've been dumbstruck by how one change you've made along the line affects the whole project. Identifying and isolating the problem without a version control system is often time- and energy-intensive, involving retracing your steps and checking all changes made before the unwanted behavior first occurred. A version control system is designed explicitly to make that process easier and provide readable comparisons between versions of text.

Another great feature that distributed version control systems such as git provide is the power of lateral movement. Traditionally, a team of programmers would implement features linearly. This meant pulling the code from the trusted source (server) and developing a section before pushing the altered version back upstream to the server. With distributed systems, every computer maintains a full repository, which means each programmer has a full history of additions, deletions and contributors as well as the ability to roll back to a previous version or break away from the trusted repository and fork the development tree (which I discuss later).

Quick Start Guide

The great thing about git is there's so little you need to know! Without further ado, let's begin with the most important commands.

First, I'm working with a previous project of mine located here:


[user@lj src]$ pwd
/home/lj/projects/java/spaceInvaders/src

To create a local repository, simply run:


[user@lj src]$ git init
Initialized empty Git repository in 
 ↪/home/lj/projects/java/spaceInvaders/src/.git/

To add all source files recursively to git's index, run:


[user@lj src]$ git add .

To push these indexed files to the local repository, run:


[user@lj src]$ git commit

You'll see a screen containing information about the commit, which allows you to leave a description of the commit:


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
#
 Initial commit

 Changes to be committed:
        new file:   engine/collisionChecker.java
        new file:   engine/direction.java
        new file:   engine/gameEngine.java
        new file:   engine/gameObjects.java
        new file:   engine/level.java
        new file:   engine/main.java
        new file:   engine/mathVector.java
        new file:   graphics/drawer.java
        new file:   sprites/baseSprite.java
        new file:   sprites/boss.java
        new file:   sprites/enemy.java
        new file:   sprites/healthBar.java
        new file:   sprites/menu/menuItem.java
        new file:   sprites/menu/menuItemExclusiveMoveOnInput.java
        new file:   sprites/menu/menuItemLevelDecrease.java
        new file:   sprites/menu/menuItemLevelIncrease.java
        new file:   sprites/menu/menuItemMovementDirections.java
        new file:   sprites/menu/menuItemProjectileLimit.java
        new file:   sprites/menu/menuItemStartGame.java
        new file:   sprites/pickup/fireRateBoost.java
        new file:   sprites/pickup/pickup.java
        new file:   sprites/pickup/shield.java
        new file:   sprites/pickup/shieldPickup.java
        new file:   sprites/pickup/speedBoost.java
        new file:   sprites/player.java
        new file:   sprites/projectile.java
        new file:   sprites/wall.java


[user@lj src]$ git commit
[master (root-commit) 4cf5218]  
 Initial commit  
 Changes to be committed: 	
        new file:   engine/collisionChecker.java 	
        new file:   engine/direction.java 	
        new file:   engine/gameEngine.java 	
        new file:   engine/gameObjects.java 	
        new file:   engine/level.java 	
        new file:   engine/main.java 	
        new file:   engine/mathVector.java 	
        new file:   graphics/drawer.java 	
        new file:   sprites/baseSprite.java 	
        new file:   sprites/boss.java 	
        new file:   sprites/enemy.java 	
        new file:   sprites/healthBar.java 	
        new file:   sprites/menu/menuItem.java 	
        new file:   sprites/menu/menuItemExclusiveMoveOnInput.java 	
        new file:   sprites/menu/menuItemLevelDecrease.java 	
        new file:   sprites/menu/menuItemLevelIncrease.java 	
        new file:   sprites/menu/menuItemMovementDirections.java 	
        new file:   sprites/menu/menuItemProjectileLimit.java 	
        new file:   sprites/menu/menuItemStartGame.java 	
        new file:   sprites/pickup/fireRateBoost.java 	
        new file:   sprites/pickup/pickup.java 	
        new file:   sprites/pickup/shield.java 	
        new file:   sprites/pickup/shieldPickup.java 	
        new file:   sprites/pickup/speedBoost.java 	
        new file:   sprites/player.java 	
        new file:   sprites/projectile.java 	
        new file:   sprites/wall.java
 27 files changed, 2557 insertions(+)
 create mode 100755 engine/collisionChecker.java
 create mode 100755 engine/direction.java
 create mode 100755 engine/gameEngine.java
 create mode 100755 engine/gameObjects.java
 create mode 100755 engine/level.java
 create mode 100755 engine/main.java
 create mode 100755 engine/mathVector.java
 create mode 100755 graphics/drawer.java
 create mode 100755 sprites/baseSprite.java
 create mode 100755 sprites/boss.java
 create mode 100755 sprites/enemy.java
 create mode 100755 sprites/healthBar.java
 create mode 100755 sprites/menu/menuItem.java
 create mode 100755 sprites/menu/menuItemExclusiveMoveOnInput.java
 create mode 100755 sprites/menu/menuItemLevelDecrease.java
 create mode 100755 sprites/menu/menuItemLevelIncrease.java
 create mode 100755 sprites/menu/menuItemMovementDirections.java
 create mode 100755 sprites/menu/menuItemProjectileLimit.java
 create mode 100755 sprites/menu/menuItemStartGame.java
 create mode 100755 sprites/pickup/fireRateBoost.java
 create mode 100755 sprites/pickup/pickup.java
 create mode 100755 sprites/pickup/shield.java
 create mode 100755 sprites/pickup/shieldPickup.java
 create mode 100755 sprites/pickup/speedBoost.java
 create mode 100755 sprites/player.java
 create mode 100755 sprites/projectile.java
 create mode 100755 sprites/wall.java

Files are removed from the index in the same *NIX style:


[user@lj src]$ git rm -r .
rm 'engine/collisionChecker.java'
rm 'engine/direction.java'
rm 'engine/gameEngine.java'

... SNIP ...

To compare my local index with the repository, I use git diff (note: the row of hyphens at the end of most lines was trimmed for readability):


[user@lj src]$ git diff --cached --stat
 engine/collisionChecker.java                   | 281 ----- ...
 engine/direction.java                          |  14 ----
 engine/gameEngine.java                         | 504 ----- ...
 engine/gameObjects.java                        |  61 ----- ...
 engine/level.java                              | 134 ----- ...
 engine/main.java                               |  51 ----- ...
 engine/mathVector.java                         |  46 ----- ...
 graphics/drawer.java                           | 323 ----- ...
 sprites/baseSprite.java                        | 303 ----- ...
 sprites/boss.java                              |  73 ----- ...
 sprites/enemy.java                             | 119 ----- ...
 sprites/healthBar.java                         |  64 ----- ...
 sprites/menu/menuItem.java                     |  21 ----- ...
 sprites/menu/menuItemExclusiveMoveOnInput.java |  31 ----- ...
 sprites/menu/menuItemLevelDecrease.java        |  28 ----- ...
 sprites/menu/menuItemLevelIncrease.java        |  27 ----- ...
 sprites/menu/menuItemMovementDirections.java   |  30 ----- ...
 sprites/menu/menuItemProjectileLimit.java      |  31 ----- ...
 sprites/menu/menuItemStartGame.java            |  33 ----- ...
 sprites/pickup/fireRateBoost.java              |  39 ----- ...
 sprites/pickup/pickup.java                     |  77 ----- ...
 sprites/pickup/shield.java                     |  39 ----- ...
 sprites/pickup/shieldPickup.java               |  47 ----- ...
 sprites/pickup/speedBoost.java                 |  38 ----- ...
 sprites/player.java                            |  64 ----- ...
 sprites/projectile.java                        |  44 ----- ...
 sprites/wall.java                              |  35 ----- ...
 27 files changed, 2557 deletions(-)

When used without the cached option, only changes that have been made without being added to the index will be displayed. The stat option provides a quick table instead of the standard line-by-line review provided through the less command without it. This is often useful when looking for a summary of many files instead of analyzing small changes.

git status provides a more verbose output similar to diff:


[user@lj src]$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	deleted:    engine/collisionChecker.java
	deleted:    engine/direction.java
	deleted:    engine/gameEngine.java

... SNIP ...

Branches are an essential part of git, and understanding them is paramount to grasping how git truly operates. Each branch is a different development path, which is to say that all branches are independent of one another. To explain this better, let's look at an example.

All projects start with the "master" branch. You can view the current branches with:


[user@lj src]$ git branch -a
* master

New branches are used to develop features that then can be merged back into the master branch. This way, different modules can remain separate until they are stable and ready to merge with the master branch, from which all other branches should derive. In this way, the master branch is more like a tree trunk than a branch. Creating a new branch often is referred to as "forking" the development tree, splitting the linear development into two before merging the branches again once development on the forked branch is complete.

To create a new branch, with all the data of the current branch, use:


[user@lj src]$ git checkout -b development
Switched to a new branch 'development'

checkout is used to finalize all operations on the current branch before detaching from it. The -b switch creates a new branch from the current branch. In this instance, I have created a development branch that will contain "hot" or unstable code.

Next, rename the master branch to "stable":


[user@lj src]$ git branch -m master stable

Now that you have your two branches set up, let's compare them to ensure that the development branch has been populated with the stable branch's data:


[user@lj src]$ git diff --stat development stable 
[user@lj src]$ 

Since diff hasn't returned anything, you know they contain exactly the same data.

Now that you've got your two branches set up, let's begin making changes on the development branch:


[user@lj src]$ git diff --cached
diff --git a/engine/main.java b/engine/main.java
index 38577b5..5900d80 100755
--- a/engine/main.java
+++ b/engine/main.java
@@ -11,6 +11,8 @@ import graphics.drawer;
 
 public class main 
 {
+       // WHEN CAN I GO BACK TO C!?!?!?
+      
        /*
         * Class:                       main 
         * Author:                      Patrick

And commit them to the branch:


[user@lj src]$ git commit
[development e1f13bd]  Changes to be committed: modified:   
 ↪engine/main.java
 1 file changed, 2 insertions(+)

After committing the change, you're given a unique identifier—"e1f13bd" in this case. It's used to refer to this commit; however, it's pretty hard for feeble human minds to remember something like that, so let's tag it with something more memorable.

First, find and tag the initial commit:


[user@lj src]$ git log
commit e1f13bde0bbe3d64f563f1abb30d4393dd9bd8d9
Author: user <user@lj.linux>
Date:   Wed Jun 6 15:51:18 2018 +0100

     Changes to be committed:
            modified:   engine/main.java

commit 4cf52187829c935dac40ad4b65f02c9fb6dab7ba
Author: user <user@lj.linux>
Date:   Tue Jun 5 21:31:14 2018 +0100

     Initial commit
     Changes to be committed:
            new file:   engine/collisionChecker.java
            new file:   engine/direction.java
            new file:   engine/gameEngine.java
... SNIP ...



[user@lj src]$ git tag v0.1 4cf52187829c935dac40ad4b65f02c9fb
↪6dab7ba
[user@lj src]$ git tag v0.11 e1f13bd

Now that the two commits have been tagged with a version number, they're much easier to remember and compare:


[user@lj src]$ git diff --stat v0.1 v0.11
 engine/main.java | 2 ++
 1 file changed, 2 insertions(+)

After testing and ensuring that the development branch is stable, you should update the stable branch to the current development branch:


[user@lj src]$ git checkout stable
Switched to branch 'stable'
[user@lj src]$ git merge development 
Updating 4cf5218..e1f13bd
Fast-forward
 engine/main.java | 2 ++
 1 file changed, 2 insertions(+)

As you can see below, both branches still exist and contain the same data:


[user@lj src]$ git branch -a
  development
* stable
[user@lj src]$ git diff --stat stable development 

If at any time you want to roll back, use git revert to revert a commit and undo any changes it made:


[user@lj src]$ git revert v0.11
[stable 199169f] Revert " Changes to be committed:"
 1 file changed, 2 deletions(-)
[user@lj src]$ git diff --stat stable development 
 engine/main.java | 2 ++
 1 file changed, 2 insertions(+)

This is all you need to know to get started and become proficient in using git for personal use. All git repos operate the same way; however, working with remote repositories owned by others brings its own caveats. Read on.

Working with Remote Repositories

First things first, let's get the remote repo:


[user2@lj ~]$ git clone src repo
Cloning into 'repo'...
done.

After some changes by user2, they are committed to user2's repo:


[user2@lj repo]$ git commit -a
[stable 6a04336]  Changes to be committed: modified:   
 ↪graphics/drawer.java
 1 file changed, 1 insertion(+)

Now the original owner can pull the changes back into the main repo. Pulling fetches and merges the changes in one command where possible:


[user@lj src]$ git pull /home/user2/repo
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
From /home/user2/repo
 * branch            HEAD       -> FETCH_HEAD
Updating 199169f..6a04336
Fast-forward
 graphics/drawer.java | 1 +
 1 file changed, 1 insertion(+)
[user@lj src]$ git diff --cached

All changes have been pulled into the main repo, and both parties have the most up-to-date version of the code. This works exactly the same on one system as it does on a hosted site, such as GitHub. The only difference is that the repo is located by URL rather than system path.

This is just the beginning of git! This quick start guide should be sufficient for any aspiring developer or frustrated coder sick of manually rolling back versions. Now you can call yourself a gitter, or git for short!

About the Author

Patrick Whelan is a first-year student at Edge Hill university in the UK. He is an aspiring developer, blogger and all-round hacker.