reset: support the experimental --stdin option

Just like with other Git commands, this option makes it read the paths
from the standard input. It comes in handy when resetting many, many
paths at once and wildcards are not an option (e.g. when the paths are
generated by a tool).

Note: we first parse the entire list and perform the actual reset action
only in a second phase. Not only does this make things simpler, it also
helps performance, as do_diff_cache() traverses the index and the
(sorted) pathspecs in simultaneously to avoid unnecessary lookups.

This feature is marked experimental because it is still under review in
the upstream Git project.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Johannes Schindelin
2016-09-07 18:07:04 +02:00
parent a4b818922f
commit f33dcdba9f
3 changed files with 94 additions and 1 deletions

View File

@@ -10,6 +10,7 @@ SYNOPSIS
[verse]
'git reset' [-q] [<tree-ish>] [--] <paths>...
'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
EXPERIMENTAL: 'git reset' [-q] [--stdin [-z]] [<tree-ish>]
'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
DESCRIPTION
@@ -100,6 +101,15 @@ OPTIONS
`reset.quiet` config option. `--quiet` and `--no-quiet` will
override the default behavior.
--stdin::
EXPERIMENTAL: Instead of taking list of paths from the
command line, read list of paths from the standard input.
Paths are separated by LF (i.e. one path per line) by
default.
-z::
EXPERIMENTAL: Only meaningful with `--stdin`; paths are
separated with NUL character instead of LF.
EXAMPLES
--------

View File

@@ -25,12 +25,15 @@
#include "cache-tree.h"
#include "submodule.h"
#include "submodule-config.h"
#include "strbuf.h"
#include "quote.h"
#define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
static const char * const git_reset_usage[] = {
N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
N_("EXPERIMENTAL: git reset [-q] [--stdin [-z]] [<tree-ish>]"),
N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
NULL
};
@@ -284,7 +287,9 @@ static int git_reset_config(const char *var, const char *value, void *cb)
int cmd_reset(int argc, const char **argv, const char *prefix)
{
int reset_type = NONE, update_ref_status = 0, quiet = 0;
int patch_mode = 0, unborn;
int patch_mode = 0, nul_term_line = 0, read_from_stdin = 0, unborn;
char **stdin_paths = NULL;
int stdin_nr = 0, stdin_alloc = 0;
const char *rev;
struct object_id oid;
struct pathspec pathspec;
@@ -306,6 +311,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
OPT_BOOL('N', "intent-to-add", &intent_to_add,
N_("record only the fact that removed paths will be added later")),
OPT_BOOL('z', NULL, &nul_term_line,
N_("EXPERIMENTAL: paths are separated with NUL character")),
OPT_BOOL(0, "stdin", &read_from_stdin,
N_("EXPERIMENTAL: read paths from <stdin>")),
OPT_END()
};
@@ -316,6 +325,42 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_DASHDASH);
parse_args(&pathspec, argv, prefix, patch_mode, &rev);
if (read_from_stdin) {
strbuf_getline_fn getline_fn = nul_term_line ?
strbuf_getline_nul : strbuf_getline_lf;
int flags = PATHSPEC_PREFER_FULL;
struct strbuf buf = STRBUF_INIT;
struct strbuf unquoted = STRBUF_INIT;
if (patch_mode)
die(_("--stdin is incompatible with --patch"));
if (pathspec.nr)
die(_("--stdin is incompatible with path arguments"));
while (getline_fn(&buf, stdin) != EOF) {
if (!nul_term_line && buf.buf[0] == '"') {
strbuf_reset(&unquoted);
if (unquote_c_style(&unquoted, buf.buf, NULL))
die(_("line is badly quoted"));
strbuf_swap(&buf, &unquoted);
}
ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc);
stdin_paths[stdin_nr++] = xstrdup(buf.buf);
strbuf_reset(&buf);
}
strbuf_release(&unquoted);
strbuf_release(&buf);
ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc);
stdin_paths[stdin_nr++] = NULL;
flags |= PATHSPEC_LITERAL_PATH;
parse_pathspec(&pathspec, 0, flags, prefix,
(const char **)stdin_paths);
} else if (nul_term_line)
die(_("-z requires --stdin"));
unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
if (unborn) {
/* reset on unborn branch: treat as reset to empty tree */
@@ -416,5 +461,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (!pathspec.nr)
remove_branch_state(the_repository);
if (stdin_paths) {
while (stdin_nr)
free(stdin_paths[--stdin_nr]);
free(stdin_paths);
}
return update_ref_status;
}

32
t/t7108-reset-stdin.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/sh
test_description='reset --stdin'
. ./test-lib.sh
test_expect_success 'reset --stdin' '
test_commit hello &&
git rm hello.t &&
test -z "$(git ls-files hello.t)" &&
echo hello.t | git reset --stdin &&
test hello.t = "$(git ls-files hello.t)"
'
test_expect_success 'reset --stdin -z' '
test_commit world &&
git rm hello.t world.t &&
test -z "$(git ls-files hello.t world.t)" &&
printf world.tQworld.tQhello.tQ | q_to_nul | git reset --stdin -z &&
printf "hello.t\nworld.t\n" >expect &&
git ls-files >actual &&
test_cmp expect actual
'
test_expect_success '--stdin requires --mixed' '
echo hello.t >list &&
test_must_fail git reset --soft --stdin <list &&
test_must_fail git reset --hard --stdin <list &&
git reset --mixed --stdin <list
'
test_done