git-ing out of your git messes
TRANSCRIPT
Git-ing out of your Git messes
Katie Sylor-MillerSenior Software Engineer, Etsy
ohshitgit.com
DON’T GIT INTO A MESS IN THE FIRST PLACE
Fundamentalscommits, branches, HEAD & environments
Fundamentals: Commits
Each commit contains a few pieces of information:
● A snapshot of the entire repo
● Who made this change
● When this change was made
● A message describing the commit
● A pointer to the previous commit(This is a bit of an over simplification, for a more detailed explanation, see: http://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html)
What’s in a commit
Each commit contains a few pieces of information:
● A snapshot of the entire repo
● Who made this change
● When this change was made
● A message describing the commit
● A pointer to the previous commit(This is a bit of an over-simplification, for a more detailed explanation, see: http://blog.thoughtram.io/git/2014/11/18/the-anatomy-of-a-git-commit.html)
What’s in a commit
SHA-1Unique40-charHash
Commit hashes
a4df41a8045877d50396d00113598e47f6ad10ef
aec561108fd86412c2a9083d7d95ed1668d2f4e4
6dab6a712177cf2dd2cf8b79a2cee24351be60eb
1a3531222242241153ac8a76b752d5f525a99d2c
912bde5d4e5e962269ddff87da83cc5ce55e75d0
Fun fact: commit hash abbreviations
a4df41a
aec5611
6dab6a7
1a35312
912bde5
Fundamentals: Branches
● Each git repository starts out with a single branch, called master.
● There can be multiple branches of each repo.● Each branch is essentially a copy of the master branch
and all of it’s history
● Branches are cheap and easy (unlike TFS or SVN), so use them as much as you want!
Branches: the connection between commits
Mental model: a linked list of commitsa4df41aaec56116dab6a71a35312 912bde5
Each commit contains a few pieces of information:● A snapshot of the entire repository● Who made this change● When this change was made● A message describing the commit● A pointer to the previous commit
Mental model: a linked list of commitsa4df41aaec56116dab6a71a35312 912bde5
Mental model: a linked list of commitsa4df41aaec56116dab6a71a35312 912bde5
parent child
Mental model: a linked list of commitsa4df41aaec56116dab6a71a35312 912bde5
Branches are a reference to a commit
master
a4df41aaec56116dab6a71a35312 912bde5
git branch new-branch
Create a new branch
Branches are a reference to a commit
master
a4df41aaec56116dab6a71a35312 912bde5
new-branch
Fundamentals: HEAD
HEAD points to currently checked-out branch
master
new-branch
a4df41aaec5611 912bde56dab6a7
HEAD
Check out a branch git checkout new-branch
HEAD points to currently checked-out branch
master
new-branch
a4df41aaec5611 912bde56dab6a7
HEAD
A new commit’s parent is the HEAD
master
new-branch
a4df41aaec5611 912bde5 1668d2f6dab6a7
HEAD
master
new-branch
a4df41aaec5611 912bde5
1668d2f
ca53f4f 6aac7b2
Branches are a linked list treeHEAD
Branches are a linked list tree directed acyclic graph
https://en.wikipedia.org/wiki/Directed_acyclic_graph
Fundamentals: Remote vs. Local
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
Central server where shared git repositories are stored
--
Remotes typically are “bare” - you can’t directly modify them
Your local copy of the remote git repository
--
Contains the entire history and all branches of a remote repo
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
Snapshot of changes to the current branch that you want to commit
--
Is a copy of all of the files in the repo, not just the changed files
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
Where changes to files are made
--
Analogous to the physical directory where files are stored on disk
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
A place to store changes to files that you aren’t ready to commit yet--Aka “shelving” changes for later
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
HistoryViewing and changing history
Viewing history
See the history of a
branch git log
See the history of a
branchgit log
git log
See the history of the HEAD git reflog
See the history of the HEAD
git reflog
git reflog
Moving backwards in history
CheckoutGo back to a specific
point in time
git checkout <commit hash>
Aside: Detached HEAD
Detached HEAD means that HEAD is not a symbolic reference anymore, therefore new commits will not be part of history. Happens when you:
● Checkout a commit that is not the tip of a branch, or
● Checkout a remote tracking branch
Fix it by:
● Checking out a branch, or
● Create a new branch from this state
Fixing messesReset & revert
git reset HEAD@{x}# orgit reset HEAD~x# orgit reset <commit hash>
ResetGo back to a previous
point in time
git reset --soft HEAD~git reset --mixed HEAD~git reset --hard HEAD~
Three types of resetting
soft
Takes you back in history, and
leaves your changes in staging
Takes you back in history, and discards those
changes
hardmixed
(default) takes you back in history, and leaves your
changes in the workspace
git reset --hardPro tipClear out your staging
area and workspace
git revert <commit hash># orgit revert HEAD~X
RevertUndo a public commit
● Pass in the identifier(s) of specific commits
● Git creates a new commit that undoes the work of the specified commit.
● You can revert a commit in the middle of other commits, but if a later commit modifies the same file, you will need to resolve that conflict.
Reverting commits
Revert multiple commits
You can use either HEAD~ references, or commit
hashes
# rangegit revert HEAD~3..HEAD# or list newest->oldestgit revert HEAD~2 HEAD~3 HEAD~4
Revert without
auto-commitIn case you want to
double-check the revert
git revert --no-commit <commit or range or list># leaves changes staged# for manual commit
WorkflowCommitting & branching
A simple workflow
master master
baz.php
foo.php
bar.php
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
master master
baz.php
git add
foo.php
bar.php
baz.php
foo.php
bar.php
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
master HEADmaster
git commit
baz.php
foo.php
bar.php
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
master
master
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
git push
Send changes to
remote
master
git pushmaster
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
master master
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
But… it’s usually not that simple
master
master
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
Avoiding messesStay up-to-date
master
master
origin/master
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
git fetch origin
Update local tracking branch
master
master
git fetch
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
origin/master
git merge origin/masterMerge
master
master
origin/master
New merge commit
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
master
master
origin/master
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
git pull# git fetch && git merge
PullDo a fetch & merge at the
same time
master
master
origin/master
git pull
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
git push
Sync changes to
remote
master
git push
master origin/master
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
mastermaster origin/master
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
Avoiding messesRebase all the things
master
master
origin/master
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
git rebase origin/masterRebase
master origin/master
master
Our commit
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
masterorigin/master
master
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
git pull --rebase# git fetch && git rebase
Pull with Rebase
git config --global alias.rpull ‘pull --rebase’Pro tip
Add rpull as an alias for git pull --rebase
masterorigin/master
master
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
git push
Sync changes to
remote
masterorigin/master
mastergit push
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
master origin/master master
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
Avoiding MessesAlways Be Committing
Why lots of commits are better
● Commits are cheap and easy
● Save progress over time - easier to go back
● Smaller diffs are easier to reason about
● Less chance of committing the wrong thing when you are reviewing small changelists
Problem: Committing the wrong thing
Avoid the problem: Committing the wrong thing
● Set up your command line to show you what branch you are on (https://github.com/jimeh/git-aware-prompt)
● Be careful about what files you edit - use .gitignore to your advantage
● git status is your BFF
● Understand how staging works (subsequent changes to a file are not reflected)
Fix the problem
Entering the wrong commit message
# make sure nothing is in staginggit commit -amend# follow prompts to change # the commit message
Fix the problem
Forgetting to commit a file
git add filenamegit commit -amend# follow prompts to change # the commit message
Fix the problem
Committing the wrong file
# undo your last commit# but leave the changes in staginggit reset HEAD~# unstage the filegit reset HEAD filename# re-do commitgit commit -m “Commit message”
Fix the problem
Committing to the wrong branch (version one)
# undo the last commit, but leave the changes availablegit reset HEAD~ --softgit stash# move to the correct # branchgit checkout name-of-the-correct-branchgit stash popgit add . # or add individual filesgit commit -m "your message here"# now your changes are on the correct branch
Fix the problem
Committing to the wrong branch (version two)
git checkout name-of-the-correct-branch# grab the last commit to mastergit cherry-pick master# delete it from mastergit checkout mastergit reset HEAD~ --hard
Avoiding messesAlways Be Branching
Why a feature branch workflow is better
● Create a new branch of master, do your work on that branch. When you are ready, merge your changes back into master
● Safety net: you aren’t changing master directly until your feature is ready for prime time
● Allows you to switch between tasks/manage unrelated changes
● Preserve the history of larger features or long-term work and share with a team
Problem: Staying up to date
MERGE REBASE
● Adds a new commit to your feature branch.
● You are resolving potential conflicts created by other people’s code.
● Replays your commits on top of the latest of master.
● You are resolving potential conflicts created by your own code.
Avoid the problem: rebase vs. merge
Rebasing gotchas & caveats
● Each commit is applied as a separate patch, so you might need to resolve conflicts for each commit :( :( :(
● Don’t change the public history of a branch or you’re gonna have a bad time.
● Some people like to keep commit history of branches. Discuss with your team which is preferred!
git rebase -i HEAD~x
Avoid the problem
Combine commits so you have fewer conflicts to deal with when rebasing
master
feature-branch
origin/master
git rebase-i HEAD~2
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
DON’T PANIC
master
feature-branch
origin/master
Remote/Origin Local Staging/Index Workspace Stash
Your machinegithub
git checkout master && git merge --squash feature-branch
Merging back to master
*controversial opinion*
Remote (Origin) Local Staging/Index Workspace Stash
master
Your VMgithub.etsycorp.com
Etsyweb
Etsyweb
feature-branch
master
● Combines all of the commits in your feature branch into a single changeset
● Leaves you in a state where the changes are not committed, you need to make the final commit
● Lose historical connection to the feature branch
Squash merging
Avoiding messesResolving conflicts
● Communication with teammates
● Update your local master and origin/master all.the.time.
● Periodically squash commits to reduce the number of commit conflicts to resolve (esp. When rebasing)
Avoid conflicts in the first place
DO DON’T
● Merge feature-branch into master
● Rebase feature-branch against master
● Merge master into feature-branch
● Rebase master against feature-branch
Avoid conflicts: merge & rebase in the right direction
The problem: conflict markers
Fix the problem
Check for remaining conflict markers before
committing
git diff --check
Pro tipUse git mergetool command to open a GUI to help
resolve conflicts. HIGHLY RECOMMEND any Jetbrains IDE, also meld looks good
git config merge.tool <toolname>
If all else fails
Abort! abort!
git merge --abort# orgit rebase --abort
That’s it! Easy, right?
Recap: Avoiding messes
● Understand the fundamentals (commits, branches, history, environments, workflow)
● Use tools to help you work smarter (cmd line formatting mergetool, etc.)
● Always be committing & branching
● Rebase & merge in the right direction
● DON’T PANIC! Everything is fixable (one way or another)
● Visual git cheatsheethttp://ndpsoftware.com/git-cheatsheet.html
● Oh shit, git! http://ohshitgit.com
● Atlassian git tutorial (esp. the advanced tutorials)https://www.atlassian.com/git/
● Git for Humans bookhttps://abookapart.com/products/git-for-humans
Useful links
Thank you!@ksylor && @ohshitgit