Up to this point . . .
This is the third part in a series focused on getting familiar with Git for Windows developers. The series assumes little or no experience using the command line. If you missed the first few posts, here is what we have covered so far:
In the previous post, we learned how to navigate directories within the Windows file system, create new directories, and delete directories. Now let’s look at creating files, and performing rudimentary (VERY rudimentary) text editing from the Bash command prompt.
Before We Proceed . . .
Let’s create a directory to use for the following examples. Open your Bash Command Line Interface (CLI) and make sure you are in your default Windows user folder. Create a new directory within your user folder called BashExcercises, and then move into that directory (Review the previous post if you are unsure how to do this):
Create a Directory for the Following Exercises
Also remember, when we are explaining syntax, we will often use square brackets to denote optional items, and angle brackets to denote use-provided content. In the following:
mkdir [options] <FileName>
We do not type the square brackets or angle brackets. The [options] are, well, optional, meaning there may be none or there may be many, and the <Filename> is required and provided by the user.
Bash: Create/Edit Files (touch/echo)
There are a couple of ways to create a new file using the Bash command prompt. The most basic is the touch command. The purpose of the touch command is ostensibly to change/modify the date/time stamps of files. However, it is commonly used to create new files as well, since, when used without any optional arguments, it does just that: creates a new file, with a date stamp.
Using the Touch Command to Create a New Text File
The syntax for the touch command is
Create a new file with a date stamp:
touch [options] <filename>
There are a number of options for use with the touch command, but for now, we are going to focus on simple file creation, and we won’t be using any of the options. If we want to create a simple text file, we can type:
Create a new Text File Named MyNewTextFile:
$ touch MyNewTextFile.txt
Type the above into your Bash window and hit enter:
Remember from the previous post that in many cases, when a Bash command executes successfully, we are rewarded with simply a new command prompt. However, we can use the ls command we learned from the last post to examine the contents of our directory:
What do you know? There is is. Now, you could open the new file using Notepad (or the text editor of your choosing), but you won’t see much. We have created an empty text file. To open the file using Notepad from the Bash prompt, simply type:
Open MyNewTextFile.txt in Notepad using Bash:
$ notepad MyNewTextFile.txt
Or, of course, you could open the file the old-fashioned way - by double clicking on it with your mouse!
Using the echo Command to Write Strings as Output
Before we look at using echo to create a new file, we should understand a little about what echo DOES.
The primary design purpose of echo is apparently to write strings to the standard output (usually, the Bash CLI).
The syntax for echo is:
Echo Command Syntax:
$ echo [options] <string>
Lets try that now. Type the following into Bash:
Output a line of Text to the Bash Screen:
$ echo show this line on the screen
We can also tell the echo command to write a line of text to a file, using the following syntax:
Syntax: Append text to the end of a file:
$ echo [options] <Text string> >> <FileName>
By using the >> (double right-hand angle-brackets) operator, we tell Bash to append the text to the end of the file. Type the following into the Bash window and hit enter:
$ echo This line was added to the end of the file >> MyNewTextFile.txt
Now, if we use our $ notepad MyNewTextFile.txt Command, we see the following:
Close that window, and let’s add another line. Type the following and hit enter:
Adding Another Line of Text:
$ echo This is another line of text >> MyNewTextFile.txt
The use $ notepad MyNewTextFile.txt again:
Huh. Looks like it worked, kind of. Apparently, Bash isn’t kidding about appending the new text to the end of the file. Happily, we can use the > (a single right-hand angle bracket) operator to replace the text in the current file and get rid of that mess. Type this and hit enter. Then open the file in Notepad again:
Replace the Text in a File with a New Text String:
$ echo This line replaced the old mess > MyNewTextFile.txt
Your Bash window should look like THIS:
And you should see this in your Notepad window:
Use the Echo Command to Create a New File with Text Content:
Ok, back to the original subject - creating a new text file. We can use echo to create a new file, and include the specified text string as content. In fact, it works the same as when we appended or replaced the text in an existing file, because, lacking an option argument telling it not to, Bash will assume that if the file specified in our echo command does not exist, that we want to create one. Type the following and hit enter:
$ echo This is a new line in a new file >> DifferentTextFile.txt
Then use ls -1 to display the directory contents. This is what your Bash window should look like:
Note the presence of our new file. Let’s open it in Notepad, and see what there is to see:
Well, well well. Whadd’ya know!
Bash: Remove Files (rm)
Ok, now lets get rid of our first file. To remove (delete) files using Bash, we use the rm command. The syntax for removing files is as follows;
rm [options] <FileName>
SO now, let’s delete that first file, MyNewTextFile.txt. Type the following, hit enter, then use the ls -1 command to display the directory contents:
Your Bash window should look like this:
Remove a Directory and all Contents (rm -rf)
In the previous post, we discussed how to remove an empty directory. But what if we want to remove a direcory and all of its contents? We use the rm command with the -r and -f options:
Syntax for Removing a Directory and All of its Contents:
rm -rf <FolderName>
So now we are going to remove the BashExcercises directory we created at the beginning of this post. Of course, we can’t remove a directory if we are currently IN that directory. So first, let’s return to the directory above (in this case, our home folder) using the cd .. Comand (the cd Command, followed by a single space, followed by two periods in a row. This one means “move up one level”).
Next, type the following into your Bash window, hit enter, and then use the ls -d */ command to view the directories within the current directory (which should be, at this point, your Windows User folder):
$ rm -rf BashExcercises
When you are done, your Bash window should resemble this (note that the directories are organized alphabetically in columns, and the BashExcercises directory is no longer there. Also note that for obvious reasons, your user folder will contain different folders than mine!).
Now, let’s use everything we just learned in this post, plus we’ll add one final tidbit at the end which you may find useful. We’re going to step through this right quick. We will:
- Add one more new directory
- Add a file containing a line of text to the new directory
- Remove the directory and its contents
- Have Bash tell us what was removed.
1. Add a New Directory Named OneLastFolder to our current directory:
$ mkdir OneLastFolder
2. Add a New File Named OneLastFile.txt, containing the text “One more text line” to the new folder:
$ echo One more text line >> OneLastFolder/OneMoreFile.txt
Notice in the above, I did not navigate into the new folder to create the new file, but simply used the relative path from my current location.
3. Remove the directory and all its contents, and have Bash tell us what was done:
rm -rfv OneLastFolder
Notice in the above, I added one more optional argument, the -v option (for “verbose”). This tells Bash to print the actions it took to the output window.
When you have done all of that, your window should look like this:
There you have it. You now have the most basic set of tools for using the Bash Command Line Interface. From here, we are ready to examine the basic command line operations required to start using Git to manage our code. We’ll pick that up in the next post in this series.
John on Google
This is the second installment of a multi-part series about getting your feet wet with Git for Windows Developers. If this is your first time here, you may want to review Part I of the series.
If you have been following along so far, you likely have by now installed msysgit on your development machine, and performed the initial configuration necessary to begin using git to track changes in your source code. So, what next?
Well, before we can do much with Git, we need to make sure we have at least a token familiarity with using the Bash command prompt. To do this, we will walk through some basic commands related to navigating in bash, getting information about the file system and environment, and working with directories and files. All of these are necessary in order to work with Git using the Bash command line.
Since these posts can get long (due in large part to the large number of screen shots), this may span a few posts. It’s not as bad as it looks, really. Lots of pictures.
If you are already familiar with the concepts here, and mainly just need help with the proper syntax for performing these actions in Bash, refer to my Basic Git Command Line Reference Post which contains a listing of the most common git and Bash commands without all the narrative.
Otherwise, read on, and let’s get comfortable using the Bash command line in a Windows environment.
Get Acquainted with Bash
The Bash command line comes from a Unix heritage, and even if you are familiar with the Windows command line, or Powershell, using Bash is different.
First, we should get a little comfortable moving around our file system and performing basic tasks with files and directories.
When you first open the Bash command window, you should see something like this:
In the above, the text in green is the User Name and Computer Name, displayed using the convention UserName@ComputerName. The curlicue character following means that we are in our default directory (or folder), usually our Windows User Folder.
A line which begins with the default prompt (in this case, a $ symbol) is a line on which you will enter a command. Lines without the $ symbol will represent feedback from the system. Each time you type in a command and hit enter, Bash will execute the command, return whatever output (if any) and the leave you with a new prompt.
Note that many commands simply execute, and there is no output. It is a principle amongst Linux programming that a good program executes silently, and only provides feedback if requested, or if something has gone wrong. More on this momentarily.
List the Directories within the Current Folder
We can list other directories within our current folder using the ls command. Type ls into your Bash window and hit enter:
Use Bash to list the contents of the current directory:
Huh. That’s a lot of stuff, and except for those items with file extensions, it is hard to tell what is a directory (folder) and what is a file of some sort. Wouldn’t it be nice to show just the folders, without the other stuff?
In a clear case of stating the obvious, the following will clear your Bash window. Type this and hit enter:
Clear the Bash window:
Now, let’s try to examine the folders in our current directory again, and see if we can get a more useful view of the subfolders we are working with. Type the following command into the Bash window, and hit enter again:
Print a listing of all directories within the current directory:
$ ls -d */
The result should look like this:
That’s a little more like it. Ok, so now we can see what folders exist within our current directory.
For a cleaner listing of the variations for the ls command, see the Show Directory Contents section of my Basic Git Command Line Reference Post.
Create a New Directory in the Current Directory
Next, let’s create a new folder. The command for creating a new directory is as follows:
Create a new directory within the current directory:
$ mkdir NewFolderName
Type that into your Bash window, substituting MyNewFolder for NewFolderName and hit enter. This is what you should see when you are done:
But hey - nothing happened, right? Wrong.
As mentioned above, when the mkdir command executed properly, the result is “silence.” In other words, Bash assumes YOU know that, lacking additional feedback, everything went fine. Let’s check and see. Type your new “Show me all the folders, but only the folders” command we discussed previously, and hit enter:
Well, what do you know. There is our new folder. But wait. What if we want spaces in the name of our folder? Let’s try THAT with a another new folder. Type the following and hit enter:
Create a folder named My Other New Folder:
$ mkdir My Other New Folder
When you hit enter, you should see something like this:
Hey, looks like everything worked! No complaints from Bash. Let’s see, using our “Show directory contents” command again:
Uh-oh. Looks like things didn’t go quite the way we expected. As it turns out, we confused Bash, because the actual syntax of the mkdir command is:
The full syntax for the mkdir command:
$ mkdir Folder_1 Folder_2 . . . Folder_n
So when we typed in a folder name containing spaces, Bash thought we were creating a separate new folder for each word. As it turns out, most of the time when using Bash, we need to enclose text with spaces in quotes, so that Bash will know that the quoted text represents a single object or idea. Let’s try creating our new folder with spaces in the name again. This time, type the following into the Bash window:
Create a new folder with spaces in the name:
$ mkdir "My Other New Folder"
When you are done, you should see something like this:
Again, we assume that nothing went wrong from a technical standpoint, because Bash executed the command and returned without complaint. If we check to see again, we find the following:
Yay! So now we can not only create folders, but with spaces in the names too, dammit!
For more on creating directories, see the Creating New Directories Section in my Basic Git Command Line Reference Post
Move to a Folder Within the Current Directory
Ok, so now let’s navigate ourselves into the first folder we created, MyNewFolder. First, though, let’s clear the bash window again, so we can get rid of all the clutter we have accumulated so far.
When we want to move to a new location using Bash, we use the cd command:
The correct syntax for this command is:
Change Directory Command Syntax:
$ cd [options] [<DirectoryName>]
In the above, the square brackets denote optional inputs, and the angle brackets denote user-supplied values. In both cases, we do not type the actual brackets into the command window.
Since Bash already knows the current directory we are in, we can specify a folder name which exists in the current directory, without the rest of the directory path. Type the following into the Bash window, and hit enter. You should see something similar to this:
Note in the above that the line above our prompt has changed, and appears to indicate our current path as:
Remember that Bash uses the ~ symbol to denote our default (User) directory, so this is a relative reference within the user directory. From where we are right now, there are a number of ways we could return to the user directory.
The first is to simply enter the cd command with no additional input. Doing this will return us to the default directory from wherever we happen to be in the file system:
Return to the Default Directory (“Take Me Home”)
Another option is to type cd - (that’s the cd command followed by a space, followed by a single dash). This tells bash to go back to the previous directory. In this particular case, the previous directory also happens to be our home user folder, but it really doesn’t matter what directory we came here from, cd - returns us there.
Return to the Previous Directory (“Take Me Back”)
$ cd -
The third option is to type cd .. (that is the cd command, followed by a space, followed by two periods in a row). This command tells bash to go up one level in the folder hierarchy.
Move Up One Level (“Take Me Up”)
$ cd ..
Considering that we are currently in a folder within our home directory, you can see why in the current situation, these three are all about the same in terms of returning to the home directory. At the moment, it is not only the last place we navigated from, but is also one level above our current location. And it is, indeed the home folder. So all three options work for us in this case. Try the “Take Me Up” command. Type it into your Bash window and hit enter. You should see something like this:
Note that we now appear to back in the “directory known as ~” or, our user folder.
Now type the “Take Me Back” Command (cd -) and hit enter:
Now, here we are right back in the MyNewFolder directory. Note that Bash decided to tell us where it took us in long form:
A Linux-Style Directory Path
But wait - what is with that funny directory syntax? That is the Linux directory style. In fact, when typing explicit directory paths into Bash, you will need to use that format, because Bash will not recognize the Windows directory style. It’s easy enough to follow though:
- Instead of using the forward-slash (\) character as a path delimiter, Bash (and Linux) uses the backslash (/)
- /c/ is the Linux way of expressing the familiar C:\
Move to a Specific Directory Using the Complete Directory Path
Knowing this, we can also use explicit paths for navigation. For example, let’s say I wish to move from the current directory to my Documents folder. I can type the following:
Move to a specific directory using the directory path:
$ cd /c/Users/CurrentUserFolder/Documents
Try that now, substituting your user folder, assuming that you have a standard Windows file structure, which includes a Documents folder in your user folder by default:
Ok. If all went well, you should now find yourself in your Documents folder. Note that even though you are within a subfolder of your user folder, Bash is now displaying the long form of the directory path, instead of the shorthand relative path.
Now try using the “Take Me Home” version of the cd command (simply type cd and hit enter):
Presto - back in our home directory.
For more on directory navigation, see the Navigating the File System Section of my Basic Git Command Line Reference Post
Ok, now what about those folders we “accidentally” created when we entered a multi-part directory name? You know, the folders named “My” and “Other” and “New” and “Folder”? Remember THIS:
Since these folders are all empty, we can simply use the rmdir command:
Remove One or More Empty Directories:
$ rmdir Directory_1 Directory_2 . . . Directory_n
Type the rmdir command as follows and hit enter. When you are done, you should see something like this:
Then, if we check the directory contents again, we see that those extra directories no longer exist:
Removing an empty directory or directories is a simple undertaking. The process is a little different if the directory to be removed contains files or other directories. We’ll look at this in the next post.
Next: Creating and Working with Files using Bash
John on Google
While there are GUI interfaces available for GIT (some good, some bad), familiarity with at least the basics of git’s command line interface can only enhance your ability to use the tool to maximum effectiveness. Since I am relatively new to git and version control in general, I set out to learn the basics of the git command line. In doing so, I found it handy to keep a list of the commonly-used commands nearby so that I didn’t have to keep Googling.
In this post, I am going to cover the very basic set of commands one might require to effectively navigate and maintain your source repo using only the git Bash command line interface. Probably, in creating this post, I will not need to look at this again, as the sheer fact of composing this list and explaining it all will burn these into my brain forever. On the other hand, if I am ever unsure, I will now have a place to come look!
NOTE: The references here are by no means comprehensive. I have included the basic commands required to get started, and the commonly used options for each command. There is a wealth of additional information available on the internet, and I have included some helpful reference links at the end of this post.
To more easily find what you might be looking for, here are some links to specific sections of this post:
Working with the file system
Configuring Git and Creating a Repository:
Staging and Committing Changes
Working with Remote Repositories (like Github)
Undoing Changes and Working With Tags
Git Bash: Syntax Notes
First off, note that Git Bash is a *nix application (Unix/Linux), and expects inputs according to *nix conventions when it comes to file system navigation. This is important when using Git on a windows system, because we need to mentally map the familiar Windows directory notation to Unix format:
|Windows Directory Path
||<---- Becomes ----->
||*nix Directory Path
Strings with Spaces
When we are going to provide an input string with no spaces, we need do nothing. However, strings which contain spaces must be enclosed in quotes. Remember this. Personally, I just use quotes around strings in general.
The “Home” Directory
The file system in *nix systems is set up a little differently than in Windows. Git Bash assumes the existence of a “home” directory for each user. In Windows, the default is your personal user folder. This folder is where Git Bash opens by default. Typing only cd after the command prompt will always return you to the root level of the home directory.
Command Syntax Format:
The basic command syntax for a git Bash Command is:
$ CommandName [options] [directory]
In the above, the square brackets denote optional parts of the command. The square brackets themselves are not typed into the command line. Items following a command which are not enclosed in brackets are required.
Cases and Spaces Count
Also note that git Bash is case-sensitive, and spaces count. For Example, the common command to change to another directory is cd. This is NOT the same as CD or Cd.
When portions of a command are optional, we will note this by enclosing them in square braces:
$ Command [options]
In the above, we do type the square brackets.
For our purposes here, when we are presenting command syntax examples, we will denote user-provided values between angle brackets:
$ Command [options] <SomeUserInput>
In the above, we do type either the square or angle brackets.
Git Bash: Navigating the File System (cd)
cd [options] [<directory>]
Navigate to the Home Directory (Default folder for the current user):
Navigate to a specific folder in the file system:
$ cd /c/SomeFolder/SomeOtherFolder/
Navigate to a specific folder in the file system (if there are spaces in the directory path):
$ cd “/c/Some Folder/Some Other Folder/”
Go back to the previous Location:
$ cd -
Move Up One Directory Level:
$ cd ..
In the above, the cd command is followed by a space, then two period with no space between.
Git Bash: Show Directory Contents (ls)
-1 = List 1 item per line
-r = Reverse the sort order
-a = Show Everything, including hidden items
-d = list only directories
-l = (letter L, lowercase) = Use a long listing format (more info per item, arranged in columns, vertical listing)
List the contents of the current directory (folder):
The above will display the contents of the current directory as a horizontal list. Not real convenient.
List the contents of the current directory, one item per line:
$ ls -1
That’s better. Note, however, that we can only differentiate files from subdirectories based upon the file extension.
List only the subdirectories (folders) within the current directory:
$ ls –d */
List everything in long form, vertically:
$ ls –al
The above gives a swath of information. Also, subdirectories are differentiated by the first column (begin with drwxr instead of -rw)
List all contents, including subdirectory contents, single item per line:
$ ls -1 *
Git Bash: Create a New Directory (mkdir)
mkdir [options] <folderName>
-p = Create parent directories as needed
--verbose = Show a message for each new directory created (note the double dash)
Create a folder in the current directory (without spaces in the folder name):
$ mkdir NewFolderName
Create a folder in the current directory (with spaces in the folder name):
$ mkdir “New Folder Name”
Create a folder at the specific directory path:
$ mkdir /c/ExistingParentFolder/NewFolderName
Create a folder at the specific directory path, and create parent directories as needed:
$ mkdir -p /c/NewParentFolder/NewFolderName
Create a folder at the specific directory path, create parent directories as needed, and print a description of what was done in the console window:
$ mkdir -p --verbose /c/NewParentFolder/NewFolderName
Git Bash: Create Files (touch, echo)
touch [options] <FileName>
echo [options] TextString > FileName
(NOTE: FileName can include directory. Default is the current directory).
Create a single (empty) text file in the current directory:
$ touch newFile.txt
Create a single (empty) text file in the specified directory:
$ touch /c/SomeFolder/newFile.txt
Create multiple (empty) text files in the current directory:
$ touch newFile_1.txt newFile_2 . . . newFile_n
Append text to a file. If the file does not exist, one is created:
$ echo “This text is added to the end of the file” >> newFile.txt
Overwrites text in a file. If the file does not exist, one is created:
$ echo “This text replaces existing text in the file” > newFile.txt
Overwrites text in a file at the specified location. If the file does not exist, one is created:
$ echo “This text replaces existing text in the file” > /c/SomeFolder/newFile.txt
Git Bash: Remove Files (rm)
rm [options] -<FileName>
-I (or --interactive) = Prompt before removal
-v (or --verbose) = Explain what is being done
Remove the specified file from the current directory (no spaces):
$ rm -DeleteFileName
Remove the specified file from the current directory (with spaces):
$ rm -“Delete File Name”
Prompt for confirmation before remove the specified file from the current directory (no spaces):
$ rm -i -DeleteFileName
Removes the specified file and reports what was done in the console window:
$ rm -v -DeleteFileName
Git Bash: Remove Directories (rmdir, rm -rf)
rmdir [options] <FolderName>
Removes the specified folder if empty. Operation fails if folder is not empty:
$ rmdir -DeleteFolderName
Removes the specified folder and all contents:
$ rm -rf -DeleteFileName
Git Bash: Configure Git (git config)
git config --global user.name <“User Name”>
git config --global user.email <UserEmailAddress>
Set the global User.Name value for the current user on the system:
$ git config --global user.name “FirstName LastName”
Set the global User.Email value for the current user on the system:
$ git config --global user.email UserEmailAddress
Show me the current values:
The following return the current values for the user.name and user.email properties respectively and output the values to the console window:
Print the current global User.Name value to the console window:
$ git config --global user.name
Print the current global User.Email value to the console window:
$ git config --global user.email
Git Bash: Initialize a New Git Repo (git init)
Create files required in the current directory to perform version control:
$ git init
Git Bash: Add/Stage for Commit (git add)
git add [options] [<File_1>] [<File_2>] . . . [<File_n>]
-A (or --all) = Add all new or changed files to the staged changes, including removed items (deletions)
-u = Add changes to currently tracked files and removals to the next commit. Does not add new files.
. = Adds new or changed files to the staged changes for the next commit, but does not add removals.
Note that git add -A is semantically equivalent to git add . followed by git add –u
-p = Interactive add. Walks through changes in the working directory and prompts for add
Add all changes in the working directory to the next commit, including new files and deletions:
$ git add -A
Add all changes to tracked files and all new files to the next commit, but do not add file deletions:
$ git add .
adds all changes to tracked files and all file removals to the next commit, but does not add new files:
$ git add -u
Walks through changed files and prompts user for add option. Does not include new files:
$ git add -p
Git Bash: Unstage from Commit (git reset)
git reset HEAD <File_1>
Remove the specified file from the next commit:
$ git reset HEAD FileName
Git Bash: Committing Changes (git commit)
git commit [options] [<File_1>] [<File_2>] . . . [<File_n>] [-m <“Commit Message”>]
-a = Commit all changes to tracked files since last commit
-v = Verbose: include the diffs of committed items in the commit message screen
--amend = Edit the commit message associated with the most recent commit
--amend <File_1> <File_2> . . . <File_n> = redo the previous commit and include changes to specified files
Commits the changes for the specific file(s) and includes the commit message specified:
$ git commit FileName –m “Message Text”
Note that Git requires a commit message. If you do not provide one using the -m option, you will be prompted to do so before the commit is performed.
Commits all files changed since last commit. Does not include new files.
$ git commit –a –m “Message Text”
Adds all changes to the previous commit and overwrites the commit message with the new Message Text. Does not include new files:
$ git commit –a –amend –m “Message Text”
Git Bash: Remote Repositories (git remote)
git remote add <RemoteName> <RemoteURL>
git remote show <RemoteName>
NOTE: As used here, RemoteName represents a local alias (or nickname) for your remote repository. The name of the remote on the server does not necessarily have to be the same as your local alias.
Add the specified remote repo to your git config file. The remote can then be pushed to/fetched from:
$ git remote add RemoteName https://RemoteName/Proj.git
Print information about the specified remote to the console window:
$ git remote show RemoteName
Git Bash: Branching (git branch)
git branch [options][<BranchName>][<StartPoint>]
-a = List all local and remote branches
-r = List all remote branches
List all local branches:
$ git branch
List all remote branches:
$ git branch -r
List all local and remote branches:
$ git branch -a
Create a new branch starting at the some point in history as the current branch:
$ git branch BranchName
Note that this creates the new branch, but does not “check it out” (make it the current working branch).
Switch from the current branch to the indicated branch:
$ git checkout BranchName
Create a new branch and switch to it from the current branch:
$ git checkout –b NewBranchName StartPoint
Note that StartPoint refers to a revision number (or the first 6 characters of such) or an appropriate tag.
Git Bash: Merging Branches
git merge [<BranchName>][--no-commit]
Merge the specified branch into the current branch and auto-commit the results:
$ git merge BranchName
Merge the specified branch into the current branch and do not commit the results:
$ git merge BranchName --no-commit
Git Bash: Pushing to Remote Repositories (git push)
git push [<RemoteName> <BranchName>]
Update the remote server with commits for all existing branches common to both the local system and the server. Branches on the local system which have never been pushed to the server are not shared.
$ git push
Updates the remote server with commits for the specific branch named. This command is required to push a new branch from the local repo to the server if the new branch does not exist on the server.
$ git push RemoteName BranchName
Git Bash: Fetching from Remote Repositories (git fetch)
git fetch <RemoteName>
Retrieve any commits from the server that do not already exist locally:
$ git fetch RemoteName
NOTE: git fetch retrieves information from the remote and records it locally as a branch in your current repository. In order to merge the new changes into your local branch, you need to run git fetch followed by git merge. Since there may be more than one branch on the remote repository, it is necessary to specify the branch you wish to merge into your current branch:
Merge syntax for post-fetch merge:
git merge <RemoteName/BranchName>
Merge the newly fetched branch from the remote into your current working branch:
$ git merge RemoteName/BranchName
Using fetch before merging allows you to pull changesets in from the remote, but examine them and/or resolve conflicts before attempting to merge.
Git Bash: Pulling from Remote Repositories (git pull)
git pull <RemoteName/BranchName>
Fetch changes from the specified branch in the remote, and merge them into the current local branch:
$ git pull RemoteName/BranchName
NOTE: git pull is essentially the same as running git fetch immediately followed by git merge.
Git Bash: Undo (git reset)
git reset [options]
--hard = undo everything since the last commit
--hard ORIG_HEAD = Undo most recent merge and any changes after.
--soft HEAD^ = undo last commit, keep changes staged
Undo everything since the last commit:
$ git reset --hard
Undo most recent successful merge and all changes after:
$ git reset --hard ORIG_HEAD
Undo most recent commit but retain changes in staging area:
$ git reset --soft HEAD^
Git Bash: Tags (git tag)
git tag [options] [<TagName>] [<CommitChecksum>] [<TagMessage?]
-a = Annotated Tag
-m = Annotated Tag Message
List all tags in the current repository:
$ git tag
Create a tag at the current revision:
$ git tag TagName
Create a tag at the commit specified by the partial checksum (six characters is usually plenty):
$ git tag TagName CommitChecksum
Create an annotated tag:
$ git tag -a TagName -m TagMessage
Create an annotated tag at the commit specified by the partial checksum:
$ git tag -a TagName CommitChecksum
Push tags to a remote repository:
$ git push --tags
Print information about a specific tag to the console window:
$ git show TagName
Almost all of the information presented above represents a “toe in the water” sampling designed to get started. There are plenty more commands for use both within Git itself, and from the more general Bash command line. Additionally, most of the commands listed here have more options than I have included. I tried to keep this simple, as a reference for myself, and for whoever else may find it useful to get started with the basics.
John on Google
I am a late-comer to version control in general, and, having grown up teaching myself programming in the Windows/Visual Studio/C# realm, It took the growing prominence of Github to draw my attention to what is currently the most visible Distributed Version Control System (and “social coding” site) in the developer universe.
Once I graduated to using version control for my code, there was no turning back.
Hasn’t All of this been covered somewhere else?
Why, yes it has. In many cases, better than I am about to cover it. The web is chock full of tutorials on using git and Github. Some are better than others, and it is great to get information and opinions from a variety of sources.
I am writing this post as much for myself as my two readers, as a means of increasing my familiarity with the Git/Bash ecosystem (what better way than to explain it to someone else, right?), and so that I will have a reference to my own best thinking on the subject, relevant links, and such.
In this series, plan to walk through the basics of getting started with git in a Windows environment:
Get Git for Windows
Git is a Unix-based application. In order to use Git in Windows, it is necessary to install the Windows port, msysgit. msysgit is an open source project, freely available for download. As of this writing, the most recent release is version 1.7.11. When you go to the project download page, you will notice that all versions of msysgit are named Git-1.x.xx-preview and are tagged with a little “beta” flag. Don’t worry about this. msysgit is widely used. Below is a link to the downloads page for the “Full Windows Installer” version of the download.
Go get it now at the link below. I’ll wait.
Installing Git for Windows
When you first run the downloaded installer, you may be greeted with a security warning about unknown publisher and such. You can ignore the ominous warning, and click the “Run” button. Click next to move through the “Welcome to the Git Setup Wizard” splash/greeting window, and again to accept the license terms. The next window is the installation components window:
The default values here should be fine, but make sure that the “Windows Explorer Integration” item is checked, and that “Git Bash Here” and “Git GUI Here” are selected. Now click Next again:
Command Line Environment
Of the three options available here, which you choose will depend upon your comfort level with the windows command line. Personally, I prefer to use Git Bash only. I figure, it can only help me as a developer to become fluent with the world of the *nix Bash command line, and the hybrid environment(s) created by the other two options seem more like a potential source of irritation than anything else.
Line Ending Conversion
Unix/Linux systems use a different convention for line endings than Windows. I recommend the default option, which should be “Checkout Windows-style, commit Unix-style line endings.” This option offers the greatest flexibility if you will be sharing your code with others.
Click next, and the installation will start. This should only take a minute or so (or less). Great! You now have Git installed on your windows machine. What next?
Configuring Git on your Windows Machine
Now that Git is installed on your Windows machine, you need to do some basic configuration. Git maintains several configuration files:
- The highest order configuration file contains system-level configuration values for all users, and all repositories on the system. This file is usually created in a directory relative to the msysgit installation directory.
- The global user-level configuration file, which is specific to the individual user, and usually resides in the user’s home folder (Usually, but not always, C:\Documents and Settings\<UserName>). This is where your Username and email address values are generally set up, and will maintain default configuration settings for your repo’s.
- Each repository also contains a configuration file, specific to that repository, which is located in the .git directory of the specific repository or folder.
Note that configuration settings in each more specific level override those in the level above. For example, you can override the global user name and email address values (global here, again, means user-level) within a specific repository by setting them in the repository-level configuration file.
We’ll look more closely at this momentarily.
We are concerned right now with what we call the global configuration, where we provide values for your default user configuration. Open the Git Bash command window though your start menu. You should find it at Start Menu/All Programs/Git/Git Bash.
The Git Bash Command Line Interface
If you are new to command-line interfaces (I was, and still am), overcome your fears. We’re not going to be doing anything scary, and if you are exploring version control, your are likely a developer of some level, or learning to be. We are not to live in fear of the command line anymore! Follow along for now. I will take an introductory look at command line usage in another post. For the moment, note the following:
- You will notice that the window opens with the text "xivSolutions@XIVMAIN ~” then the next line contains a “$” symbol. The xivSolutions@XIVMAIN is my log-in name on the local system and the name of the local computer: LocalUserName@LocalSystemName
- The “$” symbol you see is the default command prompt. Text you type here represents a command which will be executed when you press the enter key.
- Typing into the command prompt can be finicky. Bash is case-sensitive, and every character counts. Placement of spaces count. While you are following along, read the commands carefully, and type exactly into the command line what you are reading here (or from the images of my window).
- Once you hit the enter key, Bash will either:
- Execute an the command and present you with a new command prompt, or
- If the command was one which is supposed to return data or feedback, the requested information will be displayed, followed by a new command prompt.
- Commands tend (with some variation) to adhere to the format CommandName –Option1 –Option2 . . . –OptionN InputValue
The first thing we want to do is set our Global Username and Global User Email. Git uses these two pieces of information to identify us with, and associate us with each commit to the repository. When we want to access the user’s global config file, we supply the –global option with your command.
Again: Bash, like Linux, is case sensitive. Pay careful attention to typing your commands. While you are unlikely to hurt anything by mistyping, the command will fail to execute if you don’t use the proper case. Additionally, the spacing of commands and options is important.
Set the Global User.Name Property
Type the following into the command window, being careful to use the correct case, and note the single space which precedes the double dash in front of the global option. (the “$” symbol is the command prompt, and should already be visible at the beginning of the line):
git config --global user.name “Your Username”
Git Configuration: The User.Name Value:
When you hit “Enter",” if you have typed correctly, you should be rewarded with . . . a new command prompt.
Git Configuration: After Entering the User.Name Value:
It is a principle of Linux programming that a function or command which executed properly does so silently, unless there is a compelling reason to do otherwise (like when the command is supposed to return data, or report progress). However, we can check to see what happened by typing:
git config –global user.name
Note that this is essentially the command again, but without any input.
Git Config: Confirming User.Name Value – Before Enter:
Then hit the Enter key:
Git Config: Confirming User.Name Value – After Enter:
The line immediately following our command represents the return value, in this case, my user name as set in the previous command. Then Bash presents us with a new command line.
Set the Global User.Email Property
Next we will set the global user email. Type the following into the fresh command prompt:
git config --global user.email “yourEmailAddress”
Git Config - Set User.Email Value:
Then hit enter:
Git Config - Set User.Email Value – After Enter Key:
You can check the value of this setting the same as before: retype the command, sans any input value (In an attempt at brevity, I show the entry and the result in one step this time):
Git Config - Check User.Email Value – After Enter Key:
Congratulations! You have now installed and configured Git on your Windows machine. In the next post, We will look at using git to get some things done.
To this point, we have:
- Downloaded and installed Git for Windows: msysgit
- Walked through the basic installation of git and default set up for your Windows Machine
- Performed the most basic initial configuration of git on your local machine so that you can actually start using git to do meaningful things.
Wow. That seems like a long post just to install an application and do the minimum initial configuration for use. It seems strange to leave this post at this point, because we really haven’t looked at using git for anything useful. But I am trying to break this up into meaningful pieces, of easily digestible length. Next up, we will take a short excursion into using the Bash command line and look at a list of basic, frequently-used git commands.
John on Google
Compile for Distinctly Different Environment Configurations
NOTE: Source code for the example project in this article can be found at my Github repo.
I already understand most of this. Take me straight to the code!
I am currently working on a boring, enterprise-ey database application. You know the type – We needed it yesterday, it was built on the fly with minimal up-front design time, and pressure from on high to deliver SOMETHING right away, even if it sucks, they say, it will be better than what we have now. You KNOW, if you have read this far, that you have worked on projects such as I describe here as well. Probably many. You just don’t want to admit it. It’s OK. Admit it, and move forward. You’ll feel better.
Yes, yes, I know. This is where I am supposed to set management straight on the error of their ways, sell them on the idea of investing in an iterative design cycle, Agile/Test-Driven development methodology, the works.
Not gonna happen here, unfortunately.
SO. As the code progressed and bits and pieces of this monstrosity entered live testing and production, I am having to continue work on the rest of the application, adding features, fixing bugs, etc. Unfortunately, this application must be able to run in a couple of different scenarios:
- On my Dev machine at home (locally).
- On my Dev machine at work (locally).
- On a test server at work (Database and file persistence via LAN)
- On the production server at work (LAN, again)
- On a remote laptop with no LAN connection – files and data persisted locally until home
The database connection information is different for each of these scenarios. Also, this application persists copies of photographs and other files and stashes the directory information in a database record. The actual file location is different in each scenario as well (and hidden from the user). File access is performed through the application. In all cases, the settings required to achieve all this should NOT be available to the user (although ultimately, some level of admin functionality might be incorporated. But not right now . . . ).
My Solution – Set Application Settings at Compile Time
I will say right here that this is most likely a hack solution to what is ultimately an application design issue. I have heard it said that the use of conditional compilation is often a strong code “smell” indicating a potential area for redesign. However, in my case, I needed a fast answer.
I have identified five possible environments within which the application must run, each of which require the same code base, but different application settings. For this example, I will leave the default Debug and Release configurations as they are, and simply create two additional configurations, LIVE and TEST. Once I have done this, I can define Conditional Compilation symbols, and load different application settings depending upon which build configuration is running.
Set Up Build Configurations Using Build Configuration Manager
First, I will use the Build Configuration Manager within Visual Studio to create my Build Configurations. Configuration Manager allows you to create, customize, and manage build settings for all projects within the current solution. For this example, there is only a single project. Open Build/Configuration Manager . . .
From the drop-down menu in the Configuration Manager, select <New>:
I create a new Build Configuration named LIVE, and copy settings from the default Release configuration:
I then repeat these steps to create a TEST configuration (and as many others as I might need).
Since I am creating a simple example project for this post, there is only one project visible in the Configuration Manager list. However, if there were additional projects within this same solution, I would be able to assign different configurations to each, an indicate specific projects within the solution to build or not.
Define Conditional Compilation Symbols for Each Build Configuration
Now we need to define Conditional Compilation symbols for each build configuration. Open the Project Properties window from the solution explorer, and select the “Build” tab. in the dropdown list of available configurations, you will now see the default Debug and Release options, as well as your newly defined LIVE and TEST configurations. First we will select the LIVE configuration, and add a Conditional Compilation symbol in the space provided. Because I like to keep things simple, we’ll use “LIVE” as our symbol:
Then select the TEST configuration from the drop-down menu, and repeat the same steps, typing “TEST” into the Conditional compilation symbols space.
We have now defined a symbol for each of our two build options. Now let’s look at how we can use these in our application to load build specific application settings at compile time.
Define Version-Specific Resources, and General Application Settings
Ok. Here’s the meat and potatoes of the whole thing. First, we need to define our application settings. One of the reasons I chose to take this approach is that application settings can be accessed and modified without having to recompile the application. Same with resource files. For ease of making changes to a compiled application which is being used for production, I am going to define a couple of output file paths, and save them as resource files. I will also create a string message for each version, which we will use for illustrative purposes in this example.
First, create text files with the following text, either in your preferred text editor, or using the “Add New Text File” option in the Resource Manager:
This is the LIVE version of the Application
This is the TEST version of the application
Add the resource files to your project in the Resource Designer:
Next, define version-agnostic Application Settings using the Settings Designer like so (leave the values empty – these will be set at compile-time). Make sure to set the scope for each setting to “Application” and not “User”. Name one setting “BuildVersionMessage” and the other “OutputFolderPath”:
Ok. See that “View Code” menu item near the top of the Settings Designer window? Click on that. You should see a code file open to display a partial class file for the Settings Designer. This is where we get down to business.
Code: Setting Application Settings Values at Compile Time
When the Settings.cs file first opens, you will see some VS-generated code in the file. I deleted all that – we’re not using it here. Replace it with the following code (the complete source code for this example project is available from my Github repo):
8/19/2012 8:00 AM - UPDATE: I added similar code for the default “Debug” and “Release” configurations to the example project in my Github repo. The only difference is that these two methods explicitly specify Environment.CurrentDirectory as the root directory instead of using a resource file to grab the directory path. If you run the following code as-is under the Debug or Release build configurations, things won’t work the way you expect!
1: using System.Diagnostics;
3: namespace WindowsFormsApplication1.Properties
6: internal sealed partial class Settings
8: public Settings()
10: // Each method corresponds to a build version. We call both methods, because
11: // the conditional compilation will only compile the one indicated:
17: private void SetLiveApplicationSettings()
19: // Set the two Settings values to use the resource files designated
20: // for the LIVE version of the app:
21: this["BuildVersionMessage"] = Resources.LiveVersionMessage;
22: this["OutputFolderPath"] = Resources.LiveOutputFolderPath;
27: private void SetTestApplicationSettings()
29: // Set the two Settings values to use the resource files designated
30: // for the TEST version of the app:
31: this["BuildVersionMessage"] = Resources.TestVersionMessage;
32: this["OutputFolderPath"] = Resources.TestOutputFolderPath;
Note the use of the Conditional Attribute For the SetLiveApplicationSettings and SetTestApplicationSettings methods. Because we have used this attribute, and made reference to our version-specific Compilation Symbols for each, the code for either of these methods will only be compiled for that specific version identified in the Conditional Attribute. We call both methods from the Settings() constructor, so that whichever version is compiled will be called during initialization.
What is important here is to get the string representations for each of the Compilation Symbols correct, as well as the Setting names, used as keys to access the specific Settings properties. Misspellings here will cause the code to fail.
Demonstration of Conditional Compilation
To demonstrate how this works, create a simple Form in the designer, like this:
Name the Label “lblHeader” and the Button “btnCopyFile” (yes, I still go a little Hungarian with my control names. Makes them easier to find with Intellisense, dammit . . .).
Now, use this code in the form (the complete source code for this article is available at my GitHub repo):
public partial class Form1 : Form
this.btnCopyFile.Click += new System.EventHandler(this.btnCopyFile_Click);
private void Form1_Load(object sender, EventArgs e)
// The BuildVersionMessage setting will be set at compile time:
this.lblHeader.Text = Properties.Settings.Default.BuildVersionMessage;
private void btnCopyFile_Click(object sender, EventArgs e)
private void CopyFile()
// The correct output file path will be defined at compile time,
// and made available through the settings file:
string outputDirectory = Properties.Settings.Default.OutputFolderPath;
// Make sure the directory exists:
using (var fileDialog = new OpenFileDialog())
fileDialog.Multiselect = false;
if (fileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
string oldFullFileName = fileDialog.FileName;
string safeFileName = fileDialog.SafeFileName;
// Create a new File Name using the output directory
// defined at compile time:
string newFullFileName = outputDirectory + safeFileName;
// Check to see if a file with the same name already exists:
// File.Copy won't let us overwrite. Since the user has no knowledge
// of this directory, we just delete the old, and save the new:
// Copy the file into our secret hidden directory:
// Add code here to persist the file path and other information
// to the data store for access within the application . . .
Now, build and run the application under each of the LIVE and TEST Build Configurations. When we set the build configuration to TEST and run, the form opens as follows:
If we press the Copy File button, select a file, and hit OK, the file will be saved in the directory specified in the file we named TestOutputDirectory.txt. We can go to that directory in Windows Explorer and sure enough, there is a copy of the original file. Likewise, the text displayed in the form label is the text we saved in the TestVersionMessage.txt file.
When switch to the LIVE build configuration, we see similar results. What’s cool here is that if we needed to, we could open the text files which serve as out string resources and modify the directory paths or message text, save, and these changes would be incorporated into our running application without recompiling. While this is not a new thing, we still maintain the integrity of our build-specific settings – the LIVE version will load those settings specific to that build, and likewise the TEST version.
While this example has been simplified for the purpose of explaining what I did, the reality is that I was able to address the varying database connection and output folder requirements of my distinct application versions and tailor each to the environment in which it would run. When I compile and run the application on my home machine, I simply select the HOME build configuration. No messing about setting database connection settings or file directories. Likewise when I have added some new features to the production build. I simply select the PRODUCTION build configuration, and the network serve database connection and LAN network shares are all appropriately set so that code which requires those settings simply works.
More information on Conditional Compilation and Conditional Methods in C#/VS:
Source code for example project:
John on Google