Advanced Git

Or: more than you wanted to know, but just touching the surface

Overview

  • quick recap
  • GitHub Forks
  • Issues and Pull Requests

  • Ignoring Files with .gitignore

  • Hosting your own repositories with bare repositories
  • Branches (with git checkout)
  • Undoing your mistakes with git reset and git revert
  • Rewriting history with git rebase
  • Rewriting history, like, totally. with git rebase -i
  • The last resort: git reflog
  • My GitHub workflow

Help

Use

git somecommand --help

for unhelpful help.

Quick Recap

Configuration

In [2]:
git config --list
user.email=amueller@nyu.edu
user.name=Andreas Mueller
core.editor=vim
core.pager=
push.default=simple
pager.grep=false
color.ui=auto

Initialization

In [3]:
mkdir myrepo
cd myrepo
git init
Initialized empty Git repository in /home/andy/advanced_git_tmp/myrepo/.git/
In [4]:
ls -a
.  ..  .git
In [5]:
git status
On branch master

Initial commit

nothing to commit (create/copy files and use "git add" to track)

Adding and committing

In [6]:
echo "first line in file myfile" > myfile.txt
git status
On branch master

Initial commit

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

	myfile.txt

nothing added to commit but untracked files present (use "git add" to track)
In [7]:
git add myfile.txt
git status
On branch master

Initial commit

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

	new file:   myfile.txt

In [8]:
git commit -m "added a first line to my file"
git status
[master (root-commit) 7e4d620] added a first line to my file
 1 file changed, 1 insertion(+)
 create mode 100644 myfile.txt
On branch master
nothing to commit, working directory clean
In [9]:
git log
commit 7e4d6208c11e3daa867eff0a9d494e90a7510a1d
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:47 2016 -0400

    added a first line to my file

Changing and committing again

In [10]:
echo "second line in myfile is not much more interesting" >> myfile.txt
git status
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:   myfile.txt

no changes added to commit (use "git add" and/or "git commit -a")
In [11]:
git add myfile.txt
git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   myfile.txt

In [12]:
git commit -m "I added a second line"
git log
[master 344a292] I added a second line
 1 file changed, 1 insertion(+)
commit 344a292cc5a57060e0767f31aa020a16c8b5600a
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:48 2016 -0400

    I added a second line

commit 7e4d6208c11e3daa867eff0a9d494e90a7510a1d
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:47 2016 -0400

    added a first line to my file

Reverting a file to an older version

In [13]:
echo "omg a third line" >> myfile.txt
cat myfile.txt
fist line in file myfile
second line in myfile is not much more interesting
omg a third line

Reverting to the last commit:

In [14]:
git checkout myfile.txt
cat myfile.txt
fist line in file myfile
second line in myfile is not much more interesting

Reverting to a previous commit:

In [15]:
git checkout HEAD~1 myfile.txt
cat myfile.txt
fist line in file myfile
In [16]:
git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   myfile.txt

In [17]:
git commit -m "checked out myfile from first commit"
git log
[master 59d540c] checked out myfile from first commit
 1 file changed, 1 deletion(-)
commit 59d540c6d105d3d9b11ec7f0b2e662fc73c43532
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:49 2016 -0400

    checked out myfile from first commit

commit 344a292cc5a57060e0767f31aa020a16c8b5600a
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:48 2016 -0400

    I added a second line

commit 7e4d6208c11e3daa867eff0a9d494e90a7510a1d
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:47 2016 -0400

    added a first line to my file

Comparing files between versions

Compare all files to the previous commit (HEAD "minus" one).

In [18]:
git diff HEAD~1
diff --git a/myfile.txt b/myfile.txt
index d6cb53f..dad84b7 100644
--- a/myfile.txt
+++ b/myfile.txt
@@ -1,2 +1 @@
 fist line in file myfile
-second line in myfile is not much more interesting

Compare only myfile.txt to the previous commit.

In [19]:
git diff HEAD~1 myfile.txt
diff --git a/myfile.txt b/myfile.txt
index d6cb53f..dad84b7 100644
--- a/myfile.txt
+++ b/myfile.txt
@@ -1,2 +1 @@
 fist line in file myfile
-second line in myfile is not much more interesting

GitHub

GitHub is a way to host and share repos, but also a social network. GitHub makes it easy to contribute to existing repositories.

GitHub Recap

To work on a project with a project partner you:

  • create a repo
  • push your code to the repo
  • have them pull from your repo
  • add them as a "collaborator" on GitHub
  • now they can push their changes

Problems with the pull / push model:

  • Lack of transparency (you need to study the log)
  • Need to give out write permission to collaborators

Alternative model: Forks and Pull requests

To work on someone else's project you:

  • Fork (make your own copy) of the project
  • Change your project
  • Ask then to pull your changes into the main project - the pull request

Exercise

  • Fork my repository at http://github.com/amueller/repo_to_fork
  • Clone your forked version of the project (so you have a copy on your hard disk)
  • Create a new file, add and commit it.
  • Push the file to your forked repository.
  • Create a Pull Request to my original repository.

AKA "the simplified workflow"

Back to the command line!

Ignoring things

In [20]:
touch stupidtempfile.swp
touch whycompiling.pyc
git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

	stupidtempfile.swp
	whycompiling.pyc

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

Each line in .gitignore specifies one (kind of) file to ignore:

In [21]:
echo "*.swp" >> .gitignore
echo "*.pyc" >> .gitignore

In [22]:
ls -a
.  ..  .git  .gitignore  myfile.txt  stupidtempfile.swp  whycompiling.pyc

Ignored files will not show up in git status and will not be added when doing git add --all

In [23]:
git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

	.gitignore

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

Exercise

  • Create a new repo
  • Add file awesome_content.txt you want to commit.
  • Add a file local_notes.txt that you don't want to track.
  • do a git status
  • add local_notes.txt to .gitignore
  • do a git status
  • local_notes.txt should not show up in the status.
  • Add and commit awesome_content.txt.
  • Try adding local_notes.txt.
  • Should you add .gitignore?

Making Remotes

In [24]:
pwd
/home/andy/advanced_git_tmp/myrepo
In [25]:
cd ..
mkdir cloned_repo

In [26]:
cd cloned_repo

I can clone a local directory!

In [27]:
git clone ../myrepo
Cloning into 'myrepo'...
done.
In [28]:
tree ..
..
├── cloned_repo
│   └── myrepo
│       └── myfile.txt
└── myrepo
    ├── myfile.txt
    ├── stupidtempfile.swp
    └── whycompiling.pyc

3 directories, 4 files
In [29]:
cd myrepo
ls
myfile.txt
In [30]:
git remote -v
origin	/home/andy/advanced_git_tmp/cloned_repo/../myrepo (fetch)
origin	/home/andy/advanced_git_tmp/cloned_repo/../myrepo (push)
In [31]:
git log
commit 59d540c6d105d3d9b11ec7f0b2e662fc73c43532
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:49 2016 -0400

    checked out myfile from first commit

commit 344a292cc5a57060e0767f31aa020a16c8b5600a
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:48 2016 -0400

    I added a second line

commit 7e4d6208c11e3daa867eff0a9d494e90a7510a1d
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:47 2016 -0400

    added a first line to my file

Change something in the original repo:

In [32]:
cd ../../myrepo
cat myfile.txt
fist line in file myfile
In [33]:
echo "another line committed to the original repo" >> myfile.txt
git add myfile.txt
git commit -m "new commit in the original repo"
[master 18a3a33] new commit in the original repo
 1 file changed, 1 insertion(+)
In [34]:
git log
commit 18a3a33467da956c792681a283effcd7c72d1864
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:54 2016 -0400

    new commit in the original repo

commit 59d540c6d105d3d9b11ec7f0b2e662fc73c43532
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:49 2016 -0400

    checked out myfile from first commit

commit 344a292cc5a57060e0767f31aa020a16c8b5600a
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:48 2016 -0400

    I added a second line

commit 7e4d6208c11e3daa867eff0a9d494e90a7510a1d
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:47 2016 -0400

    added a first line to my file

Update the cloned repo ...

In [35]:
cd ../cloned_repo/myrepo

In [36]:
git pull origin
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /home/andy/advanced_git_tmp/cloned_repo/../myrepo
   59d540c..18a3a33  master     -> origin/master
Updating 59d540c..18a3a33
Fast-forward
 myfile.txt | 1 +
 1 file changed, 1 insertion(+)
In [37]:
git log
commit 18a3a33467da956c792681a283effcd7c72d1864
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:54 2016 -0400

    new commit in the original repo

commit 59d540c6d105d3d9b11ec7f0b2e662fc73c43532
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:49 2016 -0400

    checked out myfile from first commit

commit 344a292cc5a57060e0767f31aa020a16c8b5600a
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:48 2016 -0400

    I added a second line

commit 7e4d6208c11e3daa867eff0a9d494e90a7510a1d
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:47 2016 -0400

    added a first line to my file

Change something in the cloned repo:

In [38]:
echo "new line in cloned repo" >> myfile.txt
git add myfile.txt
git commit -m "new line in cloned repo"
[master b84d28d] new line in cloned repo
 1 file changed, 1 insertion(+)

But I can't push to the original repo. (Someone might have edited the files but not checked them in)

In [39]:
git push origin
true
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 320 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require 'git reset --hard' to match
remote: error: the work tree to HEAD.
remote: error: 
remote: error: You can set 'receive.denyCurrentBranch' configuration variable to
remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error: 
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To /home/andy/advanced_git_tmp/cloned_repo/../myrepo
 ! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to '/home/andy/advanced_git_tmp/cloned_repo/../myrepo'

bare repositories

Allow pushing to them, but not directly editing them. They are used only as remotes.

In [40]:
cd ../..
ls
cloned_repo  myrepo
In [41]:
mkdir bare_repo
cd bare_repo
git init --bare
Initialized empty Git repository in /home/andy/advanced_git_tmp/bare_repo/

Bare repos don't look like the content of the repository:

In [42]:
ls
branches  config  description  HEAD  hooks  info  objects  refs

Adding the bare repository as a remote to the existing repository myrepo:

In [43]:
cd ../myrepo
git remote add new_bare ../bare_repo

In [44]:
git remote -v
new_bare	../bare_repo (fetch)
new_bare	../bare_repo (push)

Pushing master of myrepo to the bare_repo:

In [45]:
git push new_bare master
Counting objects: 10, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (10/10), 907 bytes | 0 bytes/s, done.
Total 10 (delta 1), reused 0 (delta 0)
To ../bare_repo
 * [new branch]      master -> master

Add bare_repo as a remote in the cloned repo:

In [46]:
cd ../cloned_repo/myrepo

In [47]:
git remote add new_bare ../../bare_repo

Push the new changes we made in the clone to the bare repo:

In [48]:
git push new_bare master
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 320 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ../../bare_repo
   18a3a33..b84d28d  master -> master

Pull the changes from the clone to the original repo via the bare repo:

In [49]:
cd ../../myrepo
git pull new_bare master
From ../bare_repo
 * branch            master     -> FETCH_HEAD
   18a3a33..b84d28d  master     -> new_bare/master
Updating 18a3a33..b84d28d
Fast-forward
 myfile.txt | 1 +
 1 file changed, 1 insertion(+)
In [50]:
git log
commit b84d28dff6036a3f958752576dac5e9ea209598d
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:55 2016 -0400

    new line in cloned repo

commit 18a3a33467da956c792681a283effcd7c72d1864
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:54 2016 -0400

    new commit in the original repo

commit 59d540c6d105d3d9b11ec7f0b2e662fc73c43532
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:49 2016 -0400

    checked out myfile from first commit

commit 344a292cc5a57060e0767f31aa020a16c8b5600a
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:48 2016 -0400

    I added a second line

commit 7e4d6208c11e3daa867eff0a9d494e90a7510a1d
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:47 2016 -0400

    added a first line to my file

Bare repos are like your own personal GitHub! (kinda)

Exercise

  • Create a new bare repo and push to it form your original repository.
  • clone the bare repo.
  • change the clone and push to the bare repo.
  • Go to your original repository and pull from the bare repo.

Undoing your mistakes sneak preview

xkcd

Remove all files:

In [51]:
rm *
ls

Get them back!

From the last commit:

In [52]:
git reset --hard
HEAD is now at b84d28d new line in cloned repo

From any commit or remote:

In [53]:
git reset --hard new_bare/master
HEAD is now at b84d28d new line in cloned repo

The last line rescues you after screwing up your local copy.

Exercise

  • make sure your changes are pushed to the remote (bare) repository
  • delete all your files
  • commit deleting all files (git rm, not git add to stage that)
  • check the log
  • git reset --hard to your remote repository.
  • marvel at how everything turned out alright.

Branches

Allow parallel lines of development.

Create new branches with git checkout -b

In [54]:
git checkout -b new_branch_with_descriptive_name
Switched to a new branch 'new_branch_with_descriptive_name'
In [55]:
echo "new stuff that is reeeeaaally cool and maybe even works" > myfile.txt

In [56]:
git add myfile.txt
git commit -m "hurray, new stuff."
[new_branch_with_descriptive_name 3a2169a] hurray, new stuff.
 1 file changed, 1 insertion(+), 3 deletions(-)
In [57]:
git log
commit 3a2169aab7f3cf48b2926b35f19e03fbf5cfe3fb
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:59 2016 -0400

    hurray, new stuff.

commit b84d28dff6036a3f958752576dac5e9ea209598d
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:55 2016 -0400

    new line in cloned repo

commit 18a3a33467da956c792681a283effcd7c72d1864
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:54 2016 -0400

    new commit in the original repo

commit 59d540c6d105d3d9b11ec7f0b2e662fc73c43532
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:49 2016 -0400

    checked out myfile from first commit

commit 344a292cc5a57060e0767f31aa020a16c8b5600a
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:48 2016 -0400

    I added a second line

commit 7e4d6208c11e3daa867eff0a9d494e90a7510a1d
Author: Andreas Mueller <amueller@nyu.edu>
Date:   Tue Mar 29 11:32:47 2016 -0400

    added a first line to my file

Where is the branch?

In [58]:
git log --graph --decorate --all --oneline
* 3a2169a (HEAD -> new_branch_with_descriptive_name) hurray, new stuff.
* b84d28d (new_bare/master, master) new line in cloned repo
* 18a3a33 new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 I added a second line
* 7e4d620 added a first line to my file

master is at "new line in cloned repo", new_branch_with_descriptive_name is at "hurray, new stuff"

HEAD points to new_branch_with_descriptive_name.

In [59]:
alias nicelog="git log --graph --decorate --all --oneline"

Move to another branch with git checkout

In [60]:
git checkout master
Switched to branch 'master'
In [61]:
git log --graph --decorate --all --oneline
* 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
* b84d28d (HEAD -> master, new_bare/master) new line in cloned repo
* 18a3a33 new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 I added a second line
* 7e4d620 added a first line to my file

HEAD points at master.

The content of myfile.txt is the one at "new line in cloned repo", not "hurray, new stuff":

In [62]:
cat myfile.txt
fist line in file myfile
another line commited to the original repo
new line in cloned repo

We add another commit to master:

In [63]:
echo "add to the old stuff " >> another_file.txt
git add another_file.txt
git commit -m "working on another file on the master"
[master 35e6fa4] working on another file on the master
 1 file changed, 1 insertion(+)
 create mode 100644 another_file.txt

An now we see why it's called branch!

In [64]:
git log --graph --decorate --all --oneline
* 35e6fa4 (HEAD -> master) working on another file on the master
| * 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
|/  
* b84d28d (new_bare/master) new line in cloned repo
* 18a3a33 new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 I added a second line
* 7e4d620 added a first line to my file
In [65]:
ls
another_file.txt  myfile.txt

Check out the "new_branch" again:

In [67]:
git checkout new_branch_with_descriptive_name
Switched to branch 'new_branch_with_descriptive_name'

another_file.txt has disappeared:

In [68]:
ls
myfile.txt

Exercise

  • Create add and commit an empty file "homer_is_in.txt" in master.
  • Create a branch called "australia".
  • Change the content of homer_is_in.txt to "australia" and commit it.
  • Go back to the master branch, and then create a branch called "america"
  • Change the content of the file to "america" and commit it.
  • Check out the branch "australia" and check the content of the file.
  • Look at the log.

home_is_in

Merging Branches

Or reuniting the lines of development.

In [69]:
git checkout master
Switched to branch 'master'

Merge branches with git merge. Provide a message with -m.

In [70]:
git merge new_branch_with_descriptive_name -m "merge descriptive branch with cool feature"
Merge made by the 'recursive' strategy.
 myfile.txt | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

git merge --abort is your friend if there are many conflicts and you start to panic.

After a successful merge, all is clean:

In [71]:
git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

	.gitignore

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

Changes from both branches are there:

In [72]:
cat myfile.txt
new stuff that is reeeeaaally cool and maybe even works

The merge created a new commit:

In [73]:
git log --graph --decorate --all --oneline
*   266bd72 (HEAD -> master) merge descriptive branch with cool feature
|\  
| * 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
* | 35e6fa4 working on another file on the master
|/  
* b84d28d (new_bare/master) new line in cloned repo
* 18a3a33 new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 I added a second line
* 7e4d620 added a first line to my file

Exercise.

  • Check out master. Change the content of homer_is_in.txt to "doughnut plant" and commit it.
  • Look at the log.
  • Merge america into master and resolve the conflict so that the file contains "america".
  • In the master branch, the content of the file should now be "america".

Creating branches starting at commits

In [74]:
git checkout -b branch_started_further_back HEAD~3
Switched to a new branch 'branch_started_further_back'
In [75]:
git log --graph --decorate --all --oneline
*   266bd72 (master) merge descriptive branch with cool feature
|\  
| * 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
* | 35e6fa4 working on another file on the master
|/  
* b84d28d (new_bare/master) new line in cloned repo
* 18a3a33 (HEAD -> branch_started_further_back) new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 I added a second line
* 7e4d620 added a first line to my file

Deleting branches

In [76]:
git checkout -b another_new_branch
Switched to a new branch 'another_new_branch'
In [77]:
git log --graph --decorate --all --oneline
*   266bd72 (master) merge descriptive branch with cool feature
|\  
| * 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
* | 35e6fa4 working on another file on the master
|/  
* b84d28d (new_bare/master) new line in cloned repo
* 18a3a33 (HEAD -> another_new_branch, branch_started_further_back) new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 I added a second line
* 7e4d620 added a first line to my file

We can't delete the branch we're on so we go back to master:

In [80]:
git checkout master
Already on 'master'
In [81]:
git branch -d another_new_branch
Deleted branch another_new_branch (was 18a3a33).

Exercise

  • Delete the branch america
  • Delete the branch australia

Concept Review

DAG (directed acyclic graph) of commits = branches

Index vs staging area vs working tree

Remotes

So far we have:

  • Added commits to the DAG (git commit), branched (git checkout -b) and merged branches (git merge).
  • Moved the HEAD from branch to branch (git checkout)
  • Moved HEAD to a commit and started a branch there (git checkout -b <branchname> <commit>)
  • Pushed and pulled from Remotes (that kind of work like branches)

Most commands impact the DAG, and some of the Index, staging area and working tree.

  • The index stores commits.
  • The staging area stores a diff (lines added and removed).
  • The working tree stores files.

Undoing your mistakes

git revert and git reset

In [82]:
git checkout master
git log --graph --decorate --all --oneline
Already on 'master'
*   266bd72 (HEAD -> master) merge descriptive branch with cool feature
|\  
| * 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
* | 35e6fa4 working on another file on the master
|/  
* b84d28d (new_bare/master) new line in cloned repo
* 18a3a33 (branch_started_further_back) new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 I added a second line
* 7e4d620 added a first line to my file
In [83]:
ls
another_file.txt  myfile.txt
In [84]:
git revert HEAD~1 --no-edit
[master 64c8100] Revert "working on another file on the master"
 1 file changed, 1 deletion(-)
 delete mode 100644 another_file.txt
In [85]:
git log --graph --decorate --all --oneline
* 64c8100 (HEAD -> master) Revert "working on another file on the master"
*   266bd72 merge descriptive branch with cool feature
|\  
| * 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
* | 35e6fa4 working on another file on the master
|/  
* b84d28d (new_bare/master) new line in cloned repo
* 18a3a33 (branch_started_further_back) new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 I added a second line
* 7e4d620 added a first line to my file
In [86]:
ls
myfile.txt

Exercise

  • Revert the commit that merged america into master.

git reset

  • hard: working dir & move HEAD
  • mixed (default): only move HEAD

Moving head when on a branch (not headless) also moves where the branch is pointing!

In [87]:
git reset branch_started_further_back
Unstaged changes after reset:
M	myfile.txt
In [88]:
git log --graph --decorate --all --oneline
* 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
* b84d28d (new_bare/master) new line in cloned repo
* 18a3a33 (HEAD -> master, branch_started_further_back) new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 I added a second line
* 7e4d620 added a first line to my file
In [89]:
git status
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:   myfile.txt

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

	.gitignore

no changes added to commit (use "git add" and/or "git commit -a")
In [90]:
cat myfile.txt
new stuff that is reeeeaaally cool and maybe even works
In [91]:
git add myfile.txt
git commit -m "new stuff from the feature branch that I did stuff with"
[master 00cf774] new stuff from the feature branch that I did stuff with
 1 file changed, 1 insertion(+), 2 deletions(-)
In [92]:
git log --graph --decorate --all --oneline
* 00cf774 (HEAD -> master) new stuff from the feature branch that I did stuff with
| * 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
| * b84d28d (new_bare/master) new line in cloned repo
|/  
* 18a3a33 (branch_started_further_back) new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 I added a second line
* 7e4d620 added a first line to my file

--hard fixes (aka destroys) everything!

In [93]:
git checkout -b branch_well_move_back
Switched to a new branch 'branch_well_move_back'
In [94]:
git log --graph --decorate --all --oneline
* 00cf774 (HEAD -> branch_well_move_back, master) new stuff from the feature branch that I did stuff with
| * 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
| * b84d28d (new_bare/master) new line in cloned repo
|/  
* 18a3a33 (branch_started_further_back) new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 I added a second line
* 7e4d620 added a first line to my file
In [95]:
git reset --hard HEAD~3
HEAD is now at 344a292 I added a second line
In [96]:
git status
On branch branch_well_move_back
Untracked files:
  (use "git add <file>..." to include in what will be committed)

	.gitignore

nothing added to commit but untracked files present (use "git add" to track)
In [97]:
git log --graph --decorate --all --oneline
* 00cf774 (master) new stuff from the feature branch that I did stuff with
| * 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
| * b84d28d (new_bare/master) new line in cloned repo
|/  
* 18a3a33 (branch_started_further_back) new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 (HEAD -> branch_well_move_back) I added a second line
* 7e4d620 added a first line to my file

Exercise

  • Create a new branch way_back_machine.
  • Reset it to the first commit in your repository
  • Add a new commit
  • look at the graph.

git rebase

or: how to really shoot yourself in the foot even more!

rebase only works on the DAG (and moves HEAD)

rebase basically does every possible thing with the DAG

changing the tree means changing hashes!

"simple" form:

git rebase basebranch otherbranch

equivalently

git checkout otherbranch
git rebase basebranch

Put everything in otherbranch that is not in basebranch on top of otherbranch. (compute common root, cut of there)

In [98]:
git log --graph --decorate --all --oneline
* 00cf774 (master) new stuff from the feature branch that I did stuff with
| * 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
| * b84d28d (new_bare/master) new line in cloned repo
|/  
* 18a3a33 (branch_started_further_back) new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 (HEAD -> branch_well_move_back) I added a second line
* 7e4d620 added a first line to my file
In [99]:
git checkout branch_started_further_back
Switched to branch 'branch_started_further_back'
In [100]:
echo "Change in back branch" > new_file_in_back_branch.txt
git add new_file_in_back_branch.txt
git commit -m "added a file in the branch_started_further_back"

echo "more lines" >> new_file_in_back_branch.txt
git add new_file_in_back_branch.txt
git commit -m "second commit in the branch_started_further_back"
[branch_started_further_back 50d5233] added a file in the branch_started_further_back
 1 file changed, 1 insertion(+)
 create mode 100644 new_file_in_back_branch.txt
[branch_started_further_back 12be701] second commit in the branch_started_further_back
 1 file changed, 1 insertion(+)
In [101]:
git log --graph --decorate --all --oneline
* 12be701 (HEAD -> branch_started_further_back) second commit in the branch_started_further_back
* 50d5233 added a file in the branch_started_further_back
| * 00cf774 (master) new stuff from the feature branch that I did stuff with
|/  
| * 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
| * b84d28d (new_bare/master) new line in cloned repo
|/  
* 18a3a33 new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 (branch_well_move_back) I added a second line
* 7e4d620 added a first line to my file
In [102]:
git rebase master
First, rewinding head to replay your work on top of it...
Applying: added a file in the branch_started_further_back
Applying: second commit in the branch_started_further_back
In [103]:
git log --graph --decorate --all --oneline
* 59d2f69 (HEAD -> branch_started_further_back) second commit in the branch_started_further_back
* 0258c9e added a file in the branch_started_further_back
* 00cf774 (master) new stuff from the feature branch that I did stuff with
| * 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
| * b84d28d (new_bare/master) new line in cloned repo
|/  
* 18a3a33 new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 (branch_well_move_back) I added a second line
* 7e4d620 added a first line to my file

"advanced form"

using --onto

Three parts in rebase:

  • The branch to move (current HEAD or second in command)
  • The branch to compute the common parent with
  • The branch to put the stuff on top of

For the "simple" version 2 and 3 are the same. For the --onto version, you can specify 3 separately.

In [104]:
git log --graph --decorate --all --oneline
* 59d2f69 (HEAD -> branch_started_further_back) second commit in the branch_started_further_back
* 0258c9e added a file in the branch_started_further_back
* 00cf774 (master) new stuff from the feature branch that I did stuff with
| * 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
| * b84d28d (new_bare/master) new line in cloned repo
|/  
* 18a3a33 new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 (branch_well_move_back) I added a second line
* 7e4d620 added a first line to my file
In [105]:
git rebase master --onto new_branch_with_descriptive_name
First, rewinding head to replay your work on top of it...
Applying: added a file in the branch_started_further_back
Applying: second commit in the branch_started_further_back
In [106]:
git log --graph --decorate --all --oneline
* 157de4a (HEAD -> branch_started_further_back) second commit in the branch_started_further_back
* 08211a4 added a file in the branch_started_further_back
* 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
* b84d28d (new_bare/master) new line in cloned repo
| * 00cf774 (master) new stuff from the feature branch that I did stuff with
|/  
* 18a3a33 new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 (branch_well_move_back) I added a second line
* 7e4d620 added a first line to my file

Exercise

  • Add a second commit to your branch way_back_machine (from the last exercise) adding a new file.
  • Rebase only this second commit on top of master.
  • Feel like a git guru.
  • What do you expect the graph to look like?
  • Look at the graph.

Interactive rebase

(or you could apply a rusty nail to your retina)

We'll only discuss squashing and skipping. Needs to be done on the command line!

In [107]:
# git rebase -i new_branch_with_descriptive_name
false

In [108]:
git log --graph --decorate --all --oneline
* 4ec5822 (HEAD -> branch_started_further_back) all the changes I ever wanted!
* 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
* b84d28d (new_bare/master) new line in cloned repo
| * 00cf774 (master) new stuff from the feature branch that I did stuff with
|/  
* 18a3a33 new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 (branch_well_move_back) I added a second line
* 7e4d620 added a first line to my file

Exercise

  • Add two new commits to master
  • Squash them

Git reflog

Seek and ye shall find

In [109]:
git reflog -n 10
4ec5822 HEAD@{0}: rebase -i (finish): returning to refs/heads/branch_started_further_back
4ec5822 HEAD@{1}: rebase -i (squash): all the changes I ever wanted!
08211a4 HEAD@{2}: rebase -i (start): checkout new_branch_with_descriptive_name
157de4a HEAD@{3}: rebase finished: returning to refs/heads/branch_started_further_back
157de4a HEAD@{4}: rebase: second commit in the branch_started_further_back
08211a4 HEAD@{5}: rebase: added a file in the branch_started_further_back
3a2169a HEAD@{6}: rebase: checkout new_branch_with_descriptive_name
59d2f69 HEAD@{7}: rebase finished: returning to refs/heads/branch_started_further_back
59d2f69 HEAD@{8}: rebase: second commit in the branch_started_further_back
0258c9e HEAD@{9}: rebase: added a file in the branch_started_further_back
In [110]:
git checkout -b before_interactive_rebase HEAD@{3}
Switched to a new branch 'before_interactive_rebase'
In [111]:
git log --graph --decorate --all --oneline
* 4ec5822 (branch_started_further_back) all the changes I ever wanted!
| * 157de4a (HEAD -> before_interactive_rebase) second commit in the branch_started_further_back
| * 08211a4 added a file in the branch_started_further_back
|/  
* 3a2169a (new_branch_with_descriptive_name) hurray, new stuff.
* b84d28d (new_bare/master) new line in cloned repo
| * 00cf774 (master) new stuff from the feature branch that I did stuff with
|/  
* 18a3a33 new commit in the original repo
* 59d540c checked out myfile from first commit
* 344a292 (branch_well_move_back) I added a second line
* 7e4d620 added a first line to my file

Rebasing requires force-push!!

Exercise

Remember that first commit in the way_back_machine branch? It's not in the graph any more, is it? Get it back with reflog!

The Real GitHub Workflow

(to make the project maintainer love you)

  • Fork and clone the repository (you did that already)
  • Create a feature branch that contains your changes.
  • Work on your changes.
  • Push to your repository.
  • Create a pull request.

Do it!

While you where away

  • Oh no! While you were working, someone added a conflicting change - your pull request can't be merged!
  • You need to resolve the conflict!
  • Syncing with the original (aka upstream) repo: add git@github.com:amueller/repo_to_fork.git as a remote to your cloned repo check out local master. Pull upstream master. This is why you don't want to change master. Ever!
  • Rebase your branch on top of master. Oh no, the conflict!
  • Resolve the conflict.
  • Look at your log. Damn lies! (but pretty)
  • Push to your remote. (You have to force it)
  • Your PR can now be merged!

The end(?)