{"thread":{"id":"10995","subject":"[PATCH 2/7] Rename patch_update_file function to patch_update_pathspec","startedAt":"2007-11-23T19:20:43Z","lastAt":"2007-11-24T22:34:36Z","messageCount":18,"participants":["Wincent Colaiuta","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"60773","messageId":"1195845650-85962-1-git-send-email-win@wincent.com","threadId":"10995","inReplyTo":null,"subject":"[PATCH v2] git-add--interactive pathspec and patch additions","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-23T19:20:43Z","receivedAt":"2007-11-23T19:20:43Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"This is a revised series based on the feedback and suggestions\nreceived to date. The purpose of the series is to implement these\ntwo user-visible changes:\n\n    1. \"git-add --interactive\" now takes optional pathspec parameters\n       which can be used to limit the scope of an interactive session\n    2. We now have \"git-add --patch\" which takes you straight to the\n       \"patch\" subcommand using the given pathspecs and then exits\n\nThe series implements these changes in seven steps that apply on top of\n\"master\"; these patches are rebased/squashed ones which *replace* the\nones sent the other day:\n\n    1. Add -q/--quiet switch to git-ls-files\n\nNeeded because run_cmd_pipe() doesn't propagate the child exit status\nand system() likes to be chatty on the standard out. Of the possible\nworkarounds adding this switch seems to be the cleanest and most\nportable.\n\n    2. Rename patch_update_file function to patch_update_pathspec\n\nMerely cosmetic.\n\n    3. Add path-limiting to git-add--interactive\n    4. Bail if user supplies an invalid pathspec\n\nFor consistency with many other Git commands. It also shields us from\nerrors if the user starts passing funny pathspecs; eg. consider what\nhappens if the user passes a non-existent file \"foo\", and that\neventually hits git-diff-files; note how the error message changes\ndepending on where \"foo\" appears in the list:\n\n$ git diff-files --numstat --summary --\n1       0       git-commit.sh\n$ git diff-files --numstat --summary -- .\n1       0       git-commit.sh\n$ git diff-files --numstat --summary -- . foo\nerror: Could not access ''\n$ git diff-files --numstat --summary -- foo .\nerror: Could not access 'foo'\n$ git diff-files --numstat --summary -- . . foo\n1       0       git-commit.sh\n\n    5. Teach builtin-add to pass path arguments to git-add--interactive\n    6. Add \"--patch\" option to git-add--interactive\n    7. Teach builtin-add to handle \"--patch\" option\n\nAnd that's all. Here's the diff stat:\n\n Documentation/git-add.txt         |    9 +++++-\n Documentation/git-ls-files.txt    |    7 ++++-\n builtin-add.c                     |   28 ++++++++++++--------\n builtin-ls-files.c                |   12 ++++++++-\n commit.h                          |    2 +-\n git-add--interactive.perl         |   52 +++++++++++++++++++++++++++++++-----\n t/t3020-ls-files-error-unmatch.sh |   12 ++++++++\n 7 files changed, 99 insertions(+), 23 deletions(-)\n"},{"id":"60778","messageId":"1195845650-85962-2-git-send-email-win@wincent.com","threadId":"10995","inReplyTo":"1195845650-85962-1-git-send-email-win@wincent.com","subject":"[PATCH 1/7] Add -q/--quiet switch to git-ls-files","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-23T19:20:44Z","receivedAt":"2007-11-23T19:20:44Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"When using the --error-unmatch switch to git-ls-files you may only be\ninterested in the exit status rather than the actual listing that would\nnormally be printed to the stdout.\n\nTo make this kind of workflow easier, the -q/--quiet switch suppresses\nall non-error output.\n\nSigned-off-by: Wincent Colaiuta <win@wincent.com>\n---\n Documentation/git-ls-files.txt    |    7 ++++++-\n builtin-ls-files.c                |   12 +++++++++++-\n t/t3020-ls-files-error-unmatch.sh |   12 ++++++++++++\n 3 files changed, 29 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\nindex 2ec0c0d..33481b6 100644\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -9,7 +9,7 @@ git-ls-files - Show information about files in the index and the working tree\n SYNOPSIS\n --------\n [verse]\n-'git-ls-files' [-z] [-t] [-v]\n+'git-ls-files' [-z] [-t] [-v] [-q]\n \t\t(--[cached|deleted|others|ignored|stage|unmerged|killed|modified])\\*\n \t\t(-[c|d|o|i|s|u|k|m])\\*\n \t\t[-x <pattern>|--exclude=<pattern>]\n@@ -107,6 +107,11 @@ OPTIONS\n \tSimilar to `-t`, but use lowercase letters for files\n \tthat are marked as 'always matching index'.\n \n+-q|--quiet::\n+\tSuppress all non-error output. This may be useful in conjunction\n+\twith --error-unmatch when you are only interested in the exit\n+\tstatus.\n+\n --full-name::\n \tWhen run from a subdirectory, the command usually\n \toutputs paths relative to the current directory.  This\ndiff --git a/builtin-ls-files.c b/builtin-ls-files.c\nindex 7f60709..6c710a8 100644\n--- a/builtin-ls-files.c\n+++ b/builtin-ls-files.c\n@@ -20,6 +20,7 @@ static int show_unmerged;\n static int show_modified;\n static int show_killed;\n static int show_valid_bit;\n+static int quiet;\n static int line_terminator = '\\n';\n \n static int prefix_len;\n@@ -83,6 +84,8 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \tif (pathspec && !match(pathspec, ps_matched, ent->name, len))\n \t\treturn;\n \n+\tif (quiet)\n+\t\treturn;\n \tfputs(tag, stdout);\n \twrite_name_quoted(ent->name + offset, stdout, line_terminator);\n }\n@@ -205,6 +208,8 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)\n \t\ttag = alttag;\n \t}\n \n+\tif (quiet)\n+\t\treturn;\n \tif (!show_stage) {\n \t\tfputs(tag, stdout);\n \t} else {\n@@ -385,7 +390,8 @@ static void overlay_tree(const char *tree_name, const char *prefix)\n }\n \n static const char ls_files_usage[] =\n-\t\"git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* \"\n+\t\"git-ls-files [-z] [-t] [-v] [-q|--quiet] \"\n+\t\"(--[cached|deleted|others|stage|unmerged|killed|modified])* \"\n \t\"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] \"\n \t\"[ --exclude-per-directory=<filename> ] [--exclude-standard] \"\n \t\"[--full-name] [--abbrev] [--] [<file>]*\";\n@@ -423,6 +429,10 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\t\t\tshow_valid_bit = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"-q\") || !strcmp(arg, \"--quiet\")) {\n+\t\t\tquiet = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"-c\") || !strcmp(arg, \"--cached\")) {\n \t\t\tshow_cached = 1;\n \t\t\tcontinue;\ndiff --git a/t/t3020-ls-files-error-unmatch.sh b/t/t3020-ls-files-error-unmatch.sh\nindex c83f820..5178322 100755\n--- a/t/t3020-ls-files-error-unmatch.sh\n+++ b/t/t3020-ls-files-error-unmatch.sh\n@@ -23,5 +23,17 @@ test_expect_success \\\n     'git ls-files --error-unmatch should succeed eith matched paths.' \\\n     'git ls-files --error-unmatch foo bar'\n \n+test_expect_success \\\n+    'git ls-files -q --error-unmatch should be quiet with unmatched path.' \\\n+    'git ls-files -q --error-unmatch foo bar-does-not-match 1> out 2> err ||\n+     test $(cat out | wc -l) -eq 0 &&\n+     test $(cat err | wc -l) -gt 0'\n+\n+test_expect_success \\\n+   'git ls-files -q --error-unmatch should be quiet with matched paths.' \\\n+   'git ls-files -q --error-unmatch foo bar 1> out 2> err ||\n+    test $(cat out | wc -l) -eq 0 &&\n+    test $(cat err | wc -l) -eq 0'\n+\n test_done\n 1\n-- \n1.5.3.6.886.g3364\n"},{"id":"60772","messageId":"1195845650-85962-3-git-send-email-win@wincent.com","threadId":"10995","inReplyTo":"1195845650-85962-2-git-send-email-win@wincent.com","subject":"[PATCH 2/7] Rename patch_update_file function to patch_update_pathspec","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-23T19:20:45Z","receivedAt":"2007-11-23T19:20:45Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"The patch_update_file function really works on pathspecs, not files, so\nrename it to reflect its actual purpose.\n\nSigned-off-by: Wincent Colaiuta <win@wincent.com>\n---\n git-add--interactive.perl |    4 ++--\n 1 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex fb1e92a..81575e5 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -564,10 +564,10 @@ sub patch_update_cmd {\n \t\t\t\t     IMMEDIATE => 1,\n \t\t\t\t     HEADER => $status_head, },\n \t\t\t\t   @mods);\n-\tpatch_update_file($it->{VALUE}) if ($it);\n+\tpatch_update_pathspec($it->{VALUE}) if ($it);\n }\n \n-sub patch_update_file {\n+sub patch_update_pathspec {\n \tmy ($ix, $num);\n \tmy $path = shift;\n \tmy ($head, @hunk) = parse_diff($path);\n-- \n1.5.3.6.886.g3364\n"},{"id":"60774","messageId":"1195845650-85962-4-git-send-email-win@wincent.com","threadId":"10995","inReplyTo":"1195845650-85962-3-git-send-email-win@wincent.com","subject":"[PATCH 3/7] Add path-limiting to git-add--interactive","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-23T19:20:46Z","receivedAt":"2007-11-23T19:20:46Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"Implement Junio's suggestion that git-add--interactive should reproduce the\npath-limiting semantics of non-interactive git-add.\n\nIn otherwords, if \"git add -i\" (unrestricted) shows paths from a set A,\n\"git add -i paths...\" should show paths from a subset of the set A and that\nsubset should be defined with the existing ls-files pathspec semantics.\n\nSigned-off-by: Wincent Colaiuta <win@wincent.com>\n---\n git-add--interactive.perl |    6 +++---\n 1 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 81575e5..5fc48a5 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -37,7 +37,7 @@ sub list_untracked {\n \t\tchomp $_;\n \t\t$_;\n \t}\n-\trun_cmd_pipe(qw(git ls-files --others --exclude-standard --), @_);\n+\trun_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);\n }\n \n my $status_fmt = '%12s %12s %s';\n@@ -58,7 +58,7 @@ sub list_modified {\n \tmy ($add, $del, $adddel, $file);\n \n \tfor (run_cmd_pipe(qw(git diff-index --cached\n-\t\t\t     --numstat --summary HEAD))) {\n+\t\t\t     --numstat --summary HEAD --), @ARGV)) {\n \t\tif (($add, $del, $file) =\n \t\t    /^([-\\d]+)\t([-\\d]+)\t(.*)/) {\n \t\t\tmy ($change, $bin);\n@@ -81,7 +81,7 @@ sub list_modified {\n \t\t}\n \t}\n \n-\tfor (run_cmd_pipe(qw(git diff-files --numstat --summary))) {\n+\tfor (run_cmd_pipe(qw(git diff-files --numstat --summary --), @ARGV)) {\n \t\tif (($add, $del, $file) =\n \t\t    /^([-\\d]+)\t([-\\d]+)\t(.*)/) {\n \t\t\tif (!exists $data{$file}) {\n-- \n1.5.3.6.886.g3364\n"},{"id":"60775","messageId":"1195845650-85962-5-git-send-email-win@wincent.com","threadId":"10995","inReplyTo":"1195845650-85962-4-git-send-email-win@wincent.com","subject":"[PATCH 4/7] Bail if user supplies an invalid pathspec","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-23T19:20:47Z","receivedAt":"2007-11-23T19:20:47Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"Use git-ls-files to detect whether the user has supplied an invalid\npathspec.\n\nNote that the run_cmd_pipe function unfortunately does not transmit the\nexit status of the child process, so there is no way to use it in\nconjunction with a command like \"git-ls-files --error-unmatch\".\n\nThe alternative is to use Perl's system() function, but that floods the\nstandard output. So here we make use of the new -q switch to git-ls-files\nso that we can both obtain the exit status of the command and not have to\nworry about the standard output. Error conditions are still echoed back to\nthe user because they come through on the standard error.\n\nSigned-off-by: Wincent Colaiuta <win@wincent.com>\n---\n git-add--interactive.perl |    7 +++++++\n 1 files changed, 7 insertions(+), 0 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 5fc48a5..8706528 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -775,6 +775,12 @@ add untracked - add contents of untracked files to the staged set of changes\n EOF\n }\n \n+sub check_args {\n+\treturn unless @ARGV;\n+\texit $? if system(qw(git ls-files -q --exclude-standard\n+\t    --error-unmatch --with-tree=HEAD --), @ARGV);\n+}\n+\n sub main_loop {\n \tmy @cmd = ([ 'status', \\&status_cmd, ],\n \t\t   [ 'update', \\&update_cmd, ],\n@@ -804,5 +810,6 @@ sub main_loop {\n }\n \n refresh();\n+check_args();\n status_cmd();\n main_loop();\n-- \n1.5.3.6.886.g3364\n"},{"id":"60779","messageId":"1195845650-85962-6-git-send-email-win@wincent.com","threadId":"10995","inReplyTo":"1195845650-85962-5-git-send-email-win@wincent.com","subject":"[PATCH 5/7] Teach builtin-add to pass path arguments to git-add--interactive","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-23T19:20:48Z","receivedAt":"2007-11-23T19:20:48Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"The previous patches in the series taught git-add--interactive to handle\noptional path arguments. This patch teaches builtin-add to pass\nthese arguments through to the script.\n\nSigned-off-by: Wincent Colaiuta <win@wincent.com>\n---\n Documentation/git-add.txt |    4 +++-\n builtin-add.c             |   23 +++++++++++++----------\n commit.h                  |    2 +-\n 3 files changed, 17 insertions(+), 12 deletions(-)\n\ndiff --git a/Documentation/git-add.txt b/Documentation/git-add.txt\nindex 63829d9..0b0ab1d 100644\n--- a/Documentation/git-add.txt\n+++ b/Documentation/git-add.txt\n@@ -61,7 +61,9 @@ OPTIONS\n \n -i, \\--interactive::\n \tAdd modified contents in the working tree interactively to\n-\tthe index.\n+\tthe index. Optional path arguments may be supplied to limit\n+\toperation to a subset of the files in the working tree.\n+\tSee ``Interactive mode'' for details.\n \n -u::\n \tUpdate only files that git already knows about. This is similar\ndiff --git a/builtin-add.c b/builtin-add.c\nindex cf815a0..3646a45 100644\n--- a/builtin-add.c\n+++ b/builtin-add.c\n@@ -135,11 +135,17 @@ static void refresh(int verbose, const char **pathspec)\n         free(seen);\n }\n \n-int interactive_add(void)\n+int interactive_add(int argc, const char **argv)\n {\n-\tconst char *argv[2] = { \"add--interactive\", NULL };\n-\n-\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n+\tint status;\n+\tconst char **args = xmalloc(sizeof(const char *) * (argc + 1));\n+\targs[0] = \"add--interactive\";\n+\tmemcpy((void *)args + sizeof(const char *), argv, sizeof(const char *) * argc);\n+\targs[argc + 1] = NULL;\n+\n+\tstatus = run_command_v_opt(args, RUN_GIT_CMD);\n+\tfree(args);\n+\treturn status;\n }\n \n static struct lock_file lock_file;\n@@ -163,17 +169,14 @@ static struct option builtin_add_options[] = {\n \n int cmd_add(int argc, const char **argv, const char *prefix)\n {\n-\tint i, newfd, orig_argc = argc;\n+\tint i, newfd = argc;\n \tconst char **pathspec;\n \tstruct dir_struct dir;\n \n \targc = parse_options(argc, argv, builtin_add_options,\n \t\t\t  builtin_add_usage, 0);\n-\tif (add_interactive) {\n-\t\tif (add_interactive != 1 || orig_argc != 2)\n-\t\t\tdie(\"add --interactive does not take any parameters\");\n-\t\texit(interactive_add());\n-\t}\n+\tif (add_interactive)\n+\t\texit(interactive_add(argc, argv));\n \n \tgit_config(git_default_config);\n \ndiff --git a/commit.h b/commit.h\nindex aa67986..d82b8bc 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -113,7 +113,7 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,\n \n int in_merge_bases(struct commit *, struct commit **, int);\n \n-extern int interactive_add(void);\n+extern int interactive_add(int argc, const char **argv);\n extern void add_files_to_cache(int verbose, const char *prefix, const char **files);\n extern int rerere(void);\n \n-- \n1.5.3.6.886.g3364\n"},{"id":"60776","messageId":"1195845650-85962-7-git-send-email-win@wincent.com","threadId":"10995","inReplyTo":"1195845650-85962-6-git-send-email-win@wincent.com","subject":"[PATCH 6/7] Add \"--patch\" option to git-add--interactive","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-23T19:20:49Z","receivedAt":"2007-11-23T19:20:49Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"When the \"--patch\" option is supplied, the patch_update_file function is\ncalled, once for each supplied pathspec argument, and then we exit.\n\nThis commit adds an early return mechanism to the patch_update_pathspec\nfunction to prevent spurious line feeds from being echoed when the user\npasses in pathspecs which match unchanged files.\n\nSigned-off-by: Wincent Colaiuta <win@wincent.com>\n---\n git-add--interactive.perl |   35 ++++++++++++++++++++++++++++++++---\n 1 files changed, 32 insertions(+), 3 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 8706528..43a5344 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -1,6 +1,10 @@\n #!/usr/bin/perl -w\n \n use strict;\n+use Getopt::Long;\n+\n+# command line options\n+my $patch;\n \n sub run_cmd_pipe {\n \tif ($^O eq 'MSWin32') {\n@@ -335,7 +339,8 @@ sub add_untracked_cmd {\n \n sub parse_diff {\n \tmy ($path) = @_;\n-\tmy @diff = run_cmd_pipe(qw(git diff-files -p --), $path);\n+\tmy @diff = run_cmd_pipe(qw(git diff-files -p --), $path)\n+\t    or return undef;\n \tmy (@hunk) = { TEXT => [] };\n \n \tfor (@diff) {\n@@ -571,6 +576,7 @@ sub patch_update_pathspec {\n \tmy ($ix, $num);\n \tmy $path = shift;\n \tmy ($head, @hunk) = parse_diff($path);\n+\treturn unless $head;\n \tfor (@{$head->{TEXT}}) {\n \t\tprint;\n \t}\n@@ -781,6 +787,19 @@ sub check_args {\n \t    --error-unmatch --with-tree=HEAD --), @ARGV);\n }\n \n+sub usage {\n+\tprint <<EOT;\n+git-add--interactive [options] [--] [<filepattern>...]\n+Options:\n+     -p, --patch        Execute the \"patch\" subcommand and exit\n+EOT\n+\texit(1);\n+}\n+\n+sub process_options {\n+\tGetOptions(\"patch!\" => \\$patch) or usage();\n+}\n+\n sub main_loop {\n \tmy @cmd = ([ 'status', \\&status_cmd, ],\n \t\t   [ 'update', \\&update_cmd, ],\n@@ -809,7 +828,17 @@ sub main_loop {\n \t}\n }\n \n+process_options();\n refresh();\n check_args();\n-status_cmd();\n-main_loop();\n+if ($patch) {\n+\tprint \"No filepattern specified: what did you want to patch?\\n\"\n+\t    unless @ARGV;\n+\tforeach my $pathspec (@ARGV) {\n+\t\tpatch_update_pathspec($pathspec);\n+\t}\n+}\n+else {\n+\tstatus_cmd();\n+\tmain_loop();\n+}\n-- \n1.5.3.6.886.g3364\n"},{"id":"60777","messageId":"1195845650-85962-8-git-send-email-win@wincent.com","threadId":"10995","inReplyTo":"1195845650-85962-7-git-send-email-win@wincent.com","subject":"[PATCH 7/7] Teach builtin-add to handle \"--patch\" option","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-23T19:20:50Z","receivedAt":"2007-11-23T19:20:50Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"Now builtin-add can take a \"--patch\" option; when found, it passes control\nover to git-add--interactive, passing the --patch switch and the supplied\npathspecs.\n\nSigned-off-by: Wincent Colaiuta <win@wincent.com>\n---\n Documentation/git-add.txt |    5 +++++\n builtin-add.c             |   13 ++++++++-----\n 2 files changed, 13 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-add.txt b/Documentation/git-add.txt\nindex 0b0ab1d..1acf10f 100644\n--- a/Documentation/git-add.txt\n+++ b/Documentation/git-add.txt\n@@ -65,6 +65,11 @@ OPTIONS\n \toperation to a subset of the files in the working tree.\n \tSee ``Interactive mode'' for details.\n \n+-p, \\--patch:\n+\tSimilar to Interactive mode but the initial command loop is\n+\tbypassed and the 'patch' subcommand is invoked using each of\n+\tthe specified filepatterns before exiting.\n+\n -u::\n \tUpdate only files that git already knows about. This is similar\n \tto what \"git commit -a\" does in preparation for making a commit,\ndiff --git a/builtin-add.c b/builtin-add.c\nindex 3646a45..c8335f1 100644\n--- a/builtin-add.c\n+++ b/builtin-add.c\n@@ -19,6 +19,7 @@ static const char * const builtin_add_usage[] = {\n \t\"git-add [options] [--] <filepattern>...\",\n \tNULL\n };\n+static int patch_interactive = 0, add_interactive = 0;\n \n static int take_worktree_changes;\n \n@@ -138,10 +139,12 @@ static void refresh(int verbose, const char **pathspec)\n int interactive_add(int argc, const char **argv)\n {\n \tint status;\n-\tconst char **args = xmalloc(sizeof(const char *) * (argc + 1));\n+\tint pre_argc = patch_interactive ? 2 : 1;\n+\tconst char **args = xmalloc(sizeof(const char *) * (argc + pre_argc + 1));\n \targs[0] = \"add--interactive\";\n-\tmemcpy((void *)args + sizeof(const char *), argv, sizeof(const char *) * argc);\n-\targs[argc + 1] = NULL;\n+\targs[1] = \"--patch\";\n+\tmemcpy((void *)args + sizeof(const char *) * pre_argc, argv, sizeof(const char *) * argc);\n+\targs[argc + pre_argc] = NULL;\n \n \tstatus = run_command_v_opt(args, RUN_GIT_CMD);\n \tfree(args);\n@@ -154,13 +157,13 @@ static const char ignore_error[] =\n \"The following paths are ignored by one of your .gitignore files:\\n\";\n \n static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;\n-static int add_interactive = 0;\n \n static struct option builtin_add_options[] = {\n \tOPT__DRY_RUN(&show_only),\n \tOPT__VERBOSE(&verbose),\n \tOPT_GROUP(\"\"),\n \tOPT_BOOLEAN('i', \"interactive\", &add_interactive, \"interactive picking\"),\n+\tOPT_BOOLEAN('p', \"patch\", &patch_interactive, \"interactive patching\"),\n \tOPT_BOOLEAN('f', NULL, &ignored_too, \"allow adding otherwise ignored files\"),\n \tOPT_BOOLEAN('u', NULL, &take_worktree_changes, \"update tracked files\"),\n \tOPT_BOOLEAN( 0 , \"refresh\", &refresh_only, \"don't add, only refresh the index\"),\n@@ -175,7 +178,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, builtin_add_options,\n \t\t\t  builtin_add_usage, 0);\n-\tif (add_interactive)\n+\tif (add_interactive || patch_interactive)\n \t\texit(interactive_add(argc, argv));\n \n \tgit_config(git_default_config);\n-- \n1.5.3.6.886.g3364\n"},{"id":"60780","messageId":"CA22AE42-64AD-4633-8671-1901956E7C92@wincent.com","threadId":"10995","inReplyTo":"1195845650-85962-2-git-send-email-win@wincent.com","subject":"Re: [PATCH 1/7] Add -q/--quiet switch to git-ls-files","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-23T19:25:59Z","receivedAt":"2007-11-23T19:25:59Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"El 23/11/2007, a las 20:20, Wincent Colaiuta escribió:\n\n> +test_expect_success \\\n> +    'git ls-files -q --error-unmatch should be quiet with unmatched  \n> path.' \\\n> +    'git ls-files -q --error-unmatch foo bar-does-not-match 1> out  \n> 2> err ||\n> +     test $(cat out | wc -l) -eq 0 &&\n> +     test $(cat err | wc -l) -gt 0'\n> +\n> +test_expect_success \\\n> +   'git ls-files -q --error-unmatch should be quiet with matched  \n> paths.' \\\n> +   'git ls-files -q --error-unmatch foo bar 1> out 2> err ||\n> +    test $(cat out | wc -l) -eq 0 &&\n> +    test $(cat err | wc -l) -eq 0'\n> +\n> test_done\n\nDoh, in that last test it should be:\n\n+   'git ls-files -q --error-unmatch foo bar 1> out 2> err &&\n\nInstead of:\n\n+   'git ls-files -q --error-unmatch foo bar 1> out 2> err ||\n\nCheers,\nWincent\n"},{"id":"60792","messageId":"7vk5o8smaf.fsf@gitster.siamese.dyndns.org","threadId":"10995","inReplyTo":"1195845650-85962-1-git-send-email-win@wincent.com","subject":"Re: [PATCH v2] git-add--interactive pathspec and patch additions","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-23T21:07:52Z","receivedAt":"2007-11-23T21:07:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Wincent Colaiuta <win@wincent.com> writes:\n\n> The series implements these changes in seven steps that apply on top of\n> \"master\"; these patches are rebased/squashed ones which *replace* the\n> ones sent the other day:\n\nThat's very unfortunate, as some already usable bits from the\nseries are already in 'next'.\n\n>     1. Add -q/--quiet switch to git-ls-files\n>\n> Needed because run_cmd_pipe() doesn't propagate the child exit status\n> and system() likes to be chatty on the standard out. Of the possible\n> workarounds adding this switch seems to be the cleanest and most\n> portable.\n\nI do not like this very much.  If it is a problem that\nrun_cmd_pipe() does not properly signal you an error, wouldn't\nit be much better to fix _that_ problem?  That way we do not\nhave to add kludge to all commands we need to run and get the\nexit status out of.\n\n>     2. Rename patch_update_file function to patch_update_pathspec\n>\n> Merely cosmetic.\n>\n>     3. Add path-limiting to git-add--interactive\n>     4. Bail if user supplies an invalid pathspec\n\nOn the first read, I did not quite like 4, but I'd agree it is\nprobably the cleanest implementation for 3 to reject a wrong\ninvocation early.\n\n>     5. Teach builtin-add to pass path arguments to git-add--interactive\n\nI think this is already in 'next'.\n\n>     6. Add \"--patch\" option to git-add--interactive\n>     7. Teach builtin-add to handle \"--patch\" option\n\nThese should be straightforward, but use of Getopt::Long feels\nway overkill for an internal command like add--interactive which\nis called by only a very limited known callers (exactly one).\n\nIf we assume \"a single caller\", we probably can do without 1 and\n4, by making the caller in builtin-add to validate the list of\npathspecs, reusing the code for \"ls-files --error-unmatch\",\nbefore calling the external helper \"add--interactive\".\n\nThere are functions refactored as part of the builtin-commit\nseries to be usable from outside \"ls-files\", and you can build a\nimple function called from interactive_add(ac, av) using them:\n\nstatic int validate_pathspec(const char *prefix, const char **pattern)\n{\n\tint i, ret;\n\tchar *m;\n\n\tif (!pattern || !*pattern)\n        \treturn 0;\n\n\tfor (i = 0; pattern[i]; i++)\n\t\t;\n\tm = xcalloc(1, i);\n\n\tfor (i = 0; i < active_nr; i++) {\n\t\tstruct cache_entry *ce = active_cache[i];\n                (void) pathspec_match(pattern, m, ce->name, 0);\n\t}\n        ret = report_path_error(m, pattern, prefix ? strlen(prefix) : 0);\n        free(m);\n        return ret;\n}\n"},{"id":"60793","messageId":"499E75F1-88A3-411C-8B38-68D43307794D@wincent.com","threadId":"10995","inReplyTo":"7vk5o8smaf.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v2] git-add--interactive pathspec and patch additions","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-23T22:20:50Z","receivedAt":"2007-11-23T22:20:50Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"El 23/11/2007, a las 22:07, Junio C Hamano escribió:\n\n> Wincent Colaiuta <win@wincent.com> writes:\n>\n>> The series implements these changes in seven steps that apply on  \n>> top of\n>> \"master\"; these patches are rebased/squashed ones which *replace* the\n>> ones sent the other day:\n>\n> That's very unfortunate, as some already usable bits from the\n> series are already in 'next'.\n\nWell, no problem, I'll redo this tomorrow if I can get some free time.  \nOtherwise it will have to wait to Monday.\n\n>>    1. Add -q/--quiet switch to git-ls-files\n>>\n>> Needed because run_cmd_pipe() doesn't propagate the child exit status\n>> and system() likes to be chatty on the standard out. Of the possible\n>> workarounds adding this switch seems to be the cleanest and most\n>> portable.\n>\n> I do not like this very much.  If it is a problem that\n> run_cmd_pipe() does not properly signal you an error, wouldn't\n> it be much better to fix _that_ problem?  That way we do not\n> have to add kludge to all commands we need to run and get the\n> exit status out of.\n\nThe problem is that run_cmd_pipe has two code paths, one for Windows  \nand one for all the rest.\n\nI could \"fix\" the non-Windows path by reading in all the command input  \nat once and explicitly doing a close() before returning. In that case  \nI believe that the exit status would be correctly set before the  \nfunction returns. The Windows code path, however, I am reluctant to  \ntouch as I don't know its behaviour and can't test it.\n\nThe other alternative was to use qx() or backticks, but then I would  \nhave to start worrying about sanitizing paths and once again because  \nof the dual-platform issues I was hesitant to do that. And how do you  \nredirect to /dev/null on Windows?\n\nIn any case, I actually *like* the addition of -q to git-ls-files. I  \ncan see some places in the codebase (look in git-commit.sh, for  \ninstance) where git-ls-files is run and the output is thrown away  \n(redirected to /dev/null) because it's not at all interesting to the  \ncaller. It seems that there is a real use case there which amounts to  \n\"just tell me whether these pathspecs are valid or not, I don't care  \nabout any of the rest\".\n\n>>    6. Add \"--patch\" option to git-add--interactive\n>>    7. Teach builtin-add to handle \"--patch\" option\n>\n> These should be straightforward, but use of Getopt::Long feels\n> way overkill for an internal command like add--interactive which\n> is called by only a very limited known callers (exactly one).\n>\n> If we assume \"a single caller\", we probably can do without 1 and\n> 4, by making the caller in builtin-add to validate the list of\n> pathspecs, reusing the code for \"ls-files --error-unmatch\",\n> before calling the external helper \"add--interactive\".\n\nWell, you're probably right. I was thinking, \"just in case we ever  \nneed to add more options\"; but it may be that we never do, and perhaps  \ngit-add--interactive will become builtin one day anyway.\n\n> There are functions refactored as part of the builtin-commit\n> series to be usable from outside \"ls-files\", and you can build a\n> imple function called from interactive_add(ac, av) using them:\n>\n> static int validate_pathspec(const char *prefix, const char **pattern)\n\nOk, thanks for the tip, Junio. Like I said, will look at this either  \ntomorrow or Monday and get something posted that applies on top of  \n\"next\".\n\nCheers,\nWincent\n"},{"id":"60809","messageId":"1195908922-88023-1-git-send-email-win@wincent.com","threadId":"10995","inReplyTo":"7vk5o8smaf.fsf@gitster.siamese.dyndns.org","subject":"[PATCH 0/3] Updates to git-add--interactive","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-24T12:55:19Z","receivedAt":"2007-11-24T12:55:19Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"Based on feedback from Junio last night here is a new series. This one\ngoes on top of the \"next\" branch.\n\nI moved the pathspec validity checking into builtin-add, thus\nsimplifying things considerably. I also enforced some strict assumptions\nabout how the optional arguments will be passed from builtin-add to\ngit-add--interactive; I can do this because it is the only caller, and\nit once again makes things a bit simpler.\n\nI don't expect this to actually be applied as-is, as there are some\ndoubts I have which I have added to the emails for each patch. In\nparticular there is a performance issue with the pathspec validity\nchecking (no idea why it is so much slower than just invoking\ngit-ls-files), and the behaviour is not exactly where I'd like it to be\n(things like \"git-add -i .\" don't work because \".\" is considered\ninvalid).\n\nSo looking for feedback on how to address this issues and will resubmit.\n"},{"id":"60812","messageId":"1195908922-88023-2-git-send-email-win@wincent.com","threadId":"10995","inReplyTo":"1195908922-88023-1-git-send-email-win@wincent.com","subject":"[PATCH 1/3] Rename patch_update_file function to patch_update_pathspec","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-24T12:55:20Z","receivedAt":"2007-11-24T12:55:20Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"The patch_update_file function really works on pathspecs, not files, so\nrename it to reflect its actual purpose.\n\nSigned-off-by: Wincent Colaiuta <win@wincent.com>\n---\n git-add--interactive.perl |    4 ++--\n 1 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex e347216..15b2c9f 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -571,11 +571,11 @@ sub patch_update_cmd {\n \t\t\t\t       HEADER => $status_head, },\n \t\t\t\t     @mods);\n \tfor (@them) {\n-\t\tpatch_update_file($_->{VALUE});\n+\t\tpatch_update_pathspec($_->{VALUE});\n \t}\n }\n \n-sub patch_update_file {\n+sub patch_update_pathspec {\n \tmy ($ix, $num);\n \tmy $path = shift;\n \tmy ($head, @hunk) = parse_diff($path);\n-- \n1.5.3.6.1992.gd646-dirty\n"},{"id":"60810","messageId":"1195908922-88023-3-git-send-email-win@wincent.com","threadId":"10995","inReplyTo":"1195908922-88023-2-git-send-email-win@wincent.com","subject":"[PATCH 2/3] Move pathspec validation into interactive_add","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-24T12:55:21Z","receivedAt":"2007-11-24T12:55:21Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"Simplify git-add--interactive by moving the pathspec validation into the\ninteractive_add() function of builtin-add. We can do this because\nbuiltin-add is the only caller of git-add--interactive.\n\nThe validate_pathspec() function added by this commit is based on a\nsample posted to the mailing list by Junio Hamano.\n\nSigned-off-by: Wincent Colaiuta <win@wincent.com>\n---\n\nNote that I made a couple of changes to the code posted by Junio on the\nmailing list to make the behaviour a little more convenient. At this\nstage it's just a proposal, and there are some issues that I'd like to\nsee ironed out.\n\nInstead of throwing away the return status of pathspec_match() I am\nkeeping it, and any successful match breaks out of the loop early. I\nobserved that passing in a file parameter like \"commit.h\" in the Git\nrepo the loop would execute over a thousand times, with pathspec_match\nreturning 0 some of the time and 1 at others. Evidently any time it\nreturns 1 that's enough to indicate that it's a valid pathspec.\n\nOne issue is that performance is very slow. If you do something like\n\"git-add -i *.c\" then there is a very noticeable delay.\n\nAnother issue is that simple shell shortcuts don't work, so something\nas simple as \"git-add -i .\" will report:\n\n  error: pathspec '.' did not match any file(s) known to git.\n\nIt would be nice if that could be detected as valid because it does\nwork when you pass it to git-add--interactive.\n\nLikewise it's not possible to validate pathspecs like \"\\*.sh\" either,\nbut seeing as these won't work when passed to git-add--interactive\nguess it doesn't matter (because they don't work with git-diff-files\neither, which is what git-add--interactive is using).\n\n builtin-add.c             |   38 ++++++++++++++++++++++++++++++++++----\n builtin-commit.c          |    2 +-\n commit.h                  |    2 +-\n git-add--interactive.perl |   12 ++----------\n 4 files changed, 38 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin-add.c b/builtin-add.c\nindex dd895df..06212c3 100644\n--- a/builtin-add.c\n+++ b/builtin-add.c\n@@ -135,10 +135,40 @@ static void refresh(int verbose, const char **pathspec)\n         free(seen);\n }\n \n-int interactive_add(int argc, const char **argv)\n+static int validate_pathspec(const char *prefix, const char **pattern)\n {\n-\tint status;\n-\tconst char **args = xmalloc(sizeof(const char *) * (argc + 1));\n+\tint i, ret = 0;\n+\tchar *m;\n+\n+\tif (!pattern || !*pattern)\n+\t\treturn 0;\n+\n+\tfor (i = 0; pattern[i]; i++)\n+\t\t;\n+\tm = xcalloc(1, i);\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tif ((ret = pathspec_match(pattern, m, ce->name, 0)))\n+\t\t\tbreak;\n+\t}\n+\tif (!ret)\n+\t\treport_path_error(m, pattern, prefix ? strlen(prefix) : 0);\n+\tfree(m);\n+\treturn ret;\n+}\n+\n+int interactive_add(const char *prefix, int argc, const char **argv)\n+{\n+\tint i, status;\n+\tconst char **args;\n+\tif (read_cache() < 0)\n+\t\tdie(\"index file corrupt\");\n+\tfor (i = 0; i < argc; i++) {\n+\t\tif (!(status = validate_pathspec(prefix, &argv[i])))\n+\t\t\treturn 1;\n+\t}\n+\n+\targs = xmalloc(sizeof(const char *) * (argc + 2));\n \targs[0] = \"add--interactive\";\n \tmemcpy((void *)args + sizeof(const char *), argv, sizeof(const char *) * argc);\n \targs[argc + 1] = NULL;\n@@ -176,7 +206,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, builtin_add_options,\n \t\t\t  builtin_add_usage, 0);\n \tif (add_interactive)\n-\t\texit(interactive_add(argc, argv));\n+\t\texit(interactive_add(prefix, argc, argv));\n \n \tgit_config(git_default_config);\n \ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 5d27102..95d1c0d 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -165,7 +165,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)\n \tconst char **pathspec = NULL;\n \n \tif (interactive) {\n-\t\tinteractive_add(argc, argv);\n+\t\tinteractive_add(prefix, argc, argv);\n \t\tcommit_style = COMMIT_AS_IS;\n \t\treturn get_index_file();\n \t}\ndiff --git a/commit.h b/commit.h\nindex 9f0765b..dc6fe31 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -113,7 +113,7 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,\n \n int in_merge_bases(struct commit *, struct commit **, int);\n \n-extern int interactive_add(int argc, const char **argv);\n+extern int interactive_add(const char *prefix, int argc, const char **argv);\n extern int rerere(void);\n \n static inline int single_parent(struct commit *commit)\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 15b2c9f..381bcbe 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -56,17 +56,9 @@ sub list_modified {\n \tmy ($only) = @_;\n \tmy (%data, @return);\n \tmy ($add, $del, $adddel, $file);\n-\tmy @tracked = ();\n-\n-\tif (@ARGV) {\n-\t\t@tracked = map {\n-\t\t\tchomp $_; $_;\n-\t\t} run_cmd_pipe(qw(git ls-files --exclude-standard --), @ARGV);\n-\t\treturn if (!@tracked);\n-\t}\n \n \tfor (run_cmd_pipe(qw(git diff-index --cached\n-\t\t\t     --numstat --summary HEAD --), @tracked)) {\n+\t\t\t     --numstat --summary HEAD --), @ARGV)) {\n \t\tif (($add, $del, $file) =\n \t\t    /^([-\\d]+)\t([-\\d]+)\t(.*)/) {\n \t\t\tmy ($change, $bin);\n@@ -89,7 +81,7 @@ sub list_modified {\n \t\t}\n \t}\n \n-\tfor (run_cmd_pipe(qw(git diff-files --numstat --summary --), @tracked)) {\n+\tfor (run_cmd_pipe(qw(git diff-files --numstat --summary --), @ARGV)) {\n \t\tif (($add, $del, $file) =\n \t\t    /^([-\\d]+)\t([-\\d]+)\t(.*)/) {\n \t\t\tif (!exists $data{$file}) {\n-- \n1.5.3.6.1992.gd646-dirty\n"},{"id":"60811","messageId":"1195908922-88023-4-git-send-email-win@wincent.com","threadId":"10995","inReplyTo":"1195908922-88023-3-git-send-email-win@wincent.com","subject":"[PATCH 3/3] Add \"--patch\" option to git-add--interactive","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-24T12:55:22Z","receivedAt":"2007-11-24T12:55:22Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"When the \"--patch\" option is supplied, the patch_update_file function is\ncalled, once for each supplied pathspec argument, and then we exit.\n\nSeeing as builtin-add is the only caller of git-add--interactive we can\nimpose a strict requirement on the format of the arguments to avoid\npossible ambiguity: an \"--\" argument must be used whenever any pathspecs\nare passed, both with the \"--patch\" option and without it.\n\nThis commit adds an early return mechanism to the patch_update_pathspec\nfunction to prevent spurious line feeds from being echoed when the user\npasses in pathspecs which match unchanged files.\n\nSigned-off-by: Wincent Colaiuta <win@wincent.com>\n---\n\nI am sure that someone who actually knows Perl will be able to come up\nwith a more compact implementation of the process_args() function.\nThis works, but it's written by a Perl amateur.\n\n Documentation/git-add.txt |    9 ++++++++-\n builtin-add.c             |   17 ++++++++++-------\n git-add--interactive.perl |   35 ++++++++++++++++++++++++++++++++---\n 3 files changed, 50 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/git-add.txt b/Documentation/git-add.txt\nindex 63829d9..ce22de8 100644\n--- a/Documentation/git-add.txt\n+++ b/Documentation/git-add.txt\n@@ -61,7 +61,14 @@ OPTIONS\n \n -i, \\--interactive::\n \tAdd modified contents in the working tree interactively to\n-\tthe index.\n+\tthe index. Optional path arguments may be supplied to limit\n+\toperation to a subset of the working tree. See ``Interactive\n+\tmode'' for details.\n+\n+-p, \\--patch:\n+\tSimilar to Interactive mode but the initial command loop is\n+\tbypassed and the 'patch' subcommand is invoked using each of\n+\tthe specified filepatterns before exiting.\n \n -u::\n \tUpdate only files that git already knows about. This is similar\ndiff --git a/builtin-add.c b/builtin-add.c\nindex 06212c3..e69bc13 100644\n--- a/builtin-add.c\n+++ b/builtin-add.c\n@@ -19,7 +19,7 @@ static const char * const builtin_add_usage[] = {\n \t\"git-add [options] [--] <filepattern>...\",\n \tNULL\n };\n-\n+static int patch_interactive = 0, add_interactive = 0;\n static int take_worktree_changes;\n \n static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)\n@@ -159,7 +159,7 @@ static int validate_pathspec(const char *prefix, const char **pattern)\n \n int interactive_add(const char *prefix, int argc, const char **argv)\n {\n-\tint i, status;\n+\tint i, status, pre_argc;\n \tconst char **args;\n \tif (read_cache() < 0)\n \t\tdie(\"index file corrupt\");\n@@ -168,10 +168,13 @@ int interactive_add(const char *prefix, int argc, const char **argv)\n \t\t\treturn 1;\n \t}\n \n-\targs = xmalloc(sizeof(const char *) * (argc + 2));\n+\tpre_argc = patch_interactive ? 3 : 2;\n+\targs = xmalloc(sizeof(const char *) * (argc + pre_argc + 1));\n \targs[0] = \"add--interactive\";\n-\tmemcpy((void *)args + sizeof(const char *), argv, sizeof(const char *) * argc);\n-\targs[argc + 1] = NULL;\n+\targs[1]\t= patch_interactive ? \"--patch\" : \"--\";\n+\targs[2] = \"--\";\n+\tmemcpy((void *)args + sizeof(const char *) * pre_argc, argv, sizeof(const char *) * argc);\n+\targs[argc + pre_argc] = NULL;\n \n \tstatus = run_command_v_opt(args, RUN_GIT_CMD);\n \tfree(args);\n@@ -184,13 +187,13 @@ static const char ignore_error[] =\n \"The following paths are ignored by one of your .gitignore files:\\n\";\n \n static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;\n-static int add_interactive = 0;\n \n static struct option builtin_add_options[] = {\n \tOPT__DRY_RUN(&show_only),\n \tOPT__VERBOSE(&verbose),\n \tOPT_GROUP(\"\"),\n \tOPT_BOOLEAN('i', \"interactive\", &add_interactive, \"interactive picking\"),\n+\tOPT_BOOLEAN('p', \"patch\", &patch_interactive, \"interactive patching\"),\n \tOPT_BOOLEAN('f', NULL, &ignored_too, \"allow adding otherwise ignored files\"),\n \tOPT_BOOLEAN('u', NULL, &take_worktree_changes, \"update tracked files\"),\n \tOPT_BOOLEAN( 0 , \"refresh\", &refresh_only, \"don't add, only refresh the index\"),\n@@ -205,7 +208,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, builtin_add_options,\n \t\t\t  builtin_add_usage, 0);\n-\tif (add_interactive)\n+\tif (add_interactive || patch_interactive)\n \t\texit(interactive_add(prefix, argc, argv));\n \n \tgit_config(git_default_config);\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 381bcbe..7d62ceb 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -2,6 +2,9 @@\n \n use strict;\n \n+# command line options\n+my $patch;\n+\n sub run_cmd_pipe {\n \tif ($^O eq 'MSWin32') {\n \t\tmy @invalid = grep {m/[\":*]/} @_;\n@@ -335,7 +338,8 @@ sub add_untracked_cmd {\n \n sub parse_diff {\n \tmy ($path) = @_;\n-\tmy @diff = run_cmd_pipe(qw(git diff-files -p --), $path);\n+\tmy @diff = run_cmd_pipe(qw(git diff-files -p --), $path)\n+\t    or return undef;\n \tmy (@hunk) = { TEXT => [] };\n \n \tfor (@diff) {\n@@ -571,6 +575,7 @@ sub patch_update_pathspec {\n \tmy ($ix, $num);\n \tmy $path = shift;\n \tmy ($head, @hunk) = parse_diff($path);\n+\treturn unless $head;\n \tfor (@{$head->{TEXT}}) {\n \t\tprint;\n \t}\n@@ -775,6 +780,20 @@ add untracked - add contents of untracked files to the staged set of changes\n EOF\n }\n \n+sub process_args {\n+\treturn unless @ARGV;\n+\tmy $arg = shift @ARGV;\n+\tif ($arg eq \"--patch\") {\n+\t\t$patch = 1;\n+\t\t$arg = shift @ARGV or die \"missing --\";\n+\t\tdie \"invalid argument $arg, expecting --\"\n+\t\t    unless $arg eq \"--\";\n+\t}\n+\telsif ($arg ne \"--\") {\n+\t\tdie \"invalid argument $arg, expecting --\";\n+\t}\n+}\n+\n sub main_loop {\n \tmy @cmd = ([ 'status', \\&status_cmd, ],\n \t\t   [ 'update', \\&update_cmd, ],\n@@ -803,6 +822,16 @@ sub main_loop {\n \t}\n }\n \n+process_args();\n refresh();\n-status_cmd();\n-main_loop();\n+if ($patch) {\n+\tprint \"No filepattern specified: what did you want to patch?\\n\"\n+\t    unless @ARGV;\n+\tforeach my $pathspec (@ARGV) {\n+\t\tpatch_update_pathspec($pathspec);\n+\t}\n+}\n+else {\n+\tstatus_cmd();\n+\tmain_loop();\n+}\n-- \n1.5.3.6.1992.gd646-dirty\n"},{"id":"60821","messageId":"7vir3rqwts.fsf@gitster.siamese.dyndns.org","threadId":"10995","inReplyTo":"1195908922-88023-3-git-send-email-win@wincent.com","subject":"Re: [PATCH 2/3] Move pathspec validation into interactive_add","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-24T19:15:27Z","receivedAt":"2007-11-24T19:15:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Wincent Colaiuta <win@wincent.com> writes:\n\n\n> Instead of throwing away the return status of pathspec_match() I am\n> keeping it, and any successful match breaks out of the loop early.\n\nLeaving it early before checking if all the given pathspecs are\nused defeats the whole \"error-unmatch\" business, doesn't it?\n\n> Another issue is that simple shell shortcuts don't work, so something\n> as simple as \"git-add -i .\" will report:\n>\n>   error: pathspec '.' did not match any file(s) known to git.\n\nThe sample code snippet I sent you probably is not doing \nget_pathspec() before using the \"pattern\" thing.  And I suspect\nthat ...\n\n> Likewise it's not possible to validate pathspecs like \"\\*.sh\" either,\n\n... may be related to that.\n"},{"id":"60825","messageId":"4D81F973-8951-458A-958D-E24C826CA548@wincent.com","threadId":"10995","inReplyTo":"7vir3rqwts.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 2/3] Move pathspec validation into interactive_add","fromName":"Wincent Colaiuta","fromEmail":"win@wincent.com","sentAt":"2007-11-24T21:50:10Z","receivedAt":"2007-11-24T21:50:10Z","isPatch":true,"sender":{"key":"greg@hurrell.net","avatar":"https://avatars.githubusercontent.com/u/7074?v=4"},"body":"El 24/11/2007, a las 20:15, Junio C Hamano escribió:\n\n> Wincent Colaiuta <win@wincent.com> writes:\n>\n>> Instead of throwing away the return status of pathspec_match() I am\n>> keeping it, and any successful match breaks out of the loop early.\n>\n> Leaving it early before checking if all the given pathspecs are\n> used defeats the whole \"error-unmatch\" business, doesn't it?\n\nNo, I probably didn't explain that clearly enough... If you look at  \nthe patch, I break out of the *inner* loop the first time a particular  \npattern matches. Then I move on to the next pattern, and any single  \ninvalid pattern will be enough to provide the \"error-unmatch\"  \nindication.\n\n>> Another issue is that simple shell shortcuts don't work, so something\n>> as simple as \"git-add -i .\" will report:\n>>\n>>  error: pathspec '.' did not match any file(s) known to git.\n>\n> The sample code snippet I sent you probably is not doing\n> get_pathspec() before using the \"pattern\" thing.  And I suspect\n> that ...\n>\n>> Likewise it's not possible to validate pathspecs like \"\\*.sh\" either,\n>\n> ... may be related to that.\n\n\nOk, thanks for the pointer. Didn't know what the get_pathspec()  \nfunction was for, but will have a play with it.\n\nCheers,\nWincent\n"},{"id":"60826","messageId":"7vtznbp91f.fsf@gitster.siamese.dyndns.org","threadId":"10995","inReplyTo":"4D81F973-8951-458A-958D-E24C826CA548@wincent.com","subject":"Re: [PATCH 2/3] Move pathspec validation into interactive_add","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-24T22:34:36Z","receivedAt":"2007-11-24T22:34:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Wincent Colaiuta <win@wincent.com> writes:\n\n> El 24/11/2007, a las 20:15, Junio C Hamano escribió:\n>\n>> Wincent Colaiuta <win@wincent.com> writes:\n>>\n>>> Instead of throwing away the return status of pathspec_match() I am\n>>> keeping it, and any successful match breaks out of the loop early.\n>>\n>> Leaving it early before checking if all the given pathspecs are\n>> used defeats the whole \"error-unmatch\" business, doesn't it?\n>\n> No, I probably didn't explain that clearly enough... If you look at\n> the patch, I break out of the *inner* loop the first time a particular\n> pattern matches. Then I move on to the next pattern, and any single\n> invalid pattern will be enough to provide the \"error-unmatch\"\n> indication.\n\nHeh, I missed that the code was doing something so stupid ;-).\n\nThe helper function pathspec_match() takes a single path and a\nset of pathspecs (NULL terminated), and says if the path is\ncovered with that set, while recording which one of the\npathspecs caused the match in the ps_matched array.\n\nYou are checking first with the full set of patterns, then the\nfull set minus the first pattern, and then the full set minus\nthe first two patterns, and so on.  No wonder it is inefficient.\n\nWhy are you trying to micro-optimize this part in the first\nplace?  Have you benchmarked and determined that iterating over\nfull cache is the bottleneck?\n\nIn order to prove that there is a pathspec (or more) that does\nnot match anything in the cache, you need to interate over the\nwhole cache at least once.  The only case you can short-cut is\nwhen you can tell that all of them have been used before you\niterated over all cache entries.\n\nSo lose that silly outer loop, and replace the inner function\nwith something like this:\n\n        static int validate_pathspec(const char *prefix, const char **pattern)\n        {\n                int i, ret = 0, num_patterns;\n                char *m;\n\n                if (!pattern || !*pattern)\n                        return 0;\n\n                for (num_patterns = 0; pattern[num_patterns]; num_patterns++)\n                        ;\n                m = xcalloc(1, num_patterns + 1);\n                for (i = 0; i < active_nr; i++) {\n                        struct cache_entry *ce = active_cache[i];\n\n                        if (pathspec_match(pattern, m, ce->name, 0)) {\n                                /*\n                                 * You could micro optimize by leaving\n                                 * the loop early when you notice that all\n\t\t\t\t * patterns are used.\n\t\t\t\t *\n                                 * if (strlen(m) == num_patterns)\n                                 * \tgoto ok;\n\t\t\t\t *\n                                 */ \n\t\t\t\t; /* nothing */\n\t\t\t}\n                }\n                report_path_error(m, pattern, prefix ? strlen(prefix) : 0);\n        ok:\n                free(m);\n                return ret;\n        }\n\nMy gut feeling is that the micro-optimization is not worth it here, but\nI didn't try.  I think without the micro-optimization the above is the\nsame as I gave you earlier.\n"}]}