You don’t know GIT!?

Currently GIT is a relatively common tool for anyone working in the industry. For those of you who have never worked with it. Learn it. Seriously. It isn’t going away, and SVN is half way out the door.
 
Git can be confusing, there is a concept of local and remote repositories. Then there is branches, and forks. The easiest way to get started is to dive in. Rather than describing every possible option from each step. I am just going to move forward through it and explain the steps im actually taking.
 

[github@centos6-box-1 git]$ git clone [email protected]:jayninja/some-test-repo.git
Initialized empty Git repository in /home/github/git/some-test-repo/.git/
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
[github@centos6-box-1 git]$ cd some-test-repo/
[github@centos6-box-1 some-test-repo]$ ls -l
total 4
-rw-rw-r-- 1 github github 16 Jun 30 20:04 README.md
[github@centos6-box-1 some-test-repo]$ 

 
git clone – is by far one of the most commonly used commands. It clones a remote repository to your local machine. From here you can manipulate/build the code. The http url to the repository will work if you have no intention of commiting code back to the repository. Otherwise, if you have auth setup with an ssh key, you need the ssh style git url.
 

Make a branch

[github@centos6-box-1 some-test-repo]$ ls -l
total 4
-rw-rw-r-- 1 github github 16 Jun 30 20:04 README.md
[github@centos6-box-1 some-test-repo]$ git branch
* master
[github@centos6-box-1 some-test-repo]$ git branch development
[github@centos6-box-1 some-test-repo]$ git branch
  development
* master
[github@centos6-box-1 some-test-repo]$ git checkout development
Switched to branch 'development'
[github@centos6-box-1 some-test-repo]$ git branch
* development
  master
[github@centos6-box-1 some-test-repo]$ 

 
It is considered bad form to work out of the “master” branch. Master is supposed to reflect your stable, and tested code that is already released. So, when you check out code, you should create a branch to work out of. When you hear branch, think of a tree, you are created a branch of the code you checked out. This lets you work on it, and still have it commited under source control, but without necessarily having to merge it in with what you consider stable.

If we call “git branch” with no parameters, it lists the branches we know about locally. The asterisk next to the branch name is the one we currently have checked out. In other words, if I make changes, it will still be happening on my local copy of master until I “git checkout” the branch I intend on working on.
 

Commit some changes, and push.

[github@centos6-box-1 some-test-repo]$ ls
README.md
[github@centos6-box-1 some-test-repo]$ echo "I am a new file" > newfile.txt
[github@centos6-box-1 some-test-repo]$ ls
newfile.txt  README.md
[github@centos6-box-1 some-test-repo]$ git add -A
[github@centos6-box-1 some-test-repo]$ git commit -a -m "added a new file"
[development 0d5b8c1] added a new file
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 newfile.txt
[github@centos6-box-1 some-test-repo]$ 

 
After creating a new file, I needed to tell git to include it as part of the repository. You can list individual files with “git add $filename”, or you can just add everything new with “git add -A”. Now that we’ve added it, we can commit our changes. If you dont add it, git will not save changes to it, or push it to the remote repository. The commit is what will show in the history, so give short but helpful descriptions.

The change is commited to our local version of the development branch. If you look in github, you won’t see your changes. Why? Because we commited it to our local version of the repository. We still need to “git push” it up to the remote repository for it to show in github.
 

[github@centos6-box-1 some-test-repo]$ git push origin development
Warning: Permanently added the RSA host key for IP address '' to the list of known hosts.
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 293 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To [email protected]:jayninja/some-test-repo.git
 * [new branch]      development -> development
[github@centos6-box-1 some-test-repo]$ 

 
Notice that only now would you be able to find your new branch in github, because along with newfile.txt, we also pushed the creation of the development branch from our local repository to the remote repository(github). While specifying the origin is not always necessary, its a good habit, and helps to avoid accidents. I recommend you always specify an origin.
 

Merging

Currently, our changes are currently “pushed” into the development branch. We have tested it, and are ready to merge it back in with the master branch. How do we do that? Proper form for a development team is to submit a pull request from github, and then ask a teammate to review it, and merge it if they approve. Unfortunately using the git command line, you cannot do this. There is another tool called hub that fills this void. But for the sake of this tutorial I will skip this and go into merging it ourselves without a github pull request.
 

[github@centos6-box-1 some-test-repo]$ git branch
* development
  master
[github@centos6-box-1 some-test-repo]$ git checkout master
Switched to branch 'master'
[github@centos6-box-1 some-test-repo]$ git merge --ff-only development 
Updating ecccfa6..0d5b8c1
Fast-forward
 newfile.txt |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 newfile.txt
[github@centos6-box-1 some-test-repo]$ git push origin master 
Total 0 (delta 0), reused 0 (delta 0)
To [email protected]:jayninja/some-test-repo.git
   ecccfa6..0d5b8c1  master -> master
[github@centos6-box-1 some-test-repo]$

 
Merging was a bit counter-intuitive to me. When I first started looking at it, I felt like I was supposed to push the code from my working branch to the branch I wanted to merge it with. Thats actually not the case. Instead, the right way to think about it is, I am pulling the code I want to merge in.

Our goal is to merge this code from development to master. We currently have development checked out. But we need to checkout master instead, so we can pull our changes from development to merge it. Once we “git checkout master”, we need to merge the repository we were changing. Now since all of this is only happening on our local version of these branches, we need to now push our merged code back to github(remote). So we specify “git push origin master”. I used –ff-only because I dont want a commit, specificly because I merged it in the history, I also wanted it to fail if another developer merged their code into master without merging it into development(so we can go ask them why they would commit such a sin).
 

Extra

Inevitably you will accidentally start working under the wrong branch. The way to resolve this, assuming you have not commit yet, is to stash your changes.

[github@centos6-box-1 some-test-repo]$ git branch
  development
* master
[github@centos6-box-1 some-test-repo]$ echo "working under the wrong branch" > my_new_file
[github@centos6-box-1 some-test-repo]$ ls
my_new_file  newfile.txt  README.md
[github@centos6-box-1 some-test-repo]$ git add -A
[github@centos6-box-1 some-test-repo]$ git stash
Saved working directory and index state WIP on master: 0d5b8c1 added a new file
HEAD is now at 0d5b8c1 added a new file
[github@centos6-box-1 some-test-repo]$ git checkout development
Branch development set up to track remote branch development from origin.
Switched to a new branch 'development'
[github@centos6-box-1 some-test-repo]$ git stash pop
# On branch development
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	new file:   my_new_file
#
Dropped refs/stash@{0} (0befaddb46b533632c5df4a668122f8a16a440e5)
[github@centos6-box-1 some-test-repo]$

 
First, we made sure we were in the wrong branch. Then we created a new file, and “git added” it to our project. We could have done this by name, but it was the only new file, so I cheated and used “git add -A” to add everything. Next, we “git stashed” our changes. In other words, we temporarily saved the changes on a stack of unfinished work. Then we swapped to the correct branch using “git checkout”, and popped our changes off the stack(“git stash pop”). From here the next step would be to “git commit” and “git push origin development”.

This can also be used to save half finished work, that you don’t necessarily want to commit yet. Just remember, it lives local, so don’t delete your local checkout.
 

Conclusion

Git can be as complicated or as simple as you choose to make it. If you are doing something scary in git, just back the directory up. Worst case scenario you delete the repo and recreate it using the backup you took. If you dive right in, you will find you float a lot faster than you think. But don’t get too cocky, the rabbit hole goes much deeper.