# Git ## Practical Tips ### http://anders.janmyr.com ### @andersjanmyr ### anders.janmyr@jayway.com
![introduction](introduction.jpg)
## CV (Legacy) ![cv-legacy](cv.png)
## CV (Modern) ![cv-modern](github-tapirs.png)
## http://commitlogsfromlastnight.com/ ![bad-commit](commit-logs-from-last-night.png)
## A Good Commit Message ![good-commit](awesome-commit-message.png)
# Basics ![basics](basics.jpg)
# Nothing Committed is *Ever* Lost
## Git Storage ![git-storage](git-storage.png)
## DAG ### Directed Acyclic Graph ![git-dag](git-dag.jpg)
# Branch ## Pointer to a commit ### A sticky label
# Git Has Three Parts * HEAD * Index * Working Dir
## `git add` ### Adds files to the index
## `git commit` ### Adds index files to the HEAD
## `git reset` ###Moves the head pointer #### And optionally moves changes to and from the index
# Nothing Committed is *Ever* Lost
# History ![history](history.jpg)
## `git rebase` ![git-rebase.png](git-rebase.png) `git rebase` does NOT change the committed files
## `git commit --amend` # Amend latest commit with files in index $ git commit --amend # And resuse the commit message $ git commit --amend -C HEAD `git commit --amend` does NOT change the committed files
## `git rebase -i` ![git-rebase-interactive.png](git-rebase-interactive.png) `git rebase` does NOT change the committed files
## `git cherry-pick` ![cherrypick.png](cherrypick.png) `git cherry-pick` does NOT change the committed files
## `git add -p` ![git-add-p](git-add-p.png)
## `git add -p` ? y - stage this hunk n - do not stage this hunk q - quit; do not stage this hunk nor any other a - stage this hunk and all later in the file d - do not stage this hunk nor any later in the file g - select a hunk to go to / - search for a hunk matching the given regex j - leave this hunk undecided, see next undecided hunk J - leave this hunk undecided, see next hunk k - leave this hunk undecided, see previous undecided s - split the current hunk into smaller hunks e - manually edit the current hunk
## `git reset` # Moves the HEAD pointer back 4 steps $ git reset HEAD~4 # Does NOT change working dir $ git reset --mixed HEAD~4 # And adds the files to the index # Does NOT change working dir $ git reset --soft HEAD~4 # Also CHANGES working dir $ git reset --hard HEAD~4 `git reset` does NOT change the committed files
## `git reflog` Contains references to where `HEAD` has been. $ git reflog eb06230 HEAD@{0}: reset: moving to master ccfaa12 HEAD@{1}: reset: moving to head~ 7b8a6cb HEAD@{2}: reset: moving to head~ eb06230 HEAD@{3}: checkout: moving from master to an eb06230 HEAD@{4}: commit: Added reset 7b8a6cb HEAD@{5}: reset: moving to 7b8a6cb59edbfde 69c6f00 HEAD@{6}: reset: moving to 69c6f00 1910819 HEAD@{7}: reset: moving to 1910819
## `git log -g` Contains the log for the reflog $ git log -g --oneline 23d1698 HEAD@{0}: commit: Updated abstract ee3677f HEAD@{1}: cherry-pick: Fixed the find file by partial name cf56e88 HEAD@{2}: cherry-pick: Removed summary 2289c5f HEAD@{3}: checkout: moving from finding to master 26bb1a6 HEAD@{4}: commit: Fixed the find file by partial name b97bf68 HEAD@{5}: commit: Removed summary bd6af0a HEAD@{6}: commit (amend): Removed everything but finding 2289c5f HEAD@{7}: merge finding: Fast-forward ### `--oneline` - Oneline output format
# Finding in Git ![finding](finding.jpg)
## Where is my file? $ git ls-files | grep security lib/dynamo-db/security.js test/security-test.js
## What message contains "fix"? $ git log --oneline --grep=fix -i 7eeb362 Whitespace fix e72c673 Load images as background images to fix scaling ec5576b Fixed broken integration test, added test case 22e0a54 Fixed broken unit tests 99b18c3 Clean up qr resource leak, and fixed unit test f8d9537 Pass qrstore in socket.io init, fixed broken test ### `-i` - Ignore case ### `--oneline` - Oneline output format
## In what file can I find "crypt"? $ git grep -n crypt lib/dynamo-db/security.js:2:var crypto = require('crypto'); lib/dynamo-db/security.js:15: var hmac = crypto.createHmac('sha256', key); ### `-n` - show line numbers
## Find "type" in revisions `master` and `8f0fb7f`. git grep -l type master 8f0fb7f master:lib/dynamo-db/index.js master:lib/dynamo-db/security.js master:package.json 8f0fb7f:lib/dynamo-db/index.js 8f0fb7f:package.json ### `-l` - show filenames only
## What commits touched file 'Makefile'? $ git log --oneline Makefile c176cc0 Added deploy silent option to avoid tagging 86edd1e Removed client side scripts from coverage run 3a199d2 adds cdn caching for images, if env variable cdn 20a66ed pass file arguments to mocha to run single test b46c077 Notify with growl when application is deployed 044a954 Removed copying of old file 7111636 Almost working with minification ### `--oneline` - Oneline output format
## Who deleted my file? $ git log --diff-filter=D -- test/create-table-test.js commit ba6c4d8bc165b8fb8208979c3e5513bd53477d51 Author: Anders Janmyr anders@janmyr.com Date: Wed Jan 25 09:46:52 2012 +0100 Removed the stupid failing test. ### --diff-filter - supports D, A, C, M, R
## When was the file added? ### Part of name $ git log --diff-filter=A --name-status |grep -C 6 int commit 09420cfea8c7b569cd47f690104750fec358a10a Author: Anders Janmyr anders@janmyr.com Date: Tue Jan 24 16:23:52 2012 +0100 Extracted integration test A integration-test/sts-test.js commit 205db3965dec6c2c4c7b2bb75387a591d49e1951 Author: Anders Janmyr anders@janmyr.com ### --name-status - Short info about affected files
## Who changed that line? $ git blame test/security-test.js 205db396 (Anders Janmyr 2012-01-21 10:03:59 +0100 1) var vows = require('vows'), 205db396 (Anders Janmyr 2012-01-21 10:03:59 +0100 2) assert = require('assert'); 09420cfe (Anders Janmyr 2012-01-24 16:23:52 +0100 3) var access = 'access'; 09420cfe (Anders Janmyr 2012-01-24 16:23:52 +0100 4) var secret = 'secret';
## Who deleted that line? $ git blame --reverse head~6..head security-test.js 558b8e7f (Anders Janmyr 2012-01-25 16:53:52 +0100 1) var vows = require('vows'), 558b8e7f (Anders Janmyr 2012-01-25 16:53:52 +0100 2) assert = require('assert'); 093c13e9 (Anders Janmyr 2012-01-24 17:26:09 +0100 3) var Security = require('dynamo-db').Security; ba6c4d8b (Anders Janmyr 2012-01-25 09:46:52 +0100 4) ^b96c68b (Anders Janmyr 2012-01-21 12:33:50 +0100 5) var access = process.env['S3_KEY']; ### --reverse - The last revision in which a line has existed ### `git log 093c13e9..`
## What commits contain the string 'aws'? $ git log -Saws --diff-filter=M --patch commit b96c68b839f204b310b79570bc3d27dc93cff588 diff --git a/lib/dynamo-db/security.js b/lib/dyna... @@ -57,9 +58,10 @@ mod.url = function(host, path,... var extParams = _.extend({}, this... - var signedParams = 'iam.amazonaws.com' - return request({ method: method,... + var signedParams = this.signedPa... ... ### -S string - Search for string ### -G regex - Search for regex
# Config ![config](config.png)
## Global `~/.gitconfig` $ git config --global -l user.name=Anders Janmyr user.email=anders@janmyr.com ... ## Local `./.git/config` $ git config --local -l remote.origin.url=git@github.com:andersjanmyr/git-... remote.origin.fetch=+refs/heads/*:refs/remotes/origin/* ...
## Automatic rebase pull $ git config branch.master.rebase true Sets [branch "master"] rebase = true
## git-bash-completion if [ -f /usr/local/etc/bash_completion.d/git-completion.bash ] then . /usr/local/etc/bash_completion.d/git-completion.bash elif [ -f /etc/bash_completion.d/git ]; then . /etc/bash_completion.d/git fi
## Git Prompt function prompt { local sha=$(git rev-parse --short HEAD) export PS1="[\!:\W ${sha}$(__git_ps1 '@%s')]\n$ " } PROMPT_COMMAND='prompt' export GIT_PS1_SHOWDIRTYSTATE=true export GIT_PS1_SHOWUNTRACKEDFILES=true export GIT_PS1_SHOWUPSTREAM="auto" [511:git-tips-and-tricks 50e6e0c@master *%=] $
## Git Prompt ## `__git_ps1` if [ -f /usr/local/etc/bash_completion.d/git-prompt.sh ] then . /usr/local/etc/bash_completion.d/git-prompt.sh fi
## git config alias $ git --global config alias.au 'add -u' Sets [alias] au = add -u ## Explanation # Adds all tracked files to the index $ git add -u *Use `--global` for aliases*
## alias git amend $ git config alias.amend 'commit --amend -C HEAD' ## Explanation commit = The commit command --amend = Amend the changes to previous commit -C HEAD = Reuse the commit message # Alternative -c HEAD = Lets you edit the message
## alias git ld git config alias.ld 'log --diff-filter=D --summary' Show commits with deleted files ## Explanation log = The log command --diff-filter=D = Only commits with deleted files --summary = Show changes such as deletion, etc
## alias git hist ![git-hist](git-hist.png)
## git hist explained $ git config alias.hist 'log --pretty='format:%C(blue)%h%C(red)%d%C(yellow) %s %C(green)%an%Creset, %ar' --graph' ## Explanation
log               = The log command
                --graph           = Text-based graphical representation
                --pretty='format' = Format according to spec
                %C(color)         = Change color
                %h                = Abbreviated commit hash (6b266c2)
                %d                = Ref names (HEAD, origin/master)
                %s                = Subject (first line of comment)
                %an               = Author name
                %ar               = Author date, relative
# Shell functions ## When an alias can't cut it
## `g()` # `g` is short for git, defaults to `git s` (status) function g() { local cmd=${1-s} shift git $cmd $@ } ### Usage $ g ## master M README.md $ g log
## `gg()` # Commit changes and quote all args as message function gg() { git commit -m "$*" } Usage (no quotes :) $ gg Commit this awesome shell function
# Other Useful Stuff ![other-stuff](other-useful-stuff.jpg)
## List Tagged Commits ### `git log --simplify-by-decoration` ![simplify-log](simplify-by-decoration.png)
## `stash save [-u] [message]` $ git stash save -u Extracted new methods Saved working directory and index state On master: Extracted new methods HEAD is now at a9aeba6 Merge branch 'deprecate-calculations-with-block' -u - Also stash untracked
## `git stash pop` or `apply` $ stash pop # On branch master # Your branch is behind 'origin/master' by 6 commits... # # Changes not staged for commit: # # modified: activerecord/test/cases/has... # Dropped refs/stash@{0} (c6b45e5b6fa0de7c...)
## --assume-unchanged $ git status --short M config/database.yml $ git update-index --assume-unchanged config/database.yml $ git status --short $
## --no-assume-unchanged $ git update-index --no-assume-unchanged config/database.yml $ git status --short M config/database.yml
## `git stripspace` ![stripspace](git-stripspace.png) * Strips trailing whitespace * Add a newline at file end * Merges multiple newlines into one
# Submodules ### Resusing common code
## `git submodule add` $ git submodule add git://github.com/hakimel/reveal.js.git # Clones and adds a repo as a submodule. Added in .git/config [submodule "reveal.js"] url = git://github.com/hakimel/reveal.js.git Added in .gitmodules [submodule "reveal.js"] path = reveal.js url = git://github.com/hakimel/reveal.js.git
## `git submodule add -b master` $ git submodule add -b master git://github.com/hakimel/reveal.js.git # Clones and adds a repo as a tracked submodule Added in .gitmodules [submodule "reveal.js"] path = reveal.js url = git://github.com/hakimel/reveal.js.git branch = master
## `git submodule update` $ git submodule update # Updates the submodules ## `git submodule update --remote` $ git submodule update --remote # Updates the submodules if the branch has changed # Updates the files, but the change has to be committed
## `git submodule sync` $ git submodule sync # Updates `.git/config` from `.gitmodules` # useful when submodule URLs change upstream
## `git submodule foreach` $ git submodule foreach git pull origin master # Updates each version to the latest version
## `git submodule rm` *DOES NOT EXIST* * Delete the relevant section from the .gitmodules file. * Delete the relevant section from .git/config. * Run git rm --cached path-to-submodule (no trailing slash). * Commit and delete the now untracked submodule files. ### Or use script `git-submodule-rm` -- Google it!
## `git bisect` $ git bisect start failing-test first-good-test Bisecting: 5 revisions left to test (roughly 3 steps) [e29cc1e] Added two element sort function $ git bisect run npm test ✔ 3 tests complete [850743] Set the version to 1.0.0 before publishing ✔ 3 tests complete [042d9f] Added failing test to demonstrate bisect ✖ 1 of 4 tests failed [31b36f] Added travis status to readme. 042d9ff is the first bad commit
# Github ![github](github.png)
## The `hub` command $ brew install hub $ alias git=hub
## `hub create` $ git create [ repo created on GitHub ] git remote add origin git@github.com:YOUR_USER/CURRENT_REPO.git
## `hub clone` $ git clone schacon/ticgit git clone git://github.com/schacon/ticgit.git
## `hub remote add` $ git remote add origin anders git remote add origin git://github.com/anders/CURRENT_REPO.git
## `hub push` $ git push origin,staging,qa master git push origin master git push staging master git push qa master
## `hub pull-request` # while on a topic branch called "feature": $ git pull-request [ opens text editor to edit title and body for the request ] [ opened pull request on GitHub for "YOUR_USER:feature" ]
## `hub browse` $ git browse -- issues open https://github.com/YOUR_USER/CURRENT_REPO/issues
# Questions ### http://anders.janmyr.com ### @andersjanmyr ### anders.janmyr@jayway.com
## Resources ### Config http://goo.gl/sS8Kv ### Finding http://goo.gl/qQVsP ### Presentation http://andersjanmyr.github.io/git-tips-and-tricks ### `gitconfig` http://goo.gl/dl3xiG