Commit e36adf71 authored by Junio C Hamano's avatar Junio C Hamano

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
  ...
parents ffac537e 7906af0c
......@@ -82,6 +82,7 @@
/git-init-db
/git-interpret-trailers
/git-instaweb
/git-legacy-stash
/git-log
/git-ls-files
/git-ls-remote
......
......@@ -9,7 +9,7 @@ SYNOPSIS
--------
[verse]
'git stash' list [<options>]
'git stash' show [<stash>]
'git stash' show [<options>] [<stash>]
'git stash' drop [-q|--quiet] [<stash>]
'git stash' ( pop | apply ) [--index] [-q|--quiet] [<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'
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
stashed contents and the commit back when the stash entry was first
......
......@@ -613,9 +613,9 @@ SCRIPT_SH += git-merge-one-file.sh
SCRIPT_SH += git-merge-resolve.sh
SCRIPT_SH += git-mergetool.sh
SCRIPT_SH += git-quiltimport.sh
SCRIPT_SH += git-legacy-stash.sh
SCRIPT_SH += git-remote-testgit.sh
SCRIPT_SH += git-request-pull.sh
SCRIPT_SH += git-stash.sh
SCRIPT_SH += git-submodule.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-index.o
BUILTIN_OBJS += builtin/show-ref.o
BUILTIN_OBJS += builtin/stash.o
BUILTIN_OBJS += builtin/stripspace.o
BUILTIN_OBJS += builtin/submodule--helper.o
BUILTIN_OBJS += builtin/symbolic-ref.o
......
......@@ -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_index(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_submodule__helper(int argc, const char **argv, const char *prefix);
extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
......
This diff is collapsed.
......@@ -1379,6 +1379,7 @@ enum get_oid_result {
};
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_committish(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 int is_terminal_dumb(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);
struct ident_split {
......
......@@ -80,6 +80,28 @@ clear_stash () {
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 () {
prepare_fallback_ident
......@@ -112,15 +134,18 @@ create_stash () {
done
git update-index -q --refresh
if no_changes "$@"
if maybe_quiet no_changes "$@"
then
exit 0
fi
# 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
head=$(git rev-list --oneline -n 1 HEAD --)
elif test -n "$GIT_QUIET"
then
exit 1
else
die "$(gettext "You do not have the initial commit yet")"
fi
......@@ -315,7 +340,7 @@ push_stash () {
test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1
git update-index -q --refresh
if no_changes "$@"
if maybe_quiet no_changes "$@"
then
say "$(gettext "No local changes to save")"
exit 0
......@@ -370,6 +395,9 @@ save_stash () {
while test $# != 0
do
case "$1" in
-q|--quiet)
GIT_QUIET=t
;;
--)
shift
break
......
......@@ -101,6 +101,7 @@ $LONG_USAGE")"
case "$1" in
-h)
echo "$LONG_USAGE"
case "$0" in *git-legacy-stash) exit 129;; esac
exit
esac
fi
......
......@@ -577,6 +577,12 @@ static struct cmd_struct commands[] = {
{ "show-index", cmd_show_index },
{ "show-ref", cmd_show_ref, RUN_SETUP },
{ "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 },
{ "stripspace", cmd_stripspace },
{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
......
......@@ -585,6 +585,26 @@ int git_ident_config(const char *var, const char *value, void *data)
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,
const char *b_begin, const char *b_end)
{
......
......@@ -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);
}
/*
* 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
......
......@@ -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);
}
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)
{
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);
}
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)
{
strbuf_grow(sb, n);
......
......@@ -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);
/**
* 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.
*/
......@@ -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);
/**
* 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
* placeholders. To that end, it parses the string and calls the specified
......
......@@ -384,6 +384,10 @@ the --no-sparse command-line argument.
GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path
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
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
......
This diff is collapsed.
#!/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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment