Version control with

Git

Topics

So many topics, so little time

  • Version Control 101
  • Installation & Setup
  • Basic Workflow
  • Viewing Changes
  • Branching
  • Distributed Workflow
  • Best Practices
  • GUI Goodness
  • Dealing with SVN
  • Github
  • Advanced Topics
  • Resources
  • Version Control 101

    Why use Version Control

    Using bad or no version control will only lead to pain & suffering

    Why use Git

    Don't just take my word for it: http://whygitisbetterthanx.com/

    History of Git

    It's come a long way since 2005

    Git Basic Workflow

    An essential - and rather unique - git concept

    Image from http://progit.org/book/

    Git Architecture

    Learn this model, it will serve you well

    Image from “Version Control with Git, 1st Edition”, page 35. O’Reilly.

    Version Control Concepts

    I'm sure I forgot some important items here

    Install & Setup

    Installation

    These should be mostly correct ...

    http://git-scm.com/download

    Linux

    Mac

    Windows

    Environment Setup

    Just a few things to get us started ...

    bash completion

    Man pages

    git help <command>
    

    less

    git config

    Author

    git config --global user.name "Matt Kopala"
    git config --global user.email "mkopala@gmail.com"
    

    Pager w/ color

    git config --global core.pager "less -R"
    

    ~/.gitconfig

    [user]
        name = Matt Kopala
        email = mkopala@gmail.com
    [color]
        diff = auto
        status = auto
        branch = auto
    [core]
        pager = less -R
    [alias]
        st = status
        gr = log --graph --oneline
        lt = ls-tree -r --name-only
        ru = remote update
    [diff]
        tool = kdiff3
    [branch]
        autosetuprebase = always
    

    Basic Workflow

    git status

    Learn to love it

    $ git status
    # On branch master
    # Changes to be committed:
    #   (use "git reset HEAD ..." to unstage)
    #
    #   new file:   b
    #
    # Changes not staged for commit:
    #   (use "git add ..." to update what will be committed)
    #   (use "git checkout -- ..." to discard changes in working directory)
    #
    #   modified:   a
    #
    # Untracked files:
    #   (use "git add ..." to include in what will be committed)
    #
    #   c
    

    git init

    It doesn't get much easier than this

    Creates the .git directory

    mkopala@platypus:~/src/test/a$ git init
    Initialized empty Git repository in /home/mkopala/src/test/a/.git/
    

    git diff

    Because you're not going to remember what you changed. Trust me.

    View differences betwwen working directory and HEAD

    git diff
    

    View difference between index and HEAD

    git diff --cached
    

    View differences for a single file git diff myfile

    Only show how many lines have changed for each file

    git diff --stat
    

    git add

    Sorry, git can't read your mind

    Add a couple of files

    git add onefile somefile
    

    Interactively select which files to add

    git add -i
    

    Add specific sections of files

    git add -p
    

    git commit

    Let's checkpoint this bad boy!

    Generate a new tree and commit object, save them, and move HEAD

    Use EDITOR to enter commit message, display diffs in editor

    git commit -v
    

    Commit from the command line w/ the specified message

    git commit -m "Some really spiffy changes"
    

    Don't generate a new commit - replace the previous one

    git commit --amend
    

    git log

    It's all about the history

    
    commit 9db0d3e46f651ca0a357432f124f960a140d25e3
    Author: Matt Kopala 
    Date:   Tue Jan 24 02:14:05 2012 -0700
    
        Added a bunch more content
    

    SHA1

    Author

    .gitignore

    Because not everything should be versioned

    What to ignore

    $ cat .gitignore 
    tmp/
    config.ini
    *.swp
    *~
    

    git reset

    Oops, I did it again! -Britney Spears

    Clean out the index

    git reset
    

    Remove a single file from the index

    git reset HEAD myfile
    

    Discard the last commit

    git reset --soft HEAD^
    

    Discard all changes in working directory since last commit

    git reset --hard
    

    git rm

    Get rid of files you don't want anymore

    Remove a file

    git rm
    

    Remove files that you deleted manually

    git add -u
    

    Remove added file from staging area

    git rm --cached myfile
    

    git stash

    Shelve unfinished changes for later

    Save changes for later

    git stash save "Description of changes"
    

    View list of stashed changes

    git stash list
    

    Remove last stashed change and apply it

    git stash pop
    

    Apply last stashed change, but leave on stack

    git stash apply
    

    Drop last stashed change

    git stash drop
    

    git checkout

    Jump to any version of your files/code

    Switch to mybranch

    git checkout mybranch
    

    Switch to remote branch origin/master (detached head)

    git checkout origin/master
    

    Switch to a specific commit

    git checkout c828fa9
    

    Switch to the grandparent of HEAD

    git checkout HEAD~2
    

    Branching

    git branch

    View your local branches

    git branch
    

    View all of your branches (including remotes)

    git branch -a
    

    Show the last commit message & id for each branch

    git branch -v
    

    Delete a branch

    git branch -d oldbranch
    

    Examples

    $ git branch
    * master
      test
      mystuff
      v1.0
      
    $ git branch -a
    * master
      test
      mystuff
      v1.0
      remotes/origin/master
      remotes/bob/master
      remotes/origin/stuff
    

    git checkout -b

    Blaze your own trail

    Create a branch off of your current HEAD

    git checkout -b mytest
    

    Create a branch of a specific commit, and switch to it

    git checkout -b other c38fa38
    

    git merge

    Switch to master branch and merge changes from mytest branch

    git checkout master
    git merge mytest
    

    Ensure a merge commit is create (Don't fast-forward)

    git merge --no-ff
    

    git cherry-pick

    Grab the changes for a single commit from another branch, and apply them

    git cherry-pick <commit_id>
    

    git tag

    Tag the current commit with v1.0 tag and label it

    git tag -a v1.0 -m "First release"
    

    git-flow

    Bringing some sanity to branch management

    Introduced in this post:

    Available on GitHub

    Branches:

    Resolving Merge Conflicts

    Can't we all just get along?

    You did this:

    git merge mybanch
    

    or this:

    git pull
    

    and got this:

    Auto-merged file.txt
    CONFLICT (content): Merge conflict in file.txt
    Automatic merge failed; fix conflicts and then commit the result.
    

    Look in the file for this:

    <<<<<<< HEAD:file.txt
    Hello world
    =======
    Goodbye
    >>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
    

    Clean it up, so you're left with what it should be:

    Hello world
    

    Viewing Changes

    View your history, your way

    The standard

    git log
    

    One line summaries

    git log --oneline
    git shortlog
    

    View files changed

    git log --stat
    

    One line summary with ASCII art for branches

    git log --graph --oneline
    

    The whole shebang, by commit

    git log -p
    

    git grep

    This is one hella useful tool

    Like grep -r *, but only for files committed to Git

    mkopala@platypus:~/src/slideshow$ git grep php
    samples/3rd/ruby19.textile:"Defect 17700":http://rubyforge.org/tracker/index.php?func=detail&aid=17700&group_id=426&atid=1698
    samples/facebook.textile:Source: "Facebook Statistics":http://www.facebook.com/press/info.php?statistics
    samples/facebook.textile:Source: "Facebook Networks":http://www.facebook.com/networks/networks.php (Apr/14/2008)
    samples/facebooker.textile:h3. "Facebook API Test Console":http://developer.facebook.com/tools.php?api - Demo
    samples/mobile.textile:* "iUI Intro":http://www.joehewitt.com/blog/introducing_iui.php  by Joe Hewitt  
    samples/syntax.textile:#!php
    samples/syntax.textile:<?php
    samples/tagging.textile:* "LibraryThing Book Tag Cloud":http://www.librarything.com/tagcloud.php
    templates/s6/jquery.js: // http://blindsignals.com/index.php/2009/07/jquery-delay/
    

    git blame

    WTF? Who wrote this crap?

    mkopala@platypus:~/src/phpsh$ git blame setup.py
    ^9317b56 (Daniel Corson  2008-10-13 13:02:15 -0700  1) #!/usr/bin/env python
    646d4d57 (Daniel Corson  2010-02-23 00:31:18 -0800  2) from distutils.core import setup
    0166922b (Mark Marchukov 2008-11-24 14:43:44 -0800  3) from subprocess import Popen
    49481802 (Mark Marchukov 2008-11-23 21:33:02 -0800  4) import sys
    49481802 (Mark Marchukov 2008-11-23 21:33:02 -0800  5) import os
    49481802 (Mark Marchukov 2008-11-23 21:33:02 -0800  6) 
    1bc1c018 (Daniel Corson  2010-03-23 13:55:10 -0700  7) sys.path.insert(0, 'src')
    1bc1c018 (Daniel Corson  2010-03-23 13:55:10 -0700  8) from phpsh import __version__
    1bc1c018 (Daniel Corson  2010-03-23 13:55:10 -0700  9) 
    

    Distributed Workflow

    Distributed Workflow overview

    Image from http://progit.org/book/

    SSH keys

    Because typing passwords every time sucks

    Generate keys:

    $ ssh-keygen 
    Generating public/private rsa key pair.
    Enter file in which to save the key (/home/mkopala/.ssh/id_rsa): 
    Enter passphrase (empty for no passphrase): 
    Enter same passphrase again: 
    Your identification has been saved in /home/mkopala/.ssh/id_rsa.
    Your public key has been saved in /home/mkopala/.ssh/id_rsa.pub.
    The key fingerprint is:
    55:f2:06:d1:9c:7e:f8:f3:99:0b:e7:e6:81:53:42:13 mkopala@platypus
    

    Public key:

    $ cat ~/.ssh/id_rsa ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8FBFq1FZYoZtF9Sdr9/zwsU0xg/i54eq48uwd9RgyX91d2/qPJTQzsf8DKt/uTWKf1DyAUtclNz/MGQjCcHgip0XhuJLd1ChMmx0X/re81e3/WGNn/H/TcsxA2P36QVgB0kcPxnECWuEO62sQ0+2tz8l2S0io0dZntX/7oStM8E5aUA27mv2sUzdcD4p0ft5RJdT1dPdQhAB8QA8JcpCQ2OBSXjQ7gVYv3QdZDtGDJvlLr4KVUT4DAn3u6DLo3/sKkbL51srxpRMlNz/jFKA93pyGhdNDzDScJ0ucl7h+DZeF+IfoMYx8zXumismQDwTt6aHqLisPzylSeVo88q53 mkopala@home

    Private key:

    -----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY-----

    Put the contents of the id_rsa.pub file in to the ~/.ssh/authorized_keys file on the server.

    $ cat ~/.ssh/authorized_keys ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8FBFq1FZYoZtF9Sdr9/zwsU0xg/i54eq48uwd9RgyX91d2/qPJTQzsf8DKt/uTWKf1DyAUtclNz/MGQjCcHgip0XhuJLd1ChMmx0X/re81e3/WGNn/H/TcsxA2P36QVgB0kcPxnECWuEO62sQ0+2tz8l2S0io0dZntX/7oStM8E5aUA27mv2sUzdcD4p0ft5RJdT1dPdQhAB8QA8JcpCQ2OBSXjQ7gVYv3QdZDtGDJvlLr4KVUT4DAn3u6DLo3/sKkbL51srxpRMlNz/jFKA93pyGhdNDzDScJ0ucl7h+DZeF+IfoMYx8zXumismQDwTt6aHqLisPzylSeVo88q53 mkopala@home

    git clone

    Why let one person have all the fun?

    Make a local clone, pulling down the entire history for a repository

    mkopala@platypus:~/src$ git clone https://github.com/progit/progit.git
    Cloning into progit...
    remote: Counting objects: 10042, done.
    remote: Compressing objects: 100% (3056/3056), done.
    remote: Total 10042 (delta 5453), reused 9733 (delta 5187)
    Receiving objects: 100% (10042/10042), 10.64 MiB | 2.43 MiB/s, done.
    Resolving deltas: 100% (5453/5453), done.
    

    git fetch

    Pull down changes for the remote branches

    git fetch
    
    remote: Counting objects: 92, done.
    remote: Compressing objects: 100% (29/29), done.
    remote: Total 39 (delta 32), reused 13 (delta 9)
    Unpacking objects: 100% (39/39), done.
    From git+ssh://example.com/path/to/repo
     + ea78579...9562048 mybranch   -> origin/mybranch  
       0a3d8ef..bd80cd6  master     -> origin/master
    

    git remote

    git remote update
    git remote add
    

    git pull

    Central Repository Setup

    There are several ways to do this ...

    On the server:

    Create an empty bare repository

    git init --bare --shared
    

    Back at home:

    Add "origin" remote repository

    git remote add origin _url_
    git config branch.master.remote origin
    git config branch.master.merge refs/heads/master
    

    Push up changes

    git push
    

    .git/config

    Configuration file for the repository

    $ cat .git/config 
    [core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
    [remote "origin"]
        url = git+ssh://user@example.com/srv/git/someproj
        fetch = +refs/heads/*:refs/remotes/origin/*
    [branch "master"]
        remote = origin
        merge = refs/heads/master
        rebase = true
    [remote "bob"]
        url = git+ssh://user@example.com/home/bob/src/someproj
        fetch = +refs/heads/*:refs/remotes/bob/*
    [reviewboard]
        url = https://rb.example.com/
    

    Best Practices

    Before committing:

    git status
    

    When committing:

    git commit -v 
    

    Format your commit message properly

    Commit often - especially when things are working * you can always squash or amend commits later ...

    This is my commit message summary
    
    * changed some stuff in module A
    * cleaned up some heinous code duplication
    * fixed a nasty bug
    

    This makes it so git shortlog and git log --oneline are pretty and make sense

    20 minutes

    GUI Goodness

    Because pictures are pretty and useful

    Tcl/Tk based history view packaged with git

    gitk
    

    View your diffs in a GUI

    git difftool --tool=kdiff3
    * see the `difftool` page for more tool options
    

    Stage and commit changes

    git gui
    

    Code Reviews

    They're not optional any more

    git remote add
    git remote update
    git log -p
    git log --stat
    git diffall
    

    Git vs. Subversion

    http://git.or.cz/course/svn.html

    git clone _url_
    
    git pull
    
    git init
    git add .
    git commit
    
    git diff
    
    git diff rev path
    
    git status
    
    git checkout
    
    git add _file_
    git rm _file_
    git mv _file_
    
    git commit -a
    
    git log 
    git blame
    
    svn checkout _url_
    
    svn update
    
    svn admin create _repo_
    svn import file:///repo
    
    
    svn diff
    
    svn diff -rrev path
    
    svn status
    
    svn revert
    
    svn add _file_
    svn rm _file_
    svn mv _file_
    
    svn commit
    
    svn log | less
    svn blame
    

    Dealing with SVN

    If you have to ...

    git svn clone _url_
    git svn rebase
    git svn dcommit
    git svn log
    

    GitHub

    (More) Advanced Git

    My choice of 'advanced' is rather arbitrary

    Aliases
    git am
    git bisect
    git diffall
    git rebase
    git reflog
    git gc
    git patch
    git revert
    git archive
    git submodule
    rerere
    git filter-branch
    graft

    I've only got slides for some of these. I've used all of them at some point though except am and patch.

    Aliases

    Create an alias for any git command

    git config --global alias.st "status"
    

    git diffall

    Clone from github & copy in to main directory:

    git clone https://github.com/thenigan/git-diffall
    sudo cp git-diffall /usr/lib/git-core
    

    View diffs:

    git diffall <ref1> <ref2>
    

    git bisect

    Track down bugs in log n time!

    Start the binary search

    git bisect start
    

    Mark commit as "good"

    git bisect good
    

    Mark commit as "bad"

    git bisect good
    

    git rebase

    With great power, comes great responsibility

    Find out how far to go back

    git log --oneline
    

    Interactive rebase

    git rebase -i _commitid_
    

    If you have a merge conflict, fix the file, then:

    git add 
    git rebase continue
    
    # Commands:
    #  p, pick = use commit
    #  r, reword = use commit, but edit the commit message
    #  e, edit = use commit, but stop for amending
    #  s, squash = use commit, but meld into previous commit
    #  f, fixup = like "squash", but discard this commit's log message
    

    git reflog

    Never fear losing a commit again ...

    9db0d3e HEAD@{0}: commit: Added a bunch more content
    ac4e09d HEAD@{1}: commit: Lots of updates to the slide
    9e6c0c0 HEAD@{2}: commit (initial): Initial version of
    

    git gc

    Because smaller is better

    Clean up old objects, refs, and compress pack files

    git gc
    
    $ git gc
    Counting objects: 3487, done.
    Delta compression using up to 2 threads.
    Compressing objects: 100% (1665/1665), done.
    Writing objects: 100% (3487/3487), done.
    Total 3487 (delta 2254), reused 2853 (delta 1775)
    

    git revert

    So that change you made last week wasn't a good idea afterall

    Revert the changes made by a commit, and create a new commit object

    git revert <ref>
    

    git archive

    Because tar cvfz ... and zip are sooo 2005

    Get the tree SHA1 for the mydir directory from the last commit

    COMMIT=$(git ls-tree HEAD mydir | awk '{print $3}')
    

    Create an archive of the directory

    git archive --prefix mydir/ -o test.zip $COMMIT
    

    Changelog

    Ok, not actually very advanced

    git log > CHANGELOG
    

    Resources

    Lots more Git goodness

    Git Main Site

    Books

    Presentations

    Stack Overflow

    Git Man Pages

    git help
    

    End