As much as I love the concept of cheap branching and revision control, I’m a bit of a “code first, ask questions later” kind of guy. Most of my best ideas only come to me once I’m elbow-deep in code and they tend to arrive in bits small enough to make planning my branching and committing impossible.
For this reason, I get a lot of use out of git commands which help me to code first and then sort it out later. Here are my favorites:
IMPORTANT: None of these should be used on commits which have already been shared. Clean up your repository, then “git push”. Eagerness to push is one bad habit you’ll just have to break the hard way.
- Temporarily get rid of uncommitted changes
-
git stash [do your thing] git stash pop
- The manpage gives some examples for where this can be helpful. The most obvious is “boss interrupts you demanding a bugfix immediately”.
- Use a GUI to stage and commit only certain lines
-
git gui
I use this to untangle unrelated changes into clean commits after the fact. The “git citool” alias makes it automatically close when you commit.
- Add the staged changes to the last commit you made instead of a new one
-
git commit --amend
…or just use “git gui”.
- Merge “oops” commits within the last 12 into what they should have been part of
-
git rebase -i HEAD~12
…and then move each “oops” commit below its parent and change “pick” to “fixup”.
Interactive rebase is a very powerful function which can also be used to apply arbitrary changes to any commit you haven’t yet shared. For details, check out the “reword”, “edit”, and “exec” commands in the instructions it displays.
Note: Rebase can sometimes get confused, but if you ever find yourself confused by its requests for clarification and just want to change your mind, simply use
git rebase --abort
- Move the last 8 commits to a new branch
-
git branch my_new_branch git reset HEAD~8 git checkout my_new_branch
Use this when you you find yourself thinking “Dammit! This should be a feature branch.” It works by telling git to create a new branch with its HEAD here and then to move the current branch’s HEAD 8 commits into the past.
- Move the last 8 commits to an existing branch
-
git stash git checkout -b tmp git rebase --onto right_branch HEAD~8 git checkout right_branch git merge tmp git branch -d tmp git checkout wrong_branch git reset --hard HEAD~8 git checkout right_branch git stash pop
This complex string of commands is for when you make some commits, then realize you’re on the wrong branch. I wish I could offer something simpler, but moving a string of commits to a branch that could’ve diverged quite far is a complex task.
Basically, you stash away your uncommitted changes, use a temporary branch to rebase and merge the changes into the new branch, and then remove them from the old branch, and apply the uncommitted changes to the new branch.
- Undo a merge and rebase instead after accidentally running “git pull” instead of “git fetch”
- This is useful when you want to keep a nice, linear development history, you’ve developed something, you try to push it, it fails, and you respond with
git pull
rather thangit fetch
. (I tend to forget to pull before developing when switching machines) - The trick is to use git’s reflog feature (git’s Undo history). If your branch is named
mybranch
and the remote isorigin/mybranch
and `git pull` is the last thing that made changes, you can undo it like this:git stash git reset --merge mybranch@{1} git rebase origin/mybranch git stash pop
(You can omit the
git stash
lines if you don’t have any uncommitted changes)This feature isn’t limited to undoing merges either. Use this command to see what else you can feed to
git reset
:git reflog show
- And so on…
- Got a type of “oops” I missed? Tell me so I can update this post.
UPDATE: Justin Hileman has an excellent flowchart which you’ll probably also want to check out.
Git Tricks for Clean Histories by Stephan Sokolow is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
By submitting a comment here you grant this site a perpetual license to reproduce your words and name/web site in attribution under the same terms as the associated post.
All comments are moderated. If your comment is generic enough to apply to any post, it will be assumed to be spam. Borderline comments will have their URL field erased before being approved.