Skip to content

Commit

Permalink
Merge branch 'am/pathspec-from-file'
Browse files Browse the repository at this point in the history
A few commands learned to take the pathspec from the
standard input or a named file, instead of taking it as the command
line arguments.

* am/pathspec-from-file:
  commit: support the --pathspec-from-file option
  doc: commit: synchronize <pathspec> description
  reset: support the `--pathspec-from-file` option
  doc: reset: synchronize <pathspec> description
  pathspec: add new function to parse file
  parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul`
  • Loading branch information
gitster committed Dec 10, 2019
2 parents 7034cd0 + e440fc5 commit c58ae96
Show file tree
Hide file tree
Showing 9 changed files with 434 additions and 28 deletions.
29 changes: 22 additions & 7 deletions Documentation/git-commit.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ SYNOPSIS
[-F <file> | -m <msg>] [--reset-author] [--allow-empty]
[--allow-empty-message] [--no-verify] [-e] [--author=<author>]
[--date=<date>] [--cleanup=<mode>] [--[no-]status]
[-i | -o] [-S[<keyid>]] [--] [<file>...]
[-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
[-S[<keyid>]] [--] [<pathspec>...]

DESCRIPTION
-----------
Expand Down Expand Up @@ -278,6 +279,19 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
already been staged. If used together with `--allow-empty`
paths are also not required, and an empty commit will be created.

--pathspec-from-file=<file>::
Pathspec is passed in `<file>` instead of commandline args. If
`<file>` is exactly `-` then standard input is used. Pathspec
elements are separated by LF or CR/LF. Pathspec elements can be
quoted as explained for the configuration variable `core.quotePath`
(see linkgit:git-config[1]). See also `--pathspec-file-nul` and
global `--literal-pathspecs`.

--pathspec-file-nul::
Only meaningful with `--pathspec-from-file`. Pathspec elements are
separated with NUL character and all other characters are taken
literally (including newlines and quotes).

-u[<mode>]::
--untracked-files[=<mode>]::
Show untracked files.
Expand Down Expand Up @@ -345,12 +359,13 @@ changes to tracked files.
\--::
Do not interpret any more arguments as options.

<file>...::
When files are given on the command line, the command
commits the contents of the named files, without
recording the changes already staged. The contents of
these files are also staged for the next commit on top
of what have been staged before.
<pathspec>...::
When pathspec is given on the command line, commit the contents of
the files that match the pathspec without recording the changes
already added to the index. The contents of these files are also
staged for the next commit on top of what have been staged before.
+
For more details, see the 'pathspec' entry in linkgit:gitglossary[7].

:git-commit: 1
include::date-formats.txt[]
Expand Down
48 changes: 35 additions & 13 deletions Documentation/git-reset.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,36 @@ git-reset - Reset current HEAD to the specified state
SYNOPSIS
--------
[verse]
'git reset' [-q] [<tree-ish>] [--] <paths>...
'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
'git reset' [-q] [<tree-ish>] [--] <pathspec>...
'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]

DESCRIPTION
-----------
In the first and second form, copy entries from `<tree-ish>` to the index.
In the third form, set the current branch head (`HEAD`) to `<commit>`,
In the first three forms, copy entries from `<tree-ish>` to the index.
In the last form, set the current branch head (`HEAD`) to `<commit>`,
optionally modifying index and working tree to match.
The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.

'git reset' [-q] [<tree-ish>] [--] <paths>...::
This form resets the index entries for all `<paths>` to their
state at `<tree-ish>`. (It does not affect the working tree or
the current branch.)
'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]::
These forms reset the index entries for all paths that match the
`<pathspec>` to their state at `<tree-ish>`. (It does not affect
the working tree or the current branch.)
+
This means that `git reset <paths>` is the opposite of `git add
<paths>`. This command is equivalent to
`git restore [--source=<tree-ish>] --staged <paths>...`.
This means that `git reset <pathspec>` is the opposite of `git add
<pathspec>`. This command is equivalent to
`git restore [--source=<tree-ish>] --staged <pathspec>...`.
+
After running `git reset <paths>` to update the index entry, you can
After running `git reset <pathspec>` to update the index entry, you can
use linkgit:git-restore[1] to check the contents out of the index to
the working tree. Alternatively, using linkgit:git-restore[1]
and specifying a commit with `--source`, you
can copy the contents of a path out of a commit to the index and to the
working tree in one go.

'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]::
'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]::
Interactively select hunks in the difference between the index
and `<tree-ish>` (defaults to `HEAD`). The chosen hunks are applied
in reverse to the index.
Expand Down Expand Up @@ -101,6 +103,26 @@ OPTIONS
`reset.quiet` config option. `--quiet` and `--no-quiet` will
override the default behavior.

--pathspec-from-file=<file>::
Pathspec is passed in `<file>` instead of commandline args. If
`<file>` is exactly `-` then standard input is used. Pathspec
elements are separated by LF or CR/LF. Pathspec elements can be
quoted as explained for the configuration variable `core.quotePath`
(see linkgit:git-config[1]). See also `--pathspec-file-nul` and
global `--literal-pathspecs`.

--pathspec-file-nul::
Only meaningful with `--pathspec-from-file`. Pathspec elements are
separated with NUL character and all other characters are taken
literally (including newlines and quotes).

\--::
Do not interpret any more arguments as options.

<pathspec>...::
Limits the paths affected by the operation.
+
For more details, see the 'pathspec' entry in linkgit:gitglossary[7].

EXAMPLES
--------
Expand Down
25 changes: 21 additions & 4 deletions builtin/commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ static int all, also, interactive, patch_interactive, only, amend, signoff;
static int edit_flag = -1; /* unspecified */
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int config_commit_verbose = -1; /* unspecified */
static int no_post_rewrite, allow_empty_message;
static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
static char *sign_commit;
static char *sign_commit, *pathspec_from_file;

/*
* The default commit message cleanup mode will remove the lines
Expand Down Expand Up @@ -343,6 +343,23 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
PATHSPEC_PREFER_FULL,
prefix, argv);

if (pathspec_from_file) {
if (interactive)
die(_("--pathspec-from-file is incompatible with --interactive/--patch"));

if (pathspec.nr)
die(_("--pathspec-from-file is incompatible with pathspec arguments"));

parse_pathspec_file(&pathspec, 0,
PATHSPEC_PREFER_FULL,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
die(_("--pathspec-file-nul requires --pathspec-from-file"));
}

if (!pathspec.nr && (also || (only && !amend && !allow_empty)))
die(_("No paths with --include/--only does not make sense."));

if (read_cache_preload(&pathspec) < 0)
die(_("index file corrupt"));

Expand Down Expand Up @@ -1198,8 +1215,6 @@ static int parse_and_validate_options(int argc, const char *argv[],

if (also + only + all + interactive > 1)
die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
if (argc == 0 && (also || (only && !amend && !allow_empty)))
die(_("No paths with --include/--only does not make sense."));
cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);

handle_untracked_files_arg(s);
Expand Down Expand Up @@ -1513,6 +1528,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "amend", &amend, N_("amend previous commit")),
OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
/* end commit contents options */

OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty,
Expand Down
25 changes: 21 additions & 4 deletions builtin/reset.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@

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_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."),
N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"),
N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"),
NULL
};

Expand Down Expand Up @@ -284,8 +285,8 @@ 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;
const char *rev;
int patch_mode = 0, pathspec_file_nul = 0, unborn;
const char *rev, *pathspec_from_file = NULL;
struct object_id oid;
struct pathspec pathspec;
int intent_to_add = 0;
Expand All @@ -306,6 +307,8 @@ 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_PATHSPEC_FROM_FILE(&pathspec_from_file),
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_END()
};

Expand All @@ -316,6 +319,20 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_DASHDASH);
parse_args(&pathspec, argv, prefix, patch_mode, &rev);

if (pathspec_from_file) {
if (patch_mode)
die(_("--pathspec-from-file is incompatible with --patch"));

if (pathspec.nr)
die(_("--pathspec-from-file is incompatible with pathspec arguments"));

parse_pathspec_file(&pathspec, 0,
PATHSPEC_PREFER_FULL,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
die(_("--pathspec-file-nul requires --pathspec-from-file"));
}

unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
if (unborn) {
/* reset on unborn branch: treat as reset to empty tree */
Expand Down
2 changes: 2 additions & 0 deletions parse-options.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,5 +330,7 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);
#define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
#define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
#define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
#define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file"))
#define OPT_PATHSPEC_FILE_NUL(v) OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character"))

#endif
38 changes: 38 additions & 0 deletions pathspec.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "dir.h"
#include "pathspec.h"
#include "attr.h"
#include "argv-array.h"
#include "quote.h"

/*
* Finds which of the given pathspecs match items in the index.
Expand Down Expand Up @@ -613,6 +615,42 @@ void parse_pathspec(struct pathspec *pathspec,
}
}

void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask,
unsigned flags, const char *prefix,
const char *file, int nul_term_line)
{
struct argv_array parsed_file = ARGV_ARRAY_INIT;
strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul :
strbuf_getline;
struct strbuf buf = STRBUF_INIT;
struct strbuf unquoted = STRBUF_INIT;
FILE *in;

if (!strcmp(file, "-"))
in = stdin;
else
in = xfopen(file, "r");

while (getline_fn(&buf, in) != EOF) {
if (!nul_term_line && buf.buf[0] == '"') {
strbuf_reset(&unquoted);
if (unquote_c_style(&unquoted, buf.buf, NULL))
die(_("line is badly quoted: %s"), buf.buf);
strbuf_swap(&buf, &unquoted);
}
argv_array_push(&parsed_file, buf.buf);
strbuf_reset(&buf);
}

strbuf_release(&unquoted);
strbuf_release(&buf);
if (in != stdin)
fclose(in);

parse_pathspec(pathspec, magic_mask, flags, prefix, parsed_file.argv);
argv_array_clear(&parsed_file);
}

void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
{
int i, j;
Expand Down
10 changes: 10 additions & 0 deletions pathspec.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ void parse_pathspec(struct pathspec *pathspec,
unsigned flags,
const char *prefix,
const char **args);
/*
* Same as parse_pathspec() but uses file as input.
* When 'file' is exactly "-" it uses 'stdin' instead.
*/
void parse_pathspec_file(struct pathspec *pathspec,
unsigned magic_mask,
unsigned flags,
const char *prefix,
const char *file,
int nul_term_line);
void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
void clear_pathspec(struct pathspec *);

Expand Down
Loading

0 comments on commit c58ae96

Please sign in to comment.