Chapter 11: Stashing and Cleaning in Git
Chapter Objectives
By the end of this chapter, you will be able to:
- Temporarily shelve uncommitted changes using
git stash
. - List, apply, pop, and manage multiple stashes.
- Include untracked or all files in a stash.
- Safely preview files that would be removed by
git clean
. - Remove untracked files and directories from your working directory using
git clean
. - Use
git clean
interactively to selectively remove files. - Understand the importance of using
git stash
for context switching andgit clean
for maintaining a tidy working directory.
Introduction
In your day-to-day work as a developer, you’ll often find yourself needing to quickly switch contexts. Perhaps an urgent bug fix request comes in while you’re in the middle of a new feature, or you need to pull updates from a remote repository, but your working directory is messy with experimental changes. Committing half-finished work just to switch branches isn’t ideal, as it clutters your project history with incomplete snapshots.
This is where Git’s stash
and clean
commands come into play. git stash
allows you to temporarily save your modified, uncommitted changes (both staged and unstaged) so you can get a clean working directory. You can then reapply these changes later. git clean
helps you remove untracked files from your working directory, which is useful for getting rid of build artifacts or other temporary files that aren’t part of your project’s history.
Mastering these commands will significantly improve your workflow efficiency, allowing you to handle interruptions gracefully and maintain a pristine working environment. This chapter builds upon your understanding of the working directory, staging area, and commits, providing you with tools to manage changes that aren’t yet ready to be permanently recorded.
Theory
Shelving Changes with git stash
Imagine you’re working on a feature and have made several modifications to tracked files. Some changes might be staged, others just modified in your working directory. Suddenly, you need to switch to another branch to fix a critical bug. Your current work is not ready for a commit. What do you do?
git stash
is designed for exactly this scenario. It takes your uncommitted changes (both staged and unstaged modifications to tracked files) and “stashes” them away, saving them in a special, temporary storage area. This reverts your working directory to the state of the last commit (HEAD), giving you a clean slate.
What is a Stash?
A stash is essentially a commit object that isn’t on any normal branch. It stores:
- The state of your working directory (changes to tracked files).
- The state of your staging area (changes that were
git add
ed). - A reference to the commit you were on when you created the stash (e.g.,
HEAD
).
Git maintains a stack of stashes. Each time you git stash
, a new stash is pushed onto this stack.
%%{ init: { 'theme': 'base', 'themeVariables': { 'fontFamily': 'Open Sans' } } }%% graph TB subgraph Stash Stack Area direction BT S_N["stash@{n}<br>(Oldest Stash)"] --> S_Ellipsis["..."] --> S1["stash@{1}"] --> S0["<b>stash@{0}</b><br>(Most Recent / Top of Stack)"] end WD_Changes["Working Directory<br>+ Index Changes<br>(Tracked files, optionally untracked)"] subgraph Stash Operations direction LR StashPush["`git stash push`"] StashPop["`git stash pop`"] StashApply["`git stash apply`"] end WD_Changes -- "Shelve Changes" --> StashPush StashPush -- "Adds New Stash to Top" --> S0 S0 -- "Applies Top Stash & Removes it" --> StashPop StashPop -- "Restores to Working Directory" --> WD_Restored_Pop["Working Directory Updated"] S0 -- "Applies Top Stash & Keeps it" --> StashApply StashApply -- "Restores to Working Directory" --> WD_Restored_Apply["Working Directory Updated"] %% Styling classDef primary fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6; classDef process fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF; classDef success fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46; classDef datastructure fill:#FFFBEB,stroke:#F59E0B,stroke-width:1px,color:#B45309; class S0,S1,S_Ellipsis,S_N primary; class StashPush,StashPop,StashApply process; class WD_Changes datastructure; class WD_Restored_Pop,WD_Restored_Apply success;
Core Stash Operations:
git stash push [-m <message>] [-u | --include-untracked] [-a | --all]
orgit stash [save <message>] [-u | --include-untracked] [-a | --all]
:- This is the command to create a new stash. Historically,
git stash save "message"
was common, butgit stash push -m "message"
is the more modern and recommended syntax, aligning with otherpush
commands in Git. If no options are given,git stash
(which defaults togit stash push
) stashes changes to tracked files (both staged and unstaged). -m <message>
or (forsave
) just<message>
: Allows you to provide a descriptive message for the stash, making it easier to identify later.-u
or--include-untracked
: Includes untracked files in the stash.-a
or--all
: Includes all untracked files and ignored files in the stash. Use with caution.
- This is the command to create a new stash. Historically,
git stash list
: Displays all the stashes you’ve saved in a LIFO (Last-In, First-Out) stack. Each stash is given an identifier likestash@{0}
,stash@{1}
, etc., withstash@{0}
being the most recent.git stash apply [<stash>]
: Applies the changes from a stash to your working directory but keeps the stash in the stash list. If no<stash>
is specified, it applies the latest one (stash@{0}
).git stash pop [<stash>]
: Applies the changes from a stash to your working directory and then removes the stash from the stash list (if the application is successful without conflicts). If no<stash>
is specified, it pops the latest one.git stash drop [<stash>]
: Removes a specific stash from the stash list without applying it. If no<stash>
is specified, it drops the latest one.git stash clear
: Removes all stashes from the stash list. This is a destructive operation for the stashes themselves.git stash show [<stash>]
: Shows a summary of changes recorded in a stash (diffstat). Add-p
or--patch
to see the full diff.git stash branch <branchname> [<stash>]
: Creates a new branch with the given<branchname>
, checks it out, and applies the specified stash (orstash@{0}
if none is given). It then drops the stash if the application is successful. This is useful if the changes in your stash have diverged significantly from the branch you were on.
Core Stash Command | Description |
---|---|
git stash push [-m <message>] [-u | –include-untracked] [-a | –all] | Creates a new stash.
-m: Add a descriptive message. -u: Include untracked files. -a: Include all files (untracked and ignored). Use with caution. (Can also use git stash or git stash save <message> for older syntax). |
git stash list | Displays all saved stashes in a LIFO (Last-In, First-Out) stack (e.g., stash@{0}, stash@{1}). |
git stash apply [<stash>] | Applies changes from a stash to your working directory but keeps the stash in the list. Defaults to the latest stash (stash@{0}). |
git stash pop [<stash>] | Applies changes from a stash and, if successful (no conflicts), removes the stash from the list. Defaults to stash@{0}. If conflicts occur, the stash is not dropped. |
git stash drop [<stash>] | Removes a specific stash (or stash@{0} if unspecified) from the list without applying it. |
git stash clear | Removes all stashes from the list. This is a destructive operation for the stashes. |
git stash show [-p | –patch] [<stash>] | Shows a summary of changes (diffstat) recorded in a stash.
-p or –patch: Display the full diff of the changes. Defaults to stash@{0}. |
git stash branch <branchname> [<stash>] | Creates a new branch named <branchname>, checks it out, applies the specified stash (or stash@{0}), and then drops the stash if the application is successful. Useful for turning a stash into a dedicated branch. |
Stash Conflicts:
When you apply or pop a stash, Git tries to merge the stashed changes back into your working directory. If the files in your working directory have changed since you created the stash in a way that conflicts with the stashed changes, you’ll encounter a merge conflict, similar to conflicts during git merge or git rebase. You’ll need to resolve these conflicts manually, then git add the resolved files before committing. If git stash pop results in a conflict, the stash is not automatically dropped; you’ll need to git stash drop it manually after resolving conflicts and deciding you no longer need it.
%%{ init: { 'theme': 'base', 'themeVariables': { 'fontFamily': 'Open Sans' } } }%% graph TD A["Start: Attempt <i>git stash pop</i> or <i>git stash apply</i>"] --> B{Conflicts Occur?}; B -- No --> C["Success! Changes applied cleanly.<br>If <i>pop</i>, stash is dropped.<br>If <i>apply</i>, stash remains."]; C --> Z["End: Continue working"]; B -- Yes --> D["Git reports: 'Automatic merge failed; fix conflicts and then commit the result.'"]; D --> E["Files with conflicts are marked (e.g. <i><<<<<<<</i>, <i>=======</i>, <i>>>>>>>></i>)"]; E --> F["Working directory contains partially merged changes."]; F --> G{"Action: If <i>git stash pop</i> was used..."}; G --> H["<b>Important: The stash is NOT dropped automatically!</b>"]; H --> I["Manually inspect and resolve conflicts in each affected file."]; G -- "If <i>git stash apply</i> was used..." --> I; I --> J["Edit files to choose desired changes, remove conflict markers."]; J --> K["Once conflicts are resolved: <i>git add <resolved_file1> <resolved_file2> ...</i>"]; K --> L{"All conflicts resolved and staged?"}; L -- No --> I; L -- Yes --> M["Optional: <i>git commit</i> the resolved changes (often good practice).<br>Alternatively, continue working and commit later."]; M --> N{"Was the original command <i>git stash pop</i> (and stash still exists)?"}; N -- Yes --> O["Manually drop the stash: <i>git stash drop <stash_id></i>"]; O --> Z; N -- "No (was <i>apply</i> or stash already dropped)" --> Z; %% Styling classDef startNode fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6; classDef processNode fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF; classDef decisionNode fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E; classDef endNode fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46; classDef checkNode fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B; classDef infoNode fill:#E0E7FF,stroke:#4338CA,stroke-width:1px,color:#3730A3; class A startNode; class I,J,K,M,O processNode; class B,G,L,N decisionNode; class C,Z endNode; class D,E,F infoNode; class H checkNode;
Cleaning Your Working Directory with git clean
While git stash
deals with uncommitted changes to tracked files (and optionally untracked/ignored files), git clean
is specifically for removing untracked files from your working directory. Untracked files are those that have not been git add
ed and are not listed in your .gitignore
file (unless you explicitly tell git clean
to also remove ignored files).
Why Clean?
Over time, your working directory can accumulate various untracked files:
- Build artifacts (e.g.,
.o
files, compiled binaries,dist
folders). - Log files.
- Temporary files created by editors or tools.
- Files you downloaded or created experimentally.
These files can clutter your git status
output and potentially be accidentally committed. git clean
provides a way to get rid of them.
Warning:
git clean
is a destructive command. Files removed bygit clean
are not stored by Git anywhere (unlikegit stash
which saves changes). Once deleted, they are generally gone for good unless your OS or editor has a separate recovery mechanism. Always usegit clean
with caution.
Core Clean Operations:
git clean -n
orgit clean --dry-run
: This is the most important first step. It shows you which files would be removed without actually deleting anything. Always run this before an actual clean.git clean -f
orgit clean --force
: This command is required to actually delete the untracked files. Git enforces this to prevent accidental data loss.git clean -d
: By default,git clean
only removes untracked files. If you also want to remove untracked directories, you need to add the-d
option.git clean -x
: This tellsgit clean
to also remove files that are ignored by Git (i.e., files matching patterns in.gitignore
). Use this with extreme caution, as you might be ignoring important local configuration files.git clean -X
: Only remove files ignored by Git. This is useful for cleaning up build artifacts if your.gitignore
is set up to ignore them, but leaves other untracked files alone.git clean -i
orgit clean --interactive
: This presents an interactive interface allowing you to review and selectively choose which files or groups of files to delete. This is a much safer way to clean your directory if you’re unsure.
`git clean` Option | Example Usage | Description |
---|---|---|
Dry Run (-n or –dry-run) | git clean -n git clean -nd (incl. dirs) |
Shows which files/directories would be removed without actually deleting anything. Always run this first! |
Force (-f or –force) | git clean -f | Actually deletes the untracked files. Git requires this option to prevent accidental data loss. Use only after a dry run. |
Remove Directories (-d) | git clean -fd | In addition to untracked files, also removes untracked directories. Often used with -f. |
Remove Ignored Files Too (-x) | git clean -fdx | Removes untracked files AND files/directories that are normally ignored by Git (matching patterns in .gitignore). Use with EXTREME CAUTION. |
Remove Only Ignored Files (-X) | git clean -fX | Removes ONLY files/directories that are ignored by Git, leaving other untracked files untouched. Useful for cleaning build artifacts if .gitignore is well-configured. Use with caution. |
Interactive (-i or –interactive) | git clean -id | Presents an interactive interface, allowing you to review and selectively choose which files or groups of files to delete. A safer way to clean if you’re unsure. Can be combined with -d. |
Combining Options:
You often combine these options, for example:
git clean -nd
: Dry run, showing untracked files and directories that would be removed.git clean -fd
: Force remove untracked files and directories.git clean -fdx
: Force remove untracked files, directories, and ignored files (very aggressive).
[Insert diagram illustrating git clean
: A working directory with tracked files (green), modified tracked files (yellow), and untracked files (red). git clean
targets the red files.]
GUI Tools:
While this chapter focuses on CLI commands, many GUI tools like VS Code’s Git integration, GitKraken, or Sourcetree offer visual ways to manage stashes and sometimes to clean the working directory. Understanding the CLI commands, however, provides a deeper understanding of the underlying operations.
Git Stash vs Git Clean
Feature / Aspect | git stash |
git clean |
---|---|---|
Primary Purpose | Temporarily shelve uncommitted changes to tracked files. | Remove untracked files (and optionally directories) from the working directory. |
What it Affects |
Primarily modified tracked files (both staged and unstaged).
Optionally (-u): untracked files. Optionally (-a): untracked and ignored files. |
Primarily untracked files and directories.
Optionally (-x): ignored files. Optionally (-X): only ignored files. |
Safety & Data Integrity | Changes are saved in a stash list and can be reapplied. Relatively safe. | Deletes files permanently from the working directory. Potentially destructive if used carelessly. |
Recovery | Stashed changes can be listed (git stash list), applied (git stash apply), or popped (git stash pop). | No built-in Git recovery for cleaned files. Recovery relies on OS recycle bin (if applicable) or external backups. |
Typical Use Cases |
|
|
Key Commands | git stash push, git stash pop, git stash apply, git stash list | git clean -n (dry run), then git clean -f (force). Options like -d, -x, -i. |
Analogy | Putting your current work neatly on a temporary shelf to pick up later. | Tidying up your workspace by throwing away clutter that doesn’t belong. |
Practical Examples
Setup:
Let’s create a new repository and set up a scenario for practicing stashing and cleaning.
# Create and navigate to a new directory
mkdir git-stash-clean-practice
cd git-stash-clean-practice
# Initialize a Git repository
git init
# Configure user (if not set globally)
# git config user.name "Your Name"
# git config user.email "youremail@example.com"
# Create and commit some initial files
echo "Initial content for main file." > main.txt
git add main.txt
git commit -m "C1: Add main.txt"
echo "Feature A in progress" > feature_a.txt
git add feature_a.txt
git commit -m "C2: Start feature A"
# Now, let's create a messy working directory
# 1. Modify a tracked file
echo "Initial content for main file. With new updates." > main.txt
# 2. Stage another modification
echo "Feature A in progress - more work done" > feature_a.txt
git add feature_a.txt # Staged change
# 3. Create an untracked file
echo "This is a temporary log file." > temp.log
# 4. Create an untracked file in an untracked directory
mkdir build_artifacts
echo "Compiled output" > build_artifacts/output.bin
# 5. Create an ignored file
echo "*.log" > .gitignore
echo "Another log file, should be ignored." > ignored.log
git add .gitignore
git commit -m "Add .gitignore to ignore log files"
# Now, our temp.log is ignored. Let's create a new one that IS untracked.
echo "This is an untracked data file." > data.tmp
# Check status
git status
Expected git status
output (will vary slightly based on exact file states and Git version, but generally):
On branch main
Your branch is up to date with 'origin/main'. # Assuming no remote yet, or up-to-date
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: feature_a.txt
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: main.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
build_artifacts/
data.tmp
nothing added to commit but untracked files present (use "git add" to track)
Note:
ignored.log
andtemp.log
won’t show as untracked if.gitignore
is correctly processed and they match the ignore patterns.data.tmp
andbuild_artifacts/
are untracked.
1. Basic Stashing (git stash push
)
You need to switch branches, but your work on main.txt
(unstaged) and feature_a.txt
(staged) isn’t ready.
# Stash the current changes
git stash push -m "Work on main and feature_a"
Expected Output:
Saved working directory and index state On main: Work on main and feature_a
Now check your status:
git status
Expected Output:
On branch main
Untracked files:
(use "git add <file>..." to include in what will be committed)
build_artifacts/
data.tmp
nothing added to commit but untracked files present (use "git add" to track)
Your working directory is clean regarding tracked files! main.txt
and feature_a.txt
are back to their state at “C2: Start feature A”. The untracked files data.tmp
and build_artifacts/
are still there because the default stash doesn’t include them.
2. Listing Stashes (git stash list
)
git stash list
Expected Output:
stash@{0}: On main: Work on main and feature_a
This shows our stash, with stash@{0}
being the most recent.
3. Stashing with Untracked Files (git stash push -u
)
Let’s say you also want to stash data.tmp but not build_artifacts.
First, let’s make another change to a tracked file to have something new to stash.
echo "Another change to main.txt" >> main.txt
git status # Shows main.txt modified, data.tmp and build_artifacts untracked
Now, stash including untracked files:
git stash push -u -m "Updates to main.txt and untracked data.tmp"
Expected Output:
Saved working directory and index state On main: Updates to main.txt and untracked data.tmp
Check status:
git status
Expected Output:
On branch main
Untracked files:
(use "git add <file>..." to include in what will be committed)
build_artifacts/
nothing added to commit but untracked files present (use "git add" to track)
Now data.tmp
is also gone from the working directory (stashed). build_artifacts
is still there.
List stashes again:
git stash list
Expected Output:
stash@{0}: On main: Updates to main.txt and untracked data.tmp
stash@{1}: On main: Work on main and feature_a
stash@{0} is the new stash with untracked files.
4. Applying a Stash (`git stash apply`)
Let’s apply the first stash we made (`stash@{1}`), which contained changes to `main.txt` and `feature_a.txt`.
git stash apply stash@{1}
Expected Output (might include a note about conflicts if any, but unlikely here):
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: feature_a.txt
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: main.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
build_artifacts/
The changes from stash@{1}
are now back in your working directory and staging area, just as they were when you stashed them. data.tmp
is not restored because it was part of stash@{0}
.
Check the stash list:
git stash list
Expected Output:
stash@{0}: On main: Updates to main.txt and untracked data.tmp
stash@{1}: On main: Work on main and feature_a
stash@{1} is still there because `apply` doesn't remove it.
Tip: If you had conflicting changes in your working directory before applying, Git would attempt a merge and might report conflicts.
5. Popping a Stash (git stash pop
)
Let’s assume the changes from stash@{1}
are now good. We could commit them.
But first, let’s reset the working directory to HEAD to demonstrate pop
cleanly.
git reset --hard HEAD # Careful: discards current uncommitted changes
# Our working dir is now clean (back to C2 state + .gitignore)
# Untracked files (build_artifacts/) might still be there.
Now, let’s pop the most recent stash (stash@{0}
), which included main.txt
changes and data.tmp
.
git stash pop stash@{0} # You can omit stash@{0} to pop the latest
Expected Output:
On branch main
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: main.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
build_artifacts/
data.tmp
Dropped stash@{0} (e0f4e222f9c6...) # Hash will vary
The changes from stash@{0} (modified main.txt and the untracked data.tmp) are restored.
And stash@{0} has been removed from the list:
git stash list
Expected Output:
stash@{1}: On main: Work on main and feature_a
Only stash@{1}
remains. (Note: The index stash@{1}
now becomes stash@{0}
effectively, as it’s the top of the stack).
6. Dropping a Stash (git stash drop
)
We decided we don’t need stash@{1}
(which is now stash@{0}
) anymore.
git stash drop stash@{0} # Or just 'git stash drop' if it's the latest
Expected Output:
Dropped stash@{0} (abcdef123...) # Hash will vary
Check the list:
git stash list
Expected Output: (empty, or an error saying no stashes found)
# No output, or a message like "No stashes found."
All stashes are gone.
7. Cleaning the Working Directory (git clean
)
Our git status
currently shows:
On branch main
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: main.txt # From the last pop
Untracked files:
(use "git add <file>..." to include in what will be committed)
build_artifacts/
data.tmp
Let’s commit the changes to main.txt
to have a cleaner state for git clean
.
git add main.txt
git commit -m "Incorporate stashed changes into main.txt"
git status # Should show only untracked files now
a. Dry Run (git clean -n)
First, see what git clean would do:
git clean -n
Expected Output:
Would remove data.tmp
Notice it doesn’t mention build_artifacts/
yet, because by default clean
doesn’t look into directories.
b. Dry Run with Directories (git clean -nd
)
git clean -nd
Expected Output:
Would remove build_artifacts/
Would remove data.tmp
This shows it would remove the untracked data.tmp
file and the untracked build_artifacts
directory.
c. Force Clean Files and Directories (git clean -fd)
Warning: This will delete files! Make sure you’re okay with removing what the dry run showed.
git clean -fd
Expected Output (will vary if you have other untracked files/dirs):
Removing data.tmp
Removing build_artifacts/
Now check git status
:
git status
Expected Output:
On branch main
nothing to commit, working tree clean
Your working directory is now free of untracked files and directories.
d. Interactive Clean (git clean -id)
Let’s create some more untracked files to demonstrate interactive cleaning.
echo "temp file 1" > temp1.txt
echo "temp file 2" > temp2.txt
mkdir temp_dir
echo "content" > temp_dir/temp3.txt
git status # Shows temp1.txt, temp2.txt, temp_dir/ as untracked
Now, run interactive clean (including directories):
git clean -id
Expected Output (an interactive prompt):
Would remove the following items:
temp1.txt temp2.txt temp_dir/
*** Commands ***
1: clean 2: filter by pattern 3: select by numbers
4: ask each 5: quit 6: help
What now>
You can type 1 to clean all listed, 4 to be asked for each item, or select specific items.
For example, type 4 (ask each):
What now> 4
Remove temp1.txt ([y]es/[n]o/[q]uit/[a]ll/[d]one/[?]help)? y
Remove temp2.txt ([y]es/[n]o/[q]uit/[a]ll/[d]one/[?]help)? n
Remove temp_dir/ ([y]es/[n]o/[q]uit/[a]ll/[d]one/[?]help)? y
Removing temp1.txt
Removing temp_dir/
temp1.txt and temp_dir/ would be removed, but temp2.txt would remain.
OS-Specific Notes
- Path Syntax: Git CLI generally uses forward slashes (
/
) for paths, even on Windows where the native path separator is a backslash (\
). PowerShell often handles this gracefully, but in CMD on Windows, you might need to be careful or use Git Bash which provides a Unix-like environment. - Line Endings: While not directly related to
stash
orclean
functionality, remember that Git handles line endings (CRLF
on Windows,LF
on macOS/Linux). If you stash files and apply them on a different OS or with differentcore.autocrlf
settings, you might see entire files marked as modified. This is a general Git consideration. git clean
and File Permissions/Locks:- Windows: If an untracked file is currently locked by another process (e.g., an open Word document, a running executable),
git clean -f
might fail to delete it, reporting a permission error. You’ll need to close the locking program. - macOS/Linux: If Git doesn’t have write permission in a directory or on a specific untracked file,
git clean -f
might fail. You might need to adjust permissions or run the command with sufficient privileges (though runninggit
as root is generally discouraged for regular operations).
- Windows: If an untracked file is currently locked by another process (e.g., an open Word document, a running executable),
- Case Sensitivity: macOS can be configured with a case-insensitive filesystem (default) or a case-sensitive one. Linux typically uses case-sensitive filesystems. Windows is generally case-insensitive but case-preserving. This can sometimes lead to subtle issues with untracked files if naming differs only by case, though
git clean
usually handles whatgit status
reports as untracked.
Common Mistakes & Troubleshooting Tips
Git Issue / Error | Symptom(s) | Troubleshooting / Solution |
---|---|---|
git stash apply vs. pop misuse | git stash list shows many similar/identical stashes. Stash not removed after applying. |
Remember: apply keeps the stash.
|
Accidental Deletion with git clean -f | Important untracked files are permanently deleted from the working directory. |
Prevention is key:
|
Stash Doesn’t Include Untracked Files | Ran git stash (or git stash push), but untracked files remain in the working directory. |
By default, stash only affects tracked files.
|
Stash Conflicts on pop or apply | Git reports merge conflicts (e.g., <<<<<<< markers) in files. If pop was used, the stash is not automatically dropped. |
|
Forgetting Stash Messages | git stash list shows several entries like “WIP on branchname: …” making it hard to distinguish them. |
Use descriptive messages when stashing:
|
git clean not removing directories | Ran git clean -f but untracked directories are still present. |
By default, git clean only removes files.
|
git clean removing ignored files unintentionally | Ran git clean -fx and it removed important local configuration files that were in .gitignore. |
The -x option tells git clean to also remove files that are ignored by Git.
|
Exercises
Use the git-stash-clean-practice
repository. You might need to reset its state or create new files/changes for each exercise.
- Context Switching with Stash:
- On your
main
branch, modifymain.txt
and create a new untracked filenew_feature_notes.txt
. - Stage the changes to
main.txt
but leavenew_feature_notes.txt
untracked. - You need to quickly switch to a new branch called
hotfix
(create it frommain
). Stash your current work (including the untracked notes file) with a message “Feature X work in progress”. - On the
hotfix
branch, make a trivial change tomain.txt
(e.g., add a comment) and commit it. - Switch back to the
main
branch. - Apply your stashed changes. Verify that
main.txt
has your staged changes andnew_feature_notes.txt
is present. - What happens to the commit you made on the
hotfix
branch regardingmain.txt
? How does Git handle applying the stashed changes tomain.txt
? (Hint: Think about the base of the stash). - Assuming the applied changes are good, remove the stash from the list.
- On your
- Cleaning Up a Build Directory:
- Create the following untracked files and directories:
build/app.exe
build/lib.dll
logs/debug.log
temp_report.pdf
- Add
*.log
and*.pdf
to your.gitignore
file and commit it. - Modify
main.txt
but don’t stage or commit it. - Use
git clean
in dry-run mode to see what would be removed if you wanted to clean only files (not directories) that are not ignored. - Use
git clean
in dry-run mode to see what would be removed if you wanted to clean all untracked files and directories, excluding ignored files. - Use
git clean
in dry-run mode to see what would be removed if you wanted to clean all untracked files and directories, including ignored files. - Interactively clean the directory, choosing to remove the
build/
directory andtemp_report.pdf
, but keeplogs/debug.log
(even if it’s ignored, you might choose to keep it in the interactive step if not using-x
). What happens to your modifiedmain.txt
?
- Create the following untracked files and directories:
- Managing Multiple Stashes:
- Modify
feature_a.txt
and stash the change with the message “Stash 1: Feature A tweak”. - Modify
main.txt
and create a new untracked fileconfig.yml
. Stash these changes (including the untracked file) with the message “Stash 2: Main update and config”. - Modify
feature_a.txt
again with a different change. Stash this with the message “Stash 3: Another Feature A idea”. - List your stashes.
- Apply “Stash 2” (the one with
main.txt
andconfig.yml
) without removing it from the list. - Pop “Stash 1” (the first Feature A tweak). Resolve any conflicts if they occur (e.g., if the change from Stash 1 conflicts with the current state of
feature_a.txt
if it was somehow modified by Stash 2 – unlikely in this setup but good to think about). - Drop “Stash 3” without applying it.
- Verify the final state of your files and the stash list.
- Modify
Summary
git stash
(orgit stash push
): Saves uncommitted changes (staged and unstaged in tracked files) to a temporary area, cleaning the working directory.git stash push -m "message"
: Stash with a descriptive message.git stash push -u
orgit stash -u
: Includes untracked files.git stash push -a
orgit stash -a
: Includes all untracked and ignored files.
git stash list
: Shows all current stashes.git stash apply [<stash>]
: Applies a stash’s changes but keeps the stash in the list.git stash pop [<stash>]
: Applies a stash’s changes and removes it from the list (if no conflicts).git stash drop [<stash>]
: Deletes a stash from the list.git stash clear
: Deletes all stashes.git clean
: Removes untracked files from the working directory. Use with caution.git clean -n
or--dry-run
: Preview files to be deleted. Always use first.git clean -f
or--force
: Required to actually delete files.git clean -d
: Also remove untracked directories.git clean -x
: Also remove ignored files.git clean -i
or--interactive
: Interactively choose which files to delete.
Stashing is invaluable for context switching and keeping a clean working state without making premature commits. Cleaning helps manage build artifacts and other non-project files. Both contribute to a more organized and efficient Git workflow.
Further Reading
- Pro Git Book:
- Chapter 7.3 Git Tools – Stashing and Cleaning: https://git-scm.com/book/en/v2/Git-Tools-Stashing-and-Cleaning
- Official Git Documentation:
git-stash(1)
: https://git-scm.com/docs/git-stashgit-clean(1)
: https://git-scm.com/docs/git-clean
- Atlassian Git Tutorials –
git stash
: https://www.atlassian.com/git/tutorials/saving-changes/git-stash
