by @tednaleid
rm -rf .git will lose anything not yet pushed outpoint at 0..N parent commits
E---F---G/ \A---B---C---D---H---I
most commonly 1 or 2 parent commits
fixed commit pointers
A---B---C↑release_1.0
% git commit -m "adding stuff to C"A---B---C---D↑release_1.0
floating commit pointer
A---B---C↑master
% git commit -m "adding stuff to B"A---B---C---D↑master
a “remote” branch is just a commit pointer in your local repo
master↓A---B---C---D---E↑origin/master
it's updated whenever you do a fetch or pull, otherwise nothing remote about them
text files in the .git directory
% ls -1 .git/refs/heads/**/*.git/refs/heads/master.git/refs/heads/my_feature_branch
% ls -1 .git/refs/remotes/**/*.git/refs/remotes/origin/HEAD.git/refs/remotes/origin/master.git/refs/remotes/origin/my_feature_branch
contains is the SHA of the commit it's pointing at
% cat .git/refs/heads/master0981e8c8ffbd3a1277dda1173fb6f5cbf4750d51# .git/objects/09/81e8c8ffbd3a1277dda1173fb6f5cbf4750d51
Contain tree (filesystem), parent commits and commit metadata
% git cat-file -p 0981e8c8ffbd3a1277dda1173fb6f5cbf4750d51tree 4fd7894316b4659ef3f53426166697858d51a291parent e324971ecf1e0f626d4ba8b0adfc22465091c100parent d33700dde6d38b051ba240ee97d685afdaf07515author Ted Naleid <contact@naleid.com> 1328567163 -0800committer Ted Naleid <contact@naleid.com> 1328567163 -0800merge commit of two branches
The ID is the SHA of the commit's contents
commits don't “belong to” branches, there's nothing in the commit metadata about branches
a branch's commits are implied by the ancestry of the commit the branch points at
feature↓E---F---G/A---B---C---D↑master
master is A-B-C-D and feature is A-B-E-F-G
HEAD is the current branch/commit
This will be the parent of the next commit
% cat .git/HEADref: refs/heads/master
most of the time it points to a branch, but can point directly to a SHA when “detached”
a log of recent HEAD movement
% git reflogd72efc4 HEAD@{0}: commit: adding bar.txt6435f38 HEAD@{1}: commit (initial): adding foo.txt
% git commit -m "adding baz.txt"% git reflogb5416cb HEAD@{0}: commit: adding baz.txtd72efc4 HEAD@{1}: commit: adding bar.txt6435f38 HEAD@{2}: commit (initial): adding foo.txt
by default it keeps at least 30 days of history
unique to a repository instance
can be scoped to a particular branch
% git reflog my_branch347f5fe my_branch@{0}: merge master: Merge made by the recurs…4e6007e my_branch@{1}: merge origin/my_branch: Fast-forward32834d8 my_branch@{2}: commit (amend): upgrade redis version2720e40 my_branch@{3}: commit: upgrade redis version
if the only thing pointing to a commit is the reflog, it's “dangling”
A---B---C---D---E---F↑master
% git reset --hard SHA_OF_BA---B---C---D---E---F↑master
C..F are now dangling
but they will be safe for ~30 days because of the reflog
HEAD@{1}↓A---B---C---D---E---F↑master (also HEAD@{0})
HEAD@{1} will become HEAD@{2}..HEAD@{N} as refs are added to the reflog
once a dangling commit leaves the reflog, it is “loose” and is at risk of garbage collection
git does a gc when the number of “loose” objects hits a threshold
something like every 1000 commits
to prevent garbage collecting a commit, just point something at it
% git tag mytag SHA_OF_DANGLING_COMMIT
a pre-commit staging area
add -A :/ puts all changes in the index ready for commit
some bypass the index with git commit -a -m "msg"
you have weeks to retrieve prior commits if something doesn't work
~/.gitconfig:[alias]l = log --graph --pretty='%Cred%h%Creset -%C(yellow)%d%Creset %s %Cblue[%an]%Creset %Cgreen(%cr)%Creset' --abbrev-commit --date=relativela = !git l --all
git la
just like cd -, takes you to your previous branch
E---F ← feature & HEAD/A---B---C---D↑master
% git checkout -E---F ← feature/A---B---C---D↑master & HEAD
redo the last commit
A---B---C↑master & HEAD
<... change some files ... >% git commit -a --amend --no-edit
C' ← master & HEAD/A---B---C↑(dangling but still in reflog)
reapplies a series of commits to a new parent commit
then moves the current branch pointer
E---F ← feature & HEAD/A---B---C---D↑master
% git rebase master(dangling but still in reflog)↓E---F/A---B---C---D---E'--F'↑ ↑master feature & HEAD
% git rebase --abortIf you get in trouble --abort and try again.
If you really get in trouble, you can reset --hard back to your last commit.
should never be done with commits that have been pushed
public rebasing is bad as others could have the same commits with different SHAs
apply a subset of changes from another branch
E---F---G/A---B---C---D↑master & HEAD
% git cherry-pick SHA_OF_FE---F---G/A---B---C---D---F'↑master & HEAD
reset is for moving branch pointersA---B---C---D---E↑master
% git reset --soft SHA_OF_Cworking dir & index still look like↓A---B---C---D---E↑master
HEAD & the current branch to the specified <SHA>useful for squashing the last few messy commits into one pristine commit
working dir & index still look like↓A---B---C---D---E↑master
% git commit -m "perfect code on the 'first' try"A---B---C---E'↑master
What if you've got a more complicated situation:
master↓A---B---C---D---E\ \F---G---H---I ← feature & HEAD
Can't reset our way out of this, right?
Just do one last merge
% git merge mastermaster↓A---B---C---D---E\ \ \F---G---H---I---J ← feature & HEAD
and then we can reset into a single commit
% git reset --soft masterA---B---C---D---E ← feature & HEAD & master\J ← working dir & index
% git commit -m "pristine J"master↓A---B---C---D---E---J' ← feature & HEAD
% git reset --hard <SHA>HEAD & the current branch to the specified <SHA> <SHA> <SHA> dangerous if you have uncommitted work, useful for undoing bad commits
% git reset --hard HEADjust means clean out the working directory and any staged information, don't move the branch pointer
for more info on reset, see: http://progit.org/2011/07/11/reset.html
download new commits and update the remote branch pointer
does not move any local branches
origin/master(local) ↓A---B---C---D ← master & HEAD
A---B---E---F(origin) ↑master (in remote repo)
% git fetchorigin/master↓E---F(local) /A---B---C---D ← master & HEAD
pull is fetch plus merge
origin/master(local) ↓A---B---C---D ← master & HEAD
A---B---E---F(origin) ↑master (local ref in remote repo)
% git pullorigin/master↓E---F----/ \(local) A---B---C---D---G ← master & HEAD
stash any uncommitted changes (if any)fetch the latest refs and commits from originrebase -p your changes (if any) onto origin's headstash any previously stashed changesfetch + rebase avoids unnecessary commits
As of git 1.8.5, git has finally added a rebase switch to pull:
% git pull --rebaseThis will do the fetch + rebase for you (you still stash on your own).
% git reset [--mixed] <SHA>HEAD & the current branch to the specified <SHA> <SHA> git reset HEAD will unstage everything in the index
compresses N commits into one commit that's appended to a destination branch
E---F---G ← feature/A---B---C---D↑master & HEAD
% git merge --squash featureE---F---G ← feature/A---B---C---D---G'↑master & HEAD
cleans up history, when the thinking behind E..F is unimportant
Oops, I really wanted C!
C' ← master & HEAD/A---B---C ← (dangling)
% git reflog master # find SHA_OF_C% git reset --hard SHA_OF_C
C' ← (dangling)/A---B---C↑master & HEAD