You are on page 1of 58

Table of Contents

1. Introduction
2. The Deck
3. Life in the Terminal
i. Why Terminal?
ii. Pimp your Terminal
iii. Meet Bash
iv. File Nav
v. Cheat sheet
vi. Open in VI
vii. In summary
4. Git workflow
i. Branching
ii. Branch Management
iii. Push
iv. Pull request
v. Merge the code
vi. Shortcuts using aliases
vii. Summary
5. The Git Demo
i. The folders
ii. The files
iii. The content
iv. Branching
v. The delta
vi. The merge
vii. Kill w/fire
6. Github
7. Fork a repo
8. Clone a repo
9. Auto complete

Git for everyone


Unless you have been living in a cave, Git is everywhere. Creative or engineering, you have directly encountered or will
encounter Git in the workplace. A common misconception as well is that Git is only for teams? Regardless of work style or
team size, Git is the tool that everyone should be using.
Dale Sande, UX Design and Development senior instructor with Code Fellows, will introduce you to Git and get you up to
speed on concepts and workflows in no time.
Discover how working in the command line isn't all that scary. Understand how to move between projects with just a few
key strokes. Visually experience what Git is doing behind the scenes. Gain confidence with the steps involved for a
common Git branching strategy. Cloning isn't just for sheep and galactic empires.
Together we will all create and share Github repositories and understand why Groundhog's Day is the best Git movie
EVER!
Course material:
This course will be presented with a slide deck, live demonstration and Gitbook provided with related material.

Presentation outline:
Life in the Terminal
What is branching?
Git workflow (branching and merging)

Demo outline:
Create local repo
Add content and commit code
Branch the code
Merge all the codes
Fork a repo

Workshop outline:
Getting Git Installed
Exercise Git workflow
Create a Github account and generate your SSH key
Fork a repo
Clone a repo

Git for everyone: the deck


by Dale Sande
Published June 18, 2014 in Programming

Life in the Terminal


Or Bash to the UNIX kids
If you are reading this, you have been introduced to something called the Terminal on your Mac by one of your overly
pushy developer friends/colleagues.
I am thinking that the conversation went something like this?
You: How can I get to that file?
Them: Just open Terminal and then

$ cd ~/Projects/boilerplate/ && vi .gitignore

You: OMG! That is weird? What do I do?


Them: Geeze, press the
type

:wq

key, go to this line and add

<file path>/**/*

. Then hit the

esc

key to save. To get out,

, ok?

Ok, we have all been there. This is weird. This is awkward. What did all of that mean?
Not to mention, your terminal window is probably all stupid looking, am I right? On a developers screen, it's all cool
looking. There is colored text that you can read and it's probably a little transparent. Yours, it's small, the type is hard to
read, and it's white and weird looking. No wonder you don't like going in there. It's freaking weird and scary looking, I get
that.
In this article, I aim to help change all of that and make your interaction with the Terminal a little bit easier.

What's the big deal anyway?


What's the big deal with Terminal lately anyway? What's wrong with good 'ole GUI tools that we can download and install
using the all too familiar install wizard? To sum it up in three words, it's speed, power and accessibility.
Open source computing created a slew of new tools and most developers are not about creating OS specific applications
to run these little apps. Not to mention, the majority of these developers were/are UNIX devs and they LIVE in the shell.
A shell in computing provides a user interface for access to an operating system's services.
As these tools became more mainstream with developers, especially with devs on Macs, the natural thing was to adopt
the use of the Terminal. Sure, there have been some who created GUI apps for some of the more popular tools, but they
never really live up to the flexibility and speed that you can get in the Terminal. Not to mention, you are now at the behest
of the developer to update their GUI tool as new features are released.

Pimp your Terminal


The first step to making the Terminal easier to use it to make it easier to look at and work with. The Terminal's application
preferences give us some things to start with:

If you are new to Terminal, on the

Startup

tab, there isn't much you should really touch there.

The 'Settings' tab


Under

Profiles

select the theme that would like to use

Be sure to click the

default

button at the bottom so that this becomes your default theme when opening a new

Terminal session

The 'Text' sub-tab


Font: You want to update this to your preference. Monaco 14pt is a great looking font IMHO. Characters are distinct and
are difficult to confuse, which is important.
Text: Check and uncheck these options until the terminal looks good to you. Personally, I like to have the following
checked:
Antialias text
Use bold fonts
Allow blinking text
Display ANSI colors

To the right of

Antialias text

is a color well, I suggest changing this to a color you prefer.

ANSI Colors: For the inexperienced users I suggest leaving those alone.
Cursor: Select a style of cursor you like and if you want it to blink or not and you can change the cursor color.
Not sure what it is, but developers for some reason really like to tweak out their Terminal to look like old skool green
screen terminal computers. Myself, my first computer screen was amber, so I have a nostalgic connection to this and an
amber screen has been claimed to give improved ergonomics, specifically by reducing eye strain. There is no proof to
this, but who needs science?

The 'Window' sub-tab


Title: This is what will appear in the gray bar at the top of the Terminal window. The checkboxes below, with a terminal
window open, check/uncheck these options to see if they are of any value to you.
Background: This is the fun one. Click on the color well for

Color & Effects

. This is where you can set your background to

a color and adjust the transparency. There is a feature to set a background image, but this tends to be really distracting.
Window size: This will set the default new open window size.
And that is it for here. At this point, you should have a Terminal window that is looking pretty cool and makes you feel a
little but more comfortable working with it.

Meet Bash
Ok, now that you have your Terminal window looking nice, the fun really starts as we get to know what is inside.
Meet Bash. Terminal is the app that runs Bash on your computer. What is Bash?
... an acronym for the Bourne-Again SHell, a pun on Stephen Bourne, the author of the direct ancestor of the
current Unix shell
That's not very useful, but it is interesting. What you really need to know is that when you are looking up more information
on how to do things in Terminal, you will more then likely find references to this thing called Bash. Don't get confused as
the answer you are looking for is most likely there.
Also, keep in mind that Bash is a fully featured environment that is able to run applications and completely interact with
it's host OS. This is extremely powerful and maybe you are seeing why developers prefer this.
But pay that no mind at this point. For our purposes, we are going to only go as far as the features that we need to
understand.

.bashrc, .bash_profile
Probably one of the more confusing things of working with Terminal and Bash is how they are configured. Bash uses two
(dot) files that are located in your computer's home directory. They are

.bashrc

and

.bash_profile

BTW, a (dot) file is any file in the computer's file system where the name's first character is a dot. As in

.bashrc

. The

service this provides is that the file is hidden from normal view.
OSX keeps you from being able to rename a folder or file in the standard GUI for this reason. But ... we can do this from
Bash.
.bash_profile : read and the commands in it executed by Bash every time you log in to the system
.bashrc : read and executed by Bash every time you start a subshell
When using these files, there are some actions best suited for the

.bashrc

and others in the

.bash_profile

. Keeping this

straight can drive you a little crazy.


Luckily, there is a way that makes this all easy to manage. Simply put all your stuff in the

.bash_profile

file. In order to get

this to work, the two files need to be linked, we do this through what is called sourcing. By placing the following code in
your

.bash_profile

, as Terminal loads the

.bash_profile

it will also loop in the

.bashrc

file.

if [ -f ~/.bashrc ]; then
source ~/.bashrc
fi

Ok, at this time we will just let that sit there. At this point we haven't discussed how to find files in Terminal or how to open
and edit files either. Further down this tutorial I will illustrate how to edit the content of these files from within the Terminal.

File navigation
By now you should have a pretty cool looking Terminal window and have been introduced to some base concepts like
Bash and it's core config files. It's time now to learn how to navigate around in this new text based world.
When you first open Terminal you typically are placed in your home directory. So let's start looking and moving around.

Note: in the following command examples, you will see the

symbol. You are NOT to type the

with the command.

This is simply to illustrate that you should enter this command into the Terminal window.
The first command we will learn is
learn is the

$ cd

. What this does is Change Directory. Get it? With

$ cd

the next symbol we will

. This symbol is simply an alias to your home directory. So, in your Terminal, do the following:

$ cd ~

Doing this will put you in your home directory. To make sure, in the Terminal enter

$ pwd

and you should see:

/Users/[your user name]

Great. But at this point there is a good chance that your Terminal is not telling you very much information. You know
where you are, but what's in your current directory? To list out the contents of your directory, do the following:

$ ls

Great! Now you can see all the files and folders contained within your User's directory. This isn't very exciting, let's get into
some fun stuff. If you are like me, you have any number of files and folders on your Desktop. All the contents of your
Desktop are contained within your

Desktop

directory in your User's folder. So, let's change directory into

Desktop

by

doing the following:

$ cd Desktop/

And then list the items:

$ ls

Remember those (dot) files? You may notice that when you do a

$ ls

you only see files and folders that you can see in

the standard OS GUI. Commands in Bash can have additional attributes passed into them called flags. To see the hidden
files on your desktop pass in the

-la

flag like so:

$ ls -la

Now, listed in the Terminal you should see hidden files and folders, also you can see permissions, file size, modified
dates as well as some other information. Here is an example from my Desktop directory.

Notice is that it may be difficult to distinguish between files and folders. How can we make this easer? Passing in the
flag with an

ls

command will instruct Bash to list out directories with a trailing

-p

. To see this, do the following:

$ ls -p

Great! Now we can see the contents of your Desktop directory and easily tell the difference between a file and a folder.
Now we can easily see that my PeepCode content is in a directory.

Continue playing with this and


Done? Did you

$ cd

$ cd

further into folders within your desktop.

into a folder on your desktop and get stuck? I told you how to go in, but not how to back out.

Changing directories backwards is just as easy, from within a directory, do the following:

$ cd ../

That command will back you out one level from where you are. If you wanted to back out two directories, simply add
another

../

like so:

$ cd ../../

By now I am betting that your Terminal window is looking a bit messy. By entering the clear command, this will clean up
your view and give you a nice clean Terminal window to work with:

$ clear

At this point, you are doing really awesome. You have mastered some of the core commands for using Bash in the
Terminal app.

File and directory management


Next we need to learn how to add, edit and delete files and directories. While I admit, it's a little weird at first, it's actually
pretty simple once you understand the commands.

Create / Delete files


Starting off, creating a new file is amazingly easy and very powerful. You can create any type of file with any type of file
extension from Bash and the Terminal. The utility command you want to use is

touch

. This utility is capable of multiple

features, but the one that we care about is creating a new file when one does not exist.
To do this, simply use the

touch

command, add on the name of the file you want to create and the file extension. To

create a simple text file called "new-file" from within the Terminal, enter the following:

$ touch new-file.txt

Say you wanted to create a new "index.html" file? To do so, use the following:

$ touch index.html

"style.css"? Do the following:

$ touch style.css

I think you are starting to get the point here. Remember when we spoke of (dot) files and that you can't create them from
the default OSX GUI? This can easily be addressed in the Terminal by placing the (dot) in the name like so:

$ touch .config

Remember, you can't see this file in the normal OSX GUI, so from where you are, use the

ls -la

command to see this file.

This brings us to the next part, how do you delete a file from the Terminal? To do this, we use the
which is short for remove. Using this command, we can remove that

.config

rm

UNIX command

file we previously created by doing the

following:

$ rm .config

Running this command will simply delete this file and only return feedback if there is an error, for example:

rm: .config: No such file or directory

Let say for example that you are not really confident with this and want to make sure that you are deleting the correct file.
To do this, simply pass in the

-i

flag. This will instruct the command to prompt you for confirmation. Here is an example of

creating a file, using that prompt flag and addressing the prompt.

Desktop$ touch .config


Desktop$ rm -i .config
remove .config? y
Desktop$

Create / Delete directories


Now that we have files out of the way, what about folders? In the world of Bash and Terminal, folders are actually referred
to as directories. The UNIX command we are looking for is
Desktop, let's create a new folder called

foo

mkdir

, which is short for make directory. For example, on your

$ mkdir foo

Now when you do a

ls -p

from within your Desktop you should see this new

foo/

directory we created.

ProTip: You don't actually have to be in the directory you want to create a new directory in. Remember the

alias for

your Home directory? For example, imagine you are in your Dropbox folder and you want to create a new folder on your
Desktop. You could do the following:

Dropbox$ mkdir ~/Desktop/foo

The next real-world thing you will run into is how to make a directory structure that is x number of levels deep. Making a
directory, then changing into that directory just to make another directory is turtles all the way down.
There is a better way. When using the

mkdir

command, simply add on the

-p

flag and this will create all directories

leading up to the given directory that does not already exist. If it does exist, ignore the error. Let's have fun here, run the
following in your Terminal:

$ mkdir -p ~/Desktop/turtles/all/the/way/down

Now that we have mastered creating directories, what about deleting them? Remember how we used the
to delete a file? We need to use this, but you can't simply delete a directory, we need to add on the

-r

rm

command

flag which means

to remove directories and removing the contents recursively beforehand. So to remove our turtles directory, we would run
the following:

$ rm -r turtles/

Let's imagine that there is a case where there would be non-existent files confirmation prompts that get in your way of
deleting these directories. What is common is to add on the

-f

flag to force the delete like so:

$ rm -r -f turtles/

For most developers, adding the


simply be

-rf

-f

flag is just common place and as a short hand, you can combine the

and do something like the following:

-r -f

flag to

$ rm -rf turtles/

Warning: Using the

-f

flag to force something and ignore any prompts, pay attention to what you are doing. This

command will delete anything in it's path with no hesitation.

Move/copy/rename files and directories


Protip: When you are typing names of files and directories, start typing the first characters and then hit the
Terminal will attempt autocomplete the name. If it can't hit the

tab

tab

key.

key twice and Terminal will list out all the possible

options.
Now that we have created all these files and directories using the Terminal, the next obstacle to over come is to move
and rename things.
Starting with the basics, renaming things in Bash uses the

mv

command which is short for *move*. The

mv

command

takes two arguments, the first is the file you want to change and the second is the result you want.

$ mv <source> <target>

For example, using what we have learned so far and adding in the new
Desktop within a project directory and then rename it:

$
$
$
$
$

cd ~/Desktop
mkdir -p project/public/stylesheets
cd project/public/stylesheets
touch stylesheet.css
mv stylesheet.css app.css

mv

command, let's create a new file on the

Great! Now let's say that we wanted to move that new

app.css

file to a new location. Remember, the

used for renaming is really to move things. Since we are still in that
the root of the

public

stylesheets

directory. To do this, use the following command:

$ mv app.css ../../public/

In that example, we could also rename the file if needed, for example:

$ mv app.css ../../public/style.css

mv

directory, let's move the

command we
app.css

file up to

Moving directories is much like moving files. For example, let's say that we created a directory at the root of
javascripts

. Oops! We need that directory to be within

and then the destination, as this example illustrates:

$ mv javascripts/ public/

public

. To do this, use the

mv

project

called

command and pass in the source

Renaming directories goes the same way. Say that we had a directory on the Desktop called
it to be

bar

foo

and we need to rename

as shown in the following example:

$ mv foo/ bar/

The last thing we will talk about here is the process of copying a file from one location to another. For this we need to use
the

cp

UNIX utility, short for *copy*.

cp

is much like

mv , but instead

of moving the file it makes a new copy of the original

in the new location. For files, the process should be familiar by now:

$ cp <source> <target>

What you should also know is that in defining the target for the copied version of the file, you can also rename it. For
example, and what is very common is that in a project there will be an example configuration file. It is then left up to the
developer to copy that file, update the name and then edit the configurations:

$ cp .config.exmaple .config

Copying directories is much like copying files, except for the fact that they have directories within, so we need to pass in
the

-r

flag for recursive. It should also be noted that when you copy a directory, depending on how you refer to the

directory in the command determines the outcome.


Let's say that we have
file called

two.html

directory_1

and then

directory_2

. Inside

directory_1

is a file called

. In this first example, I will simply refer to the source and target:

one.html

. Inside

directory_2

is a

$ cp -r directory_1 directory_2

By doing this, the command will copy

But by adding the whack

directory_1

and it's contents into

directory_2

into the command on a directory, this will only copy the contents of the directory in to the new

location.

$ cp -r directory_1/ directory_2

This concludes the file navigation portion of this tutorial. If using Terminal is new to you and you made it this far without
throwing your computer out the window, I applaud you! This is a huge step forward and you are now moving onto bigger
and better things!

Terminal Cheatsheet for Mac ( basics )


Keyboard Shortcuts
Key/Command

Description

Tab

Auto-complete files and folder names

Ctrl + A

Go to the beginning of the line you are currently typing on

Ctrl + E

Go to the end of the line you are currently typing on

Ctrl + U

Clear the line before the cursor

Ctrl + K

Clear the line after the cursor

Ctrl + W

Delete the word before the cursor

Ctrl + T

Swap the last two characters before the cursor

Esc + T

Swap the last two words before the cursor

Ctrl + R

Lets you search through previously used commands

Ctrl + L or Command + K

Clears the Screen

Ctrl + C

Kill whatever you are running

Ctrl + D

Exit the current shell

Core Commands
Prompt

Description

$ cd

Home directory

$ cd [folder]

Change directory

$ cd ~

Home directory, e.g. 'cd ~/folder/'

$ cd /

Root of drive

$ ls

Short listing

$ ls -l

Long listing

$ ls -a

Listing incl. hidden files

$ ls -lh

Long listing with Human readable file sizes

$ ls -R

Entire content of folder recursively

$ sudo [command]

Run command with the security privileges of the superuser (Super User DO)

$ open [file]

Opens a file

$ open .

Opens the directory

$ top

Displays active processes. Press q to quit

$ nano [file]

Opens the Terminal it's editor

$ pico [file]

Opens the Terminal it's editor

$q

Exit

$ clear

Clear screen

Command History
Prompt

Description

$ history n

Shows the stuff typed - add a number to limit the last n items

$ ctrl-r

Interactively search through previously typed commands

$ ![value]

Execute the last command typed that starts with 'value'

$ !!

Execute the last command typed

File Management
Prompt

Description

$ touch [file]

Create new file

$ pwd

Full path to working directory

$ ..

Parent/enclosing directory, e.g.

$ ls -l ..

Long listing of parent directory

$ cd ../../

Move 2 levels up

$.

Current folder

$ cat

Concatenate to screen

$ rm [file]

Remove a file, e.g. rm [file] [file]

$ rm -i [file]

Remove with confirmation

$ rm -r [dir]

Remove a directory and contents

$ rm -f [file]

Force removal without confirmation

$ rm -i [file]

Will display prompt before

$ cp [file] [newfile]

Copy file to file

$ cp [file] [dir]

Copy file to directory

$ mv [file] [new filename]

Move/Rename, e.g. mv -v [file] [dir]

Directory Management
Prompt

Description

$ mkdir [dir]

Create new directory

$ mkdir -p [dir]/[dir]

Create nested directories

$ rmdir [dir]

Remove directory ( only operates on empty directories )

$ rm -R [dir]

Remove directory and contents

Pipes - Allows to combine multiple commands that generate output


Prompt

Description

$ more

Output content delivered in screen-size chunks

$ > [file]

Push output to file, keep in mind it will get overwritten

$ >> [file]

Append output to existing file

$<

Tell command to read content from a file

Help
Prompt

Description

$ [command] -h

Offers help

$ [command] --help

Offers help

$ [command] help

Offers help

$ reset

Resets the terminal display

$ man [command]

Show the help for 'command'

$ whatis [command]

Gives a one-line description of 'command'

Open and edit files using VI


Ok, gonna get a little rough here, but I am sure you can do it. Let's add some color to your Terminal experience. To do
this, we need to add some code to your

.bash_profile

. The trick here is that it's difficult to open hidden files using most

editors that rely on a typical finder. UNIX comes with a text editor called VI built in that you can access from the Terminal.
To get started, make sure that you are in your home directory:

$ cd ~

Now, open the file in VI:

$ vi .bash_profile

In your Terminal, you will have open this file in VI mode, so things are a little different. In order to add content, you need to
hit the

key. This open the

-- INSERT --

mode and add the following:

export CLICOLOR=1
export LSCLOLOLORS=GxFxCxDxBxegedabagaced

To save these edits, hit the

esc

key. To exit VI mode, type

:wq

. I know, all a little weird, but that's how it works. Welcome

to VI.
Ok, for the fun part. If you do a

ls -p

here, there will be no change. To get updated to the

have to do what is called 'sourcing'. To do this, run:

.bash_profile

to take effect, you

$ source ~/.bash_profile

Now, enter

ls -p

and your directories will be a different color from your files.

In conclusion
There is an amazing amount of material here to consume. If you are new to using the Terminal, you may have to run
through these a few times to really get them locked in. But trust me, once you start down this path, there is no turning
back. Running your computer from the Terminal is efficient, powerful and fast.

Git basics - a general workflow


There are many Git workflows out there, I heavily suggest also reading the atlassian.com Git Workflow article as there is
more detail then presented here.
The two prevailing workflows are Gitflow and feature branches. IMHO, being more of a subscriber to continuous
integration, I feel that the feature branch workflow is better suited.
When using Bash in the command line, it leaves a bit to be desired when it comes to awareness of state. I would suggest
following these instructions on setting up GIT Bash autocompletion.

Basic branching
When working with a centralized workflow the concepts are simple,

master

represented the official history and is always

deployable. With each now scope of work, aka feature, the developer is to create a new branch. For clarity, make sure to
use descriptive names like

transaction-fail-message

or

github-oauth

for your branches.

#Protip: Although you may have a feature like 'user login and registration`, this is not considered appropriate to create a
feature branch at this level, there is too much work to be done. It is better to break these large deliverables down to
smaller bits of work that can be continuously integrated into the project. Remember, commit early and often.
Before you create a branch, be sure you have all the upstream changes from the

origin/master

branch.

Make sure you are on master


Before I pull, I make sure I am on the right branch. I have GIT Bash autocompletion installed, this tells me the branch in
the prompt. Otherwise, the following command is good to know to list out the branches I have locally as well designate
which branch I am currently on.

$ git branch

The checked out branch will have a

before the name. If the return designates anything other then

master

then switch

to master

$ git checkout master

Once on master and ready to pull updates, I use the following:

$ git pull origin master

The

git pull

command combines two other commands,

git fetch

and

git merge

. When doing a

fetch

the resulting

commits are stored as remote branch allowing you to review the changes before merging. Merging on the other hand can
involve additional steps and flags in the command, but more on that later. For now, I'll stick with

git pull

Now that I am all up to date with the remote repo, I'll create a branch. For efficiency, I will use the following:

$ git checkout -b my-new-feature-branch

This command will create a new branch from


branch

master

as well checkout out that new branch at the same time. Doing a

will list out the branches in my local repo and place a

before the branch that is checked out.

master
* my-new-feature-branch

Do you have to be on master to branch from master?


No. There is a command that allows me to create a new branch from any other branch while having checked out yet
another branch. WAT?!

git

$ git checkout -b transaction-fail-message master

In that example, say I was in branch


branch? By adding

master

github-oauth

and I needed to create a new branch and then checkout the new

at the end of that command, Git will create a new branch from master and then move me

(checkout) to that new branch.


This is a nice command, but make sure you understand what you are doing before you do this. Creating bad branches
can cause a real headache when trying to merge back into master.

Branch management
As I am working on my new feature branch, it is a good idea to commit often. This allows me to move forward without fear
that if something goes wrong, or you have to back out for some reason, I don't lose too much work. Think of committing
like that save button habit you have so well programed into you.
Each commit also tells a little bit about what I just worked on. That's important when other devs on the team are reviewing
my code. It's better to have more commits with messages that explain the step versus one large commit that glosses over
important details.

Commit your code


As I am creating changes in my project, these are all unseated updates. With each commit there most likely will be
additions, and there will also be deletions from time to time. To get a baring of the updates I have made, lets get the
status.

$ git status

This command will give you a list of all the updated, added and deleted files.
To add files, I can add them individually or I can add all at once. From the root of the project I can use:

$ git add .

In order to remove deleted files from the version control, I can again either remove individually or from the root address
them all like so:

$ git add -u

I'm lazy, I don't want to think, so the following command I make heavy use of to address all additions and deletions.

$ git add --all

All the preceding commands will stage the updates for commitment. If I run a
presented differently, typically under the heading of

Changes to be committed:

git status

at this point, I will see my updates

. At this point, the changes are only staged

and not yet committed to the branch. To commit, do the following:

$ git commit -m "a commit message in the present tense"

It is considered best to illustrate your comment in the tense that this will do something to the code. It didn't do something
in the past and it won't do something in the future. The commit is doing something now.
A bad example would be:

$ git commit -m "fixed bug with login feature"

A good example would be:

$ git commit -m "update app config to address login bug"

Comments are cheap. For more on how to write expressive commit messages, read 5 Useful Tips For A Better Commit
Message.

Push your branch


When working with feature branches on a team, it is typically not appropriate to merge your own code into master.
Although this is up to your team as how to manage these expectations, but the norm is to make use of pull requests. Pull
requests require that you push your branch to the remote repo.
To push the new feature branch to the remote repo, simply do the following:

$ git push origin my-new-feature-brach

As far as Git is concerned, there is no real difference between

master

and a feature branch. So, all the same Git features

apply.

My branch was rejected?


This is a special case when working on a team and the branch I am are pushing is out of sync with the remote. To
address this, it's simple, pull the latest changes:

$ git pull origin my-new-feature-branch

This will fetch and merge any changes on the remote repo into my local brach with the changes, thus now allowing you to
push.

Working on remote feature branches


When I am are creating the feature branch, this is all pretty simple. But when I need to work on a co-workers branch, there
are a few additional steps that I follow.

Tracking remote branches


My local

.git/

directory will of course manage all my local branches, but my local repo is not always aware of any remote

branches. To see what knowledge my local branch has of the remote branch index, adding the

-r

flag to

git branch

will

return a list.

$ git branch -r

To keep my local repo 100% in sync with deleted remote branches, I make use of this command:

$ git fetch -p

The

-p

or

--prune

flag, after fetching, will remove any remote-tracking branches which no longer exist.

Switching to a new remote feature branch


Doing a

git pull

or

git fetch

will update my local repo's index of remote branches. As long as co-workers have pushed

their branch, my local repo will have knowledge of that feature branch. By doing a
branches. By doing a

git branch -r

git branch

you will see a list of my local

I will see a list of remote branches. There is a good chance that the new feature branch

is not in my list of local branches.


The process of making this remote branch a local branch to work on is easy, simply checkout the branch.

$ git checkout new-remote-feature-branch

This command will pull it's knowledge of the remote branch and create a local instance for me to work on.

The Pull request


The pull request is where the rubber meets the road. As stated previously, one of the key points of the feature branch
workflow is that the developer who wrote the code does not merge the code with master until there has been a peer
review. Leveraging Github's pull request features, once you have completed the feature branch and pushed it to the repo,
there will be an option to review the diff and create a pull request.
In essence, a pull request is a notification of the new code in an experience that allows a peer developer to review the
individual updates within context of the update. For example, if the update was on line 18 of
only see

header.haml

header.haml

, then you will

and a few lines before and after line 18.

This experience also allows the peer reviewer to place a comment on any line within the update. This will be
communicated back to the editor of origin. This review experience really allows for everyone on the team to be actively
involved in each update.
Once the reviewer has approved the editors updates, there are two ways to merge in the code. One from the Github
interface and another from the command line.

Merging the code


Although I can merge from Github's interface, it is preferred to do it from the command line. After all, what happens if
Github merging tools are broken? It can happen.
So let's assume that I created a feature branch, edited code and pushed it to the repo. I initiated a pull request and the
code update was approved. Here are the steps I will go through to merge the new code. Keep in mind, this illustration is
simply managing code, this is not including running tests, while it can be worked into this workflow, that is a separate
concern.

Latest version
Make sure that I have the latest version of the feature branch from the remote repo

$ git checkout my-feature-branch


$ git pull origin my-feature-branch

Branch is up to date
Make sure that the feature branch is up to date with

master

, while in the feature branch, execrate the following:

$ git pull origin master

If there are any conflicts, best to address them here.

Merge the branches


Now that I know that the feature branch is up to date with the remote repo and that it has the latest code from
can now merge these branches. I also need to make sure that my local

master

master

,I

branch is up to date as well.

$ git checkout master


$ git pull origin master
$ git merge --no-ff my-feature-branch

Notice the

--no-ff

flag in the merge command. This flag keeps the repo branching history from flattening out. If I were to

look at the history of this branch, using GitX for example, when using the

--no-ff

flag, I will see the appropriate bump

illustrating the history of the feature branch. This is helpful information. If I didn't use this flag, then Git will move the
commit pointer forward.
I can either enter the

--no-ff

flag each time I merge or I can set this as my default. I prefer the default option. Running the

following command will update the global gitconfig.

$ git config --global merge.ff false

And of course, setting the bit back to

true

, will return the default setting to fast forward with merging.

NOTE: there is a mild side-effect that will happen when you set this flag to

false

. Every time you do a

pull

this will not fast

forward your local repo and basically make this a new commit. That's ok, but you will see this in the commit logs

Merge branch 'master' of github.com: ...


and
# Your branch is ahead of 'origin/master' by 1 commit.

If you are ok with this, then keep the flag. If this annoys or bothers you, do not use the flag and set

--no-ff

manually with

each merge.

Delete the local branch


Now that I have merged the code, the feature branch by definition is obsolete. First, delete the branch from the local repo.

$ git branch -d my-feature-branch

The

-d

flag for delete will typically delete any branch. Remember, you can't delete a branch you have checked out.

If you happen to see the following error when deleting a branch, then simply replace the

-d

with

-D

to force the delete.

error: The branch 'name-of-branch' is not an ancestor of your current HEAD.

Delete the remote branch


If the feature branch was pushed to the repo, as it should have been per the workflow we described, you will want to
delete this from the remote repo as well.

$ git push origin --delete my-feature-branch

Shortcuts using aliases


There are some steps in there that we should just be doing all the time. What about making a single command alias that
will cycle through all these commands just so we know things are always in good shape? Yup, we can do that.

In Bash
Using Git and Bash is like using a zipper and pants. They just go together. Creating a Bash alias is extremely simple.
From your Terminal, enter

$ open ~/.bash_profile

This will open a hidden file in a default text editor. If you have a shortcut command for opening in an editor like Sublime
Text, use that to open the file. In the file add the following:

alias refresh="git checkout master && dskil && git pull && git fetch -p"

The alias

dskil

is useful for removing annoying

.DS_Store

files. You should have a

.gitignore

file that keeps these out of

version control, but I like to keep a clean house too. To make that work, add the following:

alias dskil="find . -name '*.DS_Store' -type f -delete"

With this in your

.bash_profile

, you simply need to enter

refresh

in the command line and POW!

In Powershell
If you are on Windows and using Powershell, you can use the same aliases, but the set up is different. The article
Windows PowerShell Aliases is a good tutorial of the process.

Summary
Following this simple workflow has helped keep my projects clean and

Git hell

free. I hope it serves you well too.

But ... the story doesn't end here. Well, the post does, but the story does not. There are many more advanced features that
your team can use. I am sure some of you reading this will say "What about rebasing?" This is a perfectly practical
addition to this workflow and many teams use it. But out of all the teams I have ever worked for, only one has ever made
use of this feature. Just saying.
Outside of that, for more in-depth learning on Git, I invite you to read the Git book, it's free and contains awesome
learning.

Git Demo
The easiest way to understand what Git is doing is to 'see' what it is doing. For this demo, the exercise is simple, we will
use your desktop, a folder and some simple HTML files to illustrate what Git it really doing.

The folder
On your

/Desktop

let's create a folder and call it

gitDemo

. The following steps will make sure that you are on the Desktop

of your computer, make a new directory and change into that directory.

$ cd ~/Desktop
$ mkdir gitDemo
$ cd gitDemo/

Now that we have the folder we can make this into a Git repository. Any directory at any level can be made into a Git
repository (or repo for short). If at any point you are unaware of the status of the repo:

$ git status

Running the

status

command inside the

gitDemo/

dir should produce the following:

fatal: Not a git repository (or any of the parent directories): .git

This simply means that this dir is not a properly set up Git repo. To make this a repo, simply run the following command:

$ git init

Running this command you should see something similar to the following:

Initialized empty Git repository in /Users/<username>/Desktop/gitDemo/.git/

Running the

status

command here will now produce a completely different response:

# On branch master
#
# Initial commit
#
nothing to commit (create/copy files and use "git add" to track)

That is a little bit of a mouthful, but nothing to be concerned with. Simply, what this means is, you are on the
branch and there is nothing yet to commit.

master

The files
Now that we have a dir that is a properly formatted Git repo, let's create a few new files for a demo web app that we can
being to add to version control.
First, let's add the

index.html

file:

$ touch index.html

Great. But we need some more stuff for our demo web app. We need our primary CSS and Js files too. To do this we
need to create the proper dir for each:

$ mkdir stylesheets
$ mkdir javascripts

Using the

cd

command, we need to change directory into each dir and add a

app.js

and a

app.css

file:

$ cd stylesheets && touch app.css && cd ../


$ cd javascripts && touch app.js && cd ../

What's up with those commands? It's simple really, in Terminal you can string together commands using the

&&

operator.

If you were to take this apart:


1. Change directory into
2. Create the new

stylesheets/

app.css

file

3. Change directory back up a level to where we started from


Pretty cool, huh?
Now that we have that set up, run the

status

command again and you should see the following:

# On branch master
#
# Initial commit
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# index.html
# javascripts/
# stylesheets/
nothing added to commit but untracked files present (use "git add" to track)

GREAT! Ok, we are still on the

master

Take note that we have files in the

branch, nothing is ready to commit, but we have new

javascripts/

and

stylesheets/

Untracked files

in the output.

dirs but they are not listed out? This is because when you

add a new dir and create new files, unless you tell Git not to, it will simply gobble up all the files in the dir, so there is no
need to list them individually.
Interesting experiment: Let's create a new dir and NOT put anything inside it and then run the

status

command again:

$ mkdir foo
$ git status

What's this? The new dir

foo

is not in the output? That's weird? No, it's not really. Git tracks files, not directories. So, if you

create a directory and don't put in any files, even if they are empty files, Git will pass on the empty dir all together.

Ok, let's remove that

$ rm -rf foo

foo

dir:

The content
At this phase we have the dir, it's a repo and it has folders and files. Great. Now to add some content. Open each file and
add the following:
HTML

<h1>Hello World</h1>

CSS

h1 {
font-size: 8em;
}

JavaScript

var today = new Date()

Looking good. Having done this, let's look at our

status

again. Anything different? Nope. While Git does track every

single turned bit in these documents, it does not display this in the
It's time to get things tracking, so let's use the

add

status

output.

function and then append a statement to this

commit

$ git add .

Whoa, wait right there. Check the

#
#
#
#
#
#
#
#
#
#
#

status

now, you should see something like the following:

On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: index.html
new file: javascripts/js.css
new file: stylesheets/app.css

What does

unstage

when we ran the

mean? And if everything is working correctly the new files should be

git add .

yellow . What this

command, this updated the status of all the files in that directory to be

staged

means is that

. This means that

the edits you have added are not yet committed to the repo.
If you were to try and push code or switch branches at this time, these changes would not do what you expect them to do.
In order to commit these changes to the branch, we need to run the
There are a few parts of this we should talk about. There is the
is the

-m

commit

commit

command.

command that initiates the function and then there

flag that we need to pass in so that you can add a commit message. This is important as you need these

messages added to the log, or the individual commit messages are meaningless. We add the message at the end of the
command sequence using quotes

""

$ git commit -m "update to function ..."

After running this command sequence, you should see the following in your Terminal:

# On branch master
nothing to commit, working directory clean

Great! All the code has been updated and committed to the

master

branch!

A word on commit messages


When writing a commit message, you want to phrase things in the present tense. Meaning, the code you committed will
not do things in the future and they did not do things in the past. Examples of poor messages are:

$ git commit -m "added the new background image to the header"

or

$ git commit -m "will add new background image to the header"

or

$ git commit -m "SLAAAAAAAAAAAAAAYER!"

In both these cases, these messages when read by the other developers in the log send a confusing message. And the
last one ... well o_O
Think of things in this way, "This commit will ..."

$ git commit -m "add new background image to the header"

Make sense?

Commit history
Ok now that we have committed some code, let's see the history of these events.

$ git log

You will see:


the commit hash ID
author of the commit
the date of the commit
the commit message

commit 15d42d592fff5abe101e5985be0830d1bd03a1f1
Author: Dale Sande <dale.sande@gmail.com>
Date: Tue Jun 10 18:25:26 2014 -0700
update to function ...

Cool, now we know things about the code being committed to the repo. Want to know more?

$ git blame index.html

Yes,

blame

is the function and

index.html

is what we want to know about. This is a great way to see who did what on a

given file when things go a little left.

^15d42d5 (Dale Sande 2014-06-10 18:25:26 -0700 1) <h1>Hello World</h1>

You can see the initial part of the hash, the time stamp and the code that was modified. NICE!

Playing with branches


So far through this demo we have been working on a single

master

branch. The

master

branch is simply a convention

for stating what is the core branch of code that everyone should be committing to. Some don't use
conventions like

master

and use

and things along those lines.

stable

So, how do we work with branches? Pretty simple really. The first thing we want to know what branches we have and
what branch we are on:

$ git branch

You should see:

* master

And the word

master

should be highlighted. Great. This means that we have only one branch and that this is the one you

are on. Let's make a new branch:

$ git checkout -b new-branch

What did we do here?


a new branch, then

git checkout

new-branch

is the command to checkout a branch, passing in the

-b

flag is to create and checkout

is the name we passed in here. You can name a branch anything, but I suggest keeping

is short so that it is easy to type as you will be switching branches a lot.


Ok, now let's see those branches again:

master
* new-branch

Cool. We have two branches and since we used the


new branch right away.

-b

flag when we created the branch, this also switched us to that

The delta
These next steps will demonstrate how we can make some edits in this branch, commit them and then switch to see the
delta between the branches.
First, let's update some code in the

index.html

file. Open this up in your favorite text editor and add the following code:

<p>goodnight moon</p>

Cool. Now open the

app.css

file and add the following:

p{
font-size: 2em;
}

Cool, now we have a diff between what was originally in this branch and what we edited. Here there are a couple of
things we can do, bit for now let's get out status:

$ git status

And we should see:

# On branch new-branch
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: index.html
# modified: stylesheets/app.css
#
no changes added to commit (use "git add" and/or "git commit -a")

Ok, the next thing we want to do is commit these changes to THIS branch and switch back to the

master

branch:

$ git add .
$ git commit -m "update content and styles"

Doing a

git status

switch back to the

here we will see that there is nothing to commit and the working directory is clean. Cool! Now, let's
master

branch:

$ git checkout master

Great. Now open the

index.html

and

app.css

files. The code we just added and committed are GONE!!!! Ok, don't panic,

it's not really gone, it's just on the other branch. Let's switch back to make sure.

$ git checkout new-branch

Blamo! All the code should be back. But, we need that code in

master

The merge
Getting your branched code merged into
know that we have a version of code on
code that we want merged into

master
master

is not a magical process, it's pretty simple really. What do we know? We


and that we have more code in a brach called

master. To get started, let's checkout

new-branch

. It is the new

master`:

$ git checkout master

Ok, now we want to merge, but let's do a little check first. We can do a quick check to see what branches are merged and
not merged into

master

$ git branch --merged

At this point, we should only see the

master

branched listed. So then run this:

$ git branch --no-merged

This should return the

new-branch

in the list. Ok, let's merge these two. BUT, we need pass in the

--no-ff

flag. What does

this do?
The

--no-ff

flag prevents

git merge

from executing a "fast-forward" if it detects that your current HEAD is an ancestor of the

commit you're trying to merge. A fast-forward is when, instead of constructing a merge commit, git just moves your branch
pointer to point at the incoming commit.
Occasionally you want to prevent this behavior from happening, typically because you want to maintain a specific branch
topology (e.g. you're merging in a topic branch and you want to ensure it looks that way when reading history). In order to
do that, you can pass the

--no-ff

flag and

git merge

will always construct a merge instead of fast-forwarding.

$ git merge new-branch --no-ff

Ok, but this may have just got weird and will freak you out.

You may be seeing something like this:

Merge branch 'new-branch'


# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
".git/MERGE_MSG" 7L, 253C

Nothing to worry about there. Remember what I said about the

--no-ff

flag? It's a merge. But when we made this merge,

we didn't add a commit message. This is Git telling us that we didn't leave a commit message and it's asking us for one
now.
If we don't leave a message, then Git will insert one for us. So, do the following:

:wq

Weird, but trust me. Now you should see the following:

Merge made by the 'recursive' strategy.


index.html
| 2 ++
stylesheets/app.css | 4 ++++
2 files changed, 6 insertions(+)

Here is your confirmation that

index.html

and

app.css

added some code to the

master

Just to confirm that everything is all merged up, run this:

$ git branch --merged

You should see both our branches in this list.

The before and after


That's it, we are all merged up. Here is what our repo looked like before we merged

branch.

And here it is after

You may notice the out branch that we created when we created our feature branch and then it links back into the
branch. This is EXACTLY what we are looking for. This is the merge technique we want when using the

--no-ff

master

flag. If we

had not used that technique, we would see a single branch of code and that can be misleading when we are reviewing
our project history.

Delete the branch


Now that all our codes are merged, there is no need to keep the

new-branch

branch around. So, let's kill it with fire!

$ git branch -D new-branch

BOOM! There is no coming back from that easily. But why would we want to keep that around? Once we merge the code,
the history and code are all now part of

master

and that's where we want it. Keeping that old branch around us useless to

us. When we want to add new code, we will create a new feature branch.

Github
Without a doubt, Git and Github are like peanut butter and jelly. Once you have registered yourself as a user on Github,
the next thing you need to do is establish a connection to Github using SSH.

Generating SSH Keys


source
We strongly recommend using an SSH connection when interacting with GitHub. SSH keys are a way to identify trusted
computers, without involving passwords. The steps below will walk you through generating an SSH key and then adding
the public key to your GitHub account.

Step 1: Check for SSH keys


First, we need to check for existing SSH keys on your computer. Open up the command line and type:

$ cd ~/.ssh
$ ls -al

Lists the files in your .ssh directory


Check the directory listing to see if you have files named either

id_rsa.pub

or

id_dsa.pub

. If you don't have either of those

files go to step 2. Otherwise, you can skip to step 3.

Step 2: Generate a new SSH key


To generate a new SSH key, copy and paste the text below, making sure to substitute in your email. The default settings
are preferred, so when you're asked to "enter a file in which to save the key,"" just press enter to continue.

$ ssh-keygen -t rsa -C "your_email@example.com"


# Creates a new ssh key using the provided email
Generating public/private rsa key pair.
Enter file in which to save the key (/your_home_path/.ssh/id_rsa):

Next, you'll be asked to enter a passphrase.

Enter passphrase (empty for no passphrase): [Type a passphrase]


Enter same passphrase again: [Type passphrase again]

Then add your new key to the ssh-agent:

$ ssh-add ~/.ssh/id_rsa

Step 3: Add your SSH key to GitHub


Open the

~/.ssh/id_rsa.pub

file with a text editor. This is your SSH key. Select all and copy to your clipboard.

Or you can also run this from the command line:

pbcopy < ~/.ssh/id_rsa.pub

Now that you have the key copied, it's time to add it into GitHub:
1. In the user bar in the top-right corner of any page, click Account Settings
2. Click SSH Keys in the left sidebar.
3. Click Add SSH key.
4. In the Title field, add a descriptive label for the new key. For example, if you're using a personal Mac, you might call
this key "Personal MacBook Air".
5. Paste your key into the "Key" field.
6. Click Add key.
7. Confirm the action by entering your GitHub password.

Step 4: Test everything out


To make sure everything is working, you'll now try SSHing to GitHub. When you do this, you will be asked to authenticate
this action using your password, which for the passphrase you created earlier.
Open up the command line and type:

$ ssh -T git@github.com
# Attempts to ssh to github

You may see this warning:

The authenticity of host 'github.com (207.97.227.239)' can't be established.


RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)?

Don't worry! This is supposed to happen. Verify that the fingerprint in your terminal matches the one we've provided up
above, and then type "yes."

Hi username! You've successfully authenticated, but GitHub does not


# provide shell access.

If that username is yours, you've successfully set up your SSH key! Don't worry about the "shell access" thing, you don't
want that anyway.
If you receive a message about "access denied," you can read these instructions for diagnosing the issue.
If you're switching from HTTPS to SSH, you'll now need to update your remote repository URLs. For more information, see
Changing a remote's URL.

Fork a repo on Github


Forking a repo is amazingly simple. What forking is, Github is essentially making a copy of the repo for your use, but it's
not a total carbon copy as one might imagine.
What is happening is that Github is taking a snapshot in time and then storing that version snapshot in your account.
From here out, Git and Github will remember the edits you make. This is called the delta.
To fork a project you simply need to click the fork button on the repo page.

The Pull Request


Once you have forked and then cloned a project, this is now your version of the project. If you do not have permissions to
edit the actual project, this is how you would contribute.
Once you have made edits and committed them to

master

, you can then create a pull request that the owner of the

project will be notified of. If the updates are accepted, the admin will merge your pull request and the codes will be
added to the project.

Clone a repo
$ git clone <repo url>

That's really it. There isn't much more to this. By running the command above, this will tell Git to go to the URL and pull all
the codes to your local computer.
To get the URL, go to the Github repo and in the lower right hand side there will be an aside with the title of HTTPS clone
URL
Simply copy the URL from the text field, an example from the Node project is:

https://github.com/joyent/node.git

And there you have it, you own all the codes!

Setting up GIT Bash autocompletion


Run the following command:

curl https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash > ~/.git-completion.bash

This should create the

.git-completion.bash

Then add the following to your

~/.bashrc

file in your home dir.

or

~/.bash_profile

after

PATH

# Set the base PS1


export PS1="\t: \W$ "
# Source the git bash completion file
if [ -f ~/.git-completion.bash ]; then
source ~/.git-completion.bash
GIT_PS1_SHOWDIRTYSTATE=true
GIT_PS1_SHOWSTASHSTATE=true
GIT_PS1_SHOWUPSTREAM="auto"
PS1='\t:\[\033[32m\]$(__git_ps1 " (%s)")\[\033[00m\] \W$ '
fi
export PS1

-bash: __git_ps1: command not found


If you are seeing this error, this is because the

__git_ps1

function from the completion functionality was moved into a new

file ( git-prompt.sh ).
You can either source the

git-prompt.sh

that comes with your installed version of Git, if it has it, or you can download and

install a new one.

curl -o ~/.git-prompt.sh https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh

... and add the following line to your

~/.bash_profile

source ~/.git-prompt.sh

Git status
This will display the branch name next to the folder name in the bash prompt.
Symbols after the branch name indicate additional information about the repo state:
*

: The branch has modifications

: There are stashed changes

: The branch is equal with the remote branch

<

: The branch is behind the remote branch (can be fast-forwarded)

>

: The branch is ahead of the remote branch (remote branch can be fast-forwarded)

<>

: The branch and remote branch have diverged (will need merge)

You might also like