Rebase Workflow Workshop
Learning Goals
By the end of this lesson, you will:
- Have a better understanding of the differences between merge and rebase workflows
- Be able to speak on the ups and downs of using one git workflow over the other
- Practice one way of following a rebase workflow and experiment with interactive rebasing
Prework
Hopefully you’ve had some time to process the Rebase workflow in the prework, but you might still have some questions on the flow and the differences between a Rebase and Merge workflow. Before the lesson, please take 30 minutes to watch this video led by one of our former instructors, Ian Douglas. He diagrams out the differences between the two focusing on the differences with the commit timeline between Rebase and Merge and offers some thoughts on when you might use one workflow over the other. The second half of the video is a bit of a Q&A with past students and covers a couple different ways you can achieve a Rebase workflow. Later in this lesson, you will get the opportunity to practice this.
Checks For Understanding
- Draw out your own diagram comparing the differences between a
merge
&rebase
workflow. - What are the benefits of using a rebase workflow over a merge workflow? Are there downsides?
Getting Started
Warm Up in Breakout Rooms
- Using a
merge
workflow, how are commits organized in the timeline? - How is a
rebase
workflow different in how commits are organized? - What are the benefits and downsides of using one versus the other? Is one better than the other?
Reviewing some key points on the above
- Merging keeps the timeline of your commits. Can be really useful for knowing when code has been updated.
- However, rebasing rewrites your commit history. This can make it much easier to keep all feature commits together.
- Merge conflicts will typically happen more often in a rebase workflow because that timeline is being rewritten.
Now that you are a bit more familiar with some of the differences between a merge
and rebase
workflow on a high level, let’s focus on some of the Git commands used specifically with a rebase
workflow. Fork this repo and then clone it down. Make sure to also change into the rebase-workshop
directory! Then follow the exercises below:
Note
Although this is just a template README file, the same rules will apply when you’re actually working on your code files and making changes there as well.
Also take note that spacing is important, so if you don’t see what you expect the first time you run a command, make sure there isn’t a typo and that your spacing is correct.
Exercises
Exercise 1 (Initial commits and setting up a PR)
The rebase workflow has a lot of similarities to the merge workflow, especially around resolving conflicts that arise when multiple contributors are working on the same codebase. While there are a few different ways of doing this, we’ll cover one way you can approach it.
- Checkout a new branch (branch1) off of
main
and make some edits by adding or removing lines from the README or creating new files with some content. - Create 3 new commits on this branch (branch1). Run
git log
and note the three new commits that you have added. (you can exit the log by hittingq
) - Checkout another branch (branch2) off of
main
and make some edits here as well. You only need to create 1 new commit on this branch. - Next, go back to your previous branch (branch1), and push up your branch to GitHub with
git push origin <branch_name>
. Then go to your GH repo and clickCompare & pull request
. - When you open the pull request, change the
base repository
to be your repository. (NOT “turingschool-examples”!). Then clickCreate pull request
.
Note
In the next exercise, you will be making some edits to your git timeline. Your git text editor is typically defaulted to VIM mode (where you are editing code directly in the terminal). This means that when you run the git rebase -i ...
commands below, VIM will likely open and you must edit in VIM. However, you can change this to be VS Code through the following command:
To use VS Code as your git text editor, run:
git config --global core.editor "code --wait"
Exercise 2 (Interactive Rebase)
Let’s say we’re looking at those three commits, and they all appear to be tied together and likely could be one larger commit. Now is a perfect time for interactive rebase! Interactive rebase allows us to manipulate our commit history by editing our commits. Here are some tips to working through the interactive rebase flow!
- In your terminal, run
git log --oneline
inbranch1
to see the 3 commits you just created.- Looking at them, you’ll notice you’ve made multiple changes all relating to the README. Wouldn’t it be nice to combine (ie.
squash
) them into one?
- Looking at them, you’ll notice you’ve made multiple changes all relating to the README. Wouldn’t it be nice to combine (ie.
- To squash all three of them, run
git rebase -i <hash>
(ie98b1875
) or rungit rebase -i HEAD~3
.- Take a look at the note underneath Exercise #2 for questions about which hash to pick or what
HEAD~3
mean! - Note: When in the interactive rebase, it is possible that you’ll be in VIM mode (where you are editing code in the terminal). In order to edit anything first type
i
for insert. Once you are done editing, pressesc
. Then type:wq
(this will save your changes). If it opens up VSCode or Atom, edit the scripts and save as your normally would before closing out of it.
- Take a look at the note underneath Exercise #2 for questions about which hash to pick or what
You should see something like this…
pick c3a27af Remove requirements from README
pick 255ed0e Update first installation instruction
pick 2fb87ae Update troubleshooting section of README
# Rebase 98b1875..2fb87ae onto 98b1875 (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
- Replace the word
pick
withsquash
in front of two of the commit hashes (It shouldn’t matter which ones since you will effectively be combing all three into one. However, if it doesn’t work correctly, experiment with different ones). - Then save your changes. (In VIM mode, press
esc
and type:wq
) - You’ll see a new message with the three commits messages you previously made.
- This is your opportunity to edit/write (remember to press
i
in VIM mode) the commit messages you want to use. If you did not edit any of the messages it would keep them all. - Remove two of the commit messages and change the final one to be the new message describing the changes made.
- This is your opportunity to edit/write (remember to press
- After editing, save your changes once more.
In the terminal you should now see:
Successfully rebased and updated refs/heads/<branch_name>.
Now when you run git log --oneline
there should no longer be 3 new commits, but instead one commit with the new message you wrote. Cheers!
Note
To enter an interactive rebase you must first determine at which commit you want to begin viewing the commit history. You can either use the commit hash that is before the commit at which you want to begin editing or you can use HEAD~3
where the ~3
is the number or generations you want to go back (it will include the head (or current commit) and the 3 previous)
Another way to think of this is that the argument you supply to git rebase -i
is the hash of the parent of the last commit you want to edit. If you want to edit all three of the commits you made on branch1, then you need to select the parent of the first commit you made (which is the Initial Commit if you made 3 new commits). If you want to edit the most recent two commits, then you will use the hash for the commit directly before those two recent commits.
Exercise 3 (Pushing your new timeline up to GitHub)
Now that we’ve done that, take a look at your PR. Note that there are still three commits there. It’s not up to date with our local repo.
- Try pushing up your branch again and take note of what it says.
- Because we are rewriting the commit timeline, it’s not allowing us to push up to our remote respository (it acts as a safety measure)
- To fix this and rewrite the timeline, you can run
git push -f origin <branch_name>
.- The
-f
means to forcefully push up and rewrite the timeline which is necessary for this rebase because we want to combine these commits
- The
- Then check your Pull Request and check if it now only displays one commit.
- After confirming that there is only one commit (you may need to refresh GitHub), then click on
Merge pull request
to merge the branch intomain
.
Exercise 4 (Bringing those changes to Branch 2)
Remember that we had created two branches? What if we wanted to pull in our latest changes from main into our second branch?
- Checkout
main
and pull down your latest changes from GitHub. - Then checkout branch2 and run
git rebase main
to bring in the latest changes.- Note because there were changes made in a similar file, you may need to deal with merge conflicts.
- Work through the merge conflicts,
add
them, and then rungit rebase --continue
to finish up. You can then rungit log
to see your new and improved timeline!
Standardizing Some of the Steps
There are a lot of steps we took in this workshop, some not always being necessary every time. (You may not always need to do an interactive rebase if you’re happy with the way you made your commits) As you begin to work with your teams, here are some simple steps you can take with your workflow:
- Create feature branch
- Make commits on feature branch
- Optional: Interactive rebase to squash commits
- Git pull origin main into your
main
branch - Checkout feature branch again and run
git rebase main
- Resolve conflicts on branch
- Push up branch and create PR.
Remember to experiment and try things out. Just like the first time you worked with Git, it can be scary at first but this is the time to try things out before doing it on the job!