tag: keep the message file in case ref transaction fails

The ref transaction can fail after the user has written their tag message. In particular, if there exists a tag `foo/bar` and `git tag -a foo` is said then the command will only fail once it tries to write `refs/tags/foo`, which is after the file has been unlinked. Hold on to the message file for a little longer so that it is not unlinked before the fatal error occurs. Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Kristoffer Haugsbakk committed May 16, 2023 at 19:55 UTC 08c12ec1d0444c9ea7cf24b7157aa158f3f641f4
2 files changed +25 -9
builtin/tag.c
+15 -9
@@ -271,11 +271,10 @@ static const char message_advice_nested_tag[] =
271 static void create_tag(const struct object_id *object, const char *object_ref,
272 const char *tag,
273 struct strbuf *buf, struct create_tag_options *opt,
274 - struct object_id *prev, struct object_id *result)
274 + struct object_id *prev, struct object_id *result, char *path)
275 {
276 enum object_type type;
277 struct strbuf header = STRBUF_INIT;
278 - char *path = NULL;
278
279 type = oid_object_info(the_repository, object, NULL);
280 if (type <= OBJ_NONE)
@@ -299,7 +298,6 @@ static void create_tag(const struct object_id *object, const char *object_ref,
298 int fd;
299
300 /* write the template message before editing: */
302 - path = git_pathdup("TAG_EDITMSG");
301 fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
302
303 if (opt->message_given) {
@@ -341,10 +339,6 @@ static void create_tag(const struct object_id *object, const char *object_ref,
339 path);
340 exit(128);
341 }
344 - if (path) {
345 - unlink_or_warn(path);
346 - free(path);
347 - }
342 }
343
344 static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
@@ -495,6 +489,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
489 };
490 int ret = 0;
491 const char *only_in_list = NULL;
492 + char *path = NULL;
493
494 setup_ref_filter_porcelain_msg();
495
@@ -629,7 +624,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
624 if (create_tag_object) {
625 if (force_sign_annotate && !annotate)
626 opt.sign = 1;
632 - create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object);
627 + path = git_pathdup("TAG_EDITMSG");
628 + create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
629 + path);
630 }
631
632 transaction = ref_transaction_begin(&err);
@@ -637,8 +634,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
634 ref_transaction_update(transaction, ref.buf, &object, &prev,
635 create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
636 reflog_msg.buf, &err) ||
640 - ref_transaction_commit(transaction, &err))
637 + ref_transaction_commit(transaction, &err)) {
638 + if (path)
639 + fprintf(stderr,
640 + _("The tag message has been left in %s\n"),
641 + path);
642 die("%s", err.buf);
643 + }
644 + if (path) {
645 + unlink_or_warn(path);
646 + free(path);
647 + }
648 ref_transaction_free(transaction);
649 if (force && !is_null_oid(&prev) && !oideq(&prev, &object))
650 printf(_("Updated tag '%s' (was %s)\n"), tag,
t/t7004-tag.sh
+10
@@ -2197,4 +2197,14 @@ test_expect_success 'If tag is created then tag message file is unlinked' '
2197 test_path_is_missing .git/TAG_EDITMSG
2198 '
2199
2200 +test_expect_success 'If tag cannot be created then tag message file is not unlinked' '
2201 + test_when_finished "git tag -d foo/bar && rm .git/TAG_EDITMSG" &&
2202 + write_script fakeeditor <<-\EOF &&
2203 + echo Message >.git/TAG_EDITMSG
2204 + EOF
2205 + git tag foo/bar &&
2206 + test_must_fail env GIT_EDITOR=./fakeeditor git tag -a foo &&
2207 + test_path_exists .git/TAG_EDITMSG
2208 +'
2209 +
2210 test_done