Want to learn how to properly use git rebase -i
(also known as an interactive rebase) without putting your production
repo at risk? You're in the right place! The script included in this repo will create
a git repo (with 2 cloned copies) that has synthetic checkins and branches. It makes
for an ideal testing ground to experiment with git rebase -i
without going insane.
- Clone this repo:
git clone [email protected]:dmuth/git-rebase-i-playground.git
cd git-rebase-i-playground
./init.sh
- Stand up a set of Git repos with some nested branches./init-with-feature-branches.sh
- Stand up a set of Git repos with multiple feature branches./git-config.sh
- Run this to set the following aliases:
st
,ci
,co
, andlg
. These will make for less typing and easier viewing of logs.
- Run this to set the following aliases:
This will create the following repo and directories, each with about a dozen commits:
dev-1alice
- A clone of the repo made by "Alice", with two branches:branch1
andbranch2
.branch1
is a branch from main whilebranch2
is based onbranch1
. That is by design (and is based on a True Story, heh).dev-2bob
- A clone of the repo made by Alice's co-worker "Bob", only containingmain
.repo.git
- A "bare" clone of the repo. Note that if youcd
to this directory, commands likegit log --pretty=oneline
will work just fine. That is useful for debugging.
NOTE: Running init.sh
will remove those directories if they already exist. This is so that you can have a "clean slate" every time you run the script.
The tree looks like this:
Running git rebase -i commit_id
will bring up recent commits in the system editor and allow
you to reorder the commits, edit the commit message, or even delete commits.
Why would you want to delete a commit? Probably the most common reason is that you made many commits to a feature branch that you would like to squash into a single commit. Or perhaps you made a feature branch based on another branch, and want to deploy the changes from that feature branch but not the branch it's based on. (Happened to me once!)
Anytime you rebase, you are rewriting history. If you changes have already been pushed, you will need to push them again, and history will be overwritten. If other users have already checked out the branch in question, this will cause a merge on their end, which will (eventually) wind up on the server's repo, and in your repo. It will make the commit history just slightly uglier.
Once that you have the repos set up, here are some sample exercises to try (answers below) in dev1-alice
:
- Switch the order of commits
04-fourth
and05-fifth
- Merge the changes of
branch2
intomain
but NOT the changes ofbranch1
- Squash the two commits in
branch1
then push toorigin
- Switch the order of commits
04-fourth and 05-fifth
, push toorigin
- Switch the order of commits
01-first-will-conflict
and02-second-will-conflict
, THEN push toorigin
- Run
git rebase -i
and delete commit04-fourth
. Then recover it. - Switch the order of commits
01-first-will-conflict
and02-second-will-conflict
Here are some hints to lead you in the right direction but without fully giving away the solution
- Switch the order of commits
04-fourth
and05-fifth
- Make sure you are going far enough back in the revision history...
- Merge the changes of
branch2
intomain
but NOT the changes ofbranch1
- You'll need to remove some commits...
- Squash the two commits in
branch1
then push toorigin
- You'll need to overwrite what's already there...
- Switch the order of commits
04-fourth and 05-fifth
, push toorigin
- There's more than one way to do this
- Switch the order of commits
01-first-will-conflict
and02-second-will-conflict
, THEN push toorigin
- You'll need to handle a merge conflict AND overwrite history in the origin...
- Run
git rebase -i
and delete commit04-fourth
. Then recover it.- The commit wasn't technically deleted...
- Switch the order of commits
01-first-will-conflict
and02-second-will-conflict
- You're going to have to resolve that merge conflict...
If things go wrong, here are some suggestions:
- First, the nuclear option:
./init.sh
- This will reset your Git repo to the initial state. Useful for if you've gone down a hole and you just want to start over. - Running
git status
at any time will not harm you, and will provide you with some useful info and what you need to do next. git rebase --abort
will back out of the current rebase.git log --pretty=oneline --graph
- Will print a listing of commits in the current branch.git log --pretty=oneline --graph --all
- Will print a listing of commits across all branches, and show which branches are based on what commits.- The
tig
tool available at https://github.com/jonas/tig is super useful for browsing the commit history of a branch
A: Alice and Bob are used as placeholder names in cryptology, science, and engineering literature: https://en.wikipedia.org/wiki/Alice_and_Bob I find using the names to be useful because then I don't have to focus on the underlying details quite as much.
- https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase
- https://thoughtbot.com/blog/git-interactive-rebase-squash-amend-rewriting-history
- https://git-scm.com/docs/git-rebase
- https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History
- https://www.dmuth.org/howto-safely-use-git-rebase-i/ - My blog post on this topic
- http://gitready.com/advanced/2009/01/17/restoring-lost-commits.html
- http://effectif.com/git/recovering-lost-git-commits
- http://gitready.com/intermediate/2009/02/09/reflog-your-safety-net.html
- I want to make some simple/slides graphs that show the state of the Git tree for some of the exercises
My email is [email protected]. I am also @dmuth on Twitter and Facebook! Additional ways to find me are on my website.