From: Denton Liu <hidden> Date: 2021-02-02 09:34:35
A blindspot that I've noticed in git is that it's not possible to
properly view a stash entry that has untracked files via `git stash
show`. Teach `git stash show --include-untracked` which should do this.
In addition, this series also teaches `--only-untracked` and the
`stash.showIncludeUntracked` config option.
The first seven patches of this series are just some clean up that I've
done prior to working (because it bothers me). The remaining two patches
should be the meat of the change.
Denton Liu (9):
git-stash.txt: be explicit about subcommand options
t3905: remove spaces after redirect operators
t3905: move all commands into test cases
t3905: remove nested git in command substitution
t3905: replace test -s with test_file_not_empty
t3905: use test_cmp() to check file contents
stash: declare ref_stash as an array
stash show: teach --include-tracked and --only-untracked
stash show: learn stash.showIncludeUntracked
Documentation/config/stash.txt | 5 +
Documentation/git-stash.txt | 22 +-
builtin/stash.c | 30 ++-
contrib/completion/git-completion.bash | 2 +-
t/t3905-stash-include-untracked.sh | 278 +++++++++++++++++--------
5 files changed, 235 insertions(+), 102 deletions(-)
--
2.30.0.478.g8a0d178c01
From: Denton Liu <hidden> Date: 2021-02-02 09:34:54
Currently, the options for the `list` and `show` subcommands are just
listed as `<options>`. This seems to imply, from a cursory glance at the
summary, that they take the stash options listed below. However, reading
more carefully, we see that they take log options and diff options
respectively.
Make it more obvious that they take log and diff options by explicitly
stating this in the subcommand summary.
Signed-off-by: Denton Liu <redacted>
---
Documentation/git-stash.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
@@ -67,7 +67,7 @@ save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q Instead, all non-option arguments are concatenated to form the stash message.-list [<options>]::+list [<log options>]:: List the stash entries that you currently have. Each 'stash entry' is listed with its name (e.g. `stash@{0}` is the latest entry, `stash@{1}` is
@@ -83,7 +83,7 @@ stash@{1}: On master: 9cc0589... Add git-stash The command takes options applicable to the 'git log' command to control what is shown and how. See linkgit:git-log[1].-show [<options>] [<stash>]::+show [<diff options>] [<stash>]:: Show the changes recorded in the stash entry as a diff between the stashed contents and the commit back when the stash entry was first
From: Denton Liu <hidden> Date: 2021-02-02 09:36:10
If a git command in a nested command substitution fails, it will be
silently ignored since only the return code of the outer command
substitutions is reported. Factor out nested command substitutions so
that the error codes of those commands are reported.
Signed-off-by: Denton Liu <redacted>
---
t/t3905-stash-include-untracked.sh | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
@@ -36,8 +36,10 @@ test_expect_success 'stash save --include-untracked cleaned the untracked files'' test_expect_success'stash save --include-untracked stashed the untracked files''-tracked=$(gitrev-parse--short$(echo1|githash-object--stdin))&&-untracked=$(gitrev-parse--short$(echountracked|githash-object--stdin))&&+one_blob=$(echo1|githash-object--stdin)&&+tracked=$(gitrev-parse--short"$one_blob")&&+untracked_blob=$(echountracked|githash-object--stdin)&&+untracked=$(gitrev-parse--short"$untracked_blob")&&cat>expect.diff<<-EOF&&diff--gita/HEADb/HEADnewfilemode100644
@@ -117,7 +119,8 @@ test_expect_success 'stash save -u dirty index' '' test_expect_success'stash save --include-untracked dirty index got stashed''-blob=$(gitrev-parse--short$(echo4|githash-object--stdin))&&+four_blob=$(echo4|githash-object--stdin)&&+blob=$(gitrev-parse--short"$four_blob")&&cat>expect<<-EOF&&diff--gita/file3b/file3newfilemode100644
From: Denton Liu <hidden> Date: 2021-02-02 09:36:38
For shell scripts, the usual convention is for there to be no space
after redirection operators, (e.g. `>file`, not `> file`). Remove these
spaces wherever they appear.
Signed-off-by: Denton Liu <redacted>
---
t/t3905-stash-include-untracked.sh | 40 +++++++++++++++---------------
1 file changed, 20 insertions(+), 20 deletions(-)
@@ -8,16 +8,16 @@ test_description='Test git stash --include-untracked' ../test-lib.sh test_expect_success'stash save --include-untracked some dirty working directory''-echo1>file&&+echo1>file&&gitaddfile&&test_tick&&gitcommit-minitial&&-echo2>file&&+echo2>file&&gitaddfile&&-echo3>file&&+echo3>file&&test_tick&&-echo1>file2&&-echo1>HEAD&&+echo1>file2&&+echo1>HEAD&&mkdiruntracked&&echountracked>untracked/untracked&&gitstash--include-untracked&&
@@ -25,7 +25,7 @@ test_expect_success 'stash save --include-untracked some dirty working directorygitdiff-index--cached--quietHEAD'-cat>expect<<EOF+cat>expect<<EOF ??actual ??expect EOF
@@ -37,7 +37,7 @@ test_expect_success 'stash save --include-untracked cleaned the untracked files'tracked=$(gitrev-parse--short$(echo1|githash-object--stdin))untracked=$(gitrev-parse--short$(echountracked|githash-object--stdin))-cat>expect.diff<<EOF+cat>expect.diff<<EOF diff--gita/HEADb/HEAD newfilemode100644 index0000000..$tracked
@@ -60,7 +60,7 @@ index 0000000..$untracked @@-0,0+1@@ +untracked EOF-cat>expect.lstree<<EOF+cat>expect.lstree<<EOF HEAD file2 untracked
@@ -105,14 +105,14 @@ test_expect_success 'stash pop after save --include-untracked leaves files untra gitclean--force--quiet-d test_expect_success'stash save -u dirty index''-echo4>file3&&+echo4>file3&&gitaddfile3&&test_tick&&gitstash-u'blob=$(gitrev-parse--short$(echo4|githash-object--stdin))-cat>expect<<EOF+cat>expect<<EOF diff--gita/file3b/file3 newfilemode100644 index0000000..$blob
@@ -128,12 +128,12 @@ test_expect_success 'stash save --include-untracked dirty index got stashed' 'test_cmpexpectactual'-gitreset>/dev/null+gitreset>/dev/null# Must direct output somewhere where it won't be considered an untracked file test_expect_success'stash save --include-untracked -q is quiet''-echo1>file5&&-gitstashsave--include-untracked--quiet>.git/stash-output.out2>&1&&+echo1>file5&&+gitstashsave--include-untracked--quiet>.git/stash-output.out2>&1&&test_line_count=0.git/stash-output.out&&rm-f.git/stash-output.out'
@@ -141,7 +141,7 @@ test_expect_success 'stash save --include-untracked -q is quiet' ' test_expect_success'stash save --include-untracked removed files''rm-ffile&&gitstashsave--include-untracked&&-echo1>expect&&+echo1>expect&&test_cmpexpectfile'
@@ -169,7 +169,7 @@ test_expect_success 'stash save --include-untracked respects .gitignore' '' test_expect_success'stash save -u can stash with only untracked files different''-echo4>file4&&+echo4>file4&&gitstash-u&&test_path_is_missingfile4'
From: Denton Liu <hidden> Date: 2021-02-02 09:37:00
In order to modernize the tests, move commands that currently run
outside of test cases into a test case. Where possible, clean up files
that are produced using test_when_finished() but in the case where files
persist over multiple test cases, create a new test case to perform
cleanup.
Signed-off-by: Denton Liu <redacted>
---
t/t3905-stash-include-untracked.sh | 147 +++++++++++++++--------------
1 file changed, 75 insertions(+), 72 deletions(-)
@@ -25,48 +25,48 @@ test_expect_success 'stash save --include-untracked some dirty working directorygitdiff-index--cached--quietHEAD'-cat>expect<<EOF-??actual-??expect-EOF- test_expect_success'stash save --include-untracked cleaned the untracked files''+cat>expect<<-EOF&&+??actual+??expect+EOF+gitstatus--porcelain>actual&&test_cmpexpectactual'-tracked=$(gitrev-parse--short$(echo1|githash-object--stdin))-untracked=$(gitrev-parse--short$(echountracked|githash-object--stdin))-cat>expect.diff<<EOF-diff--gita/HEADb/HEAD-newfilemode100644-index0000000..$tracked----/dev/null-+++b/HEAD-@@-0,0+1@@-+1-diff--gita/file2b/file2-newfilemode100644-index0000000..$tracked----/dev/null-+++b/file2-@@-0,0+1@@-+1-diff--gita/untracked/untrackedb/untracked/untracked-newfilemode100644-index0000000..$untracked----/dev/null-+++b/untracked/untracked-@@-0,0+1@@-+untracked-EOF-cat>expect.lstree<<EOF-HEAD-file2-untracked-EOF- test_expect_success'stash save --include-untracked stashed the untracked files''+tracked=$(gitrev-parse--short$(echo1|githash-object--stdin))&&+untracked=$(gitrev-parse--short$(echountracked|githash-object--stdin))&&+cat>expect.diff<<-EOF&&+diff--gita/HEADb/HEAD+newfilemode100644+index0000000..$tracked+---/dev/null++++b/HEAD+@@-0,0+1@@++1+diff--gita/file2b/file2+newfilemode100644+index0000000..$tracked+---/dev/null++++b/file2+@@-0,0+1@@++1+diff--gita/untracked/untrackedb/untracked/untracked+newfilemode100644+index0000000..$untracked+---/dev/null++++b/untracked/untracked+@@-0,0+1@@++untracked+EOF+cat>expect.lstree<<-EOF&&+HEAD+file2+untracked+EOF+test_path_is_missingfile2&&test_path_is_missinguntracked&&test_path_is_missingHEAD&&
@@ -83,18 +83,21 @@ test_expect_success 'stash save --patch --all fails' 'test_must_failgitstash--patch--all'-gitclean--force--quiet+test_expect_success'clean up untracked/untracked file to prepare for next tests''+gitclean--force--quiet-cat>expect<<EOF-Mfile-??HEAD-??actual-??expect-??file2-??untracked/-EOF+' test_expect_success'stash pop after save --include-untracked leaves files untracked again''+cat>expect<<-EOF&&+Mfile+??HEAD+??actual+??expect+??file2+??untracked/+EOF+gitstashpop&&gitstatus--porcelain>actual&&test_cmpexpectactual&&
@@ -102,7 +105,9 @@ test_expect_success 'stash pop after save --include-untracked leaves files untratestuntracked="$(catuntracked/untracked)"'-gitclean--force--quiet-d+test_expect_success'clean up untracked/ directory to prepare for next tests''+gitclean--force--quiet-d+' test_expect_success'stash save -u dirty index''echo4>file3&&
@@ -111,25 +116,24 @@ test_expect_success 'stash save -u dirty index' 'gitstash-u'-blob=$(gitrev-parse--short$(echo4|githash-object--stdin))-cat>expect<<EOF-diff--gita/file3b/file3-newfilemode100644-index0000000..$blob----/dev/null-+++b/file3-@@-0,0+1@@-+4-EOF- test_expect_success'stash save --include-untracked dirty index got stashed''+blob=$(gitrev-parse--short$(echo4|githash-object--stdin))&&+cat>expect<<-EOF&&+diff--gita/file3b/file3+newfilemode100644+index0000000..$blob+---/dev/null++++b/file3+@@-0,0+1@@++4+EOF+gitstashpop--index&&+test_when_finished"git reset"&&gitdiff--cached>actual&&test_cmpexpectactual'-gitreset>/dev/null-# Must direct output somewhere where it won't be considered an untracked file test_expect_success'stash save --include-untracked -q is quiet''echo1>file5&&
@@ -142,23 +146,22 @@ test_expect_success 'stash save --include-untracked removed files' 'rm-ffile&&gitstashsave--include-untracked&&echo1>expect&&+test_when_finished"rm -f expect"&&test_cmpexpectfile'-rm-fexpect- test_expect_success'stash save --include-untracked removed files got stashed''gitstashpop&&test_path_is_missingfile'-cat>.gitignore<<EOF-.gitignore-ignored-ignored.d/-EOF- test_expect_success'stash save --include-untracked respects .gitignore''+cat>.gitignore<<-EOF&&+.gitignore+ignored+ignored.d/+EOF+echoignored>ignored&&mkdirignored.d&&echoignored>ignored.d/untracked&&
From: Denton Liu <hidden> Date: 2021-02-02 09:38:55
In order to modernize the test script, replace `test -s` with
test_file_not_empty(), which provides better diagnostic output in the
case of failure.
Signed-off-by: Denton Liu <redacted>
---
t/t3905-stash-include-untracked.sh | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
@@ -169,9 +169,9 @@ test_expect_success 'stash save --include-untracked respects .gitignore' 'mkdirignored.d&&echoignored>ignored.d/untracked&&gitstash-u&&-test-signored&&-test-signored.d/untracked&&-test-s.gitignore+test_file_not_emptyignored&&+test_file_not_emptyignored.d/untracked&&+test_file_not_empty.gitignore' test_expect_success'stash save -u can stash with only untracked files different''
@@ -189,9 +189,9 @@ test_expect_success 'stash save --all does not respect .gitignore' ' test_expect_success'stash save --all is stash poppable''gitstashpop&&-test-signored&&-test-signored.d/untracked&&-test-s.gitignore+test_file_not_emptyignored&&+test_file_not_emptyignored.d/untracked&&+test_file_not_empty.gitignore' test_expect_success'stash push --include-untracked with pathspec''
From: Denton Liu <hidden> Date: 2021-02-02 09:38:57
Stash entries can be made with untracked files via
`git stash push --include-untracked`. However, because the untracked
files are stored in the third parent of the stash entry and not the
stash entry itself, running `git stash show` does not include the
untracked files as part of the diff.
Teach stash the --include-tracked option, which also displays the
untracked files in a stash entry from the third parent (if it exists).
Do this by just concatenating the diff of the third parent against an
empty tree. One limitation of this is that it would be possible to
manually craft a stash entry which would present duplicate entries in
the diff by duplicating a file in the stash and in the third parent.
This seems like an instance of "Doctor, it hurts when I do this! So
don't do that!" so this can be written off.
Also, teach stash the --only-untracked option which only shows the
untracked files of a stash entry. This is similar to `git show stash^3`
but it is nice to provide a convenient abstraction for it so that users
do not have to think about the underlying implementation.
Signed-off-by: Denton Liu <redacted>
---
Documentation/git-stash.txt | 16 +++--
builtin/stash.c | 20 +++++-
contrib/completion/git-completion.bash | 2 +-
t/t3905-stash-include-untracked.sh | 84 ++++++++++++++++++++++++++
4 files changed, 116 insertions(+), 6 deletions(-)
@@ -83,7 +83,7 @@ stash@{1}: On master: 9cc0589... Add git-stash The command takes options applicable to the 'git log' command to control what is shown and how. See linkgit:git-log[1].-show [<diff options>] [<stash>]::+show [-u|--include-untracked|--only-untracked] [<diff options>] [<stash>]:: Show the changes recorded in the stash entry as a diff between the stashed contents and the commit back when the stash entry was first
@@ -160,10 +160,18 @@ up with `git clean`. -u:: --include-untracked::- This option is only valid for `push` and `save` commands.+--no-include-untracked::+ When used with the `push` and `save` commands,+ all untracked files are also stashed and then cleaned up with+ `git clean`. +-All untracked files are also stashed and then cleaned up with-`git clean`.+When used with the `show` command, show the untracked files in the stash+entry as part of the diff.++--only-untracked::+ This option is only valid for the `show` command.+++Show only the untracked files in the stash entry as part of the diff. --index:: This option is only valid for `pop` and `apply` commands.
@@ -795,7 +795,18 @@ static int show_stash(int argc, const char **argv, const char *prefix)structrev_inforev;structstrvecstash_args=STRVEC_INIT;structstrvecrevision_args=STRVEC_INIT;+enum{+UNTRACKED_NONE,+UNTRACKED_INCLUDE,+UNTRACKED_ONLY+}show_untracked=UNTRACKED_NONE;structoptionoptions[]={+OPT_SET_INT('u',"include-untracked",&show_untracked,+N_("include untracked files in the stash"),+UNTRACKED_INCLUDE),+OPT_SET_INT_F(0,"only-untracked",&show_untracked,+N_("only show untracked files in the stash"),+UNTRACKED_ONLY,PARSE_OPT_NONEG),OPT_END()};
From: Denton Liu <hidden> Date: 2021-02-02 09:39:25
The previous commit teaches `git stash show --include-untracked`. It
may be desirable for a user to be able to always enable the
--include-untracked behavior. Teach the stash.showIncludeUntracked
config option which allows users to do this in a similar manner to
stash.showPatch.
Signed-off-by: Denton Liu <redacted>
---
Documentation/config/stash.txt | 5 +++++
Documentation/git-stash.txt | 4 ++--
builtin/stash.c | 8 ++++++++
t/t3905-stash-include-untracked.sh | 2 ++
4 files changed, 17 insertions(+), 2 deletions(-)
@@ -5,6 +5,11 @@ stash.useBuiltin:: is always used. Setting this will emit a warning, to alert any remaining users that setting this now does nothing.+stash.showIncludeUntracked::+ If this is set to true, the `git stash show` command without an+ option will show the untracked files of a stash entry. Defaults to+ false. See description of 'show' command in linkgit:git-stash[1].+ stash.showPatch:: If this is set to true, the `git stash show` command without an option will show the stash entry in patch form. Defaults to false.
@@ -91,8 +91,8 @@ show [-u|--include-untracked|--only-untracked] [<diff options>] [<stash>]:: By default, the command shows the diffstat, but it will accept any format known to 'git diff' (e.g., `git stash show -p stash@{1}` to view the second most recent entry in patch form).- You can use stash.showStat and/or stash.showPatch config variables- to change the default behavior.+ You can use stash.showIncludeUntracked, stash.showStat, and+ stash.showPatch config variables to change the default behavior. pop [--index] [-q|--quiet] [<stash>]::
From: Denton Liu <hidden> Date: 2021-02-02 09:39:26
Save sizeof(const char *) bytes by declaring ref_stash as an array
instead of having a redundant pointer to an array.
Signed-off-by: Denton Liu <redacted>
---
builtin/stash.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
@@ -103,8 +103,10 @@ test_expect_success 'stash pop after save --include-untracked leaves files untragitstashpop&&gitstatus--porcelain>actual&&test_cmpexpectactual&&-test"1"="$(catfile2)"&&-testuntracked="$(catuntracked/untracked)"+echo1>expect_file2&&+test_cmpexpect_file2file2&&+echountracked>untracked_expect&&+test_cmpuntracked_expectuntracked/untracked' test_expect_success'clean up untracked/ directory to prepare for next tests''
From: Eric Sunshine <hidden> Date: 2021-02-02 17:41:25
On Tue, Feb 2, 2021 at 4:36 AM Denton Liu [off-list ref] wrote:
quoted hunk
Currently, the options for the `list` and `show` subcommands are just
listed as `<options>`. This seems to imply, from a cursory glance at the
summary, that they take the stash options listed below. However, reading
more carefully, we see that they take log options and diff options
respectively.
Make it more obvious that they take log and diff options by explicitly
stating this in the subcommand summary.
Signed-off-by: Denton Liu <redacted>
---
I might suggest that it is more common to hyphenate these words than
to separate them with spaces:
list [<log-options>]::
show [<diff-options>] [<stash>]::
From: Denton Liu <hidden> Date: 2021-02-09 07:30:00
A blindspot that I've noticed in git is that it's not possible to
properly view a stash entry that has untracked files via `git stash
show`. Teach `git stash show --include-untracked` which should do this.
In addition, this series also teaches `--only-untracked` and the
`stash.showIncludeUntracked` config option.
The first seven patches of this series are just some clean up that I've
done prior to working (because it bothers me). The remaining two patches
should be the meat of the change.
Changes since v1:
* Add a dash for <log-options> and <diff-options>
* Fix the misspelling of --include-untracked in a commit message
* Change the approach from concatenating diffs to using `git read-tree`
Denton Liu (9):
git-stash.txt: be explicit about subcommand options
t3905: remove spaces after redirect operators
t3905: move all commands into test cases
t3905: remove nested git in command substitution
t3905: replace test -s with test_file_not_empty
t3905: use test_cmp() to check file contents
stash: declare ref_stash as an array
stash show: teach --include-untracked and --only-untracked
stash show: learn stash.showIncludeUntracked
Documentation/config/stash.txt | 5 +
Documentation/git-stash.txt | 22 +-
builtin/stash.c | 87 +++++++-
contrib/completion/git-completion.bash | 2 +-
t/t3905-stash-include-untracked.sh | 278 +++++++++++++++++--------
5 files changed, 292 insertions(+), 102 deletions(-)
Range-diff against v1:
1: 17675b9e4c ! 1: 5697f14f1c git-stash.txt: be explicit about subcommand options
@@ Documentation/git-stash.txt: save [-p|--patch] [-k|--[no-]keep-index] [-u|--incl
message.
-list [<options>]::
-+list [<log options>]::
++list [<log-options>]::
List the stash entries that you currently have. Each 'stash entry' is
listed with its name (e.g. `stash@{0}` is the latest entry, `stash@{1}` is
@@ Documentation/git-stash.txt: stash@{1}: On master: 9cc0589... Add git-stash
command to control what is shown and how. See linkgit:git-log[1].
-show [<options>] [<stash>]::
-+show [<diff options>] [<stash>]::
++show [<diff-options>] [<stash>]::
Show the changes recorded in the stash entry as a diff between the
stashed contents and the commit back when the stash entry was first
2: 0de324e3bc = 2: 45ed17bfe2 t3905: remove spaces after redirect operators
3: 519840b1a2 = 3: 5bda09b4bd t3905: move all commands into test cases
4: 4b72d39e01 = 4: 57c21e2461 t3905: remove nested git in command substitution
5: 7fe27ab620 = 5: 2530883b6c t3905: replace test -s with test_file_not_empty
6: 4a5dd83ff4 = 6: 80194bcfa5 t3905: use test_cmp() to check file contents
7: b5f22de3fc = 7: 2f03d38b36 stash: declare ref_stash as an array
8: c2375d1fc6 ! 8: 88d4791259 stash show: teach --include-tracked and --only-untracked
@@ Metadata
Author: Denton Liu [off-list ref]
## Commit message ##
- stash show: teach --include-tracked and --only-untracked
+ stash show: teach --include-untracked and --only-untracked
Stash entries can be made with untracked files via
`git stash push --include-untracked`. However, because the untracked
@@ Commit message
stash entry itself, running `git stash show` does not include the
untracked files as part of the diff.
- Teach stash the --include-tracked option, which also displays the
+ Teach stash the --include-untracked option, which also displays the
untracked files in a stash entry from the third parent (if it exists).
- Do this by just concatenating the diff of the third parent against an
- empty tree. One limitation of this is that it would be possible to
- manually craft a stash entry which would present duplicate entries in
- the diff by duplicating a file in the stash and in the third parent.
- This seems like an instance of "Doctor, it hurts when I do this! So
- don't do that!" so this can be written off.
+ Do this via something like
+
+ GIT_INDEX_FILE=... git read-tree stash stash^3
+
+ and diffing the resulting tree object against the stash base.
+
+ One improvement that this could use for the future is performing the
+ action without writing anything to disk as one would expect this to be a
+ read-only operation. This can be fixed in the future, however.
+
+ Another limitation of this is that it would be possible to manually
+ craft a stash entry where duplicate untracked files in the stash entry
+ will mask tracked files. This seems like an instance of "Doctor, it
+ hurts when I do this! So don't do that!" so this can be written off.
Also, teach stash the --only-untracked option which only shows the
untracked files of a stash entry. This is similar to `git show stash^3`
@@ Documentation/git-stash.txt: stash@{1}: On master: 9cc0589... Add git-stash
The command takes options applicable to the 'git log'
command to control what is shown and how. See linkgit:git-log[1].
--show [<diff options>] [<stash>]::
-+show [-u|--include-untracked|--only-untracked] [<diff options>] [<stash>]::
+-show [<diff-options>] [<stash>]::
++show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>]::
Show the changes recorded in the stash entry as a diff between the
stashed contents and the commit back when the stash entry was first
@@ Documentation/git-stash.txt: up with `git clean`.
This option is only valid for `pop` and `apply` commands.
## builtin/stash.c ##
+@@ builtin/stash.c: static int git_stash_config(const char *var, const char *value, void *cb)
+ return git_diff_basic_config(var, value, cb);
+ }
+
++static int merge_track_untracked(struct object_id *result, const struct stash_info *info)
++{
++ int ret = 0;
++ struct index_state istate = { NULL };
++ struct child_process cp_read_tree = CHILD_PROCESS_INIT;
++
++ if (!info->has_u) {
++ oidcpy(result, &info->w_commit);
++ return 0;
++ }
++
++ /*
++ * TODO: is there a way of doing this all in-memory without writing
++ * anything to disk?
++ */
++ remove_path(stash_index_path.buf);
++
++ cp_read_tree.git_cmd = 1;
++ strvec_push(&cp_read_tree.args, "read-tree");
++ strvec_push(&cp_read_tree.args, oid_to_hex(&info->w_commit));
++ strvec_push(&cp_read_tree.args, oid_to_hex(&info->u_tree));
++ strvec_pushf(&cp_read_tree.env_array, "GIT_INDEX_FILE=%s",
++ stash_index_path.buf);
++
++ if (run_command(&cp_read_tree)) {
++ ret = -1;
++ goto done;
++ }
++
++ if (write_index_as_tree(result, &istate, stash_index_path.buf, 0,
++ NULL)) {
++ ret = -1;
++ goto done;
++ }
++
++done:
++ discard_index(&istate);
++ remove_path(stash_index_path.buf);
++ return ret;
++}
++
+ static int show_stash(int argc, const char **argv, const char *prefix)
+ {
+ int i;
@@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)
struct rev_info rev;
struct strvec stash_args = STRVEC_INIT;
struct strvec revision_args = STRVEC_INIT;
++ struct object_id *before = NULL;
++ struct object_id *after = NULL;
++ struct object_id untracked_merged_tree;
+ enum {
+ UNTRACKED_NONE,
+ UNTRACKED_INCLUDE,
@@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *
rev.diffopt.flags.recursive = 1;
setup_diff_pager(&rev.diffopt);
- diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt);
-+ if (show_untracked != UNTRACKED_ONLY)
-+ diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt);
-+ if (show_untracked != UNTRACKED_NONE && info.has_u)
-+ diff_root_tree_oid(&info.u_tree, "", &rev.diffopt);
++ switch (show_untracked) {
++ case UNTRACKED_NONE:
++ before = &info.b_commit;
++ after = &info.w_commit;
++ break;
++ case UNTRACKED_ONLY:
++ before = NULL;
++ after = &info.u_tree;
++ break;
++ case UNTRACKED_INCLUDE:
++ if (merge_track_untracked(&untracked_merged_tree, &info) < 0)
++ die(_("unable merge stash index with untracked files index"));
++ before = &info.b_commit;
++ after = &untracked_merged_tree;
++ break;
++ }
++ diff_tree_oid(before, after, "", &rev.diffopt);
log_tree_diff_flush(&rev);
free_stash_info(&info);
9: 2c5d5d9dd4 ! 9: ac4019f47e stash show: learn stash.showIncludeUntracked
@@ Documentation/config/stash.txt: stash.useBuiltin::
option will show the stash entry in patch form. Defaults to false.
## Documentation/git-stash.txt ##
-@@ Documentation/git-stash.txt: show [-u|--include-untracked|--only-untracked] [<diff options>] [<stash>]::
+@@ Documentation/git-stash.txt: show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>]::
By default, the command shows the diffstat, but it will accept any
format known to 'git diff' (e.g., `git stash show -p stash@{1}`
to view the second most recent entry in patch form).
--
2.30.0.478.g8a0d178c01
From: Denton Liu <hidden> Date: 2021-02-09 07:30:03
Currently, the options for the `list` and `show` subcommands are just
listed as `<options>`. This seems to imply, from a cursory glance at the
summary, that they take the stash options listed below. However, reading
more carefully, we see that they take log options and diff options
respectively.
Make it more obvious that they take log and diff options by explicitly
stating this in the subcommand summary.
Signed-off-by: Denton Liu <redacted>
---
Documentation/git-stash.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
@@ -67,7 +67,7 @@ save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q Instead, all non-option arguments are concatenated to form the stash message.-list [<options>]::+list [<log-options>]:: List the stash entries that you currently have. Each 'stash entry' is listed with its name (e.g. `stash@{0}` is the latest entry, `stash@{1}` is
@@ -83,7 +83,7 @@ stash@{1}: On master: 9cc0589... Add git-stash The command takes options applicable to the 'git log' command to control what is shown and how. See linkgit:git-log[1].-show [<options>] [<stash>]::+show [<diff-options>] [<stash>]:: Show the changes recorded in the stash entry as a diff between the stashed contents and the commit back when the stash entry was first
From: Denton Liu <hidden> Date: 2021-02-09 07:30:21
For shell scripts, the usual convention is for there to be no space
after redirection operators, (e.g. `>file`, not `> file`). Remove these
spaces wherever they appear.
Signed-off-by: Denton Liu <redacted>
---
t/t3905-stash-include-untracked.sh | 40 +++++++++++++++---------------
1 file changed, 20 insertions(+), 20 deletions(-)
@@ -8,16 +8,16 @@ test_description='Test git stash --include-untracked' ../test-lib.sh test_expect_success'stash save --include-untracked some dirty working directory''-echo1>file&&+echo1>file&&gitaddfile&&test_tick&&gitcommit-minitial&&-echo2>file&&+echo2>file&&gitaddfile&&-echo3>file&&+echo3>file&&test_tick&&-echo1>file2&&-echo1>HEAD&&+echo1>file2&&+echo1>HEAD&&mkdiruntracked&&echountracked>untracked/untracked&&gitstash--include-untracked&&
@@ -25,7 +25,7 @@ test_expect_success 'stash save --include-untracked some dirty working directorygitdiff-index--cached--quietHEAD'-cat>expect<<EOF+cat>expect<<EOF ??actual ??expect EOF
@@ -37,7 +37,7 @@ test_expect_success 'stash save --include-untracked cleaned the untracked files'tracked=$(gitrev-parse--short$(echo1|githash-object--stdin))untracked=$(gitrev-parse--short$(echountracked|githash-object--stdin))-cat>expect.diff<<EOF+cat>expect.diff<<EOF diff--gita/HEADb/HEAD newfilemode100644 index0000000..$tracked
@@ -60,7 +60,7 @@ index 0000000..$untracked @@-0,0+1@@ +untracked EOF-cat>expect.lstree<<EOF+cat>expect.lstree<<EOF HEAD file2 untracked
@@ -105,14 +105,14 @@ test_expect_success 'stash pop after save --include-untracked leaves files untra gitclean--force--quiet-d test_expect_success'stash save -u dirty index''-echo4>file3&&+echo4>file3&&gitaddfile3&&test_tick&&gitstash-u'blob=$(gitrev-parse--short$(echo4|githash-object--stdin))-cat>expect<<EOF+cat>expect<<EOF diff--gita/file3b/file3 newfilemode100644 index0000000..$blob
@@ -128,12 +128,12 @@ test_expect_success 'stash save --include-untracked dirty index got stashed' 'test_cmpexpectactual'-gitreset>/dev/null+gitreset>/dev/null# Must direct output somewhere where it won't be considered an untracked file test_expect_success'stash save --include-untracked -q is quiet''-echo1>file5&&-gitstashsave--include-untracked--quiet>.git/stash-output.out2>&1&&+echo1>file5&&+gitstashsave--include-untracked--quiet>.git/stash-output.out2>&1&&test_line_count=0.git/stash-output.out&&rm-f.git/stash-output.out'
@@ -141,7 +141,7 @@ test_expect_success 'stash save --include-untracked -q is quiet' ' test_expect_success'stash save --include-untracked removed files''rm-ffile&&gitstashsave--include-untracked&&-echo1>expect&&+echo1>expect&&test_cmpexpectfile'
@@ -169,7 +169,7 @@ test_expect_success 'stash save --include-untracked respects .gitignore' '' test_expect_success'stash save -u can stash with only untracked files different''-echo4>file4&&+echo4>file4&&gitstash-u&&test_path_is_missingfile4'
From: Denton Liu <hidden> Date: 2021-02-09 07:30:27
In order to modernize the tests, move commands that currently run
outside of test cases into a test case. Where possible, clean up files
that are produced using test_when_finished() but in the case where files
persist over multiple test cases, create a new test case to perform
cleanup.
Signed-off-by: Denton Liu <redacted>
---
t/t3905-stash-include-untracked.sh | 147 +++++++++++++++--------------
1 file changed, 75 insertions(+), 72 deletions(-)
@@ -25,48 +25,48 @@ test_expect_success 'stash save --include-untracked some dirty working directorygitdiff-index--cached--quietHEAD'-cat>expect<<EOF-??actual-??expect-EOF- test_expect_success'stash save --include-untracked cleaned the untracked files''+cat>expect<<-EOF&&+??actual+??expect+EOF+gitstatus--porcelain>actual&&test_cmpexpectactual'-tracked=$(gitrev-parse--short$(echo1|githash-object--stdin))-untracked=$(gitrev-parse--short$(echountracked|githash-object--stdin))-cat>expect.diff<<EOF-diff--gita/HEADb/HEAD-newfilemode100644-index0000000..$tracked----/dev/null-+++b/HEAD-@@-0,0+1@@-+1-diff--gita/file2b/file2-newfilemode100644-index0000000..$tracked----/dev/null-+++b/file2-@@-0,0+1@@-+1-diff--gita/untracked/untrackedb/untracked/untracked-newfilemode100644-index0000000..$untracked----/dev/null-+++b/untracked/untracked-@@-0,0+1@@-+untracked-EOF-cat>expect.lstree<<EOF-HEAD-file2-untracked-EOF- test_expect_success'stash save --include-untracked stashed the untracked files''+tracked=$(gitrev-parse--short$(echo1|githash-object--stdin))&&+untracked=$(gitrev-parse--short$(echountracked|githash-object--stdin))&&+cat>expect.diff<<-EOF&&+diff--gita/HEADb/HEAD+newfilemode100644+index0000000..$tracked+---/dev/null++++b/HEAD+@@-0,0+1@@++1+diff--gita/file2b/file2+newfilemode100644+index0000000..$tracked+---/dev/null++++b/file2+@@-0,0+1@@++1+diff--gita/untracked/untrackedb/untracked/untracked+newfilemode100644+index0000000..$untracked+---/dev/null++++b/untracked/untracked+@@-0,0+1@@++untracked+EOF+cat>expect.lstree<<-EOF&&+HEAD+file2+untracked+EOF+test_path_is_missingfile2&&test_path_is_missinguntracked&&test_path_is_missingHEAD&&
@@ -83,18 +83,21 @@ test_expect_success 'stash save --patch --all fails' 'test_must_failgitstash--patch--all'-gitclean--force--quiet+test_expect_success'clean up untracked/untracked file to prepare for next tests''+gitclean--force--quiet-cat>expect<<EOF-Mfile-??HEAD-??actual-??expect-??file2-??untracked/-EOF+' test_expect_success'stash pop after save --include-untracked leaves files untracked again''+cat>expect<<-EOF&&+Mfile+??HEAD+??actual+??expect+??file2+??untracked/+EOF+gitstashpop&&gitstatus--porcelain>actual&&test_cmpexpectactual&&
@@ -102,7 +105,9 @@ test_expect_success 'stash pop after save --include-untracked leaves files untratestuntracked="$(catuntracked/untracked)"'-gitclean--force--quiet-d+test_expect_success'clean up untracked/ directory to prepare for next tests''+gitclean--force--quiet-d+' test_expect_success'stash save -u dirty index''echo4>file3&&
@@ -111,25 +116,24 @@ test_expect_success 'stash save -u dirty index' 'gitstash-u'-blob=$(gitrev-parse--short$(echo4|githash-object--stdin))-cat>expect<<EOF-diff--gita/file3b/file3-newfilemode100644-index0000000..$blob----/dev/null-+++b/file3-@@-0,0+1@@-+4-EOF- test_expect_success'stash save --include-untracked dirty index got stashed''+blob=$(gitrev-parse--short$(echo4|githash-object--stdin))&&+cat>expect<<-EOF&&+diff--gita/file3b/file3+newfilemode100644+index0000000..$blob+---/dev/null++++b/file3+@@-0,0+1@@++4+EOF+gitstashpop--index&&+test_when_finished"git reset"&&gitdiff--cached>actual&&test_cmpexpectactual'-gitreset>/dev/null-# Must direct output somewhere where it won't be considered an untracked file test_expect_success'stash save --include-untracked -q is quiet''echo1>file5&&
@@ -142,23 +146,22 @@ test_expect_success 'stash save --include-untracked removed files' 'rm-ffile&&gitstashsave--include-untracked&&echo1>expect&&+test_when_finished"rm -f expect"&&test_cmpexpectfile'-rm-fexpect- test_expect_success'stash save --include-untracked removed files got stashed''gitstashpop&&test_path_is_missingfile'-cat>.gitignore<<EOF-.gitignore-ignored-ignored.d/-EOF- test_expect_success'stash save --include-untracked respects .gitignore''+cat>.gitignore<<-EOF&&+.gitignore+ignored+ignored.d/+EOF+echoignored>ignored&&mkdirignored.d&&echoignored>ignored.d/untracked&&
From: Denton Liu <hidden> Date: 2021-02-09 07:30:30
If a git command in a nested command substitution fails, it will be
silently ignored since only the return code of the outer command
substitutions is reported. Factor out nested command substitutions so
that the error codes of those commands are reported.
Signed-off-by: Denton Liu <redacted>
---
t/t3905-stash-include-untracked.sh | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
@@ -36,8 +36,10 @@ test_expect_success 'stash save --include-untracked cleaned the untracked files'' test_expect_success'stash save --include-untracked stashed the untracked files''-tracked=$(gitrev-parse--short$(echo1|githash-object--stdin))&&-untracked=$(gitrev-parse--short$(echountracked|githash-object--stdin))&&+one_blob=$(echo1|githash-object--stdin)&&+tracked=$(gitrev-parse--short"$one_blob")&&+untracked_blob=$(echountracked|githash-object--stdin)&&+untracked=$(gitrev-parse--short"$untracked_blob")&&cat>expect.diff<<-EOF&&diff--gita/HEADb/HEADnewfilemode100644
@@ -117,7 +119,8 @@ test_expect_success 'stash save -u dirty index' '' test_expect_success'stash save --include-untracked dirty index got stashed''-blob=$(gitrev-parse--short$(echo4|githash-object--stdin))&&+four_blob=$(echo4|githash-object--stdin)&&+blob=$(gitrev-parse--short"$four_blob")&&cat>expect<<-EOF&&diff--gita/file3b/file3newfilemode100644
From: Denton Liu <hidden> Date: 2021-02-09 07:31:03
In order to modernize the test script, replace `test -s` with
test_file_not_empty(), which provides better diagnostic output in the
case of failure.
Signed-off-by: Denton Liu <redacted>
---
t/t3905-stash-include-untracked.sh | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
@@ -169,9 +169,9 @@ test_expect_success 'stash save --include-untracked respects .gitignore' 'mkdirignored.d&&echoignored>ignored.d/untracked&&gitstash-u&&-test-signored&&-test-signored.d/untracked&&-test-s.gitignore+test_file_not_emptyignored&&+test_file_not_emptyignored.d/untracked&&+test_file_not_empty.gitignore' test_expect_success'stash save -u can stash with only untracked files different''
@@ -189,9 +189,9 @@ test_expect_success 'stash save --all does not respect .gitignore' ' test_expect_success'stash save --all is stash poppable''gitstashpop&&-test-signored&&-test-signored.d/untracked&&-test-s.gitignore+test_file_not_emptyignored&&+test_file_not_emptyignored.d/untracked&&+test_file_not_empty.gitignore' test_expect_success'stash push --include-untracked with pathspec''
@@ -103,8 +103,10 @@ test_expect_success 'stash pop after save --include-untracked leaves files untragitstashpop&&gitstatus--porcelain>actual&&test_cmpexpectactual&&-test"1"="$(catfile2)"&&-testuntracked="$(catuntracked/untracked)"+echo1>expect_file2&&+test_cmpexpect_file2file2&&+echountracked>untracked_expect&&+test_cmpuntracked_expectuntracked/untracked' test_expect_success'clean up untracked/ directory to prepare for next tests''
From: Denton Liu <hidden> Date: 2021-02-09 07:31:52
Stash entries can be made with untracked files via
`git stash push --include-untracked`. However, because the untracked
files are stored in the third parent of the stash entry and not the
stash entry itself, running `git stash show` does not include the
untracked files as part of the diff.
Teach stash the --include-untracked option, which also displays the
untracked files in a stash entry from the third parent (if it exists).
Do this via something like
GIT_INDEX_FILE=... git read-tree stash stash^3
and diffing the resulting tree object against the stash base.
One improvement that this could use for the future is performing the
action without writing anything to disk as one would expect this to be a
read-only operation. This can be fixed in the future, however.
Another limitation of this is that it would be possible to manually
craft a stash entry where duplicate untracked files in the stash entry
will mask tracked files. This seems like an instance of "Doctor, it
hurts when I do this! So don't do that!" so this can be written off.
Also, teach stash the --only-untracked option which only shows the
untracked files of a stash entry. This is similar to `git show stash^3`
but it is nice to provide a convenient abstraction for it so that users
do not have to think about the underlying implementation.
Signed-off-by: Denton Liu <redacted>
---
Documentation/git-stash.txt | 16 +++--
builtin/stash.c | 77 ++++++++++++++++++++++-
contrib/completion/git-completion.bash | 2 +-
t/t3905-stash-include-untracked.sh | 84 ++++++++++++++++++++++++++
4 files changed, 173 insertions(+), 6 deletions(-)
@@ -83,7 +83,7 @@ stash@{1}: On master: 9cc0589... Add git-stash The command takes options applicable to the 'git log' command to control what is shown and how. See linkgit:git-log[1].-show [<diff-options>] [<stash>]::+show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>]:: Show the changes recorded in the stash entry as a diff between the stashed contents and the commit back when the stash entry was first
@@ -160,10 +160,18 @@ up with `git clean`. -u:: --include-untracked::- This option is only valid for `push` and `save` commands.+--no-include-untracked::+ When used with the `push` and `save` commands,+ all untracked files are also stashed and then cleaned up with+ `git clean`. +-All untracked files are also stashed and then cleaned up with-`git clean`.+When used with the `show` command, show the untracked files in the stash+entry as part of the diff.++--only-untracked::+ This option is only valid for the `show` command.+++Show only the untracked files in the stash entry as part of the diff. --index:: This option is only valid for `pop` and `apply` commands.
@@ -795,7 +836,21 @@ static int show_stash(int argc, const char **argv, const char *prefix)structrev_inforev;structstrvecstash_args=STRVEC_INIT;structstrvecrevision_args=STRVEC_INIT;+structobject_id*before=NULL;+structobject_id*after=NULL;+structobject_iduntracked_merged_tree;+enum{+UNTRACKED_NONE,+UNTRACKED_INCLUDE,+UNTRACKED_ONLY+}show_untracked=UNTRACKED_NONE;structoptionoptions[]={+OPT_SET_INT('u',"include-untracked",&show_untracked,+N_("include untracked files in the stash"),+UNTRACKED_INCLUDE),+OPT_SET_INT_F(0,"only-untracked",&show_untracked,+N_("only show untracked files in the stash"),+UNTRACKED_ONLY,PARSE_OPT_NONEG),OPT_END()};
From: Denton Liu <hidden> Date: 2021-02-09 07:31:59
Save sizeof(const char *) bytes by declaring ref_stash as an array
instead of having a redundant pointer to an array.
Signed-off-by: Denton Liu <redacted>
---
builtin/stash.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Denton Liu <hidden> Date: 2021-02-09 07:32:14
The previous commit teaches `git stash show --include-untracked`. It
may be desirable for a user to be able to always enable the
--include-untracked behavior. Teach the stash.showIncludeUntracked
config option which allows users to do this in a similar manner to
stash.showPatch.
Signed-off-by: Denton Liu <redacted>
---
Documentation/config/stash.txt | 5 +++++
Documentation/git-stash.txt | 4 ++--
builtin/stash.c | 8 ++++++++
t/t3905-stash-include-untracked.sh | 2 ++
4 files changed, 17 insertions(+), 2 deletions(-)
@@ -5,6 +5,11 @@ stash.useBuiltin:: is always used. Setting this will emit a warning, to alert any remaining users that setting this now does nothing.+stash.showIncludeUntracked::+ If this is set to true, the `git stash show` command without an+ option will show the untracked files of a stash entry. Defaults to+ false. See description of 'show' command in linkgit:git-stash[1].+ stash.showPatch:: If this is set to true, the `git stash show` command without an option will show the stash entry in patch form. Defaults to false.
@@ -91,8 +91,8 @@ show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>]:: By default, the command shows the diffstat, but it will accept any format known to 'git diff' (e.g., `git stash show -p stash@{1}` to view the second most recent entry in patch form).- You can use stash.showStat and/or stash.showPatch config variables- to change the default behavior.+ You can use stash.showIncludeUntracked, stash.showStat, and+ stash.showPatch config variables to change the default behavior. pop [--index] [-q|--quiet] [<stash>]::
From: Denton Liu <hidden> Date: 2021-02-16 07:13:14
A blindspot that I've noticed in git is that it's not possible to
properly view a stash entry that has untracked files via `git stash
show`. Teach `git stash show --include-untracked` which should do this.
In addition, this series also teaches `--only-untracked` and the
`stash.showIncludeUntracked` config option.
This series is based on 'dl/stash-cleanup'.
Changes since v2:
* Base this series on top of 'dl/stash-cleanup'
* Attempt to replicate the read-tree code to merge the untracked tree
Changes since v1:
* Add a dash for <log-options> and <diff-options>
* Fix the misspelling of --include-untracked in a commit message
* Change the approach from concatenating diffs to using `git read-tree`
Denton Liu (2):
stash show: teach --include-untracked and --only-untracked
stash show: learn stash.showIncludeUntracked
Documentation/config/stash.txt | 5 ++
Documentation/git-stash.txt | 22 ++++---
builtin/stash.c | 61 +++++++++++++++++-
contrib/completion/git-completion.bash | 2 +-
t/t3905-stash-include-untracked.sh | 86 ++++++++++++++++++++++++++
5 files changed, 167 insertions(+), 9 deletions(-)
Range-diff against v2:
1: 88d4791259 ! 1: 85b81f2f06 stash show: teach --include-untracked and --only-untracked
@@ Commit message
stash entry itself, running `git stash show` does not include the
untracked files as part of the diff.
- Teach stash the --include-untracked option, which also displays the
- untracked files in a stash entry from the third parent (if it exists).
- Do this via something like
+ With --include-untracked, untracked paths, which are recorded in the
+ third-parent if it exists, are shown in addition to the paths that have
+ modifications between the stash base and the working tree in the stash.
- GIT_INDEX_FILE=... git read-tree stash stash^3
-
- and diffing the resulting tree object against the stash base.
-
- One improvement that this could use for the future is performing the
- action without writing anything to disk as one would expect this to be a
- read-only operation. This can be fixed in the future, however.
-
- Another limitation of this is that it would be possible to manually
- craft a stash entry where duplicate untracked files in the stash entry
- will mask tracked files. This seems like an instance of "Doctor, it
- hurts when I do this! So don't do that!" so this can be written off.
+ One limitation of this is that it would be possible to manually craft a
+ stash entry where duplicate untracked files in the stash entry will mask
+ tracked files. This seems like an instance of "Doctor, it hurts when I
+ do this! So don't do that!" so this can be written off.
Also, teach stash the --only-untracked option which only shows the
untracked files of a stash entry. This is similar to `git show stash^3`
but it is nice to provide a convenient abstraction for it so that users
do not have to think about the underlying implementation.
+
+ ## Notes ##
+ I am not familiar with the read-tree code so this attempt at replicating
+ the read-tree code may in diff_include_untracked() may be incorrect
+ (particularly the use of the_index?).
+
+ Also, I could not figure out how to make unpack_trees() error out in the
+ case where untracked tree entry contains duplicate entries with the
+ worktree entry.
+
## Documentation/git-stash.txt ##
+@@ Documentation/git-stash.txt: SYNOPSIS
+ --------
+ [verse]
+ 'git stash' list [<log-options>]
+-'git stash' show [<diff-options>] [<stash>]
++'git stash' show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>]
+ 'git stash' drop [-q|--quiet] [<stash>]
+ 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
+ 'git stash' branch <branchname> [<stash>]
@@ Documentation/git-stash.txt: stash@{1}: On master: 9cc0589... Add git-stash
The command takes options applicable to the 'git log'
command to control what is shown and how. See linkgit:git-log[1].
@@ builtin/stash.c: static int git_stash_config(const char *var, const char *value,
return git_diff_basic_config(var, value, cb);
}
-+static int merge_track_untracked(struct object_id *result, const struct stash_info *info)
++static void diff_include_untracked(const struct stash_info *info, struct diff_options *diff_opt)
+{
-+ int ret = 0;
-+ struct index_state istate = { NULL };
-+ struct child_process cp_read_tree = CHILD_PROCESS_INIT;
++ const struct object_id *oid[] = { &info->w_commit, &info->u_tree };
++ struct tree *tree[ARRAY_SIZE(oid)];
++ struct tree_desc tree_desc[ARRAY_SIZE(oid)];
++ struct unpack_trees_options unpack_tree_opt = { 0 };
++ int i;
+
-+ if (!info->has_u) {
-+ oidcpy(result, &info->w_commit);
-+ return 0;
++ for (i = 0; i < ARRAY_SIZE(oid); i++) {
++ tree[i] = parse_tree_indirect(oid[i]);
++ if (parse_tree(tree[i]) < 0)
++ die(_("failed to parse tree"));
++ init_tree_desc(&tree_desc[i], tree[i]->buffer, tree[i]->size);
+ }
+
-+ /*
-+ * TODO: is there a way of doing this all in-memory without writing
-+ * anything to disk?
-+ */
-+ remove_path(stash_index_path.buf);
++ unpack_tree_opt.head_idx = -1;
++ unpack_tree_opt.src_index = &the_index;
++ unpack_tree_opt.dst_index = &the_index;
++ unpack_tree_opt.fn = twoway_merge;
+
-+ cp_read_tree.git_cmd = 1;
-+ strvec_push(&cp_read_tree.args, "read-tree");
-+ strvec_push(&cp_read_tree.args, oid_to_hex(&info->w_commit));
-+ strvec_push(&cp_read_tree.args, oid_to_hex(&info->u_tree));
-+ strvec_pushf(&cp_read_tree.env_array, "GIT_INDEX_FILE=%s",
-+ stash_index_path.buf);
++ if (unpack_trees(ARRAY_SIZE(tree_desc), tree_desc, &unpack_tree_opt))
++ die(_("failed to unpack trees"));
+
-+ if (run_command(&cp_read_tree)) {
-+ ret = -1;
-+ goto done;
-+ }
-+
-+ if (write_index_as_tree(result, &istate, stash_index_path.buf, 0,
-+ NULL)) {
-+ ret = -1;
-+ goto done;
-+ }
-+
-+done:
-+ discard_index(&istate);
-+ remove_path(stash_index_path.buf);
-+ return ret;
++ do_diff_cache(&info->b_commit, diff_opt);
+}
+
static int show_stash(int argc, const char **argv, const char *prefix)
@@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *
struct rev_info rev;
struct strvec stash_args = STRVEC_INIT;
struct strvec revision_args = STRVEC_INIT;
-+ struct object_id *before = NULL;
-+ struct object_id *after = NULL;
-+ struct object_id untracked_merged_tree;
+ enum {
+ UNTRACKED_NONE,
+ UNTRACKED_INCLUDE,
@@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *
- diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt);
+ switch (show_untracked) {
+ case UNTRACKED_NONE:
-+ before = &info.b_commit;
-+ after = &info.w_commit;
++ diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt);
+ break;
+ case UNTRACKED_ONLY:
-+ before = NULL;
-+ after = &info.u_tree;
++ diff_root_tree_oid(&info.u_tree, "", &rev.diffopt);
+ break;
+ case UNTRACKED_INCLUDE:
-+ if (merge_track_untracked(&untracked_merged_tree, &info) < 0)
-+ die(_("unable merge stash index with untracked files index"));
-+ before = &info.b_commit;
-+ after = &untracked_merged_tree;
++ diff_include_untracked(&info, &rev.diffopt);
+ break;
+ }
-+ diff_tree_oid(before, after, "", &rev.diffopt);
log_tree_diff_flush(&rev);
free_stash_info(&info);
2: ac4019f47e = 2: d19d07ec27 stash show: learn stash.showIncludeUntracked
--
2.30.0.478.g8a0d178c01
From: Denton Liu <hidden> Date: 2021-02-16 07:13:14
Stash entries can be made with untracked files via
`git stash push --include-untracked`. However, because the untracked
files are stored in the third parent of the stash entry and not the
stash entry itself, running `git stash show` does not include the
untracked files as part of the diff.
With --include-untracked, untracked paths, which are recorded in the
third-parent if it exists, are shown in addition to the paths that have
modifications between the stash base and the working tree in the stash.
One limitation of this is that it would be possible to manually craft a
stash entry where duplicate untracked files in the stash entry will mask
tracked files. This seems like an instance of "Doctor, it hurts when I
do this! So don't do that!" so this can be written off.
Also, teach stash the --only-untracked option which only shows the
untracked files of a stash entry. This is similar to `git show stash^3`
but it is nice to provide a convenient abstraction for it so that users
do not have to think about the underlying implementation.
Signed-off-by: Denton Liu <redacted>
---
Notes:
I am not familiar with the read-tree code so this attempt at replicating
the read-tree code may in diff_include_untracked() may be incorrect
(particularly the use of the_index?).
Also, I could not figure out how to make unpack_trees() error out in the
case where untracked tree entry contains duplicate entries with the
worktree entry.
Documentation/git-stash.txt | 18 ++++--
builtin/stash.c | 53 +++++++++++++++-
contrib/completion/git-completion.bash | 2 +-
t/t3905-stash-include-untracked.sh | 84 ++++++++++++++++++++++++++
4 files changed, 150 insertions(+), 7 deletions(-)
@@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git stash' list [<log-options>]-'git stash' show [<diff-options>] [<stash>]+'git stash' show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>] 'git stash' drop [-q|--quiet] [<stash>] 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>] 'git stash' branch <branchname> [<stash>]
@@ -83,7 +83,7 @@ stash@{1}: On master: 9cc0589... Add git-stash The command takes options applicable to the 'git log' command to control what is shown and how. See linkgit:git-log[1].-show [<diff-options>] [<stash>]::+show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>]:: Show the changes recorded in the stash entry as a diff between the stashed contents and the commit back when the stash entry was first
@@ -160,10 +160,18 @@ up with `git clean`. -u:: --include-untracked::- This option is only valid for `push` and `save` commands.+--no-include-untracked::+ When used with the `push` and `save` commands,+ all untracked files are also stashed and then cleaned up with+ `git clean`. +-All untracked files are also stashed and then cleaned up with-`git clean`.+When used with the `show` command, show the untracked files in the stash+entry as part of the diff.++--only-untracked::+ This option is only valid for the `show` command.+++Show only the untracked files in the stash entry as part of the diff. --index:: This option is only valid for `pop` and `apply` commands.
@@ -787,6 +787,32 @@ static int git_stash_config(const char *var, const char *value, void *cb)returngit_diff_basic_config(var,value,cb);}+staticvoiddiff_include_untracked(conststructstash_info*info,structdiff_options*diff_opt)+{+conststructobject_id*oid[]={&info->w_commit,&info->u_tree};+structtree*tree[ARRAY_SIZE(oid)];+structtree_desctree_desc[ARRAY_SIZE(oid)];+structunpack_trees_optionsunpack_tree_opt={0};+inti;++for(i=0;i<ARRAY_SIZE(oid);i++){+tree[i]=parse_tree_indirect(oid[i]);+if(parse_tree(tree[i])<0)+die(_("failed to parse tree"));+init_tree_desc(&tree_desc[i],tree[i]->buffer,tree[i]->size);+}++unpack_tree_opt.head_idx=-1;+unpack_tree_opt.src_index=&the_index;+unpack_tree_opt.dst_index=&the_index;+unpack_tree_opt.fn=twoway_merge;++if(unpack_trees(ARRAY_SIZE(tree_desc),tree_desc,&unpack_tree_opt))+die(_("failed to unpack trees"));++do_diff_cache(&info->b_commit,diff_opt);+}+staticintshow_stash(intargc,constchar**argv,constchar*prefix){inti;
@@ -795,7 +821,18 @@ static int show_stash(int argc, const char **argv, const char *prefix)structrev_inforev;structstrvecstash_args=STRVEC_INIT;structstrvecrevision_args=STRVEC_INIT;+enum{+UNTRACKED_NONE,+UNTRACKED_INCLUDE,+UNTRACKED_ONLY+}show_untracked=UNTRACKED_NONE;structoptionoptions[]={+OPT_SET_INT('u',"include-untracked",&show_untracked,+N_("include untracked files in the stash"),+UNTRACKED_INCLUDE),+OPT_SET_INT_F(0,"only-untracked",&show_untracked,+N_("only show untracked files in the stash"),+UNTRACKED_ONLY,PARSE_OPT_NONEG),OPT_END()};
From: Denton Liu <hidden> Date: 2021-02-16 07:13:14
The previous commit teaches `git stash show --include-untracked`. It
may be desirable for a user to be able to always enable the
--include-untracked behavior. Teach the stash.showIncludeUntracked
config option which allows users to do this in a similar manner to
stash.showPatch.
Signed-off-by: Denton Liu <redacted>
---
Documentation/config/stash.txt | 5 +++++
Documentation/git-stash.txt | 4 ++--
builtin/stash.c | 8 ++++++++
t/t3905-stash-include-untracked.sh | 2 ++
4 files changed, 17 insertions(+), 2 deletions(-)
@@ -5,6 +5,11 @@ stash.useBuiltin:: is always used. Setting this will emit a warning, to alert any remaining users that setting this now does nothing.+stash.showIncludeUntracked::+ If this is set to true, the `git stash show` command without an+ option will show the untracked files of a stash entry. Defaults to+ false. See description of 'show' command in linkgit:git-stash[1].+ stash.showPatch:: If this is set to true, the `git stash show` command without an option will show the stash entry in patch form. Defaults to false.
@@ -91,8 +91,8 @@ show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>]:: By default, the command shows the diffstat, but it will accept any format known to 'git diff' (e.g., `git stash show -p stash@{1}` to view the second most recent entry in patch form).- You can use stash.showStat and/or stash.showPatch config variables- to change the default behavior.+ You can use stash.showIncludeUntracked, stash.showStat, and+ stash.showPatch config variables to change the default behavior. pop [--index] [-q|--quiet] [<stash>]::
From: Denton Liu <hidden> Date: 2021-03-04 00:22:54
The previous commit teaches `git stash show --include-untracked`. It
may be desirable for a user to be able to always enable the
--include-untracked behavior. Teach the stash.showIncludeUntracked
config option which allows users to do this in a similar manner to
stash.showPatch.
Signed-off-by: Denton Liu <redacted>
---
Documentation/config/stash.txt | 5 +++++
Documentation/git-stash.txt | 4 ++--
builtin/stash.c | 8 ++++++++
t/t3905-stash-include-untracked.sh | 2 ++
4 files changed, 17 insertions(+), 2 deletions(-)
@@ -5,6 +5,11 @@ stash.useBuiltin:: is always used. Setting this will emit a warning, to alert any remaining users that setting this now does nothing.+stash.showIncludeUntracked::+ If this is set to true, the `git stash show` command without an+ option will show the untracked files of a stash entry. Defaults to+ false. See description of 'show' command in linkgit:git-stash[1].+ stash.showPatch:: If this is set to true, the `git stash show` command without an option will show the stash entry in patch form. Defaults to false.
@@ -91,8 +91,8 @@ show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>]:: By default, the command shows the diffstat, but it will accept any format known to 'git diff' (e.g., `git stash show -p stash@{1}` to view the second most recent entry in patch form).- You can use stash.showStat and/or stash.showPatch config variables- to change the default behavior.+ You can use stash.showIncludeUntracked, stash.showStat, and+ stash.showPatch config variables to change the default behavior. pop [--index] [-q|--quiet] [<stash>]::
From: Denton Liu <hidden> Date: 2021-03-04 00:22:54
Stash entries can be made with untracked files via
`git stash push --include-untracked`. However, because the untracked
files are stored in the third parent of the stash entry and not the
stash entry itself, running `git stash show` does not include the
untracked files as part of the diff.
With --include-untracked, untracked paths, which are recorded in the
third-parent if it exists, are shown in addition to the paths that have
modifications between the stash base and the working tree in the stash.
It is possible to manually craft a malformed stash entry where duplicate
untracked files in the stash entry will mask tracked files. We detect
and error out in that case via a custom unpack_trees() callback:
stash_worktree_untracked_merge().
Also, teach stash the --only-untracked option which only shows the
untracked files of a stash entry. This is similar to `git show stash^3`
but it is nice to provide a convenient abstraction for it so that users
do not have to think about the underlying implementation.
Signed-off-by: Denton Liu <redacted>
---
Documentation/git-stash.txt | 18 +++--
builtin/stash.c | 54 ++++++++++++-
contrib/completion/git-completion.bash | 2 +-
t/t3905-stash-include-untracked.sh | 106 +++++++++++++++++++++++++
unpack-trees.c | 22 +++++
unpack-trees.h | 2 +
6 files changed, 197 insertions(+), 7 deletions(-)
@@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git stash' list [<log-options>]-'git stash' show [<diff-options>] [<stash>]+'git stash' show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>] 'git stash' drop [-q|--quiet] [<stash>] 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>] 'git stash' branch <branchname> [<stash>]
@@ -83,7 +83,7 @@ stash@{1}: On master: 9cc0589... Add git-stash The command takes options applicable to the 'git log' command to control what is shown and how. See linkgit:git-log[1].-show [<diff-options>] [<stash>]::+show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>]:: Show the changes recorded in the stash entry as a diff between the stashed contents and the commit back when the stash entry was first
@@ -160,10 +160,18 @@ up with `git clean`. -u:: --include-untracked::- This option is only valid for `push` and `save` commands.+--no-include-untracked::+ When used with the `push` and `save` commands,+ all untracked files are also stashed and then cleaned up with+ `git clean`. +-All untracked files are also stashed and then cleaned up with-`git clean`.+When used with the `show` command, show the untracked files in the stash+entry as part of the diff.++--only-untracked::+ This option is only valid for the `show` command.+++Show only the untracked files in the stash entry as part of the diff. --index:: This option is only valid for `pop` and `apply` commands.
@@ -787,6 +787,33 @@ static int git_stash_config(const char *var, const char *value, void *cb)returngit_diff_basic_config(var,value,cb);}+staticvoiddiff_include_untracked(conststructstash_info*info,structdiff_options*diff_opt)+{+conststructobject_id*oid[]={&info->w_commit,&info->u_tree};+structtree*tree[ARRAY_SIZE(oid)];+structtree_desctree_desc[ARRAY_SIZE(oid)];+structunpack_trees_optionsunpack_tree_opt={0};+inti;++for(i=0;i<ARRAY_SIZE(oid);i++){+tree[i]=parse_tree_indirect(oid[i]);+if(parse_tree(tree[i])<0)+die(_("failed to parse tree"));+init_tree_desc(&tree_desc[i],tree[i]->buffer,tree[i]->size);+}++unpack_tree_opt.head_idx=-1;+unpack_tree_opt.src_index=&the_index;+unpack_tree_opt.dst_index=&the_index;+unpack_tree_opt.merge=1;+unpack_tree_opt.fn=stash_worktree_untracked_merge;++if(unpack_trees(ARRAY_SIZE(tree_desc),tree_desc,&unpack_tree_opt))+die(_("failed to unpack trees"));++do_diff_cache(&info->b_commit,diff_opt);+}+staticintshow_stash(intargc,constchar**argv,constchar*prefix){inti;
@@ -795,7 +822,18 @@ static int show_stash(int argc, const char **argv, const char *prefix)structrev_inforev;structstrvecstash_args=STRVEC_INIT;structstrvecrevision_args=STRVEC_INIT;+enum{+UNTRACKED_NONE,+UNTRACKED_INCLUDE,+UNTRACKED_ONLY+}show_untracked=UNTRACKED_NONE;structoptionoptions[]={+OPT_SET_INT('u',"include-untracked",&show_untracked,+N_("include untracked files in the stash"),+UNTRACKED_INCLUDE),+OPT_SET_INT_F(0,"only-untracked",&show_untracked,+N_("only show untracked files in the stash"),+UNTRACKED_ONLY,PARSE_OPT_NONEG),OPT_END()};
@@ -297,4 +297,110 @@ test_expect_success 'stash -u with globs' 'test_path_is_missinguntracked.txt'+test_expect_success'stash show --include-untracked shows untracked files''+gitreset--hard&&+gitclean-xf&&+>untracked&&+>tracked&&+gitaddtracked&&+empty_blob_oid=$(gitrev-parse--short:tracked)&&+gitstash-u&&++cat>expect<<-EOF&&+tracked|0+untracked|0+2fileschanged,0insertions(+),0deletions(-)+EOF+gitstashshow--include-untracked>actual&&+test_cmpexpectactual&&+gitstashshow-u>actual&&+test_cmpexpectactual&&+gitstashshow--no-include-untracked--include-untracked>actual&&+test_cmpexpectactual&&+gitstashshow--only-untracked--include-untracked>actual&&+test_cmpexpectactual&&++cat>expect<<-EOF&&+diff--gita/trackedb/tracked+newfilemode100644+index0000000..$empty_blob_oid+diff--gita/untrackedb/untracked+newfilemode100644+index0000000..$empty_blob_oid+EOF+gitstashshow-p--include-untracked>actual&&+test_cmpexpectactual&&+gitstashshow--include-untracked-p>actual&&+test_cmpexpectactual+'++test_expect_success'stash show --only-untracked only shows untracked files''+gitreset--hard&&+gitclean-xf&&+>untracked&&+>tracked&&+gitaddtracked&&+empty_blob_oid=$(gitrev-parse--short:tracked)&&+gitstash-u&&++cat>expect<<-EOF&&+untracked|0+1filechanged,0insertions(+),0deletions(-)+EOF+gitstashshow--only-untracked>actual&&+test_cmpexpectactual&&+gitstashshow--no-include-untracked--only-untracked>actual&&+test_cmpexpectactual&&+gitstashshow--include-untracked--only-untracked>actual&&+test_cmpexpectactual&&++cat>expect<<-EOF&&+diff--gita/untrackedb/untracked+newfilemode100644+index0000000..$empty_blob_oid+EOF+gitstashshow-p--only-untracked>actual&&+test_cmpexpectactual&&+gitstashshow--only-untracked-p>actual&&+test_cmpexpectactual+'++test_expect_success'stash show --no-include-untracked cancels --{include,show}-untracked''+gitreset--hard&&+gitclean-xf&&+>untracked&&+>tracked&&+gitaddtracked&&+gitstash-u&&++cat>expect<<-EOF&&+tracked|0+1filechanged,0insertions(+),0deletions(-)+EOF+gitstashshow--only-untracked--no-include-untracked>actual&&+test_cmpexpectactual&&+gitstashshow--include-untracked--no-include-untracked>actual&&+test_cmpexpectactual+'++test_expect_success'stash show --include-untracked errors on duplicate files''+gitreset--hard&&+gitclean-xf&&+>tracked&&+gitaddtracked&&+tree=$(gitwrite-tree)&&+i_commit=$(gitcommit-tree-pHEAD-m"index on any-branch""$tree")&&+test_when_finished"rm -f untracked_index"&&+u_commit=$(+GIT_INDEX_FILE="untracked_index"&&+exportGIT_INDEX_FILE&&+gitupdate-index--addtracked&&+u_tree=$(gitwrite-tree)&&+gitcommit-tree-m"untracked files on any-branch""$u_tree"+)&&+w_commit=$(gitcommit-tree-pHEAD-p"$i_commit"-p"$u_commit"-m"WIP on any-branch""$tree")&&+test_must_failgitstashshow--include-untracked"$w_commit"2>err&&+test_i18ngrep"worktree and untracked commit have duplicate entries: tracked"err+'+ test_done
From: Denton Liu <hidden> Date: 2021-03-04 00:22:55
A blindspot that I've noticed in git is that it's not possible to
properly view a stash entry that has untracked files via `git stash
show`. Teach `git stash show --include-untracked` which should do this.
In addition, this series also teaches `--only-untracked` and the
`stash.showIncludeUntracked` config option.
This series is based on 'dl/stash-cleanup'.
Changes since v3:
* Incorporate Junio's SQUASH??? commits
* Implement a custom unpack_trees() callback to detect the case where
there are duplicate entries in worktree and untracked commits
Changes since v2:
* Base this series on top of 'dl/stash-cleanup'
* Attempt to replicate the read-tree code to merge the untracked tree
Changes since v1:
* Add a dash for <log-options> and <diff-options>
* Fix the misspelling of --include-untracked in a commit message
* Change the approach from concatenating diffs to using `git read-tree`
Denton Liu (2):
stash show: teach --include-untracked and --only-untracked
stash show: learn stash.showIncludeUntracked
Documentation/config/stash.txt | 5 ++
Documentation/git-stash.txt | 22 +++--
builtin/stash.c | 62 +++++++++++++-
contrib/completion/git-completion.bash | 2 +-
t/t3905-stash-include-untracked.sh | 108 +++++++++++++++++++++++++
unpack-trees.c | 22 +++++
unpack-trees.h | 2 +
7 files changed, 214 insertions(+), 9 deletions(-)
Range-diff against v3:
1: 85b81f2f06 ! 1: af3757135b stash show: teach --include-untracked and --only-untracked
@@ Commit message
third-parent if it exists, are shown in addition to the paths that have
modifications between the stash base and the working tree in the stash.
- One limitation of this is that it would be possible to manually craft a
- stash entry where duplicate untracked files in the stash entry will mask
- tracked files. This seems like an instance of "Doctor, it hurts when I
- do this! So don't do that!" so this can be written off.
+ It is possible to manually craft a malformed stash entry where duplicate
+ untracked files in the stash entry will mask tracked files. We detect
+ and error out in that case via a custom unpack_trees() callback:
+ stash_worktree_untracked_merge().
Also, teach stash the --only-untracked option which only shows the
untracked files of a stash entry. This is similar to `git show stash^3`
but it is nice to provide a convenient abstraction for it so that users
do not have to think about the underlying implementation.
-
- ## Notes ##
- I am not familiar with the read-tree code so this attempt at replicating
- the read-tree code may in diff_include_untracked() may be incorrect
- (particularly the use of the_index?).
-
- Also, I could not figure out how to make unpack_trees() error out in the
- case where untracked tree entry contains duplicate entries with the
- worktree entry.
-
## Documentation/git-stash.txt ##
@@ Documentation/git-stash.txt: SYNOPSIS
--------
@@ builtin/stash.c: static int git_stash_config(const char *var, const char *value,
+ unpack_tree_opt.head_idx = -1;
+ unpack_tree_opt.src_index = &the_index;
+ unpack_tree_opt.dst_index = &the_index;
-+ unpack_tree_opt.fn = twoway_merge;
++ unpack_tree_opt.merge = 1;
++ unpack_tree_opt.fn = stash_worktree_untracked_merge;
+
+ if (unpack_trees(ARRAY_SIZE(tree_desc), tree_desc, &unpack_tree_opt))
+ die(_("failed to unpack trees"));
@@ t/t3905-stash-include-untracked.sh: test_expect_success 'stash -u with globs' '
+ >untracked &&
+ >tracked &&
+ git add tracked &&
++ empty_blob_oid=$(git rev-parse --short :tracked) &&
+ git stash -u &&
+
+ cat >expect <<-EOF &&
@@ t/t3905-stash-include-untracked.sh: test_expect_success 'stash -u with globs' '
+ cat >expect <<-EOF &&
+ diff --git a/tracked b/tracked
+ new file mode 100644
-+ index 0000000..e69de29
++ index 0000000..$empty_blob_oid
+ diff --git a/untracked b/untracked
+ new file mode 100644
-+ index 0000000..e69de29
++ index 0000000..$empty_blob_oid
+ EOF
+ git stash show -p --include-untracked >actual &&
+ test_cmp expect actual &&
@@ t/t3905-stash-include-untracked.sh: test_expect_success 'stash -u with globs' '
+ >untracked &&
+ >tracked &&
+ git add tracked &&
++ empty_blob_oid=$(git rev-parse --short :tracked) &&
+ git stash -u &&
+
+ cat >expect <<-EOF &&
@@ t/t3905-stash-include-untracked.sh: test_expect_success 'stash -u with globs' '
+ cat >expect <<-EOF &&
+ diff --git a/untracked b/untracked
+ new file mode 100644
-+ index 0000000..e69de29
++ index 0000000..$empty_blob_oid
+ EOF
+ git stash show -p --only-untracked >actual &&
+ test_cmp expect actual &&
@@ t/t3905-stash-include-untracked.sh: test_expect_success 'stash -u with globs' '
+ git stash show --include-untracked --no-include-untracked >actual &&
+ test_cmp expect actual
+'
++
++test_expect_success 'stash show --include-untracked errors on duplicate files' '
++ git reset --hard &&
++ git clean -xf &&
++ >tracked &&
++ git add tracked &&
++ tree=$(git write-tree) &&
++ i_commit=$(git commit-tree -p HEAD -m "index on any-branch" "$tree") &&
++ test_when_finished "rm -f untracked_index" &&
++ u_commit=$(
++ GIT_INDEX_FILE="untracked_index" &&
++ export GIT_INDEX_FILE &&
++ git update-index --add tracked &&
++ u_tree=$(git write-tree) &&
++ git commit-tree -m "untracked files on any-branch" "$u_tree"
++ ) &&
++ w_commit=$(git commit-tree -p HEAD -p "$i_commit" -p "$u_commit" -m "WIP on any-branch" "$tree") &&
++ test_must_fail git stash show --include-untracked "$w_commit" 2>err &&
++ test_i18ngrep "worktree and untracked commit have duplicate entries: tracked" err
++'
+
test_done
+
+ ## unpack-trees.c ##
+@@ unpack-trees.c: int oneway_merge(const struct cache_entry * const *src,
+ }
+ return merged_entry(a, old, o);
+ }
++
++/*
++ * Merge worktree and untracked entries in a stash entry.
++ *
++ * Ignore all index entries. Collapse remaining trees but make sure that they
++ * don't have any conflicting files.
++ */
++int stash_worktree_untracked_merge(const struct cache_entry * const *src,
++ struct unpack_trees_options *o)
++{
++ const struct cache_entry *worktree = src[1];
++ const struct cache_entry *untracked = src[2];
++
++ if (o->merge_size != 2)
++ BUG("invalid merge_size: %d", o->merge_size);
++
++ if (worktree && untracked)
++ return error(_("worktree and untracked commit have duplicate entries: %s"),
++ super_prefixed(worktree->name));
++
++ return merged_entry(worktree ? worktree : untracked, NULL, o);
++}
+
+ ## unpack-trees.h ##
+@@ unpack-trees.h: int bind_merge(const struct cache_entry * const *src,
+ struct unpack_trees_options *o);
+ int oneway_merge(const struct cache_entry * const *src,
+ struct unpack_trees_options *o);
++int stash_worktree_untracked_merge(const struct cache_entry * const *src,
++ struct unpack_trees_options *o);
+
+ #endif
2: d19d07ec27 = 2: 3480086f1d stash show: learn stash.showIncludeUntracked
--
2.31.0.rc1.228.gb75b4e4ce2