Created by Sean Patrick Hagen / @seanhagen
At it's core, it's a distributed revision control system
As well as a source code management system
No central service to manage the revisions
Create branches to explore an idea, discard the branch if it doesn't work out
Or merge the branch in if it turns out to be an awesome idea
Easily create branches for features, bugs, tasks
Merge code quickly between those branches
Share the code really easily
While this isn't a feature of the tool, GitHub makes Git awesome.
At it's core, Git is a command line tool.
GUIs are okay, but the command line is where it's at.
git clone <repository url>
Now you've got a copy of the code!
/home/sean/some-project
ssh://[user@]host:path/to/repo.git
GitHub tends to use the following format:
git@github.com:<your github username>/<repo name>.git
This presentation can be cloned from:
git clone git@github.com:seanhagen/git-presentation.git
Here are a few flags that you might find useful when cloning:
--branch <name>
--depth <num>
Okay, so you've got a copy of the code.
git status
$ git status # On branch feature-optimize-library-search # Changes not staged for commit: # (use "git add..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: application/configs/application.ini # modified: application/index.php # modified: application/routers/example.php # no changes added to commit (use "git add" and/or "git commit -a")
This tells git about a new file, or lets it know about changes in a file.
git add path/to/file
Technically, you're actually "staging" content for your next commit
Here are a few useful flags for when you're adding files:
Tell git you don't want it to track it anymore,
and to remove it from the working tree
git rm path/to/file
Once you've changed a file, sometimes you want to know what's changed before you commit it
git diff path/to/file
$ git diff application/routers/example.php diff --git a/application/routers/example.php b/application/routers/example.php index 7897b19..a948510 100644 --- a/application/routers/example.php +++ b/application/routers/example.php @@ -181,7 +181,7 @@ $app->delete( '/api/video/:videoid', $getUserObject, $checkVideoOwner, - function( $videoid ) use ( $app, $env ) { + function( $videoid ) use ( $app, $env, $acl ) {
Now that you've added some files, time to store those changes!
git commit
Useful options:
When you use commit, git will bring up an editor
for you to use to write a commit message.
Be awesome to your coworkers, and write a good description.
It should follow the following format:
Maximum 80 character brief description In-depth list of modifications made to each file in the commit.
Actually, it's because the git log ( and GitHub ) use the first 80 characters as a sort of title
When you clone a repository, git will
setup a default remote for you: origin
You can push to it like so:
git push -u origin master
The -u tells git you'd like add it as a tracking reference
This means that later all you have to do is:
git push
It also helps when you want to pull stuff down later
It's pretty simple:
git pull <remote name> <branch>
If you used the -u flag when pushing,
you can just write git pull
Another way to accomplish a pull is to fetch and then merge:
git fetch origin
git merge origin/master
This just gives you more control over what's happening.
Because git is a distributed system, you can have multiple remotes that you can push to.
If you want to add one, use the following:
git remote add <name> <url>
Well, what if you want to keep your
repository up-to-date with upstream?
You could try to pull-request everything
into your own forked repo
Or you could just set up a remote to
track all the other repositories:
git remote add upstream git@github.com:broadbandtv/Porcupine.git
And then fetch and merge the right
branch whenever you need to:
git fetch upstream
git merge upstream/next
Add the missing files, and then run:
git commit --ammend
That's easy:
git reset HEAD path/to/file
Rollback time!
git checkout -- path/to/file
That's okay.
Use git's revert!
git revert HEAD
That will create a patch that reverts the last commit, and commits that patch as a new commit
You can also specify a specific commit to revert, instead of 'HEAD'
Branching let's you keep areas of work separate, so that you can be switch between things that you're doing
The easiest way to create a branch is the following:
git checkout -b <new branch name>
This is shorthand for:
git branch <new branch name>
git checkout <new branch name>
Want to see what branches you've got available?
git branch
example * master testing unicorns-and-rainbows
The '*' lets you know what branch you're on right now.
Add the -a flag to see ALL branches, even ones on remotes:
example * master testing unicorns-and-rainbows remotes/origin/master remotes/origin/testing remotes/origin/not-available-locally remotes/origin/totally-awesome
So you've got a branch on one of your remotes.
But you want to work on that branch locally.
First, make sure you're up-to-date with that remote:
git fetch <remote name>
Then, create a branch off of that:
git checkout -b totally-awesome origin/totally-awesome
When creating a tracking branch for something other than origin
it's a good idea to use a branch name that lets you know what
that branch tracks.
git fetch upstream
git branch --track upstream-next upstream/next
Merging lets you take one branch and combine it with another
Say you want to merge the code from the branch 'next' into the branch 'master'.
First make sure you're in the master branch.
git checkout master
Now, merge in the code from next.
git merge next
If you've forked your repository on GitHub, here's two ways to keep your version of the 'next' branch up-to-date.
git checkout upstream-next
git pull
git checkout next
git merge upstream-next
Alternatively:
git pull upstream next
Occasionally when merging branches, you'll see something like the following:
> git pull upstream next From git://git@github.com:broadbandtv/project-name * branch upstream -> FETCH_HEAD Auto-merging public/index.php CONFLICT (content): Merge conflict in public/index.php Automatic merge failed; fix conflicts and then commit the result.
This means that Git was unable to merge the differences between your local copy of a file and the copy stored in the remote.
The first step is to find out what the differences are.
You can open the files that have conflicts in them, and they will have inlined diffs that show you the differences between your local copy and the version you're trying to pull down.
They're not pretty.
<<<<<<< HEAD:mergetest This is my third line ======= This is a fourth line I am adding >>>>>>> 4e2b407f501b68f8588aa645acafffa0224b9b78:mergetest
This doesn't look too bad, but try to resolve a merge conflict that spans hundreds of lines.
git mergetool
will launch a tool that
will help resolve merge conflicts.
It can help you resolve crazy merge conflicts without going nuts.
However, if you just want to use your version or their version of the file, you can use one of the following commands:
$ git checkout --ours path/to/filename.ext # checks out OUR copy of the file $ git checkout --theirs path/to/filename.ext # checks out THEIR copy of the file $ git add path/to/filename.ext $ git commit
Be careful when deleting branches! There is no way to recover a deleted branch!
git branch -d <branch name>
If the branch you're trying to delete hasn't been merged yet, git will throw up an error.
If you want to delete it anyways, use -D instead of -d.
Cherry picking is taking a commit ( or a range of commits ) from another branch and bringing them over to your current one.
It's similar to merging, but with much more fine-grained control.
First, check out the branch you want to cherry pick from
git checkout feature-thing
Next,use git log
or GitHub to find the first 6 or 7 characters of the commit you want to cherry pick.
commit 851a061f353976dd37d11fe6e6d2ec21ccce8234 Author: Sean Patrick Hagen Date: Thu Feb 20 18:47:06 2014 -0800
Then switch to the branch you want to insert the commit into:
git checkout my-awesome-feature
Now cherry-pick
the commit you want:
git cherry-pick 851a061f
This can be useful when you want to grab a bugfix from another branch, or when you've got a few commits you want to merge but don't want to actually do a full merge.
git bisect
helps you find a bad commit between any two commits.
You can run git bisect
manually or automatically.
First, start up the bisecting process:
git bisect start
Then, tell give it a commit to start with.
And tell it if that commit is good or bad.
git bisect good a09c834f
Now give it the other end of the range you want to test out.
git bisect bad b38d80adf
Now git
will start a binary search for you!
git
will load up a revision and ask you if the commit is good or bad.
You answer with "git bisect good
" or "git bisect bad
".
To automate the process is pretty similar to how you do it manually.
Use git bisect start
to start,
Then git bisect good <commit hash>
and git bisect bad <commit hash>
to give it a range
git bisect run <command>
Now you're off to the races. git
will use that command on every commit, and will let you know when it finds the offending commit.
So if you've got a bunch of commits
and you know one of them is breaking a particular unit test
give git bisect run
the command to run the unit test
When you give git bisect run
a command to run, that command has to follow the Unix tradition for exit codes
It needs to return a 0 if it passes, and a non-zero exit code if it fails.
git blame
lets you see which commit belongs to the last time a line in a file was changed.
5099d201 Identity.php (Lewis Zimmerman 2014-02-05 14:25:40 -0800 20) public function setId($id) 5099d201 Identity.php (Lewis Zimmerman 2014-02-05 14:25:40 -0800 21) { 4ef3d169 Identity.php (Lewis Zimmerman 2014-02-05 15:10:44 -0800 22) $this->id = (int) $id; ee5d5900 IdentityModel.php (Anthony Chernikov 2014-02-11 14:48:51 -0800 23) 5099d201 Identity.php (Lewis Zimmerman 2014-02-05 14:25:40 -0800 24) return $this;
Check out git help blame
to see some of the options, and check out the Git Book for more.
Stashing is similar to a commit, but without the commitment.
It lets you store your work so you can switch branches, without having to make a commit
Say you've been working on some code:
$ git status # On branch master # Changes to be committed: # (use "git reset HEAD..." to unstage) # # modified: index.html
And you want to switch to another branch to quickly fix a bug.
Use git stash
:
$ git stash Saved working directory and index state \ "WIP on master: 049d078 added the index file" HEAD is now at 049d078 added the index file (To restore them type "git stash apply")
Now your working directory is clean!
$ git status # On branch master nothing to commit (working directory clean)
Now you've done everything you needed to, and you want to get back to where you were:
$ git stash apply # On branch master # Changes not staged for commit: # (use "git add..." to update what will be committed) # # modified: index.html
You can keep making changes and stashing them.
If you want to see what you've got stashed, use git stash list
:
$ git stash list stash@{0}: WIP on master: 049d078 added the index file stash@{1}: WIP on master: c264051... Revert "added file_size" stash@{2}: WIP on master: 21d80a5... added number to log
GitHub is where we keep all of our code. GitHub is just another remote that you can use, and we use it because it gives us some pretty awesome features.
Forking just creates a copy of a repository in your account.
This lets you work on code and get as messy as you like.
But keeps the main repo in a pretty usable state for QA
Most of the time
This image is probably already out of date.
GitHub is super agile, so their page tends to change without warning.
Just look for a button that says "Fork"
Pull Requests are how you get code from your forked repo back into the main repository.
Click one of the two green buttons to start the pull request.
Next you'll see a screen with this at the top:
And then a list of your commits.
If you click on edit, you'll see something like this:
This lets you change what branch you're PR-ing from, and to.