git-apply--interactive: Refactor patch mode code

This makes some aspects of the 'git add -p' loop configurable (within
the code), so that we can later reuse git-add--interactive for other
similar tools.

Most fields are fairly straightforward, but APPLY gets a subroutine
(instead of just a string a la 'apply --cached') so that we can handle
'checkout -p', which will need to atomically apply the patch twice
(index and worktree).

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Thomas Rast 2009-08-13 14:29:39 +02:00 committed by Junio C Hamano
parent 6517452d7a
commit 8f0bef6df9

View file

@ -73,6 +73,22 @@ sub colored {
# command line options # command line options
my $patch_mode; my $patch_mode;
sub apply_patch;
my %patch_modes = (
'stage' => {
DIFF => 'diff-files -p',
APPLY => sub { apply_patch 'apply --cached', @_; },
APPLY_CHECK => 'apply --cached',
VERB => 'Stage',
TARGET => '',
PARTICIPLE => 'staging',
FILTER => 'file-only',
},
);
my %patch_mode_flavour = %{$patch_modes{stage}};
sub run_cmd_pipe { sub run_cmd_pipe {
if ($^O eq 'MSWin32' || $^O eq 'msys') { if ($^O eq 'MSWin32' || $^O eq 'msys') {
my @invalid = grep {m/[":*]/} @_; my @invalid = grep {m/[":*]/} @_;
@ -613,12 +629,21 @@ sub add_untracked_cmd {
print "\n"; print "\n";
} }
sub run_git_apply {
my $cmd = shift;
my $fh;
open $fh, '| git ' . $cmd;
print $fh @_;
return close $fh;
}
sub parse_diff { sub parse_diff {
my ($path) = @_; my ($path) = @_;
my @diff = run_cmd_pipe(qw(git diff-files -p --), $path); my @diff_cmd = split(" ", $patch_mode_flavour{DIFF});
my @diff = run_cmd_pipe("git", @diff_cmd, "--", $path);
my @colored = (); my @colored = ();
if ($diff_use_color) { if ($diff_use_color) {
@colored = run_cmd_pipe(qw(git diff-files -p --color --), $path); @colored = run_cmd_pipe("git", @diff_cmd, qw(--color --), $path);
} }
my (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' }; my (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };
@ -877,6 +902,7 @@ sub edit_hunk_manually {
or die "failed to open hunk edit file for writing: " . $!; or die "failed to open hunk edit file for writing: " . $!;
print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n"; print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
print $fh @$oldtext; print $fh @$oldtext;
my $participle = $patch_mode_flavour{PARTICIPLE};
print $fh <<EOF; print $fh <<EOF;
# --- # ---
# To remove '-' lines, make them ' ' lines (context). # To remove '-' lines, make them ' ' lines (context).
@ -884,7 +910,7 @@ sub edit_hunk_manually {
# Lines starting with # will be removed. # Lines starting with # will be removed.
# #
# If the patch applies cleanly, the edited hunk will immediately be # If the patch applies cleanly, the edited hunk will immediately be
# marked for staging. If it does not apply cleanly, you will be given # marked for $participle. If it does not apply cleanly, you will be given
# an opportunity to edit again. If all lines of the hunk are removed, # an opportunity to edit again. If all lines of the hunk are removed,
# then the edit is aborted and the hunk is left unchanged. # then the edit is aborted and the hunk is left unchanged.
EOF EOF
@ -918,11 +944,8 @@ sub edit_hunk_manually {
sub diff_applies { sub diff_applies {
my $fh; my $fh;
open $fh, '| git apply --recount --cached --check'; return run_git_apply($patch_mode_flavour{APPLY_CHECK} . ' --recount --check',
for my $h (@_) { map { @{$_->{TEXT}} } @_);
print $fh @{$h->{TEXT}};
}
return close $fh;
} }
sub _restore_terminal_and_die { sub _restore_terminal_and_die {
@ -988,12 +1011,14 @@ sub edit_hunk_loop {
} }
sub help_patch_cmd { sub help_patch_cmd {
print colored $help_color, <<\EOF ; my $verb = lc $patch_mode_flavour{VERB};
y - stage this hunk my $target = $patch_mode_flavour{TARGET};
n - do not stage this hunk print colored $help_color, <<EOF ;
q - quit, do not stage this hunk nor any of the remaining ones y - $verb this hunk$target
a - stage this and all the remaining hunks in the file n - do not $verb this hunk$target
d - do not stage this hunk nor any of the remaining hunks in the file q - quit, do not $verb this hunk nor any of the remaining ones
a - $verb this and all the remaining hunks in the file
d - do not $verb this hunk nor any of the remaining hunks in the file
g - select a hunk to go to g - select a hunk to go to
/ - search for a hunk matching the given regex / - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk j - leave this hunk undecided, see next undecided hunk
@ -1006,8 +1031,17 @@ sub help_patch_cmd {
EOF EOF
} }
sub apply_patch {
my $cmd = shift;
my $ret = run_git_apply $cmd . ' --recount', @_;
if (!$ret) {
print STDERR @_;
}
return $ret;
}
sub patch_update_cmd { sub patch_update_cmd {
my @all_mods = list_modified('file-only'); my @all_mods = list_modified($patch_mode_flavour{FILTER});
my @mods = grep { !($_->{BINARY}) } @all_mods; my @mods = grep { !($_->{BINARY}) } @all_mods;
my @them; my @them;
@ -1138,8 +1172,9 @@ sub patch_update_file {
for (@{$hunk[$ix]{DISPLAY}}) { for (@{$hunk[$ix]{DISPLAY}}) {
print; print;
} }
print colored $prompt_color, 'Stage ', print colored $prompt_color, $patch_mode_flavour{VERB},
($hunk[$ix]{TYPE} eq 'mode' ? 'mode change' : 'this hunk'), ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' : ' this hunk'),
$patch_mode_flavour{TARGET},
" [y,n,q,a,d,/$other,?]? "; " [y,n,q,a,d,/$other,?]? ";
my $line = prompt_single_character; my $line = prompt_single_character;
if ($line) { if ($line) {
@ -1313,16 +1348,9 @@ sub patch_update_file {
if (@result) { if (@result) {
my $fh; my $fh;
my @patch = (@{$head->{TEXT}}, @result);
open $fh, '| git apply --cached --recount'; my $apply_routine = $patch_mode_flavour{APPLY};
for (@{$head->{TEXT}}, @result) { &$apply_routine(@patch);
print $fh $_;
}
if (!close $fh) {
for (@{$head->{TEXT}}, @result) {
print STDERR $_;
}
}
refresh(); refresh();
} }