Getting up speed with Git

Rewriting history
Before pushing commits to a remote location, it's recommended to get the history back into shape. This way, other developers deal with a clean history.
Each commit should preferably, describe just one feature or fix.
- Just to let you know...
Commands which rewrite history, should be used with private commits only. By default, it's not possible to push a rewrite of the shared history.
Fixing the last commit
When a file was forgotten, or a line had to be removed, it's simple to fix. Just stage those changes, and run:
- Fixing the last commit:
git commit --amend
The staged changes will be used to correct the last commit.
Removing a commit
Removing the last commit happens with the reset command:
- Naming reasons
- This is called the "reset" command because it resets the location of HEAD, and optionally the staging area or working tree.
- Undo a commit:
git reset HEAD^
# short for git reset --mixed HEAD^
The changes of the commit are loaded back in the staging area. There are two other options as well:
- Undo a commit:
git reset --soft HEAD^
# remove a commit, keep staged and working changes
git reset --hard HEAD^# remove a commit, delete working copy changes and staged changes
Redoing history
With git rebase -i, Git allows you to redo parts of your commit history. First select the starting point or commit, after which Git should start.
- Starting the interactive rebase:
git rebase -i commit git rebase -i HEAD~4
# rebase the last 4 commits
This can be any commit beyond the origin/... branch, or simply:
- Rebasing unpushed commits:
git rebase -i origin/master
# start after the last shared commit
A text editor will open, listing all commits. Edit the file to tell Git what should happen:
- Editing a commit -> replace the word pick with edit.
- Merging two commits -> replace the word pick with squash.
- Reorder some commits -> reorder the lines.
- Remove a commit -> delete the line.
After you save & quit the text editor, Git will start the rebase.
The rebase proces
The rebase starts by rewinding history back to the old state. Next, the old history is replayed, by applying the commits one by one.
When git needs assistence, the rebase pauses. After committing the fixes, the rebase can continue:
- Resuming a rebase:
git rebase --continue
Aborting
In case things went wrong, the rebase can be aborted:
- Aborting a rebase:
git rebase --abort
# abort the rebase, back to the original situation
git rebase --skip# skip the commit if it causes trouble (deletes it)
Splitting commits
With the edit modifier, git rebase allows you to redo history. Any extra commit you make, will be inserted in the history.
So again, start git rebase -i commit and replace the word pick with edit for the commit that needs to be split. Save & quit the file so the rebase process starts.
The rebase command pauses precisely after the moment in time when the commit was made. To split it, the following is needed:
- Splitting a commit:
git reset HEAD^
# Undo the commit, load all changes in the working tree
git add file1 git commit# commit the first part
git add file2 git commit# commit the second part
git rebase --continue
Amend an older commit
To amend an older commit, the following procedure works nicely:
- First, commit the new change.
- Run git rebase -i HEAD^^^
- Reorder the last two commits, and replace pick with fixup or squash so the second commit is now merged with the first.
After closing the file, git will process to fix the history.
Undoing everything
- Git internals: the revlog
-
The revlog tracks the previous value of HEAD for about 30 days.
Commits which are no longer referenced by any object, get cleaned up automatically.
In case you totally screwed up, things can be fixed with the revlog
- Fixing a total screw up:
git revlog
# find the past version of HEAD
git reset --hard HEAD@{nr}# return to that commit
Last but not least
One extreme form of rewriting is the filter-branch tool. It can be useful for a few situations:
- Remove all commits by an author
- Updating all commit messages
- Removing a file from every commit
- Changing e-mail addresses globally
- Making a subdirectory the new root
This is a typical command you'll likely never need, but in case you really have to, it's nice to know these things are possible.
Anything next?
This is the last chapter of the tutorial. There are still some things to talk about, e.g. svn, submodule and bisect commands. There are some tricks and links as appendices, for further reading.
Enjoy!
If you have any commits, feel free to send me your feedback.
blog comments powered by Disqus