GLM 4.5 Air

Git: How to Discard Unstaged Working Directory Changes

Learn how to discard unstaged changes in Git using both git checkout and git restore commands. Complete guide with examples for specific files or all changes. Master Git file management today.

Question

How do I discard unstaged changes in Git that are in the working directory but not in the staging area?

GLM 4.5 Air

Brief Answer

To discard unstaged changes in Git that exist in your working directory but haven’t been staged, you can use either git checkout -- <file> in older Git versions or git restore <file> in newer Git versions (2.23+). For discarding all unstaged changes across all files, use git checkout . or git restore . respectively.

Contents

Understanding Git’s Working Directory and Staging Area

Git uses a three-area model for managing files:

  1. Working Directory: Your actual files on disk where you make changes
  2. Staging Area (Index): An intermediate area where you prepare changes before committing
  3. Repository: The committed snapshots of your project

When you make changes to files in your working directory, they exist as unstaged changes until you explicitly add them to the staging area with git add. The process of discarding unstaged changes means removing those modifications from your working directory while preserving files that have already been staged.

Key distinction: Staged changes are those you’ve added with git add but haven’t yet committed. Unstaged changes are everything else that’s been modified but not added.

Here’s a simple workflow visualization:

Modified file → `git add` → Staged changes → `git commit` → Repository
Modified file ← `git checkout/restore` ← (changes discarded)

Discarding Unstaged Changes with git checkout

The traditional method for discarding unstaged changes is using git checkout. This command has been part of Git for a long time but has different behavior depending on the Git version.

For older Git versions (prior to 2.23), to discard unstaged changes for a specific file:

bash
git checkout -- <file>

To discard unstaged changes for all files:

bash
git checkout .

Important note: The git checkout command has multiple uses (checking out branches, switching files, etc.), and its behavior can be confusing. In older Git versions, when used with -- and a file path, it specifically discards unstaged changes for that file.

Example workflow:

  1. Make changes to a file:
    bash
    echo "new content" >> README.md
    
  2. Check the status to see unstaged changes:
    bash
    git status
    # On branch master
    # Changes not staged for commit:
    #   (use "git add <file>..." to update what will be committed)
    #   (use "git checkout -- <file>..." to discard changes in working directory)
    #
    #       modified:   README.md
    
  3. Discard the changes:
    bash
    git checkout -- README.md
    

Discarding Unstaged Changes with git restore

Starting with Git version 2.23 (released in November 2019), Git introduced a more explicit command for restoring files: git restore. This command was created to clarify the often-confusing behavior of git checkout.

To discard unstaged changes for a specific file with git restore:

bash
git restore <file>

To discard unstaged changes for all files:

bash
git restore .

The git restore command is more explicit about its purpose and is now the recommended approach for discarding changes. It also offers additional options for more precise control:

bash
# Restore a specific file from the staging area
git restore --staged <file>

# Restore a file to a specific commit
git restore --source=<commit-hash> <file>

# Restore multiple files matching a pattern
git restore *.js

Example workflow:

  1. Make changes to multiple files:
    bash
    echo "new feature" > feature.js
    echo "bug fix" > bugfix.md
    
  2. Check the status:
    bash
    git status
    # On branch master
    # Changes not staged for commit:
    #   (use "git add <file>..." to update what will be committed)
    #   (use "git restore <file>..." to discard changes in working directory)
    #
    #       modified:   bugfix.md
    #       modified:   feature.js
    
  3. Discard changes for specific files:
    bash
    git restore feature.js bugfix.md
    

Discarding Changes for Specific Files

When you only want to discard changes for specific files while keeping changes to other files, both git checkout and git restore allow you to specify one or more file paths:

bash
# With git checkout
git checkout -- file1.txt file2.txt

# With git restore
git restore file1.txt file2.txt

You can also use glob patterns to match multiple files:

bash
# Discard changes to all .js files
git restore *.js

# Discard changes to all files in the docs directory
git restore docs/*

Practical example:
Imagine you’re working on a project with multiple files and made changes to several of them:

bash
# Make changes to multiple files
echo "console.log('hello');" > app.js
echo "new content" > README.md
echo "fix bug" > utils/bugfix.js

# Check status
git status
# You'll see all three files listed as modified

# Discard changes only to app.js and README.md
git restore app.js README.md

# Check status again
git status
# Now only utils/bugfix.js should appear as modified

Discarding All Unstaged Changes

If you want to discard all unstaged changes across your entire working directory, you can use the following commands:

bash
# With git checkout
git checkout .

# With git restore
git restore .

Important warning: These commands will discard all unstaged changes in your working directory. There is no confirmation prompt, so use these commands with caution.

Step-by-step workflow:

  1. Make changes to multiple files without staging them:
    bash
    # Create or modify several files
    echo "temporary change 1" > temp1.txt
    echo "temporary change 2" > temp2.txt
    echo "modification" > existing.txt
    
  2. Verify the changes are unstaged:
    bash
    git status
    # You should see all three files listed under "Changes not staged for commit"
    
  3. Discard all unstaged changes:
    bash
    git restore .
    
  4. Verify the changes are discarded:
    bash
    git status
    # Should show a clean working directory if no files are staged
    

Safety Considerations and Best Practices

Discarding changes in Git is a powerful operation, but it comes with risks. Here are some important safety considerations:

1. Backup Important Changes

Before discarding changes, especially when using commands that affect multiple files:

bash
# Create a backup of your current changes
git stash push -m "backup before discard"

You can restore these changes later with:

bash
git stash pop

2. Check What You’re About to Discard

Always check git status and review the changes with git diff before discarding them:

bash
# View unstaged changes
git diff

# See which files will be affected
git status --short

3. Use --dry-run When Available

While Git doesn’t have a built-in dry-run option for checkout/restore, you can simulate the effect:

bash
# See what files would be affected
git diff --name-only

4. Avoid Discarding Changes on Shared Branches

If you’re working on a shared branch (like main or develop), be cautious about discarding changes that others might depend on.

5. Use .gitignore to Prevent Accidental Changes

Create a .gitignore file to exclude files or directories that shouldn’t be tracked:

# .gitignore example
*.tmp
.DS_Store
node_modules/

Troubleshooting Common Issues

When working with discarding changes, you might encounter some common issues:

1. “fatal: pathspec ‘’ did not match any files”

This error occurs when you try to discard changes for a file that doesn’t exist or isn’t tracked by Git:

bash
# This will cause an error if the file doesn't exist
git restore non-existent-file.txt

Solution: Verify the file exists and is tracked by Git:

bash
git ls-files <file>

2. “error: Your local changes to the following files would be overwritten by checkout”

This error occurs when you try to switch branches or checkout a commit but have unstaged changes:

bash
# You have unstaged changes but try to switch branches
git checkout other-branch
# Error: Your local changes to the following files would be overwritten by checkout:
#   modified-file.txt

Solution: Either discard the changes first or stash them:

bash
# Discard changes
git restore modified-file.txt
# OR stash changes
git stash
# Then switch branches
git checkout other-branch

3. “did you mean ‘restore’ (with two dashes)?”

You might see this warning if you’re using an older version of Git with the restore command:

bash
warning: git restore is experimental

Solution: Use checkout instead or upgrade Git to a newer version.

4. Mixed Working Directory and Staging Area

Sometimes you might want to keep staged changes but discard unstaged ones:

bash
# This will discard unstaged changes but keep staged changes
git restore --staged . && git restore .

Conclusion

Discarding unstaged changes in Git is a common operation that can be performed using either the traditional git checkout command or the more modern git restore command. Key takeaways include:

  1. For specific files: Use git checkout -- <file> (older Git) or git restore <file> (newer Git)
  2. For all files: Use git checkout . or git restore .
  3. Always check git status and review changes with git diff before discarding
  4. Consider using git stash to temporarily save changes before discarding them
  5. The git restore command (Git 2.23+) is now the recommended approach for clarity

Remember that discarded changes are typically not recoverable unless you’ve used git stash or have other backups. Always be cautious when using commands that affect your entire working directory.