Now that I work with Git on a daily basis at my new job, what I’ve learned while using it has made it pretty clear why its currently the runaway favorite in the open source community. Its just great! I know that this realization is a little “late” and I can openly admit that I’ve been completely in the dark when it comes to Git and GitHub etc. Better late than never though! In an attempt to get a few friends on board, I thought I’d write this post.
The focus of this article is two-fold: to introduce the concepts of Git to people totally unaware and to help them get started using it in a Windows environment.
Git Hosting Providers – GitHub vs BitBucket (et al)
Finding a good hosting provider for your repos (repositories) might seem pretty obvious right? GitHub duh! Not quite. Yes, GitHub is great for the open source community (because public repos are free) but if you want to host anything private, even a single project, its going to cost you. About 7 bucks a month (as of the time of this writing). BitBucket supports private repos with their free accounts! On top of that, BitBucket is powered by Atlassian – creators of Confluence and JIRA! I’m really not looking to advocate either to be honest – just free private repo hosting is pretty darn enticing! (For what its worth, we use GitHub Enterprise at work.)
Either way, whichever you decide, the remainder of this article really isn’t too specific to either. So go create an account on either site!
Software Time – What you’ll need
So, to get started actually using Git, you’re going to need it installed on your machine. When I first started, the obvious choice was to just install the GUI that GitHub offers. To be honest, it sucks. A lot. It made my first experience with Git in general very unpleasant and just way too confusing. The solution here is to not use a GUI at all (yet), but just install Git raw and directly and use the command line. Yep… I’ll let that sink in for a few to you Windows folks. Good ‘ol DOS prompt!
Here are instructions on installing Git and any tricky steps along the way that you might run into. Don’t worry, its easy:
https://confluence.atlassian.com/display/BITBUCKET/Set+up+Git+and+Mercurial
(complete steps 1 & 2 – don’t worry about the Mercurial stuff)
Some terminology to get used to before we start
Here are some common terms and concepts that you will deal with when working with Git:
- repo/repository – this is the base for your code. It’s home. Whenever you clone a Git repo, a complete copy of the source code resides on your machine.
- clone – make a local copy of a Git repo.
- branch – think of a branch like an alternate timeline that you can make some changes to your code in. These changes are kept completely separate from the “master”. So if you decide you don’t like what you did, or you just totally broke everything experimenting – you can delete your branch and the master branch (master/original version of your code) remains untouched and safe and sound.
- master – the “main” branch of your repo. This is where the original source code resides. You typically branch off of master to add features or fix bugs.
- “feature branch” – usually reserved for whenever you create a new branch because you are adding a new feature or fixing a bug.
- checkout – the act of changing branches. Once you create a new branch or want to switch between branches – you checkout the branch name.
- working directory – this is current state of the files on your local branch in a repo.
- staged/staging – work that you have done and files that you have changed, that you want to commit, are first added to the staging area. You use the staging area to build your commits.
- stash – temporary area to hold changes when you aren’t ready to commit them yet, but need to change branches (you can’t change branches if you have modified files in your working directory that aren’t staged or committed)
- commit – A commit is like a checkpoint in your development. It is when you say “the code I have written looks good, works well, is concise and specific and to the point. I’m going to commit it now and continue working or be done.”
- “origin” – Origin is reserved for the version of your repo that is on the remote server.
- push – When you are happy with your code, and you’ve performed a commit – you then need to push those changes back up to the server. (Technically you can push code up to origin at any time, not just with a commit etc.)
- merge – Once your code is good to go and you are happy and finished with your branch, it needs to be merged back to master
- pull request – When you push a branch up to a remote origin, and you want that branch merged with the remote’s master branch – you submit a Pull Request. A pull request contains all of your commits and a line by line breakdown of all the files and code that you changed. Typically review feedback will occur in a Pull Request. (FYI you can merge without a pull request.)
- fetch/pull – get the latest version of the code from the remote version of the repo (i.e. other peoples code that have been merged to origin’s master, other peoples branches, etc)
Diving into the command line and your first repo!
First things first – the command line – like I said, get used to it! It’s really not that bad and once you get the hang of things you’ll understand why its so important and easier than dealing with a GUI – but don’t worry too much. I will get into using GUI tools that actually make dealing with commits a little easier (a little later).
The first thing you want to do is launch a bash shell – this is a little different than a regular DOS command prompt. The bash shell is basically a Unix command line – and a lot of the standard Unix bash commands will work in the Windows version here. To launch your bash shell simply launch Git Shell (that should have been installed from the step earlier in this article). Once launched you should be prompted with something like:
Run ‘git help git’ to display the help index.
Run ‘git help command’ to display help for specific commands.
jkat98@HOME-PC ~
$_
****At the prompt, type: ls [Enter] – you should get a listing of the files in the current directory.
A few simple bash commands:
- cd (change directory)
- ~ is your home dir so cd ~ will go back there
- cd .. will go back a level
- ls (list)
- mkdir (make a directory)
- rm (remove)
- rmdir (remove directory – must be empty)
- mv (move), etc.
OK lets clone our first repo!
First create a directory to store your repos in – you can call it whatever you want but for the purposes of this article I’ll call it ‘repos’:
$ mkdir repos
$ cd repos
Now that you’ve created the repos folder and are in it (as your prompt should show via “~/repos”) lets pull down a copy of a remote GitHub repo by cloning it:
$ git clone https://github.com/jkat98/AnimationFramework.git
You should see some information about cloning, remote counting objects, unpacking, etc which basically means it just connected to GitHub, downloaded a package of the repo, and extracted it to your local file system:
$ ls
You should see a new folder for ‘AnimationFramework’. Lets take a look:
$ cd AnimationFramework
$ ls
(quick tip, bash supports auto-completion so you can type: cd Ani TAB and it will make an educated guess as to what you probably meant and auto finish if it can. In this case AnimationFramework was the only thing in your repos folder that started with Ani so it was an obvious choice.)
Something to note – your prompt changed a bit. Once you enter a directory for an actual repo – your prompt will display the current branch for that repo as well as its status (if any files are modified and unstaged – i.e. work still needs to be committed, or if its a clean working directory, etc)
A note about the master branch:
Yes, you can start editing files right away and work exclusively off of the master branch. But that’s not a good idea – it will create all kinds of headaches when you try to undo any of your changes and any remote repos that you work with are going to hate you because you are trying to push changes directly to master (which is a no no). Just get into the habit early and don’t work off of master.
Before we make any changes to any files, we should create a new branch first. There are 2 ways to do this:
$ git branch my-new-branch-name
$ git checkout my-new-branch-name
– or –
$ git checkout -b my-new-branch-name
The difference is that the first one will simply create the branch but do nothing with it. Typically you want to checkout a branch after you create it so the second command will actually create a new branch (with the -b flag) and check it out immediately after). After you have your new branch created and checked out, you will see that it is reflected in your prompt. I created a branch called ‘bug-fix’ so my prompt says:
jkat98@HOME-PC ~/repos/AnimationFramework (bug-fix)
$_
Now if we edit a file or make any changes at all, those changes will be saved as a separate copy from master. So we can rest easy knowing that we can break stuff and experiment to our hearts content and sleep sound knowing that the master source code is safe. Lets try that now. Type the following commands:
$ vi README.md
This will launch VI, which is like Unix’s version of Notepad, except via the command line, and its on crack and speed. (You can’t tell by looking at it – but its an extremely powerful editor.) If you want, feel free to skip this part and edit the file however you want, any editor or IDE you like, using Windows Explorer or Visual Studio or whatever. If you want to continue to make a quick edit with VI do the following:
- Press the letter i (which enters you into INSERT mode, as the bottom should reflect)
- Hit enter to insert some lines, then arrow up and type some stuff “I edited the file!” or whatver.
- Press ESC key (INSERT mode will go away)
- Type : (colon – enter COMMAND mode)
- Type wq then Enter (write the changes to the file then quit)
Lets review:
- You cloned a copy of a remote repo from GitHub
- You created a new local feature branch in the repo
- You edited a file while you had that new feature branch checked out
Now, lets take a look at our current status in Git (as far as its concerned):
$ git status
You should see a bunch of stuff about On branch bug-fix, Changes not staged for commit, etc. Then a red line that simply says modified: README.md. This basically means that our working directory now contains a file that has been modified, and its red because the file is not ready to be committed yet. That is, its not staged its just sitting in the working directory, edited, waiting for you to do more work or make a decision on how to handle this file with regards to what happens next. The last line says “no changes added to commit (use “git add” and/or “git commit -a”).
Adding files to the staging area before a commit
Whenever you make any changes to a file on the file system in a repo – you are always working towards building a commit that you will eventually push back up to origin/master (or just merge with your local master). The only way to actually do a commit, is by taking files that you’ve modified and adding them to the staging area. Think of the staging area as a temporary holding area before the changes are saved as a commit. So, you’ve edited like 5 different files, but you really only want to commit one. So you just say “hey you, that one file I changed that I want to commit because you have good code. You stand over here and wait a second. The rest of you files stay where you are I’m not ready for you yet.” Then you tell the staged file “Ok you’re good to go. Go, be committed, and enjoy your life with the master branch and the rest of the awesome production ready code!”
As of right now, our README.md file is in a modified state but its not staged or ready to be committed. Lets stage it because we like our change and we want to make a commit:
$ git add README.md
$ git status
(note: you’re gonna be typing git status a lot! Also note: in this case we only edited 1 file. But if you had a bunch of files and you wanted to stage them all that would be a huge pain in the butt. To add every modified file to the staging area simply execute ‘git add .’ instead!)
Now the modified status of the README.md file is green. Also note the top that says “Changes to be committed”. Let’s try something else that might break your mind a bit. Lets see what happens if we make another edit to the README.md file:
$ vi README.md
(press i, then make a change, then esc, :, x)
$ git status
What the?! It appears that our file README.md appears as both modified and ready to be committed (green) and not staged for commit (red)?? Remember that Git really just tracks changes, not files. So the changes that we originally made to the file are what were staged and ready to be committed (not the file). So an additional round of changes are waiting to be staged for a commit. At this point you could commit the original changes, or you could add README.md again (and then both changes would be made in the same commit – effectively appearing as if you made both changes at the same time.) Lets add the file again:
$ git add .
The README.md file is now staged and ready for a commit! So lets commit it:
$ git commit
That launched VI again (note don’t worry, if you hate VI and you probably will, you can change your default commit editor to something like Notepad or whatever you want via your .gitconfig file) with a bunch of commented out lines. The commit will actually happen once you’ve created an actual commit message and saved it. So, press the i key, then type a message “Edit readme with updated text”, then press ESC, then colon (:), then wq and Enter.
You should see a small status update from Git with your branch, some hash value, and your actual commit message. Then below that 1 file changed, 2 insertions(+) (which may vary depending on what you actually did to the file when you edited it.
$ git status
Yay! Git reports that we are still on our bug-fix branch, but that theres nothing to commit and our working directory is clean! Nice job! You’ve just made your first Git commit! The only thing left to do is push that change back up to the remote version of the repo. To do this we are actually going to push the whole branch up:
$ git push origin bug-fix
(Remember origin is the default name for our remote version of our repo, push is the command to, you guessed it, push and bug-fix is the branch that we want to push up.)
When you hit Enter, you should be prompted for your GitHub username and password. At this point, it will probably fail because you don’t have access to just push branches up to my repo. To test this functionality, you should create a quick repo of your own via GitHub, add a quick readme file using the GitHub interface. Then clone a copy of your own repo and experiment with all of the steps we just did so far!
If you dig deep, you will notice that when you modified the README.md file earlier and commited the changes – you really only commited the lines of code that were changed, not the whole file. This is what makes branching so great – you are never really working with files and copies or anything like that. You are always working with changes!
Pulling down changes/updates to the remote repo
Typically when you are working on a team, and you are all working off the same remote repo, people are going to be pushing their own branches and commits up throughout the day. The beauty of Git is that it handles that stuff great!
$ git checkout master
$ git pull
You should get into the habit of doing git pull’s often so that you are always pulling down peoples changes. More importantly though, you should only be executing git pulls from the master branch (git checkout master) so that you are only downloading “production ready” code and not peoples experimental branches and/or code that could be broken for you.
Using a GUI to do multiple commits
Inevitably you are going to be in a situation where you work on a feature branch and do a significant amount of work (more so than just editing a README.md file). In this event, staging and managing your commits (of which you will have many) is going to get pretty cumbersome via the command line. There is a great free tool called SourceTree that will visually show you all of the files in your working directory that are modified but unstaged. In addition, it will show you the diff (difference between the file version in the current branch and master) of each of those files.
Download and install SourceTree
So lets say you created a feature branch, and in this branch you added a new feature to your website. Perhaps you installed a new widget on the homepage, which consists of some HTML, some related JavaScript, and some CSS to make it look nice. In theory, this should pretty much be 3 separate commits. The idea behind commits is that they should be a single thought or change. When you word your commit message, you should be able to do so within 50 characters and without using the word “and”. So if you find that you are preparing a commit message and you are writing something like “Update HTML in header and footer” then you should break it into 2 commits. What if you want to do 2 separate commits on the same file? How can you do that since you always stage the entire file before a commit? Well with Git, you can actually stage “hunks” of a file, a single line or lines, as well as the entire file (or files) itself. This is easiest to demo using SourceTree.
When you first launch SourceTree you’re going to need to add the location of any repos on your file system. To do so, click the icon in the lower left corner for Add Repository. From the popup window, you can actually do a lot of the stuff we just did via the command line. For our purposes, we just want to point to the existing directory so change to the “Add Working Copy” tab and then browse to the path for the AnimationFramework in your home/repos directory. Then click the Add button and you will see your repo in the left column along with any branches that exist underneath. Double click the repo to select it. The window should now basically be divided into 3 columns – the left that shows your repos and branches, the middle that shows Staged Changes and Working Copy Changes, and the right that will show the diff of any files that you select in either Staged or Working copy.
Right now you probably aren’t seeing much of anything since we just staged, committed, and pushed our branch up to origin. To get a better feel for how we will work with SourceTree, go ahead and make some edits to the README.md file again. Do a bunch so we can split those changes into multiple commits.
Back in SourceTree, make sure to select the README.md file in the Working Copy Changes section. Once selected, you will see on the right the diff of that file (compared to master). Any lines that you added or modified will appear green and any lines that you changed or removed will appear red. What will wind up getting committed is the combination of both those colored lines (so the red lines will be removed and the green lines will be added). At the very top of the diff of README.md are 2 buttons for Stage File or Discard File. Discard file will reset all of the changes to that file back to their original version. (The version that existed prior to you making changes – that could be identical to master or the version since your last commit.) Stage file is the exact same thing as “git add README.md”.
We want to break our changes into 2 commits though. To do that, we can either stage a hunk or selected lines. A hunk is just a logical block of code that Git determined probably belongs together (i.e. consecutive lines of changes or changes near enough to each other). If you edited a portion of code at the very beginning of a file and another change at the very end of the file, SourceTree would display those changes as 2 separate hunks. You can click to select a line and then shift click to create a selection of multiple lines. Or you can just simply click a single line. Once you have your selection, you can then click the Stage Hunk or Stage Selected Lines button (depending on what you want). Once you stage a portion of the file, you will see that the file now appears above in the Staged Changes section (as well as remains in the Working Copy Changes section). This is now identical to earlier when we did a “git add README.md” to the file and then edited it again and had the file appear to be in both places at the same time.
Since you have a few lines of changes in the Staged Changes and ready to be committed (leaving the other changes alone for now) you are ready for the first commit. Click the Commit button in the main toolbar and you will get a popup window identical to the VI window that launched when you execute “git commit” from the command line (except its in SourceTree and not VI via the command line). Add your message and you can get a nice summary below that shows you what files and lines/hunks are coming along with this commit. Press the commit button. Now you should notice that the README.md file is no longer in the Staged Changes area. You should also notice that the Working Copy version of README.md has the diff on the right that is missing the changes that you just committed. You are now in the process of chipping away at your changes and building commits change by change. Once you complete your commits, you should ultimately be left with no changes and no files modified in the Working Copy or Staged Changes section.
After your last commit you are done! You can execute “git push origin bug-fix” from the command line.
Note – you can pretty much accomplish everything we’ve done via the command line in this post with SourceTree alone 😉
Now what?!
Well this turned out to be a much longer post than I originally intended. While we covered a lot, really thats just the basics. But its the core basics and more than enough to get you started and working with Git and being fairly productive. You will no doubt run into issues when you experience your first conflict, and managing stashes can start to get hairy when you are jumping back and forth between branches. The most important thing to keep in mind when working with Git is that you really can’t break anything or cause any kind of irreversible damage unless you A. try really hard and B. know what you are doing.
To really lean the ins and outs of Git and become a true pro – further reading is obviously necessary. I strongly recommend Pro Git which can be read online in its entirety for free!