Git Immersion http://gitimmersion.com
git init
creates repo
git add
stages files
git commit
commits staged files to the repo.
Why two steps staging and committing? The separation of staging and committing allows me to control the scope of each commit. I can stage a group of files that belongs together, one after the other, then commit that group.
git commit -m MESSAGE
includes message. Without -m: message added via editor.
git status
reports:
- staged changes that have not been committed yet
- modifications that have not been staged yet
Note: The same file may be reported twice as not staged and not committed.
git log
lists commits. Very fine-grained filtering and formatting:
git log --pretty=oneline
git log --since="5 min ago"
git log --since=yesterday
git log --pretty=format:FORMAT_STRING
, where %h hash, %d decorations e.g. branch heads or tags, %a author date, %an author name, %s comment
Aliases in $HOME/.gitconfig, e.g.
[alias]
br = branch
hist = log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
git checkout HASH
resets all files to the status of the commit identified by HASH. Creates "detached HEAD" state, i.e. I am not on any branch.
git checkout BRANCH
e.g. git checkout master. Leaves detached HEAD state and goes back to the lastest version in this branch.
Commit references commits can be referred to by absolute hash, absolute tag, relative to a hash or tag. ref^
means parent state of ref, ref~3
means great-grandparent state of ref etc.
git tag name
adds a human-readable name to current version
git tag
lists all current tags. Tags also show up in the decorations (%d) of logs
git tag -d TAGNAME
deletes a tag
git reset HEAD
esets the staging area to be whatever is in HEAD. This clears the staging area of uncommitted changes. It does not reset changes in the working directory, i.e. files.
git reset REF
sets the pointer to the reference in the branch. Optionally adjusts staging area and file contents in the working directory.
git reset REF --hard
also "forgets" about all the commits more recent than REF. However, tagged commits and their ancestors are still in the repo and can be seen with git log --all
.
git revert HEAD
adds a "revert commit", which puts the files back to the state before the lastest commit. Rather than HEAD
, I can revert to any other commit, e.g. HEAD~2
. However, this may lead to merge conflicts .
- Go to an earlier commit
- Go back to the lastest state of the main branch
- List commits
- Undo unwanted changes before committing in case the changes are not staged yet:
git status # check if staged or not
git checkout FILE1 FILE2 ... # undo changes in file(s)
git status # should be back on master
- Undo unwanted changes before committing in case changes are staged but not committed:
git status # check if staged but not committed
git reset HEAD FILE1 FILE2 ... # resets staging area, but not the files
git status # shows that there are unstaged changes
git checkout FILE1 FILE2 ...
git status
- Undo commits requires another commit that undoes the unwanted commit (makes sense?). I.e. I don't throw the unwanted commit away, but I add another commit that puts my files in the earlier state.
git revert HEAD # adds a commit that goes back to earlier state
git log --max-count=3 --pretty=oneline
fff4e0d34f35ff4143b9f1b920476c93fcebd7f2 Revert "committed by mistake"
933f0028fe331514c4d9cfc625ddef447f36cf93 committed by mistake
cd8be0cee8d0a6d313d439c9b3f6117c42b8a938 comment added
- REALLY undo commits, i.e. remove the commit from the chain. The commits are still in the repo, but the only way to reference them is by their hash. The secret lies in the
--hard
option.
git reset REF --hard
git log # everything more recent than REF is gone
- REALLY undo commits with an emergency exit. When a commit is tagged, even a hard reset still keeps it and its successors in the log. Removing the tag will then remove the commit from the log.
git tag unwanted_commit
git reset earlier_commit --hard
git log # everything younger than earlier_commit is gone
git log --all # but not quite
git tag -d unwanted_commit
git log --all # after deleting the tag, it's really gone