Matt McCutchen <m...@mattmccutchen.net> writes:

> The current message printed by "git merge-recursive" for a rename/delete
> conflict is like this:
>
> CONFLICT (rename/delete): new-path deleted in HEAD and renamed in
> other-branch. Version other-branch of new-path left in tree.
>
> To be more helpful, the message should show both paths of the rename and
> state that the deletion occurred at the old path, not the new path.  So
> change the message to the following format:
>
> CONFLICT (rename/delete): old-path deleted in HEAD and renamed to
> new-path in other-branch. Version other-branch of new-path left in tree.

Sounds like a sensible goal.

> Please check that my refactoring is indeed correct!  All the existing tests 
> pass
> for me, but the existing test coverage of these conflict messages looks poor.

This unfortunately is doing a bit too many things at once from that
point of view.  I need to reserve a solid quiet 20-minutes without
distraction to check it, which I am hoping to do tonight.

Thanks.

>
>  merge-recursive.c              | 117 
> ++++++++++++++++++++++-------------------
>  t/t6045-merge-rename-delete.sh |  23 ++++++++
>  2 files changed, 86 insertions(+), 54 deletions(-)
>  create mode 100755 t/t6045-merge-rename-delete.sh
>
> diff --git a/merge-recursive.c b/merge-recursive.c
> index d327209..e8fce10 100644
> --- a/merge-recursive.c
> +++ b/merge-recursive.c
> @@ -1061,16 +1061,20 @@ static int merge_file_one(struct merge_options *o,
>  }
>  
>  static int handle_change_delete(struct merge_options *o,
> -                              const char *path,
> +                              const char *path, const char *old_path,
>                                const struct object_id *o_oid, int o_mode,
> -                              const struct object_id *a_oid, int a_mode,
> -                              const struct object_id *b_oid, int b_mode,
> +                              const struct object_id *changed_oid,
> +                              int changed_mode,
> +                              const char *change_branch,
> +                              const char *delete_branch,
>                                const char *change, const char *change_past)
>  {
> -     char *renamed = NULL;
> +     char *alt_path = NULL;
> +     const char *update_path = path;
>       int ret = 0;
> +
>       if (dir_in_way(path, !o->call_depth, 0)) {
> -             renamed = unique_path(o, path, a_oid ? o->branch1 : o->branch2);
> +             update_path = alt_path = unique_path(o, path, change_branch);
>       }
>  
>       if (o->call_depth) {
> @@ -1081,43 +1085,43 @@ static int handle_change_delete(struct merge_options 
> *o,
>                */
>               ret = remove_file_from_cache(path);
>               if (!ret)
> -                     ret = update_file(o, 0, o_oid, o_mode,
> -                                       renamed ? renamed : path);
> -     } else if (!a_oid) {
> -             if (!renamed) {
> -                     output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
> -                            "and %s in %s. Version %s of %s left in tree."),
> -                            change, path, o->branch1, change_past,
> -                            o->branch2, o->branch2, path);
> -                     ret = update_file(o, 0, b_oid, b_mode, path);
> -             } else {
> -                     output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
> -                            "and %s in %s. Version %s of %s left in tree at 
> %s."),
> -                            change, path, o->branch1, change_past,
> -                            o->branch2, o->branch2, path, renamed);
> -                     ret = update_file(o, 0, b_oid, b_mode, renamed);
> -             }
> +                     ret = update_file(o, 0, o_oid, o_mode, update_path);
>       } else {
> -             if (!renamed) {
> -                     output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
> -                            "and %s in %s. Version %s of %s left in tree."),
> -                            change, path, o->branch2, change_past,
> -                            o->branch1, o->branch1, path);
> +             if (!alt_path) {
> +                     if (!old_path) {
> +                             output(o, 1, _("CONFLICT (%s/delete): %s 
> deleted in %s "
> +                                    "and %s in %s. Version %s of %s left in 
> tree."),
> +                                    change, path, delete_branch, change_past,
> +                                    change_branch, change_branch, path);
> +                     } else {
> +                             output(o, 1, _("CONFLICT (%s/delete): %s 
> deleted in %s "
> +                                    "and %s to %s in %s. Version %s of %s 
> left in tree."),
> +                                    change, old_path, delete_branch, 
> change_past, path,
> +                                    change_branch, change_branch, path);
> +                     }
>               } else {
> -                     output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
> -                            "and %s in %s. Version %s of %s left in tree at 
> %s."),
> -                            change, path, o->branch2, change_past,
> -                            o->branch1, o->branch1, path, renamed);
> -                     ret = update_file(o, 0, a_oid, a_mode, renamed);
> +                     if (!old_path) {
> +                             output(o, 1, _("CONFLICT (%s/delete): %s 
> deleted in %s "
> +                                    "and %s in %s. Version %s of %s left in 
> tree at %s."),
> +                                    change, path, delete_branch, change_past,
> +                                    change_branch, change_branch, path, 
> alt_path);
> +                     } else {
> +                             output(o, 1, _("CONFLICT (%s/delete): %s 
> deleted in %s "
> +                                    "and %s to %s in %s. Version %s of %s 
> left in tree at %s."),
> +                                    change, old_path, delete_branch, 
> change_past, path,
> +                                    change_branch, change_branch, path, 
> alt_path);
> +                     }
>               }
>               /*
> -              * No need to call update_file() on path when !renamed, since
> -              * that would needlessly touch path.  We could call
> -              * update_file_flags() with update_cache=0 and update_wd=0,
> -              * but that's a no-op.
> +              * No need to call update_file() on path when change_branch ==
> +              * o->branch1 && !alt_path, since that would needlessly touch
> +              * path.  We could call update_file_flags() with update_cache=0
> +              * and update_wd=0, but that's a no-op.
>                */
> +             if (change_branch != o->branch1 || alt_path)
> +                     ret = update_file(o, 0, changed_oid, changed_mode, 
> update_path);
>       }
> -     free(renamed);
> +     free(alt_path);
>  
>       return ret;
>  }
> @@ -1125,28 +1129,17 @@ static int handle_change_delete(struct merge_options 
> *o,
>  static int conflict_rename_delete(struct merge_options *o,
>                                  struct diff_filepair *pair,
>                                  const char *rename_branch,
> -                                const char *other_branch)
> +                                const char *delete_branch)
>  {
>       const struct diff_filespec *orig = pair->one;
>       const struct diff_filespec *dest = pair->two;
> -     const struct object_id *a_oid = NULL;
> -     const struct object_id *b_oid = NULL;
> -     int a_mode = 0;
> -     int b_mode = 0;
> -
> -     if (rename_branch == o->branch1) {
> -             a_oid = &dest->oid;
> -             a_mode = dest->mode;
> -     } else {
> -             b_oid = &dest->oid;
> -             b_mode = dest->mode;
> -     }
>  
>       if (handle_change_delete(o,
>                                o->call_depth ? orig->path : dest->path,
> +                              o->call_depth ? NULL : orig->path,
>                                &orig->oid, orig->mode,
> -                              a_oid, a_mode,
> -                              b_oid, b_mode,
> +                              &dest->oid, dest->mode,
> +                              rename_branch, delete_branch,
>                                _("rename"), _("renamed")))
>               return -1;
>  
> @@ -1665,11 +1658,27 @@ static int handle_modify_delete(struct merge_options 
> *o,
>                                struct object_id *a_oid, int a_mode,
>                                struct object_id *b_oid, int b_mode)
>  {
> +     const char *modify_branch, *delete_branch;
> +     struct object_id *changed_oid;
> +     int changed_mode;
> +
> +     if (a_oid) {
> +             modify_branch = o->branch1;
> +             delete_branch = o->branch2;
> +             changed_oid = a_oid;
> +             changed_mode = a_mode;
> +     } else {
> +             modify_branch = o->branch2;
> +             delete_branch = o->branch1;
> +             changed_oid = b_oid;
> +             changed_mode = b_mode;
> +     }
> +
>       return handle_change_delete(o,
> -                                 path,
> +                                 path, NULL,
>                                   o_oid, o_mode,
> -                                 a_oid, a_mode,
> -                                 b_oid, b_mode,
> +                                 changed_oid, changed_mode,
> +                                 modify_branch, delete_branch,
>                                   _("modify"), _("modified"));
>  }
>  
> diff --git a/t/t6045-merge-rename-delete.sh b/t/t6045-merge-rename-delete.sh
> new file mode 100755
> index 0000000..8f33244
> --- /dev/null
> +++ b/t/t6045-merge-rename-delete.sh
> @@ -0,0 +1,23 @@
> +#!/bin/sh
> +
> +test_description='Merge-recursive rename/delete conflict message'
> +. ./test-lib.sh
> +
> +test_expect_success 'rename/delete' '
> +echo foo >A &&
> +git add A &&
> +git commit -m "initial" &&
> +
> +git checkout -b rename &&
> +git mv A B &&
> +git commit -m "rename" &&
> +
> +git checkout master &&
> +git rm A &&
> +git commit -m "delete" &&
> +
> +test_must_fail git merge --strategy=recursive rename >output &&
> +test_i18ngrep "CONFLICT (rename/delete): A deleted in HEAD and renamed to B 
> in rename. Version rename of B left in tree." output
> +'
> +
> +test_done

Reply via email to