Throw away local commits in Git

Learn throw away local commits in git with practical examples, diagrams, and best practices. Covers git development techniques with visual explanations.

How to Discard Local Commits in Git

Hero image for Throw away local commits in Git

Learn various methods to safely and effectively throw away local Git commits, whether they are unpushed, partially staged, or completely unwanted.

Working with Git often involves making local commits that, for various reasons, you might later decide to discard. Perhaps you made a mistake, committed sensitive information, or simply want to re-organize your work before pushing to a remote repository. This article will guide you through different scenarios for throwing away local commits, from simple unstaging to completely rewriting history, ensuring you understand the implications of each action.

Understanding Git's State and Commit History

Before we dive into discarding commits, it's crucial to understand how Git manages your project's state. Git tracks changes in three main areas:

  1. Working Directory: Your current files and their modifications.
  2. Staging Area (Index): A snapshot of changes you've marked to be included in the next commit.
  3. Local Repository (HEAD): The history of commits on your current branch.

Discarding commits often involves manipulating these states. The key is to know which state you want to revert and to what extent.

graph TD
    A[Working Directory] --> B{git add}
    B --> C[Staging Area]
    C --> D{git commit}
    D --> E[Local Repository (HEAD)]
    E -- git push --> F[Remote Repository]
    F -- git pull --> A

Simplified Git Workflow illustrating the three main states.

Scenario 1: Discarding Uncommitted Changes

If you have changes in your working directory that are not yet staged or committed, and you want to completely discard them, Git provides straightforward commands. This is the safest scenario as you're not altering any committed history.

# Discard changes in a specific file
git checkout -- <file>

# Discard all changes in the working directory (untracked files will remain)
git checkout -- .

# Discard all changes and untracked files (use with caution!)
git clean -fd

Commands to discard uncommitted changes.

Scenario 2: Unstaging Staged Changes

Sometimes you stage changes with git add, but then decide you don't want them in the next commit, or you want to modify them further before committing. You can easily unstage these changes without losing your work.

# Unstage a specific file
git reset HEAD <file>

# Unstage all staged changes
git reset HEAD

Commands to unstage changes from the staging area.

Scenario 3: Discarding Local Commits (Unpushed)

This is the most common scenario for 'throwing away' local commits. If you've made one or more commits on your local branch that haven't been pushed to a remote repository, you can safely revert your branch's history to an earlier state using git reset.

1. Identify the target commit

Use git log to find the hash of the commit you want to revert to. This is the commit before the ones you want to discard.

2. Perform a soft reset

If you want to keep the changes from the discarded commits in your staging area, use git reset --soft <commit-hash>. This moves HEAD to the specified commit, but keeps all changes from the discarded commits staged.

3. Perform a mixed reset (default)

If you want to keep the changes in your working directory but unstage them, use git reset --mixed <commit-hash> (or simply git reset <commit-hash>). This moves HEAD to the specified commit and moves changes from discarded commits to the working directory as unstaged changes.

4. Perform a hard reset (use with extreme caution)

If you want to completely discard the commits and all their associated changes from your working directory and staging area, use git reset --hard <commit-hash>. This is irreversible and will delete all work from the discarded commits.

# View commit history to find the target commit hash
git log --oneline

# Example: Reset to the commit before the last two commits
# (HEAD~2 refers to two commits before HEAD)
git reset --soft HEAD~2

# Example: Reset to a specific commit, keeping changes unstaged
git reset --mixed 0a1b2c3d

# Example: Reset to a specific commit, discarding all changes
git reset --hard 0a1b2c3d

Examples of git reset for discarding unpushed commits.

graph TD
    A[Commit 1] --> B[Commit 2]
    B --> C[Commit 3 (HEAD)]

    subgraph Soft Reset
        C -- git reset --soft B --> B_soft[Commit 2]
        B_soft --> D_soft[Changes from C (Staged)]
    end

    subgraph Mixed Reset
        C -- git reset --mixed B --> B_mixed[Commit 2]
        B_mixed --> D_mixed[Changes from C (Unstaged)]
    end

    subgraph Hard Reset
        C -- git reset --hard B --> B_hard[Commit 2]
        B_hard --X E_hard[Changes from C (Discarded)]
    end

Visualizing the effects of git reset --soft, --mixed, and --hard.

Scenario 4: Discarding a Specific Commit (Keeping Others)

If you want to remove a commit from your history but keep subsequent commits, git revert is the safer option. Instead of rewriting history, git revert creates a new commit that undoes the changes introduced by a previous commit.

# Revert a specific commit (creates a new commit that undoes the changes)
git revert <commit-hash>

# Revert a range of commits (from older to newer)
git revert <older-commit-hash>..<newer-commit-hash>

Using git revert to undo specific commits.

Scenario 5: Rewriting History with Interactive Rebase

For more complex scenarios, like removing a commit from the middle of your history, combining multiple commits, or reordering them, git rebase -i (interactive rebase) is the tool of choice. This is a powerful but advanced operation.

# Start an interactive rebase from a specific commit (or HEAD~N for N commits back)
git rebase -i <commit-hash-before-your-changes>

Initiating an interactive rebase.

When you run git rebase -i, Git opens an editor with a list of commits. To discard a commit, simply delete its line from the list. You can also use squash to combine commits, edit to modify a commit, or reword to change a commit message.