A "quick" introduction to git
Tags: git
Published: 2017-07-10 20:53:09

---
cover_image: https://www.mourtada.se/wp-content/uploads/2016/01/1color-orange-lightbg@2x.png
title: A "quick" introduction to git
published: true
description: Getting started with git
tags: git
canonical_url: https://www.mourtada.se/quick-introduction-to-git/
---

I wrote [this post](https://www.mourtada.se/quick-introduction-to-git/) a while back when learning git. If you like my writing take a look at [my blog](https://www.mourtada.se/).


One of my goals of the year is to contribute to an open source project. Now why would I want that? I think that it will make me a better developer and it could be a fun way to do something differentÂ than stuff I do at work. At the same time i use open source software in every project I use and now it's time to give something back. The biggest source for open source software is GitHub and I realized that i needed to learn more about git. This post is my take at learning git and I hope it will be useful for you to.
<h2>What git is</h2>
Git is a Distributed Version Control System(DVCS). It was created in 2005 by the Linux community. The goals was mainly that is was going to be fast, fully distributed, have strong support for non-linear branches and could be used for large projects.

As opposed to a Centralized Version Control System(CVCS) like Subversion or Team Foundation Version Control(TFVC) git is fully distributed. What it means is that everyone has it's own fully fledged version of the repository which is isolated from the others. You can do your work, making branches and commit without connecting to a central repository. But then when you're ready to share your changes with others you can just push the changes you've made to a central repository or if you want the latest changes someone else has committed just pull them down to your own.
<h2>Three states</h2>
Git has tree states where your files can reside and this is the very core in git so this is very important to remember.
<ul>
	<li>Modified</li>
	<li>Staged</li>
	<li>Committed</li>
</ul>
When a file isÂ changed it will be marked as modified but it will not be committed. The only files that will be committed are those which has been staged. When you stage a file it will be staged as is at that moment. If changes are made after staging the file those changes will not be committed. When files are committed they are safely stored in the repository and is under version control.

<img class="size-full wp-image-365" src="//www.mourtada.se/wp-content/uploads/2016/01/git-areas.png" alt="The three areas of git. Working directory, Staging area and repository" width="806" height="360" />

The three states areÂ connected to the tree areas
<ul>
	<li>Working Directory</li>
	<li>Staging Area / Index</li>
	<li>Repository</li>
</ul>
The working directory is the place where you work with your files.Â When you want to store the changes the first thing you need to do is getting the files to the staging area so git knows what to commit. Now that the files are staged those files can be committed. Remember that if there are changes that hasn't been staged those files will not be committed.
<h2>How changes are stored</h2>
A big difference between git and other Version Control Systems(VCS) is that it stores snapshots and not the difference since the last commit. If you change a line in a file git takes a full snapshot of that file and stores it with the commit.Â To be efficient git doesn't take snapshots of files that hasn't been changed but instead reference the last snapshot of that file.Â The way git stores files and together with that the repository is local is what makes git so fast.

Every commit is checksummed based on itsÂ contents(files and directory structure) using SHA-1 hash. This makes git aware of corruption so you don't loose any information. The checksum is then used as reference and will be the unique id for that commit.

<img class="wp-image-404 size-full" src="//www.mourtada.se/wp-content/uploads/2016/01/commits-1.png" alt="A series of commits" width="557" height="47" />

When a commit is made it stores the snapshots together with some metadata such as a commit message, time, author, etc. One important thing that also is stored is a reference to one or more parent commits. A commit doesn't have a reference to a child commit. This is very important to remember when we'll start to talk about undoing and looking at history.
<h2>Getting started</h2>
The first thing you need to do is getting git on your system. <a href="https://git-scm.com/downloads" target="_blank">Get it here</a>. The rest of the post will use git onÂ the command line but there is a built in GUI tool that can be run with "git-gui" and several third-party tools that is platform-specific.

After installing git we need to setup some first time configuration. Git has both global and repository specific settings. Your name and email will be associated with each commit and needs to be set.

<code>git config --global user.name "Jonathan M"
git config --global user.email jonathan@example.com</code>

If you don't want this name or email to be used in every repository i can just omit the <code>--global</code> parameter and it will only be used in the current repository. There's a lot more settings that can be changed like the default editor to be used. But this will be enough for now.
<h2>Getting your repository</h2>
The <code>git init</code> command will create an empty repository in your current directory and a subdirectory called <code>.git</code> which is the skeleton of the repository. The <code>.git</code> folder holds data about your repository such as configurations files, all your commits, branches and tags.

With <code>git init --bare</code> git creates a bare repository which doesn't create the <code>.git</code> folder as a subdirectory instead all information is stored in the root folder. A bare repository is not meant to do development work in because it doesn't have a working directory. The purpose of a bare repository is to be used as a central repository and just store changes without being abled to edit them. The naming convention for a bare repository ends with ".git" e.g "my-repo.git"

More often the repository is already created and you want to get it locally so you can look and start working at it.

<code>git clone https://github.com/Jontem/my-central-repo.git</code>

copies my-central-repo(in this case hosted on GitHub) to my current directory. My new local repository is now it's own repository fully isolated and a complete copy of the cloned repository. When a repository is cloned it creates a remote connection called origin pointing back to the original repository. Every change that is made in my local repository is not reflected to my central repository unless I push my changes back.

<img class="size-full wp-image-388" src="//www.mourtada.se/wp-content/uploads/2016/01/cloning.png" alt="Cloning a repository" width="446" height="122" />
<h2>Saving your changes</h2>
When you have something that you want to commit the first thing to do is to stage your changes. TheÂ <code>git add</code> commandÂ adds files orÂ directoriesÂ to the staging area/index.

You either specify which file

<code>git add &lt;file/directory&gt;</code>

or use

<code>git add .</code>

to specify all files(that is not your <code>.gitignore</code> file). An asterix can also be used to select multiple files e.g <code>git add *.txt</code>. The files in the staging area are those that will be committed. As mentioned before the add command is not just for adding a new file to the staging area but also for adding changed files.

Remember that when a file is added to the staging area git will take a snapshot of the file as is at that moment. If you change the file after that, you will have to stage those changes by running <code>git add</code> again.

To commit files in the staging area run

<code>git commit</code>

This will open an external editor so a commit message can be added.

<code>git commit -m "changed the readme"</code>

With the <code>-m</code> parameter a messageÂ can be addedÂ directly from the command-line.

<code>git commit -a</code>

If you thinkÂ the staging part is an unnecessary stepÂ there is the <code>-a</code> parameter which adds all previously added files in the repository that has changed to this commit.
<h2>Examine yourÂ repository</h2>
To see how your working directory, staging area and committed changes compare to each other git has the status command.

<code>git status</code>

With the status command this informations is presented
<ul>
	<li>Which branch you are on</li>
	<li>Changes to be committed</li>
	<li>Changes not staged for commit. To commit these files run git add</li>
	<li>Untracked files. Files that are not tracked by your repository. To commit these files run git add</li>
</ul>
Inspecting the history of a repository can be done with

<code>git log</code>

It shows you all commits in the repository with the latest commits at the top.

<code>git log -p -2</code>

This shows you the latest two commits and a diff view every change a commit has introduced.

<code>git log --stat</code>

The <code>--stat</code> parameter will only show which file have changed.

<code>git log --pretty-format="%h - %an, %ar : %s"</code>

To customize theÂ output to your needs you can make use of the <code>--pretty-format</code> parameter.Â Run <code>git help log</code> for available placeholders.
<h2>Branching</h2>
Branches are very easy and fast to create in git. They are just a pointer to a commit which is very different from centralized version control which will do a full fetch of all your files to a new directory structure. When a branch is checked out your working directory will immediately reflect those changes.Â Because gits distributed nature a branch can exist only in your local repository without needing to push that branch to every repository. Because of that it's very common to branch in git whenever you work on a new a feature, fixing a bug or some other change.

<img class="wp-image-405 size-full" src="//www.mourtada.se/wp-content/uploads/2016/01/branches-2.png" alt="Branches are just pointers" width="538" height="311" />

In git there is a special pointer called HEAD. Whatever HEAD points to is the whatÂ will be reflected in your history and working directory. In other words what HEADÂ points to is what branch you are on.

<code>git branch</code>

The branch commandÂ lists all your local branches. With <code>--remote</code> or <code>-all</code> you list only remote or local and remote branches.

<code>git branch feature</code>

Will create a new branch called feature from the current branch latest commit

<code>git checkout feature</code>

This will change the branch to feature by pointing HEAD to the branch pointer for feature.

<code>git checkout -b feature</code>

The <code>-b</code> parameter will create a new branch from the current branch pointer and then switch to the newly created branch.

<img class="size-full wp-image-408" src="//www.mourtada.se/wp-content/uploads/2016/01/HEAD_before_checkout.png" alt="HEAD points to master" width="538" height="406" />

<img class="size-full wp-image-407" src="//www.mourtada.se/wp-content/uploads/2016/01/HEAD_after_checkout.png" alt="After git checkout feature. HEAD now points to feature" width="538" height="406" />

The checkout command can also be used to look at a specific commit. One use case could be that you want to see if a bug existed last week. First get the commit id and then use

<code>git checkout 318bf</code>

This will put you in "detached HEAD" state. It will happen when you point HEAD to a specific commit instead of a branch pointer. When in detached HEAD you can look around and make experimental changes.

<img class="size-full wp-image-411" src="//www.mourtada.se/wp-content/uploads/2016/01/Detached_HEAD.png" alt="Detached HEAD" width="538" height="311" />

But this can also be dangerous because there is no pointer pointing to the new commits. Remember that a commit only has a reference to one or more parents. In a normal branch git has a branch pointer which always has a reference to the latest commit. But in "detached HEAD" only HEAD points to the latest commit. When git checkout master is used again to get back to master you will no longer have a reference to your new commits that you did in "detached HEAD". There are ways to get those changes back like <code>git reflog</code> command. If you want to do changes or have done changes in "detached HEAD" just create a new branch directly to safely save your change by invoking

<code>git checkout -b my-new-branch</code>

Checkout has a third use case when using it with a file as a parameter.

<code>git checkout HEAD -- myfile.txt</code>

This will overwrite the file in yourÂ working directory with the version of that file from the latest commit and it will not update what HEAD points to which means you're still on the same branch. The "--" parameter tells checkout that this is a file operation. It's not a required parameter but it prevents to mistakenly do a branch operation if there is branch with the same name as the file.
<h2>Merging and this thing called rebasing</h2>
Merge and rebase is two commands which has the same goal and that is integrating changes from one branch into another branch. The big difference is how they do it. To merge first switch to the branch which you will be merging into. In our example were on the master branch. Then run

<code>git merge feature</code>

This command merges master into feature and this will create a new merge commit. Merging is non-desctructive operation and it combines the history from both branches with new merge commit thas has two parents.

<img class="wp-image-414 size-full" src="//www.mourtada.se/wp-content/uploads/2016/01/merging.png" alt="The result of merging feature into master" width="859" height="406" />
If the master branch is very active and you have to merge often the history will contain lots of merge commits. To avoid this you can use rebase.

Rebase alters the history of your repository it does so by stuffing away your commits and then adds the commits from the other repository and then replays your commits last.

<code>git rebase master</code>

Sets the base commit which the current branch is based on to masters latest commit. A trick to remember what rebasing does is to pronounce the "re" in rebasingÂ for yourself.

<img class="size-full wp-image-417" src="//www.mourtada.se/wp-content/uploads/2016/01/before_rebasing.png" alt="Before rebasing master onto feature" width="708" height="406" />

<img class="size-full wp-image-416" src="//www.mourtada.se/wp-content/uploads/2016/01/after_rebasing.png" alt="After rebasing master onto feature" width="992" height="406" />

After this rebaseÂ your feature branch starts where master is now. This has the benefit of a much cleaner history because it eliminates the merge commits and creates a perfect linear history.

The important thing here is that your stuffed away commits will get new id:s and this will change the history. The "golden rule of rebasing" is to never rebase commits that has been shared with other developers because it changes history and this can put you in a situation which can be cumbersome to sort out.
<h2>Undoing things</h2>
The reset command is used for resetting your HEAD to a specific state.

<code>git reset HEAD~1</code>

This will move HEAD one commit back. A specific commit id canÂ also be specified. This is another command which important to only do on changes that hasn't been shared with others, because it changes history. Reset has three important parameters which changes how resetÂ works.
<ul>
	<li>--soft -Â This will move the branch pointer that <span style="font-weight: bold;">HEAD is referencing </span>to the commit specified. <span style="font-weight: bold;">But nothing else</span>.</li>
	<li>--mixed --Â This will move the branch pointer that HEAD is referencing to the commit specified <span style="font-weight: bold;">and update the staging area/index to reflect that commit</span>. <strong>This is the defaultÂ parameter</strong></li>
	<li>--hard -Â This will move the branch pointer that HEAD is referencing to the commit specified, update the staging area/index <span style="font-weight: bold;">and overwrite the working directory to reflect that commit.</span> Be aware that this will reset every tracked fileÂ to that commit. Unlike git checkout you will not be notified that you have unsaved changes and to undo this you will have to run "git reflog" because you will have unreferenced commits.</li>
</ul>
Reset can also be useful when you want to combineÂ commits and make one commit of all changes. For instanceÂ running

<code>git reset --soft &lt;commit&gt;</code>

will reset the history but leave the index and working directory untouched. Then run

<code>git commit -m "my new commit"</code>

and you will have cleaned up and merged your previous commits to one single commit.

<code>git reset HEAD &lt;file&gt;</code>

Reset also makes it possibleÂ of resetting files. If supplying a file as a parameter git overwrite the file in the staging area withÂ the file from HEAD. If the file is not in HEAD it will unstage the file. When using reset with files git does not move the branch pointer.

TheÂ revert command is different from reset because it will create a new commit without the reverted changes. This is a safe way to undo changes because the history will not be changed. This should always be the choice when the branch is public.

<code>git revert HEAD~2</code>

will revert the changes that the second latest commit made and create a new commit. Unlike reset, revert has no file operations

Sometimes when you made a commit you realized that you forgot something or you the commit messageÂ wrong. With

<code>git commit --amend -m "my new message"</code>

git will replace the latest commit with a new one. Remember to stage new files that you want to add before committing.
<h2>Working with remotes</h2>
<code>git remote</code>

shows your connections to other repositories. It can be a central repository for your team or colleagues repository. A remote consists of a url(HTTP/HTTPS or SSH) and a friendly name to use in your commands. With git remote you can add, rename or delete a remote.

<code>git fetch &lt;remote&gt; &lt;branch&gt;</code>

gets all commits from the remote repository and stores them locally. Fetching is a safe to see whats been changed in a remote repository and it will not affect your current work. You can review the repositoryÂ with

<code>git checkout origin/master</code>

Think of a remote branch as read-only and when you do a checkoutÂ it will put you in detached HEAD. To list all remote branches use

<code>git branch -r</code>

Another way to see whats changed between a remote branch and local branch is to run

<code>git log master..origin/master</code>

It will show you all commits that has happened in origin/master that is not in master. If you change position of the branches it will show what's in master but not in origin/master. It'sÂ also possible to show the file difference with

<code>git diff master origin/master</code>

<code>git pull origin</code>

is a shorthand for a fetch and merge on the current branch. Git pull can also use rebasing instead of merging by specifiying <code>--rebase</code>

<code>git push origin master</code>

does the opposite of fetch it pushes your local changes to the remote branch. By default git only lets you push when a fast-forward merge is possible. A fast-forward merge is when nothing has changed in the remote branch since you started and you have all those changes locally. To force an update that will overwrite the remote branch with your local branch use "--force". Forcing a push is not something that should occur very often cause it will change history. One thing to be aware of is that tags is not automatically pushed.

<code>git push origin master --tags</code>

will push your local tags.
<h2>In conclusion...</h2>
I hoped you enjoyed and learned something from this post, I know I certainly have. The post became a lot longer than I planned but I felt it was necessary and even now i have left some parts out. If you like the post or see something that is incorrect please leave a comment below.