Published on

Part 9: Rewriting History with Git

Authors

Introduction

Rewriting commit history can be a powerful tool for maintaining a clean and understandable project history. Git offers several commands to modify and refine the history, including git rebase, git cherry-pick, and git filter-branch. However, these commands must be used with care to avoid disrupting the shared history.

1. Git Rebase for History Rewriting

git rebase can be used not only for integrating changes but also for editing commit history. It helps create a linear and cleaner history by replaying changes on top of another branch.

Interactive Rebase

Interactive rebasing (git rebase -i) allows you to:

  • Reorder commits.
  • Squash commits into a single one.
  • Edit commit messages.
  • Remove commits.

How to Use Interactive Rebase:

# Start an interactive rebase for the last 5 commits
$ git rebase -i HEAD~5

This opens an editor where you can specify actions for each commit:

  • pick: Keep the commit as is.
  • reword: Change the commit message.
  • edit: Modify the content of the commit.
  • squash: Merge the commit with the previous one.
  • drop: Remove the commit.

Example: Squashing Commits

To combine multiple commits into one, mark the first commit as pick and the subsequent ones as squash.

2. Using Git Cherry-Pick

git cherry-pick allows you to apply specific commits from one branch to another. This is useful for selectively moving features or fixes.

Basic Usage:

# Apply a specific commit to the current branch
$ git cherry-pick <commit-hash>

Practical Example: If a bug fix on feature-branch needs to be applied to main:

$ git checkout main
$ git cherry-pick <commit-hash>

This will copy the changes made in the specified commit and apply them to main.

3. Git Filter-Branch for Advanced History Rewriting

git filter-branch is a powerful command for rewriting history across all branches. It’s often used to:

  • Remove sensitive data from commit history.
  • Change email addresses or author information.
  • Modify files across multiple commits.

Example: Removing a File from History:

$ git filter-branch --tree-filter 'rm -f path/to/file' -- --all

Note: git filter-branch is resource-intensive and can permanently alter history. Ensure you have backups and avoid using it on shared branches.

4. Precautions When Rewriting History

Rewriting history, especially on branches that have been shared with others, can cause significant issues. Always follow these precautions:

  • Avoid rebasing or rewriting public branches: Use rebase and filter-branch only on local or private branches.
  • Communicate with your team: Inform your team before making changes to shared history.
  • Back up your repository: Create a backup before running commands like filter-branch.

5. Practical Example: Rewording Commit Messages

  1. Start an interactive rebase:
$ git rebase -i HEAD~3
  1. Change pick to reword for the commit you want to edit.
  2. Save and close the editor.
  3. Enter the new commit message when prompted.

6. Using git reflog to Recover from Mistakes

git reflog tracks all changes made to HEAD and is invaluable for recovering lost commits or branches after rewriting history.

Recovering a Lost Commit:

# View reflog entries
$ git reflog

# Reset to a specific reflog entry
$ git reset --hard <commit-hash>

7. Automating History Rewriting with git filter-repo

git filter-repo is a faster, more efficient alternative to git filter-branch for advanced history rewriting tasks.

Installation and Basic Usage:

# Install git-filter-repo (requires Python)
$ brew install git-filter-repo

# Remove a file from history
$ git filter-repo --path path/to/file --invert-paths

Recap

  • Rebase helps create a linear and cleaner history.
  • Cherry-pick allows selective application of commits.
  • Filter-branch and filter-repo provide powerful tools for extensive history rewriting.
  • Reflog is a safety net for recovering changes.

Next Steps

In the next part, we’ll discuss Git Best Practices for Teams, focusing on collaboration tips, maintaining code quality, and integrating with CI/CD pipelines.


Stay tuned for Part 10: "Git Best Practices for Teams."