Hi Peff,
On Mon, 20 May 2019, Jeff King wrote:
> In interactive mode, "git am -i --resolved" will try to generate a patch
> based on what is in the index, so that it can prompt "apply this
> patch?". To do so it needs the tree of HEAD, which it tries to get with
> get_oid_tree(). However, this doesn't yield a tree oid; the "tree" part
> just means "if you must disambiguate short oids, then prefer trees" (and
> we do not need to disambiguate at all, since we are feeding a ref name).
>
> Instead, we must parse the oid as a commit (which should always be true
> in a non-corrupt repository), and access its tree pointer manually.
>
> This has been broken since the conversion to C in 7ff2683253
> (builtin-am: implement -i/--interactive, 2015-08-04), but there was no
> test coverage because of interactive-mode's insistence on having a tty.
> That was lifted in the previous commit, so we can now add a test for
> this case.
>
> Note that before this patch, the test would result in a BUG() which
> comes from 3506dc9445 (has_uncommitted_changes(): fall back to empty
> tree, 2018-07-11). But before that, we'd have simply segfaulted (and in
> fact this is the exact type of case the BUG() added there was trying to
> catch!).
What an old breakage! Thanks for analyzing and fixing it.
> diff --git a/builtin/am.c b/builtin/am.c
> index ea16b844f1..33bd7a6eab 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -1339,9 +1339,17 @@ static void write_index_patch(const struct am_state
> *state)
> struct rev_info rev_info;
> FILE *fp;
>
> - if (!get_oid_tree("HEAD", &head))
> - tree = lookup_tree(the_repository, &head);
> - else
> + if (!get_oid("HEAD", &head)) {
> + struct object *obj;
> + struct commit *commit;
> +
> + obj = parse_object_or_die(&head, NULL);
> + commit = object_as_type(the_repository, obj, OBJ_COMMIT, 0);
> + if (!commit)
> + die("unable to parse HEAD as a commit");
Wouldn't this be easier to read like this:
struct commit *commit =
lookup_commit_reference(the_repository, &head);
> +
> + tree = get_commit_tree(commit);
> + } else
> tree = lookup_tree(the_repository,
> the_repository->hash_algo->empty_tree);
>
> diff --git a/t/t4257-am-interactive.sh b/t/t4257-am-interactive.sh
> new file mode 100755
> index 0000000000..6989bf7aba
> --- /dev/null
> +++ b/t/t4257-am-interactive.sh
> @@ -0,0 +1,49 @@
> +#!/bin/sh
> +
> +test_description='am --interactive tests'
> +. ./test-lib.sh
> +
> +test_expect_success 'set up patches to apply' '
> + test_commit unrelated &&
> + test_commit no-conflict &&
> + test_commit conflict-patch file patch &&
> + git format-patch --stdout -2 >mbox &&
> +
> + git reset --hard unrelated &&
> + test_commit conflict-master file master base
> +'
> +
> +# Sanity check our setup.
> +test_expect_success 'applying all patches generates conflict' '
> + test_must_fail git am mbox &&
> + echo resolved >file &&
> + git add -u &&
> + git am --resolved
> +'
> +
> +test_expect_success 'interactive am can apply a single patch' '
> + git reset --hard base &&
> + printf "%s\n" y n | git am -i mbox &&
Since we want contributors to copy-edit our test cases (even if they do
not happen to be Unix shell scripting experts), it would be better to
write
test_write_lines y n | git am -i mbox &&
here. Same for similar `printf` invocations further down.
> +
> + echo no-conflict >expect &&
> + git log -1 --format=%s >actual &&
> + test_cmp expect actual
I would prefer
test no-conflict = "$(git show -s --format=%s HEAD)"
or even better:
test_cmp_head_oneline () {
if test "$1" != "$(git show -s --format=%s HEAD)"
then
echo >&4 "HEAD's oneline is '$(git show -s \
--format=%s HEAD)'; expected '$1'"
return 1
fi
}
> +'
> +
> +test_expect_success 'interactive am can resolve conflict' '
> + git reset --hard base &&
> + printf "%s\n" y y | test_must_fail git am -i mbox &&
> + echo resolved >file &&
> + git add -u &&
> + printf "%s\n" v y | git am -i --resolved &&
Maybe a comment, to explain to the casual reader what the "v" and the "y"
are supposed to do?
> +
> + echo conflict-patch >expect &&
> + git log -1 --format=%s >actual &&
> + test_cmp expect actual &&
> +
> + echo resolved >expect &&
> + git cat-file blob HEAD:file >actual &&
> + test_cmp expect actual
> +'
After wrapping my head around the intentions of these commands, I agree
that they test for the right thing.
Thanks!
Dscho
> +
> +test_done
> --
> 2.22.0.rc1.539.g7bfcdfe86d
>