diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 63829d93cc..ce22de8d2e 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -61,7 +61,14 @@ OPTIONS -i, \--interactive:: Add modified contents in the working tree interactively to - the index. + the index. Optional path arguments may be supplied to limit + operation to a subset of the working tree. See ``Interactive + mode'' for details. + +-p, \--patch: + Similar to Interactive mode but the initial command loop is + bypassed and the 'patch' subcommand is invoked using each of + the specified filepatterns before exiting. -u:: Update only files that git already knows about. This is similar diff --git a/builtin-add.c b/builtin-add.c index 865c475ec9..5c29cc2f3f 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -19,7 +19,7 @@ static const char * const builtin_add_usage[] = { "git-add [options] [--] ...", NULL }; - +static int patch_interactive = 0, add_interactive = 0; static int take_worktree_changes; static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) @@ -144,7 +144,7 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p int interactive_add(int argc, const char **argv, const char *prefix) { - int status; + int status, ac; const char **args; const char **pathspec = NULL; @@ -154,11 +154,17 @@ int interactive_add(int argc, const char **argv, const char *prefix) return -1; } - args = xcalloc(sizeof(const char *), (argc + 2)); - args[0] = "add--interactive"; - if (argc) - memcpy(&(args[1]), pathspec, sizeof(const char *) * argc); - args[argc + 1] = NULL; + args = xcalloc(sizeof(const char *), (argc + 4)); + ac = 0; + args[ac++] = "add--interactive"; + if (patch_interactive) + args[ac++] = "--patch"; + args[ac++] = "--"; + if (argc) { + memcpy(&(args[ac]), pathspec, sizeof(const char *) * argc); + ac += argc; + } + args[ac] = NULL; status = run_command_v_opt(args, RUN_GIT_CMD); free(args); @@ -171,13 +177,13 @@ static const char ignore_error[] = "The following paths are ignored by one of your .gitignore files:\n"; static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0; -static int add_interactive = 0; static struct option builtin_add_options[] = { OPT__DRY_RUN(&show_only), OPT__VERBOSE(&verbose), OPT_GROUP(""), OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"), + OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"), OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"), OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"), OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"), @@ -192,6 +198,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, builtin_add_options, builtin_add_usage, 0); + if (patch_interactive) + add_interactive = 1; if (add_interactive) exit(interactive_add(argc, argv, prefix)); diff --git a/git-add--interactive.perl b/git-add--interactive.perl index e347216550..df5df3ec90 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -2,6 +2,9 @@ use strict; +# command line options +my $patch_mode; + sub run_cmd_pipe { if ($^O eq 'MSWin32') { my @invalid = grep {m/[":*]/} @_; @@ -552,8 +555,8 @@ sub help_patch_cmd { print <<\EOF ; y - stage this hunk n - do not stage this hunk -a - stage this and all the remaining hunks -d - do not stage this hunk nor any of the remaining hunks +a - stage this and all the remaining hunks in the file +d - do not stage this hunk nor any of the remaining hunks in the file j - leave this hunk undecided, see next undecided hunk J - leave this hunk undecided, see next hunk k - leave this hunk undecided, see previous undecided hunk @@ -563,13 +566,21 @@ sub help_patch_cmd { } sub patch_update_cmd { - my @mods = list_modified('file-only'); - @mods = grep { !($_->{BINARY}) } @mods; - return if (!@mods); + my @mods = grep { !($_->{BINARY}) } list_modified('file-only'); + my @them; - my (@them) = list_and_choose({ PROMPT => 'Patch update', - HEADER => $status_head, }, - @mods); + if (!@mods) { + print STDERR "No changes.\n"; + return 0; + } + if ($patch_mode) { + @them = @mods; + } + else { + @them = list_and_choose({ PROMPT => 'Patch update', + HEADER => $status_head, }, + @mods); + } for (@them) { patch_update_file($_->{VALUE}); } @@ -783,6 +794,20 @@ sub help_cmd { EOF } +sub process_args { + return unless @ARGV; + my $arg = shift @ARGV; + if ($arg eq "--patch") { + $patch_mode = 1; + $arg = shift @ARGV or die "missing --"; + die "invalid argument $arg, expecting --" + unless $arg eq "--"; + } + elsif ($arg ne "--") { + die "invalid argument $arg, expecting --"; + } +} + sub main_loop { my @cmd = ([ 'status', \&status_cmd, ], [ 'update', \&update_cmd, ], @@ -811,6 +836,12 @@ sub main_loop { } } +process_args(); refresh(); -status_cmd(); -main_loop(); +if ($patch_mode) { + patch_update_cmd(); +} +else { + status_cmd(); + main_loop(); +}