Tuesday, May 29, 2018

Use git rebase to squash multiple commits to one

Why bother to squash commits

If you are a svn user, you probably formed a "bad" habit of committing many small changes. For svn, committing lots of small changes is fine, because svn only stores the diff between two commits. For git, committing lots of small changes is not fine, your remote repository will waste lots of disk space for these small changes. Git has a different way of storing your versioned files, unlike svn, git stores a snapshot of all changed files instead of just the diff.

For example, if you have a huge file, and you only changed one line then committed a version to remote repository. In svn, this commit only cost the storage of one line (a diff line is stored); in git, this commit/push costs the disk space of the whole file (a snapshot is stored).

How to squash multiple commits


The squash technique is like this:

Step 1. use "git log" command to figure out how many commits you have pushed to a branch. For example, you have 6 commits in development branch today, and you wish you have committed them as one big commit instead of 6 small ones.

Step 2. use "git rebase Head~6" command to squash unwanted commits. One you issued the command, the 6 commits are displayed in a list. Notice the # comment lines tell you the meaning of pick, edit and squash.
$ git rebase -i HEAD~6

pick 01a3124 add handleReq function
pick 6e342aa typo
pick 341a3ba format a space
pick 234be7 code review update
pick 2ab141c2 add annotation
pick 137a32a4 remove import

# Rebase 321191d.. 137a32a4 onto 321191d
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Step 3. you edit the above list using vi commands. For example type "i" to edit. The edited commit lists should looks like:
$ git rebase -i HEAD~6

pick 01a3124 add handleReq function
squash 6e342aa typo
squash 341a3ba format a space
squash 234be7 code review update
squash 2ab141c2 add annotation
squash 137a32a4 remove import

# Rebase 321191d.. 137a32a4 onto 321191d
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Step 4. once you typed "wq" the next screen will allow you to edit the commit comments. Use vi command to edit them. Type "i" to modify.

# This is a combination of 6 commits.
# The first commit's message is:
add handleReq function

# This is the 2nd commit message:

typo

# This is the 3rd commit message:

 format a space

# This is the 4th commit message:

code review update

# This is the 5th commit message:

add annotation

# This is the 6th commit message:

remove import

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Explicit paths specified without -i nor -o; assuming --only paths...
# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
# new file:   src/main/java/com/fishtank/FishSpecies.java
# modified:   src/main/java/com/fishtank/FishHandler.java
# modified:   src/test/java/com/fishtank/FishHandlerTest.java
# modified:   pom.xml
#

Step 5. after modifying, type "wq" to save and quit.

# This is a combination of 6 commits.
# The first commit's message is:
add handleReq function

# This is the 2nd commit message:

# typo

# This is the 3rd commit message:

# format a space

# This is the 4th commit message:

code review update

# This is the 5th commit message:

#add annotation

# This is the 6th commit message:

#remove import

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Explicit paths specified without -i nor -o; assuming --only paths...
# Not currently on any branch.
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
# new file:   src/main/java/com/fishtank/FishSpecies.java
# modified:   src/main/java/com/fishtank/FishHandler.java
# modified:   src/test/java/com/fishtank/FishHandlerTest.java
# modified:   pom.xml
#

Step 6. use "git push -f" to replace the remote branch commits with the squashed the commits. The reason to use -f (force) is now your local repository and remote repository have different commits, it is not a merge but a replace instead. You know you would like to replace the remote content with your local content for that branch, so you use -f to force the replace.

What happens if you messed up things

Not a big deal. As long as you didn't issue command "git push -f", the remote repository didn't get effected, you only messed up your local repository. You can issue command "git reset --hard origin/development" to replace your local repository with the remote repository content.

Why I stopped publishing blog posts as information provider

Now the AI can generate content. Does that mean the web publishing industry reaches the end? ChatGPT said: ChatGPT Not at all. While AI can ...