Merge branch 'jk/am-retry'

"git am" has a safety feature to prevent it from starting a new
session when there already is a session going.  It reliably
triggers when a mbox is given on the command line, but it has to
rely on the tty-ness of the standard input.  Add an explicit way to
opt out of this safety with a command line option.

* jk/am-retry:
  test-terminal: drop stdin handling
  am: add explicit "--retry" option
This commit is contained in:
Junio C Hamano 2024-06-17 15:55:56 -07:00
commit 42b8b5bfd0
4 changed files with 22 additions and 32 deletions

View File

@ -18,7 +18,7 @@ SYNOPSIS
[--quoted-cr=<action>]
[--empty=(stop|drop|keep)]
[(<mbox> | <Maildir>)...]
'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
'git am' (--continue | --skip | --abort | --quit | --retry | --show-current-patch[=(diff|raw)] | --allow-empty)
DESCRIPTION
-----------
@ -208,6 +208,12 @@ Valid <action> for the `--whitespace` option are:
Abort the patching operation but keep HEAD and the index
untouched.
--retry::
Try to apply the last conflicting patch again. This is generally
only useful for passing extra options to the retry attempt
(e.g., `--3way`), since otherwise you'll just see the same
failure again.
--show-current-patch[=(diff|raw)]::
Show the message at which `git am` has stopped due to
conflicts. If `raw` is specified, show the raw contents of

View File

@ -2393,6 +2393,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
N_("show the patch being applied"),
PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
parse_opt_show_current_patch, RESUME_SHOW_PATCH_RAW },
OPT_CMDMODE(0, "retry", &resume_mode,
N_("try to apply current patch again"),
RESUME_APPLY),
OPT_CMDMODE(0, "allow-empty", &resume_mode,
N_("record the empty patch as an empty commit"),
RESUME_ALLOW_EMPTY),

View File

@ -3,7 +3,6 @@
test_description='git-am command-line options override saved options'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
format_patch () {
git format-patch --stdout -1 "$1" >"$1".eml
@ -27,7 +26,12 @@ test_expect_success 'setup' '
format_patch side2
'
test_expect_success TTY '--3way overrides --no-3way' '
test_expect_success '--retry fails without in-progress operation' '
test_must_fail git am --retry 2>err &&
test_grep "operation not in progress" err
'
test_expect_success '--3way overrides --no-3way' '
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout renamed-file &&
@ -40,7 +44,7 @@ test_expect_success TTY '--3way overrides --no-3way' '
# Applying side1 with am --3way will succeed due to the threeway-merge.
# Applying side2 will fail as --3way does not apply to it.
test_must_fail test_terminal git am --3way </dev/zero &&
test_must_fail git am --retry --3way &&
test_path_is_dir .git/rebase-apply &&
test side1 = "$(cat file2)"
'
@ -84,7 +88,7 @@ test_expect_success '--signoff overrides --no-signoff' '
test $(git cat-file commit HEAD | grep -c "Signed-off-by:") -eq 0
'
test_expect_success TTY '--reject overrides --no-reject' '
test_expect_success '--reject overrides --no-reject' '
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout first &&
@ -94,7 +98,7 @@ test_expect_success TTY '--reject overrides --no-reject' '
test_path_is_dir .git/rebase-apply &&
test_path_is_missing file.rej &&
test_must_fail test_terminal git am --reject </dev/zero &&
test_must_fail git am --retry --reject </dev/zero &&
test_path_is_dir .git/rebase-apply &&
test_path_is_file file.rej
'

View File

@ -5,17 +5,15 @@ use warnings;
use IO::Pty;
use File::Copy;
# Run @$argv in the background with stdio redirected to $in, $out and $err.
# Run @$argv in the background with stdio redirected to $out and $err.
sub start_child {
my ($argv, $in, $out, $err) = @_;
my ($argv, $out, $err) = @_;
my $pid = fork;
if (not defined $pid) {
die "fork failed: $!"
} elsif ($pid == 0) {
open STDIN, "<&", $in;
open STDOUT, ">&", $out;
open STDERR, ">&", $err;
close $in;
close $out;
exec(@$argv) or die "cannot exec '$argv->[0]': $!"
}
@ -51,17 +49,6 @@ sub xsendfile {
copy($in, $out, 4096) or $!{EIO} or die "cannot copy from child: $!";
}
sub copy_stdin {
my ($in) = @_;
my $pid = fork;
if (!$pid) {
xsendfile($in, \*STDIN);
exit 0;
}
close($in);
return $pid;
}
sub copy_stdio {
my ($out, $err) = @_;
my $pid = fork;
@ -81,25 +68,15 @@ if ($#ARGV < 1) {
die "usage: test-terminal program args";
}
$ENV{TERM} = 'vt100';
my $parent_in = new IO::Pty;
my $parent_out = new IO::Pty;
my $parent_err = new IO::Pty;
$parent_in->set_raw();
$parent_out->set_raw();
$parent_err->set_raw();
$parent_in->slave->set_raw();
$parent_out->slave->set_raw();
$parent_err->slave->set_raw();
my $pid = start_child(\@ARGV, $parent_in->slave, $parent_out->slave, $parent_err->slave);
close $parent_in->slave;
my $pid = start_child(\@ARGV, $parent_out->slave, $parent_err->slave);
close $parent_out->slave;
close $parent_err->slave;
my $in_pid = copy_stdin($parent_in);
copy_stdio($parent_out, $parent_err);
my $ret = finish_child($pid);
# If the child process terminates before our copy_stdin() process is able to
# write all of its data to $parent_in, the copy_stdin() process could stall.
# Send SIGTERM to it to ensure it terminates.
kill 'TERM', $in_pid;
finish_child($in_pid);
exit($ret);