Git Version Control – Fundamentals (Part 2)

Now that we have git installed locally, we can start playing around with some of the basic commands. The first thing you want to do is initialise a git repository. As far as I know, there are two ways to accomplish that:

  1. Clone an existing Repo
  2. Initialise an empty Repo

Cloning an existing repo will pull the entire git history of the project from a remote server unto your local machine. Usually, you’ll do this when you wish to contribute to an existing project. The project will have a clone url (either Http or SSH).

So, to clone an existing project like EventBus on your local machine. Simply run command:

$ git clone https://github.com/greenrobot/EventBus.git

This creates a folder on your box named EventBus. If you wish to clone the project into a different folder named event_bus_local for instance, then run:

$ git clone https://github.com/greenrobot/EventBus.git event_bus_local

Asides cloning, you can create a git repository using the git init command. Run this within the project folder you intend to initialise. This will create an empty git repository.

$ cd full/path/to/project/folder
$ git init

Tracking Status, Adding Files and Committing

Through the lifecycle of any project, developers will add files, modify existing files, delete redundant files etc. Git provides a number of features for adding files, tracking updates, committing changes. Before we dive into these features, we need to be aware of the states in which git repository files can exist. Usually there’s about 4 states:

  1. New / Untracked State: implying that the file was just created and is not being tracked by git.
  2. Modified State: in this state, the file is already tracked by git, but changes have been made since the last commit.
  3. Staged State: the current state of the file has been captured so it will be saved in the next commit.
  4. Committed State: the staged state of the file has been saved into the git repo.

Let’s run a few commands to watch the transitions between states.

$ mkdir test-repo && cd $_
$ git init

What we have done is create a new directory called test-repo and navigate into it. The $_ is a placeholder for the previous command argument to avoid retyping it. Then we run git init command inside the empty repo to initialise it.

If you run ls -a within your test-repo, you should see a .git folder. This is the folder that makes your project a git repo; it stores project metadata, branch information, commit history, and a bunch of other stuff required for version control.

In order to check the current state of the git repository, we run git status. This should give a similar output to the one below:

$ git status

On branch master
No commits yet

nothing to commit (create/copy files and use "git add" to track)

Git automatically creates a master branch on an empty repository. If you’re using git to manage a software project, the master branch would usually refer to the working version of the code that is deployed on the live environment. Git uses branches to concurrently maintain multiple versions of a codebase. I’ll still get into branches later. But for now, notice that the command echoed out ‘No commits yet’. This means that we have not made any commits and git is waiting for us to start adding files. Now let’s add a dummy file.

$ echo 'Hello World' > hello.txt

This creates a hello.txt file in the project folder with contents Hello World. If you run git status again, you’ll see a different output.

$ git status

On branch master
No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	hello.txt

Our hello.txt file has been recognised as an untracked file. Git has no history of it and so we need to commit it. Before committing changes, the changes must be staged.

You stage changes with the git add. The git add command takes an argument which is the file you wish to stage. We could just type git add hello.txt, or more tersely, run git add . so that all changes are added. Try running git status again.

$ git add .
$ git status

On branch master
No commits yet

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

	new file:   hello.txt

As you can see, git recognises hello.txt as a new file that is ready to be committed. Now let’s commit our new file that has been staged.

$ git commit -m "Created hello.txt"

This will simply save the staged changes and attach a commit message. Now that we have committed, our working directory is clean.

The best time to commit is when you have accomplished a unit of work and intend to save a snapshot of your project files. 

There’s one more file state we haven’t touched on yet. Which is the modified state. When a file is in the modified state, it means the file has been previously committed to the git repository, but has undergone updates since its last commit. We’ll now make some changes to our committed hello.txt file.

$ echo 'Hello World' >> hello.txt

We have appended a new line to the hello.txt file. Now if you run git status again, you should see the output below.

$ 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:   hello.txt

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

Diffing

Git has recognised that hello.txt has changed since our last commit. To view the exact change that happened, run a git diff command.

$ git diff hello.txt

diff --git a/hello.txt b/hello.txt
index 557db03..8254f87 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1 +1,2 @@
 Hello World
+Hello World

Notice at the end of the output, we have a +Hello World. This is an indication a new line was added to the hello.txt file. Of course, a minus sign will signify a line that was removed. We can now commit the new changes to hello.txt.

$ git commit -am "Updated hello.txt"

Since hello.txt has already been tracked on git, we don’t have to explicitly type git add. We can use a -am switch with the commit command and git will add and commit at once.

Viewing Logs

So far, we have been able to log two commits on our new git repository. In order to view the history of commits on a repository, we use the git log command. This will output all the commits on the repo in reverse chronological order (meaning most recent first).

$ git log

commit 8e7c6555efa7bf3cfb7a2ad0f566a52f883d4cf0 (HEAD -> master)
Author: Julian Dumebi Duru <[email protected]>
Date:   Sun May 24 21:43:55 2020 +0100

    Updated hello.txt

commit f8437c70ec701b3e74fc586c6a088f09795bfc15
Author: Julian Dumebi Duru <[email protected]>
Date:   Sun May 24 21:12:09 2020 +0100

    Created hello.txt

We have our two commits listed out with their commit messages. You can always restrict the log output to the most recent n commits by using the command git log -n, where n is a non-negative integer.

$ git log -1

commit 8e7c6555efa7bf3cfb7a2ad0f566a52f883d4cf0 (HEAD -> master)
Author: Julian Dumebi Duru <[email protected]>
Date:   Sun May 24 21:43:55 2020 +0100

    Updated hello.txt

The weird looking string ‘8e7c6555efa7bf3cfb7a2ad0f566a52f883d4cf0’ is known as the commit hash. In computer science, hashing is used to derive a string value by applying some mathematical calculation on an input. So in many ways, the hash is mathematically related to the current content of our repository. The log command also outputs the author of the commit as well as the date.