Reputation: 16197
I would like to use this workflow:
Is there a way to do step 2?
Example:
git init
echo one >file
git add file
git commit
echo two >>file
git add file
echo three >>file
git stash push
test
git commit
git stash pop
Upvotes: 425
Views: 123385
Reputation: 19585
--keep-index
/ -k
Stashing just the working tree (unstaged changes) in Git is more difficult than it should be. The accepted answer, and quite a few other answers, stashes the unstaged changes and leaves the stage alone as requested via --keep-index
.
However what isn't obvious is that --keep-index
also stashes the staged changes. The staged changes end up in both the stage AND the stash. This is rarely what one wants because any interim changes to the stash are likely to result in conflicts when popping the stash later.
This alias works well to stage just the working copy changes:
stash-working = "!f() { \
git commit --quiet --no-verify -m \"temp for stash-working\" && \
git stash push \"$@\" && \
git reset --quiet --soft HEAD~1; }; f"
It commits the staged changes temporarily, creates a stash from the remaining changes (and allows additional arguments such as --include-untracked
and --message
to be passed as alias arguments), and then resets the temporary commit to get back the staged changes.
It is similar to @Simon Knapp's answer, but with a few minor differences -- it uses --quiet
on the temporary actions taken, and it accepts any number of parameters for the stash push
, rather than hard-coding the -m
, and it does add --soft
to the final reset so that the index remains as it started. It also uses --no-verify
on the commit to avoid changes to the working copy from pre-commit hooks (HT: @Granfalloner).
For the opposite problem of stashing just the staged changes (alias stash-index
) see this answer.
Upvotes: 38
Reputation: 1640
This may be done in 3 steps: save staged changes, stash everything else, restore index with staged changes. Which is basically:
git commit -m 'Save index'
git stash push -u -m 'Unstaged changes and untracked files'
git reset --soft HEAD^
This will do exactly what you want.
If you do not wish to stash untracked files, you can use
git commit -m 'Save index'
git stash push -m 'Unstaged changes'
git reset --soft HEAD^
Neither version affects gitignored files.
Upvotes: 96
Reputation: 1136
From Git 2.35+ (Q1 2022) you can now use the --staged
flag (man) on git stash push
to only stash the changes in your index.
Since your question asks the exact opposite, we have 2 choices:
git stash push --staged # Stash staged changes
git stash # Stash everything else
git stash pop stash@{1} # Restore staged changes stash
git stash push --staged
I got this information from this answer on another S/O post.
Upvotes: 75
Reputation: 25113
TL;DR;
Since at git 2.35 flag was implemented: git stash [push [-S|--staged]]
, you can do:
git stashu
after adding this alias:
git config --global alias.stashu '!git stash push -S; git stash; git stash pop --index stash@{1}'"
Explanation:
git stash push -S # Stash staged changes
git stash # Stash everything else
git stash pop --index stash@{1} # Restore staged changes into index
$ git config --global alias.stashu '!git stash push -S; git stash; git stash pop --index stash@{1}'
$ git diff
```diff
diff --git a/src/js/modal.js b/src/js/modal.js
index d07c085..766e39a 100644
--- a/src/js/modal.js
+++ b/src/js/modal.js
@@ -6,10 +6,12 @@
import "jquery-validation/dist/jquery.validate";
import "bootstrap/dist/js/bootstrap.bundle";
+staged
const FormDataJson = require('form-data-json-convert');
FormDataJson.defaultOptionsToJson.uncheckedValue = false;
FormDataJson.defaultOptionsToJson.skipEmpty = true;
+unstaged
import { TabulatorFull as Tabulator } from 'tabulator-tables/dist/js/tabulator';
import { ajax_query } from "./common/ajax";
```
$ git add -p
```diff
@@ -6,7 +6,8 @@
import "jquery-validation/dist/jquery.validate";
import "bootstrap/dist/js/bootstrap.bundle";
+staged
const FormDataJson = require('form-data-json-convert');
FormDataJson.defaultOptionsToJson.uncheckedValue = false;
FormDataJson.defaultOptionsToJson.skipEmpty = true;
```
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? y
```diff
@@ -9,7 +10,8 @@
const FormDataJson = require('form-data-json-convert');
FormDataJson.defaultOptionsToJson.uncheckedValue = false;
FormDataJson.defaultOptionsToJson.skipEmpty = true;
+unstaged
import { TabulatorFull as Tabulator } from 'tabulator-tables/dist/js/tabulator';
import { ajax_query } from "./common/ajax";
```
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? n
$ git status
On branch dev-Modal
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: modal.js
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: modal.js
$ git diff
```diff
diff --git a/src/js/modal.js b/src/js/modal.js
index ac48408..766e39a 100644
--- a/src/js/modal.js
+++ b/src/js/modal.js
@@ -11,6 +11,7 @@ const FormDataJson = require('form-data-json-convert');
FormDataJson.defaultOptionsToJson.uncheckedValue = false;
FormDataJson.defaultOptionsToJson.skipEmpty = true;
+unstaged
import { TabulatorFull as Tabulator } from 'tabulator-tables/dist/js/tabulator';
import { ajax_query } from "./common/ajax";
```
$ git stashu
Saved working directory and index state WIP on dev-Modal: 9f6e760 Imported Tabulator module
Saved working directory and index state WIP on dev-Modal: 9f6e760 Imported Tabulator module
On branch dev-Modal
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: src/js/modal.js
Dropped stash@{1} (509be8cf1afa340faf77aa6a2b8d008aa82a980a)
$ git stash show -p
```diff
diff --git a/src/js/modal.js b/src/js/modal.js
index d07c085..ed509ed 100644
--- a/src/js/modal.js
+++ b/src/js/modal.js
@@ -10,6 +10,7 @@ const FormDataJson = require('form-data-json-convert');
FormDataJson.defaultOptionsToJson.uncheckedValue = false;
FormDataJson.defaultOptionsToJson.skipEmpty = true;
+unstaged
import { TabulatorFull as Tabulator } from 'tabulator-tables/dist/js/tabulator';
import { ajax_query } from "./common/ajax";
```
**BONUS for those who read till here. The alias to stash staged changes:
```
$ git config --global alias.stashs 'stash push -S'
```
And now you can `stash staged`:
```
git stashs
```
With git version 2.7.4
you may use the --patch
option:
git stash save --patch
Then git
will ask you to add or not your changes into stash.
And you then just answer y
or n
.
You can restore the working directory as you always do like:
git stash pop
or, if you want to keep saved changes in stash:
git stash apply
Upvotes: 33
Reputation: 13907
git stash push
has an option --keep-index
that does exactly what you need, so run:
git stash push --keep-index
Upvotes: 481
Reputation: 1021
To add the unstagged (not added to commit) files to stash, run the following command:
git stash -k
If you want to include newly added files(which are not staged - not in green) also to the stash, do the following:
git stash -k -u
Then you can commit the staged files. After that you can get back the last stashed files using the command:
git stash pop
Upvotes: 25
Reputation: 5520
Rethink: It is not necessary to limit the stash data to the working tree changes only, but it can be decided later at apply time to only apply the working tree changes of the stash.
Thus at stash time just do as usual:
git stash [-k|--keep-index]
At apply time do
git cherry-pick -m2 -n stash
Explanation: The -m2
selects the changes to the 2nd parent of the stage commit, which is the stored index state. -n|--no-commit
blocks auto-commit. stash@{1}
would be the ref for the 2nd stash on the stack ...
Upvotes: 0
Reputation: 910
I was interested in how the Python program pre-commit
does it. Here is the code.
https://github.com/pre-commit/pre-commit/blob/3fe38dff05957f609cf7b97f471b35a8d9e0659a/pre_commit/staged_files_only.py#L50
It is functionally equivalent to something like:
git diff-index --ignore-submodules --binary --exit-code --no-color --no-ext-diff $(git write-tree) -- >stash.patch
git checkout -- .
# Do stuff now
git apply stash.patch && rm stash.patch
Upvotes: 0
Reputation: 1323363
2022: I mention in "Stashing only staged changes in git - is it possible?", Git 2.35 (Q1 2022) comes with "git stash push --staged
"(man):
This option is only valid for push and save commands.
Stash only the changes that are currently staged.
This is similar to basicgit commit
except the state is committed to the stash instead of current branch.
2019: The modern form of that command is git stash push [--] [<pathspec>...]
, since Git 2.16+ (git stash save
is deprecated)
You can combine that with a wildcard form, for example:
git stash push --all --keep-index ':(glob)**/*.testextension'
But that does not work well with Git for Windows, until Git 2.22 (Q2 2019), see issue 2037, considering git stash
has been re-implemented in C (instead of a shell script)
See commit 7db9302 (11 Mar 2019) by Thomas Gummerer (tgummerer
).
See commit 1366c78, commit 7b556aa (07 Mar 2019) by Johannes Schindelin (dscho
).
(Merged by Junio C Hamano -- gitster
-- in commit 0ba1ba4, 22 Apr 2019)
built-in
stash
: handle:(glob)
pathspecs againWhen passing a list of pathspecs to, say,
git add
, we need to be careful to use the original form, not the parsed form of the pathspecs.This makes a difference e.g. when calling
git stash -- ':(glob)**/*.txt'
where the original form includes the
:(glob)
prefix while the parsed form does not.However, in the built-in
git stash
, we passed the parsed (i.e. incorrect) form, andgit add
would fail with the error message:fatal: pathspec '**/*.txt' did not match any files
at the stage where
git stash
drops the changes from the worktree, even ifrefs/stash
has been actually updated successfully.
Upvotes: 3
Reputation: 674
Here's (in my opinion) the best solution, that does exactly what the OP has asked for. It stashes only the unstaged, tracked files – without an unnecessary commit or stashing all changed files with --keep-index
It lists all the unstaged, tracked changes (git diff --name-only
) converts newlines to spaces (| tr '\n' ' '
) and stashes all these files using git stash push
:
git stash push $(git diff --name-only | tr '\n' ' ')
Upvotes: 1
Reputation: 8027
To my knowledge, it is currently impossible to save only unstaged changes in the working tree with git stash push
, i.e. to save changes from the index state. This command saves all changes in the working tree (staged and unstaged changes), i.e. changes from the HEAD state, even with the option --keep-index
which also sets the working tree state to the index state instead of the HEAD state (thereby creating conflicts when restoring the changes from the HEAD state with git stash pop
). It would be very convenient if git stash push
had an option -U|--unstaged
for saving only unstaged changes (to me the option --keep-index
is flawed), since it has already an option -S|--staged
for saving only staged changes.
So for the moment you have to emulate
git stash push --unstaged
git stash pop
with a temporary file:
git diff >unstaged
git restore .
git apply unstaged
rm unstaged
Your use case is testing before committing partial changes and it is already in the reference documentation, but with the flawed option --keep-index
which creates conflicts. Here is the version with the emulated option -U|--unstaged
:
git init
echo one >file
git add file
git commit
echo two >>file
git add file
echo three >>file
git diff >unstaged
git restore .
test
git commit
git apply unstaged
rm unstaged
For a better understanding of stashing, I think it is important to look at the states of the working tree, index and HEAD at each step. Let’s take your use case.
git init
working | index | HEAD |
---|
echo one >file
working | index | HEAD |
---|---|---|
one |
git add file
working | index | HEAD |
---|---|---|
one | one |
git commit
working | index | HEAD |
---|---|---|
one | one | one |
echo two >>file
working | index | HEAD |
---|---|---|
one | one | one |
two |
git add file
working | index | HEAD |
---|---|---|
one | one | one |
two | two |
echo three >>file
working | index | HEAD |
---|---|---|
one | one | one |
two | two | |
three |
git diff >unstaged
git restore .
working | index | HEAD |
---|---|---|
one | one | one |
two | two |
test
git commit
working | index | HEAD |
---|---|---|
one | one | one |
two | two | two |
git apply unstaged
rm unstaged
working | index | HEAD |
---|---|---|
one | one | one |
two | two | two |
three |
Upvotes: -1
Reputation: 303
I use an an alias, which accepts a string to use as a message to the stash entry.
mystash = "!f() { git commit -m hold && git stash push -m \"$1\" && git reset HEAD^; }; f"
Which:
-u
or -a
),--soft
to keep it in the index).Upvotes: 3
Reputation: 126319
Git doesn't have a command that stashes only your unstaged changes.
Git does, however, let you specify which files you want to stash.
git stash push --message 'Unstaged changes' -- app/controllers/products_controller.rb test/controllers/products_controller_test.rb
If you only want to stash specific changes in those files, add the --patch
option.
git stash push --patch --message 'Unstaged changes' -- app/controllers/products_controller.rb test/controllers/products_controller_test.rb
The --include-untracked
option lets you stash untracked files.
git stash push --include-untracked --message 'Untracked files' -- app/controllers/widgets_controller.rb test/controllers/widgets_controller_test.rb
Run git help stash
(or man git-stash
) for more info.
Note: If your unstaged changes are rather disoganized, @alesguzik's answer is probably easier.
Upvotes: 7
Reputation: 4388
Another tip, related to the question:
When you effectively stash your unstaged changes using
$ git stash save --keep-index
you might wish to give the stash a message, so that when you to do a git stash list
it's more obvious what you have stashed before, especially if you follow that stash operation by further saves. For example
$ git stash save --keep-index "changes not yet staged"
(although actually it does contain all the changes as noted in other answers).
For example, the above might be followed immediately by:
$ git stash save "staged changes for feature X"
Beware, though, that you can't then use
$ git stash apply "stash@{1}" ### ✘ doesn't quite do what you might want
to restore just the unstaged changes.
Upvotes: 3
Reputation: 4388
Extending previous answers, I sometimes have a complex set of changes staged, but wish to commit a separate change first. For example, I might have spotted a bug or otherwise incorrect code that I'd like to fix ahead of my staged changes. One possible route to take is this:
first stash everything, but leave the staged changes intact
$ git stash save --keep-index [--include-untracked]
now stash the staged changes separately too
$ git stash save
make changes for fix; and test; commit them:
$ git add [--interactive] [--patch]
$ git commit -m"fix..."
now restore the previously staged changes:
$ git stash pop
resolve any conflicts, and note that if there were conflicts, git will have applied but not dropped that top stash entry.
(... Then commit the staged changes, and restore the stash of all the other changes, and continue ...)
Upvotes: 6
Reputation: 392863
git stash save --keep-index
Also, Re:
Why not commit your changes after staging them? – Shin
A: Because you should always checkin tested code :) That means, you need to run the tests with only the changes you are about to commit
All this apart from the fact that of course, as an experienced programmer, you have the innate urge to test and review just those changes -- only partly kidding
Upvotes: 39