These days Git is the standard version control system, though a little bit advanced topics like to remove commits from a branch might be a question some developers. With git and a few commands you can undo changes and modify the commit history. This is especially useful when you need to remove a commit from a branch after it has been pushed to a remote repository. In this blog post, we will explore the different methods you can use to remove a commit from a branch after it has been pushed. We will cover step-by-step instructions for each method, as well as some best practices and potential pitfalls to watch out for. Whether you’re a beginner or an experienced Git user, this post will help you confidently manage your Git commit history and keep your codebase organized.
In this article, we will explore the following scenarios:
- Remove n last commits from branch after push
- Remove specific commits from branch middle of the branch after push (clean way)
- Remove specific commits from branch middle of the branch after push (dirty way)
Imagine we have branch with following this commit history:
(init:8dea3ce)----(a:8d1a72)----(b:f661dc6)----(c:6d6407f)----(d:ecc4432)
Remove n last commits from branch after push
One situation that may arise is the need to remove the n last commits from a branch after they have already been pushed to a remote repository. This can happen if changes were accidentally committed, or if a branch was merged prematurely, and it’s necessary to undo those changes.
Step 1: back up
Checkout the branch from which you want to remove the commits, and create a new branch from the current branch as a backup.
git checkout [branch-name]
git branch [backup-branch-name]
Step 2: Reset to state you want to be
Reset the branch to the commit just before the ones you want to remove using the command:
# This will remove the last 2 commits from the branch's history
git reset --hard HEAD~2
# Alternatively you can use the commit-hash you want to reset to
git reset --hard f661dc6
# your branch looks like this now
# (init:8dea3ce)----(a:8d1a72)----(b:f661dc6)
Step 3: push changes to origin (server if you call it)
Force push the updated branch to the origin using:
git push origin <branch-name> --force
This is necessary because the branch’s history has changed and you want to overwrite the remote branch with your updated branch.
Step 4: team members update
If other people are working on the same branch, you should communicate the changes you made to them, as they will also need to update their local branches.
For other team members ,to get a branch in their local repository to be exactly the same as the remote origin branch, they can use the following command (this their local commits on the branch is going to be deleted):
git fetch origin
git reset --hard origin/branch-name
Remove specific commits from branch middle of the branch after push (clean way)
To remove specific commits from a Git branch after they have been pushed to a remote repository, the most commonly used and safest method is to revert the commits. Reverting creates a new commit that undoes the changes made in the specified commit, keeping a record of the undo action. Note that you might not like this extra commits but this way the rest of your team do not need to anything.
Here’s how to regenerate a branch focusing only on reverting commits:
Identify the commit to revert: Use the Git log command to identify the commit that you want to revert. The commit hash or message can be used to reference the commit. (let’s revert commit b of example above)
Revert the commit: Use the Git revert command to create a new commit that undoes the changes made in the specified commit. The command is as follows:
git revert -i <commit-hash>
# example revert commit b:
git revert f661dc6
# your branch looks like this now
#(init:8dea3ce)----(a:8d1a72)----(b:f661dc6)----(c:6d6407f)----(d:ecc4432)----(revert:a1b2c3d)
This will create a new commit that undoes the changes made.
Push the changes: After reverting the commits, push the changes to the remote repository using the Git push command
Revert multiple commits
If you want to revert multiple commits, you can use the following command:
git revert <start-commit>..<end-commit>
Remove specific commits from middle of the branch after push (dirty way)
Sometimes you want some commits to be removed from branch history, as if it never happened. To remove specific commits in the middle of a Git branch without creating a new commit, you can use Git’s interactive rebase feature. Interactive rebase allows you to modify the branch history by selectively editing, deleting, or combining commits. Here’s how to remove commits in the middle of a branch without creating a new commit (or you can do things from updating the commit messages to merge them and etc.).
Rebasing is generally considered a more advanced task for Git users. However, the good news is that the GitLens extension for Visual Studio Code (VSCode) greatly simplifies the process. Despite this assistance, it’s important to comprehend the task at hand (in this case, tidying up a branch for a cleaner history).
Tools like GitLens are helpful, but I’ll demonstrate the standard process using Git commands so you can perform these tasks in any environment.
Identify the commits to remove: Use the Git log command to identify the commits that you want to remove. Note down the commit hashes or messages.
Start interactive rebase: Use the Git rebase command with the -i or –interactive option to start interactive rebase. The command is as follows:
git rebase -i <commit-hash>
# example rebase commit b:
git rebase -i 8d1a72
This will open a text editor displaying a list of the branch’s commits from the specified commit to the current one.
pick f661dc6 b
pick 6d6407f c
pick ecc4432 d
# Rebase 8d1a72d..ecc4432 onto 8d1a72d (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 [-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.
#
Above might opens in vim editor, if you are new to it, it would be an extra complication in addition to rebasing. It is important not to panic and read all commands, as well as get familiar with basics of vim editor. Note that you can change the default editor to vscode or anyone you are more comfortable with.
Please note that while in the terminal mode, in case at the middle of rebase you found things are not going your way you can always exit the rebase mode and try again.
git rebase --abort
Delete the commits: In the editor, locate the commits that you want to delete and delete their corresponding lines. Save and close the editor. I want to remove commit b then I edit the text and use drop for that commit
drop f661dc6 b
pick 6d6407f c
pick ecc4432 d
Merge conflict: There is a good chance that you end up with merge conflicts when you drop one or few commits, command line asks you to resolve all conflicts manually, mark them as resolved by
“git add/rm “, then continue, ex.:
git add .
git rebase --continue
You keep doing it until the rebase is completed. Git will now apply the changes and remove the selected commits from the branch history. Verify that the branch history has been updated as expected. your branch should look like this :
(init:8dea3ce)----(a:8d1a72)----(c:6d6407f)----(d:ecc4432)
Push the changes: After completing the rebase, push the changes to the remote repository using the Git push command. The command is as follows:
git push <remote> <branch-name> --force
#ex.
git push origin main --force
If other people are working on the same branch, you should communicate the changes you made to them, as they will also need to update their local branches.
For other team members ,to get a branch in their local repository to be exactly the same as the remote origin branch, they can use the following command (this their local commits on the branch is going to be deleted):
git fetch origin
git reset --hard origin/branch-name
Conclusion
In Git, there are several ways to remove commits from a branch, including reset, revert, and rebase. While all three methods achieve similar outcomes, they differ in their approach to removing commits, and understanding the differences is essential to selecting the right approach for the task at hand.
Reset removes commits by resetting the branch to a previous commit, permanently discarding any changes made in the removed commits. This method is useful when the commits to remove are the most recent ones and the changes they introduced are no longer needed. However, reset should be used with caution, as it is something everyone using the branch should be aware of and act on it.
Revert removes commits by creating a new commit that undoes the changes introduced in the removed commit, while still keeping a record of the removed commit. This method is useful when you want to remove commits without losing the changes they introduced, such as when the changes are no longer needed, or when the removed commits are part of a shared branch, and their deletion may affect other team members’ work.
Rebase rewrites the history of the branch, in this case by applying changes from the removed commits and excluding the commits themselves. This method is useful when the commits to remove are scattered throughout the branch history and not the most recent ones. Rebase allows you to selectively edit, delete, or combine commits in the branch’s history, without creating a new commit, making it a powerful tool for managing branch history. But again everybody who uses the branch should be aware of it and update their branch accordingly.
Leave a Reply