I decided to write this post because I believe there are lots of people out there that probably struggle using Git or maybe don’t really know what it is. Speaking for myself, I didn’t use it for several years, mostly because I wasn’t really aware of how useful and powerful it can be! Keep in mind that I am just a Git newbie and this article is mostly a compilation on how to set up a local repository, use it and working a remote repository.

What is Git? Why should I use it?

Git is a distributed version control system (VCS) and a source control management (SCM) tool created by Linus Torvalds in 2005. As a distributed version and source control system, Git allows multiple users to work on a project (a repository) and concurrently contribute to it. Git also works on local repositories instead of relying on a remote server (of course you can work with a remote repository too).

Regarding why to use it, well, did you ever found yourself working on a project, trying to fix a bug or testing a new feature which ended up in a catastrophic error or problem which then took you hours, days or weeks to solve or undo? Well, Git can help you!

Git tracks alterations to any file within a project and allows you to compare files amongst different revisions (or commits in Git jargon) and even returning to project to a previous known-to-work state. It can also start new sandboxes (the branches in Git jargon) where you can test new functionalities or features and then merge them to the master project or simply discard them!

Another important reason to learn and to use Git is that most companies use it and expect current and future employees to use it too! So what you are waiting for?

Git States or Stages

When working with Git, it is important to understand the different states or stages a file can be. First of all, files can be tracked by Git or they can be untracked. Obviously only tracked files will be part of the VCS/SCM. Also, keep in mind that while you can track almost any kind of file, only text files can be really understood by Git, so that it can track the differences between them.

In Git jargon, all tracked files are your working files and they can be located in the same project directory or in any folder within it.

Git operations
Figure 1 – Git States and Operations (Source: wikipedia)

Working files are staged when they are added for the first time or added after a change. The staging area is a cache or buffer which stores all files until you move them to the next state (the commit stage) or get them back to the previous state.

Commit state is like a local hard copy of your files, it can work as a backup but it is much more than that because Git allows you to compare changes between committed and working files. Each commit operation also includes a description (message) of what has changed since the last commit (or, what is the reason for this commit).

Note that Git does not simply store a new version of your files, it really store the differences between current and previous version, so you can track what changed and why it changed. All these information is stored within the .git directory, which is created when you initialize a new Git project.

A local repository can also be used to update or get updates from a remote one.

Getting Started with Git

The first thing to do in order to use Git is installing it (if your computer does not have it already). I am not covering how to do that, because you can easily find information on the Internet. Just keep in mind that I am using Linux command line here, so if you are using Windows, you will probably have to use a Git command line within a cmd box instead.

Also, before creating a project with Git, it is important to set some very important internal variables: your user name and email. These are important to distinguish who changed what within a distributed version control system.

So, let’s first set your user name and email using the following commands:

You can check your Git configuration with the following command:

Keep in mind that Git can have global configurations, system configurations (per user) and local configurations (per project).

Now that you made the basic Git setup, let’s make Git to be aware of a project we want it to track. All you have to do is to go to your project base directory and from there issue git init command:

Note that I just created a “gittest” directory within my “development” directory, then changed to that directory and started nano to type a very simple C program:

Once you save that program, you can use gcc to compile it, and test your newly test.o binary:

Now let’s instruct Git to track our small project (git init command):

Git replied that it successfully created a new empty repository there (a .git directory was just created within my project directory). You can do the same in any other existing project directory.

Now Git is set and will track anything that happens in our project. But as of now, no file is being tracked! We can check that by issuing a git status command:

Note that Git is reporting we are in our master branch and that there are two untracked files. It also states that we can track a file by issuing a git add command to track a file. In fact, you can track files by specifying each file name or simply instruct Git to track all files. But be careful! Our test.o file is not a text file and in fact it should not be tracked ever! As of now, we could simply issue a git add test.c command to track our C file, but what if we had a lot of files? Well, there is a way to instruct Git to ignore some files and it is by creating a .gitignore file! Let’s create a .gitignore file with just a single line:

Note that usually you are going to add all sort of object (*.o) and intermediate files (*.a, *.map, etc) to your gitignore!

Now we can issue a git add . command to add all files (except for those explicitly listed in the .gitignore file):

Note that now we have two files staged: .gitignore and test.c, test.o was left out and will not be tracked!

From here, we can work and change our test.c file (which would lead to a new changed file, with the same file appearing in the staging section as well) or we can commit our changes and create our first “hard copy” of the project. Note that as our project was not committed yet, we can unstage any file by issuing a git rm –cached command on the desired file.

Commit

Now that we have some staged files, it is time to commit them, this will save the current state of our project. We can do that by issuing a git commit command (which will open a text editor for inputting the commit message) or you can use git commit -m “your commit message” instead. The -m instructs Git to use the supplied message instead of ask the user for a commit message:

Congratulations on your first commit! We can issue a git status command to make sure everything is fine:

From now on, every time you modify your project you can commit the changes so they can be registered and stored by Git.

But what if I want to undo or change a working file to a previous state? This is when you can use git checkout command! Suppose you changed your test.c file and now you want to restore it to the way it was in our first commit. All you have to do is to issue a git checkout command:

Now if you check your test.c file, its contents will be exactly what it was in the last commit! Cool isn’t it?

You can also unstage files by using the git reset HEAD command. Suppose you have staged a file named test2.c and now you don’t want to include it anymore:

Git can also track file deletions. You can delete a file from Git by using git rm command (which deletes the file and stages that information) or you can delete a file using your file system and then staging that change on Git:

Or you could delete it on your file system and then stage that deletion:

Anyway, keep in mind that only after you commit those changes, they will be stored into your local repository!

Now as we continue working in our project, we are going to make more changes to it and commit them, suppose we added some code to our test.c.

In order to store the new changes we must add the file to the staging area and commit it:

Can you see I used git log in order to get a list of all commits we have? Each commit includes a hash hexadecimal number, who made the change, the date and time and also the commit message. That hash code can be used with a Git checkout command in order to switch to a previous commit as we will see later.

Branches

One of the very nice things about Git is the ability to create and work with branches. A Git branch is a snapshot of your project from which you can work without disturbing your main project. This is very useful when creating or testing new features on an existing project. You can get a list of available branches at any time by using the git branch command:

As of now, our project has only one branch, which is known as master branch. The star in front of it shows that we are currently in that branch. Let’s create a new branch (version2) to include a new feature:

Now we have to move our environment to that version2 branch by issuing a git checkout command:

From now on, all changes we do will affect only version2 branch, so let’s change our test.c code:

After changing our test.c file, we can type these commands:

Now if we look at our commit log, we will see this:

We can move back and forth between our version2 and master branch without loosing any piece of code:

Nice isn’t it?

Another usage of checkout is switching to a previous commit within a branch. Let’s suppose we wanted to switch to the first commit within version2 branch. First we can use git branch to check our current branch and then change to the desired branch with git checkout:

Now we can use git log command to list all commits within the current branch and then use Git checkout to switch to one specific commit:

Note that Git warns us that we are now in a “detached HEAD” state, meaning we have just created a new temporary branch. We can issue a git checkout -b command to create a new branch or just use this temporary branch as a sandbox to test any ideas!

If we issue a git log and a git status command we will get the following result:

But if we choose to create a new branch (instead of that temporary one), we could have something like:

Now we have three branches and instead of work in that detached branch we can now work in version3 branch.

But what if I don’t want to use that version3 branch anymore and I want to delete it? You can use git branch -d command:

Note that you can’t delete a branch while you are currently using it!

For now, let’s just return to our master branch:

 

Remote Repositories

Now that we know how to work with our local repository (or simply repo), it is time to learn how to deal with remote ones. There are several Git servers you can use and probably the first and most famous is GitHub. GitHub provides unlimited storage space for public repositories but charges for private ones. An interesting alternative to GitHub is BitBucket: it provides free storage for small teams (up to five users and up to 1GB per month) and allows both private and public repositories! I am going to use Bitbucket in this section but you can use whatever Git repository server you want.

The first thing to know is that in order to use a remote repository it must exist (or you can create it) and you must have access to change it. If you only need to copy it and use it as a starting point for your own project or repository, you can clone a remote repository. Let’s create a new repository in Bitbucket:

After creating the repo, you will get the following screen:

Now all you have to do is to copy your remote repository address (shown in the top right-corner of your repository overview) and instruct Git to use it as your remote repository with the command git remote: git remote add origin https://YOURUSER@bitbucket.org/YOURUSER/REPOSITORYNAME.git . And then issue a push command git push u origin master command to push data into the remote server. You are going to be asked for your bitbucket password and after a while, Git will reply with something like this:

From now on, every time you need to push new commits to your Bitbucket repository, you just have to issue a git push command:

And to pull from your remote repository you can use a git pull command:

Once we have up-to-date local and remote repositories, let’s add a README.md file, but this time, we will create it using Bitbucket’s web interface. Go to your repository overview and press the “create a README” button:

Just use the template from Bitbucket and save it. Now your remote repository should have a README.md file which is not present in your local repo!

In order to synchronize your local repo with your remote one, all you have to do is to issue a git pull command:

Our newly created README.md should be present within our project files:

But if we pay attention to our remote repo, we will notice something wrong: we only have our master branch, where is the version2 branch? The answer is simple: it is still in our local repo, but was not pushed along with master to our remote repo. That’s because our git push command only pushed our current (master) repository. In order to push all branches we must include the –all option: git push –all

That brings another interesting question: what if I wanted to delete a branch?

We already saw that we can use git branch -d to delete a local repository, but it won’t affect the branch on a remote repository. In order to do that, you must use git push origin :BRANCHNAME command. So in order to delete version2 branch in the remote repo we could issue:

That is it! I believe this is a good starting point for anyone wanting to learn and use Git.

References

Git Official Book

Git Tutorials and Training at Atlassian.com

Learning Git and GitHub at Lynda.com

Git Essential Training at Lynda.com

A quick Git guide for newbies
Tagged on:                         

Leave a Reply