On Github seanhagen / git-presentation
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
This means that every person has a full copy of the repository
It can be pretty awesome, but it's also got some cons
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.
Now you've got a copy of the code!
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:
depth: useful when you want to get a shallow copy, like if you're deploying a demo to server
Okay, so you've got a copy of the code.
git status
Gives you a list of the following:
$ 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
the branch is the branch you want to pull from, you can specify multiple branches here. they will be merged into your current branch
Another way to accomplish a pull is to fetch and then merge:
git fetch origingit 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 upstreamgit merge upstream/next
Add the missing files, and then run:
git commit --ammend
If you don't add/remove files, this will let you edit the commit message
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.
<<<<<<<: Indicates the start of the lines that had a merge conflict. The first set of lines are the lines from the file that you were trying to merge the changes into.
=======: Indicates the break point used for comparison. Breaks up changes that user has committed (above) to changes coming from merge (below) to visually see the differences.
>>>>>>>: Indicates the end of the lines that had a merge conflict. Once you've chosen a block to keep, delete the other block ( including the conflict markers ), add the file. If you have any other files to resolve, fix them. Once you're done, commit the merge.
git mergetool will launch a tool that will help resolve merge conflicts.
It can help you resolve crazy merge conflicts without going nuts.
git help mergetool will bring up the manual page.
You can specify what tool you use to resolve the merge conflict, but that's out of the scope of this presentation.
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 commitIn this scenario, we checked out our copy of a file, then their copy and used that instead. This can be useful when two files are the same except your respective editors have changed the formatting of spaces/tabs/etc ( which shouldn't happen, but does ).
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.
Okay, now that you know how to track down what commit broke the build, what about when you already know what file is causing an issue, and you want to track down who's responsible?
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.
Those two pink arrows are pointing at the two repositories ( and branches ) that GitHub is comparing. The one on the left is where the PR is going, the one on the right is where it's coming from.
Always PR into the proper branch for your feature/task/bug!
If you click on edit, you'll see something like this:
This lets you change what branch you're PR-ing from, and to.