DEV Community

Cover image for Split a commit into 2 commits with `git rebase`
Sean Larkin
Sean Larkin

Posted on

Split a commit into 2 commits with `git rebase`

Overview

Git is a powerful version control system that allows developers to track and manage changes to their code. One of the key features of Git is the ability to use interactive rebasing to modify the history of a branch.

Interactive rebasing allows you to edit, reorder, and split commits in a branch. This can be useful when you need to clean up your branch history, or when you want to split a commit that contains changes for multiple files into separate commits.

pnpm, yarn, npm lockfiles

Have you ever had to git rebase in your project but consistently had merge conflicts in your pnpm/yarn/npm lockfiles? You may have heard the advice to 'always commit your lockfile in an isolated commit'.

This is because you can use the drop command in an interactive rebase to remove the commit so you can regenerate the lockfile after your rebase.

Often times, I forget this and sometimes I end up with multiple files (including my lockfile) inside my single commit and now I cannot drop it.

Getting Started

In this tutorial, we will show you how to split a commit during an interactive git rebase. We will use a simple example to illustrate the steps involved in splitting a commit.

To begin, let's assume that you have a branch called feature-branch that is based on the master branch. The feature-branch contains three commits, X, Y, and Z, as shown below:

          A - B - C - D [master]
         /
    X - Y - Z [feature-branch]
Enter fullscreen mode Exit fullscreen mode

The Y commit in the feature-branch contains changes for both fileA and fileB. However, you want to split this commit into two separate commits, one for fileA and one for fileB.

Using git rebase -i

To do this, you need to start an interactive rebase using the git rebase -i command. In this example, we will use the master branch as the base branch, so the command would be git rebase -i master.

This will open an editor where you can specify the actions that you want to perform on each commit in the feature-branch. In this case, you want to split the Y commit, so you need to replace the pick command for commit Y with the edit command.

Once you save and close the editor, the rebase will start and it will stop at the Y commit. This will allow you to make changes to the Y commit.

Split the Commit!

To split the Y commit, you first need to unstage the changes from the commit. This can be done using the git reset HEAD~ command. This will leave the changes in your working directory, but they will not be staged.

Next, you can use the git add command to stage only the changes for fileA. Then, you can use the git commit command to create a new commit with the changes for fileA. This will create a new commit, Z, in the feature-branch, as shown below:

          A - B - C - D [master]
         /
    X - Y - Z [feature-branch]
Enter fullscreen mode Exit fullscreen mode

Now, you can use the git add command to stage the remaining changes for fileB. Then, you can use the git commit command to create a new commit with the changes for fileB. This will create a new commit, W, in the feature-branch, as shown below:

          A - B - C - D [master]
         /
    X - Y - Z - W [feature-branch]
Enter fullscreen mode Exit fullscreen mode

Tada!

Finally, you can use the git rebase --continue command to continue the interactive rebase. This will apply the changes that you made to the Y commit and create a new branch history with the two separate commits for fileA and fileB respectively!

Now anytime you have a pesky lockfile change included with other files in a commit, you can separate it out, and then drop them during your rebases to make interactive rebase efficient and clean!

Oldest comments (2)

Collapse
 
fruntend profile image
fruntend

Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍

Collapse
 
devinrhode2 profile image
Devin Rhode • Edited

I have this git redo alias I've written:
stackoverflow.com/a/73981850/565877

But I tend to prefer changing git-rebase-todo from this:

pick 123123 Foo bar
Enter fullscreen mode Exit fullscreen mode

To:

pick 123123 Foo bar
exec git undo --soft # from tj/git-extras
break
Enter fullscreen mode Exit fullscreen mode

Really, the git rebase edit command should work differently. It should actually move everything back into your working directory (unstage/out of index).

Git rebase also has this issue where, if you have untracked files (i.e. a commit which simply adds some files) then git rebase --continue will happily ignore that untracked file.

So if you simply:

  1. Stop during interactive rebase
  2. Pop that change into working directory
  3. Accidentally or incidentally (perhaps via a script) run git rebase --continue

The commit you popped in step 2 will still be a floating untracked file and your rebase may complete successfully.