Getting Started With Git On Windows With PowerShell



Last updated: January 28th, 2024

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


Comments

No Comments

Post Comment

Prove you are human 5 + 14 =

Tagged: Git, PowerShell


Join my email list!



ryan
About Me

With over 15 years in tech, I've excelled as a senior software engineer, specializing in ASP.NET, C#, SQL, Azure, and front-end technologies. I've led diverse projects across various sectors, from startups to global corporations, particularly during my decade in the San Francisco Bay Area.


Sign Up With SoftSys Hosting! (My host)