Вы находитесь на странице: 1из 42

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
Terminal cheatsheet (short list in presentation, full doc in Gitbook)
What is branching? (visual in presentation)
Git basics - a general workflow (book and presentation)
Git demo (visual desktop demo)
Clone a repo (how to)
Fork a repo, edit and submit a pull request (how to)
Workshop outline:
Getting Git Installed
Create a Github account and generate your SSH key
Exercise Git workflow (deck visual)
Clone a repo
Fork a repo, edit and submit a pull request
Setting up GIT Bash autocompletion (mention in deck, full doc in Gitbook)
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 i key, go to this line and add <file path>/**/*. Then hit the esc key to save. To get out, type
:wq, 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.
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 $ cd. What this does is Change Directory. Get it? With $ cd the next symbol we will learn is the ~.
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 -p flag with an ls
command will instruct Bash to list out directories with a trailing /. 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 $ cd further into folders within your desktop.
Done? Did you $ cd 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][touch], 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 rm UNIX command which is short for
remove. Using this command, we can remove that .config 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 mkdir, which is short for make directory. For example, on your Desktop, let's
create a new folder called foo.
$ 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 rm command to delete a file? We
need to use this, but you can't simply delete a directory, we need to add on the -r 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 -f flag is just common place and as a short hand, you can combine the -r -f flag to simply be -rf and
do something like the following:
$ 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 tab key. Terminal will attempt
autocomplete the name. If it can't hit the tab 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 mv command, let's create a new file on the 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
Great! Now let's say that we wanted to move that new app.css file to a new location. Remember, the mv command we used for
renaming is really to move things. Since we are still in that stylesheets directory, let's move the app.css file up to the root of the
public 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
Moving directories is much like moving files. For example, let's say that we created a directory at the root of project called
javascripts. Oops! We need that directory to be within public. To do this, use the mv command and pass in the source and then the
destination, as this example illustrates:
$ mv javascripts/ public/
Renaming directories goes the same way. Say that we had a directory on the Desktop called foo and we need to rename it to be bar 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 directory_1 and then directory_2. Inside directory_1 is a file called one.html. Inside
directory_2 is a file called two.html. In this first example, I will simply refer to the source and target:
$ cp -r directory_1 directory_2
By doing this, the command will copy directory_1 and it's contents into directory_2.
But by adding the whack / 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 i 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 .bash_profile to take effect, you have to
do what is called 'sourcing'. To do this, run:
$ 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 master as well checkout out that new branch at the same time. Doing a git branch 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 checkout -b transaction-fail-message master
In that example, say I was in branch github-oauth and I needed to create a new branch and then checkout the new branch? By adding
master 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 git status at this point, I will see my updates presented
differently, typically under the heading of Changes to be committed:. 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 git branch you will see a list of my local branches. By
doing a git branch -r 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 header.haml, then you will only see header.haml 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 master, I can now merge
these branches. I also need to make sure that my local master 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 master branch and there is
nothing yet to commit.
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 stylesheets/
2. Create the new 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 branch, nothing is ready to commit, but we have new Untracked files in the output. Take
note that we have files in the javascripts/ and stylesheets/ 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 foo dir:
$ rm -rf foo
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 status output.
It's time to get things tracking, so let's use the add 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 mean? And if everything is working correctly the new files should be yellow. What this means is that when we
ran the git add . command, this updated the status of all the files in that directory to be staged. 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 commit command.
There are a few parts of this we should talk about. There is the commit command that initiates the function and then there is the -m 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 master and use conventions like stable and things
along those lines.
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? git checkout is the command to checkout a branch, passing in the -b flag is to create and checkout a new
branch, then new-branch 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 -b flag when we created the branch, this also switched us to that new branch right
away.
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 here we will see that there is nothing to commit and the working directory is clean. Cool! Now, let's switch back to
the 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 master is not a magical process, it's pretty simple really. What do we know? We know that we
have a version of code on master and that we have more code in a brach called new-branch. It is the new code that we want merged
into master. To get started, let's checkoutmaster`:
$ 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 branch.
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
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 master branch. This is
EXACTLY what we are looking for. This is the merge technique we want when using the --no-ff 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 file in your home dir.
Then add the following to your ~/.bashrc 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)
Table of Contents
Introduction 2
Life in the Terminal 2
Why Terminal? 2
Meet Bash 2
File Nav 3
Cheat sheet 13
Open in VI 13
In summary 13
Git workflow 13
Branching 16
Branch Management 16
Push 19
Pull request 19
Merge the code 21
Shortcuts using aliases 22
Summary 22
The Git Demo 22
The folders 22
The files 22
The content 25
Branching 25
The delta 25
The merge 29
Kill w/fire 33
Github 34
Fork a repo 38
Clone a repo 38
Auto complete 38

Вам также может понравиться