Git-ing out of your git messes

Post on 16-Apr-2017

1,020 views 0 download

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