Git, from the beginning

Post on 22-Jan-2018

158 views 2 download

Transcript of Git, from the beginning

git, from the beginningOr, how not cut ourselves on all the shiny options

What we'll cover

• What git is

• Core concepts in git

• Github flow: collaboration (1)

• The core set of git operations

• Github flow: collaboration (2)

What git is

• A development tool

• A history tool

• A collaboration tool

A development tool

Keep known good states as you work on a feature

C1 C2

def unlock():global.unlock()

def unlock():global.unlock()if global.errors:

raise Invalid

def unlock():if global.errors:

raise Invalidglobal.unlock()

A history tool

Find why code is the way it is, and how it changed

C1 C2

def unlock():try:

sem.unlock()except:

passif global.errors:

raise Invalidglobal.unlock()

C3

A collaboration tool

Work with others, build stuff together!

C1

C2

C3

Well, I'm stuck. I

need a function to

read from the

Wubstore.

A collaboration tool

Work with others, build stuff together!

C1

C2

C3

It's okay, I've got you.

I've just finished

exactly what you

need. C4

C5

A collaboration tool

Work with others, build stuff together!

C1

C2

C3C4

C5

C6

Core concepts in git

• Repository and working tree

• Commits

• The index

• Branches

• Collaborating across repositories

Repository and working tree

Working tree is your files; repository is the history (in .git)

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

A git commit

A commit has an id (c1 here), some metadata, and a snapshot of the working tree

C1

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

Metadata:

author(s)

date

message

And some other boring stuff.

Commits build a history graph

Each commit has a parent

C1 C2 C3 C4 C5

Commits build a history graph

Multiple commits can have the same parent

C1 C2

C3

C4 C5

C1 C2

C3

C4 C5

C6

Commits build a history graph

And we can merge these back together again

The index

The index is a staging area for your next commit

C1

my_module/

__init__.py

database.py

transform.py

my_module/

__init__.py

database.py

transform.py

utils.py

Working tree

The index

Put changes from your working tree into the index

C1

my_module/

__init__.py

database.py

transform.py

my_module/

__init__.py

database.py

transform.py

utils.py

Working tree

my_module/

__init__.py

database.py

transform.py

utils.py

Index

(no changes)

The index

And turn the index into your next commit

C1

my_module/

__init__.py

database.py

transform.py

my_module/

__init__.py

database.py

transform.py

utils.py

Working tree

my_module/

__init__.py

database.py

transform.py

utils.py

C2 Index

(now contains no changes) (no changes)

Branches point to commits

This is not a branch!

C1 C2

C3

C4 C5

C1 C2

C3

C4 C5

master

first branch

second branch

Branches point to commits

Branches allow you to keep track of the state of multiple different pieces of work.

Branches point to commits

The current branch is whatever you're working on. You can also just call this HEAD.

C1 C2

C3

C4 C5

master

first branch

second branch

HEAD

Collaborating across repositories

As well as your (local) repository, each developer will have one too

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

My repository Alice's repository

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

my_module/

__init__.py

database.py

transform.py

utils.py

setup.py

Bob's repository

Collaborating across repositories

And like our local repository, they contain commits

My repository Alice's repository

Bob's repository

C1 C2 C1 C2 C4

C1 C2

C3

Collaborating across repositories

And they also contain branches

My repository Alice's repository

Bob's repository

C1 C2 C1 C2 C4

C1 C2

C3

master master

master

bobwork

alicework

Remotes

A local repository can keep track of "remote" repositories

My repository Alice's repository

C1

C2master

C5 my_work

C1

C2master

C3

alice_work C4

Remote tracking branches

Which means it keeps track of the commits and branches from those remotes

My repository Alice's repository

C1

C2master

C5 my_work

C1

C2master

C3

alice_work C4

C3

alice/alice_work C4

Local branches and upstreams

A remote tracking branch is "upstream" of the local equivalent

My repository

C1

C2master

C5 my_work C3

alice/alice_work C4 alice_work

Alice's repository

C1

C2master

C3

alice_work C4

Upstream

Collaboration via bitbucket

Every developer keeps track of the branches in a central repository

My repository Alice's repository

C1

C2master

C5 my_work

C1

C2master

C3

alice_work C4

C3

bitbucket/alice_work C4

Bob's repository

C1

C2master

C3

bitbucket/alice_work C4

Lots of branches!

Bitbucket repository

C5

bitbucket/my_work

C5

bitbucket/my_work

Github flow: collaboration (1)

Run tests

Push your local to remote Create a pull request

Make changes in response

Make your commits

Create fixup commitsPR approvalCollapse fixup commits with the things they fixed

Force push branch Merge into master

Run tests

Review comments

The core set of git operations

• Working with remotes

• Working with branches

• Managing the index (and working tree)

• Working with commits

• Remotes and branches

• Rebasing: working on the history graph

Working with remotes

• git clone -o remote-name remote-url makes a new local repository out of a remote, giving it a name locally My repository github repository

C1 C2 C1 C2 C4

master master alicework

git clone in action

$ git clone -o github https://github.com/jaylett/lists-of-living-things.git Cloning into 'lists-of-living-things'... remote: Counting objects: 34, done. remote: Compressing objects: 100% (22/22), done. remote: Total 34 (delta 12), reused 33 (delta 11), pack-reused 0 Unpacking objects: 100% (34/34), done. $ cd lists-of-living-things $ ls README.md firs.md flowers.md

Working with remotes

• git fetch -p remote-name updates your view of a remote (fetching commits you don't have, and updating remote tracking branches)

My repository Alice's repository

C1

C2master

C5 my_work

C1

C2master

C3

alice_work C4

C3

alice/alice_work C4

git fetch in boring action

$ git fetch -p github

Working with branches

• git branch tells you what local branches there are

• git branch -r tells you about remote tracking branches

git branch in action

$ git branch * master $ git branch -r github/HEAD -> github/master github/flowers github/foxes github/master

Working with branches

• git checkout branch-name updates the index and the files in your working tree to match the branch. HEAD will now point at branch-name, which is now your current branch.

C1 C2

C3

C4 C5

branch-name

master

HEAD

C1 C2

C3

C4 C5

branch-name

master

HEAD

Working with branches

• If the branch doesn't exist, but it does exist as a remote tracking branch (as remote-name/branch-name) then a local branch is created with the remote tracking branch as its upstream My repository

C1

C2master

C5 my_work C3

alice/alice_work C4 alice_work

Alice's repository

C1

C2master

C3

alice_work C4

git checkout in action

$ git checkout master Already on 'master' Your branch is up-to-date with 'github/master'. $ ls README.md firs.md flowers.md $ git checkout foxes Branch foxes set up to track remote branch foxes from github. Switched to a new branch 'foxes' $ ls README.md firs.md flowers.md foxes.md

Working with branches

• git checkout -b branch-name will make a new branch pointing at the same commit the current branch pointed at. HEAD will now point at branch-name, which is now your current branch.

C2 C4 C5

branch-name

master

HEAD

git checkout -b in action

$ git checkout master Switched to branch 'master' Your branch is up-to-date with 'github/master'. $ ls README.md firs.md flowers.md $ git checkout -b fungi Switched to a new branch 'fungi' $ ls README.md firs.md flowers.md $ git status On branch fungi nothing to commit, working tree clean

Working with branches

• git show-branch branch1 branch2 will give an overview of which commits are in the branches you specify. A lot of IDEs and graphical tools (eg gitx on macOS) will provide a similar view.

C1 C2 C3

C4 C5 C6 C7

foxes

master

git show-branch in action

$ git show-branch master foxes * [master] Flesh out our list of firs. ! [foxes] fixup! Add a list of foxes. -- + [foxes] fixup! Add a list of foxes. + [foxes^] fixup! Add a list of foxes. + [foxes~2] Add two foxes missing from our list. + [foxes~3] Add a list of foxes. * [master] Flesh out our list of firs. *+ [foxes~4] Add some more firs.

Working with branches

• git merge --ff-only branch-name will move your current branch forward so it points at the same commit as branch-name. If it cannot (because there's no forward route through the commit graph) it will give an error.

C1 C2 C3 C4 C5

master bitbucket/master

git merge --ff-only in action (1)

$ git checkout master Switched to branch 'master' Your branch is up-to-date with 'github/master'. $ git remote add james https://github.com/jaylett/lists-of-living-things-2.git $ git fetch -p james remote: Counting objects: 3, done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 1), reused 2 (delta 0), pack-reused 0 Unpacking objects: 100% (3/3), done. From https://github.com/jaylett/lists-of-living-things-2 * [new branch] flowers -> james/flowers * [new branch] foxes -> james/foxes * [new branch] master -> james/master

git merge --ff-only in action (2)

$ git merge --ff-only james/master Updating c64f496..7b774eb Fast-forward firs.md | 4 ++++ 1 file changed, 4 insertions(+) $ git diff github/master diff --git a/firs.md b/firs.md index ed982db..4ee5697 100644 --- a/firs.md +++ b/firs.md @@ -29,3 +29,7 @@ * Noble fir * Red fir + +## Bracteata + + * Bristlecone fir

Working with branches

• git merge branch-name will create a merge commit so that your current branch contains all the changes on branch-name, back to their common parent.

C1 C2 C3

C4

branch-name

master

Working with branches

• This will update your current branch pointer, and hence also move HEAD.

• If it can, it will simply move your current branch and HEAD to point at branch-name, as with git merge --ff-only.

C1 C2 C3

C4

github/flowers

master

C4

git merge in action

$ git checkout master Already on 'master' Your branch is ahead of 'github/master' by 1 commit. (use "git push" to publish your local commits) $ git merge github/flowers Merge made by the 'recursive' strategy. flowers.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) $ git show -s HEAD commit 89f4241540a9479c2f9687becef69583dae0729e Merge: 7b774eb 8a34f24 Author: James Aylett <james@tartarus.org> Date: Sun May 14 18:59:35 2017 +0100

Merge branch 'github/flowers'

Managing the index (and working tree)

• git diff shows the differences between the working tree and the index

• git diff --cached shows the differences between the index and the last commit

• You can include a list of filenames to restrict to just those

• You can include a commit or branch name (before the filenames, if any) to show the difference between that commit and either the working tree or the index

• git status will summarise what's going on with the index and working tree

git diff in action

$ git checkout fungi Switched to branch 'fungi' $ sed -i~ -e 's/flora/living things/' README.md $ git diff diff --git a/README.md b/README.md index 184968c..9163ba0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Lists of flora +# Lists of living things -Each file in this repository will be a list of some type of flora. +Each file in this repository will be a list of some type of living things.

Managing the index (and working tree)

• git add -p allows you to add changes into the index; it will prompt for each change in the working tree

• git reset -p allows you to remove changes from the index again

• git checkout -p allows you to discard changes in the working tree (once gone, lost forever!)

git add -p in action

$ git add -p diff --git a/README.md b/README.md index 184968c..9163ba0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Lists of flora +# Lists of living things -Each file in this repository will be a list of some type of flora. +Each file in this repository will be a list of some type of living things. Stage this hunk [y,n,q,a,d,/,s,e,?]? y

git status & diff --cached in action

$ git status On branch fungi Changes to be committed: (use "git reset HEAD <file>..." to unstage)

modified: README.md $ git diff --cached diff --git a/README.md b/README.md index 184968c..9163ba0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Lists of flora +# Lists of living things -Each file in this repository will be a list of some type of flora. +Each file in this repository will be a list of some type of living things.

git reset -p in action

$ git reset -p diff --git a/README.md b/README.md index 184968c..9163ba0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Lists of flora +# Lists of living things -Each file in this repository will be a list of some type of flora. +Each file in this repository will be a list of some type of living things. Unstage this hunk [y,n,q,a,d,/,s,e,?]? y $ git status On branch fungi 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: README.md

no changes added to commit (use "git add" and/or "git commit -a")

git checkout -p in action

$ git checkout -p diff --git a/README.md b/README.md index 184968c..9163ba0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Lists of flora +# Lists of living things -Each file in this repository will be a list of some type of flora. +Each file in this repository will be a list of some type of living things. Discard this hunk from worktree [y,n,q,a,d,/,s,e,?]? y

$ git status On branch fungi nothing to commit, working tree clean

Managing the index (and working tree)

• If you have a new file, git add -N filename will tell git that it exists; you can then use git add -p to add the changes from the file. (You can use git add filename to do both in one go, but as you learn more about git this will become limiting, and it's worth getting into good practices early.)

• git mv filename new-filename will move a file from one place to another in the working tree and tells git to track that move in the index

• git rm filename removes a file from the working tree and tells git to add that removal to the index

git add -N in action

$ echo -e "# List of fungi\n\n * mushroom" > fungi.md $ git add -N fungi.md $ git add -p diff --git a/fungi.md b/fungi.md index e69de29..f47e8b0 100644 --- a/fungi.md +++ b/fungi.md @@ -0,0 +1,3 @@ +# List of fungi + + * mushroom Stage this hunk [y,n,q,a,d,/,e,?]? y

Working with commits: looking

• git diff commit-ref1 commit-ref2 will show the differences between two different commits

• git diff -w commit-ref1 commit-ref2 ignores whitespace changes

• git show commit-ref will show a summary of the commit, including its metadata and changes to previous

git diff in action

$ git diff HEAD^ HEAD diff --git a/firs.md b/firs.md index de0db27..ed982db 100644 --- a/firs.md +++ b/firs.md @@ -1,7 +1,31 @@ # Firs - * Balsam fir +## Abies + + * Silver fir + * Sicilian fir

[…]

git show in action

$ git show HEAD commit c64f496f779ac1d5f079e824aa5d66dbbb94fdaf Author: James Aylett <james@tartarus.org> Date: Sun May 14 17:44:43 2017 +0100

Flesh out our list of firs. Apparently firs come in different types! I probably haven't found all of them, but this is a better list.

diff --git a/firs.md b/firs.md index de0db27..ed982db 100644 --- a/firs.md +++ b/firs.md @@ -1,7 +1,31 @@ # Firs

[…]

Working with commits: making

• Make a commit from the index (it will open an editor for the commit message): git commit -v

• Do not use git commit -m ever. Various online tutorials tell you to. Ignore them: it will prevent you writing good commit messages.

git commit -v in action (1)

$ sed -i~ -e 's/flora/living things/' README.md $ git add -p diff --git a/README.md b/README.md index 184968c..9163ba0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Lists of flora +# Lists of living things -Each file in this repository will be a list of some type of flora. +Each file in this repository will be a list of some type of living things. Stage this hunk [y,n,q,a,d,/,s,e,?]? y

git commit -v in action (2)

# Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch fungi # Changes to be committed: # modified: README.md # new file: fungi.md # # ------------------------ >8 ------------------------ # Do not touch the line above. # Everything below will be removed. diff --git a/README.md b/README.md index 184968c..9163ba0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Lists of flora +# Lists of living things

[…]

git commit -v in action (3)

$ git commit -v

Here, git waits for you to edit the commit message and save it

[fungi cfcd7f5] Add mushrooms. 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 fungi.md $ git show -s commit cfcd7f5b02ea6008faf7235f1c3b2561ed50180f Author: James Aylett <james@tartarus.org> Date: Sun May 14 19:27:43 2017 +0100

Add mushrooms.

Working with commits: unmaking

• git reset --soft HEAD^ will undo a commit you just made, returning those changes to the index

C1

my_module/

__init__.py

database.py

transform.py

my_module/

__init__.py

database.py

transform.py

utils.py

Working tree

my_module/

__init__.py

database.py

transform.py

utils.py

C2 Index

(no changes)

my_module/

__init__.py

database.py

transform.py

* utils.py

git reset --soft in action

$ git show --oneline -s HEAD cfcd7f5 Add mushrooms. $ git reset --soft HEAD^ $ git status On branch fungi Changes to be committed: (use "git reset HEAD <file>..." to unstage)

modified: README.md new file: fungi.md $ git show --oneline -s HEAD c64f496 Flesh out our list of firs.

Working with commits: unmaking

• git reset --mixed commit-ref will undo all commits back to the given commit or branch, leaving the changes in the working tree.

C1

my_module/

__init__.py

database.py

transform.py

my_module/

__init__.py

database.py

transform.py

* utils.py

Working tree

my_module/

__init__.py

database.py

transform.py

utils.py

C2 Index

(no changes from C1)

Working with commits: unmaking

• git reset --hard commit-ref will undo all changes back to the given commit or branch. This is how you throw away changes.

C1

my_module/

__init__.py

database.py

transform.py

my_module/

__init__.py

database.py

transform.py

Working tree

my_module/

__init__.py

database.py

transform.py

utils.py

C2 Index

(no changes from C1) (no changes from C1)

Remotes and branches

• Push your commits up to a remote for the first time: git push -u remote-name branch-name

• After that, you can just do git push

My repository github repository

C1 C2 C1 C2 C3C3

master master mywork

mywork

git push -u in action

$ git commit -v [fungi 283cf75] Add mushrooms. 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 fungi.md $ git push -u james fungi Counting objects: 4, done. Delta compression using up to 24 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (4/4), 451 bytes | 0 bytes/s, done. Total 4 (delta 0), reused 0 (delta 0) To github.com:jaylett/lists-of-living-things-2.git * [new branch] fungi -> fungi Branch fungi set up to track remote branch fungi from james

git push in (boring) action

$ git push Everything up-to-date

Rebasing: working on the history graph

Rebasing is crazy powerful. We'll just look at some very simple uses.

• reword messages

• reorder commits

• collapse commits

Rebase recreates commits

• Rebase works by recreating commits from one part of the history to another (or to the same place)

C1 C2 C3 C4 C5

branch-point your-branch

C2’ C3’ C4’ C5’

your-branch

(before rebase)

(after rebase)

(the commit thatrebase starts from)

Interactive rebase can be controlled

C1 C2 C3 C4 C5

branch-point your-branch

C2’ C5’ C34

your-branch

(before rebase)

(after rebase)

(the commit thatrebase starts from)

Interactive rebase todo list:

1. Pick C2.2. Pick C5.3. Squash C3 and C4 together.

Interactive rebase

You start an interactive rebase session with:

git rebase -i commit-ref

which will drop into an editor with a list of every commit between commit-ref and HEAD. You can re-order the commits, and also change the action to be taken for each.

Interactive rebase actions

• pick will just recreate the commit • reword lets you edit the commit message first • fixup collapses the changes into the previous commit • squash is like fixup, but allows you to edit the commit

message afters

git rebase -i in action (1)

$ git checkout foxes Switched to branch 'foxes' Your branch is up-to-date with 'github/foxes'. $ git log --oneline HEAD~4..HEAD bb3f285 fixup! Add a list of foxes. 74ee814 fixup! Add a list of foxes. 8ec86e7 Add two foxes missing from our list. f1923f9 Add a list of foxes. $ git rebase -i HEAD~4

git now opens an editor with the commit & action list

git rebase -i in action (2)

pick f1923f9 Add a list of foxes. pick 8ec86e7 Add two foxes missing from our list. pick 74ee814 fixup! Add a list of foxes. pick bb3f285 fixup! Add a list of foxes.

# Rebase 9e096f3..bb3f285 onto 9e096f3 (4 commands) # # 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 rebase -i in action (3)

pick f1923f9 Add a list of foxes. fixup 74ee814 fixup! Add a list of foxes. fixup bb3f285 fixup! Add a list of foxes. pick 8ec86e7 Add two foxes missing from our list.

# Rebase 9e096f3..bb3f285 onto 9e096f3 (4 commands) # # 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 rebase -i in action (4)

$ git rebase -i HEAD~4 Successfully rebased and updated refs/heads/foxes. $ git log --oneline HEAD~2..HEAD 9ae7b9e Add two foxes missing from our list. 049c4ee Add a list of foxes.

Fixups and squashes

• If you spot that a previous commit isn't quite right, you can make a "fixup" commit: git commit --fixup commit-ref

• git rebase -i --autosquash will then automatically move those commits into the right place and mark them as fixup (rather than pick)

• to make a "squash" commit instead of fixup, use git commit --squash commit-ref

Github flow: collaboration (2)

Run tests

Push your local to remote Create a pull request

Make changes in response

Make your commits

Create fixup commitsPR approvalInteractive rebase to collapse fixup commits

Force push branch Merge into master

Run tests

Review comments

What is a pull request?

• A set of proposed commits, with a narrative

• An opportunity for review ahead of merging to master

• A way to actively collaborate rather than just using each others' work

The commands for github flow

• make commits: add -p, commit -v

• push your branch: push -u

• create fixup commits: commit --fixup

• collapse fixup commits: rebase -i --autosquash $(git merge-base HEAD master)

• force push branch: push -f

Questions?

• http://www.slideshare.net/jaylett/git-from-the-beginning

• @jaylett