Getting Started with the Git Version Control System

There's a lot to Git and there's tons of information all over the web about it. There's so much information out there that you might feel overwhelmed when you first start trying to learn what Git is and how to use it. The purpose of this blog article is to help you install Git, teach you a few basics, and point you in the right direction to learn more.

I'll start with a little background information: Back in 2009 when I started blogging on this site, I used the TechNet script repository to share my PowerShell code that I wrote about in my blog articles. Then in 2014, I started using GitHub instead of the TechNet script repository to accomplish the same sort of thing. I primarily used both as a way to share my code. I really wasn't taking advantage of the source control features in GitHub other than using it as a way to get back to a previous major version. I would develop my scripts outside of what GitHub was aware of, placing the finished product in the default master branch that GitHub created, create a commit, and sync those changes to the online repository. Needless to say it was a haphazard process especially when trying to collaborate with others.

So I decided to start learning Git which is what GitHub is built on. I know there are PowerShell cmdlets for Git, but I felt that learning pure Git would give me a better understanding and then I could learn the PowerShell cmdlets for it.

The first resource that you need to know about is the home page for Git. From there you can download Git for Windows or for other operating systems that you plan to run it on. The install is a clicker-size (even the click-next admin can install it). You're safe taking all the default options during the install.

While I won't get bogged down in the details, these particular options have to do with sharing your code with people running different operating systems. Take the defaults and move on:

git101-1a.png

I've launched Git Bash. There's also Git CMD if you feel more comfortable with it, although all of the examples I'll show in this blog article use Git Bash (on Windows).

Coming from a PowerShell background, the first thing I wanted to know was how to use Git's help system. Typing git help shows some basic help information. Typing git help help opens more detailed help information in a webpage.

1git help

git101-3a.jpg

If you're looking for help on a specific command, type git help <command_name> and the help page for that command will be opened as a webpage. The following example opens the help information for the git init command:

1git help init

git101-4a.jpg

The following command shows the version of Git that you're currently running. Notice that version begins with two dashes and that's something you'll find common to all parameters that are spelled out (if the parameter is a single letter, it only uses a single dash):

1git --version

git101-2a.jpg

One of the first things you'll want to do is configure Git. At a minimum, set the user name and email address since this information will be used in all of your commits. Remember that if you're sharing any of your Git repositories publicly online, this information (specifically your email address) will be made publicly available. For this reason, I've specified my private GitHub email address instead of my real email address.

1git config --global user.name "your name"
2git config --global user.email "your email address"

git101-5a.jpg

The previous commands set those configuration options globally for your user. It's also possible to set these options at the machine and/or repository level. To learn more run git help config.

You can verify this information was indeed set as shown in the following example:

1git config user.name
2git config user.email

git101-6a.jpg

You could also list all of the contents of the global config file by running the following command:

1git config --list

I want to create a new repository on my machine to keep track of the changes I make to my MrSQL PowerShell script module. I create a directory named MrSQL which will be used as the working folder for this Git repository. I change into that directory and then run the git init command to initialize a new git repository:

1mkdir MrSQL
2cd MrSQL
3git init

git101-7a.jpg

That creates a hidden .git folder which is what keeps track of your changes (don't mess with this folder if you value the data in your repository):

1ls -a

git101-8a.jpg

I've also placed a PowerShell script module (.psm1) and manifest (.psd1) in the folder shown in the previous example that I want Git to keep track of the changes for.

The git status command shows the current status of the working folder:

1git status

git101-9a.jpg

Notice in the previous example, there are two untracked files that Git is aware of but not currently keeping track of.

You have to tell Git what files to keep track of. I want to track all files that are currently in the working folder so I'll run git add . to add all of them to the staging area which is sometimes referred to as an index. Then when I run git status again, you see that both files are staged but not committed:

1git add .
2git status

![git101-10a.jpg]](git101-10a.jpg)

As you can see in the previous example, many commands give you tips when they're run. You can learn a lot by reading through these tips.

To commit these changes to the repository, use the git commit command:

1git commit -m "Added MrSQL script module and manifest files."

git101-11a.jpg

Notice that the repository is now up to date with what's in the working folder:

1git status

git101-12a.jpg

The git log command shows the history of the repository:

1git log

git101-13a.jpg

Notice that the commit is referenced by a SHA1 hash (the 40 character hexadecimal number in yellow next to the word commit).

Now I'm going to tag that commit with a human readable name so I'll know that it's version 1.0 of my MrSQL module:

1git tag v1.0 -m "MrSQL PowerShell Module Version 1.0" 2ae04e06d07fe8b21be7167a0cb099a42741ad8b

git101-14a.jpg

I'm going to be working on a new version of my MrSQL module. I'll create a development branch so that my changes don't accidentally get deployed before they're ready. By default, the git branch command creates a new branch that's based off of the code that's committed to whatever branch is currently checked out. I think of a new branch as an isolated environment.

The git checkout command places the code that's in the specified branch into the working folder. If the working folder currently has any unsaved changes, you'll get a warning and you'll have a chance to either commit or stash the changes because checking out another branch removes whatever is currently in the working folder.

1git branch dev
2git checkout dev
3ls

git101-15a.jpg

I've now completed my changes to my MrSQL module and I'm ready to commit the changes to the repository. Remember to commit and commit often. At this point the changes are not saved except to the files that exist in the working folder.

Notice what the file system currently knows about versus what Git knows about for the working folder:

1ls
2git ls-files

git101-16a.jpg

The git status command shows that two files have been modified and several files have been added that are not currently tracked:

1git status

git101-17a.jpg

The git diff command can be used to view the specific details of what's different between the files that are currently in the working folder versus what the staging area is aware of. This information is displayed a page at a time. Press the spacebar to page down to the next page or q to quit.

1git diff

git101-25a.jpg

Although the two modified files were previously added to the staging area to be tracked, they still have to be added again. Any time a file in the working folder is changed whether it's already being tracked or not, it must be added to the staging area first before it can be commited to the repository. What I could do since the two modified files were already being tracked is to specify the -a parameter when performing a commit to automatically add and commit them with a single command. I'll show you the problem this creates if you have both tracked and untracked files that you want in the same commit:

1git commit -a -m "Moved functions from PSM1 file to PS1 files."
2git status

git101-18a.jpg

As you can see, the shortcut used in the previous example to add and commit with a single command only works for files that are already being tracked. What I should have done in this scenario is to add all of the files to the staging area with the git add command and then commit them so they would all be in the same commit. The problem now is that I committed those two files to the repository and I meant to commit all of the changes. I want to fix this without having to performing another separate commit and I also don't want to loose any of the history of what's been done.

Here's what the git log command currently shows:

1git log --oneline

git101-19a.jpg

Instead of trying to use the git reset command, I'll go ahead and add all of those untracked files in the working folder to the staging area:

1git add .
2git status

git101-20a.jpg

Use the git diff command except with the staged parameter to see the detailed differences between the files in the staging area versus the ones that were previously committed to the repository:

1git diff --staged

git101-26a.jpg

Now I'll amend the previous commit, adding whatever changes exist in the staging area to it:

1git commit --amend

git101-21a.jpg

The previous command opened Vim since I didn't specify the -m parameter along with a commit message, but since I didn't want to change the message, I simply pressed :q to exit. You can change the default editor to Notepad using:

1git config --global core.editor "notepad"

Now I'll change back to the master branch. Notice what files are in the working folder while I have the files from the dev branch checked out. It's important to note that checking out a branch changes the actually files that are located in the working folder. The physical location on disk does not change. After checking out the master branch, all of the files that were in the working folder are removed and replaced by the ones from the most recent commit of the master branch. I could also checkout a previous commit of a branch by specifying a tag name or SHA1 hash of a commit to check out an older version of the files.

1ls
2git checkout master
3ls

git101-22a.jpg

Since I've completed my changes to the MrSQL PowerShell module and I'm ready for those changes to go into production, I now need to merge those changes from the dev branch into master so they're placed in the production code base which is what I'm using the master branch for. All I have to do is run the git merge command, specifying the branch to merge from and it will be merged into the checked out branch (which is master). Since no commits have occurred in the master branch since the dev branch was created, a fast-forward merge is performed:

1git merge dev

git101-23a.jpg

With fast-forward merges, a commit isn't performed after the merge occurs, but it doesn't need to since it only fast-forwards up to the most recent commit from the dev branch:

1ls
2git log --oneline

git101-24a.jpg

I'll create another tag since this new version is going to be version 2.0 of my MrSQL PowerShell module:

1git tag v2.0 -m "MrSQL PowerShell Module Version 2.0" 5717744
2git tag

git101-27b.jpg

Tags remind you which commit is what release version because there may be hundreds of commits between versions. I can also checkout a specific version of the code that's stored in a branch based on the tag name if I ever need to fix a bug in a previously released version.

Want to learn more? Keep an eye on this blog site.

You can download the eBook version of Pro Git 2nd Edition for free (it's open source). You can also try out Git from code school without having to install anything. Both of these resources can also be found on the home page for Git.

Update 3/28/16

Additional related blog articles on this site:

µ