git-tips-and-tricks



git-tips-and-tricks

1 2


git-tips-and-tricks


On Github andersjanmyr / git-tips-and-tricks

Git

Practical Tips

http://anders.janmyr.com

@andersjanmyr

anders.janmyr@jayway.com

CV (Legacy)

CV (Modern)

http://commitlogsfromlastnight.com/

A Good Commit Message

Basics

Nothing Committed is Ever Lost

Git Storage

DAG

Directed Acyclic Graph

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

git rebase

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 does NOT change the committed files

git cherry-pick

git cherry-pick does NOT change the committed files

git add -p

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

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

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 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

List Tagged Commits

git log --simplify-by-decoration

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

  • 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

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