fast-import: fix handling of deleted tags

If our input stream includes a tag which is later deleted, we were not properly deleting it. We did have a step which would delete it, but we left a tag in the tag list noting that it needed to be updated, and the updating of annotated tags occurred AFTER ref deletion. So, when we record that a tag needs to be deleted, also remove it from the list of annotated tags to update. While this has likely been something that has not happened in practice, it will come up more in order to support nested tags. For nested tags, we either need to give temporary names to the intermediate tags and then delete them, or else we need to use the final name for the intermediate tags. If we use the final name for the intermediate tags, then in order to keep the sanity check that someone doesn't try to update the same tag twice, we need to delete the ref after creating the intermediate tag. So, either way nested tags imply the need to delete temporary inner tag references. Helped-by: René Scharfe <l.s.r@web.de> Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Oct 3, 2019 at 13:27 UTC 3164e6bd24ead3598dcf9374077f87167b796c93
2 files changed +40
fast-import.c
+27
@@ -2778,6 +2778,7 @@ static void parse_new_tag(const char *arg)
2778 static void parse_reset_branch(const char *arg)
2779 {
2780 struct branch *b;
2781 + const char *tag_name;
2782
2783 b = lookup_branch(arg);
2784 if (b) {
@@ -2793,6 +2794,32 @@ static void parse_reset_branch(const char *arg)
2794 b = new_branch(arg);
2795 read_next_command();
2796 parse_from(b);
2797 + if (b->delete && skip_prefix(b->name, "refs/tags/", &tag_name)) {
2798 + /*
2799 + * Elsewhere, we call dump_branches() before dump_tags(),
2800 + * and dump_branches() will handle ref deletions first, so
2801 + * in order to make sure the deletion actually takes effect,
2802 + * we need to remove the tag from our list of tags to update.
2803 + *
2804 + * NEEDSWORK: replace list of tags with hashmap for faster
2805 + * deletion?
2806 + */
2807 + struct tag *t, *prev = NULL;
2808 + for (t = first_tag; t; t = t->next_tag) {
2809 + if (!strcmp(t->name, tag_name))
2810 + break;
2811 + prev = t;
2812 + }
2813 + if (t) {
2814 + if (prev)
2815 + prev->next_tag = t->next_tag;
2816 + else
2817 + first_tag = t->next_tag;
2818 + if (!t->next_tag)
2819 + last_tag = prev;
2820 + /* There is no mem_pool_free(t) function to call. */
2821 + }
2822 + }
2823 if (command_buf.len > 0)
2824 unread_command_buf = 1;
2825 }
t/t9300-fast-import.sh
+13
@@ -85,6 +85,15 @@ test_expect_success 'A: create pack from stdin' '
85 An annotated tag that annotates a blob.
86 EOF
87
88 + tag to-be-deleted
89 + from :3
90 + data <<EOF
91 + Another annotated tag that annotates a blob.
92 + EOF
93 +
94 + reset refs/tags/to-be-deleted
95 + from 0000000000000000000000000000000000000000
96 +
97 INPUT_END
98 git fast-import --export-marks=marks.out <input &&
99 git whatchanged master
@@ -157,6 +166,10 @@ test_expect_success 'A: verify tag/series-A-blob' '
166 test_cmp expect actual
167 '
168
169 +test_expect_success 'A: verify tag deletion is successful' '
170 + test_must_fail git rev-parse --verify refs/tags/to-be-deleted
171 +'
172 +
173 test_expect_success 'A: verify marks output' '
174 cat >expect <<-EOF &&
175 :2 $(git rev-parse --verify master:file2)