From a9f2adff802308481f2e638bae0c5b6e205251a3 Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Fri, 14 May 2010 23:42:07 +0200 Subject: notes: dry-run and verbose options for prune Introduce -n and -v options for "git notes prune" in complete analogy to "git prune" so that one can check for dangling notes easily. The output is a list of names of objects whose notes would be resp. are removed so that one can check the object ("git show sha1") as well as the note ("git notes show sha1"). Signed-off-by: Michael J Gruber Acked-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 52b72fca68..ba8fd178c8 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -26,7 +26,7 @@ static const char * const git_notes_usage[] = { "git notes [--ref ] edit []", "git notes [--ref ] show []", "git notes [--ref ] remove []", - "git notes [--ref ] prune", + "git notes [--ref ] prune [-n | -v]", NULL }; @@ -67,7 +67,7 @@ static const char * const git_notes_remove_usage[] = { }; static const char * const git_notes_prune_usage[] = { - "git notes prune", + "git notes prune []", NULL }; @@ -792,7 +792,10 @@ static int remove_cmd(int argc, const char **argv, const char *prefix) static int prune(int argc, const char **argv, const char *prefix) { struct notes_tree *t; + int show_only = 0, verbose = 0; struct option options[] = { + OPT_BOOLEAN('n', NULL, &show_only, "do not remove, show only"), + OPT_BOOLEAN('v', NULL, &verbose, "report pruned notes"), OPT_END() }; @@ -806,8 +809,10 @@ static int prune(int argc, const char **argv, const char *prefix) t = init_notes_check("prune"); - prune_notes(t); - commit_notes(t, "Notes removed by 'git notes prune'"); + prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) | + (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) ); + if (!show_only) + commit_notes(t, "Notes removed by 'git notes prune'"); free_notes(t); return 0; } -- cgit v1.2.3 From ef7a8e3b9575a6dc60e2d9e35008914a9eaabfff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 14 Jun 2010 23:40:05 +0000 Subject: notes: Initialize variable to appease Sun Studio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sun Studio 12 Update 1 thinks that *t could be uninitialized, ostensibly because it doesn't take rewrite_cmd into account in its static analysis. builtin/notes.c: In function `notes_copy_from_stdin': builtin/notes.c:419: warning: 't' might be used uninitialized in this function Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/notes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 52b72fca68..26617546c8 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -416,7 +416,7 @@ int notes_copy_from_stdin(int force, const char *rewrite_cmd) { struct strbuf buf = STRBUF_INIT; struct notes_rewrite_cfg *c = NULL; - struct notes_tree *t; + struct notes_tree *t = NULL; int ret = 0; if (rewrite_cmd) { -- cgit v1.2.3 From 4e0d7a80186c27f1a008dd10eaea6f25b0e1fe19 Mon Sep 17 00:00:00 2001 From: Ramsay Jones Date: Wed, 23 Jun 2010 20:40:19 +0100 Subject: msvc: Fix some "expr evaluates to function" compiler warnings In particular, the following warning is issued while compiling notes.c: notes.c(927) : warning C4550: expression evaluates to a \ function which is missing an argument list along with identical warnings on lines 928, 1016 and 1017. In order to suppress the warning, we change the definition of combine_notes_fn, so that the symbol type is an (explicit) "pointer to function ...". As a result, several other declarations need some minor fix-up to take account of the new typedef. Signed-off-by: Ramsay Jones Acked-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 26617546c8..da504ee0af 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -313,7 +313,7 @@ int commit_notes(struct notes_tree *t, const char *msg) return 0; } -combine_notes_fn *parse_combine_notes_fn(const char *v) +combine_notes_fn parse_combine_notes_fn(const char *v) { if (!strcasecmp(v, "overwrite")) return combine_notes_overwrite; -- cgit v1.2.3 From bbb1b8a35a06203020f7ab6d1ad58bcb4afe8e93 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 28 Jun 2010 04:59:07 -0400 Subject: notes: check number of parameters to "git notes copy" Otherwise we may segfault with too few parameters. Signed-off-by: Jeff King Tested-by: Bert Wesarg Signed-off-by: Junio C Hamano --- builtin/notes.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index da504ee0af..f678f9cb52 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -614,6 +614,10 @@ static int copy(int argc, const char **argv, const char *prefix) } } + if (argc < 2) { + error("too few parameters"); + usage_with_options(git_notes_copy_usage, options); + } if (2 < argc) { error("too many parameters"); usage_with_options(git_notes_copy_usage, options); -- cgit v1.2.3 From e93487d2f007176804e89042f9f199628e45c864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 6 Aug 2010 22:28:09 +0200 Subject: notes: allow --dry-run for -n and --verbose for -v For consistency with other git commands, let the prune subcommand of git notes accept the long options --dry-run and --verbose for the respective short ones -n and -v. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- builtin/notes.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 190005f3cd..fbc347c9f0 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -798,8 +798,9 @@ static int prune(int argc, const char **argv, const char *prefix) struct notes_tree *t; int show_only = 0, verbose = 0; struct option options[] = { - OPT_BOOLEAN('n', NULL, &show_only, "do not remove, show only"), - OPT_BOOLEAN('v', NULL, &verbose, "report pruned notes"), + OPT_BOOLEAN('n', "dry-run", &show_only, + "do not remove, show only"), + OPT_BOOLEAN('v', "verbose", &verbose, "report pruned notes"), OPT_END() }; -- cgit v1.2.3 From 1ee1e43df37e53b0bc50a0eda57dd1772dc220f5 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 31 Aug 2010 17:56:50 +0200 Subject: notes: Don't create (empty) commit when removing non-existing notes Extend remove_note() in the notes API to return whether or not a note was actually removed. Use this in 'git notes remove' to skip the creation of a notes commit when no notes were actually removed. Also add a test illustrating the change in behavior. Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index fbc347c9f0..6d07aac80c 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -769,6 +769,7 @@ static int remove_cmd(int argc, const char **argv, const char *prefix) const char *object_ref; struct notes_tree *t; unsigned char object[20]; + int retval; argc = parse_options(argc, argv, prefix, options, git_notes_remove_usage, 0); @@ -785,12 +786,17 @@ static int remove_cmd(int argc, const char **argv, const char *prefix) t = init_notes_check("remove"); - fprintf(stderr, "Removing note for object %s\n", sha1_to_hex(object)); - remove_note(t, object); + retval = remove_note(t, object); + if (retval) + fprintf(stderr, "Object %s has no note\n", sha1_to_hex(object)); + else { + fprintf(stderr, "Removing note for object %s\n", + sha1_to_hex(object)); - commit_notes(t, "Notes removed by 'git notes remove'"); + commit_notes(t, "Notes removed by 'git notes remove'"); + } free_notes(t); - return 0; + return retval; } static int prune(int argc, const char **argv, const char *prefix) -- cgit v1.2.3 From fd03881a48fc31197ab3343ac2f02ed0cdbbbaaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 8 Nov 2010 18:56:39 +0100 Subject: add description parameter to OPT__VERBOSE Allows better help text to be defined than "be verbose". Also make use of the macro in places that already had a different description. No object code changes intended. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- builtin/notes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 6d07aac80c..e0a1f62fc9 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -806,7 +806,7 @@ static int prune(int argc, const char **argv, const char *prefix) struct option options[] = { OPT_BOOLEAN('n', "dry-run", &show_only, "do not remove, show only"), - OPT_BOOLEAN('v', "verbose", &verbose, "report pruned notes"), + OPT__VERBOSE(&verbose, "report pruned notes"), OPT_END() }; -- cgit v1.2.3 From e21adb8c100cd2859cff1d74f7d0e622ac640085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 8 Nov 2010 18:58:51 +0100 Subject: add description parameter to OPT__DRY_RUN Allows better help text to be defined than "dry run". Also make use of the macro in places that already had a different description. No object code changes intended. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- builtin/notes.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index e0a1f62fc9..2ab070fa09 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -804,8 +804,7 @@ static int prune(int argc, const char **argv, const char *prefix) struct notes_tree *t; int show_only = 0, verbose = 0; struct option options[] = { - OPT_BOOLEAN('n', "dry-run", &show_only, - "do not remove, show only"), + OPT__DRY_RUN(&show_only, "do not remove, show only"), OPT__VERBOSE(&verbose, "report pruned notes"), OPT_END() }; -- cgit v1.2.3 From 76946b76fe3d26507d61cae97aa0676fad24ed92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 8 Nov 2010 19:01:54 +0100 Subject: add OPT__FORCE Add OPT__FORCE as a helper macro in the same spirit as OPT__VERBOSE et.al. to simplify defining -f/--force options. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- builtin/notes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 2ab070fa09..c85cbf5a47 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -538,7 +538,7 @@ static int add(int argc, const char **argv, const char *prefix) { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT", "reuse specified note object", PARSE_OPT_NONEG, parse_reuse_arg}, - OPT_BOOLEAN('f', "force", &force, "replace existing notes"), + OPT__FORCE(&force, "replace existing notes"), OPT_END() }; @@ -594,7 +594,7 @@ static int copy(int argc, const char **argv, const char *prefix) struct notes_tree *t; const char *rewrite_cmd = NULL; struct option options[] = { - OPT_BOOLEAN('f', "force", &force, "replace existing notes"), + OPT__FORCE(&force, "replace existing notes"), OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"), OPT_STRING(0, "for-rewrite", &rewrite_cmd, "command", "load rewriting config for (implies " -- cgit v1.2.3 From 180619a58572b17de0ebeb96989e0aa832765186 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Mon, 15 Nov 2010 00:52:26 +0100 Subject: notes.h/c: Propagate combine_notes_fn return value to add_note() and beyond The combine_notes_fn functions uses a non-zero return value to indicate failure. However, this return value was converted to a call to die() in note_tree_insert(). Instead, propagate this return value out to add_note(), and return it from there to enable the caller to handle errors appropriately. Existing add_note() callers are updated to die() upon failure, thus preserving the current behaviour. The only exceptions are copy_note() and notes_cache_put() where we are able to propagate the add_note() return value instead. This patch has been improved by the following contributions: - Jonathan Nieder: Future-proof by always checking add_note() return value - Jonathan Nieder: Improve clarity of final if-condition in note_tree_insert() Thanks-to: Jonathan Nieder Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index fbc347c9f0..51a11ba388 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -573,8 +573,8 @@ static int add(int argc, const char **argv, const char *prefix) if (is_null_sha1(new_note)) remove_note(t, object); - else - add_note(t, object, new_note, combine_notes_overwrite); + else if (add_note(t, object, new_note, combine_notes_overwrite)) + die("BUG: combine_notes_overwrite failed"); snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'", is_null_sha1(new_note) ? "removed" : "added", "add"); @@ -653,7 +653,8 @@ static int copy(int argc, const char **argv, const char *prefix) goto out; } - add_note(t, object, from_note, combine_notes_overwrite); + if (add_note(t, object, from_note, combine_notes_overwrite)) + die("BUG: combine_notes_overwrite failed"); commit_notes(t, "Notes added by 'git notes copy'"); out: free_notes(t); @@ -712,8 +713,8 @@ static int append_edit(int argc, const char **argv, const char *prefix) if (is_null_sha1(new_note)) remove_note(t, object); - else - add_note(t, object, new_note, combine_notes_overwrite); + else if (add_note(t, object, new_note, combine_notes_overwrite)) + die("BUG: combine_notes_overwrite failed"); snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'", is_null_sha1(new_note) ? "removed" : "added", argv[0]); -- cgit v1.2.3 From 8ef313e1ec3b9b8ca47dce1fec632597aa34bedc Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 9 Nov 2010 22:49:45 +0100 Subject: builtin/notes.c: Split notes ref DWIMmery into a separate function expand_notes_ref() is a new function that performs the DWIM transformation of "foo" -> "refs/notes/foo" where notes refs are expected. This is done in preparation for future patches which will also need this DWIM functionality. Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 51a11ba388..f35cf9bd4b 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -83,6 +83,16 @@ struct msg_arg { struct strbuf buf; }; +static void expand_notes_ref(struct strbuf *sb) +{ + if (!prefixcmp(sb->buf, "refs/notes/")) + return; /* we're happy */ + else if (!prefixcmp(sb->buf, "notes/")) + strbuf_insert(sb, 0, "refs/", 5); + else + strbuf_insert(sb, 0, "refs/notes/", 11); +} + static int list_each_note(const unsigned char *object_sha1, const unsigned char *note_sha1, char *note_path, void *cb_data) @@ -839,13 +849,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix) if (override_notes_ref) { struct strbuf sb = STRBUF_INIT; - if (!prefixcmp(override_notes_ref, "refs/notes/")) - /* we're happy */; - else if (!prefixcmp(override_notes_ref, "notes/")) - strbuf_addstr(&sb, "refs/"); - else - strbuf_addstr(&sb, "refs/notes/"); strbuf_addstr(&sb, override_notes_ref); + expand_notes_ref(&sb); setenv("GIT_NOTES_REF", sb.buf, 1); strbuf_release(&sb); } -- cgit v1.2.3 From 75ef3f4a5cc69b21bc825ed0e739030d77a4f077 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 9 Nov 2010 22:49:46 +0100 Subject: git notes merge: Initial implementation handling trivial merges only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This initial implementation of 'git notes merge' only handles the trivial merge cases (i.e. where the merge is either a no-op, or a fast-forward). The patch includes testcases for these trivial merge cases. Future patches will extend the functionality of 'git notes merge'. This patch has been improved by the following contributions: - Stephen Boyd: Simplify argc logic - Stephen Boyd: Use test_commit - Ævar Arnfjörð Bjarmason: Don't use C99 comments. - Jonathan Nieder: Add constants for common verbosity values - Jonathan Nieder: Use trace_printf(...) instead of OUTPUT(o, 5, ...) - Jonathan Nieder: Remove extraneous show() function - Jonathan Nieder: Clarify handling of empty/missing notes ref in notes_merge() - Junio C Hamano: fixup minor style issues Thanks-to: Stephen Boyd Thanks-to: Ævar Arnfjörð Bjarmason Thanks-to: Jonathan Nieder Thanks-to: Junio C Hamano Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index f35cf9bd4b..bc8b7e1e08 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -17,6 +17,7 @@ #include "run-command.h" #include "parse-options.h" #include "string-list.h" +#include "notes-merge.h" static const char * const git_notes_usage[] = { "git notes [--ref ] [list []]", @@ -25,6 +26,7 @@ static const char * const git_notes_usage[] = { "git notes [--ref ] append [-m | -F | (-c | -C) ] []", "git notes [--ref ] edit []", "git notes [--ref ] show []", + "git notes [--ref ] merge [-v | -q] ", "git notes [--ref ] remove []", "git notes [--ref ] prune [-n | -v]", NULL @@ -61,6 +63,11 @@ static const char * const git_notes_show_usage[] = { NULL }; +static const char * const git_notes_merge_usage[] = { + "git notes merge [] ", + NULL +}; + static const char * const git_notes_remove_usage[] = { "git notes remove []", NULL @@ -772,6 +779,51 @@ static int show(int argc, const char **argv, const char *prefix) return retval; } +static int merge(int argc, const char **argv, const char *prefix) +{ + struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT; + unsigned char result_sha1[20]; + struct notes_merge_options o; + int verbosity = 0, result; + struct option options[] = { + OPT__VERBOSITY(&verbosity), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, + git_notes_merge_usage, 0); + + if (argc != 1) { + error("Must specify a notes ref to merge"); + usage_with_options(git_notes_merge_usage, options); + } + + init_notes_merge_options(&o); + o.verbosity = verbosity + NOTES_MERGE_VERBOSITY_DEFAULT; + + o.local_ref = default_notes_ref(); + strbuf_addstr(&remote_ref, argv[0]); + expand_notes_ref(&remote_ref); + o.remote_ref = remote_ref.buf; + + result = notes_merge(&o, result_sha1); + + strbuf_addf(&msg, "notes: Merged notes from %s into %s", + remote_ref.buf, default_notes_ref()); + if (result == 0) { /* Merge resulted (trivially) in result_sha1 */ + /* Update default notes ref with new commit */ + update_ref(msg.buf, default_notes_ref(), result_sha1, NULL, + 0, DIE_ON_ERR); + } else { + /* TODO: */ + die("'git notes merge' cannot yet handle non-trivial merges!"); + } + + strbuf_release(&remote_ref); + strbuf_release(&msg); + return 0; +} + static int remove_cmd(int argc, const char **argv, const char *prefix) { struct option options[] = { @@ -865,6 +917,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix) result = append_edit(argc, argv, prefix); else if (!strcmp(argv[0], "show")) result = show(argc, argv, prefix); + else if (!strcmp(argv[0], "merge")) + result = merge(argc, argv, prefix); else if (!strcmp(argv[0], "remove")) result = remove_cmd(argc, argv, prefix); else if (!strcmp(argv[0], "prune")) -- cgit v1.2.3 From 56881843d4d916a166ac4c6ba1803e5ceba9c44d Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 9 Nov 2010 22:49:47 +0100 Subject: builtin/notes.c: Refactor creation of notes commits. Create new function create_notes_commit() which is slightly more general than commit_notes() (accepts multiple commit parents and does not auto-update the notes ref). This function will be used by the notes-merge functionality in future patches. Also rewrite builtin/notes.c:commit_notes() to reuse this new function. Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index bc8b7e1e08..32d8a24944 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -288,18 +288,17 @@ static int parse_reedit_arg(const struct option *opt, const char *arg, int unset return parse_reuse_arg(opt, arg, unset); } -int commit_notes(struct notes_tree *t, const char *msg) +void commit_notes(struct notes_tree *t, const char *msg) { - struct commit_list *parent; - unsigned char tree_sha1[20], prev_commit[20], new_commit[20]; struct strbuf buf = STRBUF_INIT; + unsigned char commit_sha1[20]; if (!t) t = &default_notes_tree; if (!t->initialized || !t->ref || !*t->ref) die("Cannot commit uninitialized/unreferenced notes tree"); if (!t->dirty) - return 0; /* don't have to commit an unchanged tree */ + return; /* don't have to commit an unchanged tree */ /* Prepare commit message and reflog message */ strbuf_addstr(&buf, "notes: "); /* commit message starts at index 7 */ @@ -307,27 +306,10 @@ int commit_notes(struct notes_tree *t, const char *msg) if (buf.buf[buf.len - 1] != '\n') strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */ - /* Convert notes tree to tree object */ - if (write_notes_tree(t, tree_sha1)) - die("Failed to write current notes tree to database"); - - /* Create new commit for the tree object */ - if (!read_ref(t->ref, prev_commit)) { /* retrieve parent commit */ - parent = xmalloc(sizeof(*parent)); - parent->item = lookup_commit(prev_commit); - parent->next = NULL; - } else { - hashclr(prev_commit); - parent = NULL; - } - if (commit_tree(buf.buf + 7, tree_sha1, parent, new_commit, NULL)) - die("Failed to commit notes tree to database"); - - /* Update notes ref with new commit */ - update_ref(buf.buf, t->ref, new_commit, prev_commit, 0, DIE_ON_ERR); + create_notes_commit(t, NULL, buf.buf + 7, commit_sha1); + update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR); strbuf_release(&buf); - return 0; } combine_notes_fn parse_combine_notes_fn(const char *v) -- cgit v1.2.3 From 2085b16aefdbb1dc081aa40c62eb8110a222731d Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Mon, 15 Nov 2010 00:54:11 +0100 Subject: git notes merge: Handle real, non-conflicting notes merges This continuation of the 'git notes merge' implementation teaches notes-merge to properly do real merges between notes trees: Two diffs are performed, one from $base to $remote, and another from $base to $local. The paths in each diff are normalized to SHA1 object names. The two diffs are then consolidated into a single list of change pairs to be evaluated. Each change pair consist of: - The annotated object's SHA1 - The $base SHA1 (i.e. the common ancestor notes for this object) - The $local SHA1 (i.e. the current notes for this object) - The $remote SHA1 (i.e. the to-be-merged notes for this object) From the pair ($base -> $local, $base -> $remote), we can determine the merge result using regular 3-way rules. If conflicts are encountered in this process, we fail loudly and exit (conflict handling to be added in a future patch), If we can complete the merge without conflicts, the resulting notes tree is committed, and the current notes ref updated. The patch includes added testcases verifying that we can successfully do real conflict-less merges. This patch has been improved by the following contributions: - Jonathan Nieder: Future-proof by always checking add_note() return value - Stephen Boyd: Use test_commit - Jonathan Nieder: Use trace_printf(...) instead of OUTPUT(o, 5, ...) - Junio C Hamano: fixup minor style issues Thanks-to: Jonathan Nieder Thanks-to: Stephen Boyd Thanks-to: Junio C Hamano Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 32d8a24944..c7761b1140 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -765,6 +765,7 @@ static int merge(int argc, const char **argv, const char *prefix) { struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT; unsigned char result_sha1[20]; + struct notes_tree *t; struct notes_merge_options o; int verbosity = 0, result; struct option options[] = { @@ -788,19 +789,23 @@ static int merge(int argc, const char **argv, const char *prefix) expand_notes_ref(&remote_ref); o.remote_ref = remote_ref.buf; - result = notes_merge(&o, result_sha1); + t = init_notes_check("merge"); strbuf_addf(&msg, "notes: Merged notes from %s into %s", remote_ref.buf, default_notes_ref()); - if (result == 0) { /* Merge resulted (trivially) in result_sha1 */ + o.commit_msg = msg.buf + 7; // skip "notes: " prefix + + result = notes_merge(&o, t, result_sha1); + + if (result >= 0) /* Merge resulted (trivially) in result_sha1 */ /* Update default notes ref with new commit */ update_ref(msg.buf, default_notes_ref(), result_sha1, NULL, 0, DIE_ON_ERR); - } else { + else /* TODO: */ - die("'git notes merge' cannot yet handle non-trivial merges!"); - } + die("'git notes merge' cannot yet handle conflicts!"); + free_notes(t); strbuf_release(&remote_ref); strbuf_release(&msg); return 0; -- cgit v1.2.3 From 3228e67120a8c71bf7804a5c52448a841d6f3b58 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Mon, 15 Nov 2010 00:55:12 +0100 Subject: git notes merge: Add automatic conflict resolvers (ours, theirs, union) The new -s/--strategy command-line option to 'git notes merge' allow the user to choose how notes merge conflicts should be resolved. There are four valid strategies to choose from: 1. "manual" (the default): This will let the user manually resolve conflicts. This option currently fails with an error message. It will be implemented properly in future patches. 2. "ours": This automatically chooses the local version of a conflict, and discards the remote version. 3. "theirs": This automatically chooses the remote version of a conflict, and discards the local version. 4. "union": This automatically resolves the conflict by appending the remote version to the local version. The strategies are implemented using the combine_notes_* functions from the notes.h API. The patch also includes testcases verifying the correct implementation of these strategies. This patch has been improved by the following contributions: - Jonathan Nieder: Future-proof by always checking add_note() return value - Stephen Boyd: Use test_commit - Stephen Boyd: Use correct option name Thanks-to: Jonathan Nieder Thanks-to: Stephen Boyd Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index c7761b1140..64b2be97ef 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -26,7 +26,7 @@ static const char * const git_notes_usage[] = { "git notes [--ref ] append [-m | -F | (-c | -C) ] []", "git notes [--ref ] edit []", "git notes [--ref ] show []", - "git notes [--ref ] merge [-v | -q] ", + "git notes [--ref ] merge [-v | -q] [-s ] ", "git notes [--ref ] remove []", "git notes [--ref ] prune [-n | -v]", NULL @@ -768,8 +768,12 @@ static int merge(int argc, const char **argv, const char *prefix) struct notes_tree *t; struct notes_merge_options o; int verbosity = 0, result; + const char *strategy = NULL; struct option options[] = { OPT__VERBOSITY(&verbosity), + OPT_STRING('s', "strategy", &strategy, "strategy", + "resolve notes conflicts using the given " + "strategy (manual/ours/theirs/union)"), OPT_END() }; @@ -789,6 +793,21 @@ static int merge(int argc, const char **argv, const char *prefix) expand_notes_ref(&remote_ref); o.remote_ref = remote_ref.buf; + if (strategy) { + if (!strcmp(strategy, "manual")) + o.strategy = NOTES_MERGE_RESOLVE_MANUAL; + else if (!strcmp(strategy, "ours")) + o.strategy = NOTES_MERGE_RESOLVE_OURS; + else if (!strcmp(strategy, "theirs")) + o.strategy = NOTES_MERGE_RESOLVE_THEIRS; + else if (!strcmp(strategy, "union")) + o.strategy = NOTES_MERGE_RESOLVE_UNION; + else { + error("Unknown -s/--strategy: %s", strategy); + usage_with_options(git_notes_merge_usage, options); + } + } + t = init_notes_check("merge"); strbuf_addf(&msg, "notes: Merged notes from %s into %s", -- cgit v1.2.3 From 809f38c8abacdbeb7015fdeef03931568c7fddda Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 9 Nov 2010 22:49:51 +0100 Subject: git notes merge: Manual conflict resolution, part 1/2 Conflicts (that are to be resolved manually) are written into a special- purpose working tree, located at .git/NOTES_MERGE_WORKTREE. Within this directory, conflicting notes entries are stored (with conflict markers produced by ll_merge()) using the SHA1 of the annotated object. The .git/NOTES_MERGE_WORKTREE directory will only contain the _conflicting_ note entries. The non-conflicting note entries (aka. the partial merge result) are stored in 'local_tree', and the SHA1 of the resulting commit is written to 'result_sha1'. The return value from notes_merge() is -1. The user is told to edit the files within the .git/NOTES_MERGE_WORKTREE directory in order to resolve the conflicts. The patch also contains documentation and testcases for the correct setup of .git/NOTES_MERGE_WORKTREE. The next part will recombine the partial notes merge result with the resolved conflicts in .git/NOTES_MERGE_WORKTREE to produce the complete merge result. This patch has been improved by the following contributions: - Jonathan Nieder: Use trace_printf(...) instead of OUTPUT(o, 5, ...) Thanks-to: Jonathan Nieder Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 64b2be97ef..309edc741e 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -820,14 +820,14 @@ static int merge(int argc, const char **argv, const char *prefix) /* Update default notes ref with new commit */ update_ref(msg.buf, default_notes_ref(), result_sha1, NULL, 0, DIE_ON_ERR); - else - /* TODO: */ - die("'git notes merge' cannot yet handle conflicts!"); + else /* Merge has unresolved conflicts */ + printf("Automatic notes merge failed. Fix conflicts in %s.\n", + git_path(NOTES_MERGE_WORKTREE)); free_notes(t); strbuf_release(&remote_ref); strbuf_release(&msg); - return 0; + return result < 0; /* return non-zero on conflicts */ } static int remove_cmd(int argc, const char **argv, const char *prefix) -- cgit v1.2.3 From 6abb3655efe364cf0375b5cdae2729af7562ed45 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 9 Nov 2010 22:49:52 +0100 Subject: git notes merge: Manual conflict resolution, part 2/2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the notes merge conflicts in .git/NOTES_MERGE_WORKTREE have been resolved, we need to record a new notes commit on the appropriate notes ref with the resolved notes. This patch implements 'git notes merge --commit' which the user should run after resolving conflicts in the notes merge worktree. This command finalizes the notes merge by recombining the partial notes tree from part 1 with the now-resolved conflicts in the notes merge worktree in a merge commit, and updating the appropriate ref to this merge commit. In order to correctly finalize the merge, we need to keep track of three things: - The partial merge result from part 1, containing the auto-merged notes. This is now stored into a ref called .git/NOTES_MERGE_PARTIAL. - The unmerged notes. These are already stored in .git/NOTES_MERGE_WORKTREE, thanks to part 1. - The notes ref to be updated by the finalized merge result. This is now stored in a symref called .git/NOTES_MERGE_REF. In addition to "git notes merge --commit", which uses the above details to create the finalized notes merge commit, this patch also implements "git notes merge --reset", which aborts the ongoing notes merge by simply removing the files/directory described above. FTR, "git notes merge --commit" reuses "git notes merge --reset" to remove the information described above (.git/NOTES_MERGE_*) after the notes merge have been successfully finalized. The patch also contains documentation and testcases for the two new options. This patch has been improved by the following contributions: - Ævar Arnfjörð Bjarmason: Fix nonsense sentence in --commit description - Sverre Rabbelier: Rename --reset to --abort Thanks-to: Ævar Arnfjörð Bjarmason Thanks-to: Sverre Rabbelier Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 3 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 309edc741e..b5385238ea 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -27,6 +27,8 @@ static const char * const git_notes_usage[] = { "git notes [--ref ] edit []", "git notes [--ref ] show []", "git notes [--ref ] merge [-v | -q] [-s ] ", + "git notes merge --commit [-v | -q]", + "git notes merge --abort [-v | -q]", "git notes [--ref ] remove []", "git notes [--ref ] prune [-n | -v]", NULL @@ -65,6 +67,8 @@ static const char * const git_notes_show_usage[] = { static const char * const git_notes_merge_usage[] = { "git notes merge [] ", + "git notes merge --commit []", + "git notes merge --abort []", NULL }; @@ -761,33 +765,119 @@ static int show(int argc, const char **argv, const char *prefix) return retval; } +static int merge_abort(struct notes_merge_options *o) +{ + int ret = 0; + + /* + * Remove .git/NOTES_MERGE_PARTIAL and .git/NOTES_MERGE_REF, and call + * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE. + */ + + if (delete_ref("NOTES_MERGE_PARTIAL", NULL, 0)) + ret += error("Failed to delete ref NOTES_MERGE_PARTIAL"); + if (delete_ref("NOTES_MERGE_REF", NULL, REF_NODEREF)) + ret += error("Failed to delete ref NOTES_MERGE_REF"); + if (notes_merge_abort(o)) + ret += error("Failed to remove 'git notes merge' worktree"); + return ret; +} + +static int merge_commit(struct notes_merge_options *o) +{ + struct strbuf msg = STRBUF_INIT; + unsigned char sha1[20]; + struct notes_tree *t; + struct commit *partial; + struct pretty_print_context pretty_ctx; + + /* + * Read partial merge result from .git/NOTES_MERGE_PARTIAL, + * and target notes ref from .git/NOTES_MERGE_REF. + */ + + if (get_sha1("NOTES_MERGE_PARTIAL", sha1)) + die("Failed to read ref NOTES_MERGE_PARTIAL"); + else if (!(partial = lookup_commit_reference(sha1))) + die("Could not find commit from NOTES_MERGE_PARTIAL."); + else if (parse_commit(partial)) + die("Could not parse commit from NOTES_MERGE_PARTIAL."); + + t = xcalloc(1, sizeof(struct notes_tree)); + init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0); + + o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, 0); + if (!o->local_ref) + die("Failed to resolve NOTES_MERGE_REF"); + + if (notes_merge_commit(o, t, partial, sha1)) + die("Failed to finalize notes merge"); + + /* Reuse existing commit message in reflog message */ + memset(&pretty_ctx, 0, sizeof(pretty_ctx)); + format_commit_message(partial, "%s", &msg, &pretty_ctx); + strbuf_trim(&msg); + strbuf_insert(&msg, 0, "notes: ", 7); + update_ref(msg.buf, o->local_ref, sha1, NULL, 0, DIE_ON_ERR); + + free_notes(t); + strbuf_release(&msg); + return merge_abort(o); +} + static int merge(int argc, const char **argv, const char *prefix) { struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT; unsigned char result_sha1[20]; struct notes_tree *t; struct notes_merge_options o; + int do_merge = 0, do_commit = 0, do_abort = 0; int verbosity = 0, result; const char *strategy = NULL; struct option options[] = { + OPT_GROUP("General options"), OPT__VERBOSITY(&verbosity), + OPT_GROUP("Merge options"), OPT_STRING('s', "strategy", &strategy, "strategy", "resolve notes conflicts using the given " "strategy (manual/ours/theirs/union)"), + OPT_GROUP("Committing unmerged notes"), + { OPTION_BOOLEAN, 0, "commit", &do_commit, NULL, + "finalize notes merge by committing unmerged notes", + PARSE_OPT_NOARG | PARSE_OPT_NONEG }, + OPT_GROUP("Aborting notes merge resolution"), + { OPTION_BOOLEAN, 0, "abort", &do_abort, NULL, + "abort notes merge", + PARSE_OPT_NOARG | PARSE_OPT_NONEG }, OPT_END() }; argc = parse_options(argc, argv, prefix, options, git_notes_merge_usage, 0); - if (argc != 1) { + if (strategy || do_commit + do_abort == 0) + do_merge = 1; + if (do_merge + do_commit + do_abort != 1) { + error("cannot mix --commit, --abort or -s/--strategy"); + usage_with_options(git_notes_merge_usage, options); + } + + if (do_merge && argc != 1) { error("Must specify a notes ref to merge"); usage_with_options(git_notes_merge_usage, options); + } else if (!do_merge && argc) { + error("too many parameters"); + usage_with_options(git_notes_merge_usage, options); } init_notes_merge_options(&o); o.verbosity = verbosity + NOTES_MERGE_VERBOSITY_DEFAULT; + if (do_abort) + return merge_abort(&o); + if (do_commit) + return merge_commit(&o); + o.local_ref = default_notes_ref(); strbuf_addstr(&remote_ref, argv[0]); expand_notes_ref(&remote_ref); @@ -820,9 +910,19 @@ static int merge(int argc, const char **argv, const char *prefix) /* Update default notes ref with new commit */ update_ref(msg.buf, default_notes_ref(), result_sha1, NULL, 0, DIE_ON_ERR); - else /* Merge has unresolved conflicts */ - printf("Automatic notes merge failed. Fix conflicts in %s.\n", + else { /* Merge has unresolved conflicts */ + /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */ + update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL, + 0, DIE_ON_ERR); + /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */ + if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL)) + die("Failed to store link to current notes ref (%s)", + default_notes_ref()); + printf("Automatic notes merge failed. Fix conflicts in %s and " + "commit the result with 'git notes merge --commit', or " + "abort the merge with 'git notes merge --abort'.\n", git_path(NOTES_MERGE_WORKTREE)); + } free_notes(t); strbuf_release(&remote_ref); -- cgit v1.2.3 From 443259cf929c0041310e3c77946252cbfc3f787d Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 9 Nov 2010 22:49:53 +0100 Subject: git notes merge: List conflicting notes in notes merge commit message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This brings notes merge in line with regular merge's behaviour. This patch has been improved by the following contributions: - Ævar Arnfjörð Bjarmason: Don't use C99 comments. Thanks-to: Ævar Arnfjörð Bjarmason Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index b5385238ea..ee1df7030c 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -902,7 +902,7 @@ static int merge(int argc, const char **argv, const char *prefix) strbuf_addf(&msg, "notes: Merged notes from %s into %s", remote_ref.buf, default_notes_ref()); - o.commit_msg = msg.buf + 7; // skip "notes: " prefix + strbuf_add(&(o.commit_msg), msg.buf + 7, msg.len - 7); /* skip "notes: " */ result = notes_merge(&o, t, result_sha1); -- cgit v1.2.3 From 6cfd6a9dea889707fa207ee2003010c3b56b2131 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 9 Nov 2010 22:49:54 +0100 Subject: git notes merge: --commit should fail if underlying notes ref has moved When manually resolving a notes merge, if the merging ref has moved since the merge started, we should fail to complete the merge, and alert the user to what's going on. This situation may arise if you start a 'git notes merge' which results in conflicts, and you then update the current notes ref (using for example 'git notes add/copy/amend/edit/remove/prune', 'git update-ref', etc.), before you get around to resolving the notes conflicts and calling 'git notes merge --commit'. We detect this situation by comparing the first parent of the partial merge commit (which was created when the merge started) to the current value of the merging notes ref (pointed to by the .git/NOTES_MERGE_REF symref). If we don't fail in this situation, the notes merge commit would overwrite the updated notes ref, thus losing the changes that happened in the meantime. The patch includes a testcase verifying that we fail correctly in this situation. Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index ee1df7030c..710a89da28 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -786,7 +786,7 @@ static int merge_abort(struct notes_merge_options *o) static int merge_commit(struct notes_merge_options *o) { struct strbuf msg = STRBUF_INIT; - unsigned char sha1[20]; + unsigned char sha1[20], parent_sha1[20]; struct notes_tree *t; struct commit *partial; struct pretty_print_context pretty_ctx; @@ -803,6 +803,11 @@ static int merge_commit(struct notes_merge_options *o) else if (parse_commit(partial)) die("Could not parse commit from NOTES_MERGE_PARTIAL."); + if (partial->parents) + hashcpy(parent_sha1, partial->parents->item->object.sha1); + else + hashclr(parent_sha1); + t = xcalloc(1, sizeof(struct notes_tree)); init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0); @@ -818,7 +823,9 @@ static int merge_commit(struct notes_merge_options *o) format_commit_message(partial, "%s", &msg, &pretty_ctx); strbuf_trim(&msg); strbuf_insert(&msg, 0, "notes: ", 7); - update_ref(msg.buf, o->local_ref, sha1, NULL, 0, DIE_ON_ERR); + update_ref(msg.buf, o->local_ref, sha1, + is_null_sha1(parent_sha1) ? NULL : parent_sha1, + 0, DIE_ON_ERR); free_notes(t); strbuf_release(&msg); -- cgit v1.2.3 From a6a09095a08339afc8468d053ff978ed4662a1d5 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Mon, 15 Nov 2010 00:57:17 +0100 Subject: git notes merge: Add another auto-resolving strategy: "cat_sort_uniq" This new strategy is similar to "concatenate", but in addition to concatenating the two note candidates, this strategy sorts the resulting lines, and removes duplicate lines from the result. This is equivalent to applying the "cat | sort | uniq" shell pipeline to the two note candidates. This strategy is useful if the notes follow a line-based format where one wants to avoid duplicate lines in the merge result. Note that if either of the note candidates contain duplicate lines _prior_ to the merge, these will also be removed by this merge strategy. The patch also contains tests and documentation for the new strategy. Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 710a89da28..455046e88a 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -324,6 +324,8 @@ combine_notes_fn parse_combine_notes_fn(const char *v) return combine_notes_ignore; else if (!strcasecmp(v, "concatenate")) return combine_notes_concatenate; + else if (!strcasecmp(v, "cat_sort_uniq")) + return combine_notes_cat_sort_uniq; else return NULL; } @@ -846,8 +848,8 @@ static int merge(int argc, const char **argv, const char *prefix) OPT__VERBOSITY(&verbosity), OPT_GROUP("Merge options"), OPT_STRING('s', "strategy", &strategy, "strategy", - "resolve notes conflicts using the given " - "strategy (manual/ours/theirs/union)"), + "resolve notes conflicts using the given strategy " + "(manual/ours/theirs/union/cat_sort_uniq)"), OPT_GROUP("Committing unmerged notes"), { OPTION_BOOLEAN, 0, "commit", &do_commit, NULL, "finalize notes merge by committing unmerged notes", @@ -899,6 +901,8 @@ static int merge(int argc, const char **argv, const char *prefix) o.strategy = NOTES_MERGE_RESOLVE_THEIRS; else if (!strcmp(strategy, "union")) o.strategy = NOTES_MERGE_RESOLVE_UNION; + else if (!strcmp(strategy, "cat_sort_uniq")) + o.strategy = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ; else { error("Unknown -s/--strategy: %s", strategy); usage_with_options(git_notes_merge_usage, options); -- cgit v1.2.3 From 618cd75707580819f19a5f01dba406ac72219c78 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 9 Nov 2010 22:49:57 +0100 Subject: Provide 'git notes get-ref' to easily retrieve current notes ref Script may use 'git notes get-ref' to easily retrieve the current notes ref. Suggested-by: Jonathan Nieder Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 455046e88a..f5abf7aa6e 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -31,6 +31,7 @@ static const char * const git_notes_usage[] = { "git notes merge --abort [-v | -q]", "git notes [--ref ] remove []", "git notes [--ref ] prune [-n | -v]", + "git notes [--ref ] get-ref", NULL }; @@ -82,6 +83,11 @@ static const char * const git_notes_prune_usage[] = { NULL }; +static const char * const git_notes_get_ref_usage[] = { + "git notes get-ref", + NULL +}; + static const char note_template[] = "\n" "#\n" @@ -1002,6 +1008,21 @@ static int prune(int argc, const char **argv, const char *prefix) return 0; } +static int get_ref(int argc, const char **argv, const char *prefix) +{ + struct option options[] = { OPT_END() }; + argc = parse_options(argc, argv, prefix, options, + git_notes_get_ref_usage, 0); + + if (argc) { + error("too many parameters"); + usage_with_options(git_notes_get_ref_usage, options); + } + + puts(default_notes_ref()); + return 0; +} + int cmd_notes(int argc, const char **argv, const char *prefix) { int result; @@ -1040,6 +1061,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix) result = remove_cmd(argc, argv, prefix); else if (!strcmp(argv[0], "prune")) result = prune(argc, argv, prefix); + else if (!strcmp(argv[0], "get-ref")) + result = get_ref(argc, argv, prefix); else { result = error("Unknown subcommand: %s", argv[0]); usage_with_options(git_notes_usage, options); -- cgit v1.2.3 From 23c6a803d3e6523ec20154e30ae8a51d2ff998ca Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Tue, 15 Feb 2011 14:09:12 +0100 Subject: Make lowercase as per CodingGuidelines *.c part for matches with '"[A-Z]+"'. Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- builtin/notes.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 4d5556e2cb..0aab150c52 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -537,16 +537,16 @@ static int add(int argc, const char **argv, const char *prefix) const unsigned char *note; struct msg_arg msg = { 0, 0, STRBUF_INIT }; struct option options[] = { - { OPTION_CALLBACK, 'm', "message", &msg, "MSG", + { OPTION_CALLBACK, 'm', "message", &msg, "msg", "note contents as a string", PARSE_OPT_NONEG, parse_msg_arg}, - { OPTION_CALLBACK, 'F', "file", &msg, "FILE", + { OPTION_CALLBACK, 'F', "file", &msg, "file", "note contents in a file", PARSE_OPT_NONEG, parse_file_arg}, - { OPTION_CALLBACK, 'c', "reedit-message", &msg, "OBJECT", + { OPTION_CALLBACK, 'c', "reedit-message", &msg, "object", "reuse and edit specified note object", PARSE_OPT_NONEG, parse_reedit_arg}, - { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT", + { OPTION_CALLBACK, 'C', "reuse-message", &msg, "object", "reuse specified note object", PARSE_OPT_NONEG, parse_reuse_arg}, OPT__FORCE(&force, "replace existing notes"), @@ -682,16 +682,16 @@ static int append_edit(int argc, const char **argv, const char *prefix) const char * const *usage; struct msg_arg msg = { 0, 0, STRBUF_INIT }; struct option options[] = { - { OPTION_CALLBACK, 'm', "message", &msg, "MSG", + { OPTION_CALLBACK, 'm', "message", &msg, "msg", "note contents as a string", PARSE_OPT_NONEG, parse_msg_arg}, - { OPTION_CALLBACK, 'F', "file", &msg, "FILE", + { OPTION_CALLBACK, 'F', "file", &msg, "file", "note contents in a file", PARSE_OPT_NONEG, parse_file_arg}, - { OPTION_CALLBACK, 'c', "reedit-message", &msg, "OBJECT", + { OPTION_CALLBACK, 'c', "reedit-message", &msg, "object", "reuse and edit specified note object", PARSE_OPT_NONEG, parse_reedit_arg}, - { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT", + { OPTION_CALLBACK, 'C', "reuse-message", &msg, "object", "reuse specified note object", PARSE_OPT_NONEG, parse_reuse_arg}, OPT_END() -- cgit v1.2.3 From caeba0ef153574d0240020228e0c48807b665a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Tue, 22 Feb 2011 23:42:26 +0000 Subject: i18n: git-notes basic commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/notes.c | 106 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 53 insertions(+), 53 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 4d5556e2cb..4c6cf85637 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -146,13 +146,13 @@ static void write_commented_object(int fd, const unsigned char *object) show.err = 0; show.git_cmd = 1; if (start_command(&show)) - die("unable to start 'show' for object '%s'", + die(_("unable to start 'show' for object '%s'"), sha1_to_hex(object)); /* Open the output as FILE* so strbuf_getline() can be used. */ show_out = xfdopen(show.out, "r"); if (show_out == NULL) - die_errno("can't fdopen 'show' output fd"); + die_errno(_("can't fdopen 'show' output fd")); /* Prepend "# " to each output line and write result to 'fd' */ while (strbuf_getline(&buf, show_out, '\n') != EOF) { @@ -162,10 +162,10 @@ static void write_commented_object(int fd, const unsigned char *object) } strbuf_release(&buf); if (fclose(show_out)) - die_errno("failed to close pipe to 'show' for object '%s'", + die_errno(_("failed to close pipe to 'show' for object '%s'"), sha1_to_hex(object)); if (finish_command(&show)) - die("failed to finish 'show' for object '%s'", + die(_("failed to finish 'show' for object '%s'"), sha1_to_hex(object)); } @@ -182,7 +182,7 @@ static void create_note(const unsigned char *object, struct msg_arg *msg, path = git_pathdup("NOTES_EDITMSG"); fd = open(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'"), path); if (msg->given) write_or_die(fd, msg->buf.buf, msg->buf.len); @@ -196,8 +196,8 @@ static void create_note(const unsigned char *object, struct msg_arg *msg, strbuf_reset(&(msg->buf)); if (launch_editor(path, &(msg->buf), NULL)) { - die("Please supply the note contents using either -m" \ - " or -F option"); + die(_("Please supply the note contents using either -m" \ + " or -F option")); } stripspace(&(msg->buf), 1); } @@ -217,14 +217,14 @@ static void create_note(const unsigned char *object, struct msg_arg *msg, } if (!msg->buf.len) { - fprintf(stderr, "Removing note for object %s\n", + 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"); + error(_("unable to write note object")); if (path) - error("The note contents has been left in %s", + error(_("The note contents has been left in %s"), path); exit(128); } @@ -258,9 +258,9 @@ static int parse_file_arg(const struct option *opt, const char *arg, int unset) strbuf_addch(&(msg->buf), '\n'); if (!strcmp(arg, "-")) { if (strbuf_read(&(msg->buf), 0, 1024) < 0) - die_errno("cannot read '%s'", arg); + die_errno(_("cannot read '%s'"), arg); } else if (strbuf_read_file(&(msg->buf), arg, 1024) < 0) - die_errno("could not open or read '%s'", arg); + die_errno(_("could not open or read '%s'"), arg); stripspace(&(msg->buf), 0); msg->given = 1; @@ -279,10 +279,10 @@ static int parse_reuse_arg(const struct option *opt, const char *arg, int unset) strbuf_addch(&(msg->buf), '\n'); if (get_sha1(arg, object)) - die("Failed to resolve '%s' as a valid ref.", arg); + die(_("Failed to resolve '%s' as a valid ref."), arg); if (!(buf = read_sha1_file(object, &type, &len)) || !len) { free(buf); - die("Failed to read object '%s'.", arg);; + die(_("Failed to read object '%s'."), arg);; } strbuf_add(&(msg->buf), buf, len); free(buf); @@ -306,7 +306,7 @@ void commit_notes(struct notes_tree *t, const char *msg) if (!t) t = &default_notes_tree; if (!t->initialized || !t->ref || !*t->ref) - die("Cannot commit uninitialized/unreferenced notes tree"); + die(_("Cannot commit uninitialized/unreferenced notes tree")); if (!t->dirty) return; /* don't have to commit an unchanged tree */ @@ -347,7 +347,7 @@ static int notes_rewrite_config(const char *k, const char *v, void *cb) config_error_nonbool(k); c->combine = parse_combine_notes_fn(v); if (!c->combine) { - error("Bad notes.rewriteMode value: '%s'", v); + error(_("Bad notes.rewriteMode value: '%s'"), v); return 1; } return 0; @@ -357,8 +357,8 @@ static int notes_rewrite_config(const char *k, const char *v, void *cb) if (!prefixcmp(v, "refs/notes/")) string_list_add_refs_by_glob(c->refs, v); else - warning("Refusing to rewrite notes in %s" - " (outside of refs/notes/)", v); + warning(_("Refusing to rewrite notes in %s" + " (outside of refs/notes/)"), v); return 0; } @@ -446,13 +446,13 @@ int notes_copy_from_stdin(int force, const char *rewrite_cmd) split = strbuf_split(&buf, ' '); if (!split[0] || !split[1]) - die("Malformed input line: '%s'.", buf.buf); + die(_("Malformed input line: '%s'."), buf.buf); strbuf_rtrim(split[0]); strbuf_rtrim(split[1]); if (get_sha1(split[0]->buf, from_obj)) - die("Failed to resolve '%s' as a valid ref.", split[0]->buf); + die(_("Failed to resolve '%s' as a valid ref."), split[0]->buf); if (get_sha1(split[1]->buf, to_obj)) - die("Failed to resolve '%s' as a valid ref.", split[1]->buf); + die(_("Failed to resolve '%s' as a valid ref."), split[1]->buf); if (rewrite_cmd) err = copy_note_for_rewrite(c, from_obj, to_obj); @@ -461,7 +461,7 @@ int notes_copy_from_stdin(int force, const char *rewrite_cmd) combine_notes_overwrite); if (err) { - error("Failed to copy notes from '%s' to '%s'", + error(_("Failed to copy notes from '%s' to '%s'"), split[0]->buf, split[1]->buf); ret = 1; } @@ -505,20 +505,20 @@ static int list(int argc, const char **argv, const char *prefix) git_notes_list_usage, 0); if (1 < argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(git_notes_list_usage, options); } t = init_notes_check("list"); if (argc) { if (get_sha1(argv[0], object)) - die("Failed to resolve '%s' as a valid ref.", argv[0]); + die(_("Failed to resolve '%s' as a valid ref."), argv[0]); note = get_note(t, object); if (note) { puts(sha1_to_hex(note)); retval = 0; } else - retval = error("No note found for object %s.", + retval = error(_("No note found for object %s."), sha1_to_hex(object)); } else retval = for_each_note(t, 0, list_each_note, NULL); @@ -557,26 +557,26 @@ static int add(int argc, const char **argv, const char *prefix) 0); if (1 < argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(git_notes_add_usage, options); } object_ref = argc ? argv[0] : "HEAD"; if (get_sha1(object_ref, object)) - die("Failed to resolve '%s' as a valid ref.", object_ref); + die(_("Failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("add"); note = get_note(t, object); if (note) { if (!force) { - retval = error("Cannot add notes. Found existing notes " + retval = error(_("Cannot add notes. Found existing notes " "for object %s. Use '-f' to overwrite " - "existing notes", sha1_to_hex(object)); + "existing notes"), sha1_to_hex(object)); goto out; } - fprintf(stderr, "Overwriting existing notes for object %s\n", + fprintf(stderr, _("Overwriting existing notes for object %s\n"), sha1_to_hex(object)); } @@ -618,7 +618,7 @@ static int copy(int argc, const char **argv, const char *prefix) if (from_stdin || rewrite_cmd) { if (argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(git_notes_copy_usage, options); } else { return notes_copy_from_stdin(force, rewrite_cmd); @@ -626,41 +626,41 @@ static int copy(int argc, const char **argv, const char *prefix) } if (argc < 2) { - error("too few parameters"); + error(_("too few parameters")); usage_with_options(git_notes_copy_usage, options); } if (2 < argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(git_notes_copy_usage, options); } if (get_sha1(argv[0], from_obj)) - die("Failed to resolve '%s' as a valid ref.", argv[0]); + die(_("Failed to resolve '%s' as a valid ref."), argv[0]); object_ref = 1 < argc ? argv[1] : "HEAD"; if (get_sha1(object_ref, object)) - die("Failed to resolve '%s' as a valid ref.", object_ref); + die(_("Failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("copy"); note = get_note(t, object); if (note) { if (!force) { - retval = error("Cannot copy notes. Found existing " + retval = error(_("Cannot copy notes. Found existing " "notes for object %s. Use '-f' to " - "overwrite existing notes", + "overwrite existing notes"), sha1_to_hex(object)); goto out; } - fprintf(stderr, "Overwriting existing notes for object %s\n", + fprintf(stderr, _("Overwriting existing notes for object %s\n"), sha1_to_hex(object)); } from_note = get_note(t, from_obj); if (!from_note) { - retval = error("Missing notes on source object %s. Cannot " - "copy.", sha1_to_hex(from_obj)); + retval = error(_("Missing notes on source object %s. Cannot " + "copy."), sha1_to_hex(from_obj)); goto out; } @@ -703,19 +703,19 @@ static int append_edit(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_ARGV0); if (2 < argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(usage, options); } if (msg.given && edit) - fprintf(stderr, "The -m/-F/-c/-C options have been deprecated " + 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"); + "Please use 'git notes add -f -m/-F/-c/-C' instead.\n")); object_ref = 1 < argc ? argv[1] : "HEAD"; if (get_sha1(object_ref, object)) - die("Failed to resolve '%s' as a valid ref.", object_ref); + die(_("Failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check(argv[0]); note = get_note(t, object); @@ -750,20 +750,20 @@ static int show(int argc, const char **argv, const char *prefix) 0); if (1 < argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(git_notes_show_usage, options); } object_ref = argc ? argv[0] : "HEAD"; if (get_sha1(object_ref, object)) - die("Failed to resolve '%s' as a valid ref.", object_ref); + die(_("Failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("show"); note = get_note(t, object); if (!note) - retval = error("No note found for object %s.", + retval = error(_("No note found for object %s."), sha1_to_hex(object)); else { const char *show_args[3] = {"show", sha1_to_hex(note), NULL}; @@ -961,22 +961,22 @@ static int remove_cmd(int argc, const char **argv, const char *prefix) git_notes_remove_usage, 0); if (1 < argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(git_notes_remove_usage, options); } object_ref = argc ? argv[0] : "HEAD"; if (get_sha1(object_ref, object)) - die("Failed to resolve '%s' as a valid ref.", object_ref); + die(_("Failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("remove"); retval = remove_note(t, object); if (retval) - fprintf(stderr, "Object %s has no note\n", sha1_to_hex(object)); + fprintf(stderr, _("Object %s has no note\n"), sha1_to_hex(object)); else { - fprintf(stderr, "Removing note for object %s\n", + fprintf(stderr, _("Removing note for object %s\n"), sha1_to_hex(object)); commit_notes(t, "Notes removed by 'git notes remove'"); @@ -999,7 +999,7 @@ static int prune(int argc, const char **argv, const char *prefix) 0); if (argc) { - error("too many parameters"); + error(_("too many parameters")); usage_with_options(git_notes_prune_usage, options); } @@ -1069,7 +1069,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[0], "get-ref")) result = get_ref(argc, argv, prefix); else { - result = error("Unknown subcommand: %s", argv[0]); + result = error(_("Unknown subcommand: %s"), argv[0]); usage_with_options(git_notes_usage, options); } -- cgit v1.2.3 From e3bd758335ccb2d46ddde628bf391d5155f87730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Tue, 22 Feb 2011 23:42:27 +0000 Subject: i18n: git-notes GIT_NOTES_REWRITE_MODE error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use sprintf format for the error message that's displayed if GIT_NOTES_REWRITE_MODE is invalid, and leave a note in a TRANSLATORS comment indicating what the message means. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/notes.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 4c6cf85637..a0aa995158 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -382,8 +382,10 @@ struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd) c->mode_from_env = 1; c->combine = parse_combine_notes_fn(rewrite_mode_env); if (!c->combine) - error("Bad " GIT_NOTES_REWRITE_MODE_ENVIRONMENT - " value: '%s'", rewrite_mode_env); + /* TRANSLATORS: The first %s is the name of the + environment variable, the second %s is its value */ + error(_("Bad %s value: '%s'"), GIT_NOTES_REWRITE_MODE_ENVIRONMENT, + rewrite_mode_env); } if (rewrite_refs_env) { c->refs_from_env = 1; -- cgit v1.2.3 From c2e86addb86689306b992065328ec52aa2479658 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 22 Mar 2011 00:51:05 -0700 Subject: Fix sparse warnings Fix warnings from 'make check'. - These files don't include 'builtin.h' causing sparse to complain that cmd_* isn't declared: builtin/clone.c:364, builtin/fetch-pack.c:797, builtin/fmt-merge-msg.c:34, builtin/hash-object.c:78, builtin/merge-index.c:69, builtin/merge-recursive.c:22 builtin/merge-tree.c:341, builtin/mktag.c:156, builtin/notes.c:426 builtin/notes.c:822, builtin/pack-redundant.c:596, builtin/pack-refs.c:10, builtin/patch-id.c:60, builtin/patch-id.c:149, builtin/remote.c:1512, builtin/remote-ext.c:240, builtin/remote-fd.c:53, builtin/reset.c:236, builtin/send-pack.c:384, builtin/unpack-file.c:25, builtin/var.c:75 - These files have symbols which should be marked static since they're only file scope: submodule.c:12, diff.c:631, replace_object.c:92, submodule.c:13, submodule.c:14, trace.c:78, transport.c:195, transport-helper.c:79, unpack-trees.c:19, url.c:3, url.c:18, url.c:104, url.c:117, url.c:123, url.c:129, url.c:136, thread-utils.c:21, thread-utils.c:48 - These files redeclare symbols to be different types: builtin/index-pack.c:210, parse-options.c:564, parse-options.c:571, usage.c:49, usage.c:58, usage.c:63, usage.c:72 - These files use a literal integer 0 when they really should use a NULL pointer: daemon.c:663, fast-import.c:2942, imap-send.c:1072, notes-merge.c:362 While we're in the area, clean up some unused #includes in builtin files (mostly exec_cmd.h). Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- builtin/notes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 0aab150c52..a0f310b729 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -423,7 +423,7 @@ void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c) free(c); } -int notes_copy_from_stdin(int force, const char *rewrite_cmd) +static int notes_copy_from_stdin(int force, const char *rewrite_cmd) { struct strbuf buf = STRBUF_INIT; struct notes_rewrite_cfg *c = NULL; @@ -819,7 +819,7 @@ static int merge_commit(struct notes_merge_options *o) t = xcalloc(1, sizeof(struct notes_tree)); init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0); - o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, 0); + o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, NULL); if (!o->local_ref) die("Failed to resolve NOTES_MERGE_REF"); -- cgit v1.2.3 From 03bb5789cd97de989897e1c9de71a2831ada0544 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 29 Mar 2011 16:55:32 -0400 Subject: notes: make expand_notes_ref globally accessible This function is useful for other commands besides "git notes" which want to let users refer to notes by their shorthand name. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/notes.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index a0f310b729..62276072fc 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -100,16 +100,6 @@ struct msg_arg { struct strbuf buf; }; -static void expand_notes_ref(struct strbuf *sb) -{ - if (!prefixcmp(sb->buf, "refs/notes/")) - return; /* we're happy */ - else if (!prefixcmp(sb->buf, "notes/")) - strbuf_insert(sb, 0, "refs/", 5); - else - strbuf_insert(sb, 0, "refs/notes/", 11); -} - static int list_each_note(const unsigned char *object_sha1, const unsigned char *note_sha1, char *note_path, void *cb_data) -- cgit v1.2.3 From 84a7e35eea97388ba5ca458808119650818c2fd2 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Wed, 30 Mar 2011 02:02:55 +0200 Subject: Make "git notes add" more user-friendly when there are existing notes Currently, "notes add" (without -f/--force) will abort when the given object already has existing notes. This makes sense for the modes of "git notes add" that would necessarily overwrite the old message (when using the -m/-F/-C/-c options). However, when no options are given (meaning the notes are created from scratch in the editor) it is not very user-friendly to abort on existing notes, and forcing the user to run "git notes edit". Instead, it is better to simply "redirect" to "git notes edit" automatically, i.e. open the existing notes in the editor and let the user edit them. This patch does just that. This changes the behavior of "git notes add" without options when notes already exist for the given object, but I doubt that many users really depend on the previous failure from "git notes add" in this case. Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index a0f310b729..0d133be49b 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -527,6 +527,8 @@ static int list(int argc, const char **argv, const char *prefix) return retval; } +static int append_edit(int argc, const char **argv, const char *prefix); + static int add(int argc, const char **argv, const char *prefix) { int retval = 0, force = 0; @@ -554,14 +556,14 @@ static int add(int argc, const char **argv, const char *prefix) }; argc = parse_options(argc, argv, prefix, options, git_notes_add_usage, - 0); + PARSE_OPT_KEEP_ARGV0); - if (1 < argc) { + if (2 < argc) { error("too many parameters"); usage_with_options(git_notes_add_usage, options); } - object_ref = argc ? argv[0] : "HEAD"; + object_ref = argc > 1 ? argv[1] : "HEAD"; if (get_sha1(object_ref, object)) die("Failed to resolve '%s' as a valid ref.", object_ref); @@ -571,6 +573,18 @@ static int add(int argc, const char **argv, const char *prefix) 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); + } retval = error("Cannot add notes. Found existing notes " "for object %s. Use '-f' to overwrite " "existing notes", sha1_to_hex(object)); -- cgit v1.2.3 From c3ab1a8e4cb8a84967efcf45c5e6bee41b17f9f9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 18 May 2011 15:44:37 -0700 Subject: notes remove: allow removing more than one While "xargs -n1 git notes rm" is certainly a possible way to remove notes from many objects, this would create one notes "commit" per removal, which is not quite suitable for seasonal housekeeping. Allow taking more than one on the command line, and record their removal as a single atomic event if everthing goes well. Even though the old code insisted that "git notes rm" must be given only one object (or zero, in which case it would default to HEAD), this condition was not tested. Add tests to handle the new case where we feed multiple objects, and also make sure if there is a bad input, no change is recorded. Signed-off-by: Junio C Hamano --- builtin/notes.c | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 1fb1f73439..30cee0fd3c 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -29,7 +29,7 @@ static const char * const git_notes_usage[] = { "git notes [--ref ] merge [-v | -q] [-s ] ", "git notes merge --commit [-v | -q]", "git notes merge --abort [-v | -q]", - "git notes [--ref ] remove []", + "git notes [--ref ] remove [...]", "git notes [--ref ] prune [-n | -v]", "git notes [--ref ] get-ref", NULL @@ -953,40 +953,43 @@ static int merge(int argc, const char **argv, const char *prefix) return result < 0; /* return non-zero on conflicts */ } +static int remove_one_note(struct notes_tree *t, const char *name) +{ + int status; + unsigned char sha1[20]; + if (get_sha1(name, sha1)) + return error(_("Failed to resolve '%s' as a valid ref."), name); + status = remove_note(t, sha1); + if (status) + fprintf(stderr, _("Object %s has no note\n"), name); + else + fprintf(stderr, _("Removing note for object %s\n"), name); + return status; +} + static int remove_cmd(int argc, const char **argv, const char *prefix) { struct option options[] = { OPT_END() }; - const char *object_ref; struct notes_tree *t; - unsigned char object[20]; - int retval; + int retval = 0; argc = parse_options(argc, argv, prefix, options, git_notes_remove_usage, 0); - if (1 < argc) { - error(_("too many parameters")); - usage_with_options(git_notes_remove_usage, options); - } - - object_ref = argc ? argv[0] : "HEAD"; - - if (get_sha1(object_ref, object)) - die(_("Failed to resolve '%s' as a valid ref."), object_ref); - t = init_notes_check("remove"); - retval = remove_note(t, object); - if (retval) - fprintf(stderr, _("Object %s has no note\n"), sha1_to_hex(object)); - else { - fprintf(stderr, _("Removing note for object %s\n"), - sha1_to_hex(object)); - - commit_notes(t, "Notes removed by 'git notes remove'"); + if (!argc) { + retval = remove_one_note(t, "HEAD"); + } else { + while (*argv) { + retval |= remove_one_note(t, *argv); + argv++; + } } + if (!retval) + commit_notes(t, "Notes removed by 'git notes remove'"); free_notes(t); return retval; } -- cgit v1.2.3 From 2d370d2fbcf896912cc0739b806224f9a270bb5e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 18 May 2011 16:02:58 -0700 Subject: notes remove: --ignore-missing Depending on the application, it is not necessarily an error for an object to lack a note, especially if the only thing the caller wants to make sure is that notes are cleared for an object. By passing this option from the command line, the "git notes remove" command considers it a success if the object did not have any note to begin with. Signed-off-by: Junio C Hamano --- builtin/notes.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 30cee0fd3c..541f776ebb 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -953,7 +953,9 @@ static int merge(int argc, const char **argv, const char *prefix) return result < 0; /* return non-zero on conflicts */ } -static int remove_one_note(struct notes_tree *t, const char *name) +#define MISSING_OK 1 + +static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag) { int status; unsigned char sha1[20]; @@ -964,12 +966,16 @@ static int remove_one_note(struct notes_tree *t, const char *name) fprintf(stderr, _("Object %s has no note\n"), name); else fprintf(stderr, _("Removing note for object %s\n"), name); - return status; + return (flag & MISSING_OK) ? 0 : status; } static int remove_cmd(int argc, const char **argv, const char *prefix) { + unsigned flag = 0; struct option options[] = { + OPT_BIT(0, "ignore-missing", &flag, + "attempt to remove non-existent note is not an error", + MISSING_OK), OPT_END() }; struct notes_tree *t; @@ -981,10 +987,10 @@ static int remove_cmd(int argc, const char **argv, const char *prefix) t = init_notes_check("remove"); if (!argc) { - retval = remove_one_note(t, "HEAD"); + retval = remove_one_note(t, "HEAD", flag); } else { while (*argv) { - retval |= remove_one_note(t, *argv); + retval |= remove_one_note(t, *argv, flag); argv++; } } -- cgit v1.2.3 From 46538012d943156ead4024fb510a1ce5d4ff443a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 18 May 2011 16:44:30 -0700 Subject: notes remove: --stdin reads from the standard input Teach the command to read object names to remove from the standard input, in addition to the object names given from the command line. Signed-off-by: Junio C Hamano --- builtin/notes.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'builtin/notes.c') diff --git a/builtin/notes.c b/builtin/notes.c index 541f776ebb..f8e437db01 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -953,7 +953,7 @@ static int merge(int argc, const char **argv, const char *prefix) return result < 0; /* return non-zero on conflicts */ } -#define MISSING_OK 1 +#define IGNORE_MISSING 1 static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag) { @@ -966,16 +966,19 @@ static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag fprintf(stderr, _("Object %s has no note\n"), name); else fprintf(stderr, _("Removing note for object %s\n"), name); - return (flag & MISSING_OK) ? 0 : status; + return (flag & IGNORE_MISSING) ? 0 : status; } static int remove_cmd(int argc, const char **argv, const char *prefix) { unsigned flag = 0; + int from_stdin = 0; struct option options[] = { OPT_BIT(0, "ignore-missing", &flag, "attempt to remove non-existent note is not an error", - MISSING_OK), + IGNORE_MISSING), + OPT_BOOLEAN(0, "stdin", &from_stdin, + "read object names from the standard input"), OPT_END() }; struct notes_tree *t; @@ -986,7 +989,7 @@ static int remove_cmd(int argc, const char **argv, const char *prefix) t = init_notes_check("remove"); - if (!argc) { + if (!argc && !from_stdin) { retval = remove_one_note(t, "HEAD", flag); } else { while (*argv) { @@ -994,6 +997,14 @@ static int remove_cmd(int argc, const char **argv, const char *prefix) argv++; } } + if (from_stdin) { + struct strbuf sb = STRBUF_INIT; + while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) { + strbuf_rtrim(&sb); + retval |= remove_one_note(t, sb.buf, flag); + } + strbuf_release(&sb); + } if (!retval) commit_notes(t, "Notes removed by 'git notes remove'"); free_notes(t); -- cgit v1.2.3