From: Junio C Hamano Date: Tue, 25 Aug 2015 21:57:08 +0000 (-0700) Subject: Merge branch 'dt/notes-multiple' X-Git-Tag: v2.6.0-rc0~49 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/32561f5dd39da3ac720f6778bc2e8aafed771eb5?ds=inline;hp=-c Merge branch 'dt/notes-multiple' 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 --- 32561f5dd39da3ac720f6778bc2e8aafed771eb5 diff --combined branch.c index 3364adf182,483c52740f..d013374e5a --- a/branch.c +++ 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); @@@ -302,30 -301,32 +302,32 @@@ 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/") || @@@ -347,33 -348,53 +349,53 @@@ 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 63f95fc554,b97bcb07ef..0423480827 --- a/builtin/notes.c +++ b/builtin/notes.c @@@ -19,20 -19,21 +19,21 @@@ #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 ] [list []]"), - N_("git notes [--ref ] add [-f] [-m | -F | (-c | -C) ] []"), - N_("git notes [--ref ] copy [-f] "), - N_("git notes [--ref ] append [-m | -F | (-c | -C) ] []"), - N_("git notes [--ref ] edit []"), - N_("git notes [--ref ] show []"), - N_("git notes [--ref ] merge [-v | -q] [-s ] "), + N_("git notes [--ref ] [list []]"), + N_("git notes [--ref ] add [-f] [--allow-empty] [-m | -F | (-c | -C) ] []"), + N_("git notes [--ref ] copy [-f] "), + N_("git notes [--ref ] append [--allow-empty] [-m | -F | (-c | -C) ] []"), + N_("git notes [--ref ] edit [--allow-empty] []"), + N_("git notes [--ref ] show []"), + N_("git notes [--ref ] merge [-v | -q] [-s ] "), N_("git notes merge --commit [-v | -q]"), N_("git notes merge --abort [-v | -q]"), - N_("git notes [--ref ] remove [...]"), - N_("git notes [--ref ] prune [-n | -v]"), - N_("git notes [--ref ] get-ref"), + N_("git notes [--ref ] remove [...]"), + N_("git notes [--ref ] prune [-n | -v]"), + N_("git 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 [] "), + N_("git notes merge [] "), N_("git notes merge --commit []"), N_("git notes merge --abort []"), 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) @@@ -116,7 -107,7 +117,7 @@@ 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)); @@@ -186,71 -180,94 +187,71 @@@ 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); } @@@ -258,17 -275,17 +259,17 @@@ 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() }; @@@ -424,44 -440,41 +425,44 @@@ 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"); @@@ -578,7 -588,7 +579,7 @@@ 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")); @@@ -591,39 -601,18 +592,39 @@@ 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 ")), + N_("use notes from ")), OPT_END() };