Intro
I need to use distributed version control for my project files. Everyone who works with code or mark up files should be using a distributed version control system to stay organized and potentially collaborate with others. One of the best, and most popular, tools to do this is Git. It provides a way to add, update and remove files, make or clone a repository, commit locally offline and has a full local history of the changes. This allows reverting to previous versions as well as making feature or bug fix branches. Additionally, it's a great choice for continuous deployment because an automated release system can pick up the changes when they are pushed and run tests and file transfers without effort. It was designed to manage the Linux code base so it's able to take on the largest of projects.
Git Installation
Since I'm on Windows I'll use msysGit but there are other versions of Git for Mac and Linux that are essentially the same. The main differences with other OS versions are the line endings and ways to locate files in directories. After downloading msysGit and installing it to run with a command line, I open PowerShell (as administrator) and run:
PS> git --version
If Git is installed, this will display which version it is.
There are system-level, user-level and repository-level configurations for users, coloring, line endings and other such settings.
To see all the system, global and local configurations run:
PS> git config --list
To see settings at a specific level the following can be appended: --system, --global or --local
To set values, run:
PS> git config --global user.name “My Name” PS> git config --global user.email “my@email.com”
To see values set in this config file (config -l is the same as config --list):
PS> git config -l
To remove a configuration setting (to remove a setting from all levels use --unset-all):
PS> git config --global --unset user.name
The configs are set in the order of system, user, repository, when repository settings are made they override all. The file can be edited in a text editor and resides in the config file in the .git folder.
Lastly after installing, for a better command line experience with Git, install Posh Git: https://github.com/dahlbyk/posh-git. It will show the current working branch, the file status counts and other useful color coding.
SSH Key Authentication To A Remote Repository
The best way to use Git is through SSH. Not only is it secure, it also can store your authentication credentials to a remote repository so that entering the username and password is not required. After Git is installed it's best to create SSH keys, backing up old ones if there are any first.
First see if there are any keys (make note that these executables are located in the Git bin folder):
PS> ssh-add -l
If there are some, go to that location and back them up.
PS> cd ~/.ssh
If none are present they may also be stored with the user profile:
PS> cd C:\Users\USERNAME\Documents\WindowsPowerShell
If there are keys at either location, they should be backed up:
PS> mkdir key_backup PS> cp id_rsa* key_backup PS> rm id_rsa*
To make a new SSH key:
PS> ssh-keygen -t rsa -C "my_email@host.com"
No need to enter the location, it should default to the correct path. Entering a passphase is required.
Once the key is generated then it needs to be added to the profile locally and the key needs to be added to the remote GitHub location.
For more details on this process go to:
http://haacked.com/archive/2011/12/19/get-git-for-windows.aspx
https://help.github.com/articles/generating-ssh-keys
https://help.github.com/articles/error-permission-denied-publickey
Creating a Local Repository
Once in an empty folder that should be turned into a repository, run:
PS> git init
In the folder, verify the hidden .git folder exists:
PS> Get-ChildItem -force
If there is a Git repository here, a .git folder should show.
Cloning a Remote Repository
To clone a remote repository, navigate to a directory where the repository folder should be created. There are many different protocols for git, including:
-
HTTP
-
Port: 80
-
Permissions: read, write
-
Authentication: password required
-
-
HTTPS
-
Port: 443
-
Permissions: read, write
-
Authentication: password required
-
-
SSH
-
Port: 22
-
Permission: read, write
-
Authentication: SSH key (password is automatically entered)
-
-
git
-
Port: 9418
-
Permission: read
-
Authentication: Anonymous only
-
-
file
-
Port: n/a
-
Permissions: read, write
-
Authentication: local only
-
In the long run, getting SSH running will save the most time because it will store the username and password for the remote repository so that it can be communicated with without constant authentication. The following is an example of cloning an SSH GitHub repository.
PS> git clone git@github.com:USERNAME/REPO.git -verbose
It is possible to change the remote origin in the local repository so that pushes go to a different location. It's also possible to fetch changes from one of the different remote repositories.
Adding, Updating, Removing and Undoing Local Files
To see the current status of a Git repository, run:
PS> git status
This will show the working branch and any files that can be added, updated or removed with a commit. Adding a text file to this directory will show it as an untracked change.
PS> echo “testing” > test.txt
Now when checking the status again, the file test.txt is marked as a untracked change.
PS> git status
This file can now be staged for inclusion in the next commit:
PS> git add .\test.txt
Now When running:
PS> git status
The file test.txt is marked as a new file. There are other ways to do this such as adding, updating and removing all files with:
PS> git add -A
A way to stage files that have only been modified or deleted, but not added, can be done with:
PS> git add -u
To commit all the added, updated and deleted files as a changeset, run:
PS> git commit -m “Added a test file”
This will take all the staged files that are seen with the git status check and give them a unique message and changeset identifier. To see what the history has been so far, run:
PS> git log
This will show the commits, when they happened and who did them. The list is pageable and pressing: q will return control to the prompt.
If the file is modified:
PS> echo “testing edit” > test.txt
Git will show in a status check that the file was modified. Running the add, update and remove command will stage the modified file for the next commit.
PS> git add -A PS> git status
Now, this file can be added as a new commit (other files could be added, updated or removed prior to a commit):
PS> git commit -m “Updated the text file”
To see the differences between these commits, they can be diff compared by using the first seven characters. For example:
PS> git diff 64d5559..ad0ae419
This will identify the two versions of the file that were changed.
Alternatively this can be done with a step backwards from the head (current working copy):
PS> git diff HEAD~1..HEAD
It's possible to add selected files to be staged as part of different commits. For example, adding just one specific file to a stage, then committing it, then adding a different file to the stage then committing that. This can be good for grouping changes that are related but it may cause problems in terms of compilation of code.
To delete a file from disk and then stage that deletion:
PS> rm .\test.txt PS> git add -A
This will stage the file to be deleted in the next commit. From here more files can be added, updated or removed until the commit.
I'll make a commit of this deleted file:
PS> git commit -m “deleted test file”
In the case that a file is staged, modified and needs to be reverted to its state when it was last staged, Git allows a checkout. This reverts the modified file to its last staged version. For example:
PS> echo “testing checkout” > test2.txt PS> git status PS> cat .\test2.txt PS> git add -A PS> git status PS> echo “edited testing checkout” > test2.txt PS> cat .\test2.txt PS> git status PS> git checkout .\test2.txt PS> git status PS> cat .\test2.txt
Now, in the case that all the changes that are staged need to be reverted to the last commit, Git allows the ability to reset to the head.
PS> git reset --hard
Additionally the last commit to the repository can be removed:
PS> git log PS> git reset --soft HEAD~1 PS> git log PS> git status
This shows that the local repository was rolled back one commit. All the changes are back into the staging area and are in the working copy.
From here it's possible to discard the local changes and go back to the head by a revision (commit):
PS> git reset --hard HEAD~1 PS> git status PS> git log
This effectively rolls back to one previous commit from the current head (working copy).
To fast forward the current working branch to the head, run:
PS> git checkout master
PS> git pull origin
Cleaning Local Files
If there are files which have been created that should be removed, they can be removed from the working copy. This will create two empty files which are untracked:
PS> touch temp1.txt temp2.txt PS> git status
Running a clear command with -n will show what would happen if a forced clean was run on the repository.
PS> git clean -n
Now, running a forced clean will remove these files and I can check to see that:
PS> git clean -f PS> git status PS> ls
This shows that the two temp files have been wiped away.
Ignoring Local Files
It's not a good idea to store files that are generated and disposed of constantly or user specific because it bloats source control with files that are irrelevant. To avoid adding these types of files to source control, Git can be instructed to ignore them. This is done with a file called: .gitignore that should be placed in the root of the repository. An example of a file like this is:
.svn* obj/ bin/ *.suo *.user Log/ log/ *.db
This file needs to be added to source control. A good ignore file for Visual Studio is the one recommended by GitHub here.
History and Statistics
In a repository, past commits can be viewed in a readable and pageable fashion (exit the paging with q):
PS> git log --oneline
To see how many commits are in the repository:
PS> git log --oneline | wc -l
To see a graph of the different branches and merges run:
PS> git log --oneline –-graph
To see a detailed version of the log with branches and tags:
PS> git log --graph --oneline --all –decorate
To see the authors and their commits in alphabetical order:
PS> git shortlog
Since Git stores all the changesets in the repository locally, it allows running these types of statistics. To see all the authors in the repository, ordered by the quantity of commits they've made with their name and email address, run:
PS> git shortlog -sne
GitHub has more detailed displays of the data including graphs. These are viewable on the web interface of GitHub.
To see the last commit made and the changes it entailed, run:
PS> git show head
It's possible to see the changes in any commit by specifying it's position from the head or it's specific changeset identifier.
PS> git show head~1
or with the changeset:
PS> git show 23e16f4
It's possible to see where the source came from and where it is pushed to. When cloning a remote repository this is added automatically. They might actually be different, the -v parameter is verbose for showing both fetch and push URLs:
PS> git remote -v
Another useful option for viewing the repository is to look at a reference log of all the commits that the head of the repository has pointed to:
PS> git reflog
Another useful way to see history with all branches is through a UI tool:
PS> gitk --all
Creating Aliases
There are times when commands become too long to type out and difficult to remember. Aliases allow a way to give a set of characters which will run a statement. For example, the log graph that shows the branches and tags is useful so I'll make an alias for it to save time:
PS> git config --global alias.lga “log --graph --oneline --all –decorate”
The alias is in the .gitconfig file in the repository. Now when I run the aliased command I can see the output much easier:
PS> git lga
Pushing Local Committed Changes To A Remote Repository
Running a status check will show if there are any files that need to be committed. If there are commits to be pushed then it will say how many.
PS> git status
If there are changes then from here they can be pushed. If the remote origin is with HTTP or HTTPS then it will require the username and password for each push. It can be changed to work with an SSH key so that it will automatically authenticate.
This is how all the local working commits can be pushed back to the remote repository (appending -v is verbose in this case and will give more detail but is not required):
PS> git push -v
Now, if those changes are successfully pushed to GitHub I can see the commits on their website.
Working With Tags
To see all the tags:
PS> git tag
Perhaps there are no tags, it's possible to stamp the current working repository with a tag. When a tag is added, it's always on the current commit. That tag could represent anything but a good use would be a version number.
PS> git tag v1.0
Now running the tag command will show this new added tag:
PS> git tag
It should represent a stable point in the code base. Running a log on the commit will reveal that the topmost commit was the one that got that tag.
PS> git log
Now, if I wanted to delete that tag I could do so. The reason that I want to delete the tag is because I want to add a message to it. I will delete the tag I just made and then add a new tag that will include a message for a new tag with the same name.
PS> git tag -d v1.0 PS> git tag -a v1.0 -m “This is a stable point”
There is an option of adding a signed tag. A signed tag insures that it came from a specific person. It can be validated using public and private cryptography. This is useful for setting an authority on a release. I am not going to do that because it requires more set up with my keys but it can be added like this:
PS> git tag -a v1.0 -s -m “This is a stable point”
Pushing code does not push the tags. Tags are pushed separately. This is how to push the local tags to the remote repository:
PS> git push --tags
Now on GitHub there are tags that show the time the commit was made. Tagged releases allow navigating to different tags. The state of the project at a specific time can be selected with a tag on GitHub. All the files that were part of that tag can then be viewed on GitHub. It is a good idea to include the tag name in a text file that gets included with the push. That way there is an easy way to know what tag/ version is in a copy of the source code. Tags can be added with from a build server as well.
Fetching vs Pulling
Fetching retrieves changes without merging them. Pulling retrieves changes and merges them. To fetch changes from a remote repository without merging them into the working copy run:
PS> git fetch
To merge the remote changes into the branch, run:
PS> git merge origin/master
It's possible to simply get the remote changes and merge them into the project in one line with:
PS> git pull
If running the pull request indicates that: “You asked me to pull without telling me which branch you want to merge with” then the local and remote branch can be set up:
PS> git branch –set-upstream master origin/master
This will say which branch remotely that the local branch mirrors. It's possible to pull which remote branch to pull in from per pull request.
Working With Branches
To see all the branches in the repository:
PS> git branch
To see all the branches in the remote repository:
PS> git branch -r
To switch to a specific branch:
PS> git checkout branchname
Using A Profile
To see where the currently loaded profile is:
PS> $profile
Editing this script can allow things like setting the location to the repository to start with and pulling down all the changes before working:
Set-Location C:\git\myrepo git pull
This is also where modules can be loaded and welcome messages can be added:
# Welcome message Write-Host "*****************************************" -Backgroundcolor green -foregroundcolor black "-----------------------------------------" "Welcome: $env:Username!" $startTime = Get-Date $startTime = $startTime.ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ss") "Begin : $startTime UTC" "-----------------------------------------" Write-Host "*****************************************" -Backgroundcolor green -foregroundcolor black " "
To reload the profile:
PS> . $PROFILE
Moving On
This was only an intro to Git. There are many more topics that need to be known such as merging, conflicts, reverting changes, cherry picking and branching. There will need to be more detail on these later.
Useful Resouces
- http://gitready.com/
- http://www-cs-students.stanford.edu/~blynn/gitmagic/
- http://git-scm.com/book
- http://gitcasts.com/