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/master
0981e8c8ffbd3a1277dda1173fb6f5cbf4750d51
# .git/objects/09/81e8c8ffbd3a1277dda1173fb6f5cbf4750d51
Contain tree
(filesystem), parent
commits and commit metadata
% git cat-file -p 0981e8c8ffbd3a1277dda1173fb6f5cbf4750d51
tree 4fd7894316b4659ef3f53426166697858d51a291
parent e324971ecf1e0f626d4ba8b0adfc22465091c100
parent d33700dde6d38b051ba240ee97d685afdaf07515
author Ted Naleid <contact@naleid.com> 1328567163 -0800
committer Ted Naleid <contact@naleid.com> 1328567163 -0800
merge 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/HEAD
ref: 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 reflog
d72efc4 HEAD@{0}: commit: adding bar.txt
6435f38 HEAD@{1}: commit (initial): adding foo.txt
% git commit -m "adding baz.txt"
% git reflog
b5416cb HEAD@{0}: commit: adding baz.txt
d72efc4 HEAD@{1}: commit: adding bar.txt
6435f38 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_branch
347f5fe my_branch@{0}: merge master: Merge made by the recurs…
4e6007e my_branch@{1}: merge origin/my_branch: Fast-forward
32834d8 my_branch@{2}: commit (amend): upgrade redis version
2720e40 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_B
A---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=relative
la = !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 --abort
If 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_F
E---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_C
working 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 master
master
↓
A---B---C---D---E
\ \ \
F---G---H---I---J ← feature & HEAD
and then we can reset
into a single commit
% git reset --soft master
A---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 HEAD
just 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 fetch
origin/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 pull
origin/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 --rebase
This 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 feature
E---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