Skip to content

Commit c2885f5

Browse files
dschoGit for Windows Build Agent
authored andcommitted
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 <[email protected]>
1 parent f8e8c43 commit c2885f5

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed

Documentation/git-reset.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ SYNOPSIS
1010
[verse]
1111
'git reset' [-q] [<tree-ish>] [--] <paths>...
1212
'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
13+
EXPERIMENTAL: 'git reset' [-q] [--stdin [-z]] [<tree-ish>]
1314
'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
1415

1516
DESCRIPTION
@@ -100,6 +101,15 @@ OPTIONS
100101
`reset.quiet` config option. `--quiet` and `--no-quiet` will
101102
override the default behavior.
102103

104+
--stdin::
105+
EXPERIMENTAL: Instead of taking list of paths from the
106+
command line, read list of paths from the standard input.
107+
Paths are separated by LF (i.e. one path per line) by
108+
default.
109+
110+
-z::
111+
EXPERIMENTAL: Only meaningful with `--stdin`; paths are
112+
separated with NUL character instead of LF.
103113

104114
EXAMPLES
105115
--------

builtin/reset.c

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@
2525
#include "cache-tree.h"
2626
#include "submodule.h"
2727
#include "submodule-config.h"
28+
#include "strbuf.h"
29+
#include "quote.h"
2830

2931
#define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
3032

3133
static const char * const git_reset_usage[] = {
3234
N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
3335
N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
36+
N_("EXPERIMENTAL: git reset [-q] [--stdin [-z]] [<tree-ish>]"),
3437
N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
3538
NULL
3639
};
@@ -284,7 +287,9 @@ static int git_reset_config(const char *var, const char *value, void *cb)
284287
int cmd_reset(int argc, const char **argv, const char *prefix)
285288
{
286289
int reset_type = NONE, update_ref_status = 0, quiet = 0;
287-
int patch_mode = 0, unborn;
290+
int patch_mode = 0, nul_term_line = 0, read_from_stdin = 0, unborn;
291+
char **stdin_paths = NULL;
292+
int stdin_nr = 0, stdin_alloc = 0;
288293
const char *rev;
289294
struct object_id oid;
290295
struct pathspec pathspec;
@@ -306,6 +311,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
306311
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
307312
OPT_BOOL('N', "intent-to-add", &intent_to_add,
308313
N_("record only the fact that removed paths will be added later")),
314+
OPT_BOOL('z', NULL, &nul_term_line,
315+
N_("EXPERIMENTAL: paths are separated with NUL character")),
316+
OPT_BOOL(0, "stdin", &read_from_stdin,
317+
N_("EXPERIMENTAL: read paths from <stdin>")),
309318
OPT_END()
310319
};
311320

@@ -316,6 +325,42 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
316325
PARSE_OPT_KEEP_DASHDASH);
317326
parse_args(&pathspec, argv, prefix, patch_mode, &rev);
318327

328+
if (read_from_stdin) {
329+
strbuf_getline_fn getline_fn = nul_term_line ?
330+
strbuf_getline_nul : strbuf_getline_lf;
331+
int flags = PATHSPEC_PREFER_FULL;
332+
struct strbuf buf = STRBUF_INIT;
333+
struct strbuf unquoted = STRBUF_INIT;
334+
335+
if (patch_mode)
336+
die(_("--stdin is incompatible with --patch"));
337+
338+
if (pathspec.nr)
339+
die(_("--stdin is incompatible with path arguments"));
340+
341+
while (getline_fn(&buf, stdin) != EOF) {
342+
if (!nul_term_line && buf.buf[0] == '"') {
343+
strbuf_reset(&unquoted);
344+
if (unquote_c_style(&unquoted, buf.buf, NULL))
345+
die(_("line is badly quoted"));
346+
strbuf_swap(&buf, &unquoted);
347+
}
348+
ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc);
349+
stdin_paths[stdin_nr++] = xstrdup(buf.buf);
350+
strbuf_reset(&buf);
351+
}
352+
strbuf_release(&unquoted);
353+
strbuf_release(&buf);
354+
355+
ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc);
356+
stdin_paths[stdin_nr++] = NULL;
357+
flags |= PATHSPEC_LITERAL_PATH;
358+
parse_pathspec(&pathspec, 0, flags, prefix,
359+
(const char **)stdin_paths);
360+
361+
} else if (nul_term_line)
362+
die(_("-z requires --stdin"));
363+
319364
unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
320365
if (unborn) {
321366
/* reset on unborn branch: treat as reset to empty tree */
@@ -423,5 +468,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
423468
if (!pathspec.nr)
424469
remove_branch_state(the_repository);
425470

471+
if (stdin_paths) {
472+
while (stdin_nr)
473+
free(stdin_paths[--stdin_nr]);
474+
free(stdin_paths);
475+
}
476+
426477
return update_ref_status;
427478
}

t/t7108-reset-stdin.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/sh
2+
3+
test_description='reset --stdin'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success 'reset --stdin' '
8+
test_commit hello &&
9+
git rm hello.t &&
10+
test -z "$(git ls-files hello.t)" &&
11+
echo hello.t | git reset --stdin &&
12+
test hello.t = "$(git ls-files hello.t)"
13+
'
14+
15+
test_expect_success 'reset --stdin -z' '
16+
test_commit world &&
17+
git rm hello.t world.t &&
18+
test -z "$(git ls-files hello.t world.t)" &&
19+
printf world.tQworld.tQhello.tQ | q_to_nul | git reset --stdin -z &&
20+
printf "hello.t\nworld.t\n" >expect &&
21+
git ls-files >actual &&
22+
test_cmp expect actual
23+
'
24+
25+
test_expect_success '--stdin requires --mixed' '
26+
echo hello.t >list &&
27+
test_must_fail git reset --soft --stdin <list &&
28+
test_must_fail git reset --hard --stdin <list &&
29+
git reset --mixed --stdin <list
30+
'
31+
32+
test_done

0 commit comments

Comments
 (0)