Me, Myself & Git

An opiniated talk on Git and workflows

By Joris Kraak / @joriskraak using Reveal.js

Available online at https://bauglir.github.io/me-myself-and-git

Agenda

  • Why Git?
  • Structure
  • Git in Action
  • Collaboration
  • SSH
  • Tips & Tricks

Why Git?

Version Control

Track history

Collaboration

Proposal evaluation

Backup purposes

Distributed

Every user has their own copy of the repository

Changes can be shared directly between repositories

The (optional) server copy is not really special

Structure

Git in Action

Create a repository

$ git init my-project && cd my-project
Initialized empty Git repository in /home/jkraak/Projects/my-project/.git/

Or clone an existing one

$ git clone git@github.com:bauglir/me-myself-and-git.git
Cloning into 'me-myself-and-git'...
remote: Counting objects: 7127, done.
remote: Total 7127 (delta 0), reused 0 (delta 0), pack-reused 7127
Receiving objects: 100% (7127/7127), 5.38 MiB | 2.52 MiB/s, done.
Resolving deltas: 100% (3767/3767), done.
Checking connectivity... done.

Add some files

$ echo 'foo' > foo.txt
$ echo 'bar' > bar.txt
$ git status
On branch master

Initial commit

Untracked files:
  (use "git add < file>..." to include in what will be committed)

          bar.txt
          foo.txt

nothing added to commit but untracked files present (use "git add" to track)

git status is your friend! It provides hints on what to do next

$ git add foo.txt
$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached < file>..." to unstage)

        new file:   foo.txt
        new file:   bar.txt
$ git commit -m 'Add foo and bar literature'
[master (root-commit) 3f91579] Add foo and bar literature
 2 files changed, 2 insertions(+)
 create mode 100644 bar.txt
 create mode 100644 foo.txt

Making Changes

$ echo 'baz' >> foo.txt
On branch master
Changes not staged for commit:
  (use "git add < file>..." to update what will be committed)
  (use "git checkout -- < file>..." to discard changes in working directory)

        modified:   foo.txt

no changes added to commit (use "git add" and/or "git commit -a")
$ git diff
diff --git a/foo.txt b/foo.txt
index 257cc56..0c071e1 100644
--- a/foo.txt
+++ b/foo.txt
@@ -1 +1,2 @@
 foo
 +baz
$ git add -u && git commit -m 'Add baz reference'
[master e981856] Add baz reference
 1 file changed, 1 insertion(+)

Viewing the history

$ git log
commit e9818560edc7f0cf41317f15eec28167e507a3f7
Author: Joris Kraak < me@joriskraak.nl>
Date:   Tue Feb 24 12:20:25 2015 +0100

    Add baz reference

commit 3f91579623ef93cde0ebf5300b6da99bd70fa835
Author: Joris Kraak < me@joriskraak.nl>
Date:   Tue Feb 24 12:08:31 2015 +0100

    Add foo and bar literature

Undoing Mistakes

$ git rm *
rm 'bar.txt'
rm 'foo.txt'
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD < file>..." to unstage)

          deleted:    bar.txt
          deleted:    foo.txt
$ git reset HEAD foo.txt
Unstaged changes after reset:
D       foo.txt
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD < file>..." to unstage)

        deleted:    bar.txt

Changes not staged for commit:
  (use "git add/rm < file>..." to update what will be committed)
  (use "git checkout -- < file>..." to discard changes in working directory)

        deleted:    foo.txt
$ git checkout -- foo.txt
On branch master
Changes to be committed:
  (use "git reset HEAD < file>..." to unstage)

        deleted:    bar.txt
            
$ ls
foo.txt

Collaboration

Branches make it easier to collaborate on a shared code base

@tabqwerty: git gets easier once you get the basic idea that branches are homeomorphic endofunctors mapping submanifolds of a Hilbert space.

What made Git easy for me is visualizing everything as a tree

$ git log --graph --oneline --decorate --all

or get a GUI capable of showing the history formatted as a tree

Workflow

Git is very flexible with regards to picking a workflow

Popular choices are:

My Flow

Keep a single master branch

Implement new features/bug fixes taking more than a single commit on a separate branch, then merge into master

Before merging, 'rebase' feature branch on master and fix any conflicts that may arise

Make the merge back into master explicit by forcing a merge commit using git merge --no-ff

This results in a very clean history where the work on features stays clearly visible after a merge

http://mislav.uniqpath.com/2013/02/merge-vs-rebase/

My Flow in Action

Let's force two branches to diverge

$ git checkout -b my-branch
Switched to a new branch 'my-branch'
$ cp foo.txt quuz.txt
$ git add quuz.txt && git commit -m 'Add quuz literature'
[my-branch cb4c59e] Add quuz literature
 1 file changed, 2 insertions(+)
 create mode 100644 quuz.txt
$ git checkout master
Switched to branch 'master'
$ cp foo.txt quux.txt
$ git add quux.txt && git commit -m 'Add quux literature'
[master afda51f] Add quux literature
 1 file changed, 2 insertions(+)
 create mode 100644 quux.txt
$ git checkout my-branch
Switched to branch 'my-branch'

'my-branch' does not yet contain the quux literature

$ ls
bar.txt foo.txt quuz.txt

Take 'my-branch' and attach it to the new top of the tree

$ git rebase master my-branch
First, rewinding head to replay your work on top of it...
Applying: Add quuz literature
$ ls
bar.txt foo.txt quux.txt quuz.txt

Finally merge 'my-branch' into 'master'

$ git checkout master
Switched to branch 'master'
$ git merge --no-ff my-branch -m 'Merge branch my-branch'
Merge made by the 'recursive' strategy.
 quuz.txt | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 quuz.txt

Updated History

*   f08e76f (HEAD, master) Merge branch my-branch
|\
| * 29233b8 (my-branch) Add quuz literature
|/
* afda51f Add quux literature
* e981856 Add baz reference
* 3f91579 Add foo and bar literature

Sharing Work

Work can be shared using 'remotes'

The 'server' is typically referred to as 'origin'

When cloning a repository, the 'origin' is automatically set

Sharing Work in Action

First create a 'server' repository

$ git init --bare server

Then add the 'server' as a remote

$ git remote add origin server

Verify the remotes of the current repository

$ git remote -v
origin  server (fetch)
origin  server (push)

Publish our local work

$ git push origin master

Local and remote history

* deadbeef (origin/foo) Remote only work
| *   f08e76f (HEAD, master, origin/master) Merge branch my-branch
| |\
| | * 29233b8 (my-branch) Add quuz literature
| |/
|/
* afda51f Add quux literature
* e981856 Add baz reference
* 3f91579 Add foo and bar literature

git fetch remote_name branch_name synchronizes local branch history with remote

git pull remote_name branch_name fetches and merges or rebases a local branch with its remote equivalent

git push remote_name branch_name publishes local history to remote repository

Hosting

BitBucket, GitHub, GitLab (CE)

Websites hosting Git repositories

Provide online functionality for branching and merging (Pull Requests)

Issue tracking capable of linking to code

Typically used as the 'server'

SSH

Git typically communicates over SSH

SSH requires public/private key pairs

These have to be in openSSH format

For generation use either the GitHub Client or PuTTYGen

The private key should be password protected and kept safe

The public key can be distributed where necessary

To cache the private key password, solutions such as Pageant can be used

Tips & Tricks

  • Never rebase 'published' commits
  • Same goes for git commit --amend
  • Branches are just labels for commit hashes
  • Git only tracks files, to include empty directories put a `.gitkeep` file in them
  • Adding -p to most operations allows more granular control (i.e. within files)
  • Get comfortable with the command line, for GUI SourceTree seems nice
  • git config --global alias.graph 'log --graph --oneline --decorate --all'
  • The stash (git stash) allows temporary storage of in-progress work, useful when rebasing
  • git push -u remote_name branch_name 'tracks' remote branches
  • In case SSH is blocked use git config --global url.https://.insteadOf git://
  • git config --global color.ui true forces pretty colors
  • git config --global pull.rebase true forces a pull to always 'rebase' instead of 'merge'
  • merge, rebase, etc. have a --abort flag

Questions?

Further Reading/Watching

Useful Links