lab 40 Using Rebase to Modify Old Commits
Goals
Suppose you found a change that should be made in an old commit. You can use git rebase
to add that change to the commit seamlessly.
Fix a Bug in the Greeter Class
Our greeter class doesn’t use proper punctuation when greeting the user. Let’s fix that by adding a period to our greeting line.
lib/greeter.rb
class Greeter def initialize(who) @who = who end def greet "Hello, #{@who}." end end
Execute:
git add lib/greeter.rb git commit -m "Add missing punctuation"
If we haven’t made the greeter class public yet there’s no need to keep this as a separate commit. It would just add noise to the final history. You can use git rebase
to squash this commit into the one that added the greeter class. First, you’ll need to find a rebase starting point. Usually the main
branch is good for this when working on your own branch, but for this exercise we’ll use the commit before the one that added the greeter class.
First find the commit that added the greeter class.
Execute:
git hist
Output:
$ git hist * c86fb5a 2022-10-24 | Add missing punctuation (HEAD -> main) [Jim Weirich] * 62eac9b 2022-10-24 | Updated Rakefile (greet) [Jim Weirich] * 7a0c973 2022-10-24 | Hello uses Greeter [Jim Weirich] * 5729241 2022-10-24 | Added greeter class [Jim Weirich] * 62f7394 2022-10-24 | Added README [Jim Weirich] * 0022837 2022-10-24 | Added a Rakefile. [Jim Weirich] * 3172288 2022-10-24 | Moved hello.rb to lib [Jim Weirich] * 3e56dbf 2022-10-24 | Add an author/email comment [Jim Weirich] * 1dee7f9 2022-10-24 | Tell user how many names they have (tag: v1) [Jim Weirich] * c72af6b 2022-10-24 | Rename variable to match its usage (tag: v1-beta) [Jim Weirich] * 8cdd2cd 2022-10-24 | Can specify multiple names [Jim Weirich] * 28fe396 2022-10-24 | Added a comment [Jim Weirich] * 15c7573 2022-10-24 | Added a default value [Jim Weirich] * 7d55044 2022-10-24 | Using ARGV [Jim Weirich] * 91b926e 2022-10-24 | First Commit [Jim Weirich]
Then perform the rebase
Execute:
git rebase -i <hash>^
You should see a file open in your editor that looks similar to the example below.
Output:
pick 0b741fa Added greeter class pick b597367 Hello uses Greeter pick d1233fa Updated Rakefile pick dc9d1f3 Add missing punctuation pick e62cb79 Changed README in original repo pick 6cac757 Added shared comment to readme # Rebase 827c6b1..6cac757 onto 827c6b1 (6 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 [-C | -c] <commit> = like "squash" but keep only the previous # commit's log message, unless -C is used, in which case # keep only this commit's message; -c is same as -C but # opens the editor # 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. #
The bottom portion includes some instructions and reminders of the possible commands. By default, the rebase operation will “pick” each commit and add them one after the other in the order specified. We will want to use the “fixup” command. Change the file to match the example below.
Output:
pick 0b741fa Added greeter class fixup dc9d1f3 Add missing punctuation pick b597367 Hello uses Greeter pick d1233fa Updated Rakefile pick e62cb79 Changed README in original repo pick 6cac757 Added shared comment to readme # Rebase 827c6b1..6cac757 onto 827c6b1 (6 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 [-C | -c] <commit> = like "squash" but keep only the previous # commit's log message, unless -C is used, in which case # keep only this commit's message; -c is same as -C but # opens the editor # 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. #
Then save and exit and the rebase will continue as normal. If you look at the commit adding the greeter class now you will see that it includes the punctuation change we just added.
Execute:
git show <hash>
Output:
$ git show 09b3f43 commit 09b3f43c4d37cb78bfcbb62670adbb08f4e75491 Author: Jim Weirich <jim (at) edgecase.com> Date: Mon Oct 24 19:02:01 2022 +0000 Added greeter class diff --git a/lib/greeter.rb b/lib/greeter.rb new file mode 100644 index 0000000..66e6370 --- /dev/null +++ b/lib/greeter.rb @@ -0,0 +1,8 @@ +class Greeter + def initialize(who) + @who = who + end + def greet + "Hello, #{@who}." + end +end
Up Next
You’ll learn how to automate this process with the --autosquash
option, but first you’ll learn about the reflog.