Merge branch 'dt/notes-multiple'
authorJunio C Hamano <gitster@pobox.com>
Tue, 25 Aug 2015 21:57:08 +0000 (14:57 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 25 Aug 2015 21:57:08 +0000 (14:57 -0700)
When linked worktree is used, simultaneous "notes merge" instances
for the same ref in refs/notes/* are prevented from stomping on
each other.

* dt/notes-multiple:
notes: handle multiple worktrees
worktrees: add find_shared_symref

1  2 
branch.c
builtin/notes.c
diff --combined branch.c
index 3364adf1821b9a8b9bb0d81e8f1987f611eecdbc,483c52740f75b1b47a80df67f167d039c7ef9d1b..d013374e5a0b5714836be77709b07a5e3f67ed2a
+++ b/branch.c
@@@ -284,9 -284,8 +284,9 @@@ void create_branch(const char *head
  
                transaction = ref_transaction_begin(&err);
                if (!transaction ||
 -                  ref_transaction_update(transaction, ref.buf, sha1,
 -                                         null_sha1, 0, !forcing, msg, &err) ||
 +                  ref_transaction_update(transaction, ref.buf,
 +                                         sha1, forcing ? NULL : null_sha1,
 +                                         0, msg, &err) ||
                    ref_transaction_commit(transaction, &err))
                        die("%s", err.buf);
                ref_transaction_free(transaction);
  
  void remove_branch_state(void)
  {
 -      unlink(git_path("CHERRY_PICK_HEAD"));
 -      unlink(git_path("REVERT_HEAD"));
 -      unlink(git_path("MERGE_HEAD"));
 -      unlink(git_path("MERGE_RR"));
 -      unlink(git_path("MERGE_MSG"));
 -      unlink(git_path("MERGE_MODE"));
 -      unlink(git_path("SQUASH_MSG"));
 +      unlink(git_path_cherry_pick_head());
 +      unlink(git_path_revert_head());
 +      unlink(git_path_merge_head());
 +      unlink(git_path_merge_rr());
 +      unlink(git_path_merge_msg());
 +      unlink(git_path_merge_mode());
 +      unlink(git_path_squash_msg());
  }
  
- static void check_linked_checkout(const char *branch, const char *id)
+ static char *find_linked_symref(const char *symref, const char *branch,
+                               const char *id)
  {
        struct strbuf sb = STRBUF_INIT;
        struct strbuf path = STRBUF_INIT;
        struct strbuf gitdir = STRBUF_INIT;
+       char *existing = NULL;
  
        /*
-        * $GIT_COMMON_DIR/HEAD is practically outside
-        * $GIT_DIR so resolve_ref_unsafe() won't work (it
-        * uses git_path). Parse the ref ourselves.
+        * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside
+        * $GIT_DIR so resolve_ref_unsafe() won't work (it uses
+        * git_path). Parse the ref ourselves.
         */
        if (id)
-               strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
+               strbuf_addf(&path, "%s/worktrees/%s/%s", get_git_common_dir(), id, symref);
        else
-               strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
+               strbuf_addf(&path, "%s/%s", get_git_common_dir(), symref);
  
        if (!strbuf_readlink(&sb, path.buf, 0)) {
                if (!starts_with(sb.buf, "refs/") ||
                strbuf_rtrim(&gitdir);
        } else
                strbuf_addstr(&gitdir, get_git_common_dir());
-       skip_prefix(branch, "refs/heads/", &branch);
        strbuf_strip_suffix(&gitdir, ".git");
-       die(_("'%s' is already checked out at '%s'"), branch, gitdir.buf);
+       existing = strbuf_detach(&gitdir, NULL);
  done:
        strbuf_release(&path);
        strbuf_release(&sb);
        strbuf_release(&gitdir);
+       return existing;
  }
  
void die_if_checked_out(const char *branch)
char *find_shared_symref(const char *symref, const char *target)
  {
        struct strbuf path = STRBUF_INIT;
        DIR *dir;
        struct dirent *d;
+       char *existing;
  
-       check_linked_checkout(branch, NULL);
+       if ((existing = find_linked_symref(symref, target, NULL)))
+               return existing;
  
        strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
        dir = opendir(path.buf);
        strbuf_release(&path);
        if (!dir)
-               return;
+               return NULL;
  
        while ((d = readdir(dir)) != NULL) {
                if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
                        continue;
-               check_linked_checkout(branch, d->d_name);
+               existing = find_linked_symref(symref, target, d->d_name);
+               if (existing)
+                       goto done;
        }
+ done:
        closedir(dir);
+       return existing;
+ }
+ void die_if_checked_out(const char *branch)
+ {
+       char *existing;
+       existing = find_shared_symref("HEAD", branch);
+       if (existing) {
+               skip_prefix(branch, "refs/heads/", &branch);
+               die(_("'%s' is already checked out at '%s'"), branch, existing);
+       }
  }
diff --combined builtin/notes.c
index 63f95fc55439060670879fd987033000b8ba3401,b97bcb07ef05ac8efec77e57c17d552f85b9ac68..04234808270908ecc5a8b6e015749ceaa29bb081
  #include "string-list.h"
  #include "notes-merge.h"
  #include "notes-utils.h"
+ #include "branch.h"
  
  static const char * const git_notes_usage[] = {
 -      N_("git notes [--ref <notes_ref>] [list [<object>]]"),
 -      N_("git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
 -      N_("git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>"),
 -      N_("git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
 -      N_("git notes [--ref <notes_ref>] edit [<object>]"),
 -      N_("git notes [--ref <notes_ref>] show [<object>]"),
 -      N_("git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>"),
 +      N_("git notes [--ref <notes-ref>] [list [<object>]]"),
 +      N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
 +      N_("git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"),
 +      N_("git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
 +      N_("git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"),
 +      N_("git notes [--ref <notes-ref>] show [<object>]"),
 +      N_("git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"),
        N_("git notes merge --commit [-v | -q]"),
        N_("git notes merge --abort [-v | -q]"),
 -      N_("git notes [--ref <notes_ref>] remove [<object>...]"),
 -      N_("git notes [--ref <notes_ref>] prune [-n | -v]"),
 -      N_("git notes [--ref <notes_ref>] get-ref"),
 +      N_("git notes [--ref <notes-ref>] remove [<object>...]"),
 +      N_("git notes [--ref <notes-ref>] prune [-n | -v]"),
 +      N_("git notes [--ref <notes-ref>] get-ref"),
        NULL
  };
  
@@@ -68,7 -69,7 +69,7 @@@ static const char * const git_notes_sho
  };
  
  static const char * const git_notes_merge_usage[] = {
 -      N_("git notes merge [<options>] <notes_ref>"),
 +      N_("git notes merge [<options>] <notes-ref>"),
        N_("git notes merge --commit [<options>]"),
        N_("git notes merge --abort [<options>]"),
        NULL
@@@ -92,22 -93,12 +93,22 @@@ static const char * const git_notes_get
  static const char note_template[] =
        "\nWrite/edit the notes for the following object:\n";
  
 -struct msg_arg {
 +struct note_data {
        int given;
        int use_editor;
 +      char *edit_path;
        struct strbuf buf;
  };
  
 +static void free_note_data(struct note_data *d)
 +{
 +      if (d->edit_path) {
 +              unlink_or_warn(d->edit_path);
 +              free(d->edit_path);
 +      }
 +      strbuf_release(&d->buf);
 +}
 +
  static int list_each_note(const unsigned char *object_sha1,
                const unsigned char *note_sha1, char *note_path,
                void *cb_data)
        return 0;
  }
  
 -static void write_note_data(int fd, const unsigned char *sha1)
 +static void copy_obj_to_fd(int fd, const unsigned char *sha1)
  {
        unsigned long size;
        enum object_type type;
@@@ -159,23 -150,26 +160,23 @@@ static void write_commented_object(int 
                    sha1_to_hex(object));
  }
  
 -static void create_note(const unsigned char *object, struct msg_arg *msg,
 -                      int append_only, const unsigned char *prev,
 -                      unsigned char *result)
 +static void prepare_note_data(const unsigned char *object, struct note_data *d,
 +              const unsigned char *old_note)
  {
 -      char *path = NULL;
 -
 -      if (msg->use_editor || !msg->given) {
 +      if (d->use_editor || !d->given) {
                int fd;
                struct strbuf buf = STRBUF_INIT;
  
                /* write the template message before editing: */
 -              path = git_pathdup("NOTES_EDITMSG");
 -              fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 +              d->edit_path = git_pathdup("NOTES_EDITMSG");
 +              fd = open(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
                if (fd < 0)
 -                      die_errno(_("could not create file '%s'"), path);
 +                      die_errno(_("could not create file '%s'"), d->edit_path);
  
 -              if (msg->given)
 -                      write_or_die(fd, msg->buf.buf, msg->buf.len);
 -              else if (prev && !append_only)
 -                      write_note_data(fd, prev);
 +              if (d->given)
 +                      write_or_die(fd, d->buf.buf, d->buf.len);
 +              else if (old_note)
 +                      copy_obj_to_fd(fd, old_note);
  
                strbuf_addch(&buf, '\n');
                strbuf_add_commented_lines(&buf, note_template, strlen(note_template));
  
                close(fd);
                strbuf_release(&buf);
 -              strbuf_reset(&(msg->buf));
 -
 -              if (launch_editor(path, &(msg->buf), NULL)) {
 -                      die(_("Please supply the note contents using either -m" \
 -                          " or -F option"));
 -              }
 -              stripspace(&(msg->buf), 1);
 -      }
 -
 -      if (prev && append_only) {
 -              /* Append buf to previous note contents */
 -              unsigned long size;
 -              enum object_type type;
 -              char *prev_buf = read_sha1_file(prev, &type, &size);
 +              strbuf_reset(&d->buf);
  
 -              strbuf_grow(&(msg->buf), size + 1);
 -              if (msg->buf.len && prev_buf && size)
 -                      strbuf_insert(&(msg->buf), 0, "\n", 1);
 -              if (prev_buf && size)
 -                      strbuf_insert(&(msg->buf), 0, prev_buf, size);
 -              free(prev_buf);
 -      }
 -
 -      if (!msg->buf.len) {
 -              fprintf(stderr, _("Removing note for object %s\n"),
 -                      sha1_to_hex(object));
 -              hashclr(result);
 -      } else {
 -              if (write_sha1_file(msg->buf.buf, msg->buf.len, blob_type, result)) {
 -                      error(_("unable to write note object"));
 -                      if (path)
 -                              error(_("The note contents have been left in %s"),
 -                                    path);
 -                      exit(128);
 +              if (launch_editor(d->edit_path, &d->buf, NULL)) {
 +                      die(_("Please supply the note contents using either -m or -F option"));
                }
 +              stripspace(&d->buf, 1);
        }
 +}
  
 -      if (path) {
 -              unlink_or_warn(path);
 -              free(path);
 +static void write_note_data(struct note_data *d, unsigned char *sha1)
 +{
 +      if (write_sha1_file(d->buf.buf, d->buf.len, blob_type, sha1)) {
 +              error(_("unable to write note object"));
 +              if (d->edit_path)
 +                      error(_("The note contents have been left in %s"),
 +                              d->edit_path);
 +              exit(128);
        }
  }
  
  static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
  {
 -      struct msg_arg *msg = opt->value;
 +      struct note_data *d = opt->value;
  
 -      strbuf_grow(&(msg->buf), strlen(arg) + 2);
 -      if (msg->buf.len)
 -              strbuf_addch(&(msg->buf), '\n');
 -      strbuf_addstr(&(msg->buf), arg);
 -      stripspace(&(msg->buf), 0);
 +      strbuf_grow(&d->buf, strlen(arg) + 2);
 +      if (d->buf.len)
 +              strbuf_addch(&d->buf, '\n');
 +      strbuf_addstr(&d->buf, arg);
 +      stripspace(&d->buf, 0);
  
 -      msg->given = 1;
 +      d->given = 1;
        return 0;
  }
  
  static int parse_file_arg(const struct option *opt, const char *arg, int unset)
  {
 -      struct msg_arg *msg = opt->value;
 +      struct note_data *d = opt->value;
  
 -      if (msg->buf.len)
 -              strbuf_addch(&(msg->buf), '\n');
 +      if (d->buf.len)
 +              strbuf_addch(&d->buf, '\n');
        if (!strcmp(arg, "-")) {
 -              if (strbuf_read(&(msg->buf), 0, 1024) < 0)
 +              if (strbuf_read(&d->buf, 0, 1024) < 0)
                        die_errno(_("cannot read '%s'"), arg);
 -      } else if (strbuf_read_file(&(msg->buf), arg, 1024) < 0)
 +      } else if (strbuf_read_file(&d->buf, arg, 1024) < 0)
                die_errno(_("could not open or read '%s'"), arg);
 -      stripspace(&(msg->buf), 0);
 +      stripspace(&d->buf, 0);
  
 -      msg->given = 1;
 +      d->given = 1;
        return 0;
  }
  
  static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
  {
 -      struct msg_arg *msg = opt->value;
 +      struct note_data *d = opt->value;
        char *buf;
        unsigned char object[20];
        enum object_type type;
        unsigned long len;
  
 -      if (msg->buf.len)
 -              strbuf_addch(&(msg->buf), '\n');
 +      if (d->buf.len)
 +              strbuf_addch(&d->buf, '\n');
  
        if (get_sha1(arg, object))
                die(_("Failed to resolve '%s' as a valid ref."), arg);
 -      if (!(buf = read_sha1_file(object, &type, &len)) || !len) {
 +      if (!(buf = read_sha1_file(object, &type, &len))) {
                free(buf);
                die(_("Failed to read object '%s'."), arg);
        }
                free(buf);
                die(_("Cannot read note data from non-blob object '%s'."), arg);
        }
 -      strbuf_add(&(msg->buf), buf, len);
 +      strbuf_add(&d->buf, buf, len);
        free(buf);
  
 -      msg->given = 1;
 +      d->given = 1;
        return 0;
  }
  
  static int parse_reedit_arg(const struct option *opt, const char *arg, int unset)
  {
 -      struct msg_arg *msg = opt->value;
 -      msg->use_editor = 1;
 +      struct note_data *d = opt->value;
 +      d->use_editor = 1;
        return parse_reuse_arg(opt, arg, unset);
  }
  
@@@ -381,27 -398,26 +382,27 @@@ static int append_edit(int argc, const 
  
  static int add(int argc, const char **argv, const char *prefix)
  {
 -      int retval = 0, force = 0;
 +      int force = 0, allow_empty = 0;
        const char *object_ref;
        struct notes_tree *t;
        unsigned char object[20], new_note[20];
 -      char logmsg[100];
        const unsigned char *note;
 -      struct msg_arg msg = { 0, 0, STRBUF_INIT };
 +      struct note_data d = { 0, 0, NULL, STRBUF_INIT };
        struct option options[] = {
 -              { OPTION_CALLBACK, 'm', "message", &msg, N_("message"),
 +              { OPTION_CALLBACK, 'm', "message", &d, N_("message"),
                        N_("note contents as a string"), PARSE_OPT_NONEG,
                        parse_msg_arg},
 -              { OPTION_CALLBACK, 'F', "file", &msg, N_("file"),
 +              { OPTION_CALLBACK, 'F', "file", &d, N_("file"),
                        N_("note contents in a file"), PARSE_OPT_NONEG,
                        parse_file_arg},
 -              { OPTION_CALLBACK, 'c', "reedit-message", &msg, N_("object"),
 +              { OPTION_CALLBACK, 'c', "reedit-message", &d, N_("object"),
                        N_("reuse and edit specified note object"), PARSE_OPT_NONEG,
                        parse_reedit_arg},
 -              { OPTION_CALLBACK, 'C', "reuse-message", &msg, N_("object"),
 +              { OPTION_CALLBACK, 'C', "reuse-message", &d, N_("object"),
                        N_("reuse specified note object"), PARSE_OPT_NONEG,
                        parse_reuse_arg},
 +              OPT_BOOL(0, "allow-empty", &allow_empty,
 +                      N_("allow storing empty note")),
                OPT__FORCE(&force, N_("replace existing notes")),
                OPT_END()
        };
  
        if (note) {
                if (!force) {
 -                      if (!msg.given) {
 -                              /*
 -                               * Redirect to "edit" subcommand.
 -                               *
 -                               * We only end up here if none of -m/-F/-c/-C
 -                               * or -f are given. The original args are
 -                               * therefore still in argv[0-1].
 -                               */
 -                              argv[0] = "edit";
 -                              free_notes(t);
 -                              return append_edit(argc, argv, prefix);
 +                      free_notes(t);
 +                      if (d.given) {
 +                              free_note_data(&d);
 +                              return error(_("Cannot add notes. "
 +                                      "Found existing notes for object %s. "
 +                                      "Use '-f' to overwrite existing notes"),
 +                                      sha1_to_hex(object));
                        }
 -                      retval = error(_("Cannot add notes. Found existing notes "
 -                                     "for object %s. Use '-f' to overwrite "
 -                                     "existing notes"), sha1_to_hex(object));
 -                      goto out;
 +                      /*
 +                       * Redirect to "edit" subcommand.
 +                       *
 +                       * We only end up here if none of -m/-F/-c/-C or -f are
 +                       * given. The original args are therefore still in
 +                       * argv[0-1].
 +                       */
 +                      argv[0] = "edit";
 +                      return append_edit(argc, argv, prefix);
                }
                fprintf(stderr, _("Overwriting existing notes for object %s\n"),
                        sha1_to_hex(object));
        }
  
 -      create_note(object, &msg, 0, note, new_note);
 -
 -      if (is_null_sha1(new_note))
 +      prepare_note_data(object, &d, note);
 +      if (d.buf.len || allow_empty) {
 +              write_note_data(&d, new_note);
 +              if (add_note(t, object, new_note, combine_notes_overwrite))
 +                      die("BUG: combine_notes_overwrite failed");
 +              commit_notes(t, "Notes added by 'git notes add'");
 +      } else {
 +              fprintf(stderr, _("Removing note for object %s\n"),
 +                      sha1_to_hex(object));
                remove_note(t, object);
 -      else if (add_note(t, object, new_note, combine_notes_overwrite))
 -              die("BUG: combine_notes_overwrite failed");
 +              commit_notes(t, "Notes removed by 'git notes add'");
 +      }
  
 -      snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
 -               is_null_sha1(new_note) ? "removed" : "added", "add");
 -      commit_notes(t, logmsg);
 -out:
 +      free_note_data(&d);
        free_notes(t);
 -      strbuf_release(&(msg.buf));
 -      return retval;
 +      return 0;
  }
  
  static int copy(int argc, const char **argv, const char *prefix)
@@@ -542,29 -555,26 +543,29 @@@ out
  
  static int append_edit(int argc, const char **argv, const char *prefix)
  {
 +      int allow_empty = 0;
        const char *object_ref;
        struct notes_tree *t;
        unsigned char object[20], new_note[20];
        const unsigned char *note;
        char logmsg[100];
        const char * const *usage;
 -      struct msg_arg msg = { 0, 0, STRBUF_INIT };
 +      struct note_data d = { 0, 0, NULL, STRBUF_INIT };
        struct option options[] = {
 -              { OPTION_CALLBACK, 'm', "message", &msg, N_("message"),
 +              { OPTION_CALLBACK, 'm', "message", &d, N_("message"),
                        N_("note contents as a string"), PARSE_OPT_NONEG,
                        parse_msg_arg},
 -              { OPTION_CALLBACK, 'F', "file", &msg, N_("file"),
 +              { OPTION_CALLBACK, 'F', "file", &d, N_("file"),
                        N_("note contents in a file"), PARSE_OPT_NONEG,
                        parse_file_arg},
 -              { OPTION_CALLBACK, 'c', "reedit-message", &msg, N_("object"),
 +              { OPTION_CALLBACK, 'c', "reedit-message", &d, N_("object"),
                        N_("reuse and edit specified note object"), PARSE_OPT_NONEG,
                        parse_reedit_arg},
 -              { OPTION_CALLBACK, 'C', "reuse-message", &msg, N_("object"),
 +              { OPTION_CALLBACK, 'C', "reuse-message", &d, N_("object"),
                        N_("reuse specified note object"), PARSE_OPT_NONEG,
                        parse_reuse_arg},
 +              OPT_BOOL(0, "allow-empty", &allow_empty,
 +                      N_("allow storing empty note")),
                OPT_END()
        };
        int edit = !strcmp(argv[0], "edit");
                usage_with_options(usage, options);
        }
  
 -      if (msg.given && edit)
 +      if (d.given && edit)
                fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated "
                        "for the 'edit' subcommand.\n"
                        "Please use 'git notes add -f -m/-F/-c/-C' instead.\n"));
        t = init_notes_check(argv[0]);
        note = get_note(t, object);
  
 -      create_note(object, &msg, !edit, note, new_note);
 +      prepare_note_data(object, &d, edit ? note : NULL);
  
 -      if (is_null_sha1(new_note))
 -              remove_note(t, object);
 -      else if (add_note(t, object, new_note, combine_notes_overwrite))
 -              die("BUG: combine_notes_overwrite failed");
 +      if (note && !edit) {
 +              /* Append buf to previous note contents */
 +              unsigned long size;
 +              enum object_type type;
 +              char *prev_buf = read_sha1_file(note, &type, &size);
 +
 +              strbuf_grow(&d.buf, size + 1);
 +              if (d.buf.len && prev_buf && size)
 +                      strbuf_insert(&d.buf, 0, "\n", 1);
 +              if (prev_buf && size)
 +                      strbuf_insert(&d.buf, 0, prev_buf, size);
 +              free(prev_buf);
 +      }
  
 -      snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
 -               is_null_sha1(new_note) ? "removed" : "added", argv[0]);
 +      if (d.buf.len || allow_empty) {
 +              write_note_data(&d, new_note);
 +              if (add_note(t, object, new_note, combine_notes_overwrite))
 +                      die("BUG: combine_notes_overwrite failed");
 +              snprintf(logmsg, sizeof(logmsg), "Notes added by 'git notes %s'",
 +                      argv[0]);
 +      } else {
 +              fprintf(stderr, _("Removing note for object %s\n"),
 +                      sha1_to_hex(object));
 +              remove_note(t, object);
 +              snprintf(logmsg, sizeof(logmsg), "Notes removed by 'git notes %s'",
 +                      argv[0]);
 +      }
        commit_notes(t, logmsg);
 +
 +      free_note_data(&d);
        free_notes(t);
 -      strbuf_release(&(msg.buf));
        return 0;
  }
  
@@@ -825,10 -814,15 +826,15 @@@ static int merge(int argc, const char *
                update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
                           0, UPDATE_REFS_DIE_ON_ERR);
        else { /* Merge has unresolved conflicts */
+               char *existing;
                /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
                update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
                           0, UPDATE_REFS_DIE_ON_ERR);
                /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
+               existing = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
+               if (existing)
+                       die(_("A notes merge into %s is already in-progress at %s"),
+                           default_notes_ref(), existing);
                if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
                        die("Failed to store link to current notes ref (%s)",
                            default_notes_ref());
@@@ -951,7 -945,7 +957,7 @@@ int cmd_notes(int argc, const char **ar
        const char *override_notes_ref = NULL;
        struct option options[] = {
                OPT_STRING(0, "ref", &override_notes_ref, N_("notes-ref"),
 -                         N_("use notes from <notes_ref>")),
 +                         N_("use notes from <notes-ref>")),
                OPT_END()
        };