rerere: do use multiple variants

This enables the multiple-variant support for real. Multiple conflicts of the same shape can have differences in contexts where they appear, interfering the replaying of recorded resolution of one conflict to another, and in such a case, their resolutions are recorded as different variants under the same conflict ID. We still need to adjust garbage collection codepaths for this change, but the basic "replay" functionality is functional with this change. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jul 30, 2015 at 15:49 UTC 629716d256a792179325c2cc7945bb2d81dda8c2
2 files changed +61 -39
rerere.c
+60 -38
@@ -74,7 +74,9 @@ static void assign_variant(struct rerere_id *id)
74
75 variant = id->variant;
76 if (variant < 0) {
77 - variant = 0; /* for now */
77 + for (variant = 0; variant < rr_dir->status_nr; variant++)
78 + if (!rr_dir->status[variant])
79 + break;
80 }
81 fit_variant(rr_dir, variant);
82 id->variant = variant;
@@ -177,15 +179,6 @@ static int has_rerere_resolution(const struct rerere_id *id)
179 return ((id->collection->status[variant] & both) == both);
180 }
181
180 -static int has_rerere_preimage(const struct rerere_id *id)
181 -{
182 - int variant = id->variant;
183 -
184 - if (variant < 0)
185 - return 0;
186 - return (id->collection->status[variant] & RR_HAS_PREIMAGE);
187 -}
188 -
182 static struct rerere_id *new_rerere_id_hex(char *hex)
183 {
184 struct rerere_id *id = xmalloc(sizeof(*id));
@@ -818,6 +811,13 @@ static void update_paths(struct string_list *update)
811 rollback_lock_file(&index_lock);
812 }
813
814 +static void remove_variant(struct rerere_id *id)
815 +{
816 + unlink_or_warn(rerere_path(id, "postimage"));
817 + unlink_or_warn(rerere_path(id, "preimage"));
818 + id->collection->status[id->variant] = 0;
819 +}
820 +
821 /*
822 * The path indicated by rr_item may still have conflict for which we
823 * have a recorded resolution, in which case replay it and optionally
@@ -830,32 +830,46 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
830 {
831 const char *path = rr_item->string;
832 struct rerere_id *id = rr_item->util;
833 + struct rerere_dir *rr_dir = id->collection;
834 int variant;
835
835 - if (id->variant < 0)
836 - assign_variant(id);
836 variant = id->variant;
837
839 - if (!has_rerere_preimage(id)) {
838 + /* Has the user resolved it already? */
839 + if (variant >= 0) {
840 + if (!handle_file(path, NULL, NULL)) {
841 + copy_file(rerere_path(id, "postimage"), path, 0666);
842 + id->collection->status[variant] |= RR_HAS_POSTIMAGE;
843 + fprintf(stderr, "Recorded resolution for '%s'.\n", path);
844 + free_rerere_id(rr_item);
845 + rr_item->util = NULL;
846 + return;
847 + }
848 /*
841 - * We are the first to encounter this conflict. Ask
842 - * handle_file() to write the normalized contents to
843 - * the "preimage" file.
849 + * There may be other variants that can cleanly
850 + * replay. Try them and update the variant number for
851 + * this one.
852 */
845 - handle_file(path, NULL, rerere_path(id, "preimage"));
846 - if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
847 - const char *path = rerere_path(id, "postimage");
848 - if (unlink(path))
849 - die_errno("cannot unlink stray '%s'", path);
850 - id->collection->status[variant] &= ~RR_HAS_POSTIMAGE;
851 - }
852 - id->collection->status[variant] |= RR_HAS_PREIMAGE;
853 - fprintf(stderr, "Recorded preimage for '%s'\n", path);
854 - return;
855 - } else if (has_rerere_resolution(id)) {
856 - /* Is there a recorded resolution we could attempt to apply? */
857 - if (merge(id, path))
858 - return; /* failed to replay */
853 + }
854 +
855 + /* Does any existing resolution apply cleanly? */
856 + for (variant = 0; variant < rr_dir->status_nr; variant++) {
857 + const int both = RR_HAS_PREIMAGE | RR_HAS_POSTIMAGE;
858 + struct rerere_id vid = *id;
859 +
860 + if ((rr_dir->status[variant] & both) != both)
861 + continue;
862 +
863 + vid.variant = variant;
864 + if (merge(&vid, path))
865 + continue; /* failed to replay */
866 +
867 + /*
868 + * If there already is a different variant that applies
869 + * cleanly, there is no point maintaining our own variant.
870 + */
871 + if (0 <= id->variant && id->variant != variant)
872 + remove_variant(id);
873
874 if (rerere_autoupdate)
875 string_list_insert(update, path);
@@ -863,16 +877,24 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
877 fprintf(stderr,
878 "Resolved '%s' using previous resolution.\n",
879 path);
866 - } else if (!handle_file(path, NULL, NULL)) {
867 - /* The user has resolved it. */
868 - copy_file(rerere_path(id, "postimage"), path, 0666);
869 - id->collection->status[variant] |= RR_HAS_POSTIMAGE;
870 - fprintf(stderr, "Recorded resolution for '%s'.\n", path);
871 - } else {
880 + free_rerere_id(rr_item);
881 + rr_item->util = NULL;
882 return;
883 }
874 - free_rerere_id(rr_item);
875 - rr_item->util = NULL;
884 +
885 + /* None of the existing one applies; we need a new variant */
886 + assign_variant(id);
887 +
888 + variant = id->variant;
889 + handle_file(path, NULL, rerere_path(id, "preimage"));
890 + if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
891 + const char *path = rerere_path(id, "postimage");
892 + if (unlink(path))
893 + die_errno("cannot unlink stray '%s'", path);
894 + id->collection->status[variant] &= ~RR_HAS_POSTIMAGE;
895 + }
896 + id->collection->status[variant] |= RR_HAS_PREIMAGE;
897 + fprintf(stderr, "Recorded preimage for '%s'\n", path);
898 }
899
900 static int do_plain_rerere(struct string_list *rr, int fd)
t/t4200-rerere.sh
+1 -1
@@ -412,7 +412,7 @@ concat_insert () {
412 cat early && printf "%s\n" "$@" && cat late "$last"
413 }
414
415 -test_expect_failure 'multiple identical conflicts' '
415 +test_expect_success 'multiple identical conflicts' '
416 git reset --hard &&
417
418 test_seq 1 6 >early &&