1. USING GIT LIKE A PRO
JESÚS MIGUEL BENITO CALZADA
EDD - 27/10/2018
2. About me
git config --global user.name “Jesús Miguel Benito Calzada”
git config --global user.email “beni0888@hotmail.com”
@beni0888
https://www.linkedin.com/in/jesusmiguelbenito/
3. Goals
Increase our understanding of Git internals
Get the knowledge to solve cumbersome situations
Be able to produce a better (cleaner, coherent) Git history
Have fun!
4. Git objects
The Git object database store four different
object types:
Blob
Tree
Commit
Tag
5. References
A reference is a “pointer” to a commit.
It holds the SHA-1 hash of a commit.
Allows us to refer to commits in a human-friendly way.
Stored in files under .git/refs directory.
7. What is a git branch?
It is just a reference to a commit!
Stored in files under .git/refs/heads (.git/refs/remotes)
Tags are also references under .git/refs/tags
8. References – HEAD
HEAD is a special reference that
points to the commit you have
currently checked out.
Stored in .git/HEAD file.
It usually points to another
reference instead of a commit!
When it points to a commit, we said it
is in a detached state.
9.
10. Tip
You can just do as follows:
After creating a local branch, we need to provide its upstream
before pushing.
> git checkout -b my-branch-with-a-long-name
> git push
fatal: The current branch my-branch-with-a-long-name has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin my-branch-with-a-long-name
> git push origin HEAD
13. Merge
$> git merge master feature
The most common and easy way of synchronization.
Produces ugly merge commits.
Keeps the context of the synchronization.
Branch history can be difficult to follow.
16. Rebase
Rewrites all feature branch’s commits on top of master branch.
Much cleaner project history:
Avoid ugly merge commits.
Perfectly linear project history.
Some drawbacks:
It rewrites the history! Generate new different commits, with different
SHA-1.
Loses the context of synchronization provided by a merge commit.
Golden rule: never use it on public branches!!
Merging vs Rebasing
17. Lab
Clone repo: https://github.com/beni0888/gitlikeapro
Execute a rebase:
Checkout rebase-no-conflicts
Rebase it onto rebase-base
Tip: git log rebase-base…rebase-no-conflicts (show divergent
commits)
Execute a rebase with conflicts:
Checkout rebase-with-conflicts
Rebase it onto rebase-base
Resolve conflicts and finish rebase
18. Interactive rebase
git checkout feature
git rebase (--interactive | -i) <commit-ref>
Allows to edit the history from a commit on.
It opens an editor showing the list of commits that are going to
be replayed, and allows to perform certain actions on them:
Join commits: squash, fixup
Edit commit message: reword
Edit commit content: edit
Remove commit: drop
Reorder commits: just change commit’s order in the editor
19.
20.
21. Rebase Tip
Returns the SHA-1 of the base commit of feature branch.
Useful to interactively rebase the whole branch.
git merge-base master feature
git rebase -i `git merge-base master feature`
22. Lab
Execute a interactive rebase:
Checkout rebase-base branch.
You can get its origin commit with git merge-base master head
Do some experimentation: join commits, edit, reorder...
Be creative!
Finish the rebase operation and check the results.
23. Rerere
Stands for Reuse Recorded Resolution.
It is kind of machine learning mechanism.
Learns from your previous decisions on conflicts resolution.
Apply them automatically reducing the number of manual
interventions.
Warning! If you do it wrong, it will learn also…
git config --global rerere.enabled 1
24. Lab
Run git config rerere.enabled 1
Checkout rerere-with-conflicts.
Run a rebase onto rerere-base.
Solve conflicts and finish the rebase.
Undo rebase: git reset --hard ORIG_HEAD
Run the rebase again and see what happens
26. Git reset to the rescue!
Versatile and powerful command for undoing changes.
Similar to checkout in some aspects.
Potentially dangerous: destructive operation.
It allows to:
Undo changes.
Unstage files.
git reset
28. Reset main modes
Soft:
Keep undone changes on the index.
Mixed:
Default mode, keep undone changes in the working tree, but
without staging them (out of the index).
Hard:
Totally removes the undone changes, they are not keep neither in
the index, nor in the working tree.
Use it with care!!
Great explanation in stackoverflow
29. Reset vs Revert
Revert is a safe operation, it takes a commit and generates
a new commit which inverses the specified commit.
Revert add a new commit to the history, but does not modify
the existent ones.
Reset rewrites the history to make the branch match the
state of an specific commit.
Reset vs Revert vs Checkout
git revert <commit-ref>
31. What really happens when rebasing?
New commits are generated at the tip of the target branch.
But the original old commits are still available in the repo.
Although they are unreachable, there is no reference pointing
to them.
32. Reflog
Reference logs, or "reflogs", record when the tips of branches
and other references were updated in the local repository.
Remember: a reference is a pointer to a commit.
Tracks refs updates up to 90 days by default.
It allows us to recover from errors by accessing to
“unreachable commits”.
Subcommands: show, expire and delete.
33. Reflog subcommands
Show:
Show the log for a reference, or all reference (--all option).
Expire:
Prunes older reflog entries, rarely used by end users.
Delete:
Removes single entries from the log, typically not used by end users
neither.
34. Reflog - show
Shows the reflog of the HEAD ref.
git reflog
git reflog show HEAD
f90e666 (HEAD -> master) HEAD@{0}: checkout: moving from branch to master
d92a3b7 (branch) HEAD@{1}: rebase -i (finish): returning to refs/heads/branch
d92a3b7 (branch) HEAD@{2}: rebase -i (start): checkout 19e164a71d87ffa0902c71e92da9f0b61bcc858f
d92a3b7 (branch) HEAD@{3}: rebase -i (finish): returning to refs/heads/branch
d92a3b7 (branch) HEAD@{4}: rebase -i (pick): BRANCH - Add e
da961c4 HEAD@{5}: rebase -i (pick): BRANCH - Add d
5eeef42 HEAD@{6}: rebase -i (fixup): BRANCH - Add c
84d25d0 HEAD@{7}: rebase -i (start): checkout 19e164a71d87ffa0902c71e92da9f0b61bcc858f
35. Reflog - show
Shows the reflogs for all references
git reflog show --all
git reflog stash
Reflog also tracks the stash references.
36. Undoing a rebase: reflog + reset
1. Look in the reflog for the commit at the tip of the branch
at the moment prior to the rebase.
2. Git reset --hard to that commit
> git reflog
1544114 (HEAD -> feature) HEAD@{0}: rebase finished: returning to refs/heads/feature
1544114 (HEAD -> feature) HEAD@{1}: rebase: Add bar
3b021d4 HEAD@{2}: rebase: Add foo
d92a3b7 (master) HEAD@{3}: rebase: checkout master
bf12c54 HEAD@{4}: commit: Add bar
e99a22b HEAD@{5}: commit: Add foo
…
> git reset --hard HEAD@{4}
HEAD before rebase
37.
38. Undoing changes: ORIG_HEAD
ORIG_HEAD is a special reference that stores the previous state
of HEAD. It is set by commands that have possibly dangerous
behaviour (rebase, merge, reset, etc), to be able to easily
revert them.
Undoing a rebase, reset, merge:
git reset --hard ORIG_HEAD
40. Bisect
Sometimes you discover that a bug has been introduced in
your code base, but you do not know when it happened.
The bisect command does a binary search through your
commit history to help you identify as quickly as possible
which commit introduced an issue.
It allows to identify the commit that introduced the issue,
through a large range of commits, in just a few steps.
41. Bisect
First, you have to indicate git to start bisecting your history.
Then you indicate that the current commit is “bad”, or provide
a reference to a previous commit you know is bad.
And you provide the commit with the last good state known.
Or just in one command:
> git bisect start
> git bisect bad [<commit-ref>]
> git bisect good v1.2.6-rc
> git bisect start HEAD v1.2.6-rc
42. Bisect
After that, git will check out the commit in the middle of
the provided range, so you can test it.
Git will figure out the number of revisions left to test
before finding the target commit.
Bisecting: 2 revisions left to test after this (roughly 1 step)
[da961c4e9813bbc92c308e4487ef527aa25f3bc7] Foo commit message
43. Bisect
After testing the commit, you should inform git whether it is
”good” or “bad” with git bisect (good|bad) commands.
Git will narrow down the range of remaining revisions, and
will repeat the same process until the commit which
introduced the breakage is found.
You can skip testing a commit with bisect skip subcommand.
d92a3b789d3e5e7fcee880b106bb6d0d9694f58b is the first bad commit
commit d92a3b789d3e5e7fcee880b106bb6d0d9694f58b
Author: Jesús Miguel Benito Calzada beni0888@hotmail.com
Date: Thu Oct 4 18:49:17 2018 +0200
Added a really cool feature!
44. Bisect
You should finish the process with bisect reset command,
to cleanup the bisection state and return to the original
HEAD.
> git bisect reset
Previous HEAD position was d92a3b7... Foo commit message
Switched to branch 'feature'
45. Lab
Checkout bisect branch
File.txt contains several lines with the text “EDD rocks!”, each
one added by a different commit, but one line contains a typo:
“EED” instead of ”EDD”.
Make use of bisect to identify the commit which introduced the
line with a typo.
Tip: use the commad grep EED file.txt to check if the commit
is wrong.
46. Bisect: let’s automate!
This is a really manual error-prone process, right?
Bisect provides a run subcommand that allows us to run a
script to determine whether the commit is good or bad.
The script must exit with 0 if the commit is good, or
whatever code between 1 and 127 (except 125, used to
skip) for a bad commit.
You can for example run your test suite to check your
commits.
> git bisect start HEAD v2.1.0-rc
> git bisect run vendor/bin/phpunit
> git bisect reset
47. Lab
Checkout bisect-run branch.
Prerequisites:
You need to have PHP (version >= 5) installed on your computer.
You need to have composer installed globally, or download its phar from
https://getcomposer.org/download/.
Run composer install (or ./comporser.phar install).
The current branch’s test suite has a failing test, and you have to
find the commit that introduce it.
Make use of bisect run to find the commit which introduced the
error:
Tip: use vendor/bin/phpunit as script for bisect run.
48.
49. Oh-my-zsh git plugin
Oh-my-zsh comes with a really handy git plugin that saves
us to write a lot: https://github.com/robbyrussell/oh-my-
zsh/wiki/Plugin:git
g=git
ga='git add’
gaa='git add --all’
gap='git apply’
gapa='git add --patch’
gau='git add --update’
gb='git branch’
gba='git branch -a’
gbd='git branch -d’
gbda='git branch --no-color --merged | command grep -vE
"^(*|s*(master|develop|dev)s*$)" | command xargs -n 1 git branch -d’
gbl='git blame -b -w’
…
You can ask the students to:
Squash all “add method” commits
Remove “intentionally add a failing commit”
Reorder commits: move “add instructions to readme” to the tip of the branch
The main difference between reset and checkout is that checkout only operates on the HEAD ref, it doesn’t change the branch’s tip, while reset operates on both HEAD and branch references.