Merge branch 'ps/stash-in-c'

"git stash" rewritten in C.

* ps/stash-in-c: (28 commits)
  tests: add a special setup where stash.useBuiltin is off
  stash: optionally use the scripted version again
  stash: add back the original, scripted `git stash`
  stash: convert `stash--helper.c` into `stash.c`
  stash: replace all `write-tree` child processes with API calls
  stash: optimize `get_untracked_files()` and `check_changes()`
  stash: convert save to builtin
  stash: make push -q quiet
  stash: convert push to builtin
  stash: convert create to builtin
  stash: convert store to builtin
  stash: convert show to builtin
  stash: convert list to builtin
  stash: convert pop to builtin
  stash: convert branch to builtin
  stash: convert drop and clear to builtin
  stash: convert apply to builtin
  stash: mention options in `show` synopsis
  stash: add tests for `git stash show` config
  stash: rename test cases to be more descriptive
  ...
This commit is contained in:
Junio C Hamano 2019-04-22 11:14:43 +09:00
commit e36adf7122
16 changed files with 2017 additions and 72 deletions

1
.gitignore vendored
View File

@ -82,6 +82,7 @@
/git-init-db /git-init-db
/git-interpret-trailers /git-interpret-trailers
/git-instaweb /git-instaweb
/git-legacy-stash
/git-log /git-log
/git-ls-files /git-ls-files
/git-ls-remote /git-ls-remote

View File

@ -9,7 +9,7 @@ SYNOPSIS
-------- --------
[verse] [verse]
'git stash' list [<options>] 'git stash' list [<options>]
'git stash' show [<stash>] 'git stash' show [<options>] [<stash>]
'git stash' drop [-q|--quiet] [<stash>] 'git stash' drop [-q|--quiet] [<stash>]
'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>] 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
'git stash' branch <branchname> [<stash>] 'git stash' branch <branchname> [<stash>]
@ -106,7 +106,7 @@ stash@{1}: On master: 9cc0589... Add git-stash
The command takes options applicable to the 'git log' The command takes options applicable to the 'git log'
command to control what is shown and how. See linkgit:git-log[1]. command to control what is shown and how. See linkgit:git-log[1].
show [<stash>]:: show [<options>] [<stash>]::
Show the changes recorded in the stash entry as a diff between the 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 stashed contents and the commit back when the stash entry was first

View File

@ -613,9 +613,9 @@ SCRIPT_SH += git-merge-one-file.sh
SCRIPT_SH += git-merge-resolve.sh SCRIPT_SH += git-merge-resolve.sh
SCRIPT_SH += git-mergetool.sh SCRIPT_SH += git-mergetool.sh
SCRIPT_SH += git-quiltimport.sh SCRIPT_SH += git-quiltimport.sh
SCRIPT_SH += git-legacy-stash.sh
SCRIPT_SH += git-remote-testgit.sh SCRIPT_SH += git-remote-testgit.sh
SCRIPT_SH += git-request-pull.sh SCRIPT_SH += git-request-pull.sh
SCRIPT_SH += git-stash.sh
SCRIPT_SH += git-submodule.sh SCRIPT_SH += git-submodule.sh
SCRIPT_SH += git-web--browse.sh SCRIPT_SH += git-web--browse.sh
@ -1130,6 +1130,7 @@ BUILTIN_OBJS += builtin/shortlog.o
BUILTIN_OBJS += builtin/show-branch.o BUILTIN_OBJS += builtin/show-branch.o
BUILTIN_OBJS += builtin/show-index.o BUILTIN_OBJS += builtin/show-index.o
BUILTIN_OBJS += builtin/show-ref.o BUILTIN_OBJS += builtin/show-ref.o
BUILTIN_OBJS += builtin/stash.o
BUILTIN_OBJS += builtin/stripspace.o BUILTIN_OBJS += builtin/stripspace.o
BUILTIN_OBJS += builtin/submodule--helper.o BUILTIN_OBJS += builtin/submodule--helper.o
BUILTIN_OBJS += builtin/symbolic-ref.o BUILTIN_OBJS += builtin/symbolic-ref.o

View File

@ -225,6 +225,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix);
extern int cmd_show_branch(int argc, const char **argv, const char *prefix); extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
extern int cmd_show_index(int argc, const char **argv, const char *prefix); extern int cmd_show_index(int argc, const char **argv, const char *prefix);
extern int cmd_status(int argc, const char **argv, const char *prefix); extern int cmd_status(int argc, const char **argv, const char *prefix);
extern int cmd_stash(int argc, const char **argv, const char *prefix);
extern int cmd_stripspace(int argc, const char **argv, const char *prefix); extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix); extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix); extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);

1635
builtin/stash.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1379,6 +1379,7 @@ enum get_oid_result {
}; };
extern int get_oid(const char *str, struct object_id *oid); extern int get_oid(const char *str, struct object_id *oid);
extern int get_oidf(struct object_id *oid, const char *fmt, ...);
extern int get_oid_commit(const char *str, struct object_id *oid); extern int get_oid_commit(const char *str, struct object_id *oid);
extern int get_oid_committish(const char *str, struct object_id *oid); extern int get_oid_committish(const char *str, struct object_id *oid);
extern int get_oid_tree(const char *str, struct object_id *oid); extern int get_oid_tree(const char *str, struct object_id *oid);
@ -1552,6 +1553,10 @@ extern const char *git_sequence_editor(void);
extern const char *git_pager(int stdout_is_tty); extern const char *git_pager(int stdout_is_tty);
extern int is_terminal_dumb(void); extern int is_terminal_dumb(void);
extern int git_ident_config(const char *, const char *, void *); extern int git_ident_config(const char *, const char *, void *);
/*
* Prepare an ident to fall back on if the user didn't configure it.
*/
void prepare_fallback_ident(const char *name, const char *email);
extern void reset_ident_date(void); extern void reset_ident_date(void);
struct ident_split { struct ident_split {

View File

@ -80,6 +80,28 @@ clear_stash () {
fi fi
} }
maybe_quiet () {
case "$1" in
--keep-stdout)
shift
if test -n "$GIT_QUIET"
then
eval "$@" 2>/dev/null
else
eval "$@"
fi
;;
*)
if test -n "$GIT_QUIET"
then
eval "$@" >/dev/null 2>&1
else
eval "$@"
fi
;;
esac
}
create_stash () { create_stash () {
prepare_fallback_ident prepare_fallback_ident
@ -112,15 +134,18 @@ create_stash () {
done done
git update-index -q --refresh git update-index -q --refresh
if no_changes "$@" if maybe_quiet no_changes "$@"
then then
exit 0 exit 0
fi fi
# state of the base commit # state of the base commit
if b_commit=$(git rev-parse --verify HEAD) if b_commit=$(maybe_quiet --keep-stdout git rev-parse --verify HEAD)
then then
head=$(git rev-list --oneline -n 1 HEAD --) head=$(git rev-list --oneline -n 1 HEAD --)
elif test -n "$GIT_QUIET"
then
exit 1
else else
die "$(gettext "You do not have the initial commit yet")" die "$(gettext "You do not have the initial commit yet")"
fi fi
@ -315,7 +340,7 @@ push_stash () {
test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1 test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1
git update-index -q --refresh git update-index -q --refresh
if no_changes "$@" if maybe_quiet no_changes "$@"
then then
say "$(gettext "No local changes to save")" say "$(gettext "No local changes to save")"
exit 0 exit 0
@ -370,6 +395,9 @@ save_stash () {
while test $# != 0 while test $# != 0
do do
case "$1" in case "$1" in
-q|--quiet)
GIT_QUIET=t
;;
--) --)
shift shift
break break

View File

@ -101,6 +101,7 @@ $LONG_USAGE")"
case "$1" in case "$1" in
-h) -h)
echo "$LONG_USAGE" echo "$LONG_USAGE"
case "$0" in *git-legacy-stash) exit 129;; esac
exit exit
esac esac
fi fi

6
git.c
View File

@ -577,6 +577,12 @@ static struct cmd_struct commands[] = {
{ "show-index", cmd_show_index }, { "show-index", cmd_show_index },
{ "show-ref", cmd_show_ref, RUN_SETUP }, { "show-ref", cmd_show_ref, RUN_SETUP },
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE }, { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
/*
* NEEDSWORK: Until the builtin stash is thoroughly robust and no
* longer needs redirection to the stash shell script this is kept as
* is, then should be changed to RUN_SETUP | NEED_WORK_TREE
*/
{ "stash", cmd_stash },
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE }, { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
{ "stripspace", cmd_stripspace }, { "stripspace", cmd_stripspace },
{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT }, { "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },

20
ident.c
View File

@ -585,6 +585,26 @@ int git_ident_config(const char *var, const char *value, void *data)
return set_ident(var, value); return set_ident(var, value);
} }
static void set_env_if(const char *key, const char *value, int *given, int bit)
{
if ((*given & bit) || getenv(key))
return; /* nothing to do */
setenv(key, value, 0);
*given |= bit;
}
void prepare_fallback_ident(const char *name, const char *email)
{
set_env_if("GIT_AUTHOR_NAME", name,
&author_ident_explicitly_given, IDENT_NAME_GIVEN);
set_env_if("GIT_AUTHOR_EMAIL", email,
&author_ident_explicitly_given, IDENT_MAIL_GIVEN);
set_env_if("GIT_COMMITTER_NAME", name,
&committer_ident_explicitly_given, IDENT_NAME_GIVEN);
set_env_if("GIT_COMMITTER_EMAIL", email,
&committer_ident_explicitly_given, IDENT_MAIL_GIVEN);
}
static int buf_cmp(const char *a_begin, const char *a_end, static int buf_cmp(const char *a_begin, const char *a_end,
const char *b_begin, const char *b_end) const char *b_begin, const char *b_end)
{ {

View File

@ -1530,6 +1530,25 @@ int get_oid(const char *name, struct object_id *oid)
return get_oid_with_context(the_repository, name, 0, oid, &unused); return get_oid_with_context(the_repository, name, 0, oid, &unused);
} }
/*
* This returns a non-zero value if the string (built using printf
* format and the given arguments) is not a valid object.
*/
int get_oidf(struct object_id *oid, const char *fmt, ...)
{
va_list ap;
int ret;
struct strbuf sb = STRBUF_INIT;
va_start(ap, fmt);
strbuf_vaddf(&sb, fmt, ap);
va_end(ap);
ret = get_oid(sb.buf, oid);
strbuf_release(&sb);
return ret;
}
/* /*
* Many callers know that the user meant to name a commit-ish by * Many callers know that the user meant to name a commit-ish by

View File

@ -249,6 +249,42 @@ void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len)
strbuf_splice(sb, pos, 0, data, len); strbuf_splice(sb, pos, 0, data, len);
} }
void strbuf_vinsertf(struct strbuf *sb, size_t pos, const char *fmt, va_list ap)
{
int len, len2;
char save;
va_list cp;
if (pos > sb->len)
die("`pos' is too far after the end of the buffer");
va_copy(cp, ap);
len = vsnprintf(sb->buf + sb->len, 0, fmt, cp);
va_end(cp);
if (len < 0)
BUG("your vsnprintf is broken (returned %d)", len);
if (!len)
return; /* nothing to do */
if (unsigned_add_overflows(sb->len, len))
die("you want to use way too much memory");
strbuf_grow(sb, len);
memmove(sb->buf + pos + len, sb->buf + pos, sb->len - pos);
/* vsnprintf() will append a NUL, overwriting one of our characters */
save = sb->buf[pos + len];
len2 = vsnprintf(sb->buf + pos, len + 1, fmt, ap);
sb->buf[pos + len] = save;
if (len2 != len)
BUG("your vsnprintf is broken (returns inconsistent lengths)");
strbuf_setlen(sb, sb->len + len);
}
void strbuf_insertf(struct strbuf *sb, size_t pos, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
strbuf_vinsertf(sb, pos, fmt, ap);
va_end(ap);
}
void strbuf_remove(struct strbuf *sb, size_t pos, size_t len) void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)
{ {
strbuf_splice(sb, pos, len, "", 0); strbuf_splice(sb, pos, len, "", 0);
@ -268,6 +304,21 @@ void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)
strbuf_setlen(sb, sb->len + sb2->len); strbuf_setlen(sb, sb->len + sb2->len);
} }
const char *strbuf_join_argv(struct strbuf *buf,
int argc, const char **argv, char delim)
{
if (!argc)
return buf->buf;
strbuf_addstr(buf, *argv);
while (--argc) {
strbuf_addch(buf, delim);
strbuf_addstr(buf, *(++argv));
}
return buf->buf;
}
void strbuf_addchars(struct strbuf *sb, int c, size_t n) void strbuf_addchars(struct strbuf *sb, int c, size_t n)
{ {
strbuf_grow(sb, n); strbuf_grow(sb, n);

View File

@ -244,6 +244,15 @@ void strbuf_addchars(struct strbuf *sb, int c, size_t n);
*/ */
void strbuf_insert(struct strbuf *sb, size_t pos, const void *, size_t); void strbuf_insert(struct strbuf *sb, size_t pos, const void *, size_t);
/**
* Insert data to the given position of the buffer giving a printf format
* string. The contents will be shifted, not overwritten.
*/
void strbuf_vinsertf(struct strbuf *sb, size_t pos, const char *fmt,
va_list ap);
void strbuf_insertf(struct strbuf *sb, size_t pos, const char *fmt, ...);
/** /**
* Remove given amount of data from a given position of the buffer. * Remove given amount of data from a given position of the buffer.
*/ */
@ -288,6 +297,13 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s)
*/ */
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2); void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
/**
* Join the arguments into a buffer. `delim` is put between every
* two arguments.
*/
const char *strbuf_join_argv(struct strbuf *buf, int argc,
const char **argv, char delim);
/** /**
* This function can be used to expand a format string containing * This function can be used to expand a format string containing
* placeholders. To that end, it parses the string and calls the specified * placeholders. To that end, it parses the string and calls the specified

View File

@ -384,6 +384,10 @@ the --no-sparse command-line argument.
GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path
by overriding the minimum number of cache entries required per thread. by overriding the minimum number of cache entries required per thread.
GIT_TEST_STASH_USE_BUILTIN=<boolean>, when false, disables the
built-in version of git-stash. See 'stash.useBuiltin' in
git-config(1).
GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading
of the index for the whole test suite by bypassing the default number of of the index for the whole test suite by bypassing the default number of
cache entries and thread minimums. Setting this to 1 will make the cache entries and thread minimums. Setting this to 1 will make the

View File

@ -8,22 +8,22 @@ test_description='Test git stash'
. ./test-lib.sh . ./test-lib.sh
test_expect_success 'stash some dirty working directory' ' test_expect_success 'stash some dirty working directory' '
echo 1 > file && echo 1 >file &&
git add file && git add file &&
echo unrelated >other-file && echo unrelated >other-file &&
git add other-file && git add other-file &&
test_tick && test_tick &&
git commit -m initial && git commit -m initial &&
echo 2 > file && echo 2 >file &&
git add file && git add file &&
echo 3 > file && echo 3 >file &&
test_tick && test_tick &&
git stash && git stash &&
git diff-files --quiet && git diff-files --quiet &&
git diff-index --cached --quiet HEAD git diff-index --cached --quiet HEAD
' '
cat > expect << EOF cat >expect <<EOF
diff --git a/file b/file diff --git a/file b/file
index 0cfbf08..00750ed 100644 index 0cfbf08..00750ed 100644
--- a/file --- a/file
@ -35,7 +35,7 @@ EOF
test_expect_success 'parents of stash' ' test_expect_success 'parents of stash' '
test $(git rev-parse stash^) = $(git rev-parse HEAD) && test $(git rev-parse stash^) = $(git rev-parse HEAD) &&
git diff stash^2..stash > output && git diff stash^2..stash >output &&
test_cmp expect output test_cmp expect output
' '
@ -74,7 +74,7 @@ test_expect_success 'apply stashed changes' '
test_expect_success 'apply stashed changes (including index)' ' test_expect_success 'apply stashed changes (including index)' '
git reset --hard HEAD^ && git reset --hard HEAD^ &&
echo 6 > other-file && echo 6 >other-file &&
git add other-file && git add other-file &&
test_tick && test_tick &&
git commit -m other-file && git commit -m other-file &&
@ -99,12 +99,12 @@ test_expect_success 'stash drop complains of extra options' '
test_expect_success 'drop top stash' ' test_expect_success 'drop top stash' '
git reset --hard && git reset --hard &&
git stash list > stashlist1 && git stash list >expected &&
echo 7 > file && echo 7 >file &&
git stash && git stash &&
git stash drop && git stash drop &&
git stash list > stashlist2 && git stash list >actual &&
test_cmp stashlist1 stashlist2 && test_cmp expected actual &&
git stash apply && git stash apply &&
test 3 = $(cat file) && test 3 = $(cat file) &&
test 1 = $(git show :file) && test 1 = $(git show :file) &&
@ -113,9 +113,9 @@ test_expect_success 'drop top stash' '
test_expect_success 'drop middle stash' ' test_expect_success 'drop middle stash' '
git reset --hard && git reset --hard &&
echo 8 > file && echo 8 >file &&
git stash && git stash &&
echo 9 > file && echo 9 >file &&
git stash && git stash &&
git stash drop stash@{1} && git stash drop stash@{1} &&
test 2 = $(git stash list | wc -l) && test 2 = $(git stash list | wc -l) &&
@ -160,7 +160,7 @@ test_expect_success 'stash pop' '
test 0 = $(git stash list | wc -l) test 0 = $(git stash list | wc -l)
' '
cat > expect << EOF cat >expect <<EOF
diff --git a/file2 b/file2 diff --git a/file2 b/file2
new file mode 100644 new file mode 100644
index 0000000..1fe912c index 0000000..1fe912c
@ -170,7 +170,7 @@ index 0000000..1fe912c
+bar2 +bar2
EOF EOF
cat > expect1 << EOF cat >expect1 <<EOF
diff --git a/file b/file diff --git a/file b/file
index 257cc56..5716ca5 100644 index 257cc56..5716ca5 100644
--- a/file --- a/file
@ -180,7 +180,7 @@ index 257cc56..5716ca5 100644
+bar +bar
EOF EOF
cat > expect2 << EOF cat >expect2 <<EOF
diff --git a/file b/file diff --git a/file b/file
index 7601807..5716ca5 100644 index 7601807..5716ca5 100644
--- a/file --- a/file
@ -198,79 +198,79 @@ index 0000000..1fe912c
EOF EOF
test_expect_success 'stash branch' ' test_expect_success 'stash branch' '
echo foo > file && echo foo >file &&
git commit file -m first && git commit file -m first &&
echo bar > file && echo bar >file &&
echo bar2 > file2 && echo bar2 >file2 &&
git add file2 && git add file2 &&
git stash && git stash &&
echo baz > file && echo baz >file &&
git commit file -m second && git commit file -m second &&
git stash branch stashbranch && git stash branch stashbranch &&
test refs/heads/stashbranch = $(git symbolic-ref HEAD) && test refs/heads/stashbranch = $(git symbolic-ref HEAD) &&
test $(git rev-parse HEAD) = $(git rev-parse master^) && test $(git rev-parse HEAD) = $(git rev-parse master^) &&
git diff --cached > output && git diff --cached >output &&
test_cmp expect output && test_cmp expect output &&
git diff > output && git diff >output &&
test_cmp expect1 output && test_cmp expect1 output &&
git add file && git add file &&
git commit -m alternate\ second && git commit -m alternate\ second &&
git diff master..stashbranch > output && git diff master..stashbranch >output &&
test_cmp output expect2 && test_cmp output expect2 &&
test 0 = $(git stash list | wc -l) test 0 = $(git stash list | wc -l)
' '
test_expect_success 'apply -q is quiet' ' test_expect_success 'apply -q is quiet' '
echo foo > file && echo foo >file &&
git stash && git stash &&
git stash apply -q > output.out 2>&1 && git stash apply -q >output.out 2>&1 &&
test_must_be_empty output.out test_must_be_empty output.out
' '
test_expect_success 'save -q is quiet' ' test_expect_success 'save -q is quiet' '
git stash save --quiet > output.out 2>&1 && git stash save --quiet >output.out 2>&1 &&
test_must_be_empty output.out test_must_be_empty output.out
' '
test_expect_success 'pop -q is quiet' ' test_expect_success 'pop -q is quiet' '
git stash pop -q > output.out 2>&1 && git stash pop -q >output.out 2>&1 &&
test_must_be_empty output.out test_must_be_empty output.out
' '
test_expect_success 'pop -q --index works and is quiet' ' test_expect_success 'pop -q --index works and is quiet' '
echo foo > file && echo foo >file &&
git add file && git add file &&
git stash save --quiet && git stash save --quiet &&
git stash pop -q --index > output.out 2>&1 && git stash pop -q --index >output.out 2>&1 &&
test foo = "$(git show :file)" && test foo = "$(git show :file)" &&
test_must_be_empty output.out test_must_be_empty output.out
' '
test_expect_success 'drop -q is quiet' ' test_expect_success 'drop -q is quiet' '
git stash && git stash &&
git stash drop -q > output.out 2>&1 && git stash drop -q >output.out 2>&1 &&
test_must_be_empty output.out test_must_be_empty output.out
' '
test_expect_success 'stash -k' ' test_expect_success 'stash -k' '
echo bar3 > file && echo bar3 >file &&
echo bar4 > file2 && echo bar4 >file2 &&
git add file2 && git add file2 &&
git stash -k && git stash -k &&
test bar,bar4 = $(cat file),$(cat file2) test bar,bar4 = $(cat file),$(cat file2)
' '
test_expect_success 'stash --no-keep-index' ' test_expect_success 'stash --no-keep-index' '
echo bar33 > file && echo bar33 >file &&
echo bar44 > file2 && echo bar44 >file2 &&
git add file2 && git add file2 &&
git stash --no-keep-index && git stash --no-keep-index &&
test bar,bar2 = $(cat file),$(cat file2) test bar,bar2 = $(cat file),$(cat file2)
' '
test_expect_success 'stash --invalid-option' ' test_expect_success 'stash --invalid-option' '
echo bar5 > file && echo bar5 >file &&
echo bar6 > file2 && echo bar6 >file2 &&
git add file2 && git add file2 &&
test_must_fail git stash --invalid-option && test_must_fail git stash --invalid-option &&
test_must_fail git stash save --invalid-option && test_must_fail git stash save --invalid-option &&
@ -287,6 +287,14 @@ test_expect_success 'stash an added file' '
test new = "$(cat file3)" test new = "$(cat file3)"
' '
test_expect_success 'stash --intent-to-add file' '
git reset --hard &&
echo new >file4 &&
git add --intent-to-add file4 &&
test_when_finished "git rm -f file4" &&
test_must_fail git stash
'
test_expect_success 'stash rm then recreate' ' test_expect_success 'stash rm then recreate' '
git reset --hard && git reset --hard &&
git rm file && git rm file &&
@ -444,6 +452,36 @@ test_expect_failure 'stash file to directory' '
test foo = "$(cat file/file)" test foo = "$(cat file/file)"
' '
test_expect_success 'giving too many ref arguments does not modify files' '
git stash clear &&
test_when_finished "git reset --hard HEAD" &&
echo foo >file2 &&
git stash &&
echo bar >file2 &&
git stash &&
test-tool chmtime =123456789 file2 &&
for type in apply pop "branch stash-branch"
do
test_must_fail git stash $type stash@{0} stash@{1} 2>err &&
test_i18ngrep "Too many revisions" err &&
test 123456789 = $(test-tool chmtime -g file2) || return 1
done
'
test_expect_success 'drop: too many arguments errors out (does nothing)' '
git stash list >expect &&
test_must_fail git stash drop stash@{0} stash@{1} 2>err &&
test_i18ngrep "Too many revisions" err &&
git stash list >actual &&
test_cmp expect actual
'
test_expect_success 'show: too many arguments errors out (does nothing)' '
test_must_fail git stash show stash@{0} stash@{1} 2>err 1>out &&
test_i18ngrep "Too many revisions" err &&
test_must_be_empty out
'
test_expect_success 'stash create - no changes' ' test_expect_success 'stash create - no changes' '
git stash clear && git stash clear &&
test_when_finished "git reset --hard HEAD" && test_when_finished "git reset --hard HEAD" &&
@ -456,11 +494,12 @@ test_expect_success 'stash branch - no stashes on stack, stash-like argument' '
git stash clear && git stash clear &&
test_when_finished "git reset --hard HEAD" && test_when_finished "git reset --hard HEAD" &&
git reset --hard && git reset --hard &&
echo foo >> file && echo foo >>file &&
STASH_ID=$(git stash create) && STASH_ID=$(git stash create) &&
git reset --hard && git reset --hard &&
git stash branch stash-branch ${STASH_ID} && git stash branch stash-branch ${STASH_ID} &&
test_when_finished "git reset --hard HEAD && git checkout master && git branch -D stash-branch" && test_when_finished "git reset --hard HEAD && git checkout master &&
git branch -D stash-branch" &&
test $(git ls-files --modified | wc -l) -eq 1 test $(git ls-files --modified | wc -l) -eq 1
' '
@ -468,25 +507,31 @@ test_expect_success 'stash branch - stashes on stack, stash-like argument' '
git stash clear && git stash clear &&
test_when_finished "git reset --hard HEAD" && test_when_finished "git reset --hard HEAD" &&
git reset --hard && git reset --hard &&
echo foo >> file && echo foo >>file &&
git stash && git stash &&
test_when_finished "git stash drop" && test_when_finished "git stash drop" &&
echo bar >> file && echo bar >>file &&
STASH_ID=$(git stash create) && STASH_ID=$(git stash create) &&
git reset --hard && git reset --hard &&
git stash branch stash-branch ${STASH_ID} && git stash branch stash-branch ${STASH_ID} &&
test_when_finished "git reset --hard HEAD && git checkout master && git branch -D stash-branch" && test_when_finished "git reset --hard HEAD && git checkout master &&
git branch -D stash-branch" &&
test $(git ls-files --modified | wc -l) -eq 1 test $(git ls-files --modified | wc -l) -eq 1
' '
test_expect_success 'stash branch complains with no arguments' '
test_must_fail git stash branch 2>err &&
test_i18ngrep "No branch name specified" err
'
test_expect_success 'stash show format defaults to --stat' ' test_expect_success 'stash show format defaults to --stat' '
git stash clear && git stash clear &&
test_when_finished "git reset --hard HEAD" && test_when_finished "git reset --hard HEAD" &&
git reset --hard && git reset --hard &&
echo foo >> file && echo foo >>file &&
git stash && git stash &&
test_when_finished "git stash drop" && test_when_finished "git stash drop" &&
echo bar >> file && echo bar >>file &&
STASH_ID=$(git stash create) && STASH_ID=$(git stash create) &&
git reset --hard && git reset --hard &&
cat >expected <<-EOF && cat >expected <<-EOF &&
@ -501,10 +546,10 @@ test_expect_success 'stash show - stashes on stack, stash-like argument' '
git stash clear && git stash clear &&
test_when_finished "git reset --hard HEAD" && test_when_finished "git reset --hard HEAD" &&
git reset --hard && git reset --hard &&
echo foo >> file && echo foo >>file &&
git stash && git stash &&
test_when_finished "git stash drop" && test_when_finished "git stash drop" &&
echo bar >> file && echo bar >>file &&
STASH_ID=$(git stash create) && STASH_ID=$(git stash create) &&
git reset --hard && git reset --hard &&
echo "1 0 file" >expected && echo "1 0 file" >expected &&
@ -516,10 +561,10 @@ test_expect_success 'stash show -p - stashes on stack, stash-like argument' '
git stash clear && git stash clear &&
test_when_finished "git reset --hard HEAD" && test_when_finished "git reset --hard HEAD" &&
git reset --hard && git reset --hard &&
echo foo >> file && echo foo >>file &&
git stash && git stash &&
test_when_finished "git stash drop" && test_when_finished "git stash drop" &&
echo bar >> file && echo bar >>file &&
STASH_ID=$(git stash create) && STASH_ID=$(git stash create) &&
git reset --hard && git reset --hard &&
cat >expected <<-EOF && cat >expected <<-EOF &&
@ -539,7 +584,7 @@ test_expect_success 'stash show - no stashes on stack, stash-like argument' '
git stash clear && git stash clear &&
test_when_finished "git reset --hard HEAD" && test_when_finished "git reset --hard HEAD" &&
git reset --hard && git reset --hard &&
echo foo >> file && echo foo >>file &&
STASH_ID=$(git stash create) && STASH_ID=$(git stash create) &&
git reset --hard && git reset --hard &&
echo "1 0 file" >expected && echo "1 0 file" >expected &&
@ -551,7 +596,7 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '
git stash clear && git stash clear &&
test_when_finished "git reset --hard HEAD" && test_when_finished "git reset --hard HEAD" &&
git reset --hard && git reset --hard &&
echo foo >> file && echo foo >>file &&
STASH_ID=$(git stash create) && STASH_ID=$(git stash create) &&
git reset --hard && git reset --hard &&
cat >expected <<-EOF && cat >expected <<-EOF &&
@ -567,13 +612,13 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '
test_cmp expected actual test_cmp expected actual
' '
test_expect_success 'stash drop - fail early if specified stash is not a stash reference' ' test_expect_success 'drop: fail early if specified stash is not a stash ref' '
git stash clear && git stash clear &&
test_when_finished "git reset --hard HEAD && git stash clear" && test_when_finished "git reset --hard HEAD && git stash clear" &&
git reset --hard && git reset --hard &&
echo foo > file && echo foo >file &&
git stash && git stash &&
echo bar > file && echo bar >file &&
git stash && git stash &&
test_must_fail git stash drop $(git rev-parse stash@{0}) && test_must_fail git stash drop $(git rev-parse stash@{0}) &&
git stash pop && git stash pop &&
@ -581,13 +626,13 @@ test_expect_success 'stash drop - fail early if specified stash is not a stash r
git reset --hard HEAD git reset --hard HEAD
' '
test_expect_success 'stash pop - fail early if specified stash is not a stash reference' ' test_expect_success 'pop: fail early if specified stash is not a stash ref' '
git stash clear && git stash clear &&
test_when_finished "git reset --hard HEAD && git stash clear" && test_when_finished "git reset --hard HEAD && git stash clear" &&
git reset --hard && git reset --hard &&
echo foo > file && echo foo >file &&
git stash && git stash &&
echo bar > file && echo bar >file &&
git stash && git stash &&
test_must_fail git stash pop $(git rev-parse stash@{0}) && test_must_fail git stash pop $(git rev-parse stash@{0}) &&
git stash pop && git stash pop &&
@ -597,8 +642,8 @@ test_expect_success 'stash pop - fail early if specified stash is not a stash re
test_expect_success 'ref with non-existent reflog' ' test_expect_success 'ref with non-existent reflog' '
git stash clear && git stash clear &&
echo bar5 > file && echo bar5 >file &&
echo bar6 > file2 && echo bar6 >file2 &&
git add file2 && git add file2 &&
git stash && git stash &&
test_must_fail git rev-parse --quiet --verify does-not-exist && test_must_fail git rev-parse --quiet --verify does-not-exist &&
@ -618,8 +663,8 @@ test_expect_success 'ref with non-existent reflog' '
test_expect_success 'invalid ref of the form stash@{n}, n >= N' ' test_expect_success 'invalid ref of the form stash@{n}, n >= N' '
git stash clear && git stash clear &&
test_must_fail git stash drop stash@{0} && test_must_fail git stash drop stash@{0} &&
echo bar5 > file && echo bar5 >file &&
echo bar6 > file2 && echo bar6 >file2 &&
git add file2 && git add file2 &&
git stash && git stash &&
test_must_fail git stash drop stash@{1} && test_must_fail git stash drop stash@{1} &&
@ -645,7 +690,7 @@ test_expect_success 'invalid ref of the form "n", n >= N' '
git stash drop git stash drop
' '
test_expect_success 'stash branch should not drop the stash if the branch exists' ' test_expect_success 'branch: do not drop the stash if the branch exists' '
git stash clear && git stash clear &&
echo foo >file && echo foo >file &&
git add file && git add file &&
@ -656,7 +701,7 @@ test_expect_success 'stash branch should not drop the stash if the branch exists
git rev-parse stash@{0} -- git rev-parse stash@{0} --
' '
test_expect_success 'stash branch should not drop the stash if the apply fails' ' test_expect_success 'branch: should not drop the stash if the apply fails' '
git stash clear && git stash clear &&
git reset HEAD~1 --hard && git reset HEAD~1 --hard &&
echo foo >file && echo foo >file &&
@ -670,7 +715,7 @@ test_expect_success 'stash branch should not drop the stash if the apply fails'
git rev-parse stash@{0} -- git rev-parse stash@{0} --
' '
test_expect_success 'stash apply shows status same as git status (relative to current directory)' ' test_expect_success 'apply: show same status as git status (relative to ./)' '
git stash clear && git stash clear &&
echo 1 >subdir/subfile1 && echo 1 >subdir/subfile1 &&
echo 2 >subdir/subfile2 && echo 2 >subdir/subfile2 &&
@ -689,7 +734,7 @@ test_expect_success 'stash apply shows status same as git status (relative to cu
test_i18ncmp expect actual test_i18ncmp expect actual
' '
cat > expect << EOF cat >expect <<EOF
diff --git a/HEAD b/HEAD diff --git a/HEAD b/HEAD
new file mode 100644 new file mode 100644
index 0000000..fe0cbee index 0000000..fe0cbee
@ -702,14 +747,14 @@ EOF
test_expect_success 'stash where working directory contains "HEAD" file' ' test_expect_success 'stash where working directory contains "HEAD" file' '
git stash clear && git stash clear &&
git reset --hard && git reset --hard &&
echo file-not-a-ref > HEAD && echo file-not-a-ref >HEAD &&
git add HEAD && git add HEAD &&
test_tick && test_tick &&
git stash && git stash &&
git diff-files --quiet && git diff-files --quiet &&
git diff-index --cached --quiet HEAD && git diff-index --cached --quiet HEAD &&
test "$(git rev-parse stash^)" = "$(git rev-parse HEAD)" && test "$(git rev-parse stash^)" = "$(git rev-parse HEAD)" &&
git diff stash^..stash > output && git diff stash^..stash >output &&
test_cmp expect output test_cmp expect output
' '
@ -1011,7 +1056,7 @@ test_expect_success 'stash push -p with pathspec shows no changes only once' '
test_i18ncmp expect actual test_i18ncmp expect actual
' '
test_expect_success 'stash push with pathspec shows no changes when there are none' ' test_expect_success 'push <pathspec>: show no changes when there are none' '
>foo && >foo &&
git add foo && git add foo &&
git commit -m "tmp" && git commit -m "tmp" &&
@ -1021,12 +1066,35 @@ test_expect_success 'stash push with pathspec shows no changes when there are no
test_i18ncmp expect actual test_i18ncmp expect actual
' '
test_expect_success 'stash push with pathspec not in the repository errors out' ' test_expect_success 'push: <pathspec> not in the repository errors out' '
>untracked && >untracked &&
test_must_fail git stash push untracked && test_must_fail git stash push untracked &&
test_path_is_file untracked test_path_is_file untracked
' '
test_expect_success 'push: -q is quiet with changes' '
>foo &&
git add foo &&
git stash push -q >output 2>&1 &&
test_must_be_empty output
'
test_expect_success 'push: -q is quiet with no changes' '
git stash push -q >output 2>&1 &&
test_must_be_empty output
'
test_expect_success 'push: -q is quiet even if there is no initial commit' '
git init foo_dir &&
test_when_finished rm -rf foo_dir &&
(
cd foo_dir &&
>bar &&
test_must_fail git stash push -q >output 2>&1 &&
test_must_be_empty output
)
'
test_expect_success 'untracked files are left in place when -u is not given' ' test_expect_success 'untracked files are left in place when -u is not given' '
>file && >file &&
git add file && git add file &&
@ -1096,6 +1164,12 @@ test_expect_success 'stash -- <subdir> works with binary files' '
test_path_is_file subdir/untracked test_path_is_file subdir/untracked
' '
test_expect_success 'stash with user.name and user.email set works' '
test_config user.name "A U Thor" &&
test_config user.email "a.u@thor" &&
git stash
'
test_expect_success 'stash works when user.name and user.email are not set' ' test_expect_success 'stash works when user.name and user.email are not set' '
git reset && git reset &&
>1 && >1 &&

83
t/t3907-stash-show-config.sh Executable file
View File

@ -0,0 +1,83 @@
#!/bin/sh
test_description='Test git stash show configuration.'
. ./test-lib.sh
test_expect_success 'setup' '
test_commit file
'
# takes three parameters:
# 1. the stash.showStat value (or "<unset>")
# 2. the stash.showPatch value (or "<unset>")
# 3. the diff options of the expected output (or nothing for no output)
test_stat_and_patch () {
if test "<unset>" = "$1"
then
test_unconfig stash.showStat
else
test_config stash.showStat "$1"
fi &&
if test "<unset>" = "$2"
then
test_unconfig stash.showPatch
else
test_config stash.showPatch "$2"
fi &&
shift 2 &&
echo 2 >file.t &&
if test $# != 0
then
git diff "$@" >expect
fi &&
git stash &&
git stash show >actual &&
if test $# = 0
then
test_must_be_empty actual
else
test_cmp expect actual
fi
}
test_expect_success 'showStat unset showPatch unset' '
test_stat_and_patch "<unset>" "<unset>" --stat
'
test_expect_success 'showStat unset showPatch false' '
test_stat_and_patch "<unset>" false --stat
'
test_expect_success 'showStat unset showPatch true' '
test_stat_and_patch "<unset>" true --stat -p
'
test_expect_success 'showStat false showPatch unset' '
test_stat_and_patch false "<unset>"
'
test_expect_success 'showStat false showPatch false' '
test_stat_and_patch false false
'
test_expect_success 'showStat false showPatch true' '
test_stat_and_patch false true -p
'
test_expect_success 'showStat true showPatch unset' '
test_stat_and_patch true "<unset>" --stat
'
test_expect_success 'showStat true showPatch false' '
test_stat_and_patch true false --stat
'
test_expect_success 'showStat true showPatch true' '
test_stat_and_patch true true --stat -p
'
test_done