Git logo xkcd

🤔

0. Recap

“git” is not an acronym

A completely ignorant, childish person with no manners.

A distributed database

Distributed VCS

What's a commit?

$ git show
commit deadbeef16165bb95a541321a7acf9cef9731c1d
Author: Alexander Groß <agross@therightstuff.de>
Date:   Sat Feb 6 12:41:24 2010 +0100

    This is an awesome commit!

    Why, you ask? Let me explain how I…

diff --git a/hello.txt b/hello.txt
index afdb358..4b5fa63 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1,3 +1,4 @@
-I love deleting code

-I replaced this line
+with this line, because it’s better
+
+This should have been here since forever

What's a commit?

A commit object has references to tree and blob objects

History = Linked commits

The history is a sequence of commits objects

What's a branch?

The HEAD pointer

Creating a branch

$ git branch topic [<where>]

( <where> defaults to HEAD)

Current branch = where HEAD is

$ git checkout topic

Rule #1

The only branch that can change is the current branch.

Rule #2

When HEAD's position changes your working copy will be updated.

Uncommitted changes will be attempted to be preserved.

Pointers everywhere

Pointers reference individual commits

Pointers everywhere

Full data model (first commit)

Pointers everywhere

Full data model (second commit)

Pointers everywhere

Full data model (third commit)

1. Detached HEAD

A detached head of a sculpture

Detached HEAD

$ git checkout deadbeef
Note: checking out 'deadbeef'.

You are in 'detached HEAD' state. You can look
around, make experimental changes and commit them…

You're on no branch when you checkout a SHA, tag or remote branch.

Restoring unreachable commits

$ git reflog
6bbd21d HEAD@{0}: commit (amend): JUG WIP
392bf47 HEAD@{1}: commit (amend): JUG WIP
e7d4298 HEAD@{2}: commit: JUG WIP
71ab831 HEAD@{3}: checkout: moving from gh-pages to jug
71ab831 HEAD@{4}: commit: Support more than one fade
76cd1b0 HEAD@{5}: commit: Tabs -> spaces
b6f3d91 HEAD@{6}: commit: Tune letter spacing for long git commands
cbc3b80 HEAD@{7}: commit: Fix creation of reproduction commit

2. The index

Content Lifecycle

Take-aways

  • Partial operations
    $ git {add,reset HEAD,checkout} --patch
  • Temporarily ignore changes to tracked files
    $ git update-index --[no-]assume-unchanged
  • List ignored changes
    $ git ls-files -v | grep '^h'

3. History rewriting

Modifying the last commit

$ git commit --amend -m 'C was bad'

Undoing the last commit

$ git reset --hard HEAD~

Rewriting the whole graph 💣

  • Extracting libraries from projects
    $ git filter-branch --subdirectory-filter src/lib -- --all
  • Ensuring no internal files are published when a project is open-sourced
    $ git filter-branch --index-filter 'git rm secret.txt' HEAD
  • Converting Subversion repositories with svn:externals (there be 🐉)
    $ git svn-clone-externals svn://…

Rewriting parts of the graph

🔜

I'll tell my story after I heard yours

$ git checkout topic
$ git rebase master

Commit C and D is applied on top of master as C' and D'.

Complex Rebase

How to get rid of C when rebasing client on master?

Complex Rebase

$ git rebase --onto master server client

A Second Variant

$ git rebase --onto B server client

Preparing for code review

$ git rebase --interactive A

4. Integration strategies

Diverged history

Recursive merge

$ git checkout master
$ git merge topic

Recursive merge: Integrates two diverged branches.

Undoing the merge

$ git reset --hard E

# Like a pro (covers recursive and ff merges):
$ git reset --hard @@{1}

Linear history

Fast-forward merge

$ git checkout master
$ git merge topic

Fast-forward merge: The master pointer can be moved from C to E without losing commits reachable from master.

Squash merge 🍋

$ git merge --squash [--commit] topic

Apply commits from the topic branch combined as a single commit on top of HEAD.

Octopus merge 🐙

$ git merge perf css report

Integrate any number of non-conflicting branches with a single merge commit.

Cherry-pick 🍒

$ git cherry-pick [--no-commit] D

Apply a commit from somewhere else.

More options

  • Make obsolete's commits reachable, but keep tree as-is
    (i.e. archive obsolete)
    $ git merge --strategy=ours obsolete
  • Craft merge commit with release's tree
    (i.e. no file-based merging)
    $ git commit-tree release^{tree}
                      -m "Merge branch 'release'"
                      -p HEAD
                      -p release

Throw-Away Integration Branches

  • Use temporary pu branches
  • Do not base any work off of pu
  • Enable “Reuse recorded resolution”
    git config --global rerere.enabled true
    git config --global rerere.autoUpdate true
# All branches were tested in isolation
Three features are done and work in isolation
$ git checkout -b pu master
Create a temporary branch for proposed updates
$ for b in perf css report; do git merge $b; done
Merge topics and resolve conflicts, test all feature together
$ git reset --hard master
Back to square one
$ for b in css report; do git merge $b; done
The second integration attempt is successful
$ git checkout master; git merge pu; git branch -d pu
Merge pu into master

5. Remotes

Centralized

Centralized workflow

Integration Manager

Integration manager

Benevolent Dictator

Benevolent dictator

Ad-hoc

Server (9418/tcp)

$ git daemon --base-path=. --export-all --verbose
[4242] Ready to rumble

Client

$ git clone git://host/relative/path/to/repo/.git foo
Cloning into 'foo'...
$ git remote add hans git://host/relative/path/to/repo/.git
$ git fetch hans
remote: Counting objects: 42, done.
…
From git://host/relative/path/to/repo/.git
   e3205a5..0282413  master        -> hans/master

6. Bug searching

v1.0 works but v1.1 contains a regression

$ git stash push -m "whatever you're doing"
v1.0 works but commits between v1.0 and v1.1 broke something

Start looking for the bug

$ git bisect start v1.1 v1.0
Start searching the breaking commit

Test commit D

$ make test # => error
Test your app at commit D

Give feedback about D

$ git bisect bad
Give git feedback about the test result

Test commit B

$ make test # => success
Test your app at commit B

Give feedback about B

$ git bisect good
Give git feedback about the test result

Test commit C

$ make test # => success
Test your app at commit C

Give feedback about C

$ git bisect good
Give git feedback about the test result

Culprit found!

# D is the first bad revision
git reports first bad revision

Exit search

$ git bisect reset
After reset the graph looks like before

Variant: bisect with cherry-pick

$ git checkout -b repro v1.1 && git commit -m 'repro'
Create bug reproduction commit

Start looking for the bug

$ git bisect start v1.1 v1.0
Create bug reproduction commit

Apply repro commit

$ git cherry-pick repro
Apply reproduction on top of HEAD

Test commit D + R'

$ make test # => error
Test your app at commit D with R'

Undo R'

$ git reset --hard HEAD~
Undo R' and return to commit D

Give feedback about D

$ git bisect bad
Give git feedback about the test result

Repeat!

$ git cherry-pick repro
Give git feedback about the test result

Bisecting

  • Attempts to find bug-introducing commits by testing a minimum set of revisions
  • Enters detached HEAD state while searching
  • Automate testing
    git bisect run <some-script>
  • some-script can do things like cherry-picking reproductions, demo available
  • After the bad commit has been found, undo it
    git revert [--no-commit] <bad-commit>

Thanks!