Re: [PATCH 2/7] revert: allow cherry-pick --continue to commit before resuming

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi again,

Jonathan Nieder wrote:
> When "git cherry-pick ..bar" encounters conflicts, permit the operator
> to use cherry-pick --continue after resolving them as a shortcut for
> "git commit && git cherry-pick --continue" to record the resolution
> and carry on with the rest of the sequence.

Sounds good.  I remember my implementation being quite complicated;
let's see how you've done this.

> Example: after encountering a conflict from running "git cherry-pick
> foo bar baz":
>
>        CONFLICT (content): Merge conflict in main.c
>        error: could not apply f78a8d98c... bar!
>        hint: after resolving the conflicts, mark the corrected paths
>        hint: with 'git add <paths>' or 'git rm <paths>'
>        hint: and commit the result with 'git commit'
>
> We edit main.c to resolve the conflict, mark it acceptable with "git
> add main.c", and can run "cherry-pick --continue" to resume the
> sequence.
>
>        $ git cherry-pick --continue
>        [editor opens to confirm commit message]
>        [master 78c8a8c98] bar!
>         1 files changed, 1 insertions(+), 1 deletions(-)
>        [master 87ca8798c] baz!
>         1 files changed, 1 insertions(+), 1 deletions(-)

I like the presentation of this example: much clearer than my examples.

>  builtin/revert.c                |   23 ++++++-
>  t/t3510-cherry-pick-sequence.sh |  139 ++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 156 insertions(+), 6 deletions(-)

Oh, good -- lots of new tests :)

> diff --git a/builtin/revert.c b/builtin/revert.c
> index 9f6c85c1..a43b4d85 100644
> --- a/builtin/revert.c
> +++ b/builtin/revert.c
> @@ -1038,18 +1038,35 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
>        return 0;
>  }
>
> +static int continue_single_pick(void)
> +{
> +       const char *argv[] = { "commit", NULL };
> +
> +       if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
> +           !file_exists(git_path("REVERT_HEAD")))
> +               return error(_("no cherry-pick or revert in progress"));
> +       return run_command_v_opt(argv, RUN_GIT_CMD);
> +}

Very nice!  I can see how the introduction of REVERT_HEAD simplifies things :)
I'm totally embarrassed by the horribly convoluted logic in the "New
sequencer workflow!" I posted earlier.

Note to self: don't capitalize error() messages.

>  static int sequencer_continue(struct replay_opts *opts)
>  {
>        struct commit_list *todo_list = NULL;
>
>        if (!file_exists(git_path(SEQ_TODO_FILE)))
> -               return error(_("No %s in progress"), action_name(opts));
> +               return continue_single_pick();
>        read_populate_opts(&opts);
>        read_populate_todo(&todo_list, opts);
>
>        /* Verify that the conflict has been resolved */
> -       if (!index_differs_from("HEAD", 0))
> -               todo_list = todo_list->next;
> +       if (file_exists(git_path("CHERRY_PICK_HEAD")) ||
> +           file_exists(git_path("REVERT_HEAD"))) {
> +               int ret = continue_single_pick();
> +               if (ret)
> +                       return ret;
> +       }
> +       if (index_differs_from("HEAD", 0))
> +               return error_dirty_index(opts);
> +       todo_list = todo_list->next;
>        return pick_commits(todo_list, opts);
>  }

Very nicely done.  I can see why 1/7 makes so much sense now:  it
helps think of different operations independently.

> diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
> index 2c4c1c85..4d1883b7 100755
> --- a/t/t3510-cherry-pick-sequence.sh
> +++ b/t/t3510-cherry-pick-sequence.sh
> @@ -2,6 +2,7 @@
>
>  test_description='Test cherry-pick continuation features
>
> + +  conflicting: rewrites unrelated to conflicting
>   + yetanotherpick: rewrites foo to e
>   + anotherpick: rewrites foo to d
>   + picked: rewrites foo to c

Note to self: this list of commits is becoming quite unwieldy.  We
should probably refactor these sometime.

> @@ -27,6 +28,7 @@ test_cmp_rev () {
>  }
>
>  test_expect_success setup '
> +       git config advice.detachedhead false
>        echo unrelated >unrelated &&
>        git add unrelated &&
>        test_commit initial foo a &&

Huh, why are you moving this line up?  Oh, right: there are
"test_commit" statements in the setup- good catch.  This is unrelated
to your patch and should be a separate commit though.

> @@ -35,8 +37,8 @@ test_expect_success setup '
>        test_commit picked foo c &&
>        test_commit anotherpick foo d &&
>        test_commit yetanotherpick foo e &&
> -       git config advice.detachedhead false
> -
> +       pristine_detach initial &&
> +       test_commit conflicting unrelated
>  '

Looks fishy- I wonder why you're doing this.  Let's read ahead and find out.

> @@ -243,7 +245,66 @@ test_expect_success '--continue complains when there are unresolved conflicts' '
>        test_must_fail git cherry-pick --continue
>  '
>
> -test_expect_success '--continue continues after conflicts are resolved' '
> +test_expect_success '--continue of single cherry-pick' '
> +       pristine_detach initial &&
> +       echo c >expect &&
> +       test_must_fail git cherry-pick picked &&
> +       echo c >foo &&
> +       git add foo &&
> +       git cherry-pick --continue &&
> +
> +       test_cmp expect foo &&
> +       test_cmp_rev initial HEAD^ &&
> +       git diff --exit-code HEAD &&
> +       test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
> +'

Beautiful.  The tests I wrote are ugly in comparison :\

> +test_expect_success '--continue of single revert' '
> +       pristine_detach initial &&
> +       echo resolved >expect &&
> +       echo "Revert \"picked\"" >expect.msg &&
> +       test_must_fail git revert picked &&
> +       echo resolved >foo &&
> +       git add foo &&
> +       git cherry-pick --continue &&

Huh?  You're continuing a "git revert" with a a "git cherry-pick
--continue"?  The current 'master' still uses a commit_list, and
doesn't allow mixed "pick" and "revert" instructions yet.

> +       git diff --exit-code HEAD &&
> +       test_cmp expect foo &&
> +       test_cmp_rev initial HEAD^ &&
> +       git diff-tree -s --pretty=tformat:%s HEAD >msg &&
> +       test_cmp expect.msg msg &&
> +       test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
> +       test_must_fail git rev-parse --verify REVERT_HEAD
> +'

A couple of notes:

1. I haven't used the "-s" flag of "git diff-tree" before, so I opened
up the documentation to find this:

        By default, 'git diff-tree --stdin' shows differences,
        either in machine-readable form (without '-p') or in patch
        form (with '-p').  This output can be suppressed.  It is
        only useful with '-v' flag.

Very misleading.  TODO: Fix this.

2. Why did you use "diff-tree" to get the commit message?  Isn't
"cat-file commit" much more straightforward?

> +test_expect_success '--continue after resolving conflicts' '
> +       pristine_detach initial &&
> +       echo d >expect &&
> +       cat >expect.log <<-\EOF &&
> +       OBJID
> +       :100644 100644 OBJID OBJID M    foo
> +       OBJID
> +       :100644 100644 OBJID OBJID M    foo
> +       OBJID
> +       :100644 100644 OBJID OBJID M    unrelated
> +       OBJID
> +       :000000 100644 OBJID OBJID A    foo
> +       :000000 100644 OBJID OBJID A    unrelated
> +       EOF
> +       test_must_fail git cherry-pick base..anotherpick &&
> +       echo c >foo &&
> +       git add foo &&
> +       git cherry-pick --continue &&
> +       {
> +               git rev-list HEAD |
> +               git diff-tree --root --stdin |
> +               sed "s/$_x40/OBJID/g"
> +       } >actual.log &&
> +       test_cmp expect foo &&
> +       test_cmp expect.log actual.log
> +'

Unchanged from the original: I suspect you've moved the generation of
expectation messages up to produce a clean diff.

> +test_expect_success '--continue after resolving conflicts and committing' '
>        pristine_detach initial &&
>        test_must_fail git cherry-pick base..anotherpick &&
>        echo "c" >foo &&

Okay, the diff isn't all that clean :P

> +test_expect_success '--continue asks for help after resolving patch to nil' '
> +       pristine_detach conflicting &&
> +       test_must_fail git cherry-pick initial..picked &&
> +
> +       test_cmp_rev unrelatedpick CHERRY_PICK_HEAD &&
> +       git checkout HEAD -- unrelated &&
> +       test_must_fail git cherry-pick --continue 2>msg &&
> +       test_i18ngrep "The previous cherry-pick is now empty" msg
> +'

I thought it was a bad idea to grep for specific output messages,
because of their volatile nature?  Remind me what this test has to do
with the rest of your patch?

> +test_expect_failure 'follow advice and skip nil patch' '
> +       pristine_detach conflicting &&
> +       test_must_fail git cherry-pick initial..picked &&
> +
> +       git checkout HEAD -- unrelated &&
> +       test_must_fail git cherry-pick --continue &&
> +       git reset &&
> +       git cherry-pick --continue &&
> +
> +       git rev-list initial..HEAD >commits &&
> +       test_line_count = 3 commits
> +'

Again, what does this test have to do with the rest of your patch?

>  test_expect_success '--continue respects opts' '
>        pristine_detach initial &&
>        test_must_fail git cherry-pick -x base..anotherpick &&
> @@ -288,6 +372,29 @@ test_expect_success '--continue respects opts' '
>        grep "cherry picked from" anotherpick_msg
>  '
>
> +test_expect_success '--continue of single-pick respects -x' '
> +       pristine_detach initial &&
> +       test_must_fail git cherry-pick -x picked &&
> +       echo c >foo &&
> +       git add foo &&
> +       git cherry-pick --continue &&
> +       test_path_is_missing .git/sequencer &&
> +       git cat-file commit HEAD >msg &&
> +       grep "cherry picked from" msg
> +'

I'd have liked s/respects -x/respects opts/ here for symmetry with the
previous test.

> +test_expect_success '--continue respects -x in first commit in multi-pick' '
> +       pristine_detach initial &&
> +       test_must_fail git cherry-pick -x picked anotherpick &&
> +       echo c >foo &&
> +       git add foo &&
> +       git cherry-pick --continue &&
> +       test_path_is_missing .git/sequencer &&
> +       git cat-file commit HEAD^ >msg &&
> +       picked=$(git rev-parse --verify picked) &&
> +       grep "cherry picked from.*$picked" msg
> +'

Can you explain why "first commit in a multi-pick" is a special case?

> @@ -306,6 +413,32 @@ test_expect_success '--signoff is not automatically propagated to resolved confl
>        grep "Signed-off-by:" anotherpick_msg
>  '
>
> +test_expect_success '--signoff dropped for implicit commit of resolution, multi-pick case' '
> +       pristine_detach initial &&
> +       test_must_fail git cherry-pick -s picked anotherpick &&
> +       echo c >foo &&
> +       git add foo &&
> +       git cherry-pick --continue &&
> +
> +       git diff --exit-code HEAD &&
> +       test_cmp_rev initial HEAD^^ &&
> +       git cat-file commit HEAD^ >msg &&
> +       ! grep Signed-off-by: msg
> +'

Unrelated.

> +test_expect_success 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' '
> +       pristine_detach initial &&
> +       test_must_fail git cherry-pick -s picked &&
> +       echo c >foo &&
> +       git add foo &&
> +       git cherry-pick --continue &&
> +
> +       git diff --exit-code HEAD &&
> +       test_cmp_rev initial HEAD^ &&
> +       git cat-file commit HEAD >msg &&
> +       ! grep Signed-off-by: msg
> +'

If the previous test were a separate patch preceding this one, this'd
belong here.

Thanks for working on this.

-- Ram
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]